/* * This file is part of the flashrom project. * * Copyright (C) 2011 Sven Schnelle * * 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 #include #include #include #include #include #include #include #include #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(struct flashctx *flash, unsigned int writecnt, unsigned int readcnt, const unsigned char *txbuf, unsigned char *rxbuf); static int linux_spi_read(struct flashctx *flash, uint8_t *buf, unsigned int start, unsigned int len); static int linux_spi_write_256(struct flashctx *flash, uint8_t *buf, unsigned int start, unsigned 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; uint32_t 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 = (uint32_t)strtoul(p, &endp, 10) * 1024; if (p == endp) { msg_perr("%s: invalid clock: %s kHz\n", __func__, p); return 1; } } msg_pdbg("Using device %s\n", dev); 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) { if (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; } msg_pdbg("Using %d kHz clock\n", speed); } 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(struct flashctx *flash, unsigned int writecnt, unsigned int readcnt, const unsigned char *txbuf, unsigned char *rxbuf) { struct spi_ioc_transfer msg[2] = { { .tx_buf = (uint64_t)(ptrdiff_t)txbuf, .len = writecnt, }, { .rx_buf = (uint64_t)(ptrdiff_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 flashctx *flash, uint8_t *buf, unsigned int start, unsigned int len) { return spi_read_chunked(flash, buf, start, len, (unsigned int)getpagesize()); } static int linux_spi_write_256(struct flashctx *flash, uint8_t *buf, unsigned int start, unsigned int len) { return spi_write_chunked(flash, buf, start, len, ((unsigned int)getpagesize()) - 4); }