mirror of
https://review.coreboot.org/flashrom.git
synced 2025-04-28 07:23:43 +02:00
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>
This commit is contained in:
parent
728062f7ff
commit
324929c3d7
20
Makefile
20
Makefile
@ -190,6 +190,11 @@ UNSUPPORTED_FEATURES += CONFIG_CH341A_SPI=yes
|
|||||||
else
|
else
|
||||||
override CONFIG_CH341A_SPI = no
|
override CONFIG_CH341A_SPI = no
|
||||||
endif
|
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
|
# libjaylink is also not available for DOS
|
||||||
ifeq ($(CONFIG_JLINK_SPI), yes)
|
ifeq ($(CONFIG_JLINK_SPI), yes)
|
||||||
UNSUPPORTED_FEATURES += CONFIG_JLINK_SPI=yes
|
UNSUPPORTED_FEATURES += CONFIG_JLINK_SPI=yes
|
||||||
@ -366,6 +371,11 @@ UNSUPPORTED_FEATURES += CONFIG_PICKIT2_SPI=yes
|
|||||||
else
|
else
|
||||||
override CONFIG_PICKIT2_SPI = no
|
override CONFIG_PICKIT2_SPI = no
|
||||||
endif
|
endif
|
||||||
|
ifeq ($(CONFIG_STLINKV3_SPI), yes)
|
||||||
|
UNSUPPORTED_FEATURES += CONFIG_STLINKV3_SPI=yes
|
||||||
|
else
|
||||||
|
override CONFIG_STLINKV3_SPI = no
|
||||||
|
endif
|
||||||
ifeq ($(CONFIG_CH341A_SPI), yes)
|
ifeq ($(CONFIG_CH341A_SPI), yes)
|
||||||
UNSUPPORTED_FEATURES += CONFIG_CH341A_SPI=yes
|
UNSUPPORTED_FEATURES += CONFIG_CH341A_SPI=yes
|
||||||
else
|
else
|
||||||
@ -631,6 +641,9 @@ CONFIG_MSTARDDC_SPI ?= no
|
|||||||
# Always enable PICkit2 SPI dongles for now.
|
# Always enable PICkit2 SPI dongles for now.
|
||||||
CONFIG_PICKIT2_SPI ?= yes
|
CONFIG_PICKIT2_SPI ?= yes
|
||||||
|
|
||||||
|
# Always enable STLink V3
|
||||||
|
CONFIG_STLINKV3_SPI ?= yes
|
||||||
|
|
||||||
# Always enable dummy tracing for now.
|
# Always enable dummy tracing for now.
|
||||||
CONFIG_DUMMY ?= yes
|
CONFIG_DUMMY ?= yes
|
||||||
|
|
||||||
@ -709,6 +722,7 @@ override CONFIG_DEDIPROG = no
|
|||||||
override CONFIG_DIGILENT_SPI = no
|
override CONFIG_DIGILENT_SPI = no
|
||||||
override CONFIG_DEVELOPERBOX_SPI = no
|
override CONFIG_DEVELOPERBOX_SPI = no
|
||||||
override CONFIG_PICKIT2_SPI = no
|
override CONFIG_PICKIT2_SPI = no
|
||||||
|
override CONFIG_STLINKV3_SPI = no
|
||||||
endif
|
endif
|
||||||
ifeq ($(CONFIG_ENABLE_LIBPCI_PROGRAMMERS), no)
|
ifeq ($(CONFIG_ENABLE_LIBPCI_PROGRAMMERS), no)
|
||||||
override CONFIG_INTERNAL = no
|
override CONFIG_INTERNAL = no
|
||||||
@ -876,6 +890,12 @@ PROGRAMMER_OBJS += pickit2_spi.o
|
|||||||
NEED_LIBUSB1 += CONFIG_PICKIT2_SPI
|
NEED_LIBUSB1 += CONFIG_PICKIT2_SPI
|
||||||
endif
|
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), )
|
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")
|
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'")
|
FEATURE_CFLAGS += $(call debug_shell,grep -q "FT232H := yes" .features && printf "%s" "-D'HAVE_FT232H=1'")
|
||||||
|
@ -343,6 +343,8 @@ bitbanging adapter)
|
|||||||
.sp
|
.sp
|
||||||
.BR "* ni845x_spi" " (for SPI flash ROMs attached to National Instruments USB-8451 or USB-8452)"
|
.BR "* ni845x_spi" " (for SPI flash ROMs attached to National Instruments USB-8451 or USB-8452)"
|
||||||
.sp
|
.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
|
Some programmers have optional or mandatory parameters which are described
|
||||||
in detail in the
|
in detail in the
|
||||||
.B PROGRAMMER-SPECIFIC INFORMATION
|
.B PROGRAMMER-SPECIFIC INFORMATION
|
||||||
@ -1260,6 +1262,31 @@ The SPI speed can be selected by using the
|
|||||||
syntax where \fBfrequency\fP is the SPI clock frequency in kHz.
|
syntax where \fBfrequency\fP is the SPI clock frequency in kHz.
|
||||||
The maximum speed depends on the device in use.
|
The maximum speed depends on the device in use.
|
||||||
.SS
|
.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
|
.SH EXAMPLES
|
||||||
To back up and update your BIOS, run
|
To back up and update your BIOS, run
|
||||||
|
13
flashrom.c
13
flashrom.c
@ -460,6 +460,19 @@ const struct programmer_entry programmer_table[] = {
|
|||||||
.delay = internal_delay,
|
.delay = internal_delay,
|
||||||
},
|
},
|
||||||
#endif
|
#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. */
|
{0}, /* This entry corresponds to PROGRAMMER_INVALID. */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -61,6 +61,7 @@ config_satamv = get_option('config_satamv')
|
|||||||
config_satasii = get_option('config_satasii')
|
config_satasii = get_option('config_satasii')
|
||||||
config_serprog = get_option('config_serprog')
|
config_serprog = get_option('config_serprog')
|
||||||
config_usbblaster_spi = get_option('config_usbblaster_spi')
|
config_usbblaster_spi = get_option('config_usbblaster_spi')
|
||||||
|
config_stlinkv3_spi = get_option('config_stlinkv3_spi')
|
||||||
|
|
||||||
cargs = []
|
cargs = []
|
||||||
deps = []
|
deps = []
|
||||||
@ -271,6 +272,10 @@ if config_usbblaster_spi
|
|||||||
srcs += 'usbblaster_spi.c'
|
srcs += 'usbblaster_spi.c'
|
||||||
cargs += '-DCONFIG_USBBLASTER_SPI=1'
|
cargs += '-DCONFIG_USBBLASTER_SPI=1'
|
||||||
endif
|
endif
|
||||||
|
if config_stlinkv3_spi
|
||||||
|
srcs += 'stlinkv3_spi.c'
|
||||||
|
cargs += '-DCONFIG_STLINKV3_SPI=1'
|
||||||
|
endif
|
||||||
|
|
||||||
# bitbanging SPI infrastructure
|
# bitbanging SPI infrastructure
|
||||||
if config_bitbang_spi
|
if config_bitbang_spi
|
||||||
|
@ -123,6 +123,9 @@ enum programmer {
|
|||||||
#endif
|
#endif
|
||||||
#if CONFIG_NI845X_SPI == 1
|
#if CONFIG_NI845X_SPI == 1
|
||||||
PROGRAMMER_NI845X_SPI,
|
PROGRAMMER_NI845X_SPI,
|
||||||
|
#endif
|
||||||
|
#if CONFIG_STLINKV3_SPI == 1
|
||||||
|
PROGRAMMER_STLINKV3_SPI,
|
||||||
#endif
|
#endif
|
||||||
PROGRAMMER_INVALID /* This must always be the last entry. */
|
PROGRAMMER_INVALID /* This must always be the last entry. */
|
||||||
};
|
};
|
||||||
@ -501,6 +504,12 @@ int pickit2_spi_init(void);
|
|||||||
extern const struct dev_entry devs_pickit2_spi[];
|
extern const struct dev_entry devs_pickit2_spi[];
|
||||||
#endif
|
#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 */
|
/* rayer_spi.c */
|
||||||
#if CONFIG_RAYER_SPI == 1
|
#if CONFIG_RAYER_SPI == 1
|
||||||
int rayer_spi_init(void);
|
int rayer_spi_init(void);
|
||||||
|
516
stlinkv3_spi.c
Normal file
516
stlinkv3_spi.c
Normal file
@ -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;
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user