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.
Corresponding to flashrom svn r1442.
Signed-off-by: Urja Rannikko <urjaman@gmail.com>
Signed-off-by: Stefan Tauner <stefan.tauner@alumni.tuwien.ac.at>
Acked-by: Uwe Hermann <uwe@hermann-uwe.de>
diff --git a/programmer.h b/programmer.h
index 1283909..73b94c1 100644
--- a/programmer.h
+++ b/programmer.h
@@ -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 */
diff --git a/serprog-protocol.txt b/serprog-protocol.txt
index 52f901a..168a28e 100644
--- a/serprog-protocol.txt
+++ b/serprog-protocol.txt
@@ -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. */
diff --git a/serprog.c b/serprog.c
index 3adb958..9d554c9 100644
--- a/serprog.c
+++ b/serprog.c
@@ -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);
+
+ 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. */
}
- /* 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);
+ 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);
}
- /* 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_check_commandavail(S_CMD_O_DELAY) == 0) {
- msg_perr("Error: Write to opbuf: delay not supported\n");
- exit(1);
- }
- 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_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_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);
}
- 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_O_INIT, 0, NULL, 0, NULL)) {
+ msg_perr("Error: NAK to initialize operation buffer\n");
+ exit(1);
+ }
+
+ 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;
+}