mail.coreboot.org
Sign In
Sign Up
Sign In
Sign Up
Manage this list
×
Keyboard Shortcuts
Thread View
j
: Next unread message
k
: Previous unread message
j a
: Jump to all threads
j l
: Jump to MailingList overview
2025
January
2024
December
November
October
September
August
July
June
May
April
March
February
January
2023
December
November
October
September
August
July
June
May
April
March
February
January
2022
December
November
October
September
August
July
June
May
April
March
February
January
2021
December
November
October
September
August
July
June
May
April
March
February
January
2020
December
November
October
September
August
July
June
May
April
March
February
January
2019
December
November
October
September
August
July
June
May
April
March
February
January
2018
December
November
October
September
August
July
June
May
April
March
February
January
2017
December
November
October
September
August
July
June
May
April
March
List overview
Download
flashrom-gerrit
August 2019
----- 2025 -----
January 2025
----- 2024 -----
December 2024
November 2024
October 2024
September 2024
August 2024
July 2024
June 2024
May 2024
April 2024
March 2024
February 2024
January 2024
----- 2023 -----
December 2023
November 2023
October 2023
September 2023
August 2023
July 2023
June 2023
May 2023
April 2023
March 2023
February 2023
January 2023
----- 2022 -----
December 2022
November 2022
October 2022
September 2022
August 2022
July 2022
June 2022
May 2022
April 2022
March 2022
February 2022
January 2022
----- 2021 -----
December 2021
November 2021
October 2021
September 2021
August 2021
July 2021
June 2021
May 2021
April 2021
March 2021
February 2021
January 2021
----- 2020 -----
December 2020
November 2020
October 2020
September 2020
August 2020
July 2020
June 2020
May 2020
April 2020
March 2020
February 2020
January 2020
----- 2019 -----
December 2019
November 2019
October 2019
September 2019
August 2019
July 2019
June 2019
May 2019
April 2019
March 2019
February 2019
January 2019
----- 2018 -----
December 2018
November 2018
October 2018
September 2018
August 2018
July 2018
June 2018
May 2018
April 2018
March 2018
February 2018
January 2018
----- 2017 -----
December 2017
November 2017
October 2017
September 2017
August 2017
July 2017
June 2017
May 2017
April 2017
March 2017
flashrom-gerrit@flashrom.org
1 participants
72 discussions
Start a n
N
ew thread
Change in flashrom[master]: Add support for STLINK V3 debugger/programmer via it's SPI bridge
by Miklós Márton (Code Review)
01 Aug '19
01 Aug '19
Miklós Márton has uploaded this change for review. (
https://review.coreboot.org/c/flashrom/+/34659
) 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: I5e889f88f89c43f7763c71497fc96e70edd200d6 Signed-off-by: Miklós Márton <martonmiklosqdev(a)gmail.com> --- M Makefile M cli_classic.c M flashrom.8.tmpl M
…
[View More]
flashrom.c M meson.build M programmer.h A stlinkv3_spi.c 7 files changed, 675 insertions(+), 0 deletions(-) git pull ssh://review.coreboot.org:29418/flashrom refs/changes/59/34659/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/cli_classic.c b/cli_classic.c index b79f953..0591bfe 100644 --- a/cli_classic.c +++ b/cli_classic.c @@ -383,6 +383,11 @@ fprintf(stderr, "Log file not supported in standalone mode. Aborting.\n"); cli_classic_abort_usage(); #else /* STANDALONE */ + if (logfile) { + fprintf(stderr, "Warning: -o/--output specified multiple times.\n"); + free(logfile); + } + logfile = strdup(optarg); if (logfile[0] == '\0') { fprintf(stderr, "No log filename specified.\n"); 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(a)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; +} -- To view, visit
https://review.coreboot.org/c/flashrom/+/34659
To unsubscribe, or for help writing mail filters, visit
https://review.coreboot.org/settings
Gerrit-Project: flashrom Gerrit-Branch: master Gerrit-Change-Id: I5e889f88f89c43f7763c71497fc96e70edd200d6 Gerrit-Change-Number: 34659 Gerrit-PatchSet: 1 Gerrit-Owner: Miklós Márton <martonmiklosqdev(a)gmail.com> Gerrit-MessageType: newchange
[View Less]
1
3
0
0
Change in flashrom[staging]: Add support for STLINK V3 debugger/programmer via it's SPI bridge
by Miklós Márton (Code Review)
01 Aug '19
01 Aug '19
Miklós Márton has uploaded this change for review. (
https://review.coreboot.org/c/flashrom/+/34660
) 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: I5e889f88f89c43f7763c71497fc96e70edd200d6 Signed-off-by: Miklós Márton <martonmiklosqdev(a)gmail.com> --- M Makefile M cli_classic.c M flashrom.8.tmpl M
…
[View More]
flashrom.c M meson.build M programmer.h A stlinkv3_spi.c 7 files changed, 671 insertions(+), 0 deletions(-) git pull ssh://review.coreboot.org:29418/flashrom refs/changes/60/34660/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/cli_classic.c b/cli_classic.c index b79f953..4cc9056 100644 --- a/cli_classic.c +++ b/cli_classic.c @@ -383,6 +383,7 @@ fprintf(stderr, "Log file not supported in standalone mode. Aborting.\n"); cli_classic_abort_usage(); #else /* STANDALONE */ + logfile = strdup(optarg); if (logfile[0] == '\0') { fprintf(stderr, "No log filename specified.\n"); 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(a)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; +} -- To view, visit
https://review.coreboot.org/c/flashrom/+/34660
To unsubscribe, or for help writing mail filters, visit
https://review.coreboot.org/settings
Gerrit-Project: flashrom Gerrit-Branch: staging Gerrit-Change-Id: I5e889f88f89c43f7763c71497fc96e70edd200d6 Gerrit-Change-Number: 34660 Gerrit-PatchSet: 1 Gerrit-Owner: Miklós Márton <martonmiklosqdev(a)gmail.com> Gerrit-MessageType: newchange
[View Less]
1
1
0
0
← Newer
1
2
3
4
5
6
7
8
Older →
Jump to page:
1
2
3
4
5
6
7
8
Results per page:
10
25
50
100
200