X-Git-Url: https://review.openocd.org/gitweb?p=openocd.git;a=blobdiff_plain;f=src%2Fjtag%2Fdrivers%2Fstlink_usb.c;h=9ba4f4af987212360281eac2f0b8b279d94296be;hp=b54fc4d7377ed2cf3ca8a8281eaae9c7ad555c1f;hb=d46b28983dc27369e90f4dc8a0e54b5057583b38;hpb=d940a3224b3c06e6008c03d8dcb2682ea4135b7a diff --git a/src/jtag/drivers/stlink_usb.c b/src/jtag/drivers/stlink_usb.c index b54fc4d737..9ba4f4af98 100644 --- a/src/jtag/drivers/stlink_usb.c +++ b/src/jtag/drivers/stlink_usb.c @@ -1,4 +1,8 @@ /*************************************************************************** + * SWIM contributions by Ake Rehnman * + * Copyright (C) 2017 Ake Rehnman * + * ake.rehnman(at)gmail.com * + * * * Copyright (C) 2011-2012 by Mathias Kuester * * Mathias Kuester * * * @@ -18,9 +22,7 @@ * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * + * along with this program. If not, see . * ***************************************************************************/ #ifdef HAVE_CONFIG_H @@ -39,6 +41,10 @@ #include "libusb_common.h" +#ifdef HAVE_LIBUSB1 +#define USE_LIBUSB_ASYNCIO +#endif + #define ENDPOINT_IN 0x80 #define ENDPOINT_OUT 0x00 @@ -61,10 +67,19 @@ #define STLINK_V1_PID (0x3744) #define STLINK_V2_PID (0x3748) #define STLINK_V2_1_PID (0x374B) - -/* the current implementation of the stlink limits - * 8bit read/writes to max 64 bytes. */ +#define STLINK_V2_1_NO_MSD_PID (0x3752) +#define STLINK_V3_USBLOADER_PID (0x374D) +#define STLINK_V3E_PID (0x374E) +#define STLINK_V3S_PID (0x374F) +#define STLINK_V3_2VCP_PID (0x3753) + +/* + * ST-Link/V1, ST-Link/V2 and ST-Link/V2.1 are full-speed USB devices and + * this limits the bulk packet size and the 8bit read/writes to max 64 bytes. + * STLINK-V3 is a high speed USB 2.0 and the limit is 512 bytes. + */ #define STLINK_MAX_RW8 (64) +#define STLINKV3_MAX_RW8 (512) /* "WAIT" responses will be retried (with exponential backoff) at * most this many times before failing to caller. @@ -74,6 +89,7 @@ enum stlink_jtag_api_version { STLINK_JTAG_API_V1 = 1, STLINK_JTAG_API_V2, + STLINK_JTAG_API_V3, }; /** */ @@ -84,8 +100,10 @@ struct stlink_usb_version { int jtag; /** */ int swim; - /** highest supported jtag api version */ - enum stlink_jtag_api_version jtag_api_max; + /** jtag api version supported */ + enum stlink_jtag_api_version jtag_api; + /** one bit for each feature supported. See macros STLINK_F_* */ + uint32_t flags; }; /** */ @@ -118,28 +136,39 @@ struct stlink_usb_handle_s { uint16_t vid; /** */ uint16_t pid; - /** this is the currently used jtag api */ - enum stlink_jtag_api_version jtag_api; /** */ struct { /** whether SWO tracing is enabled or not */ bool enabled; - /** trace data destination file */ - FILE *output_f; - /** trace module source clock (for prescaler) */ + /** trace module source clock */ uint32_t source_hz; - /** trace module clock prescaler */ - uint32_t prescale; } trace; /** reconnect is needed next time we try to query the * status */ bool reconnect_pending; }; +#define STLINK_SWIM_ERR_OK 0x00 +#define STLINK_SWIM_BUSY 0x01 #define STLINK_DEBUG_ERR_OK 0x80 #define STLINK_DEBUG_ERR_FAULT 0x81 #define STLINK_SWD_AP_WAIT 0x10 +#define STLINK_SWD_AP_FAULT 0x11 +#define STLINK_SWD_AP_ERROR 0x12 +#define STLINK_SWD_AP_PARITY_ERROR 0x13 +#define STLINK_JTAG_GET_IDCODE_ERROR 0x09 +#define STLINK_JTAG_WRITE_ERROR 0x0c +#define STLINK_JTAG_WRITE_VERIF_ERROR 0x0d #define STLINK_SWD_DP_WAIT 0x14 +#define STLINK_SWD_DP_FAULT 0x15 +#define STLINK_SWD_DP_ERROR 0x16 +#define STLINK_SWD_DP_PARITY_ERROR 0x17 + +#define STLINK_SWD_AP_WDATA_ERROR 0x18 +#define STLINK_SWD_AP_STICKY_ERROR 0x19 +#define STLINK_SWD_AP_STICKYORUN_ERROR 0x1a + +#define STLINK_BAD_AP_ERROR 0x1d #define STLINK_CORE_RUNNING 0x80 #define STLINK_CORE_HALTED 0x81 @@ -161,10 +190,37 @@ struct stlink_usb_handle_s { #define STLINK_DFU_EXIT 0x07 -#define STLINK_SWIM_ENTER 0x00 -#define STLINK_SWIM_EXIT 0x01 +/* + STLINK_SWIM_ENTER_SEQ + 1.3ms low then 750Hz then 1.5kHz + + STLINK_SWIM_GEN_RST + STM8 DM pulls reset pin low 50us + + STLINK_SWIM_SPEED + uint8_t (0=low|1=high) + + STLINK_SWIM_WRITEMEM + uint16_t length + uint32_t address + + STLINK_SWIM_RESET + send syncronization seq (16us low, response 64 clocks low) +*/ +#define STLINK_SWIM_ENTER 0x00 +#define STLINK_SWIM_EXIT 0x01 +#define STLINK_SWIM_READ_CAP 0x02 +#define STLINK_SWIM_SPEED 0x03 +#define STLINK_SWIM_ENTER_SEQ 0x04 +#define STLINK_SWIM_GEN_RST 0x05 +#define STLINK_SWIM_RESET 0x06 +#define STLINK_SWIM_ASSERT_RESET 0x07 +#define STLINK_SWIM_DEASSERT_RESET 0x08 +#define STLINK_SWIM_READSTATUS 0x09 +#define STLINK_SWIM_WRITEMEM 0x0a +#define STLINK_SWIM_READMEM 0x0b +#define STLINK_SWIM_READBUF 0x0c -#define STLINK_DEBUG_ENTER_JTAG 0x00 #define STLINK_DEBUG_GETSTATUS 0x01 #define STLINK_DEBUG_FORCEDEBUG 0x02 #define STLINK_DEBUG_APIV1_RESETSYS 0x03 @@ -182,8 +238,9 @@ struct stlink_usb_handle_s { #define STLINK_DEBUG_APIV1_WRITEDEBUGREG 0x0f #define STLINK_DEBUG_APIV1_SETWATCHPOINT 0x10 -#define STLINK_DEBUG_ENTER_JTAG 0x00 -#define STLINK_DEBUG_ENTER_SWD 0xa3 +#define STLINK_DEBUG_ENTER_JTAG_RESET 0x00 +#define STLINK_DEBUG_ENTER_SWD_NO_RESET 0xa3 +#define STLINK_DEBUG_ENTER_JTAG_NO_RESET 0xa4 #define STLINK_DEBUG_APIV1_ENTER 0x20 #define STLINK_DEBUG_EXIT 0x21 @@ -201,18 +258,30 @@ struct stlink_usb_handle_s { #define STLINK_DEBUG_APIV2_GETLASTRWSTATUS 0x3B #define STLINK_DEBUG_APIV2_DRIVE_NRST 0x3C +#define STLINK_DEBUG_APIV2_GETLASTRWSTATUS2 0x3E + #define STLINK_DEBUG_APIV2_START_TRACE_RX 0x40 #define STLINK_DEBUG_APIV2_STOP_TRACE_RX 0x41 #define STLINK_DEBUG_APIV2_GET_TRACE_NB 0x42 #define STLINK_DEBUG_APIV2_SWD_SET_FREQ 0x43 +#define STLINK_DEBUG_APIV2_JTAG_SET_FREQ 0x44 + +#define STLINK_DEBUG_APIV2_READMEM_16BIT 0x47 +#define STLINK_DEBUG_APIV2_WRITEMEM_16BIT 0x48 + +#define STLINK_APIV3_SET_COM_FREQ 0x61 +#define STLINK_APIV3_GET_COM_FREQ 0x62 + +#define STLINK_APIV3_GET_VERSION_EX 0xFB #define STLINK_DEBUG_APIV2_DRIVE_NRST_LOW 0x00 #define STLINK_DEBUG_APIV2_DRIVE_NRST_HIGH 0x01 #define STLINK_DEBUG_APIV2_DRIVE_NRST_PULSE 0x02 -#define STLINK_TRACE_SIZE 1024 +#define STLINK_TRACE_SIZE 4096 #define STLINK_TRACE_MAX_HZ 2000000 -#define STLINK_TRACE_MIN_VERSION 13 + +#define STLINK_V3_MAX_FREQ_NB 10 /** */ enum stlink_mode { @@ -227,10 +296,26 @@ enum stlink_mode { #define REQUEST_SENSE 0x03 #define REQUEST_SENSE_LENGTH 18 -static const struct { +/* + * Map the relevant features, quirks and workaround for specific firmware + * version of stlink + */ +#define STLINK_F_HAS_TRACE (1UL << 0) +#define STLINK_F_HAS_SWD_SET_FREQ (1UL << 1) +#define STLINK_F_HAS_JTAG_SET_FREQ (1UL << 2) +#define STLINK_F_HAS_MEM_16BIT (1UL << 3) +#define STLINK_F_HAS_GETLASTRWSTATUS2 (1UL << 4) + +/* aliases */ +#define STLINK_F_HAS_TARGET_VOLT STLINK_F_HAS_TRACE + +struct speed_map { int speed; int speed_divisor; -} stlink_khz_to_speed_map[] = { +}; + +/* SWD clock speed */ +static const struct speed_map stlink_khz_to_speed_map_swd[] = { {4000, 0}, {1800, 1}, /* default */ {1200, 2}, @@ -245,7 +330,187 @@ static const struct { {5, 798} }; +/* JTAG clock speed */ +static const struct speed_map stlink_khz_to_speed_map_jtag[] = { + {18000, 2}, + {9000, 4}, + {4500, 8}, + {2250, 16}, + {1125, 32}, /* default */ + {562, 64}, + {281, 128}, + {140, 256} +}; + static void stlink_usb_init_buffer(void *handle, uint8_t direction, uint32_t size); +static int stlink_swim_status(void *handle); + +/** */ +static unsigned int stlink_usb_block(void *handle) +{ + struct stlink_usb_handle_s *h = handle; + + assert(handle != NULL); + + if (h->version.stlink == 3) + return STLINKV3_MAX_RW8; + else + return STLINK_MAX_RW8; +} + + + +#ifdef USE_LIBUSB_ASYNCIO + +static LIBUSB_CALL void sync_transfer_cb(struct libusb_transfer *transfer) +{ + int *completed = transfer->user_data; + *completed = 1; + /* caller interprets result and frees transfer */ +} + + +static void sync_transfer_wait_for_completion(struct libusb_transfer *transfer) +{ + int r, *completed = transfer->user_data; + + /* Assuming a single libusb context exists. There no existing interface into this + * module to pass a libusb context. + */ + struct libusb_context *ctx = NULL; + + while (!*completed) { + r = libusb_handle_events_completed(ctx, completed); + if (r < 0) { + if (r == LIBUSB_ERROR_INTERRUPTED) + continue; + libusb_cancel_transfer(transfer); + continue; + } + } +} + + +static int transfer_error_status(const struct libusb_transfer *transfer) +{ + int r = 0; + + switch (transfer->status) { + case LIBUSB_TRANSFER_COMPLETED: + r = 0; + break; + case LIBUSB_TRANSFER_TIMED_OUT: + r = LIBUSB_ERROR_TIMEOUT; + break; + case LIBUSB_TRANSFER_STALL: + r = LIBUSB_ERROR_PIPE; + break; + case LIBUSB_TRANSFER_OVERFLOW: + r = LIBUSB_ERROR_OVERFLOW; + break; + case LIBUSB_TRANSFER_NO_DEVICE: + r = LIBUSB_ERROR_NO_DEVICE; + break; + case LIBUSB_TRANSFER_ERROR: + case LIBUSB_TRANSFER_CANCELLED: + r = LIBUSB_ERROR_IO; + break; + default: + r = LIBUSB_ERROR_OTHER; + break; + } + + return r; +} + +struct jtag_xfer { + int ep; + uint8_t *buf; + size_t size; + /* Internal */ + int retval; + int completed; + size_t transfer_size; + struct libusb_transfer *transfer; +}; + +static int jtag_libusb_bulk_transfer_n( + jtag_libusb_device_handle * dev_handle, + struct jtag_xfer *transfers, + size_t n_transfers, + int timeout) +{ + int retval = 0; + int returnval = ERROR_OK; + + + for (size_t i = 0; i < n_transfers; ++i) { + transfers[i].retval = 0; + transfers[i].completed = 0; + transfers[i].transfer_size = 0; + transfers[i].transfer = libusb_alloc_transfer(0); + + if (transfers[i].transfer == NULL) { + for (size_t j = 0; j < i; ++j) + libusb_free_transfer(transfers[j].transfer); + + LOG_DEBUG("ERROR, failed to alloc usb transfers"); + for (size_t k = 0; k < n_transfers; ++k) + transfers[k].retval = LIBUSB_ERROR_NO_MEM; + return ERROR_FAIL; + } + } + + for (size_t i = 0; i < n_transfers; ++i) { + libusb_fill_bulk_transfer( + transfers[i].transfer, + dev_handle, + transfers[i].ep, transfers[i].buf, transfers[i].size, + sync_transfer_cb, &transfers[i].completed, timeout); + transfers[i].transfer->type = LIBUSB_TRANSFER_TYPE_BULK; + + retval = libusb_submit_transfer(transfers[i].transfer); + if (retval < 0) { + LOG_DEBUG("ERROR, failed to submit transfer %zu, error %d", i, retval); + + /* Probably no point continuing to submit transfers once a submission fails. + * As a result, tag all remaining transfers as errors. + */ + for (size_t j = i; j < n_transfers; ++j) + transfers[j].retval = retval; + + returnval = ERROR_FAIL; + break; + } + } + + /* Wait for every submitted USB transfer to complete. + */ + for (size_t i = 0; i < n_transfers; ++i) { + if (transfers[i].retval == 0) { + sync_transfer_wait_for_completion(transfers[i].transfer); + + retval = transfer_error_status(transfers[i].transfer); + if (retval) { + returnval = ERROR_FAIL; + transfers[i].retval = retval; + LOG_DEBUG("ERROR, transfer %zu failed, error %d", i, retval); + } else { + /* Assuming actual_length is only valid if there is no transfer error. + */ + transfers[i].transfer_size = transfers[i].transfer->actual_length; + } + } + + libusb_free_transfer(transfers[i].transfer); + transfers[i].transfer = NULL; + } + + return returnval; +} + +#endif + /** */ static int stlink_usb_xfer_v1_get_status(void *handle) @@ -280,7 +545,45 @@ static int stlink_usb_xfer_v1_get_status(void *handle) return ERROR_OK; } -/** */ +#ifdef USE_LIBUSB_ASYNCIO +static int stlink_usb_xfer_rw(void *handle, int cmdsize, const uint8_t *buf, int size) +{ + struct stlink_usb_handle_s *h = handle; + + assert(handle != NULL); + + size_t n_transfers = 0; + struct jtag_xfer transfers[2]; + + memset(transfers, 0, sizeof(transfers)); + + transfers[0].ep = h->tx_ep; + transfers[0].buf = h->cmdbuf; + transfers[0].size = cmdsize; + + ++n_transfers; + + if (h->direction == h->tx_ep && size) { + transfers[1].ep = h->tx_ep; + transfers[1].buf = (uint8_t *)buf; + transfers[1].size = size; + + ++n_transfers; + } else if (h->direction == h->rx_ep && size) { + transfers[1].ep = h->rx_ep; + transfers[1].buf = (uint8_t *)buf; + transfers[1].size = size; + + ++n_transfers; + } + + return jtag_libusb_bulk_transfer_n( + h->fd, + transfers, + n_transfers, + STLINK_WRITE_TIMEOUT); +} +#else static int stlink_usb_xfer_rw(void *handle, int cmdsize, const uint8_t *buf, int size) { struct stlink_usb_handle_s *h = handle; @@ -308,6 +611,7 @@ static int stlink_usb_xfer_rw(void *handle, int cmdsize, const uint8_t *buf, int return ERROR_OK; } +#endif /** */ static int stlink_usb_xfer_v1_get_sense(void *handle) @@ -336,7 +640,11 @@ static int stlink_usb_xfer_v1_get_sense(void *handle) return ERROR_OK; } -/** */ +/* + transfers block in cmdbuf + indicates number of bytes in the following + data phase. +*/ static int stlink_usb_xfer(void *handle, const uint8_t *buf, int size) { int err, cmdsize = STLINK_CMD_SIZE_V2; @@ -344,8 +652,11 @@ static int stlink_usb_xfer(void *handle, const uint8_t *buf, int size) assert(handle != NULL); - if (h->version.stlink == 1) + if (h->version.stlink == 1) { cmdsize = STLINK_SG_SIZE; + /* put length in bCBWCBLength */ + h->cmdbuf[14] = h->cmdidx-15; + } err = stlink_usb_xfer_rw(handle, cmdsize, buf, size); @@ -367,7 +678,6 @@ static int stlink_usb_xfer(void *handle, const uint8_t *buf, int size) return ERROR_OK; } - /** Converts an STLINK status code held in the first byte of a response to an openocd error, logs any error/wait status as debug output. @@ -378,8 +688,20 @@ static int stlink_usb_error_check(void *handle) assert(handle != NULL); + if (h->transport == HL_TRANSPORT_SWIM) { + switch (h->databuf[0]) { + case STLINK_SWIM_ERR_OK: + return ERROR_OK; + case STLINK_SWIM_BUSY: + return ERROR_WAIT; + default: + LOG_DEBUG("unknown/unexpected STLINK status code 0x%x", h->databuf[0]); + return ERROR_FAIL; + } + } + /* TODO: no error checking yet on api V1 */ - if (h->jtag_api == STLINK_JTAG_API_V1) + if (h->version.jtag_api == STLINK_JTAG_API_V1) h->databuf[0] = STLINK_DEBUG_ERR_OK; switch (h->databuf[0]) { @@ -392,8 +714,52 @@ static int stlink_usb_error_check(void *handle) LOG_DEBUG("wait status SWD_AP_WAIT (0x%x)", STLINK_SWD_AP_WAIT); return ERROR_WAIT; case STLINK_SWD_DP_WAIT: - LOG_DEBUG("wait status SWD_DP_WAIT (0x%x)", STLINK_SWD_AP_WAIT); + LOG_DEBUG("wait status SWD_DP_WAIT (0x%x)", STLINK_SWD_DP_WAIT); return ERROR_WAIT; + case STLINK_JTAG_GET_IDCODE_ERROR: + LOG_DEBUG("STLINK_JTAG_GET_IDCODE_ERROR"); + return ERROR_FAIL; + case STLINK_JTAG_WRITE_ERROR: + LOG_DEBUG("Write error"); + return ERROR_FAIL; + case STLINK_JTAG_WRITE_VERIF_ERROR: + LOG_DEBUG("Write verify error, ignoring"); + return ERROR_OK; + case STLINK_SWD_AP_FAULT: + /* git://git.ac6.fr/openocd commit 657e3e885b9ee10 + * returns ERROR_OK with the comment: + * Change in error status when reading outside RAM. + * This fix allows CDT plugin to visualize memory. + */ + LOG_DEBUG("STLINK_SWD_AP_FAULT"); + return ERROR_FAIL; + case STLINK_SWD_AP_ERROR: + LOG_DEBUG("STLINK_SWD_AP_ERROR"); + return ERROR_FAIL; + case STLINK_SWD_AP_PARITY_ERROR: + LOG_DEBUG("STLINK_SWD_AP_PARITY_ERROR"); + return ERROR_FAIL; + case STLINK_SWD_DP_FAULT: + LOG_DEBUG("STLINK_SWD_DP_FAULT"); + return ERROR_FAIL; + case STLINK_SWD_DP_ERROR: + LOG_DEBUG("STLINK_SWD_DP_ERROR"); + return ERROR_FAIL; + case STLINK_SWD_DP_PARITY_ERROR: + LOG_DEBUG("STLINK_SWD_DP_PARITY_ERROR"); + return ERROR_FAIL; + case STLINK_SWD_AP_WDATA_ERROR: + LOG_DEBUG("STLINK_SWD_AP_WDATA_ERROR"); + return ERROR_FAIL; + case STLINK_SWD_AP_STICKY_ERROR: + LOG_DEBUG("STLINK_SWD_AP_STICKY_ERROR"); + return ERROR_FAIL; + case STLINK_SWD_AP_STICKYORUN_ERROR: + LOG_DEBUG("STLINK_SWD_AP_STICKYORUN_ERROR"); + return ERROR_FAIL; + case STLINK_BAD_AP_ERROR: + LOG_DEBUG("STLINK_BAD_AP_ERROR"); + return ERROR_FAIL; default: LOG_DEBUG("unknown/unexpected STLINK status code 0x%x", h->databuf[0]); return ERROR_FAIL; @@ -404,7 +770,7 @@ static int stlink_usb_error_check(void *handle) /** Issue an STLINK command via USB transfer, with retries on any wait status responses. Works for commands where the STLINK_DEBUG status is returned in the first - byte of the response packet. + byte of the response packet. For SWIM a SWIM_READSTATUS is requested instead. Returns an openocd result code. */ @@ -412,13 +778,26 @@ static int stlink_cmd_allow_retry(void *handle, const uint8_t *buf, int size) { int retries = 0; int res; + struct stlink_usb_handle_s *h = handle; + while (1) { - res = stlink_usb_xfer(handle, buf, size); - if (res != ERROR_OK) - return res; + if ((h->transport != HL_TRANSPORT_SWIM) || !retries) { + res = stlink_usb_xfer(handle, buf, size); + if (res != ERROR_OK) + return res; + } + + if (h->transport == HL_TRANSPORT_SWIM) { + res = stlink_swim_status(handle); + if (res != ERROR_OK) + return res; + } + res = stlink_usb_error_check(handle); if (res == ERROR_WAIT && retries < MAX_WAIT_RETRIES) { - usleep((1<version.stlink >= 2); + assert(h->version.flags & STLINK_F_HAS_TRACE); if (jtag_libusb_bulk_read(h->fd, h->trace_ep, (char *)buf, size, STLINK_READ_TIMEOUT) != size) { @@ -443,7 +822,17 @@ static int stlink_usb_read_trace(void *handle, const uint8_t *buf, int size) return ERROR_OK; } -/** */ +/* + this function writes transfer length in + the right place in the cb +*/ +static void stlink_usb_set_cbw_transfer_datalength(void *handle, uint32_t size) +{ + struct stlink_usb_handle_s *h = handle; + + buf_set_u32(h->cmdbuf+8, 0, 32, size); +} + static void stlink_usb_xfer_v1_create_cmd(void *handle, uint8_t direction, uint32_t size) { struct stlink_usb_handle_s *h = handle; @@ -452,12 +841,16 @@ static void stlink_usb_xfer_v1_create_cmd(void *handle, uint8_t direction, uint3 strcpy((char *)h->cmdbuf, "USBC"); h->cmdidx += 4; /* csw tag not used */ + buf_set_u32(h->cmdbuf+h->cmdidx, 0, 32, 0); h->cmdidx += 4; + /* cbw data transfer length (in the following data phase in or out) */ buf_set_u32(h->cmdbuf+h->cmdidx, 0, 32, size); h->cmdidx += 4; + /* cbw flags */ h->cmdbuf[h->cmdidx++] = (direction == h->rx_ep ? ENDPOINT_IN : ENDPOINT_OUT); h->cmdbuf[h->cmdidx++] = 0; /* lun */ - h->cmdbuf[h->cmdidx++] = STLINK_CMD_SIZE_V1; + /* cdb clength (is filled in at xfer) */ + h->cmdbuf[h->cmdidx++] = 0; } /** */ @@ -480,7 +873,11 @@ static void stlink_usb_init_buffer(void *handle, uint8_t direction, uint32_t siz static int stlink_usb_version(void *handle) { int res; - uint16_t v; + uint32_t flags; + uint16_t version; + uint8_t v, x, y, jtag, swim, msd, bridge = 0; + char v_str[5 * (1 + 3) + 1]; /* VvJjMmBbSs */ + char *p; struct stlink_usb_handle_s *h = handle; assert(handle != NULL); @@ -494,27 +891,131 @@ static int stlink_usb_version(void *handle) if (res != ERROR_OK) return res; - v = (h->databuf[0] << 8) | h->databuf[1]; + version = be_to_h_u16(h->databuf); + v = (version >> 12) & 0x0f; + x = (version >> 6) & 0x3f; + y = version & 0x3f; + + h->vid = le_to_h_u16(h->databuf + 2); + h->pid = le_to_h_u16(h->databuf + 4); + + switch (h->pid) { + case STLINK_V2_1_PID: + case STLINK_V2_1_NO_MSD_PID: + if ((x <= 22 && y == 7) || (x >= 25 && y >= 7 && y <= 12)) { + /* MxSy : STM8 V2.1 - SWIM only */ + msd = x; + swim = y; + jtag = 0; + } else { + /* JxMy : STM32 V2.1 - JTAG/SWD only */ + jtag = x; + msd = y; + swim = 0; + } + break; + default: + jtag = x; + swim = y; + msd = 0; + break; + } - h->version.stlink = (v >> 12) & 0x0f; - h->version.jtag = (v >> 6) & 0x3f; - h->version.swim = v & 0x3f; - h->vid = buf_get_u32(h->databuf, 16, 16); - h->pid = buf_get_u32(h->databuf, 32, 16); + /* STLINK-V3 requires a specific command */ + if (v == 3 && x == 0 && y == 0) { + stlink_usb_init_buffer(handle, h->rx_ep, 16); - /* set the supported jtag api version - * API V2 is supported since JTAG V11 - */ - if (h->version.jtag >= 11) - h->version.jtag_api_max = STLINK_JTAG_API_V2; - else - h->version.jtag_api_max = STLINK_JTAG_API_V1; + h->cmdbuf[h->cmdidx++] = STLINK_APIV3_GET_VERSION_EX; - LOG_INFO("STLINK v%d JTAG v%d API v%d SWIM v%d VID 0x%04X PID 0x%04X", - h->version.stlink, - h->version.jtag, - (h->version.jtag_api_max == STLINK_JTAG_API_V1) ? 1 : 2, - h->version.swim, + res = stlink_usb_xfer(handle, h->databuf, 12); + if (res != ERROR_OK) + return res; + + v = h->databuf[0]; + swim = h->databuf[1]; + jtag = h->databuf[2]; + msd = h->databuf[3]; + bridge = h->databuf[4]; + h->vid = le_to_h_u16(h->databuf + 8); + h->pid = le_to_h_u16(h->databuf + 10); + } + + h->version.stlink = v; + h->version.jtag = jtag; + h->version.swim = swim; + + flags = 0; + switch (h->version.stlink) { + case 1: + /* ST-LINK/V1 from J11 switch to api-v2 (and support SWD) */ + if (h->version.jtag >= 11) + h->version.jtag_api = STLINK_JTAG_API_V2; + else + h->version.jtag_api = STLINK_JTAG_API_V1; + + break; + case 2: + /* all ST-LINK/V2 and ST-Link/V2.1 use api-v2 */ + h->version.jtag_api = STLINK_JTAG_API_V2; + + /* API for trace from J13 */ + /* API for target voltage from J13 */ + if (h->version.jtag >= 13) + flags |= STLINK_F_HAS_TRACE; + + /* preferred API to get last R/W status from J15 */ + if (h->version.jtag >= 15) + flags |= STLINK_F_HAS_GETLASTRWSTATUS2; + + /* API to set SWD frequency from J22 */ + if (h->version.jtag >= 22) + flags |= STLINK_F_HAS_SWD_SET_FREQ; + + /* API to set JTAG frequency from J24 */ + if (h->version.jtag >= 24) + flags |= STLINK_F_HAS_JTAG_SET_FREQ; + + /* API to read/write memory at 16 bit from J26 */ + if (h->version.jtag >= 26) + flags |= STLINK_F_HAS_MEM_16BIT; + + break; + case 3: + /* all STLINK-V3 use api-v3 */ + h->version.jtag_api = STLINK_JTAG_API_V3; + + /* STLINK-V3 is a superset of ST-LINK/V2 */ + + /* API for trace */ + /* API for target voltage */ + flags |= STLINK_F_HAS_TRACE; + + /* preferred API to get last R/W status */ + flags |= STLINK_F_HAS_GETLASTRWSTATUS2; + + /* API to read/write memory at 16 bit */ + flags |= STLINK_F_HAS_MEM_16BIT; + + break; + default: + break; + } + h->version.flags = flags; + + p = v_str; + p += sprintf(p, "V%d", v); + if (jtag || !msd) + p += sprintf(p, "J%d", jtag); + if (msd) + p += sprintf(p, "M%d", msd); + if (bridge) + p += sprintf(p, "B%d", bridge); + if (swim || !msd) + sprintf(p, "S%d", swim); + + LOG_INFO("STLINK %s (API v%d) VID:PID %04X:%04X", + v_str, + h->version.jtag_api, h->vid, h->pid); @@ -526,8 +1027,8 @@ static int stlink_usb_check_voltage(void *handle, float *target_voltage) struct stlink_usb_handle_s *h = handle; uint32_t adc_results[2]; - /* only supported by stlink/v2 and for firmware >= 13 */ - if (h->version.stlink == 1 || h->version.jtag < 13) + /* no error message, simply quit with error */ + if (!(h->version.flags & STLINK_F_HAS_TARGET_VOLT)) return ERROR_COMMAND_NOTFOUND; stlink_usb_init_buffer(handle, h->rx_ep, 8); @@ -559,8 +1060,7 @@ static int stlink_usb_set_swdclk(void *handle, uint16_t clk_divisor) assert(handle != NULL); - /* only supported by stlink/v2 and for firmware >= 22 */ - if (h->version.stlink == 1 || h->version.jtag < 22) + if (!(h->version.flags & STLINK_F_HAS_SWD_SET_FREQ)) return ERROR_COMMAND_NOTFOUND; stlink_usb_init_buffer(handle, h->rx_ep, 2); @@ -578,6 +1078,30 @@ static int stlink_usb_set_swdclk(void *handle, uint16_t clk_divisor) return ERROR_OK; } +static int stlink_usb_set_jtagclk(void *handle, uint16_t clk_divisor) +{ + struct stlink_usb_handle_s *h = handle; + + assert(handle != NULL); + + if (!(h->version.flags & STLINK_F_HAS_JTAG_SET_FREQ)) + return ERROR_COMMAND_NOTFOUND; + + stlink_usb_init_buffer(handle, h->rx_ep, 2); + + h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND; + h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_JTAG_SET_FREQ; + h_u16_to_le(h->cmdbuf+h->cmdidx, clk_divisor); + h->cmdidx += 2; + + int result = stlink_cmd_allow_retry(handle, h->databuf, 2); + + if (result != ERROR_OK) + return result; + + return ERROR_OK; +} + /** */ static int stlink_usb_current_mode(void *handle, uint8_t *mode) { @@ -612,7 +1136,7 @@ static int stlink_usb_mode_enter(void *handle, enum stlink_mode type) * status * TODO: we need the test on api V1 too */ - if (h->jtag_api == STLINK_JTAG_API_V2) + if (h->version.jtag_api != STLINK_JTAG_API_V1) rx_size = 2; stlink_usb_init_buffer(handle, h->rx_ep, rx_size); @@ -620,23 +1144,25 @@ static int stlink_usb_mode_enter(void *handle, enum stlink_mode type) switch (type) { case STLINK_MODE_DEBUG_JTAG: h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND; - if (h->jtag_api == STLINK_JTAG_API_V1) + if (h->version.jtag_api == STLINK_JTAG_API_V1) h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV1_ENTER; else h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_ENTER; - h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_ENTER_JTAG; + h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_ENTER_JTAG_NO_RESET; break; case STLINK_MODE_DEBUG_SWD: h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND; - if (h->jtag_api == STLINK_JTAG_API_V1) + if (h->version.jtag_api == STLINK_JTAG_API_V1) h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV1_ENTER; else h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_ENTER; - h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_ENTER_SWD; + h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_ENTER_SWD_NO_RESET; break; case STLINK_MODE_DEBUG_SWIM: h->cmdbuf[h->cmdidx++] = STLINK_SWIM_COMMAND; h->cmdbuf[h->cmdidx++] = STLINK_SWIM_ENTER; + /* no answer for this function... */ + rx_size = 0; break; case STLINK_MODE_DFU: case STLINK_MODE_MASS: @@ -780,17 +1306,29 @@ static int stlink_usb_init_mode(void *handle, bool connect_under_reset) return ERROR_FAIL; } + /* preliminary SRST assert: + * We want SRST is asserted before activating debug signals (mode_enter). + * As the required mode has not been set, the adapter may not know what pin to use. + * Tested firmware STLINK v2 JTAG v29 API v2 SWIM v0 uses T_NRST pin by default + * Tested firmware STLINK v2 JTAG v27 API v2 SWIM v6 uses T_NRST pin by default + * after power on, SWIM_RST stays unchanged */ + if (connect_under_reset && emode != STLINK_MODE_DEBUG_SWIM) + stlink_usb_assert_srst(handle, 0); + /* do not check the return status here, we will + proceed and enter the desired mode below + and try asserting srst again. */ + + res = stlink_usb_mode_enter(handle, emode); + if (res != ERROR_OK) + return res; + + /* assert SRST again: a little bit late but now the adapter knows for sure what pin to use */ if (connect_under_reset) { res = stlink_usb_assert_srst(handle, 0); if (res != ERROR_OK) return res; } - res = stlink_usb_mode_enter(handle, emode); - - if (res != ERROR_OK) - return res; - res = stlink_usb_current_mode(handle, &mode); if (res != ERROR_OK) @@ -801,63 +1339,262 @@ static int stlink_usb_init_mode(void *handle, bool connect_under_reset) return ERROR_OK; } -/** */ -static int stlink_usb_idcode(void *handle, uint32_t *idcode) +/* request status from last swim request */ +static int stlink_swim_status(void *handle) { - int res; struct stlink_usb_handle_s *h = handle; - - assert(handle != NULL); + int res; stlink_usb_init_buffer(handle, h->rx_ep, 4); - - h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND; - h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_READCOREID; - + h->cmdbuf[h->cmdidx++] = STLINK_SWIM_COMMAND; + h->cmdbuf[h->cmdidx++] = STLINK_SWIM_READSTATUS; res = stlink_usb_xfer(handle, h->databuf, 4); - if (res != ERROR_OK) return res; - - *idcode = le_to_h_u32(h->databuf); - - LOG_DEBUG("IDCODE: 0x%08" PRIX32, *idcode); - return ERROR_OK; } - -static int stlink_usb_v2_read_debug_reg(void *handle, uint32_t addr, uint32_t *val) +/* + the purpose of this function is unknown... + capabilites? anyway for swim v6 it returns + 0001020600000000 +*/ +__attribute__((unused)) +static int stlink_swim_cap(void *handle, uint8_t *cap) { struct stlink_usb_handle_s *h = handle; int res; - assert(handle != NULL); - stlink_usb_init_buffer(handle, h->rx_ep, 8); + h->cmdbuf[h->cmdidx++] = STLINK_SWIM_COMMAND; + h->cmdbuf[h->cmdidx++] = STLINK_SWIM_READ_CAP; + h->cmdbuf[h->cmdidx++] = 0x01; + res = stlink_usb_xfer(handle, h->databuf, 8); + if (res != ERROR_OK) + return res; + memcpy(cap, h->databuf, 8); + return ERROR_OK; +} - h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND; - h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_READDEBUGREG; - h_u32_to_le(h->cmdbuf+h->cmdidx, addr); - h->cmdidx += 4; +/* debug dongle assert/deassert sreset line */ +static int stlink_swim_assert_reset(void *handle, int reset) +{ + struct stlink_usb_handle_s *h = handle; + int res; - res = stlink_cmd_allow_retry(handle, h->databuf, 8); + stlink_usb_init_buffer(handle, h->rx_ep, 0); + h->cmdbuf[h->cmdidx++] = STLINK_SWIM_COMMAND; + if (!reset) + h->cmdbuf[h->cmdidx++] = STLINK_SWIM_ASSERT_RESET; + else + h->cmdbuf[h->cmdidx++] = STLINK_SWIM_DEASSERT_RESET; + res = stlink_cmd_allow_retry(handle, h->databuf, 0); if (res != ERROR_OK) return res; - - *val = le_to_h_u32(h->databuf + 4); return ERROR_OK; } -static int stlink_usb_write_debug_reg(void *handle, uint32_t addr, uint32_t val) +/* + send swim enter seq + 1.3ms low then 750Hz then 1.5kHz +*/ +static int stlink_swim_enter(void *handle) { struct stlink_usb_handle_s *h = handle; + int res; - assert(handle != NULL); + stlink_usb_init_buffer(handle, h->rx_ep, 0); + h->cmdbuf[h->cmdidx++] = STLINK_SWIM_COMMAND; + h->cmdbuf[h->cmdidx++] = STLINK_SWIM_ENTER_SEQ; + res = stlink_cmd_allow_retry(handle, h->databuf, 0); + if (res != ERROR_OK) + return res; + return ERROR_OK; +} - stlink_usb_init_buffer(handle, h->rx_ep, 2); +/* switch high/low speed swim */ +static int stlink_swim_speed(void *handle, int speed) +{ + struct stlink_usb_handle_s *h = handle; + int res; + + stlink_usb_init_buffer(handle, h->rx_ep, 0); + h->cmdbuf[h->cmdidx++] = STLINK_SWIM_COMMAND; + h->cmdbuf[h->cmdidx++] = STLINK_SWIM_SPEED; + if (speed) + h->cmdbuf[h->cmdidx++] = 1; + else + h->cmdbuf[h->cmdidx++] = 0; + res = stlink_cmd_allow_retry(handle, h->databuf, 0); + if (res != ERROR_OK) + return res; + return ERROR_OK; +} + +/* + initiate srst from swim. + nrst is pulled low for 50us. +*/ +static int stlink_swim_generate_rst(void *handle) +{ + struct stlink_usb_handle_s *h = handle; + int res; + + stlink_usb_init_buffer(handle, h->rx_ep, 0); + h->cmdbuf[h->cmdidx++] = STLINK_SWIM_COMMAND; + h->cmdbuf[h->cmdidx++] = STLINK_SWIM_GEN_RST; + res = stlink_cmd_allow_retry(handle, h->databuf, 0); + if (res != ERROR_OK) + return res; + return ERROR_OK; +} + +/* + send resyncronize sequence + swim is pulled low for 16us + reply is 64 clks low +*/ +static int stlink_swim_resync(void *handle) +{ + struct stlink_usb_handle_s *h = handle; + int res; + + stlink_usb_init_buffer(handle, h->rx_ep, 0); + h->cmdbuf[h->cmdidx++] = STLINK_SWIM_COMMAND; + h->cmdbuf[h->cmdidx++] = STLINK_SWIM_RESET; + res = stlink_cmd_allow_retry(handle, h->databuf, 0); + if (res != ERROR_OK) + return res; + return ERROR_OK; +} + +static int stlink_swim_writebytes(void *handle, uint32_t addr, uint32_t len, const uint8_t *data) +{ + struct stlink_usb_handle_s *h = handle; + int res; + unsigned int i; + unsigned int datalen = 0; + int cmdsize = STLINK_CMD_SIZE_V2; + + if (len > STLINK_DATA_SIZE) + return ERROR_FAIL; + + if (h->version.stlink == 1) + cmdsize = STLINK_SG_SIZE; + + stlink_usb_init_buffer(handle, h->tx_ep, 0); + h->cmdbuf[h->cmdidx++] = STLINK_SWIM_COMMAND; + h->cmdbuf[h->cmdidx++] = STLINK_SWIM_WRITEMEM; + h_u16_to_be(h->cmdbuf+h->cmdidx, len); + h->cmdidx += 2; + h_u32_to_be(h->cmdbuf+h->cmdidx, addr); + h->cmdidx += 4; + for (i = 0; i < len; i++) { + if (h->cmdidx == cmdsize) + h->databuf[datalen++] = *(data++); + else + h->cmdbuf[h->cmdidx++] = *(data++); + } + if (h->version.stlink == 1) + stlink_usb_set_cbw_transfer_datalength(handle, datalen); + + res = stlink_cmd_allow_retry(handle, h->databuf, datalen); + if (res != ERROR_OK) + return res; + return ERROR_OK; +} + +static int stlink_swim_readbytes(void *handle, uint32_t addr, uint32_t len, uint8_t *data) +{ + struct stlink_usb_handle_s *h = handle; + int res; + + if (len > STLINK_DATA_SIZE) + return ERROR_FAIL; + + stlink_usb_init_buffer(handle, h->rx_ep, 0); + h->cmdbuf[h->cmdidx++] = STLINK_SWIM_COMMAND; + h->cmdbuf[h->cmdidx++] = STLINK_SWIM_READMEM; + h_u16_to_be(h->cmdbuf+h->cmdidx, len); + h->cmdidx += 2; + h_u32_to_be(h->cmdbuf+h->cmdidx, addr); + h->cmdidx += 4; + res = stlink_cmd_allow_retry(handle, h->databuf, 0); + if (res != ERROR_OK) + return res; + + stlink_usb_init_buffer(handle, h->rx_ep, len); + h->cmdbuf[h->cmdidx++] = STLINK_SWIM_COMMAND; + h->cmdbuf[h->cmdidx++] = STLINK_SWIM_READBUF; + res = stlink_usb_xfer(handle, data, len); + if (res != ERROR_OK) + return res; + + return ERROR_OK; +} + +/** */ +static int stlink_usb_idcode(void *handle, uint32_t *idcode) +{ + int res; + struct stlink_usb_handle_s *h = handle; + + assert(handle != NULL); + + /* there is no swim read core id cmd */ + if (h->transport == HL_TRANSPORT_SWIM) { + *idcode = 0; + return ERROR_OK; + } + + stlink_usb_init_buffer(handle, h->rx_ep, 4); + + h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND; + h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_READCOREID; + + res = stlink_usb_xfer(handle, h->databuf, 4); + + if (res != ERROR_OK) + return res; + + *idcode = le_to_h_u32(h->databuf); + + LOG_DEBUG("IDCODE: 0x%08" PRIX32, *idcode); + + return ERROR_OK; +} + +static int stlink_usb_v2_read_debug_reg(void *handle, uint32_t addr, uint32_t *val) +{ + struct stlink_usb_handle_s *h = handle; + int res; + + assert(handle != NULL); + + stlink_usb_init_buffer(handle, h->rx_ep, 8); h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND; - if (h->jtag_api == STLINK_JTAG_API_V1) + h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_READDEBUGREG; + h_u32_to_le(h->cmdbuf+h->cmdidx, addr); + h->cmdidx += 4; + + res = stlink_cmd_allow_retry(handle, h->databuf, 8); + if (res != ERROR_OK) + return res; + + *val = le_to_h_u32(h->databuf + 4); + return ERROR_OK; +} + +static int stlink_usb_write_debug_reg(void *handle, uint32_t addr, uint32_t val) +{ + struct stlink_usb_handle_s *h = handle; + + assert(handle != NULL); + + stlink_usb_init_buffer(handle, h->rx_ep, 2); + + h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND; + if (h->version.jtag_api == STLINK_JTAG_API_V1) h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV1_WRITEDEBUGREG; else h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_WRITEDEBUGREG; @@ -870,13 +1607,13 @@ static int stlink_usb_write_debug_reg(void *handle, uint32_t addr, uint32_t val) } /** */ -static void stlink_usb_trace_read(void *handle) +static int stlink_usb_trace_read(void *handle, uint8_t *buf, size_t *size) { struct stlink_usb_handle_s *h = handle; assert(handle != NULL); - if (h->trace.enabled && h->version.jtag >= STLINK_TRACE_MIN_VERSION) { + if (h->trace.enabled && (h->version.flags & STLINK_F_HAS_TRACE)) { int res; stlink_usb_init_buffer(handle, h->rx_ep, 10); @@ -885,29 +1622,20 @@ static void stlink_usb_trace_read(void *handle) h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_GET_TRACE_NB; res = stlink_usb_xfer(handle, h->databuf, 2); - if (res == ERROR_OK) { - uint8_t buf[STLINK_TRACE_SIZE]; - size_t size = le_to_h_u16(h->databuf); - - if (size > 0) { - size = size < sizeof(buf) ? size : sizeof(buf) - 1; - - res = stlink_usb_read_trace(handle, buf, size); - if (res == ERROR_OK) { - if (h->trace.output_f) { - /* Log retrieved trace output */ - if (fwrite(buf, 1, size, h->trace.output_f) > 0) - fflush(h->trace.output_f); - } - } - } + if (res != ERROR_OK) + return res; + + size_t bytes_avail = le_to_h_u16(h->databuf); + *size = bytes_avail < *size ? bytes_avail : *size - 1; + + if (*size > 0) { + res = stlink_usb_read_trace(handle, buf, *size); + if (res != ERROR_OK) + return res; + return ERROR_OK; } } -} - -static int stlink_usb_trace_read_callback(void *handle) -{ - stlink_usb_trace_read(handle); + *size = 0; return ERROR_OK; } @@ -925,8 +1653,6 @@ static enum target_state stlink_usb_v2_get_status(void *handle) else if (status & S_RESET_ST) return TARGET_RESET; - stlink_usb_trace_read(handle); - return TARGET_RUNNING; } @@ -938,6 +1664,18 @@ static enum target_state stlink_usb_state(void *handle) assert(handle != NULL); + if (h->transport == HL_TRANSPORT_SWIM) { + res = stlink_usb_mode_enter(handle, stlink_get_mode(h->transport)); + 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, stlink_get_mode(h->transport)); @@ -948,7 +1686,7 @@ static enum target_state stlink_usb_state(void *handle) h->reconnect_pending = false; } - if (h->jtag_api == STLINK_JTAG_API_V2) { + if (h->version.jtag_api != STLINK_JTAG_API_V1) { res = stlink_usb_v2_get_status(handle); if (res == TARGET_UNKNOWN) h->reconnect_pending = true; @@ -975,32 +1713,16 @@ static enum target_state stlink_usb_state(void *handle) return TARGET_UNKNOWN; } -/** */ -static int stlink_usb_reset(void *handle) -{ - struct stlink_usb_handle_s *h = handle; - - assert(handle != NULL); - - stlink_usb_init_buffer(handle, h->rx_ep, 2); - - h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND; - - if (h->jtag_api == STLINK_JTAG_API_V1) - h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV1_RESETSYS; - else - h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_RESETSYS; - - return stlink_cmd_allow_retry(handle, h->databuf, 2); -} - static int stlink_usb_assert_srst(void *handle, int srst) { struct stlink_usb_handle_s *h = handle; assert(handle != NULL); - if (h->jtag_api == STLINK_JTAG_API_V1) + if (h->transport == HL_TRANSPORT_SWIM) + return stlink_swim_assert_reset(handle, srst); + + if (h->version.stlink == 1) return ERROR_COMMAND_NOTFOUND; stlink_usb_init_buffer(handle, h->rx_ep, 2); @@ -1012,62 +1734,6 @@ static int stlink_usb_assert_srst(void *handle, int srst) return stlink_cmd_allow_retry(handle, h->databuf, 2); } -/** */ -static int stlink_configure_target_trace_port(void *handle) -{ - int res; - uint32_t reg; - struct stlink_usb_handle_s *h = handle; - - assert(handle != NULL); - - /* configure the TPI */ - - /* enable the trace subsystem */ - res = stlink_usb_v2_read_debug_reg(handle, DCB_DEMCR, ®); - if (res != ERROR_OK) - goto out; - res = stlink_usb_write_debug_reg(handle, DCB_DEMCR, TRCENA|reg); - if (res != ERROR_OK) - goto out; - /* set the TPI clock prescaler */ - res = stlink_usb_write_debug_reg(handle, TPI_ACPR, h->trace.prescale); - if (res != ERROR_OK) - goto out; - /* select the pin protocol. The STLinkv2 only supports asynchronous - * UART emulation (NRZ) mode, so that's what we pick. */ - res = stlink_usb_write_debug_reg(handle, TPI_SPPR, 0x02); - if (res != ERROR_OK) - goto out; - /* disable continuous formatting */ - res = stlink_usb_write_debug_reg(handle, TPI_FFCR, (1<<8)); - if (res != ERROR_OK) - goto out; - - /* configure the ITM */ - - /* unlock access to the ITM registers */ - res = stlink_usb_write_debug_reg(handle, ITM_LAR, 0xC5ACCE55); - if (res != ERROR_OK) - goto out; - /* enable trace with ATB ID 1 */ - res = stlink_usb_write_debug_reg(handle, ITM_TCR, (1<<16)|(1<<0)|(1<<2)); - if (res != ERROR_OK) - goto out; - /* trace privilege */ - res = stlink_usb_write_debug_reg(handle, ITM_TPR, 1); - if (res != ERROR_OK) - goto out; - /* trace port enable (port 0) */ - res = stlink_usb_write_debug_reg(handle, ITM_TER, (1<<0)); - if (res != ERROR_OK) - goto out; - - res = ERROR_OK; -out: - return res; -} - /** */ static void stlink_usb_trace_disable(void *handle) { @@ -1076,7 +1742,7 @@ static void stlink_usb_trace_disable(void *handle) assert(handle != NULL); - assert(h->version.jtag >= STLINK_TRACE_MIN_VERSION); + assert(h->version.flags & STLINK_F_HAS_TRACE); LOG_DEBUG("Tracing: disable"); @@ -1085,10 +1751,8 @@ static void stlink_usb_trace_disable(void *handle) h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_STOP_TRACE_RX; res = stlink_usb_xfer(handle, h->databuf, 2); - if (res == ERROR_OK) { + if (res == ERROR_OK) h->trace.enabled = false; - target_unregister_timer_callback(stlink_usb_trace_read_callback, handle); - } } @@ -1100,39 +1764,21 @@ static int stlink_usb_trace_enable(void *handle) assert(handle != NULL); - if (h->version.jtag >= STLINK_TRACE_MIN_VERSION) { - uint32_t trace_hz; - - res = stlink_configure_target_trace_port(handle); - if (res != ERROR_OK) - LOG_ERROR("Unable to configure tracing on target"); - - trace_hz = h->trace.prescale > 0 ? - h->trace.source_hz / (h->trace.prescale + 1) : - h->trace.source_hz; - + if (h->version.flags & STLINK_F_HAS_TRACE) { stlink_usb_init_buffer(handle, h->rx_ep, 10); h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND; h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_START_TRACE_RX; h_u16_to_le(h->cmdbuf+h->cmdidx, (uint16_t)STLINK_TRACE_SIZE); h->cmdidx += 2; - h_u32_to_le(h->cmdbuf+h->cmdidx, trace_hz); + h_u32_to_le(h->cmdbuf+h->cmdidx, h->trace.source_hz); h->cmdidx += 4; res = stlink_usb_xfer(handle, h->databuf, 2); if (res == ERROR_OK) { h->trace.enabled = true; - LOG_DEBUG("Tracing: recording at %" PRIu32 "Hz", trace_hz); - /* We need the trace read function to be called at a - * high-enough frequency to ensure reasonable - * "timeliness" in processing ITM/DWT data. - * TODO: An alternative could be using the asynchronous - * features of the libusb-1.0 API to queue up one or more - * reads in advance and requeue them once they are - * completed. */ - target_register_timer_callback(stlink_usb_trace_read_callback, 1, 1, handle); + LOG_DEBUG("Tracing: recording at %" PRIu32 "Hz", h->trace.source_hz); } } else { LOG_ERROR("Tracing is not supported by this version."); @@ -1142,6 +1788,38 @@ static int stlink_usb_trace_enable(void *handle) return res; } +/** */ +static int stlink_usb_reset(void *handle) +{ + struct stlink_usb_handle_s *h = handle; + int retval; + + assert(handle != NULL); + + if (h->transport == HL_TRANSPORT_SWIM) + return stlink_swim_generate_rst(handle); + + stlink_usb_init_buffer(handle, h->rx_ep, 2); + + h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND; + + if (h->version.jtag_api == STLINK_JTAG_API_V1) + h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV1_RESETSYS; + else + h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_RESETSYS; + + retval = stlink_cmd_allow_retry(handle, h->databuf, 2); + if (retval != ERROR_OK) + return retval; + + if (h->trace.enabled) { + stlink_usb_trace_disable(h); + return stlink_usb_trace_enable(h); + } + + return ERROR_OK; +} + /** */ static int stlink_usb_run(void *handle) { @@ -1150,17 +1828,9 @@ static int stlink_usb_run(void *handle) assert(handle != NULL); - if (h->jtag_api == STLINK_JTAG_API_V2) { + if (h->version.jtag_api != STLINK_JTAG_API_V1) { res = stlink_usb_write_debug_reg(handle, DCB_DHCSR, DBGKEY|C_DEBUGEN); - /* Try to start tracing, if requested */ - if (res == ERROR_OK && h->trace.source_hz && !h->trace.enabled) { - if (stlink_usb_trace_enable(handle) == ERROR_OK) - LOG_DEBUG("Tracing: enabled"); - else - LOG_ERROR("Tracing: enable failed"); - } - return res; } @@ -1180,12 +1850,9 @@ static int stlink_usb_halt(void *handle) assert(handle != NULL); - if (h->jtag_api == STLINK_JTAG_API_V2) { + if (h->version.jtag_api != STLINK_JTAG_API_V1) { res = stlink_usb_write_debug_reg(handle, DCB_DHCSR, DBGKEY|C_HALT|C_DEBUGEN); - if (res == ERROR_OK && h->trace.enabled) - stlink_usb_trace_disable(handle); - return res; } @@ -1204,9 +1871,9 @@ static int stlink_usb_step(void *handle) assert(handle != NULL); - if (h->jtag_api == STLINK_JTAG_API_V2) { + if (h->version.jtag_api != STLINK_JTAG_API_V1) { /* TODO: this emulates the v1 api, it should really use a similar auto mask isr - * that the cortex-m3 currently does. */ + * that the Cortex-M3 currently does. */ stlink_usb_write_debug_reg(handle, DCB_DHCSR, DBGKEY|C_HALT|C_MASKINTS|C_DEBUGEN); stlink_usb_write_debug_reg(handle, DCB_DHCSR, DBGKEY|C_STEP|C_MASKINTS|C_DEBUGEN); return stlink_usb_write_debug_reg(handle, DCB_DHCSR, DBGKEY|C_HALT|C_DEBUGEN); @@ -1228,20 +1895,21 @@ static int stlink_usb_read_regs(void *handle) assert(handle != NULL); - stlink_usb_init_buffer(handle, h->rx_ep, 84); + stlink_usb_init_buffer(handle, h->rx_ep, 88); h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND; - if (h->jtag_api == STLINK_JTAG_API_V1) + if (h->version.jtag_api == STLINK_JTAG_API_V1) { + h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV1_READALLREGS; - else + res = stlink_usb_xfer(handle, h->databuf, 84); + /* regs data from offset 0 */ + } else { h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_READALLREGS; + res = stlink_usb_xfer(handle, h->databuf, 88); + /* status at offset 0, regs data from offset 4 */ + } - res = stlink_usb_xfer(handle, h->databuf, 84); - - if (res != ERROR_OK) - return res; - - return ERROR_OK; + return res; } /** */ @@ -1252,16 +1920,16 @@ static int stlink_usb_read_reg(void *handle, int num, uint32_t *val) assert(handle != NULL); - stlink_usb_init_buffer(handle, h->rx_ep, h->jtag_api == STLINK_JTAG_API_V1 ? 4 : 8); + 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; - if (h->jtag_api == STLINK_JTAG_API_V1) + if (h->version.jtag_api == STLINK_JTAG_API_V1) h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV1_READREG; else h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_READREG; h->cmdbuf[h->cmdidx++] = num; - if (h->jtag_api == STLINK_JTAG_API_V1) { + if (h->version.jtag_api == STLINK_JTAG_API_V1) { res = stlink_usb_xfer(handle, h->databuf, 4); if (res != ERROR_OK) return res; @@ -1286,7 +1954,7 @@ static int stlink_usb_write_reg(void *handle, int num, uint32_t val) stlink_usb_init_buffer(handle, h->rx_ep, 2); h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND; - if (h->jtag_api == STLINK_JTAG_API_V1) + if (h->version.jtag_api == STLINK_JTAG_API_V1) h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV1_WRITEREG; else h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_WRITEREG; @@ -1304,15 +1972,21 @@ static int stlink_usb_get_rw_status(void *handle) assert(handle != NULL); - if (h->jtag_api == STLINK_JTAG_API_V1) + if (h->version.jtag_api == STLINK_JTAG_API_V1) return ERROR_OK; stlink_usb_init_buffer(handle, h->rx_ep, 2); h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND; - h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_GETLASTRWSTATUS; + if (h->version.flags & STLINK_F_HAS_GETLASTRWSTATUS2) { + h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_GETLASTRWSTATUS2; - res = stlink_usb_xfer(handle, h->databuf, 2); + res = stlink_usb_xfer(handle, h->databuf, 12); + } else { + h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_GETLASTRWSTATUS; + + res = stlink_usb_xfer(handle, h->databuf, 2); + } if (res != ERROR_OK) return res; @@ -1330,9 +2004,9 @@ static int stlink_usb_read_mem8(void *handle, uint32_t addr, uint16_t len, assert(handle != NULL); - /* max 8bit read/write is 64bytes */ - if (len > STLINK_MAX_RW8) { - LOG_DEBUG("max buffer length exceeded"); + /* max 8 bit read/write is 64 bytes or 512 bytes for v3 */ + if (len > stlink_usb_block(h)) { + LOG_DEBUG("max buffer (%d) length exceeded", stlink_usb_block(h)); return ERROR_FAIL; } @@ -1368,9 +2042,9 @@ static int stlink_usb_write_mem8(void *handle, uint32_t addr, uint16_t len, assert(handle != NULL); - /* max 8bit read/write is 64bytes */ - if (len > STLINK_MAX_RW8) { - LOG_DEBUG("max buffer length exceeded"); + /* max 8 bit read/write is 64 bytes or 512 bytes for v3 */ + if (len > stlink_usb_block(h)) { + LOG_DEBUG("max buffer length (%d) exceeded", stlink_usb_block(h)); return ERROR_FAIL; } @@ -1391,6 +2065,78 @@ static int stlink_usb_write_mem8(void *handle, uint32_t addr, uint16_t len, return stlink_usb_get_rw_status(handle); } +/** */ +static int stlink_usb_read_mem16(void *handle, uint32_t addr, uint16_t len, + uint8_t *buffer) +{ + int res; + struct stlink_usb_handle_s *h = handle; + + assert(handle != NULL); + + if (!(h->version.flags & STLINK_F_HAS_MEM_16BIT)) + return ERROR_COMMAND_NOTFOUND; + + /* data must be a multiple of 2 and half-word aligned */ + if (len % 2 || addr % 2) { + LOG_DEBUG("Invalid data alignment"); + return ERROR_TARGET_UNALIGNED_ACCESS; + } + + stlink_usb_init_buffer(handle, h->rx_ep, len); + + h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND; + h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_READMEM_16BIT; + h_u32_to_le(h->cmdbuf+h->cmdidx, addr); + h->cmdidx += 4; + h_u16_to_le(h->cmdbuf+h->cmdidx, len); + h->cmdidx += 2; + + res = stlink_usb_xfer(handle, h->databuf, len); + + if (res != ERROR_OK) + return res; + + memcpy(buffer, h->databuf, len); + + return stlink_usb_get_rw_status(handle); +} + +/** */ +static int stlink_usb_write_mem16(void *handle, uint32_t addr, uint16_t len, + const uint8_t *buffer) +{ + int res; + struct stlink_usb_handle_s *h = handle; + + assert(handle != NULL); + + if (!(h->version.flags & STLINK_F_HAS_MEM_16BIT)) + return ERROR_COMMAND_NOTFOUND; + + /* data must be a multiple of 2 and half-word aligned */ + if (len % 2 || addr % 2) { + LOG_DEBUG("Invalid data alignment"); + return ERROR_TARGET_UNALIGNED_ACCESS; + } + + stlink_usb_init_buffer(handle, h->tx_ep, len); + + h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND; + h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_WRITEMEM_16BIT; + h_u32_to_le(h->cmdbuf+h->cmdidx, addr); + h->cmdidx += 4; + h_u16_to_le(h->cmdbuf+h->cmdidx, len); + h->cmdidx += 2; + + res = stlink_usb_xfer(handle, buffer, len); + + if (res != ERROR_OK) + return res; + + return stlink_usb_get_rw_status(handle); +} + /** */ static int stlink_usb_read_mem32(void *handle, uint32_t addr, uint16_t len, uint8_t *buffer) @@ -1476,30 +2222,43 @@ static int stlink_usb_read_mem(void *handle, uint32_t addr, uint32_t size, /* calculate byte count */ count *= size; + /* switch to 8 bit if stlink does not support 16 bit memory read */ + if (size == 2 && !(h->version.flags & STLINK_F_HAS_MEM_16BIT)) + size = 1; + while (count) { - bytes_remaining = (size == 4) ? \ - stlink_max_block_size(h->max_mem_packet, addr) : STLINK_MAX_RW8; + bytes_remaining = (size != 1) ? \ + stlink_max_block_size(h->max_mem_packet, addr) : stlink_usb_block(h); if (count < bytes_remaining) bytes_remaining = count; - /* the stlink only supports 8/32bit memory read/writes - * honour 32bit, all others will be handled as 8bit access */ - if (size == 4) { - - /* When in jtag mode the stlink uses the auto-increment functinality. + if (h->transport == HL_TRANSPORT_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. + * Honour 32 bit and, if possible, 16 bit too. Otherwise, handle + * as 8bit access. + */ + if (size != 1) { + + /* When in jtag mode the stlink uses the auto-increment functionality. * However it expects us to pass the data correctly, this includes * alignment and any page boundaries. We already do this as part of the * adi_v5 implementation, but the stlink is a hla adapter and so this - * needs implementiong manually. + * needs implementing manually. * currently this only affects jtag mode, according to ST they do single * access in SWD mode - but this may change and so we do it for both modes */ /* we first need to check for any unaligned bytes */ - if (addr % 4) { + if (addr & (size - 1)) { - uint32_t head_bytes = 4 - (addr % 4); + uint32_t head_bytes = size - (addr & (size - 1)); retval = stlink_usb_read_mem8(handle, addr, head_bytes, buffer); if (retval == ERROR_WAIT && retries < MAX_WAIT_RETRIES) { usleep((1<version.flags & STLINK_F_HAS_MEM_16BIT)) + size = 1; + while (count) { - bytes_remaining = (size == 4) ? \ - stlink_max_block_size(h->max_mem_packet, addr) : STLINK_MAX_RW8; + bytes_remaining = (size != 1) ? \ + stlink_max_block_size(h->max_mem_packet, addr) : stlink_usb_block(h); if (count < bytes_remaining) bytes_remaining = count; - /* the stlink only supports 8/32bit memory read/writes - * honour 32bit, all others will be handled as 8bit access */ - if (size == 4) { - - /* When in jtag mode the stlink uses the auto-increment functinality. + if (h->transport == HL_TRANSPORT_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. + * Honour 32 bit and, if possible, 16 bit too. Otherwise, handle + * as 8bit access. + */ + if (size != 1) { + + /* When in jtag mode the stlink uses the auto-increment functionality. * However it expects us to pass the data correctly, this includes * alignment and any page boundaries. We already do this as part of the * adi_v5 implementation, but the stlink is a hla adapter and so this - * needs implementiong manually. + * needs implementing manually. * currently this only affects jtag mode, according to ST they do single * access in SWD mode - but this may change and so we do it for both modes */ /* we first need to check for any unaligned bytes */ - if (addr % 4) { + if (addr & (size - 1)) { - uint32_t head_bytes = 4 - (addr % 4); + uint32_t head_bytes = size - (addr & (size - 1)); retval = stlink_usb_write_mem8(handle, addr, head_bytes, buffer); if (retval == ERROR_WAIT && retries < MAX_WAIT_RETRIES) { usleep((1<= 22 */ - if (h && (h->version.stlink == 1 || h->version.jtag < 22)) - return khz; + int last_valid_speed = -1; + bool match = true; - for (i = 0; i < ARRAY_SIZE(stlink_khz_to_speed_map); i++) { - if (khz == stlink_khz_to_speed_map[i].speed) { + for (i = 0; i < map_size; i++) { + if (!map[i].speed) + continue; + last_valid_speed = i; + if (khz == map[i].speed) { speed_index = i; break; } else { - int current_diff = khz - stlink_khz_to_speed_map[i].speed; + int current_diff = khz - map[i].speed; /* get abs value for comparison */ current_diff = (current_diff > 0) ? current_diff : -current_diff; - if ((current_diff < speed_diff) && khz >= stlink_khz_to_speed_map[i].speed) { + if ((current_diff < speed_diff) && khz >= map[i].speed) { speed_diff = current_diff; speed_index = i; } } } - bool match = true; - if (speed_index == -1) { /* this will only be here if we cannot match the slow speed. * use the slowest speed we support.*/ - speed_index = ARRAY_SIZE(stlink_khz_to_speed_map) - 1; + speed_index = last_valid_speed; match = false; - } else if (i == ARRAY_SIZE(stlink_khz_to_speed_map)) + } else if (i == map_size) match = false; if (!match && query) { LOG_INFO("Unable to match requested speed %d kHz, using %d kHz", \ - khz, stlink_khz_to_speed_map[speed_index].speed); + khz, map[speed_index].speed); + } + + return speed_index; +} + +static int stlink_speed_swd(void *handle, int khz, bool query) +{ + int speed_index; + struct stlink_usb_handle_s *h = handle; + + /* old firmware cannot change it */ + if (!(h->version.flags & STLINK_F_HAS_SWD_SET_FREQ)) + return khz; + + speed_index = stlink_match_speed_map(stlink_khz_to_speed_map_swd, + ARRAY_SIZE(stlink_khz_to_speed_map_swd), khz, query); + + if (!query) { + int result = stlink_usb_set_swdclk(h, stlink_khz_to_speed_map_swd[speed_index].speed_divisor); + if (result != ERROR_OK) { + LOG_ERROR("Unable to set adapter speed"); + return khz; + } } - if (h && !query) { - int result = stlink_usb_set_swdclk(h, stlink_khz_to_speed_map[speed_index].speed_divisor); + return stlink_khz_to_speed_map_swd[speed_index].speed; +} + +static int stlink_speed_jtag(void *handle, int khz, bool query) +{ + int speed_index; + struct stlink_usb_handle_s *h = handle; + + /* old firmware cannot change it */ + if (!(h->version.flags & STLINK_F_HAS_JTAG_SET_FREQ)) + return khz; + + speed_index = stlink_match_speed_map(stlink_khz_to_speed_map_jtag, + ARRAY_SIZE(stlink_khz_to_speed_map_jtag), khz, query); + + if (!query) { + int result = stlink_usb_set_jtagclk(h, stlink_khz_to_speed_map_jtag[speed_index].speed_divisor); if (result != ERROR_OK) { LOG_ERROR("Unable to set adapter speed"); return khz; } } - return stlink_khz_to_speed_map[speed_index].speed; + return stlink_khz_to_speed_map_jtag[speed_index].speed; +} + +void stlink_dump_speed_map(const struct speed_map *map, unsigned int map_size) +{ + unsigned int i; + + LOG_DEBUG("Supported clock speeds are:"); + for (i = 0; i < map_size; i++) + if (map[i].speed) + LOG_DEBUG("%d kHz", map[i].speed); +} + +static int stlink_get_com_freq(void *handle, bool is_jtag, struct speed_map *map) +{ + struct stlink_usb_handle_s *h = handle; + int i; + + if (h->version.jtag_api != STLINK_JTAG_API_V3) { + LOG_ERROR("Unknown command"); + return 0; + } + + stlink_usb_init_buffer(handle, h->rx_ep, 16); + + h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND; + h->cmdbuf[h->cmdidx++] = STLINK_APIV3_GET_COM_FREQ; + h->cmdbuf[h->cmdidx++] = is_jtag ? 1 : 0; + + int res = stlink_usb_xfer(handle, h->databuf, 52); + + int size = h->databuf[8]; + + if (size > STLINK_V3_MAX_FREQ_NB) + size = STLINK_V3_MAX_FREQ_NB; + + for (i = 0; i < size; i++) { + map[i].speed = le_to_h_u32(&h->databuf[12 + 4 * i]); + map[i].speed_divisor = i; + } + + /* set to zero all the next entries */ + for (i = size; i < STLINK_V3_MAX_FREQ_NB; i++) + map[i].speed = 0; + + return res; +} + +static int stlink_set_com_freq(void *handle, bool is_jtag, unsigned int frequency) +{ + struct stlink_usb_handle_s *h = handle; + + if (h->version.jtag_api != STLINK_JTAG_API_V3) { + LOG_ERROR("Unknown command"); + return 0; + } + + stlink_usb_init_buffer(handle, h->rx_ep, 16); + + h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND; + h->cmdbuf[h->cmdidx++] = STLINK_APIV3_SET_COM_FREQ; + h->cmdbuf[h->cmdidx++] = is_jtag ? 1 : 0; + h->cmdbuf[h->cmdidx++] = 0; + + h_u32_to_le(&h->cmdbuf[4], frequency); + + return stlink_usb_xfer(handle, h->databuf, 8); +} + +static int stlink_speed_v3(void *handle, bool is_jtag, int khz, bool query) +{ + struct stlink_usb_handle_s *h = handle; + int speed_index; + struct speed_map map[STLINK_V3_MAX_FREQ_NB]; + + stlink_get_com_freq(h, is_jtag, map); + + speed_index = stlink_match_speed_map(map, ARRAY_SIZE(map), khz, query); + + if (!query) { + int result = stlink_set_com_freq(h, is_jtag, map[speed_index].speed); + if (result != ERROR_OK) { + LOG_ERROR("Unable to set adapter speed"); + return khz; + } + } + return map[speed_index].speed; +} + +static int stlink_speed(void *handle, int khz, bool query) +{ + struct stlink_usb_handle_s *h = handle; + + if (!handle) + return khz; + + switch (h->transport) { + case HL_TRANSPORT_SWIM: + return stlink_speed_swim(handle, khz, query); + break; + case HL_TRANSPORT_SWD: + if (h->version.jtag_api == STLINK_JTAG_API_V3) + return stlink_speed_v3(handle, false, khz, query); + else + return stlink_speed_swd(handle, khz, query); + break; + case HL_TRANSPORT_JTAG: + if (h->version.jtag_api == STLINK_JTAG_API_V3) + return stlink_speed_v3(handle, true, khz, query); + else + return stlink_speed_jtag(handle, khz, query); + break; + default: + break; + } + + return khz; } /** */ -static int stlink_usb_close(void *fd) +static int stlink_usb_close(void *handle) { - struct stlink_usb_handle_s *h = fd; + int res; + uint8_t mode; + enum stlink_mode emode; + struct stlink_usb_handle_s *h = handle; - if (h->fd) + 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) jtag_libusb_close(h->fd); - free(fd); + free(h); return ERROR_OK; } @@ -1681,7 +2659,6 @@ static int stlink_usb_open(struct hl_interface_param_s *param, void **fd) { int err, retry_count = 1; struct stlink_usb_handle_s *h; - enum stlink_jtag_api_version api; LOG_DEBUG("stlink_usb_open"); @@ -1694,13 +2671,11 @@ static int stlink_usb_open(struct hl_interface_param_s *param, void **fd) h->transport = param->transport; - const uint16_t vids[] = { param->vid, 0 }; - const uint16_t pids[] = { param->pid, 0 }; - const char *serial = param->serial; - - LOG_DEBUG("transport: %d vid: 0x%04x pid: 0x%04x serial: %s", - param->transport, param->vid, param->pid, - param->serial ? param->serial : ""); + for (unsigned i = 0; param->vid[i]; i++) { + LOG_DEBUG("transport: %d vid: 0x%04x pid: 0x%04x serial: %s", + param->transport, param->vid[i], param->pid[i], + param->serial ? param->serial : ""); + } /* On certain host USB configurations(e.g. MacBook Air) @@ -1712,7 +2687,7 @@ static int stlink_usb_open(struct hl_interface_param_s *param, void **fd) in order to become operational. */ do { - if (jtag_libusb_open(vids, pids, serial, &h->fd) != ERROR_OK) { + if (jtag_libusb_open(param->vid, param->pid, param->serial, &h->fd) != ERROR_OK) { LOG_ERROR("open failed"); goto error_open; } @@ -1727,14 +2702,28 @@ static int stlink_usb_open(struct hl_interface_param_s *param, void **fd) /* RX EP is common for all versions */ h->rx_ep = STLINK_RX_EP; + uint16_t pid; + if (jtag_libusb_get_pid(jtag_libusb_get_device(h->fd), &pid) != ERROR_OK) { + LOG_DEBUG("libusb_get_pid failed"); + goto error_open; + } + /* wrap version for first read */ - switch (param->pid) { + switch (pid) { case STLINK_V1_PID: h->version.stlink = 1; h->tx_ep = STLINK_TX_EP; - h->trace_ep = STLINK_TRACE_EP; + break; + case STLINK_V3_USBLOADER_PID: + case STLINK_V3E_PID: + case STLINK_V3S_PID: + case STLINK_V3_2VCP_PID: + h->version.stlink = 3; + h->tx_ep = STLINK_V2_1_TX_EP; + h->trace_ep = STLINK_V2_1_TRACE_EP; break; case STLINK_V2_1_PID: + case STLINK_V2_1_NO_MSD_PID: h->version.stlink = 2; h->tx_ep = STLINK_V2_1_TX_EP; h->trace_ep = STLINK_V2_1_TRACE_EP; @@ -1780,17 +2769,14 @@ static int stlink_usb_open(struct hl_interface_param_s *param, void **fd) } } while (1); - /* compare usb vid/pid */ - if ((param->vid != h->vid) || (param->pid != h->pid)) - LOG_INFO("vid/pid are not identical: 0x%04X/0x%04X 0x%04X/0x%04X", - param->vid, param->pid, - h->vid, h->pid); - /* check if mode is supported */ err = ERROR_OK; switch (h->transport) { case HL_TRANSPORT_SWD: + if (h->version.jtag_api == STLINK_JTAG_API_V1) + err = ERROR_FAIL; + /* fall-through */ case HL_TRANSPORT_JTAG: if (h->version.jtag == 0) err = ERROR_FAIL; @@ -1809,39 +2795,42 @@ static int stlink_usb_open(struct hl_interface_param_s *param, void **fd) goto error_open; } - api = h->version.jtag_api_max; - - LOG_INFO("using stlink api v%d", api); - - /* set the used jtag api, this will default to the newest supported version */ - h->jtag_api = api; - - if (h->jtag_api >= 2 && param->trace_source_hz > 0) { - uint32_t prescale; - - prescale = param->trace_source_hz > STLINK_TRACE_MAX_HZ ? - (param->trace_source_hz / STLINK_TRACE_MAX_HZ) - 1 : 0; - - h->trace.output_f = param->trace_f; - h->trace.source_hz = param->trace_source_hz; - h->trace.prescale = prescale; - } - /* initialize the debug hardware */ err = stlink_usb_init_mode(h, param->connect_under_reset); if (err != ERROR_OK) { - LOG_ERROR("init mode failed"); + LOG_ERROR("init mode failed (unable to connect to the target)"); goto error_open; } - /* clock speed only supported by stlink/v2 and for firmware >= 22 */ - if (h->version.stlink >= 2 && h->version.jtag >= 22) { - LOG_DEBUG("Supported clock speeds are:"); + if (h->transport == HL_TRANSPORT_SWIM) { + err = stlink_swim_enter(h); + if (err != ERROR_OK) { + LOG_ERROR("stlink_swim_enter_failed (unable to connect to the target)"); + goto error_open; + } + *fd = h; + h->max_mem_packet = STLINK_DATA_SIZE; + return ERROR_OK; + } + + if (h->transport == HL_TRANSPORT_JTAG) { + if (h->version.flags & STLINK_F_HAS_JTAG_SET_FREQ) { + stlink_dump_speed_map(stlink_khz_to_speed_map_jtag, ARRAY_SIZE(stlink_khz_to_speed_map_jtag)); + stlink_speed(h, param->initial_interface_speed, false); + } + } else if (h->transport == HL_TRANSPORT_SWD) { + if (h->version.flags & STLINK_F_HAS_SWD_SET_FREQ) { + stlink_dump_speed_map(stlink_khz_to_speed_map_swd, ARRAY_SIZE(stlink_khz_to_speed_map_swd)); + stlink_speed(h, param->initial_interface_speed, false); + } + } - for (unsigned i = 0; i < ARRAY_SIZE(stlink_khz_to_speed_map); i++) - LOG_DEBUG("%d kHz", stlink_khz_to_speed_map[i].speed); + if (h->version.jtag_api == STLINK_JTAG_API_V3) { + struct speed_map map[STLINK_V3_MAX_FREQ_NB]; + stlink_get_com_freq(h, (h->transport == HL_TRANSPORT_JTAG), map); + stlink_dump_speed_map(map, ARRAY_SIZE(map)); stlink_speed(h, param->initial_interface_speed, false); } @@ -1872,6 +2861,37 @@ error_open: return ERROR_FAIL; } +int stlink_config_trace(void *handle, bool enabled, enum tpiu_pin_protocol pin_protocol, + uint32_t port_size, unsigned int *trace_freq) +{ + struct stlink_usb_handle_s *h = handle; + + if (enabled && (!(h->version.flags & STLINK_F_HAS_TRACE) || + pin_protocol != TPIU_PIN_PROTOCOL_ASYNC_UART)) { + LOG_ERROR("The attached ST-LINK version doesn't support this trace mode"); + return ERROR_FAIL; + } + + if (!enabled) { + stlink_usb_trace_disable(h); + return ERROR_OK; + } + + if (*trace_freq > STLINK_TRACE_MAX_HZ) { + LOG_ERROR("ST-LINK doesn't support SWO frequency higher than %u", + STLINK_TRACE_MAX_HZ); + return ERROR_FAIL; + } + + stlink_usb_trace_disable(h); + + if (!*trace_freq) + *trace_freq = STLINK_TRACE_MAX_HZ; + h->trace.source_hz = *trace_freq; + + return stlink_usb_trace_enable(h); +} + /** */ struct hl_layout_api_s stlink_usb_layout_api = { /** */ @@ -1908,4 +2928,8 @@ struct hl_layout_api_s stlink_usb_layout_api = { .override_target = stlink_usb_override_target, /** */ .speed = stlink_speed, + /** */ + .config_trace = stlink_config_trace, + /** */ + .poll_trace = stlink_usb_trace_read, };