stlink: add support for STLINK-V3 17/4717/3
authorAntonio Borneo <borneo.antonio@gmail.com>
Mon, 24 Sep 2018 13:25:46 +0000 (15:25 +0200)
committerSpencer Oliver <spen@spen-soft.co.uk>
Thu, 6 Dec 2018 13:06:59 +0000 (13:06 +0000)
Extend the driver to include the minimal functionality to support
the HLA model.
Due to the small change in the name (ST-LINK/V2 => STLINK-V3), fix
the existing names in the comments in udev rules.

Change-Id: Ied33e38063a6da81d9bf249ed195444d7cdf4f03
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/4717
Reviewed-by: Spencer Oliver <spen@spen-soft.co.uk>
Tested-by: Spencer Oliver <spen@spen-soft.co.uk>
contrib/60-openocd.rules
doc/openocd.texi
src/jtag/drivers/stlink_usb.c
tcl/interface/stlink.cfg

index 9d18e369d58cae25a405e8d19f9ffba9c4b25fa6..ac574bb140f160b78484f1345b5de269ebc04b30 100644 (file)
@@ -55,16 +55,22 @@ ATTRS{idVendor}=="0403", ATTRS{idProduct}=="cff8", MODE="660", GROUP="plugdev",
 # TI ICDI
 ATTRS{idVendor}=="0451", ATTRS{idProduct}=="c32a", MODE="660", GROUP="plugdev", TAG+="uaccess"
 
-# STLink v1
+# STMicroelectronics ST-LINK V1
 ATTRS{idVendor}=="0483", ATTRS{idProduct}=="3744", MODE="660", GROUP="plugdev", TAG+="uaccess"
 
-# STLink v2
+# STMicroelectronics ST-LINK/V2
 ATTRS{idVendor}=="0483", ATTRS{idProduct}=="3748", MODE="660", GROUP="plugdev", TAG+="uaccess"
 
-# STLink v2-1
+# STMicroelectronics ST-LINK/V2.1
 ATTRS{idVendor}=="0483", ATTRS{idProduct}=="374b", MODE="660", GROUP="plugdev", TAG+="uaccess"
 ATTRS{idVendor}=="0483", ATTRS{idProduct}=="3752", MODE="660", GROUP="plugdev", TAG+="uaccess"
 
+# STMicroelectronics STLINK-V3
+ATTRS{idVendor}=="0483", ATTRS{idProduct}=="374d", MODE="660", GROUP="plugdev", TAG+="uaccess"
+ATTRS{idVendor}=="0483", ATTRS{idProduct}=="374e", MODE="660", GROUP="plugdev", TAG+="uaccess"
+ATTRS{idVendor}=="0483", ATTRS{idProduct}=="374f", MODE="660", GROUP="plugdev", TAG+="uaccess"
+ATTRS{idVendor}=="0483", ATTRS{idProduct}=="3753", MODE="660", GROUP="plugdev", TAG+="uaccess"
+
 # Cypress KitProg in KitProg mode
 ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="f139", MODE="660", GROUP="plugdev", TAG+="uaccess"
 
index 6ad19e1e892bd64652921119d0b0747d5ef4aab8..179bf4d5b2fee180424d23986ce0bc01028db3c0 100644 (file)
@@ -487,6 +487,9 @@ They only work with ST Micro chips, notably STM32 and STM8.
 @item @b{ST-LINK/V2}
 @* This is available standalone and as part of some kits, eg. STM32F4DISCOVERY.
 @* Link: @url{http://www.st.com/internet/evalboard/product/251168.jsp}
+@item @b{STLINK-V3}
+@* This is available standalone and as part of some kits.
+@* Link: @url{http://www.st.com/stlink-v3}
 @end itemize
 
 For info the original ST-LINK enumerates using the mass storage usb class; however,
index ffe4d684699d4152f899d0cce34db4af132a3ed3..aed30b6f7360b8fa1b038f79e2340f2bc326bedf 100644 (file)
 #define STLINK_V2_PID         (0x3748)
 #define STLINK_V2_1_PID       (0x374B)
 #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)
 
-/* the current implementation of the stlink limits
- * 8bit read/writes to max 64 bytes. */
+/*
+ * 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.
@@ -77,6 +85,7 @@
 enum stlink_jtag_api_version {
        STLINK_JTAG_API_V1 = 1,
        STLINK_JTAG_API_V2,
+       STLINK_JTAG_API_V3,
 };
 
 /** */
@@ -253,6 +262,11 @@ struct stlink_usb_handle_s {
 #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
@@ -260,6 +274,8 @@ struct stlink_usb_handle_s {
 #define STLINK_TRACE_SIZE               4096
 #define STLINK_TRACE_MAX_HZ             2000000
 
+#define STLINK_V3_MAX_FREQ_NB               10
+
 /** */
 enum stlink_mode {
        STLINK_MODE_UNKNOWN = 0,
@@ -322,6 +338,19 @@ static const struct speed_map stlink_khz_to_speed_map_jtag[] = {
 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;
+}
+
 /** */
 static int stlink_usb_xfer_v1_get_status(void *handle)
 {
@@ -638,8 +667,8 @@ static int stlink_usb_version(void *handle)
        int res;
        uint32_t flags;
        uint16_t version;
-       uint8_t v, x, y, jtag, swim, msd;
-       char v_str[4 * (1 + 3) + 1]; /* VvJjMmSs */
+       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;
 
@@ -684,6 +713,25 @@ static int stlink_usb_version(void *handle)
                break;
        }
 
+       /* STLINK-V3 requires a specific command */
+       if (v == 3 && x == 0 && y == 0) {
+               stlink_usb_init_buffer(handle, h->rx_ep, 16);
+
+               h->cmdbuf[h->cmdidx++] = STLINK_APIV3_GET_VERSION_EX;
+
+               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;
@@ -723,6 +771,23 @@ static int stlink_usb_version(void *handle)
                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;
@@ -735,6 +800,8 @@ static int stlink_usb_version(void *handle)
                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)
                p += sprintf(p, "S%d", swim);
 
@@ -861,7 +928,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->version.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);
@@ -1411,7 +1478,7 @@ static enum target_state stlink_usb_state(void *handle)
                h->reconnect_pending = false;
        }
 
-       if (h->version.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;
@@ -1553,7 +1620,7 @@ static int stlink_usb_run(void *handle)
 
        assert(handle != NULL);
 
-       if (h->version.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);
 
                return res;
@@ -1575,7 +1642,7 @@ static int stlink_usb_halt(void *handle)
 
        assert(handle != NULL);
 
-       if (h->version.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);
 
                return res;
@@ -1596,7 +1663,7 @@ static int stlink_usb_step(void *handle)
 
        assert(handle != NULL);
 
-       if (h->version.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. */
                stlink_usb_write_debug_reg(handle, DCB_DHCSR, DBGKEY|C_HALT|C_MASKINTS|C_DEBUGEN);
@@ -1728,9 +1795,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;
        }
 
@@ -1766,9 +1833,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;
        }
 
@@ -1953,7 +2020,7 @@ static int stlink_usb_read_mem(void *handle, uint32_t addr, uint32_t size,
        while (count) {
 
                bytes_remaining = (size != 1) ? \
-                               stlink_max_block_size(h->max_mem_packet, addr) : STLINK_MAX_RW8;
+                               stlink_max_block_size(h->max_mem_packet, addr) : stlink_usb_block(h);
 
                if (count < bytes_remaining)
                        bytes_remaining = count;
@@ -2038,7 +2105,7 @@ static int stlink_usb_write_mem(void *handle, uint32_t addr, uint32_t size,
        while (count) {
 
                bytes_remaining = (size != 1) ? \
-                               stlink_max_block_size(h->max_mem_packet, addr) : STLINK_MAX_RW8;
+                               stlink_max_block_size(h->max_mem_packet, addr) : stlink_usb_block(h);
 
                if (count < bytes_remaining)
                        bytes_remaining = count;
@@ -2131,9 +2198,13 @@ static int stlink_match_speed_map(const struct speed_map *map, unsigned int map_
        unsigned int i;
        int speed_index = -1;
        int speed_diff = INT_MAX;
+       int last_valid_speed = -1;
        bool match = true;
 
        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;
@@ -2151,7 +2222,7 @@ static int stlink_match_speed_map(const struct speed_map *map, unsigned int map_
        if (speed_index == -1) {
                /* this will only be here if we cannot match the slow speed.
                 * use the slowest speed we support.*/
-               speed_index = map_size - 1;
+               speed_index = last_valid_speed;
                match = false;
        } else if (i == map_size)
                match = false;
@@ -2216,7 +2287,84 @@ void stlink_dump_speed_map(const struct speed_map *map, unsigned int map_size)
 
        LOG_DEBUG("Supported clock speeds are:");
        for (i = 0; i < map_size; i++)
-               LOG_DEBUG("%d kHz", map[i].speed);
+               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)
@@ -2226,12 +2374,25 @@ static int stlink_speed(void *handle, int khz, bool query)
        if (!handle)
                return khz;
 
-       if (h->transport == HL_TRANSPORT_SWIM)
+       switch (h->transport) {
+       case HL_TRANSPORT_SWIM:
                return stlink_speed_swim(handle, khz, query);
-       else if (h->transport == HL_TRANSPORT_SWD)
-               return stlink_speed_swd(handle, khz, query);
-       else if (h->transport == HL_TRANSPORT_JTAG)
-               return stlink_speed_jtag(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;
 }
@@ -2344,6 +2505,14 @@ static int stlink_usb_open(struct hl_interface_param_s *param, void **fd)
                                h->version.stlink = 1;
                                h->tx_ep = STLINK_TX_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;
@@ -2448,6 +2617,14 @@ static int stlink_usb_open(struct hl_interface_param_s *param, void **fd)
                }
        }
 
+       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);
+       }
+
        /* get cpuid, so we can determine the max page size
         * start with a safe default */
        h->max_mem_packet = (1 << 10);
index f7f70c88a5c6e6f469e15db689cfbcb8572694e8..735ad5a4ec9cdb9df62a14bf7af213ea61c8b160 100644 (file)
@@ -1,12 +1,12 @@
 #
-# STMicroelectronics ST-LINK/V1, ST-LINK/V2, ST-LINK/V2-1 in-circuit
+# STMicroelectronics ST-LINK/V1, ST-LINK/V2, ST-LINK/V2-1, STLINK-V3 in-circuit
 # debugger/programmer
 #
 
 interface hla
 hla_layout stlink
 hla_device_desc "ST-LINK"
-hla_vid_pid 0x0483 0x3744 0x0483 0x3748 0x0483 0x374b 0x0483 0x3752
+hla_vid_pid 0x0483 0x3744 0x0483 0x3748 0x0483 0x374b 0x0483 0x374d 0x0483 0x374e 0x0483 0x374f 0x0483 0x3752 0x0483 0x3753
 
 # Optionally specify the serial number of ST-LINK/V2 usb device.  ST-LINK/V2
 # devices seem to have serial numbers with unreadable characters.  ST-LINK/V2

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)