#include <jtag/hla/hla_layout.h>
#include <jtag/hla/hla_transport.h>
#include <jtag/hla/hla_interface.h>
+#include <jtag/swim.h>
#include <target/target.h>
#include <transport/transport.h>
uint32_t address
STLINK_SWIM_RESET
- send syncronization seq (16us low, response 64 clocks low)
+ send synchronization seq (16us low, response 64 clocks low)
*/
#define STLINK_SWIM_ENTER 0x00
#define STLINK_SWIM_EXIT 0x01
#define STLINK_TRACE_SIZE 4096
#define STLINK_TRACE_MAX_HZ 2000000
+#define STLINK_V3_TRACE_MAX_HZ 24000000
#define STLINK_V3_MAX_FREQ_NB 10
#define STLINK_F_HAS_AP_INIT BIT(7)
#define STLINK_F_HAS_DPBANKSEL BIT(8)
#define STLINK_F_HAS_RW8_512BYTES BIT(9)
+#define STLINK_F_FIX_CLOSE_AP BIT(10)
/* aliases */
#define STLINK_F_HAS_TARGET_VOLT STLINK_F_HAS_TRACE
+#define STLINK_F_HAS_FPU_REG STLINK_F_HAS_GETLASTRWSTATUS2
+
+#define STLINK_REGSEL_IS_FPU(x) ((x) > 0x1F)
struct speed_map {
int speed;
static void stlink_usb_init_buffer(void *handle, uint8_t direction, uint32_t size);
static int stlink_swim_status(void *handle);
-void stlink_dump_speed_map(const struct speed_map *map, unsigned int map_size);
+static void stlink_dump_speed_map(const struct speed_map *map, unsigned int map_size);
static int stlink_get_com_freq(void *handle, bool is_jtag, struct speed_map *map);
static int stlink_speed(void *handle, int khz, bool query);
+static int stlink_usb_open_ap(void *handle, unsigned short apsel);
/** */
static unsigned int stlink_usb_block(void *handle)
if (h->version.jtag >= 28)
flags |= STLINK_F_HAS_AP_INIT;
+ /* API required to return proper error code on close AP from J29 */
+ if (h->version.jtag >= 29)
+ flags |= STLINK_F_FIX_CLOSE_AP;
+
/* Banked regs (DPv1 & DPv2) support from V2J32 */
if (h->version.jtag >= 32)
flags |= STLINK_F_HAS_DPBANKSEL;
/* API required to init AP before any AP access */
flags |= STLINK_F_HAS_AP_INIT;
+ /* API required to return proper error code on close AP */
+ flags |= STLINK_F_FIX_CLOSE_AP;
+
/* Banked regs (DPv1 & DPv2) support from V3J2 */
if (h->version.jtag >= 2)
flags |= STLINK_F_HAS_DPBANKSEL;
return STLINK_MODE_DEBUG_SWD;
case HL_TRANSPORT_JTAG:
return STLINK_MODE_DEBUG_JTAG;
- case HL_TRANSPORT_SWIM:
- return STLINK_MODE_DEBUG_SWIM;
default:
return STLINK_MODE_UNKNOWN;
}
}
/** */
-static int stlink_usb_init_mode(void *handle, bool connect_under_reset, int initial_interface_speed)
+static int stlink_usb_exit_mode(void *handle)
{
int res;
uint8_t mode;
enum stlink_mode emode;
- struct stlink_usb_handle_s *h = handle;
assert(handle != NULL);
break;
}
- if (emode != STLINK_MODE_UNKNOWN) {
- res = stlink_usb_mode_leave(handle, emode);
+ if (emode != STLINK_MODE_UNKNOWN)
+ return stlink_usb_mode_leave(handle, emode);
- if (res != ERROR_OK)
- return res;
- }
+ return ERROR_OK;
+}
+
+/** */
+static int stlink_usb_init_mode(void *handle, bool connect_under_reset, int initial_interface_speed)
+{
+ int res;
+ uint8_t mode;
+ enum stlink_mode emode;
+ struct stlink_usb_handle_s *h = handle;
+
+ assert(handle != NULL);
+
+ res = stlink_usb_exit_mode(handle);
+ if (res != ERROR_OK)
+ return res;
res = stlink_usb_current_mode(handle, &mode);
}
/*
the purpose of this function is unknown...
- capabilites? anyway for swim v6 it returns
+ capabilities? anyway for swim v6 it returns
0001020600000000
*/
__attribute__((unused))
}
/*
- send resyncronize sequence
+ send resynchronize sequence
swim is pulled low for 16us
reply is 64 clks low
*/
assert(handle != NULL);
- if (h->st_mode == STLINK_MODE_DEBUG_SWIM) {
- res = stlink_usb_mode_enter(handle, h->st_mode);
- if (res != ERROR_OK)
- return TARGET_UNKNOWN;
-
- res = stlink_swim_resync(handle);
- if (res != ERROR_OK)
- return TARGET_UNKNOWN;
-
- return ERROR_OK;
- }
-
if (h->reconnect_pending) {
LOG_INFO("Previous state query failed, trying to reconnect");
res = stlink_usb_mode_enter(handle, h->st_mode);
assert(handle != NULL);
- if (h->st_mode == STLINK_MODE_DEBUG_SWIM)
- return stlink_swim_generate_rst(handle);
-
stlink_usb_init_buffer(handle, h->rx_ep, 2);
h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
}
/** */
-static int stlink_usb_read_reg(void *handle, int num, uint32_t *val)
+static int stlink_usb_read_reg(void *handle, unsigned int regsel, uint32_t *val)
{
int res;
struct stlink_usb_handle_s *h = handle;
assert(handle != NULL);
+ if (STLINK_REGSEL_IS_FPU(regsel) && !(h->version.flags & STLINK_F_HAS_FPU_REG)) {
+ res = stlink_usb_write_debug_reg(h, DCB_DCRSR, regsel & 0x7f);
+ if (res != ERROR_OK)
+ return res;
+
+ /* FIXME: poll DHCSR.S_REGRDY before read DCRDR */
+ return stlink_usb_v2_read_debug_reg(h, DCB_DCRDR, val);
+ }
+
stlink_usb_init_buffer(handle, h->rx_ep, h->version.jtag_api == STLINK_JTAG_API_V1 ? 4 : 8);
h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV1_READREG;
else
h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_READREG;
- h->cmdbuf[h->cmdidx++] = num;
+ h->cmdbuf[h->cmdidx++] = regsel;
if (h->version.jtag_api == STLINK_JTAG_API_V1) {
res = stlink_usb_xfer_noerrcheck(handle, h->databuf, 4);
}
/** */
-static int stlink_usb_write_reg(void *handle, int num, uint32_t val)
+static int stlink_usb_write_reg(void *handle, unsigned int regsel, uint32_t val)
{
struct stlink_usb_handle_s *h = handle;
assert(handle != NULL);
+ if (STLINK_REGSEL_IS_FPU(regsel) && !(h->version.flags & STLINK_F_HAS_FPU_REG)) {
+ int res = stlink_usb_write_debug_reg(h, DCB_DCRDR, val);
+ if (res != ERROR_OK)
+ return res;
+
+ return stlink_usb_write_debug_reg(h, DCB_DCRSR, DCRSR_WnR | (regsel & 0x7f));
+ /* FIXME: poll DHCSR.S_REGRDY after write DCRSR */
+ }
+
stlink_usb_init_buffer(handle, h->rx_ep, 2);
h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV1_WRITEREG;
else
h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_WRITEREG;
- h->cmdbuf[h->cmdidx++] = num;
+ h->cmdbuf[h->cmdidx++] = regsel;
h_u32_to_le(h->cmdbuf+h->cmdidx, val);
h->cmdidx += 4;
if (count < bytes_remaining)
bytes_remaining = count;
- if (h->st_mode == STLINK_MODE_DEBUG_SWIM) {
- retval = stlink_swim_readbytes(handle, addr, bytes_remaining, buffer);
- if (retval != ERROR_OK)
- return retval;
- } else
/*
* all stlink support 8/32bit memory read/writes and only from
* stlink V2J26 there is support for 16 bit memory read/write.
if (count < bytes_remaining)
bytes_remaining = count;
- if (h->st_mode == STLINK_MODE_DEBUG_SWIM) {
- retval = stlink_swim_writebytes(handle, addr, bytes_remaining, buffer);
- if (retval != ERROR_OK)
- return retval;
- } else
/*
* all stlink support 8/32bit memory read/writes and only from
* stlink V2J26 there is support for 16 bit memory read/write.
static int stlink_speed_swim(void *handle, int khz, bool query)
{
+ int retval;
+
/*
- we dont care what the khz rate is
we only have low and high speed...
before changing speed the SWIM_CSR HS bit
must be updated
*/
- if (khz == 0)
- stlink_swim_speed(handle, 0);
- else
- stlink_swim_speed(handle, 1);
- return khz;
+ if (!query) {
+ retval = stlink_swim_speed(handle, (khz < SWIM_FREQ_HIGH) ? 0 : 1);
+ if (retval != ERROR_OK)
+ LOG_ERROR("Unable to set adapter speed");
+ }
+
+ return (khz < SWIM_FREQ_HIGH) ? SWIM_FREQ_LOW : SWIM_FREQ_HIGH;
}
static int stlink_match_speed_map(const struct speed_map *map, unsigned int map_size, int khz, bool query)
return stlink_khz_to_speed_map_jtag[speed_index].speed;
}
-void stlink_dump_speed_map(const struct speed_map *map, unsigned int map_size)
+static void stlink_dump_speed_map(const struct speed_map *map, unsigned int map_size)
{
unsigned int i;
/** */
static int stlink_usb_close(void *handle)
{
- int res;
- uint8_t mode;
- enum stlink_mode emode;
struct stlink_usb_handle_s *h = handle;
- if (h && h->fd)
- res = stlink_usb_current_mode(handle, &mode);
- else
- res = ERROR_FAIL;
- /* do not exit if return code != ERROR_OK,
- it prevents us from closing jtag_libusb */
-
- if (res == ERROR_OK) {
- /* try to exit current mode */
- switch (mode) {
- case STLINK_DEV_DFU_MODE:
- emode = STLINK_MODE_DFU;
- break;
- case STLINK_DEV_DEBUG_MODE:
- emode = STLINK_MODE_DEBUG_SWD;
- break;
- case STLINK_DEV_SWIM_MODE:
- emode = STLINK_MODE_DEBUG_SWIM;
- break;
- case STLINK_DEV_BOOTLOADER_MODE:
- case STLINK_DEV_MASS_MODE:
- default:
- emode = STLINK_MODE_UNKNOWN;
- break;
- }
-
- if (emode != STLINK_MODE_UNKNOWN)
- stlink_usb_mode_leave(handle, emode);
- /* do not check return code, it prevent
- us from closing jtag_libusb */
- }
-
- if (h && h->fd)
+ if (h && h->fd) {
+ stlink_usb_exit_mode(h);
+ /* do not check return code, it prevent
+ us from closing jtag_libusb */
jtag_libusb_close(h->fd);
+ }
free(h);
* based on the length (0x1a = 26) we could easily decide if we have to fixup the serial
* and then we have just to convert the raw data into printable characters using sprintf
*/
-char *stlink_usb_get_alternate_serial(libusb_device_handle *device,
+static char *stlink_usb_get_alternate_serial(libusb_device_handle *device,
struct libusb_device_descriptor *dev_desc)
{
int usb_retval;
h->max_mem_packet = (1 << 10);
uint8_t buffer[4];
+ stlink_usb_open_ap(h, 0);
err = stlink_usb_read_mem32(h, CPUID, 4, buffer);
if (err == ERROR_OK) {
uint32_t cpuid = le_to_h_u32(buffer);
return stlink_usb_open(param, stlink_get_mode(param->transport), fd);
}
-int stlink_config_trace(void *handle, bool enabled,
+static int stlink_config_trace(void *handle, bool enabled,
enum tpiu_pin_protocol pin_protocol, uint32_t port_size,
unsigned int *trace_freq, unsigned int traceclkin_freq,
uint16_t *prescaler)
{
struct stlink_usb_handle_s *h = handle;
- uint16_t presc;
if (enabled && (!(h->version.flags & STLINK_F_HAS_TRACE) ||
pin_protocol != TPIU_PIN_PROTOCOL_ASYNC_UART)) {
return ERROR_FAIL;
}
- if (!enabled) {
- stlink_usb_trace_disable(h);
- return ERROR_OK;
- }
+ unsigned int max_trace_freq = (h->version.stlink == 3) ?
+ STLINK_V3_TRACE_MAX_HZ : STLINK_TRACE_MAX_HZ;
- if (*trace_freq > STLINK_TRACE_MAX_HZ) {
+ /* Only concern ourselves with the frequency if the STlink is processing it. */
+ if (enabled && *trace_freq > max_trace_freq) {
LOG_ERROR("ST-LINK doesn't support SWO frequency higher than %u",
- STLINK_TRACE_MAX_HZ);
+ max_trace_freq);
return ERROR_FAIL;
}
stlink_usb_trace_disable(h);
if (!*trace_freq)
- *trace_freq = STLINK_TRACE_MAX_HZ;
+ *trace_freq = max_trace_freq;
- presc = traceclkin_freq / *trace_freq;
-
- if (traceclkin_freq % *trace_freq > 0)
- presc++;
+ unsigned int presc = (traceclkin_freq + *trace_freq / 2) / *trace_freq;
+ if (presc == 0 || presc > TPIU_ACPR_MAX_SWOSCALER + 1) {
+ LOG_ERROR("SWO frequency is not suitable. Please choose a different "
+ "frequency.");
+ return ERROR_FAIL;
+ }
- if (presc > TPIU_ACPR_MAX_SWOSCALER) {
+ /* Probe's UART speed must be within 3% of the TPIU's SWO baud rate. */
+ unsigned int max_deviation = (traceclkin_freq * 3) / 100;
+ if (presc * *trace_freq < traceclkin_freq - max_deviation ||
+ presc * *trace_freq > traceclkin_freq + max_deviation) {
LOG_ERROR("SWO frequency is not suitable. Please choose a different "
"frequency.");
return ERROR_FAIL;
}
*prescaler = presc;
+
+ if (!enabled)
+ return ERROR_OK;
+
h->trace.source_hz = *trace_freq;
return stlink_usb_trace_enable(h);
h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_CLOSE_AP_DBG;
h->cmdbuf[h->cmdidx++] = ap_num;
- return stlink_usb_xfer_errcheck(handle, h->databuf, 2);
+ /* ignore incorrectly returned error on bogus FW */
+ if (h->version.flags & STLINK_F_FIX_CLOSE_AP)
+ return stlink_usb_xfer_errcheck(handle, h->databuf, 2);
+ else
+ return stlink_usb_xfer_noerrcheck(handle, h->databuf, 2);
+
}
/** */
retval = stlink_usb_xfer_errcheck(handle, h->databuf, 8);
*val = le_to_h_u32(h->databuf + 4);
- LOG_DEBUG_IO("dap_port_read = %d, addr = 0x%x, value = 0x%x", dap_port, addr, *val);
+ LOG_DEBUG_IO("dap_port_read = %d, addr = 0x%x, value = 0x%" PRIx32, dap_port, addr, *val);
return retval;
}
if (!(h->version.flags & STLINK_F_HAS_DAP_REG))
return ERROR_COMMAND_NOTFOUND;
- LOG_DEBUG_IO("dap_write port = %d, addr = 0x%x, value = 0x%x", dap_port, addr, val);
+ LOG_DEBUG_IO("dap_write port = %d, addr = 0x%x, value = 0x%" PRIx32, dap_port, addr, val);
stlink_usb_init_buffer(handle, h->rx_ep, 16);
h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_WRITE_DAP_REG;
return retval;
}
-/** */
-static int stlink_dap_open_ap(unsigned short apsel)
+static int stlink_usb_open_ap(void *handle, unsigned short apsel)
{
+ struct stlink_usb_handle_s *h = handle;
int retval;
/* nothing to do on old versions */
- if (!(stlink_dap_handle->version.flags & STLINK_F_HAS_AP_INIT))
+ if (!(h->version.flags & STLINK_F_HAS_AP_INIT))
return ERROR_OK;
if (apsel > DP_APSEL_MAX)
if (test_bit(apsel, opened_ap))
return ERROR_OK;
- retval = stlink_usb_init_access_port(stlink_dap_handle, apsel);
+ retval = stlink_usb_init_access_port(h, apsel);
if (retval != ERROR_OK)
return retval;
return ERROR_OK;
}
+static int stlink_dap_open_ap(unsigned short apsel)
+{
+ return stlink_usb_open_ap(stlink_dap_handle, apsel);
+}
+
/** */
static int stlink_dap_closeall_ap(void)
{
LOG_ERROR("Error closing APs");
}
+static int stlink_swim_op_srst(void)
+{
+ return stlink_swim_generate_rst(stlink_dap_handle);
+}
+
+static int stlink_swim_op_read_mem(uint32_t addr, uint32_t size,
+ uint32_t count, uint8_t *buffer)
+{
+ int retval;
+ uint32_t bytes_remaining;
+
+ LOG_DEBUG_IO("read at 0x%08" PRIx32 " len %" PRIu32 "*0x%08" PRIx32, addr, size, count);
+ count *= size;
+
+ while (count) {
+ bytes_remaining = (count > STLINK_DATA_SIZE) ? STLINK_DATA_SIZE : count;
+ retval = stlink_swim_readbytes(stlink_dap_handle, addr, bytes_remaining, buffer);
+ if (retval != ERROR_OK)
+ return retval;
+
+ buffer += bytes_remaining;
+ addr += bytes_remaining;
+ count -= bytes_remaining;
+ }
+
+ return ERROR_OK;
+}
+
+static int stlink_swim_op_write_mem(uint32_t addr, uint32_t size,
+ uint32_t count, const uint8_t *buffer)
+{
+ int retval;
+ uint32_t bytes_remaining;
+
+ LOG_DEBUG_IO("write at 0x%08" PRIx32 " len %" PRIu32 "*0x%08" PRIx32, addr, size, count);
+ count *= size;
+
+ while (count) {
+ bytes_remaining = (count > STLINK_DATA_SIZE) ? STLINK_DATA_SIZE : count;
+ retval = stlink_swim_writebytes(stlink_dap_handle, addr, bytes_remaining, buffer);
+ if (retval != ERROR_OK)
+ return retval;
+
+ buffer += bytes_remaining;
+ addr += bytes_remaining;
+ count -= bytes_remaining;
+ }
+
+ return ERROR_OK;
+}
+
+static int stlink_swim_op_reconnect(void)
+{
+ int retval;
+
+ retval = stlink_usb_mode_enter(stlink_dap_handle, STLINK_MODE_DEBUG_SWIM);
+ if (retval != ERROR_OK)
+ return retval;
+
+ return stlink_swim_resync(stlink_dap_handle);
+}
+
static int stlink_dap_config_trace(bool enabled,
enum tpiu_pin_protocol pin_protocol, uint32_t port_size,
unsigned int *trace_freq, unsigned int traceclkin_freq,
mode = STLINK_MODE_DEBUG_SWD;
else if (transport_is_dapdirect_jtag())
mode = STLINK_MODE_DEBUG_JTAG;
+ else if (transport_is_swim())
+ mode = STLINK_MODE_DEBUG_SWIM;
else {
LOG_ERROR("Unsupported transport");
return ERROR_FAIL;
if (retval != ERROR_OK)
return retval;
- if (!(stlink_dap_handle->version.flags & STLINK_F_HAS_DAP_REG)) {
+ if ((mode != STLINK_MODE_DEBUG_SWIM) &&
+ !(stlink_dap_handle->version.flags & STLINK_F_HAS_DAP_REG)) {
LOG_ERROR("ST-Link version does not support DAP direct transport");
return ERROR_FAIL;
}
.quit = stlink_dap_op_quit, /* optional */
};
-static const char *const stlink_dap_transport[] = { "dapdirect_jtag", "dapdirect_swd", NULL };
+static const struct swim_driver stlink_swim_ops = {
+ .srst = stlink_swim_op_srst,
+ .read_mem = stlink_swim_op_read_mem,
+ .write_mem = stlink_swim_op_write_mem,
+ .reconnect = stlink_swim_op_reconnect,
+};
+
+static const char *const stlink_dap_transport[] = { "dapdirect_swd", "dapdirect_jtag", "swim", NULL };
struct adapter_driver stlink_dap_adapter_driver = {
.name = "st-link",
.dap_jtag_ops = &stlink_dap_ops,
.dap_swd_ops = &stlink_dap_ops,
+ .swim_ops = &stlink_swim_ops,
};