diff --git a/Makefile b/Makefile index 9ad9d95dd322a0a60b274f6e46027ee70e6184fb..6407e6b4ca48dc88fa3b99401d70e5c5893f4104 100644 --- a/Makefile +++ b/Makefile @@ -313,6 +313,10 @@ CONFIG_OGP_SPI ?= yes # Always enable Bus Pirate SPI for now. CONFIG_BUSPIRATE_SPI ?= yes +# Disable Linux spidev interface support for now, until we check for a Linux +# device (not host, as DOS binaries for example are built on a Linux host). +CONFIG_LINUX_SPI ?= no + # Disable Dediprog SF100 until support is complete and tested. CONFIG_DEDIPROG ?= no @@ -449,6 +453,11 @@ PROGRAMMER_OBJS += buspirate_spi.o NEED_SERIAL := yes endif +ifeq ($(CONFIG_LINUX_SPI), yes) +FEATURE_CFLAGS += -D'CONFIG_LINUX_SPI=1' +PROGRAMMER_OBJS += linux_spi.o +endif + ifeq ($(CONFIG_DEDIPROG), yes) FEATURE_CFLAGS += -D'CONFIG_DEDIPROG=1' FEATURE_LIBS += -lusb diff --git a/flashrom.c b/flashrom.c index c9d1e610c2a074874ad4767755a53eca7cefce19..977617cdf76e5fc9816bbad3e4ce12d993ce1e31 100644 --- a/flashrom.c +++ b/flashrom.c @@ -104,6 +104,9 @@ enum programmer programmer = #if CONFIG_SATAMV == 1 PROGRAMMER_SATAMV #endif +#if CONFIG_LINUX_SPI == 1 + PROGRAMMER_LINUX_SPI +#endif ; #endif @@ -451,6 +454,24 @@ const struct programmer_entry programmer_table[] = { }, #endif +#if CONFIG_LINUX_SPI == 1 + { + .name = "linux_spi", + .init = linux_spi_init, + .map_flash_region = fallback_map, + .unmap_flash_region = fallback_unmap, + .chip_readb = noop_chip_readb, + .chip_readw = fallback_chip_readw, + .chip_readl = fallback_chip_readl, + .chip_readn = fallback_chip_readn, + .chip_writeb = noop_chip_writeb, + .chip_writew = fallback_chip_writew, + .chip_writel = fallback_chip_writel, + .chip_writen = fallback_chip_writen, + .delay = internal_delay, + }, +#endif + {}, /* This entry corresponds to PROGRAMMER_INVALID. */ }; diff --git a/linux_spi.c b/linux_spi.c new file mode 100644 index 0000000000000000000000000000000000000000..fa6ec17135c145d1f027a121d909c2cb55280592 --- /dev/null +++ b/linux_spi.c @@ -0,0 +1,139 @@ +/* + * This file is part of the flashrom project. + * + * Copyright (C) 2011 Sven Schnelle <svens@stackframe.org> + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <sys/fcntl.h> +#include <errno.h> +#include <ctype.h> +#include <unistd.h> +#include <linux/spi/spidev.h> +#include <sys/ioctl.h> +#include "flash.h" +#include "chipdrivers.h" +#include "programmer.h" +#include "spi.h" + +static int fd = -1; + +static int linux_spi_shutdown(void *data); +static int linux_spi_send_command(unsigned int writecnt, unsigned int readcnt, + const unsigned char *txbuf, unsigned char *rxbuf); +static int linux_spi_read(struct flashchip *flash, uint8_t *buf, int start, + int len); +static int linux_spi_write_256(struct flashchip *flash, uint8_t *buf, + int start, int len); + +static const struct spi_programmer spi_programmer_linux = { + .type = SPI_CONTROLLER_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, +}; + +int linux_spi_init(void) +{ + char *p, *endp, *dev; + int speed = 0; + + dev = extract_programmer_param("dev"); + if (!dev || !strlen(dev)) { + msg_perr("No SPI device given. Use flashrom -p " + "linux_spi:dev=/dev/spidevX.Y\n"); + return 1; + } + + p = extract_programmer_param("speed"); + if (p && strlen(p)) { + speed = strtoul(p, &endp, 10) * 1024; + if (p == endp) { + msg_perr("%s: invalid clock: %s kHz\n", __func__, p); + return 1; + } + } + + if ((fd = open(dev, O_RDWR)) == -1) { + msg_perr("%s: failed to open %s: %s\n", __func__, + dev, strerror(errno)); + return 1; + } + + if (speed > 0 && ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed) == -1) { + msg_perr("%s: failed to set speed %dHz: %s\n", + __func__, speed, strerror(errno)); + close(fd); + return 1; + } + + if (register_shutdown(linux_spi_shutdown, NULL)) + return 1; + + register_spi_programmer(&spi_programmer_linux); + + return 0; +} + +static int linux_spi_shutdown(void *data) +{ + if (fd != -1) { + close(fd); + fd = -1; + } + return 0; +} + +static int linux_spi_send_command(unsigned int writecnt, unsigned int readcnt, + const unsigned char *txbuf, unsigned char *rxbuf) +{ + struct spi_ioc_transfer msg[2] = { + { + .tx_buf = (uint64_t)txbuf, + .len = writecnt, + }, + { + .rx_buf = (uint64_t)rxbuf, + .len = readcnt, + }, + }; + + if (fd == -1) + return -1; + + if (ioctl(fd, SPI_IOC_MESSAGE(2), msg) == -1) { + msg_cerr("%s: ioctl: %s\n", __func__, strerror(errno)); + return -1; + } + return 0; +} + +static int linux_spi_read(struct flashchip *flash, uint8_t *buf, int start, + int len) +{ + return spi_read_chunked(flash, buf, start, len, getpagesize()); +} + +static int linux_spi_write_256(struct flashchip *flash, uint8_t *buf, + int start, int len) +{ + return spi_write_chunked(flash, buf, start, len, getpagesize() - 4); +} diff --git a/programmer.h b/programmer.h index 20e0f17b473409570b97f6604b1a1728a8237dd6..f878a5333eddd1d1fb8895e919776519e841904e 100644 --- a/programmer.h +++ b/programmer.h @@ -78,6 +78,9 @@ enum programmer { #endif #if CONFIG_SATAMV == 1 PROGRAMMER_SATAMV, +#endif +#if CONFIG_LINUX_SPI == 1 + PROGRAMMER_LINUX_SPI, #endif PROGRAMMER_INVALID /* This must always be the last entry. */ }; @@ -485,6 +488,11 @@ int bitbang_spi_shutdown(const struct bitbang_spi_master *master); int buspirate_spi_init(void); #endif +/* linux_spi.c */ +#if CONFIG_LINUX_SPI == 1 +int linux_spi_init(void); +#endif + /* dediprog.c */ #if CONFIG_DEDIPROG == 1 int dediprog_init(void); @@ -536,6 +544,9 @@ enum spi_controller { #if CONFIG_OGP_SPI == 1 || CONFIG_NICINTEL_SPI == 1 || CONFIG_RAYER_SPI == 1 || (CONFIG_INTERNAL == 1 && (defined(__i386__) || defined(__x86_64__))) SPI_CONTROLLER_BITBANG, #endif +#if CONFIG_LINUX_SPI == 1 + SPI_CONTROLLER_LINUX, +#endif }; extern const int spi_programmer_count;