Xiang Wang has uploaded this change for review.

View Change

sysfsgpio.c implement spi interface via linux sysfs

Linux can operate gpio through sysfs, this patch implements a bitbang
spi interface through sysfs. Through this patch, some SBC can be used
as flash programmers.

Change-Id: I1bc47ef8011bba560326b501a80869340bb9f733
Signed-off-by: Xiang Wang <merle@hardenedliux.org>
---
M Makefile
M flashrom.c
M programmer.h
A sysfsgpio.c
4 files changed, 280 insertions(+), 0 deletions(-)

git pull ssh://review.coreboot.org:29418/flashrom refs/changes/54/49254/1
diff --git a/Makefile b/Makefile
index 1f7ea46..b4383a2 100644
--- a/Makefile
+++ b/Makefile
@@ -482,6 +482,7 @@

ifeq ($(TARGET_OS), Linux)
CONFIG_LINUX_I2C_HELPER = yes
+CONFIG_SYSFSGPIO = yes
endif

###############################################################################
@@ -797,6 +798,8 @@
# Disable wiki printing by default. It is only useful if you have wiki access.
CONFIG_PRINT_WIKI ?= no

+CONFIG_SYSFSGPIO ?= no
+
# Disable all features if CONFIG_NOTHING=yes is given unless CONFIG_EVERYTHING was also set
ifeq ($(CONFIG_NOTHING), yes)
ifeq ($(CONFIG_EVERYTHING), yes)
@@ -1026,6 +1029,11 @@
PROGRAMMER_OBJS += realtek_mst_i2c_spi.o
endif

+ifeq ($(CONFIG_SYSFSGPIO), yes)
+FEATURE_CFLAGS += -D'CONFIG_SYSFSGPIO=1'
+PROGRAMMER_OBJS += sysfsgpio.o
+endif
+
ifneq ($(NEED_LIBFTDI), )
FTDILIBS := $(call debug_shell,[ -n "$(PKG_CONFIG_LIBDIR)" ] && export PKG_CONFIG_LIBDIR="$(PKG_CONFIG_LIBDIR)" ; $(PKG_CONFIG) --libs libftdi1 || $(PKG_CONFIG) --libs libftdi || printf "%s" "-lftdi -lusb")
FEATURE_CFLAGS += $(call debug_shell,grep -q "FT232H := yes" .features && printf "%s" "-D'HAVE_FT232H=1'")
diff --git a/flashrom.c b/flashrom.c
index c89abad..dc9834b 100644
--- a/flashrom.c
+++ b/flashrom.c
@@ -533,6 +533,18 @@
},
#endif

+#if CONFIG_SYSFSGPIO == 1
+ {
+ .name = "sysfsgpio",
+ .type = OTHER,
+ .devs.note = "bitbang spi interface using gpio via sysfs\n",
+ .init = sysfsgpio_init,
+ .map_flash_region = fallback_map,
+ .unmap_flash_region = fallback_unmap,
+ .delay = internal_delay,
+ },
+#endif
+
{0}, /* This entry corresponds to PROGRAMMER_INVALID. */
};

diff --git a/programmer.h b/programmer.h
index d66d239..5dbbaf9 100644
--- a/programmer.h
+++ b/programmer.h
@@ -142,6 +142,9 @@
#if CONFIG_REALTEK_MST_I2C_SPI == 1
PROGRAMMER_REALTEK_MST_I2C_SPI,
#endif
+#if CONFIG_SYSFSGPIO == 1
+ PROGRAMMER_SYSFSGPIO,
+#endif
PROGRAMMER_INVALID /* This must always be the last entry. */
};

@@ -599,6 +602,11 @@
int ni845x_spi_init(void);
#endif

+/* sysfsgpio.c */
+#if CONFIG_SYSFSGPIO == 1
+int sysfsgpio_init(void);
+#endif
+
/* flashrom.c */
struct decode_sizes {
uint32_t parallel;
diff --git a/sysfsgpio.c b/sysfsgpio.c
new file mode 100644
index 0000000..1b3dff7
--- /dev/null
+++ b/sysfsgpio.c
@@ -0,0 +1,252 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2021 HardenedLinux
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/* This driver implements a bitbang spi interface using gpio via sysfs. */
+
+#include "platform.h"
+
+#if IS_LINUX
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include "programmer.h"
+
+#define GPIO_EXPORT "/sys/class/gpio/export"
+#define GPIO_UNEXPORT "/sys/class/gpio/unexport"
+#define GPIO_PATH "/sys/class/gpio/gpio%d/"
+#define GPIO_VALUE "/sys/class/gpio/gpio%d/value"
+#define GPIO_DIRECTION "/sys/class/gpio/gpio%d/direction"
+
+#define EXIST_PATH(path) (access((path), F_OK) == 0)
+
+struct pin_desc {
+ const char *name;
+ int pin;
+ int fd_direction;
+ int fd_value;
+};
+
+static struct pin_desc pin_cs = {
+ .name = "cs",
+ .fd_direction = -1,
+ .fd_value = -1
+};
+
+static struct pin_desc pin_sck = {
+ .name = "sck",
+ .fd_direction = -1,
+ .fd_value = -1
+};
+
+static struct pin_desc pin_mosi = {
+ .name = "mosi",
+ .fd_direction = -1,
+ .fd_value = -1
+};
+
+static struct pin_desc pin_miso = {
+ .name = "miso",
+ .fd_direction = -1,
+ .fd_value = -1
+};
+
+static int open_write_close(const char *path, const char *str)
+{
+ int fd = open(path, O_WRONLY);
+ if (fd < 0) {
+ msg_perr("Error: failed to open %s\n", path);
+ return -1;
+ }
+ int ret = write(fd, str, strlen(str));
+ close(fd);
+ if (ret == (int)strlen(str))
+ return 0;
+ msg_perr("Error: failed to write %s to %s", str, path);
+ return -1;
+}
+
+static int export_sysfsgpio(struct pin_desc *desc)
+{
+ char s[64];
+
+ snprintf(s, sizeof(s), GPIO_PATH, desc->pin);
+ if (!EXIST_PATH(s)) {
+ snprintf(s, sizeof(s), "%d", desc->pin);
+ if (open_write_close(GPIO_EXPORT, s))
+ return -1;
+ }
+
+ snprintf(s, sizeof(s), GPIO_DIRECTION, desc->pin);
+ desc->fd_direction = open(s, O_WRONLY);
+ if (desc->fd_direction < 0) {
+ msg_perr("Error: failed to open %s\n", s);
+ return -1;
+ }
+
+ snprintf(s, sizeof(s), GPIO_VALUE, desc->pin);
+ desc->fd_value = open(s, O_RDONLY);
+ if (desc->fd_value < 0) {
+ msg_perr("Error: failed to open %s\n", s);
+ return -1;
+ }
+
+ if (write(desc->fd_direction, "in", 2) != 2) {
+ msg_perr("Error: failed to set %s as input", desc->name);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void unexport_sysfsgpio(struct pin_desc *desc)
+{
+ char s[64];
+
+ if (desc->fd_direction >= 0)
+ close(desc->fd_direction);
+ if (desc->fd_value >= 0)
+ close(desc->fd_value);
+
+ snprintf(s, sizeof(s), GPIO_PATH, desc->pin);
+ if (EXIST_PATH(s)) {
+ snprintf(s, sizeof(s), "%d", desc->pin);
+ open_write_close(GPIO_UNEXPORT, s);
+ }
+
+ desc->fd_value = -1;
+ desc->fd_direction = -1;
+}
+
+static void sysfsgpio_set(struct pin_desc *desc, int val)
+{
+ const char *s = val ? "high" : "low";
+ int count = val ? 4 : 3;
+ if (write(desc->fd_direction, s, count) != count)
+ msg_perr("Error: failed to set %s\n", desc->name);
+}
+
+static int sysfsgpio_get(struct pin_desc *desc)
+{
+ char v;
+ lseek(desc->fd_value, 0, SEEK_SET);
+ if (read(desc->fd_value, &v, 1) != 1)
+ msg_perr("Error: failed to get %s\n", desc->name);
+ return v != '0';
+}
+
+static void sysfsgpio_set_cs(int val)
+{
+ sysfsgpio_set(&pin_cs, val);
+}
+
+static void sysfsgpio_set_sck(int val)
+{
+ sysfsgpio_set(&pin_sck, val);
+}
+
+static void sysfsgpio_set_mosi(int val)
+{
+ sysfsgpio_set(&pin_mosi, val);
+}
+
+static int sysfsgpio_get_miso(void)
+{
+ return sysfsgpio_get(&pin_miso);
+}
+
+static struct bitbang_spi_master bitbang_spi_sysfsgpio = {
+ .set_cs = sysfsgpio_set_cs,
+ .set_sck = sysfsgpio_set_sck,
+ .set_mosi = sysfsgpio_set_mosi,
+ .get_miso = sysfsgpio_get_miso,
+};
+
+static int shutdown_sysfsgpio(void *data)
+{
+ unexport_sysfsgpio(&pin_cs);
+ unexport_sysfsgpio(&pin_sck);
+ unexport_sysfsgpio(&pin_mosi);
+ unexport_sysfsgpio(&pin_miso);
+ return 0;
+}
+
+static int atoi_s(const char *s, int allstr, long *out)
+{
+ char *endptr;
+ errno = 0;
+ *out = strtol(s, &endptr, 0);
+ if ((errno == ERANGE && (*out == LONG_MAX || *out == LONG_MIN))
+ || errno || s == endptr)
+ return -1;
+ if (allstr && *endptr != '\0')
+ return -1;
+ return 0;
+}
+
+int sysfsgpio_init(void)
+{
+ if (register_shutdown(shutdown_sysfsgpio, NULL))
+ return -1;
+
+ /* parameter format: pins=cs_pin:sck_pin:mosi_pin:miso_pin */
+ char *pins = extract_programmer_param("pins");
+ int pins_inited = 0;
+ do {
+ struct pin_desc *pins_tab[] = {
+ &pin_cs, &pin_sck, &pin_mosi, &pin_miso
+ };
+ if (!(pins && strlen(pins)))
+ break;
+ char *token = strtok(pins, ":");
+ for (unsigned i = 0; i < ARRAY_SIZE(pins_tab); i++) {
+ long v;
+ if (!token)
+ break;
+ if (atoi_s(token, 1, &v))
+ break;
+ pins_tab[i]->pin = v;
+ if (export_sysfsgpio(pins_tab[i]))
+ break;
+ token = strtok(NULL, ":");
+ pins_inited = (i + 1 == ARRAY_SIZE(pins_tab));
+ }
+ } while (0);
+ if (pins)
+ free(pins);
+ if (!pins_inited)
+ return 1;
+
+ msg_pdbg("sysfsgpio: CS pin is %d\n", pin_cs.pin);
+ msg_pdbg("sysfsgpio: SCK pin is %d\n", pin_sck.pin);
+ msg_pdbg("sysfsgpio: MOSI pin is %d\n", pin_mosi.pin);
+ msg_pdbg("sysfsgpio: MISO pin is %d\n", pin_miso.pin);
+ msg_pdbg("sysfsgpio: half_period is %d\n",
+ bitbang_spi_sysfsgpio.half_period);
+
+ if (register_spi_bitbang_master(&bitbang_spi_sysfsgpio)) {
+ msg_perr("sysfsgpio bitbang SPI master init failed!\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+#endif

To view, visit change 49254. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: flashrom
Gerrit-Branch: master
Gerrit-Change-Id: I1bc47ef8011bba560326b501a80869340bb9f733
Gerrit-Change-Number: 49254
Gerrit-PatchSet: 1
Gerrit-Owner: Xiang Wang <merle@hardenedlinux.org>
Gerrit-MessageType: newchange