Xiang Wang has uploaded this change for review. ( https://review.coreboot.org/c/flashrom/+/49254 )
Change subject: sysfsgpio.c implement spi interface via linux sysfs ......................................................................
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