summaryrefslogtreecommitdiffstats
path: root/serprog.c
diff options
context:
space:
mode:
Diffstat (limited to 'serprog.c')
-rw-r--r--serprog.c255
1 files changed, 148 insertions, 107 deletions
diff --git a/serprog.c b/serprog.c
index d583a40d4..b51f0cfe3 100644
--- a/serprog.c
+++ b/serprog.c
@@ -15,8 +15,7 @@
* GNU General Public License for more details.
*/
-#include "platform.h"
-
+#include <stdbool.h>
#include <stdio.h>
#if ! IS_WINDOWS /* stuff (presumably) needed for sockets only */
#include <stdlib.h>
@@ -38,7 +37,32 @@
#include "flash.h"
#include "programmer.h"
#include "chipdrivers.h"
-#include "serprog.h"
+
+/* According to Serial Flasher Protocol Specification - version 1 */
+#define S_ACK 0x06
+#define S_NAK 0x15
+#define S_CMD_NOP 0x00 /* No operation */
+#define S_CMD_Q_IFACE 0x01 /* Query interface version */
+#define S_CMD_Q_CMDMAP 0x02 /* Query supported commands bitmap */
+#define S_CMD_Q_PGMNAME 0x03 /* Query programmer name */
+#define S_CMD_Q_SERBUF 0x04 /* Query Serial Buffer Size */
+#define S_CMD_Q_BUSTYPE 0x05 /* Query supported bustypes */
+#define S_CMD_Q_CHIPSIZE 0x06 /* Query supported chipsize (2^n format) */
+#define S_CMD_Q_OPBUF 0x07 /* Query operation buffer size */
+#define S_CMD_Q_WRNMAXLEN 0x08 /* Query Write to opbuf: Write-N maximum length */
+#define S_CMD_R_BYTE 0x09 /* Read a single byte */
+#define S_CMD_R_NBYTES 0x0A /* Read n bytes */
+#define S_CMD_O_INIT 0x0B /* Initialize operation buffer */
+#define S_CMD_O_WRITEB 0x0C /* Write opbuf: Write byte with address */
+#define S_CMD_O_WRITEN 0x0D /* Write to opbuf: Write-N */
+#define S_CMD_O_DELAY 0x0E /* Write opbuf: udelay */
+#define S_CMD_O_EXEC 0x0F /* Execute operation buffer */
+#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. */
+#define S_CMD_S_SPI_FREQ 0x14 /* Set SPI clock frequency */
+#define S_CMD_S_PIN_STATE 0x15 /* Enable/disable output drivers */
#define MSGHEADER "serprog: "
@@ -133,7 +157,7 @@ static int sp_synchronize(void)
goto err_out;
}
/* A second should be enough to get all the answers to the buffer */
- internal_delay(1000 * 1000);
+ default_delay(1000 * 1000);
sp_flush_incoming();
/* Then try up to 8 times to send syncnop and get the correct special *
@@ -268,7 +292,8 @@ static int sp_stream_buffer_op(uint8_t cmd, uint32_t parmlen, uint8_t *parms)
return 1;
}
sp[0] = cmd;
- memcpy(&(sp[1]), parms, parmlen);
+ if (parms)
+ memcpy(&(sp[1]), parms, parmlen);
if (sp_streamed_transmit_bytes >= (1 + parmlen + sp_device_serbuf_size)) {
if (sp_flush_stream() != 0) {
@@ -394,15 +419,51 @@ static int serprog_spi_send_command(const struct flashctx *flash,
return ret;
}
+static int serprog_shutdown(void *data)
+{
+ if ((sp_opbuf_usage) || (sp_max_write_n && sp_write_n_bytes))
+ if (sp_execute_opbuf() != 0)
+ msg_pwarn("Could not flush command buffer.\n");
+ if (sp_check_commandavail(S_CMD_S_PIN_STATE)) {
+ uint8_t dis = 0;
+ if (sp_docommand(S_CMD_S_PIN_STATE, 1, &dis, 0, NULL) == 0)
+ msg_pdbg(MSGHEADER "Output drivers disabled\n");
+ else
+ msg_pwarn(MSGHEADER "%s: Warning: could not disable output buffers\n", __func__);
+ }
+ /* FIXME: fix sockets on windows(?), especially closing */
+ serialport_shutdown(&sp_fd);
+ if (sp_max_write_n)
+ free(sp_write_n_buf);
+ return 0;
+}
+
+static void *serprog_map(const char *descr, uintptr_t phys_addr, size_t len)
+{
+ /* Serprog transmits 24 bits only and assumes the underlying implementation handles any remaining bits
+ * correctly (usually setting them to one either in software (for FWH/LPC) or relying on the fact that
+ * the hardware observes a subset of the address bits only). Combined with the standard mapping of
+ * flashrom this creates a 16 MB-wide window just below the 4 GB boundary where serprog can operate (as
+ * needed for non-SPI chips). Below we make sure that the requested range is within this window. */
+ if ((phys_addr & 0xFF000000) == 0xFF000000) {
+ return (void*)phys_addr;
+ }
+ msg_pwarn(MSGHEADER "requested mapping %s is incompatible: 0x%zx bytes at 0x%0*" PRIxPTR ".\n",
+ descr, len, PRIxPTR_WIDTH, phys_addr);
+ return NULL;
+}
+
+static void serprog_delay(const struct flashctx *flash, unsigned int usecs);
+
static struct spi_master spi_master_serprog = {
+ .map_flash_region = serprog_map,
.features = SPI_MASTER_4BA,
.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 = default_spi_read,
.write_256 = default_spi_write_256,
- .write_aai = default_spi_write_aai,
+ .delay = serprog_delay,
};
static int sp_check_opbuf_usage(int bytes_to_be_added)
@@ -507,49 +568,48 @@ static void serprog_chip_readn(const struct flashctx *flash, uint8_t * buf,
sp_do_read_n(&(buf[addrm-addr]), addrm, lenm); // FIXME: return error
}
-static const struct par_master par_master_serprog = {
- .chip_readb = serprog_chip_readb,
- .chip_readw = fallback_chip_readw,
- .chip_readl = fallback_chip_readl,
- .chip_readn = serprog_chip_readn,
- .chip_writeb = serprog_chip_writeb,
- .chip_writew = fallback_chip_writew,
- .chip_writel = fallback_chip_writel,
- .chip_writen = fallback_chip_writen,
-};
-
-static int serprog_shutdown(void *data)
+static void serprog_delay(const struct flashctx *flash, unsigned int usecs)
{
- if ((sp_opbuf_usage) || (sp_max_write_n && sp_write_n_bytes))
- if (sp_execute_opbuf() != 0)
- msg_pwarn("Could not flush command buffer.\n");
- if (sp_check_commandavail(S_CMD_S_PIN_STATE)) {
- uint8_t dis = 0;
- if (sp_docommand(S_CMD_S_PIN_STATE, 1, &dis, 0, NULL) == 0)
- msg_pdbg(MSGHEADER "Output drivers disabled\n");
- else
- msg_pwarn(MSGHEADER "%s: Warning: could not disable output buffers\n", __func__);
+ unsigned char buf[4];
+ msg_pspew("%s usecs=%d\n", __func__, usecs);
+ if (!sp_check_commandavail(S_CMD_O_DELAY)) {
+ msg_pdbg2("serprog_delay used, but programmer doesn't support delays natively - emulating\n");
+ default_delay(usecs);
+ return;
}
- /* FIXME: fix sockets on windows(?), especially closing */
- serialport_shutdown(&sp_fd);
- if (sp_max_write_n)
- free(sp_write_n_buf);
- return 0;
+ if ((sp_max_write_n) && (sp_write_n_bytes))
+ sp_pass_writen();
+ sp_check_opbuf_usage(5);
+ buf[0] = ((usecs >> 0) & 0xFF);
+ buf[1] = ((usecs >> 8) & 0xFF);
+ buf[2] = ((usecs >> 16) & 0xFF);
+ buf[3] = ((usecs >> 24) & 0xFF);
+ sp_stream_buffer_op(S_CMD_O_DELAY, 4, buf);
+ sp_opbuf_usage += 5;
+ sp_prev_was_write = 0;
}
+static const struct par_master par_master_serprog = {
+ .map_flash_region = serprog_map,
+ .chip_readb = serprog_chip_readb,
+ .chip_readn = serprog_chip_readn,
+ .chip_writeb = serprog_chip_writeb,
+ .delay = serprog_delay,
+};
+
static enum chipbustype serprog_buses_supported = BUS_NONE;
-int serprog_init(void)
+static int serprog_init(const struct programmer_cfg *cfg)
{
uint16_t iface;
unsigned char pgmname[17];
unsigned char rbuf[3];
unsigned char c;
char *device;
- int have_device = 0;
+ bool have_device = false;
/* the parameter is either of format "dev=/dev/device[:baud]" or "ip=ip:port" */
- device = extract_programmer_param("dev");
+ device = extract_programmer_param_str(cfg, "dev");
if (device && strlen(device)) {
char *baud_str = strstr(device, ":");
if (baud_str != NULL) {
@@ -564,15 +624,16 @@ int serprog_init(void)
if (baud_str == NULL || *baud_str == '\0') {
baud = -1;
msg_pdbg("No baudrate specified, using the hardware's defaults.\n");
- } else
+ } else {
baud = atoi(baud_str); // FIXME: replace atoi with strtoul
+ }
if (strlen(device) > 0) {
sp_fd = sp_openserport(device, baud);
if (sp_fd == SER_INV_FD) {
free(device);
return 1;
}
- have_device++;
+ have_device = true;
}
}
@@ -585,7 +646,7 @@ int serprog_init(void)
}
free(device);
- device = extract_programmer_param("ip");
+ device = extract_programmer_param_str(cfg, "ip");
if (have_device && device) {
msg_perr("Error: Both host and device specified.\n"
"Please use either dev= or ip= but not both.\n");
@@ -611,7 +672,7 @@ int serprog_init(void)
free(device);
return 1;
}
- have_device++;
+ have_device = true;
}
}
if (device && !strlen(device)) {
@@ -635,33 +696,30 @@ int serprog_init(void)
return 1;
}
- if (register_shutdown(serprog_shutdown, NULL))
- return 1;
-
msg_pdbg(MSGHEADER "connected - attempting to synchronize\n");
sp_check_avail_automatic = 0;
if (sp_synchronize())
- return 1;
+ goto init_err_cleanup_exit;
msg_pdbg(MSGHEADER "Synchronized\n");
if (sp_docommand(S_CMD_Q_IFACE, 0, NULL, 2, &iface)) {
msg_perr("Error: NAK to query interface version\n");
- return 1;
+ goto init_err_cleanup_exit;
}
if (iface != 1) {
msg_perr("Error: Unknown interface version: %d\n", iface);
- return 1;
+ goto init_err_cleanup_exit;
}
msg_pdbg(MSGHEADER "Interface version ok.\n");
if (sp_docommand(S_CMD_Q_CMDMAP, 0, NULL, 32, sp_cmdmap)) {
msg_perr("Error: query command map not supported\n");
- return 1;
+ goto init_err_cleanup_exit;
}
sp_check_avail_automatic = 1;
@@ -688,10 +746,10 @@ int serprog_init(void)
if (sp_check_commandavail(S_CMD_O_SPIOP) == 0) {
msg_perr("Error: SPI operation not supported while the "
"bustype is SPI\n");
- return 1;
+ goto init_err_cleanup_exit;
}
if (sp_docommand(S_CMD_S_BUSTYPE, 1, &bt, 0, NULL))
- return 1;
+ goto init_err_cleanup_exit;
/* 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
@@ -716,7 +774,7 @@ int serprog_init(void)
spi_master_serprog.max_data_read = v;
msg_pdbg(MSGHEADER "Maximum read-n length is %d\n", v);
}
- spispeed = extract_programmer_param("spispeed");
+ spispeed = extract_programmer_param_str(cfg, "spispeed");
if (spispeed && strlen(spispeed)) {
uint32_t f_spi_req, f_spi;
uint8_t buf[4];
@@ -727,22 +785,22 @@ int serprog_init(void)
if (errno != 0 || spispeed == f_spi_suffix) {
msg_perr("Error: Could not convert 'spispeed'.\n");
free(spispeed);
- return 1;
+ goto init_err_cleanup_exit;
}
if (strlen(f_spi_suffix) == 1) {
- if (!strcasecmp(f_spi_suffix, "M"))
+ if (!strcasecmp(f_spi_suffix, "M")) {
f_spi_req *= 1000000;
- else if (!strcasecmp(f_spi_suffix, "k"))
+ } else if (!strcasecmp(f_spi_suffix, "k")) {
f_spi_req *= 1000;
- else {
+ } else {
msg_perr("Error: Garbage following 'spispeed' value.\n");
free(spispeed);
- return 1;
+ goto init_err_cleanup_exit;
}
} else if (strlen(f_spi_suffix) > 1) {
msg_perr("Error: Garbage following 'spispeed' value.\n");
free(spispeed);
- return 1;
+ goto init_err_cleanup_exit;
}
buf[0] = (f_spi_req >> (0 * 8)) & 0xFF;
@@ -750,53 +808,54 @@ int serprog_init(void)
buf[2] = (f_spi_req >> (2 * 8)) & 0xFF;
buf[3] = (f_spi_req >> (3 * 8)) & 0xFF;
- if (sp_check_commandavail(S_CMD_S_SPI_FREQ) == 0)
+ if (sp_check_commandavail(S_CMD_S_SPI_FREQ) == 0) {
msg_pwarn(MSGHEADER "Warning: Setting the SPI clock rate is not supported!\n");
- else if (sp_docommand(S_CMD_S_SPI_FREQ, 4, buf, 4, buf) == 0) {
+ } else if (sp_docommand(S_CMD_S_SPI_FREQ, 4, buf, 4, buf) == 0) {
f_spi = buf[0];
f_spi |= buf[1] << (1 * 8);
f_spi |= buf[2] << (2 * 8);
f_spi |= buf[3] << (3 * 8);
msg_pdbg(MSGHEADER "Requested to set SPI clock frequency to %u Hz. "
"It was actually set to %u Hz\n", f_spi_req, f_spi);
- } else
+ } else {
msg_pwarn(MSGHEADER "Setting SPI clock rate to %u Hz failed!\n", f_spi_req);
+ }
}
free(spispeed);
bt = serprog_buses_supported;
if (sp_docommand(S_CMD_S_BUSTYPE, 1, &bt, 0, NULL))
- return 1;
+ goto init_err_cleanup_exit;
}
if (serprog_buses_supported & BUS_NONSPI) {
if (sp_check_commandavail(S_CMD_O_INIT) == 0) {
msg_perr("Error: Initialize operation buffer "
"not supported\n");
- return 1;
+ goto init_err_cleanup_exit;
}
if (sp_check_commandavail(S_CMD_O_DELAY) == 0) {
msg_perr("Error: Write to opbuf: "
"delay not supported\n");
- return 1;
+ goto init_err_cleanup_exit;
}
/* 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");
- return 1;
+ goto init_err_cleanup_exit;
}
/* 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");
- return 1;
+ goto init_err_cleanup_exit;
}
if (sp_check_commandavail(S_CMD_O_WRITEB) == 0) {
msg_perr("Error: Write to opbuf: "
"write byte not supported\n");
- return 1;
+ goto init_err_cleanup_exit;
}
if (sp_docommand(S_CMD_Q_WRNMAXLEN, 0, NULL, 3, rbuf)) {
@@ -815,7 +874,7 @@ int serprog_init(void)
if (!sp_write_n_buf) {
msg_perr("Error: cannot allocate memory for "
"Write-n buffer\n");
- return 1;
+ goto init_err_cleanup_exit;
}
sp_write_n_bytes = 0;
}
@@ -853,12 +912,12 @@ int serprog_init(void)
if (sp_check_commandavail(S_CMD_O_EXEC) == 0) {
msg_perr("Error: Execute operation buffer not "
"supported\n");
- return 1;
+ goto init_err_cleanup_exit;
}
if (sp_docommand(S_CMD_O_INIT, 0, NULL, 0, NULL)) {
msg_perr("Error: NAK to initialize operation buffer\n");
- return 1;
+ goto init_err_cleanup_exit;
}
if (sp_docommand(S_CMD_Q_OPBUF, 0, NULL, 2,
@@ -873,54 +932,36 @@ int serprog_init(void)
uint8_t en = 1;
if (sp_docommand(S_CMD_S_PIN_STATE, 1, &en, 0, NULL) != 0) {
msg_perr("Error: could not enable output buffers\n");
- return 1;
- } else
+ goto init_err_cleanup_exit;
+ } else {
msg_pdbg(MSGHEADER "Output drivers enabled\n");
- } else
+ }
+ } else {
msg_pdbg(MSGHEADER "Warning: Programmer does not support toggling its output drivers\n");
+ }
+
sp_prev_was_write = 0;
sp_streamed_transmit_ops = 0;
sp_streamed_transmit_bytes = 0;
sp_opbuf_usage = 0;
+
+ if (register_shutdown(serprog_shutdown, NULL))
+ goto init_err_cleanup_exit;
if (serprog_buses_supported & BUS_SPI)
- register_spi_master(&spi_master_serprog);
+ register_spi_master(&spi_master_serprog, NULL);
if (serprog_buses_supported & BUS_NONSPI)
- register_par_master(&par_master_serprog, serprog_buses_supported & BUS_NONSPI);
+ register_par_master(&par_master_serprog, serprog_buses_supported & BUS_NONSPI, NULL);
return 0;
-}
-void serprog_delay(unsigned int usecs)
-{
- unsigned char buf[4];
- msg_pspew("%s usecs=%d\n", __func__, usecs);
- if (!sp_check_commandavail(S_CMD_O_DELAY)) {
- msg_pdbg2("serprog_delay used, but programmer doesn't support delays natively - emulating\n");
- internal_delay(usecs);
- return;
- }
- if ((sp_max_write_n) && (sp_write_n_bytes))
- sp_pass_writen();
- sp_check_opbuf_usage(5);
- buf[0] = ((usecs >> 0) & 0xFF);
- buf[1] = ((usecs >> 8) & 0xFF);
- buf[2] = ((usecs >> 16) & 0xFF);
- buf[3] = ((usecs >> 24) & 0xFF);
- sp_stream_buffer_op(S_CMD_O_DELAY, 4, buf);
- sp_opbuf_usage += 5;
- sp_prev_was_write = 0;
+init_err_cleanup_exit:
+ serprog_shutdown(NULL);
+ return 1;
}
-void *serprog_map(const char *descr, uintptr_t phys_addr, size_t len)
-{
- /* Serprog transmits 24 bits only and assumes the underlying implementation handles any remaining bits
- * correctly (usually setting them to one either in software (for FWH/LPC) or relying on the fact that
- * the hardware observes a subset of the address bits only). Combined with the standard mapping of
- * flashrom this creates a 16 MB-wide window just below the 4 GB boundary where serprog can operate (as
- * needed for non-SPI chips). Below we make sure that the requested range is within this window. */
- if ((phys_addr & 0xFF000000) == 0xFF000000) {
- return (void*)phys_addr;
- }
- msg_pwarn(MSGHEADER "requested mapping %s is incompatible: 0x%zx bytes at 0x%0*" PRIxPTR ".\n",
- descr, len, PRIxPTR_WIDTH, phys_addr);
- return NULL;
-}
+const struct programmer_entry programmer_serprog = {
+ .name = "serprog",
+ .type = OTHER,
+ /* FIXME */
+ .devs.note = "All programmer devices speaking the serprog protocol\n",
+ .init = serprog_init,
+};