From bbd8daec122cb985c8b1f7de49afd04d7bb9c603 Mon Sep 17 00:00:00 2001 From: Vincent Palatin Date: Thu, 16 Mar 2017 18:06:39 +0100 Subject: stm32mon: add support for SPI flashing mode Allow to flash an STM32 in bootloader mode through its SPI slave interface. Signed-off-by: Vincent Palatin BRANCH=none BUG=b:36125319 TEST=run from Eve AP: export SPIDEV="/dev/spidev32765.0" export GPIO_NRST=418 export GPIO_BOOT0=419 echo ${GPIO_BOOT0} > /sys/class/gpio/export echo "out" > /sys/class/gpio/gpio${GPIO_BOOT0}/direction echo ${GPIO_NRST} > /sys/class/gpio/export echo "out" > /sys/class/gpio/gpio${GPIO_NRST}/direction echo 1 > /sys/class/gpio/gpio${GPIO_BOOT0}/value echo 0 > /sys/class/gpio/gpio${GPIO_NRST}/value echo 1 > /sys/class/gpio/gpio${GPIO_NRST}/value stm32mon -s ${SPIDEV} -r /tmp/mcu-image.bin echo 0 > /sys/class/gpio/gpio${GPIO_BOOT0}/value echo 0 > /sys/class/gpio/gpio${GPIO_NRST}/value echo 1 > /sys/class/gpio/gpio${GPIO_NRST}/value echo "in" > /sys/class/gpio/gpio${GPIO_BOOT0}/direction echo "in" > /sys/class/gpio/gpio${GPIO_NRST}/direction re-verify UART flashing mode with 'flash_ec --board=eve_fp' Change-Id: Ic268dd9e62a2f279dd7992a4bbcf16fcf44c5f9e Reviewed-on: https://chromium-review.googlesource.com/456596 Commit-Ready: Vincent Palatin Tested-by: Vincent Palatin Reviewed-by: Todd Broch --- util/stm32mon.c | 115 ++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 99 insertions(+), 16 deletions(-) diff --git a/util/stm32mon.c b/util/stm32mon.c index aa395534ab..2ff127b358 100644 --- a/util/stm32mon.c +++ b/util/stm32mon.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -50,11 +51,17 @@ #define RESP_NACK 0x1f #define RESP_ACK 0x79 +/* SPI Start of Frame */ +#define SOF 0x5A + /* Extended erase special parameters */ #define ERASE_ALL 0xffff #define ERASE_BANK1 0xfffe #define ERASE_BANK2 0xfffd +/* Upper bound of fully erasing the flash and rebooting the monitor */ +#define MAX_DELAY_MASS_ERASE_REBOOT 100000 /* us */ + /* known STM32 SoC parameters */ struct stm32_def { uint16_t id; @@ -83,9 +90,16 @@ struct stm32_def { #define PAGE_SIZE 256 #define INVALID_I2C_ADAPTER -1 +enum interface_mode { + MODE_SERIAL, + MODE_I2C, + MODE_SPI, +} mode = MODE_SERIAL; + /* store custom parameters */ speed_t baudrate = DEFAULT_BAUDRATE; int i2c_adapter = INVALID_I2C_ADAPTER; +const char *spi_adapter; const char *serial_port = "/dev/ttyUSB1"; const char *input_filename; const char *output_filename; @@ -202,14 +216,43 @@ int open_i2c(const int port) return fd; } +int open_spi(const char *port) +{ + int fd; + int res; + uint32_t mode = SPI_MODE_0; + uint8_t bits = 8; + + fd = open(port, O_RDWR); + if (fd == -1) { + perror("Unable to open SPI controller"); + return -1; + } + + res = ioctl(fd, SPI_IOC_WR_MODE32, &mode); + if (res == -1) { + perror("Cannot set SPI mode"); + close(fd); + return -1; + } + + res = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits); + if (res == -1) { + perror("Cannot set SPI bits per word"); + close(fd); + return -1; + } + + return fd; +} static void discard_input(int fd) { uint8_t buffer[64]; int res, i; - /* Skip in i2c mode */ - if (i2c_adapter != INVALID_I2C_ADAPTER) + /* Skip in i2c and spi modes */ + if (mode != MODE_SERIAL) return; /* eat trailing garbage */ @@ -229,6 +272,7 @@ int wait_for_ack(int fd) uint8_t resp; int res; time_t deadline = time(NULL) + DEFAULT_TIMEOUT; + uint8_t ack = RESP_ACK; while (time(NULL) < deadline) { res = read(fd, &resp, 1); @@ -237,14 +281,22 @@ int wait_for_ack(int fd) return -EIO; } if (res == 1) { - if (resp == RESP_ACK) + if (resp == RESP_ACK) { + if (mode == MODE_SPI) /* Ack the ACK */ + if (write(fd, &ack, 1) != 1) + return -EIO; return 0; - else if (resp == RESP_NACK) { + } else if (resp == RESP_NACK) { fprintf(stderr, "NACK\n"); + if (mode == MODE_SPI) /* Ack the NACK */ + if (write(fd, &ack, 1) != 1) + return -EIO; discard_input(fd); return -EINVAL; } else { - fprintf(stderr, "Receive junk: %02x\n", resp); + if (mode == MODE_SERIAL) + fprintf(stderr, "Receive junk: %02x\n", + resp); } } } @@ -258,10 +310,12 @@ int send_command(int fd, uint8_t cmd, payload_t *loads, int cnt, int res, i, c; payload_t *p; int readcnt = 0; - uint8_t cmd_frame[] = { cmd, 0xff ^ cmd }; /* XOR checksum */ + uint8_t cmd_frame[] = { SOF, cmd, 0xff ^ cmd }; /* XOR checksum */ + /* only the SPI mode needs the Start Of Frame byte */ + int cmd_off = mode == MODE_SPI ? 0 : 1; /* Send the command index */ - res = write(fd, cmd_frame, 2); + res = write(fd, cmd_frame + cmd_off, sizeof(cmd_frame) - cmd_off); if (res <= 0) { perror("Failed to write command frame"); return -1; @@ -315,6 +369,9 @@ int send_command(int fd, uint8_t cmd, payload_t *loads, int cnt, /* Read the answer payload */ if (resp) { + if (mode == MODE_SPI) /* ignore dummy byte */ + if (read(fd, resp, 1) < 0) + return -1; while ((resp_size > 0) && (res = read(fd, resp, resp_size))) { if (res < 0) { perror("Failed to read payload"); @@ -368,10 +425,10 @@ struct stm32_def *command_get_id(int fd) int init_monitor(int fd) { int res; - uint8_t init = CMD_INIT; + uint8_t init = mode == MODE_SPI ? SOF : CMD_INIT; /* Skip in i2c mode */ - if (i2c_adapter != INVALID_I2C_ADAPTER) + if (mode == MODE_I2C) return 0; printf("Waiting for the monitor startup ..."); @@ -435,7 +492,7 @@ int command_get_commands(int fd, struct stm32_def *chip) printf("%02x ", cmds[i]); } - if (i2c_adapter != INVALID_I2C_ADAPTER) + if (mode == MODE_I2C) erase = command_erase_i2c; printf("\n"); @@ -632,7 +689,13 @@ int command_read_unprotect(int fd) } printf("Flash read unprotected.\n"); - /* This commands triggers a reset */ + /* + * This command triggers a reset. + * + * Wait at least the 'mass-erase' delay, else we could reconnect + * before the actual reset depending on the bootloader. + */ + usleep(MAX_DELAY_MASS_ERASE_REBOOT); if (init_monitor(fd) < 0) { fprintf(stderr, "Cannot recover after RP reset\n"); return -EIO; @@ -656,7 +719,13 @@ int command_write_unprotect(int fd) } printf("Flash write unprotected.\n"); - /* This commands triggers a reset */ + /* + * This command triggers a reset. + * + * Wait at least the 'mass-erase' delay, else we could reconnect + * before the actual reset depending on the bootloader. + */ + usleep(MAX_DELAY_MASS_ERASE_REBOOT); if (init_monitor(fd) < 0) { fprintf(stderr, "Cannot recover after WP reset\n"); return -EIO; @@ -779,6 +848,7 @@ static const struct option longopts[] = { {"unprotect", 0, 0, 'u'}, {"baudrate", 1, 0, 'b'}, {"adapter", 1, 0, 'a'}, + {"spi", 1, 0, 's'}, {NULL, 0, 0, 0} }; @@ -799,6 +869,7 @@ void display_usage(char *program) fprintf(stderr, "--e[rase] : erase all the flash content\n"); fprintf(stderr, "--r[ead] : read the flash content and " "write it into \n"); + fprintf(stderr, "--s[pi] : use SPI adapter on .\n"); fprintf(stderr, "--w[rite] : read or\n\t" "standard input and write it to flash\n"); fprintf(stderr, "--g[o] : jump to execute flash entrypoint\n"); @@ -833,17 +904,19 @@ int parse_parameters(int argc, char **argv) int opt, idx; int flags = 0; - while ((opt = getopt_long(argc, argv, "a:b:d:eghr:w:uU?", + while ((opt = getopt_long(argc, argv, "a:b:d:eghr:s:w:uU?", longopts, &idx)) != -1) { switch (opt) { case 'a': i2c_adapter = atoi(optarg); + mode = MODE_I2C; break; case 'b': baudrate = parse_baudrate(optarg); break; case 'd': serial_port = optarg; + mode = MODE_SERIAL; break; case 'e': flags |= FLAG_ERASE; @@ -858,6 +931,10 @@ int parse_parameters(int argc, char **argv) case 'r': input_filename = optarg; break; + case 's': + spi_adapter = optarg; + mode = MODE_SPI; + break; case 'w': output_filename = optarg; break; @@ -882,11 +959,17 @@ int main(int argc, char **argv) /* Parse command line options */ flags = parse_parameters(argc, argv); - if (i2c_adapter == INVALID_I2C_ADAPTER) { + switch (mode) { + case MODE_SPI: + ser = open_spi(spi_adapter); + break; + case MODE_I2C: + ser = open_i2c(i2c_adapter); + break; + case MODE_SERIAL: + default: /* Open the serial port tty */ ser = open_serial(serial_port); - } else { - ser = open_i2c(i2c_adapter); } if (ser < 0) return 1; -- cgit v1.2.1