Author: stefanct Date: Fri Sep 16 01:38:14 2011 New Revision: 1442 URL: http://flashrom.org/trac/flashrom/changeset/1442
Log: serprog: add SPI support
Adds a new opcode (0x13) that just relays SPI bytes and wires it up to be usable within serprog.c. Checks for mandatory opcodes are moved around and changed a bit, but non-SPI programmers should not be harmed by this patch.
Signed-off-by: Urja Rannikko urjaman@gmail.com Signed-off-by: Stefan Tauner stefan.tauner@student.tuwien.ac.at Acked-by: Uwe Hermann uwe@hermann-uwe.de
Modified: trunk/programmer.h trunk/serprog-protocol.txt trunk/serprog.c
Modified: trunk/programmer.h ============================================================================== --- trunk/programmer.h Thu Sep 15 00:09:48 2011 (r1441) +++ trunk/programmer.h Fri Sep 16 01:38:14 2011 (r1442) @@ -545,6 +545,9 @@ #if CONFIG_LINUX_SPI == 1 SPI_CONTROLLER_LINUX, #endif +#if CONFIG_SERPROG == 1 + SPI_CONTROLLER_SERPROG, +#endif }; extern const int spi_programmer_count;
@@ -605,6 +608,9 @@ uint8_t serprog_chip_readb(const chipaddr addr); void serprog_chip_readn(uint8_t *buf, const chipaddr addr, size_t len); void serprog_delay(int delay); +int serprog_spi_send_command(unsigned int writecnt, unsigned int readcnt, + const unsigned char *writearr, unsigned char *readarr); +int serprog_spi_read(struct flashchip *flash, uint8_t *buf, int start, int len); #endif
/* serial.c */
Modified: trunk/serprog-protocol.txt ============================================================================== --- trunk/serprog-protocol.txt Thu Sep 15 00:09:48 2011 (r1441) +++ trunk/serprog-protocol.txt Fri Sep 16 01:38:14 2011 (r1442) @@ -31,6 +31,8 @@ 0x10 Sync NOP none NAK + ACK (for synchronization) 0x11 Query maximum read-n length none ACK + 24-bit length (0==2^24) / NAK 0x12 Set used bustype 8-bit flags (as with 0x05) ACK / NAK +0x13 Perform SPI operation 24-bit slen + 24-bit rlen ACK + rlen bytes of data / NAK + + slen bytes of data 0x?? unimplemented command - invalid.
@@ -50,7 +52,7 @@ it should return a big bogus value - eg 0xFFFF. 0x05 (Q_BUSTYPE): The bit's are defined as follows: - bit 0: PARALLEL, bit 1: LPC, bit 2: FWH, bit 3: SPI (if ever supported). + bit 0: PARALLEL, bit 1: LPC, bit 2: FWH, bit 3: SPI. 0x06 (Q_CHIPSIZE): Only applicable to parallel programmers. An LPC/FWH/SPI-programmer can report this as not supported in the command bitmap. @@ -66,6 +68,11 @@ Set's the used bustype if the programmer can support more than one flash protocol. Sending a byte with more than 1 bit set will make the programmer decide among them on it's own. Bit values as with Q_BUSTYPE. + 0x13 (O_SPIOP): + Send and receive bytes via SPI. + Maximum slen is Q_WRNMAXLEN in case Q_BUSTYPE returns SPI only or S_BUSTYPE was used + to set SPI exclusively before. Same for rlen and Q_RDNMAXLEN. + This operation is immediate, meaning it doesnt use the operation buffer. About mandatory commands: The only truly mandatory commands for any device are 0x00, 0x01, 0x02 and 0x10, but one can't really do anything with these commands. @@ -99,3 +106,4 @@ #define S_CMD_SYNCNOP 0x10 /* Special no-operation that returns NAK+ACK */ #define S_CMD_Q_RDNMAXLEN 0x11 /* Query read-n maximum length */ #define S_CMD_S_BUSTYPE 0x12 /* Set used bustype(s). */ +#define S_CMD_O_SPIOP 0x13 /* Perform SPI operation. */
Modified: trunk/serprog.c ============================================================================== --- trunk/serprog.c Thu Sep 15 00:09:48 2011 (r1441) +++ trunk/serprog.c Fri Sep 16 01:38:14 2011 (r1442) @@ -1,7 +1,7 @@ /* * This file is part of the flashrom project. * - * Copyright (C) 2009 Urja Rannikko urjaman@gmail.com + * Copyright (C) 2009, 2011 Urja Rannikko urjaman@gmail.com * Copyright (C) 2009 Carl-Daniel Hailfinger * * This program is free software; you can redistribute it and/or modify @@ -36,6 +36,7 @@ #include <termios.h> #include "flash.h" #include "programmer.h" +#include "chipdrivers.h"
#define MSGHEADER "serprog:"
@@ -67,6 +68,7 @@ #define S_CMD_SYNCNOP 0x10 /* Special no-operation that returns NAK+ACK */ #define S_CMD_Q_RDNMAXLEN 0x11 /* Query read-n maximum length */ #define S_CMD_S_BUSTYPE 0x12 /* Set used bustype(s). */ +#define S_CMD_O_SPIOP 0x13 /* Perform SPI operation. */
static uint16_t sp_device_serbuf_size = 16; static uint16_t sp_device_opbuf_size = 300; @@ -302,6 +304,16 @@ return 0; }
+static struct spi_programmer spi_programmer_serprog = { + .type = SPI_CONTROLLER_SERPROG, + .max_data_read = MAX_DATA_READ_UNLIMITED, + .max_data_write = MAX_DATA_WRITE_UNLIMITED, + .command = serprog_spi_send_command, + .multicommand = default_spi_send_multicommand, + .read = serprog_spi_read, + .write_256 = default_spi_write_256, +}; + int serprog_init(void) { uint16_t iface; @@ -325,7 +337,7 @@ msg_perr("Error: No baudrate specified.\n" "Use flashrom -p serprog:dev=/dev/device:baud\n"); free(device); - return 1; + return 1; } if (strlen(device)) { sp_fd = sp_openserport(device, atoi(baudport)); @@ -358,7 +370,7 @@ msg_perr("Error: No port specified.\n" "Use flashrom -p serprog:ip=ipaddr:port\n"); free(device); - return 1; + return 1; } if (strlen(device)) { sp_fd = sp_opensocket(device, atoi(baudport)); @@ -410,35 +422,115 @@
sp_check_avail_automatic = 1;
- /* Check for the minimum operational set of commands */ - if (sp_check_commandavail(S_CMD_R_BYTE) == 0) { - msg_perr("Error: Single byte read not supported\n"); - exit(1); - } - /* This could be translated to single byte reads (if missing), * - * but now we dont support that. */ - if (sp_check_commandavail(S_CMD_R_NBYTES) == 0) { - msg_perr("Error: Read n bytes not supported\n"); - exit(1); - } - /* In the future one could switch to read-only mode if these * - * are not available. */ - if (sp_check_commandavail(S_CMD_O_INIT) == 0) { - msg_perr("Error: Initialize operation buffer not supported\n"); - exit(1); - } - if (sp_check_commandavail(S_CMD_O_WRITEB) == 0) { - msg_perr("Error: Write to opbuf: write byte not supported\n"); - exit(1); + + if (sp_docommand(S_CMD_Q_BUSTYPE, 0, NULL, 1, &c)) { + msg_perr("Warning: NAK to query supported buses\n"); + c = BUS_NONSPI; /* A reasonable default for now. */ } - if (sp_check_commandavail(S_CMD_O_DELAY) == 0) { - msg_perr("Error: Write to opbuf: delay not supported\n"); - exit(1); + buses_supported = c; + /* Check for the minimum operational set of commands. */ + if (buses_supported & BUS_SPI) { + uint8_t bt = BUS_SPI; + if (sp_check_commandavail(S_CMD_O_SPIOP) == 0) { + msg_perr("Error: SPI operation not supported while the " + "bustype is SPI\n"); + exit(1); + } + /* Success of any of these commands is optional. We don't need + the programmer to tell us its limits, but if it doesn't, we + will assume stuff, so it's in the programmers best interest + to tell us. */ + sp_docommand(S_CMD_S_BUSTYPE, 1, &bt, 0, NULL); + if (!sp_docommand(S_CMD_Q_WRNMAXLEN, 0, NULL, 3, rbuf)) { + uint32_t v; + v = ((unsigned int)(rbuf[0]) << 0); + v |= ((unsigned int)(rbuf[1]) << 8); + v |= ((unsigned int)(rbuf[2]) << 16); + if (v == 0) + v = (1 << 24) - 1; /* SPI-op maximum. */ + spi_programmer_serprog.max_data_write = v; + msg_pdbg(MSGHEADER "Maximum write-n length is %d\n", v); + } + if (!sp_docommand(S_CMD_Q_RDNMAXLEN, 0, NULL, 3, rbuf)) { + uint32_t v; + v = ((unsigned int)(rbuf[0]) << 0); + v |= ((unsigned int)(rbuf[1]) << 8); + v |= ((unsigned int)(rbuf[2]) << 16); + if (v == 0) + v = (1 << 24) - 1; /* SPI-op maximum. */ + spi_programmer_serprog.max_data_read = v; + msg_pdbg(MSGHEADER "Maximum read-n length is %d\n", v); + } + bt = buses_supported; + sp_docommand(S_CMD_S_BUSTYPE, 1, &bt, 0, NULL); + register_spi_programmer(&spi_programmer_serprog); } - if (sp_check_commandavail(S_CMD_O_EXEC) == 0) { - msg_perr( - "Error: Execute operation buffer not supported\n"); - exit(1); + + if (buses_supported & BUS_NONSPI) { + if (sp_check_commandavail(S_CMD_O_INIT) == 0) { + msg_perr("Error: Initialize operation buffer " + "not supported\n"); + exit(1); + } + + if (sp_check_commandavail(S_CMD_O_DELAY) == 0) { + msg_perr("Error: Write to opbuf: " + "delay not supported\n"); + exit(1); + } + + /* S_CMD_O_EXEC availability checked later. */ + + if (sp_check_commandavail(S_CMD_R_BYTE) == 0) { + msg_perr("Error: Single byte read not supported\n"); + exit(1); + } + /* This could be translated to single byte reads (if missing), + * but now we don't support that. */ + if (sp_check_commandavail(S_CMD_R_NBYTES) == 0) { + msg_perr("Error: Read n bytes not supported\n"); + exit(1); + } + if (sp_check_commandavail(S_CMD_O_WRITEB) == 0) { + msg_perr("Error: Write to opbuf: " + "write byte not supported\n"); + exit(1); + } + + if (sp_docommand(S_CMD_Q_WRNMAXLEN, 0, NULL, 3, rbuf)) { + msg_pdbg(MSGHEADER "Write-n not supported"); + sp_max_write_n = 0; + } else { + sp_max_write_n = ((unsigned int)(rbuf[0]) << 0); + sp_max_write_n |= ((unsigned int)(rbuf[1]) << 8); + sp_max_write_n |= ((unsigned int)(rbuf[2]) << 16); + if (!sp_max_write_n) { + sp_max_write_n = (1 << 24); + } + msg_pdbg(MSGHEADER "Maximum write-n length is %d\n", + sp_max_write_n); + sp_write_n_buf = malloc(sp_max_write_n); + if (!sp_write_n_buf) { + msg_perr("Error: cannot allocate memory for " + "Write-n buffer\n"); + exit(1); + } + sp_write_n_bytes = 0; + } + + if (sp_check_commandavail(S_CMD_Q_RDNMAXLEN) && + (sp_docommand(S_CMD_Q_RDNMAXLEN, 0, NULL, 3, rbuf) == 0)) { + sp_max_read_n = ((unsigned int)(rbuf[0]) << 0); + sp_max_read_n |= ((unsigned int)(rbuf[1]) << 8); + sp_max_read_n |= ((unsigned int)(rbuf[2]) << 16); + msg_pdbg(MSGHEADER "Maximum read-n length is %d\n", + sp_max_read_n ? sp_max_read_n : (1 << 24)); + } else { + msg_pdbg(MSGHEADER "Maximum read-n length " + "not reported\n"); + sp_max_read_n = 0; + } + }
if (sp_docommand(S_CMD_Q_PGMNAME, 0, NULL, 16, pgmname)) { @@ -454,51 +546,27 @@ msg_pdbg(MSGHEADER "serial buffer size %d\n", sp_device_serbuf_size);
- if (sp_docommand(S_CMD_Q_OPBUF, 0, NULL, 2, &sp_device_opbuf_size)) { - msg_perr("Warning: NAK to query operation buffer size\n"); - } - msg_pdbg(MSGHEADER "operation buffer size %d\n", - sp_device_opbuf_size); - - if (sp_docommand(S_CMD_Q_BUSTYPE, 0, NULL, 1, &c)) { - msg_perr("Warning: NAK to query supported buses\n"); - c = BUS_NONSPI; /* A reasonable default for now. */ - } - buses_supported = c; - - if (sp_docommand(S_CMD_O_INIT, 0, NULL, 0, NULL)) { - msg_perr("Error: NAK to initialize operation buffer\n"); - exit(1); - } + if (sp_check_commandavail(S_CMD_O_INIT)) { + /* This would be inconsistent. */ + if (sp_check_commandavail(S_CMD_O_EXEC) == 0) { + msg_perr("Error: Execute operation buffer not " + "supported\n"); + exit(1); + }
- if (sp_docommand(S_CMD_Q_WRNMAXLEN, 0, NULL, 3, rbuf)) { - msg_pdbg(MSGHEADER "Write-n not supported"); - sp_max_write_n = 0; - } else { - sp_max_write_n = ((unsigned int)(rbuf[0]) << 0); - sp_max_write_n |= ((unsigned int)(rbuf[1]) << 8); - sp_max_write_n |= ((unsigned int)(rbuf[2]) << 16); - msg_pdbg(MSGHEADER "Maximum write-n length %d\n", - sp_max_write_n); - sp_write_n_buf = malloc(sp_max_write_n); - if (!sp_write_n_buf) { - msg_perr("Error: cannot allocate memory for Write-n buffer\n"); + if (sp_docommand(S_CMD_O_INIT, 0, NULL, 0, NULL)) { + msg_perr("Error: NAK to initialize operation buffer\n"); exit(1); } - sp_write_n_bytes = 0; - } - - if ((sp_check_commandavail(S_CMD_Q_RDNMAXLEN)) - &&((sp_docommand(S_CMD_Q_RDNMAXLEN,0,NULL, 3, rbuf) == 0))) { - sp_max_read_n = ((unsigned int)(rbuf[0]) << 0); - sp_max_read_n |= ((unsigned int)(rbuf[1]) << 8); - sp_max_read_n |= ((unsigned int)(rbuf[2]) << 16); - msg_pdbg(MSGHEADER "Maximum read-n length %d\n", - sp_max_read_n ? sp_max_read_n : (1<<24)); - } else { - msg_pdbg(MSGHEADER "Maximum read-n length not reported\n"); - sp_max_read_n = 0; - } + + if (sp_docommand(S_CMD_Q_OPBUF, 0, NULL, 2, + &sp_device_opbuf_size)) { + msg_perr("Warning: NAK to query operation buffer " + "size\n"); + } + msg_pdbg(MSGHEADER "operation buffer size %d\n", + sp_device_opbuf_size); + }
sp_prev_was_write = 0; sp_streamed_transmit_ops = 0; @@ -679,6 +747,12 @@ { unsigned char buf[4]; msg_pspew("%s\n", __func__); + if (!sp_check_commandavail(S_CMD_O_DELAY)) { + internal_delay(delay); + msg_pdbg("Note: serprog_delay used, but the programmer doesn't " + "support delay\n"); + return; + } if ((sp_max_write_n) && (sp_write_n_bytes)) sp_pass_writen(); sp_check_opbuf_usage(5); @@ -690,3 +764,48 @@ sp_opbuf_usage += 5; sp_prev_was_write = 0; } + +int serprog_spi_send_command(unsigned int writecnt, unsigned int readcnt, + const unsigned char *writearr, + unsigned char *readarr) +{ + unsigned char *parmbuf; + int ret; + msg_pspew("%s, writecnt=%i, readcnt=%i\n", __func__, writecnt, readcnt); + if ((sp_opbuf_usage) || (sp_max_write_n && sp_write_n_bytes)) + sp_execute_opbuf(); + parmbuf = malloc(writecnt + 6); + if (!parmbuf) + sp_die("Error: cannot malloc SPI send param buffer"); + parmbuf[0] = (writecnt >> 0) & 0xFF; + parmbuf[1] = (writecnt >> 8) & 0xFF; + parmbuf[2] = (writecnt >> 16) & 0xFF; + parmbuf[3] = (readcnt >> 0) & 0xFF; + parmbuf[4] = (readcnt >> 8) & 0xFF; + parmbuf[5] = (readcnt >> 16) & 0xFF; + memcpy(parmbuf + 6, writearr, writecnt); + ret = sp_docommand(S_CMD_O_SPIOP, writecnt + 6, parmbuf, readcnt, + readarr); + free(parmbuf); + return ret; +} + +/* FIXME: This function is optimized so that it does not split each transaction + * into chip page_size long blocks unnecessarily like spi_read_chunked. This has + * the advantage that it is much faster for most chips, but breaks those with + * non-contiguous address space (like AT45DB161D). When spi_read_chunked is + * fixed this method can be removed. */ +int serprog_spi_read(struct flashchip *flash, uint8_t *buf, int start, int len) +{ + int i; + int cur_len; + const int max_read = spi_programmer_serprog.max_data_read; + for (i = 0; i < len; i += cur_len) { + int ret; + cur_len = min(max_read, (len - i)); + ret = spi_nbyte_read(start + i, buf + i, cur_len); + if (ret) + return ret; + } + return 0; +}