diff options
Diffstat (limited to 'linux_spi.c')
-rw-r--r-- | linux_spi.c | 174 |
1 files changed, 99 insertions, 75 deletions
diff --git a/linux_spi.c b/linux_spi.c index bbd45f3b5..ceca05f9a 100644 --- a/linux_spi.c +++ b/linux_spi.c @@ -13,8 +13,6 @@ * GNU General Public License for more details. */ -#if CONFIG_LINUX_SPI == 1 - #include <stdio.h> #include <string.h> #include <stdlib.h> @@ -44,29 +42,33 @@ * HummingBoard */ -static int fd = -1; #define BUF_SIZE_FROM_SYSFS "/sys/module/spidev/parameters/bufsiz" -static size_t max_kernel_buf_size; + +struct linux_spi_data { + int fd; + size_t max_kernel_buf_size; +}; static int linux_spi_read(struct flashctx *flash, uint8_t *buf, unsigned int start, unsigned int len) { + struct linux_spi_data *spi_data = flash->mst->spi.data; /* Older kernels use a single buffer for combined input and output data. So account for longest possible command + address, too. */ - return spi_read_chunked(flash, buf, start, len, max_kernel_buf_size - 5); + return spi_read_chunked(flash, buf, start, len, spi_data->max_kernel_buf_size - 5); } static int linux_spi_write_256(struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len) { + struct linux_spi_data *spi_data = flash->mst->spi.data; /* 5 bytes must be reserved for longest possible command + address. */ - return spi_write_chunked(flash, buf, start, len, max_kernel_buf_size - 5); + return spi_write_chunked(flash, buf, start, len, spi_data->max_kernel_buf_size - 5); } static int linux_spi_shutdown(void *data) { - if (fd != -1) { - close(fd); - fd = -1; - } + struct linux_spi_data *spi_data = data; + close(spi_data->fd); + free(spi_data); return 0; } @@ -75,6 +77,7 @@ static int linux_spi_send_command(const struct flashctx *flash, unsigned int wri const unsigned char *txbuf, unsigned char *rxbuf) { + struct linux_spi_data *spi_data = flash->mst->spi.data; int iocontrol_code; struct spi_ioc_transfer msg[2] = { { @@ -87,7 +90,7 @@ static int linux_spi_send_command(const struct flashctx *flash, unsigned int wri }, }; - if (fd == -1) + if (spi_data->fd == -1) return -1; /* The implementation currently does not support requests that don't start with sending a command. */ @@ -101,7 +104,7 @@ static int linux_spi_send_command(const struct flashctx *flash, unsigned int wri else iocontrol_code = SPI_IOC_MESSAGE(2); - if (ioctl(fd, iocontrol_code, msg) == -1) { + if (ioctl(spi_data->fd, iocontrol_code, msg) == -1) { msg_cerr("%s: ioctl: %s\n", __func__, strerror(errno)); return -1; } @@ -113,27 +116,70 @@ static const struct spi_master spi_master_linux = { .max_data_read = MAX_DATA_UNSPECIFIED, /* TODO? */ .max_data_write = MAX_DATA_UNSPECIFIED, /* TODO? */ .command = linux_spi_send_command, - .multicommand = default_spi_send_multicommand, .read = linux_spi_read, .write_256 = linux_spi_write_256, - .write_aai = default_spi_write_aai, + .shutdown = linux_spi_shutdown, }; -int linux_spi_init(void) +/* Read max buffer size from sysfs, or use page size as fallback. */ +static size_t get_max_kernel_buf_size() +{ + size_t result = 0; + FILE *fp; + fp = fopen(BUF_SIZE_FROM_SYSFS, "r"); + if (!fp) { + msg_pwarn("Cannot open %s: %s.\n", BUF_SIZE_FROM_SYSFS, strerror(errno)); + goto out; + } + + char buf[10]; + if (!fgets(buf, sizeof(buf), fp)) { + if (feof(fp)) + msg_pwarn("Cannot read %s: file is empty.\n", BUF_SIZE_FROM_SYSFS); + else + msg_pwarn("Cannot read %s: %s.\n", BUF_SIZE_FROM_SYSFS, strerror(errno)); + goto out; + } + + long int tmp; + errno = 0; + tmp = strtol(buf, NULL, 0); + if ((tmp < 0) || errno) { + msg_pwarn("Buffer size %ld from %s seems wrong.\n", tmp, BUF_SIZE_FROM_SYSFS); + } else { + msg_pdbg("%s: Using value from %s as max buffer size.\n", __func__, BUF_SIZE_FROM_SYSFS); + result = (size_t)tmp; + } + +out: + if (fp) + fclose(fp); + + if (!result) { + msg_pdbg("%s: Using page size as max buffer size.\n", __func__); + result = (size_t)getpagesize(); + } + return result; +} + +static int linux_spi_init(const struct programmer_cfg *cfg) { - char *p, *endp, *dev; + char *param_str, *endp; uint32_t speed_hz = 2 * 1000 * 1000; /* FIXME: make the following configurable by CLI options. */ /* SPI mode 0 (beware this also includes: MSB first, CS active low and others */ const uint8_t mode = SPI_MODE_0; const uint8_t bits = 8; - - p = extract_programmer_param("spispeed"); - if (p && strlen(p)) { - speed_hz = (uint32_t)strtoul(p, &endp, 10) * 1000; - if (p == endp || speed_hz == 0) { - msg_perr("%s: invalid clock: %s kHz\n", __func__, p); - free(p); + int fd; + size_t max_kernel_buf_size; + struct linux_spi_data *spi_data; + + param_str = extract_programmer_param_str(cfg, "spispeed"); + if (param_str && strlen(param_str)) { + speed_hz = (uint32_t)strtoul(param_str, &endp, 10) * 1000; + if (param_str == endp || speed_hz == 0) { + msg_perr("%s: invalid clock: %s kHz\n", __func__, param_str); + free(param_str); return 1; } } else { @@ -141,87 +187,65 @@ int linux_spi_init(void) "kHz clock. Use 'spispeed' parameter to override.\n", speed_hz / 1000); } - free(p); + free(param_str); - dev = extract_programmer_param("dev"); - if (!dev || !strlen(dev)) { + param_str = extract_programmer_param_str(cfg, "dev"); + if (!param_str || !strlen(param_str)) { msg_perr("No SPI device given. Use flashrom -p " "linux_spi:dev=/dev/spidevX.Y\n"); - free(dev); + free(param_str); return 1; } - msg_pdbg("Using device %s\n", dev); - if ((fd = open(dev, O_RDWR)) == -1) { + msg_pdbg("Using device %s\n", param_str); + if ((fd = open(param_str, O_RDWR)) == -1) { msg_perr("%s: failed to open %s: %s\n", __func__, - dev, strerror(errno)); - free(dev); + param_str, strerror(errno)); + free(param_str); return 1; } - free(dev); - - if (register_shutdown(linux_spi_shutdown, NULL)) - return 1; - /* We rely on the shutdown function for cleanup from here on. */ + free(param_str); if (ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed_hz) == -1) { msg_perr("%s: failed to set speed to %"PRIu32"Hz: %s\n", __func__, speed_hz, strerror(errno)); - return 1; + goto init_err; } msg_pdbg("Using %"PRIu32"kHz clock\n", speed_hz / 1000); if (ioctl(fd, SPI_IOC_WR_MODE, &mode) == -1) { msg_perr("%s: failed to set SPI mode to 0x%02x: %s\n", __func__, mode, strerror(errno)); - return 1; + goto init_err; } if (ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits) == -1) { msg_perr("%s: failed to set the number of bits per SPI word to %u: %s\n", __func__, bits == 0 ? 8 : bits, strerror(errno)); - return 1; + goto init_err; } - /* Read max buffer size from sysfs, or use page size as fallback. */ - FILE *fp; - fp = fopen(BUF_SIZE_FROM_SYSFS, "r"); - if (!fp) { - msg_pwarn("Cannot open %s: %s.\n", BUF_SIZE_FROM_SYSFS, strerror(errno)); - goto out; - } - - char buf[10]; - if (!fgets(buf, sizeof(buf), fp)) { - if (feof(fp)) - msg_pwarn("Cannot read %s: file is empty.\n", BUF_SIZE_FROM_SYSFS); - else - msg_pwarn("Cannot read %s: %s.\n", BUF_SIZE_FROM_SYSFS, strerror(errno)); - goto out; - } + max_kernel_buf_size = get_max_kernel_buf_size(); + msg_pdbg("%s: max_kernel_buf_size: %zu\n", __func__, max_kernel_buf_size); - long int tmp; - errno = 0; - tmp = strtol(buf, NULL, 0); - if ((tmp < 0) || errno) { - msg_pwarn("Buffer size %ld from %s seems wrong.\n", tmp, BUF_SIZE_FROM_SYSFS); - } else { - msg_pdbg("%s: Using value from %s as max buffer size.\n", __func__, BUF_SIZE_FROM_SYSFS); - max_kernel_buf_size = (size_t)tmp; + spi_data = calloc(1, sizeof(*spi_data)); + if (!spi_data) { + msg_perr("Unable to allocated space for SPI master data\n"); + goto init_err; } + spi_data->fd = fd; + spi_data->max_kernel_buf_size = max_kernel_buf_size; -out: - if (fp) - fclose(fp); + return register_spi_master(&spi_master_linux, spi_data); - if (!max_kernel_buf_size) { - msg_pdbg("%s: Using page size as max buffer size.\n", __func__); - max_kernel_buf_size = (size_t)getpagesize(); - } - - msg_pdbg("%s: max_kernel_buf_size: %zu\n", __func__, max_kernel_buf_size); - register_spi_master(&spi_master_linux); - return 0; +init_err: + close(fd); + return 1; } -#endif // CONFIG_LINUX_SPI == 1 +const struct programmer_entry programmer_linux_spi = { + .name = "linux_spi", + .type = OTHER, + .devs.note = "Device files /dev/spidev*.*\n", + .init = linux_spi_init, +}; |