Miklós Márton has uploaded this change for review. ( https://review.coreboot.org/c/flashrom/+/34661 )
Change subject: Add support for STLINK V3 debugger/programmer via it's SPI bridge ......................................................................
Add support for STLINK V3 debugger/programmer via it's SPI bridge
Change-Id: Icffab87ac8f2c570187ed753ec70f054541873a4 Signed-off-by: Miklós Márton martonmiklosqdev@gmail.com --- M Makefile M flashrom.8.tmpl M flashrom.c M meson.build M programmer.h A stlinkv3_spi.c 6 files changed, 670 insertions(+), 0 deletions(-)
git pull ssh://review.coreboot.org:29418/flashrom refs/changes/61/34661/1
diff --git a/Makefile b/Makefile index 545e84a..58dc86a 100644 --- a/Makefile +++ b/Makefile @@ -190,6 +190,11 @@ else override CONFIG_CH341A_SPI = no endif +ifeq ($(CONFIG_STLINKV3_SPI), yes) +UNSUPPORTED_FEATURES += CONFIG_STLINKV3_SPI=yes +else +override CONFIG_STLINKV3_SPI = no +endif # libjaylink is also not available for DOS ifeq ($(CONFIG_JLINK_SPI), yes) UNSUPPORTED_FEATURES += CONFIG_JLINK_SPI=yes @@ -353,6 +358,11 @@ else override CONFIG_PICKIT2_SPI = no endif +ifeq ($(CONFIG_STLINKV3_SPI), yes) +UNSUPPORTED_FEATURES += CONFIG_STLINKV3_SPI=yes +else +override CONFIG_STLINKV3_SPI = no +endif ifeq ($(CONFIG_CH341A_SPI), yes) UNSUPPORTED_FEATURES += CONFIG_CH341A_SPI=yes else @@ -618,6 +628,9 @@ # Always enable PICkit2 SPI dongles for now. CONFIG_PICKIT2_SPI ?= yes
+# Always enable STLink V3 +CONFIG_STLINKV3_SPI ?= yes + # Always enable dummy tracing for now. CONFIG_DUMMY ?= yes
@@ -696,6 +709,7 @@ override CONFIG_DIGILENT_SPI = no override CONFIG_DEVELOPERBOX_SPI = no override CONFIG_PICKIT2_SPI = no +override CONFIG_STLINKV3_SPI = no endif ifeq ($(CONFIG_ENABLE_LIBPCI_PROGRAMMERS), no) override CONFIG_INTERNAL = no @@ -863,6 +877,12 @@ NEED_LIBUSB1 += CONFIG_PICKIT2_SPI endif
+ifeq ($(CONFIG_STLINKV3_SPI), yes) +FEATURE_CFLAGS += -D'CONFIG_STLINKV3_SPI=1' +PROGRAMMER_OBJS += stlinkv3_spi.o +NEED_LIBUSB1 += CONFIG_STLINKV3_SPI +endif + ifneq ($(NEED_LIBFTDI), ) FTDILIBS := $(call debug_shell,[ -n "$(PKG_CONFIG_LIBDIR)" ] && export PKG_CONFIG_LIBDIR="$(PKG_CONFIG_LIBDIR)" ; $(PKG_CONFIG) --libs libftdi1 || $(PKG_CONFIG) --libs libftdi || printf "%s" "-lftdi -lusb") FEATURE_CFLAGS += $(call debug_shell,grep -q "FT232H := yes" .features && printf "%s" "-D'HAVE_FT232H=1'") diff --git a/flashrom.8.tmpl b/flashrom.8.tmpl index 30dc97d..5f518a4 100644 --- a/flashrom.8.tmpl +++ b/flashrom.8.tmpl @@ -333,6 +333,8 @@ .sp .BR "* jlink_spi" " (for SPI flash ROMs attached to SEGGER J-Link and compatible devices)" .sp +.BR "* stlinkv3_spi" " (for SPI flash ROMs attached to STMicroelectronics STLINK V3 devices)" +.sp Some programmers have optional or mandatory parameters which are described in detail in the .B PROGRAMMER-SPECIFIC INFORMATION @@ -1186,6 +1188,31 @@ .sp syntax where \fBfrequency\fP is the SPI clock frequency in kHz. The maximum speed depends on the device in use. +SS +.BR "stlinkv3_spi " programmer +.IP +This module supports SPI flash programming through the STMicroelectronics +STLINK V3 programmer/debugger's SPI bridge interface +.sp +.B " flashrom -p stlinkv3_spi" +.sp +If there is more than one compatible device connected, you can select which one +should be used by specifying its serial number with the +.sp +.B " flashrom -p stlinkv3_spi:serial=number" +.sp +syntax where +.B number +is the serial number of the device (which can be found for example in the +output of lsusb -v). +.sp +The SPI speed can be selected by using the +.sp +.B " flashrom -p stlinkv3_spi:spispeed=frequency" +.sp +syntax where \fBfrequency\fP is the SPI clock frequency in kHz. +If the passed frequency is not supported by the adapter the nearest lower +supported frequency will be used. .SS
.SH EXAMPLES diff --git a/flashrom.c b/flashrom.c index cb1dca6..fdb77b6 100644 --- a/flashrom.c +++ b/flashrom.c @@ -449,6 +449,18 @@ }, #endif
+#if CONFIG_STLINKV3_SPI == 1 + { + .name = "stlinkv3_spi", + .type = USB, + .devs.dev = devs_stlinkv3_spi, + .init = stlinkv3_spi_init, + .map_flash_region = fallback_map, + .unmap_flash_region = fallback_unmap, + .delay = internal_delay, + }, +#endif + {0}, /* This entry corresponds to PROGRAMMER_INVALID. */ };
diff --git a/meson.build b/meson.build index e1b6c16..2b5ef2e 100644 --- a/meson.build +++ b/meson.build @@ -62,6 +62,7 @@ config_satasii = get_option('config_satasii') config_serprog = get_option('config_serprog') config_usbblaster_spi = get_option('config_usbblaster_spi') +config_stlinkv3_spi = get_option('config_stlinkv3_spi')
cargs = [] deps = [] @@ -272,6 +273,10 @@ srcs += 'usbblaster_spi.c' cargs += '-DCONFIG_USBBLASTER_SPI=1' endif +if config_stlinkv3_spi + srcs += 'stlinkv3_spi.c' + cargs += '-DCONFIG_STLINKV3_SPI=1' +endif
# bitbanging SPI infrastructure if config_bitbang_spi diff --git a/programmer.h b/programmer.h index f4e8b46..1843426 100644 --- a/programmer.h +++ b/programmer.h @@ -121,6 +121,9 @@ #if CONFIG_JLINK_SPI == 1 PROGRAMMER_JLINK_SPI, #endif +#if CONFIG_STLINKV3_SPI == 1 + PROGRAMMER_STLINKV3_SPI, +#endif PROGRAMMER_INVALID /* This must always be the last entry. */ };
@@ -498,6 +501,12 @@ extern const struct dev_entry devs_pickit2_spi[]; #endif
+/* stlinkv3_spi.c */ +#if CONFIG_STLINKV3_SPI == 1 +int stlinkv3_spi_init(void); +extern const struct dev_entry devs_stlinkv3_spi[]; +#endif + /* rayer_spi.c */ #if CONFIG_RAYER_SPI == 1 int rayer_spi_init(void); diff --git a/stlinkv3_spi.c b/stlinkv3_spi.c new file mode 100644 index 0000000..817d28f --- /dev/null +++ b/stlinkv3_spi.c @@ -0,0 +1,597 @@ +/* + * This file is part of the flashrom project. + * + * Copyright (C) 2019 Miklós Márton martonmiklosqdev@gmail.com + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + */ + +/* + * Driver for programming SPI flash chips using the SPI port + * of the STMicroelectronics's STLINK-V3 programmer/debugger. + * + * The implementation is inspired by the ST's STLINK-V3-BRIDGE C++ API: + * https://www.st.com/en/development-tools/stlink-v3-bridge.html + */ + +#include "flash.h" +#include "programmer.h" +#include "spi.h" + +#include <libusb.h> +#include <limits.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +typedef enum { + FW_VERSION_OK, + FW_VERSION_OLD, +} fw_version_check_result_t; + +typedef enum { + SPI_BAUDRATEPRESCALER_2 = 0, + SPI_BAUDRATEPRESCALER_4 = 1, + SPI_BAUDRATEPRESCALER_8 = 2, + SPI_BAUDRATEPRESCALER_16 = 3, + SPI_BAUDRATEPRESCALER_32 = 4, + SPI_BAUDRATEPRESCALER_64 = 5, + SPI_BAUDRATEPRESCALER_128 = 6, + SPI_BAUDRATEPRESCALER_256 = 7 +} SPI_prescaler_t; + +typedef enum { + SPI_DIRECTION_2LINES_FULLDUPLEX = 0, + SPI_DIRECTION_2LINES_RXONLY = 1, + SPI_DIRECTION_1LINE_RX = 2, + SPI_DIRECTION_1LINE_TX = 3 +} SPI_dir_t; + +typedef enum { + SPI_MODE_SLAVE = 0, + SPI_MODE_MASTER = 1 +} SPI_mode_t; + +typedef enum { + SPI_DATASIZE_16B = 0, + SPI_DATASIZE_8B = 1 +} SPI_datasize_t; + +typedef enum { + SPI_CPOL_LOW = 0, + SPI_CPOL_HIGH = 1 +} SPI_CPOL_t; + +typedef enum { + SPI_CPHA_1EDGE = 0, + SPI_CPHA_2EDGE = 1 +} SPI_CPHA_t; + +typedef enum { + SPI_FIRSTBIT_LSB = 0, + SPI_FIRSTBIT_MSB = 1 +} SPI_firstbit_t; + +typedef enum { + SPI_NSS_SOFT = 0, + SPI_NSS_HARD = 1 +} SPI_NSS_t; + +typedef enum { + SPI_NSS_LOW = 0, + SPI_NSS_HIGH = 1 +} SPI_NSS_Level_t; + +#define ST_GETVERSION_EXT 0xFB + +#define STLINK_BRIDGE_COMMAND 0xFC +#define STLINK_BRIDGE_CLOSE 0x01 +#define STLINK_BRIDGE_GET_RWCMD_STATUS 0x02 +#define STLINK_BRIDGE_GET_CLOCK 0x03 +#define STLINK_BRIDGE_INIT_SPI 0x20 +#define STLINK_BRIDGE_WRITE_SPI 0x21 +#define STLINK_BRIDGE_READ_SPI 0x22 +#define STLINK_BRIDGE_CS_SPI 0x23 + +#define STLINK_BRIDGE_SPI_ERROR 0x02 + +#define STLINK_SPI_COM 0x02 + +#define STLINK_EP_OUT 0x06 +#define STLINK_EP_IN 0x86 + +#define FIRMWARE_BRIDGE_STLINK_V3_LAST_VERSION 3 + +#define USB_TIMEOUT 5000 + +const struct dev_entry devs_stlinkv3_spi[] = { + {0x0483, 0x374F, OK, "STMicroelectronics", "STLINK-V3"}, + {0} +}; + +static struct libusb_context *usb_ctx; +static libusb_device_handle *stlinkv3_handle; + +/** + * @param[out] bridge_input_clk Current input frequency in kHz of the given com. + */ +static int stlinkv3_get_clk(uint32_t *bridge_input_clk) +{ + uint8_t command[16]; + uint8_t answer[12]; + int actualLength = 0; + int rc = 0; + + if (bridge_input_clk == NULL) + return -1; + + memset(command, 0, sizeof(command)); + memset(answer, 0, sizeof(answer)); + + command[0] = STLINK_BRIDGE_COMMAND; + command[1] = STLINK_BRIDGE_GET_CLOCK; + command[2] = STLINK_SPI_COM; + + rc = libusb_bulk_transfer(stlinkv3_handle, STLINK_EP_OUT, + command, sizeof(command), + &actualLength, USB_TIMEOUT); + if (rc != LIBUSB_TRANSFER_COMPLETED || actualLength != sizeof(command)) { + msg_perr("Failed to issue the STLINK_BRIDGE_GET_CLOCK command: '%s'\n", + libusb_error_name(rc)); + return -1; + } + + rc = libusb_bulk_transfer(stlinkv3_handle, STLINK_EP_IN, + answer, sizeof(answer), + &actualLength, USB_TIMEOUT); + if (rc != LIBUSB_TRANSFER_COMPLETED || actualLength != sizeof(answer)) { + msg_perr("Failed to get STLINK_BRIDGE_GET_CLOCK answer: '%s'\n", + libusb_error_name(rc)); + return -1; + } + + *bridge_input_clk = (uint32_t)answer[4] + | (uint32_t)answer[5]<<8 + | (uint32_t)answer[6]<<16 + | (uint32_t)answer[7]<<24; + return 0; + +} + +static int stlinkv3_spi_calc_prescaler(uint16_t reqd_freq_in_kHz, + SPI_prescaler_t *prescaler, + uint16_t *calculated_freq_in_kHz) +{ + uint32_t bridge_clk_in_kHz; + uint32_t calculated_prescaler = 1; + uint16_t prescaler_value; + + if (stlinkv3_get_clk(&bridge_clk_in_kHz)) + return -1; + + calculated_prescaler = bridge_clk_in_kHz/reqd_freq_in_kHz; + // Apply a smaller frequency if not exact + if (calculated_prescaler <= 2) { + *prescaler = SPI_BAUDRATEPRESCALER_2; + prescaler_value = 2; + } else if (calculated_prescaler <= 4) { + *prescaler = SPI_BAUDRATEPRESCALER_4; + prescaler_value = 4; + } else if (calculated_prescaler <= 8) { + *prescaler = SPI_BAUDRATEPRESCALER_8; + prescaler_value = 8; + } else if (calculated_prescaler <= 16) { + *prescaler = SPI_BAUDRATEPRESCALER_16; + prescaler_value = 16; + } else if (calculated_prescaler <= 32) { + *prescaler = SPI_BAUDRATEPRESCALER_32; + prescaler_value = 32; + } else if (calculated_prescaler <= 64) { + *prescaler = SPI_BAUDRATEPRESCALER_64; + prescaler_value = 64; + } else if (calculated_prescaler <= 128) { + *prescaler = SPI_BAUDRATEPRESCALER_128; + prescaler_value = 128; + } else if (calculated_prescaler <= 256) { + *prescaler = SPI_BAUDRATEPRESCALER_256; + prescaler_value = 256; + } else { + // smaller frequency not possible + *prescaler = SPI_BAUDRATEPRESCALER_256; + prescaler_value = 256; + } + + *calculated_freq_in_kHz = bridge_clk_in_kHz / prescaler_value; + + return 0; +} + +static int stlinkv3_check_version(fw_version_check_result_t *result) +{ + uint8_t answer[12]; + uint8_t command[16]; + int actualLength = 0; + int rc = 0; + + memset(command, 0, sizeof(command)); + + command[0] = ST_GETVERSION_EXT; + command[1] = 0x80; + + rc = libusb_bulk_transfer(stlinkv3_handle, STLINK_EP_OUT, + command, sizeof(command), + &actualLength, USB_TIMEOUT); + if (rc != LIBUSB_TRANSFER_COMPLETED || actualLength != sizeof(command)) { + msg_perr("Failed to issue the ST_GETVERSION_EXT command: '%s'\n", + libusb_error_name(rc)); + return -1; + } + + rc = libusb_bulk_transfer(stlinkv3_handle, STLINK_EP_IN, + answer, sizeof(answer), + &actualLength, USB_TIMEOUT); + if (rc != LIBUSB_TRANSFER_COMPLETED || actualLength != sizeof(answer)) { + msg_perr("Failed to retrive the ST_GETVERSION_EXT answer: '%s'\n", + libusb_error_name(rc)); + return -1; + } + + msg_pinfo("Connected to STLink V3 with bridge FW version: %d\n", answer[4]); + *result = answer[4] >= FIRMWARE_BRIDGE_STLINK_V3_LAST_VERSION + ? FW_VERSION_OK + : FW_VERSION_OLD; + return 0; +} + +static int stlinkv3_spi_open(uint16_t reqested_freq_in_kHz) +{ + uint8_t command[16]; + uint8_t answer[2]; + uint16_t SCK_freq_in_kHz; + int actualLength = 0; + int rc = 0; + SPI_prescaler_t prescaler; + fw_version_check_result_t fw_check_result; + + if (stlinkv3_check_version(&fw_check_result)) { + msg_perr("Failed to query FW version"); + return -1; + } + + if (fw_check_result != FW_VERSION_OK) { + msg_pinfo("Your STLink V3 has too old version of the bridge interface\n" + "Please update the firmware with the " + "STSW-LINK007 which can be downloaded from here:\n" + "https://www.st.com/en/development-tools/stsw-link007.html"); + return -1; + } + + if (stlinkv3_spi_calc_prescaler(reqested_freq_in_kHz, + &prescaler, + &SCK_freq_in_kHz)) { + msg_perr("Failed to calculate SPI clock prescaler"); + return -1; + } + msg_pinfo("SCK frequency set to %d kHz\n", SCK_freq_in_kHz); + + memset(command, 0, sizeof(command)); + memset(answer, 0, sizeof(answer)); + + command[0] = STLINK_BRIDGE_COMMAND; + command[1] = STLINK_BRIDGE_INIT_SPI; + command[2] = SPI_DIRECTION_2LINES_FULLDUPLEX; + command[3] = (SPI_MODE_MASTER + | (SPI_CPHA_1EDGE << 1) + | (SPI_CPOL_LOW << 2) + | (SPI_FIRSTBIT_MSB << 3)); + command[4] = SPI_DATASIZE_8B; + command[5] = SPI_NSS_SOFT; + command[6] = (uint8_t)prescaler; + + rc = libusb_bulk_transfer(stlinkv3_handle, STLINK_EP_OUT, + command, sizeof(command), + &actualLength, USB_TIMEOUT); + if (rc != LIBUSB_TRANSFER_COMPLETED && actualLength != sizeof(command)) { + msg_perr("Failed to issue the STLINK_BRIDGE_INIT_SPI command: '%s'\n", + libusb_error_name(rc)); + return -1; + } + + rc = libusb_bulk_transfer(stlinkv3_handle, STLINK_EP_IN, + answer, sizeof(answer), + &actualLength, USB_TIMEOUT); + if (rc != LIBUSB_TRANSFER_COMPLETED || actualLength != sizeof(answer)) { + msg_perr("Failed to retrive the STLINK_BRIDGE_INIT_SPI answer: '%s'\n", + libusb_error_name(rc)); + return -1; + } + return 0; +} + +static int stlinkv3_get_last_readwrite_status(uint32_t *status) +{ + uint8_t command[16]; + uint16_t answer[4]; + int rc = 0; + int actualLength = 0; + + memset(command, 0, sizeof(command)); + memset(answer, 0, sizeof(answer)); + + command[0] = STLINK_BRIDGE_COMMAND; + command[1] = STLINK_BRIDGE_GET_RWCMD_STATUS; + + rc = libusb_bulk_transfer(stlinkv3_handle, STLINK_EP_OUT, + command, sizeof(command), + &actualLength, USB_TIMEOUT); + if (rc != LIBUSB_TRANSFER_COMPLETED || actualLength != sizeof(command)) { + msg_perr("Failed to issue the STLINK_BRIDGE_GET_RWCMD_STATUS command: '%s'\n", + libusb_error_name(rc)); + return -1; + } + + rc = libusb_bulk_transfer(stlinkv3_handle, + STLINK_EP_IN, + (uint8_t *)answer, + sizeof(answer), + &actualLength, + USB_TIMEOUT); + if (rc != LIBUSB_TRANSFER_COMPLETED || actualLength != sizeof(answer)) { + msg_perr("Failed to retrive the STLINK_BRIDGE_GET_RWCMD_STATUS answer: '%s'\n", + libusb_error_name(rc)); + return -1; + } + + *status = (uint32_t)answer[2] | (uint32_t)answer[3]<<16; + return 0; +} + +static int stlinkv3_spi_set_SPI_NSS(SPI_NSS_Level_t NSS_level) +{ + uint8_t command[16]; + uint8_t answer[2]; + int rc = 0; + int actualLength = 0; + + memset(command, 0, sizeof(command)); + memset(answer, 0, sizeof(answer)); + + command[0] = STLINK_BRIDGE_COMMAND; + command[1] = STLINK_BRIDGE_CS_SPI; + command[2] = (uint8_t) (NSS_level); + + rc = libusb_bulk_transfer(stlinkv3_handle, STLINK_EP_OUT, + command, sizeof(command), + &actualLength, USB_TIMEOUT); + if (rc != LIBUSB_TRANSFER_COMPLETED || actualLength != sizeof(command)) { + msg_perr("Failed to issue the STLINK_BRIDGE_CS_SPI command: '%s'\n", + libusb_error_name(rc)); + return -1; + } + + rc = libusb_bulk_transfer(stlinkv3_handle, + STLINK_EP_IN, + (uint8_t *)answer, + sizeof(answer), + &actualLength, + USB_TIMEOUT); + if (rc != LIBUSB_TRANSFER_COMPLETED || actualLength != sizeof(answer)) { + msg_perr("Failed to retrive the STLINK_BRIDGE_CS_SPI answer: '%s'\n", + libusb_error_name(rc)); + return -1; + } + return 0; +} + +static int stlinkv3_spi_transmit(struct flashctx *flash, + unsigned int write_cnt, + unsigned int read_cnt, + const unsigned char *write_arr, + unsigned char *read_arr) +{ + uint8_t command[16]; + int rc = 0 ; + int actualLength = 0; + uint32_t rw_status = 0; + + if (stlinkv3_spi_set_SPI_NSS(SPI_NSS_LOW)) { + msg_perr("Failed to set the NSS pin to low\n"); + return -1; + } + + memset(command, 0, sizeof(command)); + + command[0] = STLINK_BRIDGE_COMMAND; + command[1] = STLINK_BRIDGE_WRITE_SPI; + command[2] = (uint8_t)write_cnt; + command[3] = (uint8_t)(write_cnt >> 8); + + for (unsigned int i = 0; (i < 8) && (i < write_cnt); i++) + command[4+i] = write_arr[i]; + + rc = libusb_bulk_transfer(stlinkv3_handle, STLINK_EP_OUT, + command, sizeof(command), + &actualLength, USB_TIMEOUT); + if (rc != LIBUSB_TRANSFER_COMPLETED || actualLength != sizeof(command)) { + msg_perr("Failed to issue the STLINK_BRIDGE_WRITE_SPI command: '%s'\n", + libusb_error_name(rc)); + goto transmit_err; + } + + if (write_cnt > 8) { + rc = libusb_bulk_transfer(stlinkv3_handle, + STLINK_EP_OUT, + (unsigned char *)&write_arr[8], + (unsigned int)(write_cnt - 8), + &actualLength, + USB_TIMEOUT); + if (rc != LIBUSB_TRANSFER_COMPLETED || actualLength != (write_cnt - 8)) { + msg_perr("Failed to send the data after the " + "STLINK_BRIDGE_WRITE_SPI command: '%s'\n", + libusb_error_name(rc)); + goto transmit_err; + } + } + + if (stlinkv3_get_last_readwrite_status(&rw_status)) + return -1; + + if (rw_status != 0) { + msg_perr("SPI read/write failure: %d\n", rw_status); + goto transmit_err; + } + + if (read_cnt) { + command[1] = STLINK_BRIDGE_READ_SPI; + command[2] = (uint8_t)read_cnt; + command[3] = (uint8_t)(read_cnt >> 8); + + rc = libusb_bulk_transfer(stlinkv3_handle, STLINK_EP_OUT, + command, sizeof(command), + &actualLength, USB_TIMEOUT); + if (rc != LIBUSB_TRANSFER_COMPLETED || actualLength != sizeof(command)) { + msg_perr("Failed to issue the STLINK_BRIDGE_READ_SPI command: '%s'\n", + libusb_error_name(rc)); + goto transmit_err; + } + + rc = libusb_bulk_transfer(stlinkv3_handle, + STLINK_EP_IN, + (unsigned char *)read_arr, + (int)read_cnt, + &actualLength, + USB_TIMEOUT); + if (rc != LIBUSB_TRANSFER_COMPLETED || actualLength != read_cnt) { + msg_perr("Failed to retrive the STLINK_BRIDGE_READ_SPI answer: '%s'\n", + libusb_error_name(rc)); + goto transmit_err; + } + } + + if (stlinkv3_get_last_readwrite_status(&rw_status)) + goto transmit_err; + + if (rw_status != 0) { + msg_perr("SPI read/write failure: %d\n", rw_status); + goto transmit_err; + } + + if (stlinkv3_spi_set_SPI_NSS(SPI_NSS_HIGH)) { + msg_perr("Failed to set the NSS pin to high\n"); + return -1; + } + return 0; + +transmit_err: + if (stlinkv3_spi_set_SPI_NSS(SPI_NSS_HIGH)) + msg_perr("Failed to set the NSS pin to high\n"); + return -1; +} + +static int stlinkv3_spi_shutdown(void *data) +{ + uint8_t command[16]; + uint8_t answer[2]; + int actualLength = 0; + int rc = 0; + + memset(command, 0, sizeof(command)); + memset(answer, 0, sizeof(answer)); + + command[0] = STLINK_BRIDGE_COMMAND; + command[1] = STLINK_BRIDGE_CLOSE; + command[2] = STLINK_SPI_COM; + + rc = libusb_bulk_transfer(stlinkv3_handle, STLINK_EP_OUT, + command, sizeof(command), &actualLength, USB_TIMEOUT); + if (rc != LIBUSB_TRANSFER_COMPLETED || actualLength != sizeof(command)) + msg_perr("Failed to issue the STLINK_BRIDGE_CLOSE command: '%s'\n", + libusb_error_name(rc)); + + rc = libusb_bulk_transfer(stlinkv3_handle, STLINK_EP_IN, + answer, sizeof(answer), &actualLength, USB_TIMEOUT); + if (rc != LIBUSB_TRANSFER_COMPLETED || actualLength != sizeof(answer)) + msg_perr("Failed to retrive the STLINK_BRIDGE_CLOSE answer: '%s'\n", + libusb_error_name(rc)); + + libusb_close(stlinkv3_handle); + libusb_exit(usb_ctx); + + return 0; +} + +static const struct spi_master spi_programmer_stlinkv3 = { + .max_data_read = UINT16_MAX, + .max_data_write = UINT16_MAX, + .command = stlinkv3_spi_transmit, + .multicommand = default_spi_send_multicommand, + .read = default_spi_read, + .write_256 = default_spi_write_256, + .write_aai = default_spi_write_aai, +}; + +int stlinkv3_spi_init(void) +{ + uint16_t sck_freq_kHz = 1000; // selecting 1 MHz SCK is a good bet + char *speed_str = NULL; + char *serialno = NULL; + char *endptr = NULL; + + libusb_init(&usb_ctx); + if (!usb_ctx) { + msg_perr("Could not initialize libusb!\n"); + return 1; + } + + serialno = extract_programmer_param("serial"); + if (serialno) + msg_pdbg("Opening STLINK-V3 with serial: %s\n", serialno); + stlinkv3_handle = usb_dev_get_by_vid_pid_serial(usb_ctx, + devs_stlinkv3_spi[0].vendor_id, devs_stlinkv3_spi[0].device_id, serialno); + + if (!stlinkv3_handle) { + if (serialno) + msg_perr("No STLINK-V3 seems to be connected with serial %s\n", serialno); + else + msg_perr("Could not find any connected STLINK-V3\n"); + free(serialno); + goto err_exit; + } + free(serialno); + + speed_str = extract_programmer_param("spispeed"); + if (speed_str) { + sck_freq_kHz = strtoul(speed_str, &endptr, 0); + if (*endptr) { + msg_perr("The spispeed parameter passed with invalid format: %s\n", + speed_str); + msg_perr("Please pass the parameter with a simple number in kHz\n"); + return -1; + } + free(speed_str); + } + + if (stlinkv3_spi_open(sck_freq_kHz)) + goto err_exit; + + if (register_shutdown(stlinkv3_spi_shutdown, NULL)) + goto err_exit; + + if (register_spi_master(&spi_programmer_stlinkv3)) + goto err_exit; + + return 0; + +err_exit: + libusb_exit(usb_ctx); + return 1; +}
Nico Huber has posted comments on this change. ( https://review.coreboot.org/c/flashrom/+/34661 )
Change subject: Add support for STLINK V3 debugger/programmer via it's SPI bridge ......................................................................
Patch Set 2:
(4 comments)
Looks good overall, but I'd want to have a closer look after the style issues are fixed (tabs should be 8 characters in width).
https://review.coreboot.org/c/flashrom/+/34661/2/programmer.h File programmer.h:
https://review.coreboot.org/c/flashrom/+/34661/2/programmer.h@125 PS2, Line 125: Use tab for indent, please.
https://review.coreboot.org/c/flashrom/+/34661/2/stlinkv3_spi.c File stlinkv3_spi.c:
https://review.coreboot.org/c/flashrom/+/34661/2/stlinkv3_spi.c@39 PS2, Line 39: } fw_version_check_result_t; No typedefs, please. It would hide the fact that this is an `enum` all over the code.
(And someone once told me that `_t` is reserved for standard C types.
https://review.coreboot.org/c/flashrom/+/34661/2/stlinkv3_spi.c@50 PS2, Line 50: SPI Just personal taste: I understand that SPI is an acronym and hence the urge to write it in all caps, but it doesn't seem to make it easier to read (nor to type).
https://review.coreboot.org/c/flashrom/+/34661/2/stlinkv3_spi.c@131 PS2, Line 131: actualLength `actual_length` (no CammelCase, please)
Hello build bot (Jenkins),
I'd like you to reexamine a change. Please visit
https://review.coreboot.org/c/flashrom/+/34661
to look at the new patch set (#3).
Change subject: Add support for STLINK V3 debugger/programmer via it's SPI bridge ......................................................................
Add support for STLINK V3 debugger/programmer via it's SPI bridge
Change-Id: Icffab87ac8f2c570187ed753ec70f054541873a4 Signed-off-by: Miklós Márton martonmiklosqdev@gmail.com --- M Makefile M flashrom.8.tmpl M flashrom.c M meson.build M programmer.h A stlinkv3_spi.c 6 files changed, 670 insertions(+), 0 deletions(-)
git pull ssh://review.coreboot.org:29418/flashrom refs/changes/61/34661/3
Miklós Márton has posted comments on this change. ( https://review.coreboot.org/c/flashrom/+/34661 )
Change subject: Add support for STLINK V3 debugger/programmer via it's SPI bridge ......................................................................
Patch Set 2:
(4 comments)
Thank you for your review, and I apologise for the styling mistakes.
https://review.coreboot.org/c/flashrom/+/34661/2/programmer.h File programmer.h:
https://review.coreboot.org/c/flashrom/+/34661/2/programmer.h@125 PS2, Line 125:
Use tab for indent, please.
Done
https://review.coreboot.org/c/flashrom/+/34661/2/stlinkv3_spi.c File stlinkv3_spi.c:
https://review.coreboot.org/c/flashrom/+/34661/2/stlinkv3_spi.c@39 PS2, Line 39: } fw_version_check_result_t;
No typedefs, please. It would hide the fact that this is an `enum` […]
Done
https://review.coreboot.org/c/flashrom/+/34661/2/stlinkv3_spi.c@50 PS2, Line 50: SPI
Just personal taste: I understand that SPI is an acronym and hence the […]
Done
https://review.coreboot.org/c/flashrom/+/34661/2/stlinkv3_spi.c@131 PS2, Line 131: actualLength
`actual_length` (no CammelCase, please)
Done
Paul Menzel has posted comments on this change. ( https://review.coreboot.org/c/flashrom/+/34661 )
Change subject: Add support for STLINK V3 debugger/programmer via it's SPI bridge ......................................................................
Patch Set 3:
(1 comment)
https://review.coreboot.org/c/flashrom/+/34661/3//COMMIT_MSG Commit Message:
https://review.coreboot.org/c/flashrom/+/34661/3//COMMIT_MSG@7 PS3, Line 7: it's its
Hello build bot (Jenkins),
I'd like you to reexamine a change. Please visit
https://review.coreboot.org/c/flashrom/+/34661
to look at the new patch set (#4).
Change subject: Add support for STLINK V3 debugger/programmer via its SPI bridge ......................................................................
Add support for STLINK V3 debugger/programmer via its SPI bridge
Change-Id: Icffab87ac8f2c570187ed753ec70f054541873a4 Signed-off-by: Miklós Márton martonmiklosqdev@gmail.com --- M Makefile M flashrom.8.tmpl M flashrom.c M meson.build M programmer.h A stlinkv3_spi.c 6 files changed, 670 insertions(+), 0 deletions(-)
git pull ssh://review.coreboot.org:29418/flashrom refs/changes/61/34661/4
Miklós Márton has posted comments on this change. ( https://review.coreboot.org/c/flashrom/+/34661 )
Change subject: Add support for STLINK V3 debugger/programmer via its SPI bridge ......................................................................
Patch Set 4:
(1 comment)
https://review.coreboot.org/c/flashrom/+/34661/3//COMMIT_MSG Commit Message:
https://review.coreboot.org/c/flashrom/+/34661/3//COMMIT_MSG@7 PS3, Line 7: it's
its
Done
Nico Huber has posted comments on this change. ( https://review.coreboot.org/c/flashrom/+/34661 )
Change subject: Add support for STLINK V3 debugger/programmer via its SPI bridge ......................................................................
Patch Set 4: Code-Review+1
(5 comments)
Looks quite good. Only blocker is the missing . in the manpage. The other comments are mostly for my understanding.
AFAICS, the code in stlinkv3_spi.c could be much condensed by a simple function that handles the command/answer pattern, e.g.
int stlinkv3_command(const uint8_t *command, size_t command_length, uint8_t *answer, size_t answer_length);
https://review.coreboot.org/c/flashrom/+/34661/4/flashrom.8.tmpl File flashrom.8.tmpl:
https://review.coreboot.org/c/flashrom/+/34661/4/flashrom.8.tmpl@1191 PS4, Line 1191: SS .SS
https://review.coreboot.org/c/flashrom/+/34661/4/stlinkv3_spi.c File stlinkv3_spi.c:
https://review.coreboot.org/c/flashrom/+/34661/4/stlinkv3_spi.c@112 PS4, Line 112: LAST What does `last` refer to? the last known version? the last digit of the version?
https://review.coreboot.org/c/flashrom/+/34661/4/stlinkv3_spi.c@114 PS4, Line 114: #define USB_TIMEOUT 5000 Can you give the unit in the name? e.g. USB_TIMEOUT_MS, if it's ms, I don't know what it is.
https://review.coreboot.org/c/flashrom/+/34661/4/stlinkv3_spi.c@138 PS4, Line 138: memset(answer, 0, sizeof(answer)); This is not necessary, is it? I only noticed, because you didn't clear it in stlinkv3_check_version() below.
https://review.coreboot.org/c/flashrom/+/34661/4/stlinkv3_spi.c@170 PS4, Line 170: reqd what is `reqd`?
Hello build bot (Jenkins), Nico Huber,
I'd like you to reexamine a change. Please visit
https://review.coreboot.org/c/flashrom/+/34661
to look at the new patch set (#5).
Change subject: Add support for STLINK V3 debugger/programmer via its SPI bridge ......................................................................
Add support for STLINK V3 debugger/programmer via its SPI bridge
Change-Id: Icffab87ac8f2c570187ed753ec70f054541873a4 Signed-off-by: Miklós Márton martonmiklosqdev@gmail.com --- M Makefile M flashrom.8.tmpl M flashrom.c M meson.build M programmer.h A stlinkv3_spi.c 6 files changed, 665 insertions(+), 0 deletions(-)
git pull ssh://review.coreboot.org:29418/flashrom refs/changes/61/34661/5
Hello build bot (Jenkins), Nico Huber,
I'd like you to reexamine a change. Please visit
https://review.coreboot.org/c/flashrom/+/34661
to look at the new patch set (#6).
Change subject: Add support for STLINK V3 debugger/programmer via its SPI bridge ......................................................................
Add support for STLINK V3 debugger/programmer via its SPI bridge
Change-Id: Icffab87ac8f2c570187ed753ec70f054541873a4 Signed-off-by: Miklós Márton martonmiklosqdev@gmail.com --- M Makefile M flashrom.8.tmpl M flashrom.c M meson.build M programmer.h A stlinkv3_spi.c 6 files changed, 587 insertions(+), 0 deletions(-)
git pull ssh://review.coreboot.org:29418/flashrom refs/changes/61/34661/6
Miklós Márton has posted comments on this change. ( https://review.coreboot.org/c/flashrom/+/34661 )
Change subject: Add support for STLINK V3 debugger/programmer via its SPI bridge ......................................................................
Patch Set 5:
(5 comments)
Many thanks for the review! I have fixed your review comments, but have not tested with actual hardware yet. I will try to test it tomorrow and let you know the results.
https://review.coreboot.org/c/flashrom/+/34661/4/flashrom.8.tmpl File flashrom.8.tmpl:
https://review.coreboot.org/c/flashrom/+/34661/4/flashrom.8.tmpl@1191 PS4, Line 1191: SS
.SS
Done
https://review.coreboot.org/c/flashrom/+/34661/4/stlinkv3_spi.c File stlinkv3_spi.c:
https://review.coreboot.org/c/flashrom/+/34661/4/stlinkv3_spi.c@112 PS4, Line 112: LAST
What does `last` refer to? the last known version? the last digit of the version?
ST versions the STLink's firmware parts separately (JTAG/Bridge/SWIM/Serial). This constant is used for checking the bridge firmware version to make sure that it is compatible with the current host implementation. I have renamed this constant to FIRST_COMPATIBLE_BRIDGE_FW_VERSION which is more descriptive I think.
https://review.coreboot.org/c/flashrom/+/34661/4/stlinkv3_spi.c@114 PS4, Line 114: #define USB_TIMEOUT 5000
Can you give the unit in the name? e.g. USB_TIMEOUT_MS, if it's ms, […]
Sure, it is in milliseconds.
https://review.coreboot.org/c/flashrom/+/34661/4/stlinkv3_spi.c@138 PS4, Line 138: memset(answer, 0, sizeof(answer));
This is not necessary, is it? I only noticed, because you didn't clear […]
Done
https://review.coreboot.org/c/flashrom/+/34661/4/stlinkv3_spi.c@170 PS4, Line 170: reqd
what is `reqd`?
requested sorry.
Idwer Vollering has posted comments on this change. ( https://review.coreboot.org/c/flashrom/+/34661 )
Change subject: Add support for STLINK V3 debugger/programmer via its SPI bridge ......................................................................
Patch Set 6:
(1 comment)
https://review.coreboot.org/c/flashrom/+/34661/6/stlinkv3_spi.c File stlinkv3_spi.c:
https://review.coreboot.org/c/flashrom/+/34661/6/stlinkv3_spi.c@84 PS6, Line 84: enum spi_nss { What does 'NSS' mean? Please add a little comment, explaining the brevity.
Hello build bot (Jenkins), Nico Huber,
I'd like you to reexamine a change. Please visit
https://review.coreboot.org/c/flashrom/+/34661
to look at the new patch set (#7).
Change subject: Add support for STLINK V3 debugger/programmer via its SPI bridge ......................................................................
Add support for STLINK V3 debugger/programmer via its SPI bridge
Change-Id: Icffab87ac8f2c570187ed753ec70f054541873a4 Signed-off-by: Miklós Márton martonmiklosqdev@gmail.com --- M Makefile M flashrom.8.tmpl M flashrom.c M meson.build M programmer.h A stlinkv3_spi.c 6 files changed, 588 insertions(+), 0 deletions(-)
git pull ssh://review.coreboot.org:29418/flashrom refs/changes/61/34661/7
Miklós Márton has posted comments on this change. ( https://review.coreboot.org/c/flashrom/+/34661 )
Change subject: Add support for STLINK V3 debugger/programmer via its SPI bridge ......................................................................
Patch Set 7:
(1 comment)
https://review.coreboot.org/c/flashrom/+/34661/6/stlinkv3_spi.c File stlinkv3_spi.c:
https://review.coreboot.org/c/flashrom/+/34661/6/stlinkv3_spi.c@84 PS6, Line 84: enum spi_nss {
What does 'NSS' mean? Please add a little comment, explaining the brevity.
NSS stands for Negated Slave Select. ST refers to the chip select pin in their docs (and in their API), so I have choosen to conforman with that. But yeah a bit comment never hurts.
Nico Huber has posted comments on this change. ( https://review.coreboot.org/c/flashrom/+/34661 )
Change subject: Add support for STLINK V3 debugger/programmer via its SPI bridge ......................................................................
Patch Set 7:
(3 comments)
https://review.coreboot.org/c/flashrom/+/34661/7/stlinkv3_spi.c File stlinkv3_spi.c:
https://review.coreboot.org/c/flashrom/+/34661/7/stlinkv3_spi.c@130 PS7, Line 130: sizeof(command) I guess this should be `command_length` now.
https://review.coreboot.org/c/flashrom/+/34661/7/stlinkv3_spi.c@140 PS7, Line 140: sizeof(answer) and `answer_length` here
https://review.coreboot.org/c/flashrom/+/34661/7/stlinkv3_spi.c@442 PS7, Line 442: return -1; This looked actually better before: print an error message, but continue to clean up (don't return early).
Hello build bot (Jenkins), Nico Huber,
I'd like you to reexamine a change. Please visit
https://review.coreboot.org/c/flashrom/+/34661
to look at the new patch set (#8).
Change subject: Add support for STLINK V3 debugger/programmer via its SPI bridge ......................................................................
Add support for STLINK V3 debugger/programmer via its SPI bridge
Change-Id: Icffab87ac8f2c570187ed753ec70f054541873a4 Signed-off-by: Miklós Márton martonmiklosqdev@gmail.com --- M Makefile M flashrom.8.tmpl M flashrom.c M meson.build M programmer.h A stlinkv3_spi.c 6 files changed, 587 insertions(+), 0 deletions(-)
git pull ssh://review.coreboot.org:29418/flashrom refs/changes/61/34661/8
Miklós Márton has posted comments on this change. ( https://review.coreboot.org/c/flashrom/+/34661 )
Change subject: Add support for STLINK V3 debugger/programmer via its SPI bridge ......................................................................
Patch Set 8:
(3 comments)
https://review.coreboot.org/c/flashrom/+/34661/7/stlinkv3_spi.c File stlinkv3_spi.c:
https://review.coreboot.org/c/flashrom/+/34661/7/stlinkv3_spi.c@130 PS7, Line 130: sizeof(command)
I guess this should be `command_length` now.
Done
https://review.coreboot.org/c/flashrom/+/34661/7/stlinkv3_spi.c@140 PS7, Line 140: sizeof(answer)
and `answer_length` here
Done
https://review.coreboot.org/c/flashrom/+/34661/7/stlinkv3_spi.c@442 PS7, Line 442: return -1;
This looked actually better before: print an error message, but continue […]
Done
Nico Huber has posted comments on this change. ( https://review.coreboot.org/c/flashrom/+/34661 )
Change subject: Add support for STLINK V3 debugger/programmer via its SPI bridge ......................................................................
Patch Set 8: Code-Review+2
Miklós Márton has posted comments on this change. ( https://review.coreboot.org/c/flashrom/+/34661 )
Change subject: Add support for STLINK V3 debugger/programmer via its SPI bridge ......................................................................
Patch Set 8: Code-Review+1
Tested with actual hardware after the review modification.
Nico Huber has posted comments on this change. ( https://review.coreboot.org/c/flashrom/+/34661 )
Change subject: Add support for STLINK V3 debugger/programmer via its SPI bridge ......................................................................
Patch Set 8:
Looks like it needs a manual rebase...
Miklós Márton has posted comments on this change. ( https://review.coreboot.org/c/flashrom/+/34661 )
Change subject: Add support for STLINK V3 debugger/programmer via its SPI bridge ......................................................................
Patch Set 8: -Code-Review
Patch Set 8:
Looks like it needs a manual rebase...
I am working on it! In the meantime it seems that some error levels changed since it fails to compile due to the signed - unsigned comparisons in the stlinkv3_command.
Hello build bot (Jenkins), Nico Huber,
I'd like you to reexamine a change. Please visit
https://review.coreboot.org/c/flashrom/+/34661
to look at the new patch set (#9).
Change subject: Add support for STLINK V3 debugger/programmer via its SPI bridge ......................................................................
Add support for STLINK V3 debugger/programmer via its SPI bridge
Change-Id: Icffab87ac8f2c570187ed753ec70f054541873a4 Signed-off-by: Miklós Márton martonmiklosqdev@gmail.com --- M Makefile M flashrom.8.tmpl M flashrom.c M meson.build M programmer.h A stlinkv3_spi.c 6 files changed, 590 insertions(+), 0 deletions(-)
git pull ssh://review.coreboot.org:29418/flashrom refs/changes/61/34661/9
Nico Huber has posted comments on this change. ( https://review.coreboot.org/c/flashrom/+/34661 )
Change subject: Add support for STLINK V3 debugger/programmer via its SPI bridge ......................................................................
Patch Set 9: Code-Review+2
Nico Huber has posted comments on this change. ( https://review.coreboot.org/c/flashrom/+/34661 )
Change subject: Add support for STLINK V3 debugger/programmer via its SPI bridge ......................................................................
Patch Set 9: Code-Review+1
(1 comment)
Sorry, I've run some additional tests and one more nit showed up. When this is fixed, I can merge it instantly :)
https://review.coreboot.org/c/flashrom/+/34661/9/stlinkv3_spi.c File stlinkv3_spi.c:
https://review.coreboot.org/c/flashrom/+/34661/9/stlinkv3_spi.c@349 PS9, Line 349: for (unsigned int i = 0; (i < 8) && (i < write_cnt); i++) Some older compilers complain about the declaration inside the `for ()`. Please move the declaration up to the others.
Nico Huber has posted comments on this change. ( https://review.coreboot.org/c/flashrom/+/34661 )
Change subject: Add support for STLINK V3 debugger/programmer via its SPI bridge ......................................................................
Patch Set 9: Code-Review+2
Sorry, I've run some additional tests and one more nit showed up. When this is fixed, I can merge it instantly :)
Actually, it seemed fairer to do it myself.
Nico Huber has submitted this change. ( https://review.coreboot.org/c/flashrom/+/34661 )
Change subject: Add support for STLINK V3 debugger/programmer via its SPI bridge ......................................................................
Add support for STLINK V3 debugger/programmer via its SPI bridge
Change-Id: Icffab87ac8f2c570187ed753ec70f054541873a4 Signed-off-by: Miklós Márton martonmiklosqdev@gmail.com Reviewed-on: https://review.coreboot.org/c/flashrom/+/34661 Tested-by: build bot (Jenkins) no-reply@coreboot.org Reviewed-by: Nico Huber nico.h@gmx.de --- M Makefile M flashrom.8.tmpl M flashrom.c M meson.build M programmer.h A stlinkv3_spi.c 6 files changed, 590 insertions(+), 0 deletions(-)
Approvals: build bot (Jenkins): Verified Nico Huber: Looks good to me, approved
diff --git a/Makefile b/Makefile index 518d41b..7242b09 100644 --- a/Makefile +++ b/Makefile @@ -190,6 +190,11 @@ else override CONFIG_CH341A_SPI = no endif +ifeq ($(CONFIG_STLINKV3_SPI), yes) +UNSUPPORTED_FEATURES += CONFIG_STLINKV3_SPI=yes +else +override CONFIG_STLINKV3_SPI = no +endif # libjaylink is also not available for DOS ifeq ($(CONFIG_JLINK_SPI), yes) UNSUPPORTED_FEATURES += CONFIG_JLINK_SPI=yes @@ -366,6 +371,11 @@ else override CONFIG_PICKIT2_SPI = no endif +ifeq ($(CONFIG_STLINKV3_SPI), yes) +UNSUPPORTED_FEATURES += CONFIG_STLINKV3_SPI=yes +else +override CONFIG_STLINKV3_SPI = no +endif ifeq ($(CONFIG_CH341A_SPI), yes) UNSUPPORTED_FEATURES += CONFIG_CH341A_SPI=yes else @@ -631,6 +641,9 @@ # Always enable PICkit2 SPI dongles for now. CONFIG_PICKIT2_SPI ?= yes
+# Always enable STLink V3 +CONFIG_STLINKV3_SPI ?= yes + # Always enable dummy tracing for now. CONFIG_DUMMY ?= yes
@@ -709,6 +722,7 @@ override CONFIG_DIGILENT_SPI = no override CONFIG_DEVELOPERBOX_SPI = no override CONFIG_PICKIT2_SPI = no +override CONFIG_STLINKV3_SPI = no endif ifeq ($(CONFIG_ENABLE_LIBPCI_PROGRAMMERS), no) override CONFIG_INTERNAL = no @@ -876,6 +890,12 @@ NEED_LIBUSB1 += CONFIG_PICKIT2_SPI endif
+ifeq ($(CONFIG_STLINKV3_SPI), yes) +FEATURE_CFLAGS += -D'CONFIG_STLINKV3_SPI=1' +PROGRAMMER_OBJS += stlinkv3_spi.o +NEED_LIBUSB1 += CONFIG_STLINKV3_SPI +endif + ifneq ($(NEED_LIBFTDI), ) FTDILIBS := $(call debug_shell,[ -n "$(PKG_CONFIG_LIBDIR)" ] && export PKG_CONFIG_LIBDIR="$(PKG_CONFIG_LIBDIR)" ; $(PKG_CONFIG) --libs libftdi1 || $(PKG_CONFIG) --libs libftdi || printf "%s" "-lftdi -lusb") FEATURE_CFLAGS += $(call debug_shell,grep -q "FT232H := yes" .features && printf "%s" "-D'HAVE_FT232H=1'") diff --git a/flashrom.8.tmpl b/flashrom.8.tmpl index 8ef2cbf..aa5bcd4 100644 --- a/flashrom.8.tmpl +++ b/flashrom.8.tmpl @@ -343,6 +343,8 @@ .sp .BR "* ni845x_spi" " (for SPI flash ROMs attached to National Instruments USB-8451 or USB-8452)" .sp +.BR "* stlinkv3_spi" " (for SPI flash ROMs attached to STMicroelectronics STLINK V3 devices)" +.sp Some programmers have optional or mandatory parameters which are described in detail in the .B PROGRAMMER-SPECIFIC INFORMATION @@ -1260,6 +1262,31 @@ syntax where \fBfrequency\fP is the SPI clock frequency in kHz. The maximum speed depends on the device in use. .SS +.BR "stlinkv3_spi " programmer +.IP +This module supports SPI flash programming through the STMicroelectronics +STLINK V3 programmer/debugger's SPI bridge interface +.sp +.B " flashrom -p stlinkv3_spi" +.sp +If there is more than one compatible device connected, you can select which one +should be used by specifying its serial number with the +.sp +.B " flashrom -p stlinkv3_spi:serial=number" +.sp +syntax where +.B number +is the serial number of the device (which can be found for example in the +output of lsusb -v). +.sp +The SPI speed can be selected by using the +.sp +.B " flashrom -p stlinkv3_spi:spispeed=frequency" +.sp +syntax where \fBfrequency\fP is the SPI clock frequency in kHz. +If the passed frequency is not supported by the adapter the nearest lower +supported frequency will be used. +.SS
.SH EXAMPLES To back up and update your BIOS, run diff --git a/flashrom.c b/flashrom.c index 2534e4a..e540027 100644 --- a/flashrom.c +++ b/flashrom.c @@ -460,6 +460,19 @@ .delay = internal_delay, }, #endif + +#if CONFIG_STLINKV3_SPI == 1 + { + .name = "stlinkv3_spi", + .type = USB, + .devs.dev = devs_stlinkv3_spi, + .init = stlinkv3_spi_init, + .map_flash_region = fallback_map, + .unmap_flash_region = fallback_unmap, + .delay = internal_delay, + }, +#endif + {0}, /* This entry corresponds to PROGRAMMER_INVALID. */ };
diff --git a/meson.build b/meson.build index b63875e..375089c 100644 --- a/meson.build +++ b/meson.build @@ -61,6 +61,7 @@ config_satasii = get_option('config_satasii') config_serprog = get_option('config_serprog') config_usbblaster_spi = get_option('config_usbblaster_spi') +config_stlinkv3_spi = get_option('config_stlinkv3_spi')
cargs = [] deps = [] @@ -271,6 +272,10 @@ srcs += 'usbblaster_spi.c' cargs += '-DCONFIG_USBBLASTER_SPI=1' endif +if config_stlinkv3_spi + srcs += 'stlinkv3_spi.c' + cargs += '-DCONFIG_STLINKV3_SPI=1' +endif
# bitbanging SPI infrastructure if config_bitbang_spi diff --git a/programmer.h b/programmer.h index 5a21b2e..3cf53b9 100644 --- a/programmer.h +++ b/programmer.h @@ -124,6 +124,9 @@ #if CONFIG_NI845X_SPI == 1 PROGRAMMER_NI845X_SPI, #endif +#if CONFIG_STLINKV3_SPI == 1 + PROGRAMMER_STLINKV3_SPI, +#endif PROGRAMMER_INVALID /* This must always be the last entry. */ };
@@ -501,6 +504,12 @@ extern const struct dev_entry devs_pickit2_spi[]; #endif
+/* stlinkv3_spi.c */ +#if CONFIG_STLINKV3_SPI == 1 +int stlinkv3_spi_init(void); +extern const struct dev_entry devs_stlinkv3_spi[]; +#endif + /* rayer_spi.c */ #if CONFIG_RAYER_SPI == 1 int rayer_spi_init(void); diff --git a/stlinkv3_spi.c b/stlinkv3_spi.c new file mode 100644 index 0000000..02e87bc --- /dev/null +++ b/stlinkv3_spi.c @@ -0,0 +1,516 @@ +/* + * This file is part of the flashrom project. + * + * Copyright (C) 2019 Miklós Márton martonmiklosqdev@gmail.com + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + */ + +/* + * Driver for programming SPI flash chips using the SPI port + * of the STMicroelectronics's STLINK-V3 programmer/debugger. + * + * The implementation is inspired by the ST's STLINK-V3-BRIDGE C++ API: + * https://www.st.com/en/development-tools/stlink-v3-bridge.html + */ + +#include "flash.h" +#include "programmer.h" +#include "spi.h" + +#include <libusb.h> +#include <limits.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +enum fw_version_check_result { + FW_VERSION_OK, + FW_VERSION_OLD, +}; + +enum spi_prescaler { + SPI_BAUDRATEPRESCALER_2 = 0, + SPI_BAUDRATEPRESCALER_4 = 1, + SPI_BAUDRATEPRESCALER_8 = 2, + SPI_BAUDRATEPRESCALER_16 = 3, + SPI_BAUDRATEPRESCALER_32 = 4, + SPI_BAUDRATEPRESCALER_64 = 5, + SPI_BAUDRATEPRESCALER_128 = 6, + SPI_BAUDRATEPRESCALER_256 = 7 +}; + +enum spi_dir { + SPI_DIRECTION_2LINES_FULLDUPLEX = 0, + SPI_DIRECTION_2LINES_RXONLY = 1, + SPI_DIRECTION_1LINE_RX = 2, + SPI_DIRECTION_1LINE_TX = 3 +}; + +enum spi_mode { + SPI_MODE_SLAVE = 0, + SPI_MODE_MASTER = 1 +}; + +enum spi_datasize { + SPI_DATASIZE_16B = 0, + SPI_DATASIZE_8B = 1 +}; + +enum spi_cpol { + SPI_CPOL_LOW = 0, + SPI_CPOL_HIGH = 1 +}; + +enum spi_cpha { + SPI_CPHA_1EDGE = 0, + SPI_CPHA_2EDGE = 1 +}; + +enum spi_firstbit { + SPI_FIRSTBIT_LSB = 0, + SPI_FIRSTBIT_MSB = 1 +}; + +// ST calls the Chip select (CS) NSS == Negated Slave Select +enum spi_nss { + SPI_NSS_SOFT = 0, + SPI_NSS_HARD = 1 +}; + +enum spi_nss_level { + SPI_NSS_LOW = 0, + SPI_NSS_HIGH = 1 +}; + +#define ST_GETVERSION_EXT 0xFB + +#define STLINK_BRIDGE_COMMAND 0xFC +#define STLINK_BRIDGE_CLOSE 0x01 +#define STLINK_BRIDGE_GET_RWCMD_STATUS 0x02 +#define STLINK_BRIDGE_GET_CLOCK 0x03 +#define STLINK_BRIDGE_INIT_SPI 0x20 +#define STLINK_BRIDGE_WRITE_SPI 0x21 +#define STLINK_BRIDGE_READ_SPI 0x22 +#define STLINK_BRIDGE_CS_SPI 0x23 + +#define STLINK_BRIDGE_SPI_ERROR 0x02 + +#define STLINK_SPI_COM 0x02 + +#define STLINK_EP_OUT 0x06 +#define STLINK_EP_IN 0x86 + +#define FIRST_COMPATIBLE_BRIDGE_FW_VERSION 3 + +#define USB_TIMEOUT_IN_MS 5000 + +const struct dev_entry devs_stlinkv3_spi[] = { + {0x0483, 0x374F, OK, "STMicroelectronics", "STLINK-V3"}, + {0} +}; + +static struct libusb_context *usb_ctx; +static libusb_device_handle *stlinkv3_handle; + +static int stlinkv3_command(uint8_t *command, size_t command_length, + uint8_t *answer, size_t answer_length, const char *command_name) +{ + int actual_length = 0; + int rc = libusb_bulk_transfer(stlinkv3_handle, STLINK_EP_OUT, + command, command_length, + &actual_length, USB_TIMEOUT_IN_MS); + if (rc != LIBUSB_TRANSFER_COMPLETED || (size_t)actual_length != command_length) { + msg_perr("Failed to issue the %s command: '%s'\n", + command_name, + libusb_error_name(rc)); + return -1; + } + + rc = libusb_bulk_transfer(stlinkv3_handle, STLINK_EP_IN, + answer, answer_length, + &actual_length, USB_TIMEOUT_IN_MS); + if (rc != LIBUSB_TRANSFER_COMPLETED || (size_t)actual_length != answer_length) { + msg_perr("Failed to get %s answer: '%s'\n", + command_name, + libusb_error_name(rc)); + return -1; + } + return 0; +} + +/** + * @param[out] bridge_input_clk Current input frequency in kHz of the given com. + */ +static int stlinkv3_get_clk(uint32_t *bridge_input_clk) +{ + uint8_t command[16]; + uint8_t answer[12]; + + if (bridge_input_clk == NULL) + return -1; + + memset(command, 0, sizeof(command)); + + command[0] = STLINK_BRIDGE_COMMAND; + command[1] = STLINK_BRIDGE_GET_CLOCK; + command[2] = STLINK_SPI_COM; + + if (stlinkv3_command(command, sizeof(command), answer, sizeof(answer), "STLINK_BRIDGE_GET_CLOCK") != 0) + return -1; + + *bridge_input_clk = (uint32_t)answer[4] + | (uint32_t)answer[5]<<8 + | (uint32_t)answer[6]<<16 + | (uint32_t)answer[7]<<24; + return 0; + +} + +static int stlinkv3_spi_calc_prescaler(uint16_t reqested_freq_in_kHz, + enum spi_prescaler *prescaler, + uint16_t *calculated_freq_in_kHz) +{ + uint32_t bridge_clk_in_kHz; + uint32_t calculated_prescaler = 1; + uint16_t prescaler_value; + + if (stlinkv3_get_clk(&bridge_clk_in_kHz)) + return -1; + + calculated_prescaler = bridge_clk_in_kHz/reqested_freq_in_kHz; + // Apply a smaller frequency if not exact + if (calculated_prescaler <= 2) { + *prescaler = SPI_BAUDRATEPRESCALER_2; + prescaler_value = 2; + } else if (calculated_prescaler <= 4) { + *prescaler = SPI_BAUDRATEPRESCALER_4; + prescaler_value = 4; + } else if (calculated_prescaler <= 8) { + *prescaler = SPI_BAUDRATEPRESCALER_8; + prescaler_value = 8; + } else if (calculated_prescaler <= 16) { + *prescaler = SPI_BAUDRATEPRESCALER_16; + prescaler_value = 16; + } else if (calculated_prescaler <= 32) { + *prescaler = SPI_BAUDRATEPRESCALER_32; + prescaler_value = 32; + } else if (calculated_prescaler <= 64) { + *prescaler = SPI_BAUDRATEPRESCALER_64; + prescaler_value = 64; + } else if (calculated_prescaler <= 128) { + *prescaler = SPI_BAUDRATEPRESCALER_128; + prescaler_value = 128; + } else if (calculated_prescaler <= 256) { + *prescaler = SPI_BAUDRATEPRESCALER_256; + prescaler_value = 256; + } else { + // smaller frequency not possible + *prescaler = SPI_BAUDRATEPRESCALER_256; + prescaler_value = 256; + } + + *calculated_freq_in_kHz = bridge_clk_in_kHz / prescaler_value; + + return 0; +} + +static int stlinkv3_check_version(enum fw_version_check_result *result) +{ + uint8_t answer[12]; + uint8_t command[16]; + + memset(command, 0, sizeof(command)); + + command[0] = ST_GETVERSION_EXT; + command[1] = 0x80; + + if (stlinkv3_command(command, sizeof(command), answer, sizeof(answer), "ST_GETVERSION_EXT") != 0) + return -1; + + msg_pinfo("Connected to STLink V3 with bridge FW version: %d\n", answer[4]); + *result = answer[4] >= FIRST_COMPATIBLE_BRIDGE_FW_VERSION + ? FW_VERSION_OK + : FW_VERSION_OLD; + return 0; +} + +static int stlinkv3_spi_open(uint16_t reqested_freq_in_kHz) +{ + uint8_t command[16]; + uint8_t answer[2]; + uint16_t SCK_freq_in_kHz; + enum spi_prescaler prescaler; + enum fw_version_check_result fw_check_result; + + if (stlinkv3_check_version(&fw_check_result)) { + msg_perr("Failed to query FW version"); + return -1; + } + + if (fw_check_result != FW_VERSION_OK) { + msg_pinfo("Your STLink V3 has too old version of the bridge interface\n" + "Please update the firmware with the STSW-LINK007 which can be downloaded from here:\n" + "https://www.st.com/en/development-tools/stsw-link007.html"); + return -1; + } + + if (stlinkv3_spi_calc_prescaler(reqested_freq_in_kHz, + &prescaler, + &SCK_freq_in_kHz)) { + msg_perr("Failed to calculate SPI clock prescaler"); + return -1; + } + msg_pinfo("SCK frequency set to %d kHz\n", SCK_freq_in_kHz); + + memset(command, 0, sizeof(command)); + + command[0] = STLINK_BRIDGE_COMMAND; + command[1] = STLINK_BRIDGE_INIT_SPI; + command[2] = SPI_DIRECTION_2LINES_FULLDUPLEX; + command[3] = (SPI_MODE_MASTER + | (SPI_CPHA_1EDGE << 1) + | (SPI_CPOL_LOW << 2) + | (SPI_FIRSTBIT_MSB << 3)); + command[4] = SPI_DATASIZE_8B; + command[5] = SPI_NSS_SOFT; + command[6] = (uint8_t)prescaler; + + return stlinkv3_command(command, sizeof(command), answer, sizeof(answer), "STLINK_BRIDGE_INIT_SPI"); +} + +static int stlinkv3_get_last_readwrite_status(uint32_t *status) +{ + uint8_t command[16]; + uint16_t answer[4]; + + memset(command, 0, sizeof(command)); + + command[0] = STLINK_BRIDGE_COMMAND; + command[1] = STLINK_BRIDGE_GET_RWCMD_STATUS; + + if (stlinkv3_command(command, sizeof(command), + (uint8_t *)answer, sizeof(answer), + "STLINK_BRIDGE_GET_RWCMD_STATUS") != 0) + return -1; + + *status = (uint32_t)answer[2] | (uint32_t)answer[3]<<16; + return 0; +} + +static int stlinkv3_spi_set_SPI_NSS(enum spi_nss_level nss_level) +{ + uint8_t command[16]; + uint8_t answer[2]; + + memset(command, 0, sizeof(command)); + + command[0] = STLINK_BRIDGE_COMMAND; + command[1] = STLINK_BRIDGE_CS_SPI; + command[2] = (uint8_t) (nss_level); + + if (stlinkv3_command(command, sizeof(command), answer, sizeof(answer), "STLINK_BRIDGE_CS_SPI") != 0) + return -1; + return 0; +} + +static int stlinkv3_spi_transmit(struct flashctx *flash, + unsigned int write_cnt, + unsigned int read_cnt, + const unsigned char *write_arr, + unsigned char *read_arr) +{ + uint8_t command[16]; + int rc = 0; + int actual_length = 0; + uint32_t rw_status = 0; + + if (stlinkv3_spi_set_SPI_NSS(SPI_NSS_LOW)) { + msg_perr("Failed to set the NSS pin to low\n"); + return -1; + } + + memset(command, 0, sizeof(command)); + + command[0] = STLINK_BRIDGE_COMMAND; + command[1] = STLINK_BRIDGE_WRITE_SPI; + command[2] = (uint8_t)write_cnt; + command[3] = (uint8_t)(write_cnt >> 8); + + for (unsigned int i = 0; (i < 8) && (i < write_cnt); i++) + command[4+i] = write_arr[i]; + + rc = libusb_bulk_transfer(stlinkv3_handle, STLINK_EP_OUT, + command, sizeof(command), + &actual_length, USB_TIMEOUT_IN_MS); + if (rc != LIBUSB_TRANSFER_COMPLETED || actual_length != sizeof(command)) { + msg_perr("Failed to issue the STLINK_BRIDGE_WRITE_SPI command: '%s'\n", + libusb_error_name(rc)); + goto transmit_err; + } + + if (write_cnt > 8) { + rc = libusb_bulk_transfer(stlinkv3_handle, + STLINK_EP_OUT, + (unsigned char *)&write_arr[8], + (unsigned int)(write_cnt - 8), + &actual_length, + USB_TIMEOUT_IN_MS); + if (rc != LIBUSB_TRANSFER_COMPLETED || (unsigned int)actual_length != (write_cnt - 8)) { + msg_perr("Failed to send the data after the STLINK_BRIDGE_WRITE_SPI command: '%s'\n", + libusb_error_name(rc)); + goto transmit_err; + } + } + + if (stlinkv3_get_last_readwrite_status(&rw_status)) + return -1; + + if (rw_status != 0) { + msg_perr("SPI read/write failure: %d\n", rw_status); + goto transmit_err; + } + + if (read_cnt) { + command[1] = STLINK_BRIDGE_READ_SPI; + command[2] = (uint8_t)read_cnt; + command[3] = (uint8_t)(read_cnt >> 8); + + rc = libusb_bulk_transfer(stlinkv3_handle, STLINK_EP_OUT, + command, sizeof(command), + &actual_length, USB_TIMEOUT_IN_MS); + if (rc != LIBUSB_TRANSFER_COMPLETED || (unsigned int)actual_length != sizeof(command)) { + msg_perr("Failed to issue the STLINK_BRIDGE_READ_SPI command: '%s'\n", + libusb_error_name(rc)); + goto transmit_err; + } + + rc = libusb_bulk_transfer(stlinkv3_handle, + STLINK_EP_IN, + (unsigned char *)read_arr, + (int)read_cnt, + &actual_length, + USB_TIMEOUT_IN_MS); + if (rc != LIBUSB_TRANSFER_COMPLETED || (unsigned int)actual_length != read_cnt) { + msg_perr("Failed to retrive the STLINK_BRIDGE_READ_SPI answer: '%s'\n", + libusb_error_name(rc)); + goto transmit_err; + } + } + + if (stlinkv3_get_last_readwrite_status(&rw_status)) + goto transmit_err; + + if (rw_status != 0) { + msg_perr("SPI read/write failure: %d\n", rw_status); + goto transmit_err; + } + + if (stlinkv3_spi_set_SPI_NSS(SPI_NSS_HIGH)) { + msg_perr("Failed to set the NSS pin to high\n"); + return -1; + } + return 0; + +transmit_err: + if (stlinkv3_spi_set_SPI_NSS(SPI_NSS_HIGH)) + msg_perr("Failed to set the NSS pin to high\n"); + return -1; +} + +static int stlinkv3_spi_shutdown(void *data) +{ + uint8_t command[16]; + uint8_t answer[2]; + + memset(command, 0, sizeof(command)); + + command[0] = STLINK_BRIDGE_COMMAND; + command[1] = STLINK_BRIDGE_CLOSE; + command[2] = STLINK_SPI_COM; + + stlinkv3_command(command, sizeof(command), answer, sizeof(answer), "STLINK_BRIDGE_CLOSE"); + + libusb_close(stlinkv3_handle); + libusb_exit(usb_ctx); + + return 0; +} + +static const struct spi_master spi_programmer_stlinkv3 = { + .max_data_read = UINT16_MAX, + .max_data_write = UINT16_MAX, + .command = stlinkv3_spi_transmit, + .multicommand = default_spi_send_multicommand, + .read = default_spi_read, + .write_256 = default_spi_write_256, + .write_aai = default_spi_write_aai, +}; + +int stlinkv3_spi_init(void) +{ + uint16_t sck_freq_kHz = 1000; // selecting 1 MHz SCK is a good bet + char *speed_str = NULL; + char *serialno = NULL; + char *endptr = NULL; + + libusb_init(&usb_ctx); + if (!usb_ctx) { + msg_perr("Could not initialize libusb!\n"); + return 1; + } + + serialno = extract_programmer_param("serial"); + if (serialno) + msg_pdbg("Opening STLINK-V3 with serial: %s\n", serialno); + stlinkv3_handle = usb_dev_get_by_vid_pid_serial(usb_ctx, + devs_stlinkv3_spi[0].vendor_id, + devs_stlinkv3_spi[0].device_id, + serialno); + + if (!stlinkv3_handle) { + if (serialno) + msg_perr("No STLINK-V3 seems to be connected with serial %s\n", serialno); + else + msg_perr("Could not find any connected STLINK-V3\n"); + free(serialno); + goto err_exit; + } + free(serialno); + + speed_str = extract_programmer_param("spispeed"); + if (speed_str) { + sck_freq_kHz = strtoul(speed_str, &endptr, 0); + if (*endptr) { + msg_perr("The spispeed parameter passed with invalid format: %s\n", + speed_str); + msg_perr("Please pass the parameter with a simple number in kHz\n"); + return -1; + } + free(speed_str); + } + + if (stlinkv3_spi_open(sck_freq_kHz)) + goto err_exit; + + if (register_shutdown(stlinkv3_spi_shutdown, NULL)) + goto err_exit; + + if (register_spi_master(&spi_programmer_stlinkv3)) + goto err_exit; + + return 0; + +err_exit: + libusb_exit(usb_ctx); + return 1; +}