stlink_usb: Changes to make connect_under_reset work
[fw/openocd] / src / jtag / drivers / stlink_usb.c
index 597df6d00e8b26e47f6b9af977ee5a265af76a29..6f720b86b3ad2bbecbf224fe216caa0886d1938c 100644 (file)
@@ -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 <kesmtp@freenet.de>                                   *
  *                                                                         *
@@ -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 <http://www.gnu.org/licenses/>. *
  ***************************************************************************/
 
 #ifdef HAVE_CONFIG_H
 #define STLINK_WRITE_TIMEOUT 1000
 #define STLINK_READ_TIMEOUT 1000
 
-#define STLINK_NULL_EP     0
-#define STLINK_RX_EP       (1|ENDPOINT_IN)
-#define STLINK_TX_EP       (2|ENDPOINT_OUT)
-#define STLINK_TRACE_EP    (3|ENDPOINT_IN)
-#define STLINK_SG_SIZE     (31)
-#define STLINK_DATA_SIZE   (4096)
-#define STLINK_CMD_SIZE_V2 (16)
-#define STLINK_CMD_SIZE_V1 (10)
+#define STLINK_NULL_EP        0
+#define STLINK_RX_EP          (1|ENDPOINT_IN)
+#define STLINK_TX_EP          (2|ENDPOINT_OUT)
+#define STLINK_TRACE_EP       (3|ENDPOINT_IN)
+
+#define STLINK_V2_1_TX_EP     (1|ENDPOINT_OUT)
+#define STLINK_V2_1_TRACE_EP  (2|ENDPOINT_IN)
+
+#define STLINK_SG_SIZE        (31)
+#define STLINK_DATA_SIZE      (4096)
+#define STLINK_CMD_SIZE_V2    (16)
+#define STLINK_CMD_SIZE_V1    (10)
+
+#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_MAX_RW8         (64)
 
+/* "WAIT" responses will be retried (with exponential backoff) at
+ * most this many times before failing to caller.
+ */
+#define MAX_WAIT_RETRIES 8
+
 enum stlink_jtag_api_version {
        STLINK_JTAG_API_V1 = 1,
        STLINK_JTAG_API_V2,
@@ -82,6 +97,12 @@ struct stlink_usb_handle_s {
        /** */
        struct libusb_transfer *trans;
        /** */
+       uint8_t rx_ep;
+       /** */
+       uint8_t tx_ep;
+       /** */
+       uint8_t trace_ep;
+       /** */
        uint8_t cmdbuf[STLINK_SG_SIZE];
        /** */
        uint8_t cmdidx;
@@ -105,19 +126,32 @@ struct stlink_usb_handle_s {
        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_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_CORE_RUNNING            0x80
 #define STLINK_CORE_HALTED             0x81
@@ -139,8 +173,36 @@ 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
@@ -182,12 +244,13 @@ struct stlink_usb_handle_s {
 #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_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
 
@@ -204,7 +267,26 @@ enum stlink_mode {
 #define REQUEST_SENSE        0x03
 #define REQUEST_SENSE_LENGTH 18
 
+static const struct {
+       int speed;
+       int speed_divisor;
+} stlink_khz_to_speed_map[] = {
+       {4000, 0},
+       {1800, 1}, /* default */
+       {1200, 2},
+       {950,  3},
+       {480,  7},
+       {240, 15},
+       {125, 31},
+       {100, 40},
+       {50,  79},
+       {25, 158},
+       {15, 265},
+       {5,  798}
+};
+
 static void stlink_usb_init_buffer(void *handle, uint8_t direction, uint32_t size);
+static int stlink_swim_status(void *handle);
 
 /** */
 static int stlink_usb_xfer_v1_get_status(void *handle)
@@ -216,7 +298,7 @@ static int stlink_usb_xfer_v1_get_status(void *handle)
        /* read status */
        memset(h->cmdbuf, 0, STLINK_SG_SIZE);
 
-       if (jtag_libusb_bulk_read(h->fd, STLINK_RX_EP, (char *)h->cmdbuf,
+       if (jtag_libusb_bulk_read(h->fd, h->rx_ep, (char *)h->cmdbuf,
                        13, STLINK_READ_TIMEOUT) != 13)
                return ERROR_FAIL;
 
@@ -246,19 +328,19 @@ static int stlink_usb_xfer_rw(void *handle, int cmdsize, const uint8_t *buf, int
 
        assert(handle != NULL);
 
-       if (jtag_libusb_bulk_write(h->fd, STLINK_TX_EP, (char *)h->cmdbuf, cmdsize,
+       if (jtag_libusb_bulk_write(h->fd, h->tx_ep, (char *)h->cmdbuf, cmdsize,
                        STLINK_WRITE_TIMEOUT) != cmdsize) {
                return ERROR_FAIL;
        }
 
-       if (h->direction == STLINK_TX_EP && size) {
-               if (jtag_libusb_bulk_write(h->fd, STLINK_TX_EP, (char *)buf,
+       if (h->direction == h->tx_ep && size) {
+               if (jtag_libusb_bulk_write(h->fd, h->tx_ep, (char *)buf,
                                size, STLINK_WRITE_TIMEOUT) != size) {
                        LOG_DEBUG("bulk write failed");
                        return ERROR_FAIL;
                }
-       } else if (h->direction == STLINK_RX_EP && size) {
-               if (jtag_libusb_bulk_read(h->fd, STLINK_RX_EP, (char *)buf,
+       } else if (h->direction == h->rx_ep && size) {
+               if (jtag_libusb_bulk_read(h->fd, h->rx_ep, (char *)buf,
                                size, STLINK_READ_TIMEOUT) != size) {
                        LOG_DEBUG("bulk read failed");
                        return ERROR_FAIL;
@@ -276,7 +358,7 @@ static int stlink_usb_xfer_v1_get_sense(void *handle)
 
        assert(handle != NULL);
 
-       stlink_usb_init_buffer(handle, STLINK_RX_EP, 16);
+       stlink_usb_init_buffer(handle, h->rx_ep, 16);
 
        h->cmdbuf[h->cmdidx++] = REQUEST_SENSE;
        h->cmdbuf[h->cmdidx++] = 0;
@@ -295,7 +377,11 @@ static int stlink_usb_xfer_v1_get_sense(void *handle)
        return ERROR_OK;
 }
 
-/** */
+/*
+       transfers block in cmdbuf
+       <size> 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;
@@ -303,8 +389,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);
 
@@ -326,6 +415,124 @@ 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.
+*/
+static int stlink_usb_error_check(void *handle)
+{
+       struct stlink_usb_handle_s *h = 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)
+               h->databuf[0] = STLINK_DEBUG_ERR_OK;
+
+       switch (h->databuf[0]) {
+               case STLINK_DEBUG_ERR_OK:
+                       return ERROR_OK;
+               case STLINK_DEBUG_ERR_FAULT:
+                       LOG_DEBUG("SWD fault response (0x%x)", STLINK_DEBUG_ERR_FAULT);
+                       return ERROR_FAIL;
+               case STLINK_SWD_AP_WAIT:
+                       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_DP_WAIT);
+                       return ERROR_WAIT;
+               case STLINK_JTAG_WRITE_ERROR:
+                       LOG_DEBUG("Write error");
+                       return ERROR_FAIL;
+               case STLINK_JTAG_WRITE_VERIF_ERROR:
+                       LOG_DEBUG("Verify error");
+                       return ERROR_FAIL;
+               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;
+               default:
+                       LOG_DEBUG("unknown/unexpected STLINK status code 0x%x", h->databuf[0]);
+                       return ERROR_FAIL;
+       }
+}
+
+
+/** 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. For SWIM a SWIM_READSTATUS is requested instead.
+
+    Returns an openocd result code.
+*/
+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) {
+               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<<retries++) * 1000);
+                       continue;
+               }
+               return res;
+       }
+}
+
 /** */
 static int stlink_usb_read_trace(void *handle, const uint8_t *buf, int size)
 {
@@ -335,7 +542,7 @@ static int stlink_usb_read_trace(void *handle, const uint8_t *buf, int size)
 
        assert(h->version.stlink >= 2);
 
-       if (jtag_libusb_bulk_read(h->fd, STLINK_TRACE_EP, (char *)buf,
+       if (jtag_libusb_bulk_read(h->fd, h->trace_ep, (char *)buf,
                        size, STLINK_READ_TIMEOUT) != size) {
                LOG_ERROR("bulk trace read failed");
                return ERROR_FAIL;
@@ -344,7 +551,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;
@@ -353,12 +570,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;
-       h->cmdbuf[h->cmdidx++] = (direction == STLINK_RX_EP ? ENDPOINT_IN : ENDPOINT_OUT);
+       /* 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;
 }
 
 /** */
@@ -377,40 +598,6 @@ static void stlink_usb_init_buffer(void *handle, uint8_t direction, uint32_t siz
                stlink_usb_xfer_v1_create_cmd(handle, direction, size);
 }
 
-static const char * const stlink_usb_error_msg[] = {
-       "unknown"
-};
-
-/** */
-static int stlink_usb_error_check(void *handle)
-{
-       int res;
-       const char *err_msg = 0;
-       struct stlink_usb_handle_s *h = handle;
-
-       assert(handle != NULL);
-
-       /* TODO: no error checking yet on api V1 */
-       if (h->jtag_api == STLINK_JTAG_API_V1)
-               h->databuf[0] = STLINK_DEBUG_ERR_OK;
-
-       switch (h->databuf[0]) {
-               case STLINK_DEBUG_ERR_OK:
-                       res = ERROR_OK;
-                       break;
-               case STLINK_DEBUG_ERR_FAULT:
-               default:
-                       err_msg = stlink_usb_error_msg[0];
-                       res = ERROR_FAIL;
-                       break;
-       }
-
-       if (res != ERROR_OK)
-               LOG_DEBUG("status error: %d ('%s')", h->databuf[0], err_msg);
-
-       return res;
-}
-
 /** */
 static int stlink_usb_version(void *handle)
 {
@@ -420,7 +607,7 @@ static int stlink_usb_version(void *handle)
 
        assert(handle != NULL);
 
-       stlink_usb_init_buffer(handle, STLINK_RX_EP, 6);
+       stlink_usb_init_buffer(handle, h->rx_ep, 6);
 
        h->cmdbuf[h->cmdidx++] = STLINK_GET_VERSION;
 
@@ -465,7 +652,7 @@ static int stlink_usb_check_voltage(void *handle, float *target_voltage)
        if (h->version.stlink == 1 || h->version.jtag < 13)
                return ERROR_COMMAND_NOTFOUND;
 
-       stlink_usb_init_buffer(handle, STLINK_RX_EP, 8);
+       stlink_usb_init_buffer(handle, h->rx_ep, 8);
 
        h->cmdbuf[h->cmdidx++] = STLINK_GET_TARGET_VOLTAGE;
 
@@ -488,6 +675,31 @@ static int stlink_usb_check_voltage(void *handle, float *target_voltage)
        return ERROR_OK;
 }
 
+static int stlink_usb_set_swdclk(void *handle, uint16_t clk_divisor)
+{
+       struct stlink_usb_handle_s *h = handle;
+
+       assert(handle != NULL);
+
+       /* only supported by stlink/v2 and for firmware >= 22 */
+       if (h->version.stlink == 1 || h->version.jtag < 22)
+               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_SWD_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)
 {
@@ -496,7 +708,7 @@ static int stlink_usb_current_mode(void *handle, uint8_t *mode)
 
        assert(handle != NULL);
 
-       stlink_usb_init_buffer(handle, STLINK_RX_EP, 2);
+       stlink_usb_init_buffer(handle, h->rx_ep, 2);
 
        h->cmdbuf[h->cmdidx++] = STLINK_GET_CURRENT_MODE;
 
@@ -513,7 +725,6 @@ static int stlink_usb_current_mode(void *handle, uint8_t *mode)
 /** */
 static int stlink_usb_mode_enter(void *handle, enum stlink_mode type)
 {
-       int res;
        int rx_size = 0;
        struct stlink_usb_handle_s *h = handle;
 
@@ -526,7 +737,7 @@ static int stlink_usb_mode_enter(void *handle, enum stlink_mode type)
        if (h->jtag_api == STLINK_JTAG_API_V2)
                rx_size = 2;
 
-       stlink_usb_init_buffer(handle, STLINK_RX_EP, rx_size);
+       stlink_usb_init_buffer(handle, h->rx_ep, rx_size);
 
        switch (type) {
                case STLINK_MODE_DEBUG_JTAG:
@@ -548,6 +759,8 @@ static int stlink_usb_mode_enter(void *handle, enum stlink_mode type)
                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:
@@ -555,14 +768,7 @@ static int stlink_usb_mode_enter(void *handle, enum stlink_mode type)
                        return ERROR_FAIL;
        }
 
-       res = stlink_usb_xfer(handle, h->databuf, rx_size);
-
-       if (res != ERROR_OK)
-               return res;
-
-       res = stlink_usb_error_check(h);
-
-       return res;
+       return stlink_cmd_allow_retry(handle, h->databuf, rx_size);
 }
 
 /** */
@@ -604,6 +810,20 @@ static int stlink_usb_mode_leave(void *handle, enum stlink_mode type)
 
 static int stlink_usb_assert_srst(void *handle, int srst);
 
+static enum stlink_mode stlink_get_mode(enum hl_transports t)
+{
+       switch (t) {
+       case HL_TRANSPORT_SWD:
+               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)
 {
@@ -677,43 +897,235 @@ static int stlink_usb_init_mode(void *handle, bool connect_under_reset)
        LOG_DEBUG("MODE: 0x%02X", mode);
 
        /* set selected mode */
-       switch (h->transport) {
-               case HL_TRANSPORT_SWD:
-                       emode = STLINK_MODE_DEBUG_SWD;
-                       break;
-               case HL_TRANSPORT_JTAG:
-                       emode = STLINK_MODE_DEBUG_JTAG;
-                       break;
-               case HL_TRANSPORT_SWIM:
-                       emode = STLINK_MODE_DEBUG_SWIM;
-                       break;
-               default:
-                       emode = STLINK_MODE_UNKNOWN;
-                       break;
-       }
+       emode = stlink_get_mode(h->transport);
 
        if (emode == STLINK_MODE_UNKNOWN) {
                LOG_ERROR("selected mode (transport) not supported");
                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);
+       res = stlink_usb_current_mode(handle, &mode);
 
        if (res != ERROR_OK)
                return res;
 
-       res = stlink_usb_current_mode(handle, &mode);
+       LOG_DEBUG("MODE: 0x%02X", mode);
+
+       return ERROR_OK;
+}
+
+/* request status from last swim request */
+static int stlink_swim_status(void *handle)
+{
+       struct stlink_usb_handle_s *h = handle;
+       int res;
+
+       stlink_usb_init_buffer(handle, h->rx_ep, 4);
+       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;
+       return ERROR_OK;
+}
+/*
+       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;
 
+       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;
+}
 
-       LOG_DEBUG("MODE: 0x%02X", mode);
+/*     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;
+
+       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;
+       return ERROR_OK;
+}
+
+/*
+       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;
+
+       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;
+}
+
+/*     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;
 }
@@ -726,7 +1138,13 @@ static int stlink_usb_idcode(void *handle, uint32_t *idcode)
 
        assert(handle != NULL);
 
-       stlink_usb_init_buffer(handle, STLINK_RX_EP, 4);
+       /* 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;
@@ -738,7 +1156,7 @@ static int stlink_usb_idcode(void *handle, uint32_t *idcode)
 
        *idcode = le_to_h_u32(h->databuf);
 
-       LOG_DEBUG("IDCODE: 0x%08X", *idcode);
+       LOG_DEBUG("IDCODE: 0x%08" PRIX32, *idcode);
 
        return ERROR_OK;
 }
@@ -750,31 +1168,28 @@ static int stlink_usb_v2_read_debug_reg(void *handle, uint32_t addr, uint32_t *v
 
        assert(handle != NULL);
 
-       stlink_usb_init_buffer(handle, STLINK_RX_EP, 8);
+       stlink_usb_init_buffer(handle, h->rx_ep, 8);
 
        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;
 
-       res = stlink_usb_xfer(handle, h->databuf, 8);
-
+       res = stlink_cmd_allow_retry(handle, h->databuf, 8);
        if (res != ERROR_OK)
                return res;
 
        *val = le_to_h_u32(h->databuf + 4);
-
-       return h->databuf[0] == STLINK_DEBUG_ERR_OK ? ERROR_OK : ERROR_FAIL;
+       return ERROR_OK;
 }
 
 static int stlink_usb_write_debug_reg(void *handle, uint32_t addr, uint32_t val)
 {
-       int res;
        struct stlink_usb_handle_s *h = handle;
 
        assert(handle != NULL);
 
-       stlink_usb_init_buffer(handle, STLINK_RX_EP, 2);
+       stlink_usb_init_buffer(handle, h->rx_ep, 2);
 
        h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
        if (h->jtag_api == STLINK_JTAG_API_V1)
@@ -786,16 +1201,11 @@ static int stlink_usb_write_debug_reg(void *handle, uint32_t addr, uint32_t val)
        h_u32_to_le(h->cmdbuf+h->cmdidx, val);
        h->cmdidx += 4;
 
-       res = stlink_usb_xfer(handle, h->databuf, 2);
-
-       if (res != ERROR_OK)
-               return res;
-
-       return h->databuf[0] == STLINK_DEBUG_ERR_OK ? ERROR_OK : ERROR_FAIL;
+       return stlink_cmd_allow_retry(handle, h->databuf, 2);
 }
 
 /** */
-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;
 
@@ -804,30 +1214,27 @@ static void stlink_usb_trace_read(void *handle)
        if (h->trace.enabled && h->version.jtag >= STLINK_TRACE_MIN_VERSION) {
                int res;
 
-               stlink_usb_init_buffer(handle, STLINK_RX_EP, 10);
+               stlink_usb_init_buffer(handle, h->rx_ep, 10);
 
                h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
                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;
                }
        }
+       *size = 0;
+       return ERROR_OK;
 }
 
 static enum target_state stlink_usb_v2_get_status(void *handle)
@@ -844,8 +1251,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;
 }
 
@@ -857,136 +1262,74 @@ static enum target_state stlink_usb_state(void *handle)
 
        assert(handle != NULL);
 
-       if (h->jtag_api == STLINK_JTAG_API_V2)
-               return stlink_usb_v2_get_status(handle);
-
-       stlink_usb_init_buffer(handle, STLINK_RX_EP, 2);
-
-       h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
-       h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_GETSTATUS;
+       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_usb_xfer(handle, h->databuf, 2);
+               res = stlink_swim_resync(handle);
+               if (res != ERROR_OK)
+                       return TARGET_UNKNOWN;
 
-       if (res != ERROR_OK)
-               return TARGET_UNKNOWN;
+               return ERROR_OK;
+       }
 
-       if (h->databuf[0] == STLINK_CORE_RUNNING)
-               return TARGET_RUNNING;
-       if (h->databuf[0] == STLINK_CORE_HALTED)
-               return TARGET_HALTED;
+       if (h->reconnect_pending) {
+               LOG_INFO("Previous state query failed, trying to reconnect");
+               res = stlink_usb_mode_enter(handle, stlink_get_mode(h->transport));
 
-       return TARGET_UNKNOWN;
-}
+               if (res != ERROR_OK)
+                       return TARGET_UNKNOWN;
 
-/** */
-static int stlink_usb_reset(void *handle)
-{
-       int res;
-       struct stlink_usb_handle_s *h = handle;
+               h->reconnect_pending = false;
+       }
 
-       assert(handle != NULL);
+       if (h->jtag_api == STLINK_JTAG_API_V2) {
+               res = stlink_usb_v2_get_status(handle);
+               if (res == TARGET_UNKNOWN)
+                       h->reconnect_pending = true;
+               return res;
+       }
 
-       stlink_usb_init_buffer(handle, STLINK_RX_EP, 2);
+       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;
+       h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_GETSTATUS;
 
        res = stlink_usb_xfer(handle, h->databuf, 2);
 
        if (res != ERROR_OK)
-               return res;
+               return TARGET_UNKNOWN;
 
-       LOG_DEBUG("RESET: 0x%08X", h->databuf[0]);
+       if (h->databuf[0] == STLINK_CORE_RUNNING)
+               return TARGET_RUNNING;
+       if (h->databuf[0] == STLINK_CORE_HALTED)
+               return TARGET_HALTED;
 
-       /* the following is not a error under swd (using hardware srst), so return success */
-       if (h->databuf[0] == STLINK_SWD_AP_WAIT || h->databuf[0] == STLINK_SWD_DP_WAIT)
-               return ERROR_OK;
+       h->reconnect_pending = true;
 
-       return h->databuf[0] == STLINK_DEBUG_ERR_OK ? ERROR_OK : ERROR_FAIL;
+       return TARGET_UNKNOWN;
 }
 
 static int stlink_usb_assert_srst(void *handle, int srst)
 {
-       int res;
        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, STLINK_RX_EP, 2);
+       stlink_usb_init_buffer(handle, h->rx_ep, 2);
 
        h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
        h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_DRIVE_NRST;
        h->cmdbuf[h->cmdidx++] = srst;
 
-       res = stlink_usb_xfer(handle, h->databuf, 2);
-
-       if (res != ERROR_OK)
-               return res;
-
-       return h->databuf[0] == STLINK_DEBUG_ERR_OK ? ERROR_OK : ERROR_FAIL;
-}
-
-/** */
-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, &reg);
-       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;
+       return stlink_cmd_allow_retry(handle, h->databuf, 2);
 }
 
 /** */
@@ -999,9 +1342,9 @@ static void stlink_usb_trace_disable(void *handle)
 
        assert(h->version.jtag >= STLINK_TRACE_MIN_VERSION);
 
-       LOG_DEBUG("Tracing: disable\n");
+       LOG_DEBUG("Tracing: disable");
 
-       stlink_usb_init_buffer(handle, STLINK_RX_EP, 2);
+       stlink_usb_init_buffer(handle, h->rx_ep, 2);
        h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
        h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_STOP_TRACE_RX;
        res = stlink_usb_xfer(handle, h->databuf, 2);
@@ -1020,30 +1363,20 @@ 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\n");
-
-               trace_hz = h->trace.prescale > 0 ?
-                       h->trace.source_hz / (h->trace.prescale + 1) :
-                       h->trace.source_hz;
-
-               stlink_usb_init_buffer(handle, STLINK_RX_EP, 10);
+               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 %uHz\n", trace_hz);
+                       LOG_DEBUG("Tracing: recording at %" PRIu32 "Hz", h->trace.source_hz);
                }
        } else {
                LOG_ERROR("Tracing is not supported by this version.");
@@ -1053,6 +1386,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->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)
 {
@@ -1064,28 +1429,15 @@ static int stlink_usb_run(void *handle)
        if (h->jtag_api == STLINK_JTAG_API_V2) {
                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) {
-                       if (stlink_usb_trace_enable(handle) == ERROR_OK)
-                               LOG_DEBUG("Tracing: enabled\n");
-                       else
-                               LOG_ERROR("Tracing: enable failed\n");
-               }
-
                return res;
        }
 
-       stlink_usb_init_buffer(handle, STLINK_RX_EP, 2);
+       stlink_usb_init_buffer(handle, h->rx_ep, 2);
 
        h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
        h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_RUNCORE;
 
-       res = stlink_usb_xfer(handle, h->databuf, 2);
-
-       if (res != ERROR_OK)
-               return res;
-
-       return h->databuf[0] == STLINK_DEBUG_ERR_OK ? ERROR_OK : ERROR_FAIL;
+       return stlink_cmd_allow_retry(handle, h->databuf, 2);
 }
 
 /** */
@@ -1099,52 +1451,38 @@ static int stlink_usb_halt(void *handle)
        if (h->jtag_api == STLINK_JTAG_API_V2) {
                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;
        }
 
-       stlink_usb_init_buffer(handle, STLINK_RX_EP, 2);
+       stlink_usb_init_buffer(handle, h->rx_ep, 2);
 
        h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
        h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_FORCEDEBUG;
 
-       res = stlink_usb_xfer(handle, h->databuf, 2);
-
-       if (res != ERROR_OK)
-               return res;
-
-       return h->databuf[0] == STLINK_DEBUG_ERR_OK ? ERROR_OK : ERROR_FAIL;
+       return stlink_cmd_allow_retry(handle, h->databuf, 2);
 }
 
 /** */
 static int stlink_usb_step(void *handle)
 {
-       int res;
        struct stlink_usb_handle_s *h = handle;
 
        assert(handle != NULL);
 
        if (h->jtag_api == STLINK_JTAG_API_V2) {
                /* 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);
        }
 
-       stlink_usb_init_buffer(handle, STLINK_RX_EP, 2);
+       stlink_usb_init_buffer(handle, h->rx_ep, 2);
 
        h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
        h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_STEPCORE;
 
-       res = stlink_usb_xfer(handle, h->databuf, 2);
-
-       if (res != ERROR_OK)
-               return res;
-
-       return h->databuf[0] == STLINK_DEBUG_ERR_OK ? ERROR_OK : ERROR_FAIL;
+       return stlink_cmd_allow_retry(handle, h->databuf, 2);
 }
 
 /** */
@@ -1155,7 +1493,7 @@ static int stlink_usb_read_regs(void *handle)
 
        assert(handle != NULL);
 
-       stlink_usb_init_buffer(handle, STLINK_RX_EP, 84);
+       stlink_usb_init_buffer(handle, h->rx_ep, 84);
 
        h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
        if (h->jtag_api == STLINK_JTAG_API_V1)
@@ -1179,7 +1517,7 @@ static int stlink_usb_read_reg(void *handle, int num, uint32_t *val)
 
        assert(handle != NULL);
 
-       stlink_usb_init_buffer(handle, STLINK_RX_EP, h->jtag_api == STLINK_JTAG_API_V1 ? 4 : 8);
+       stlink_usb_init_buffer(handle, h->rx_ep, h->jtag_api == STLINK_JTAG_API_V1 ? 4 : 8);
 
        h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
        if (h->jtag_api == STLINK_JTAG_API_V1)
@@ -1188,30 +1526,29 @@ static int stlink_usb_read_reg(void *handle, int num, uint32_t *val)
                h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_READREG;
        h->cmdbuf[h->cmdidx++] = num;
 
-       res = stlink_usb_xfer(handle, h->databuf, h->jtag_api == STLINK_JTAG_API_V1 ? 4 : 8);
-
-       if (res != ERROR_OK)
-               return res;
-
-       if (h->jtag_api == STLINK_JTAG_API_V1)
+       if (h->jtag_api == STLINK_JTAG_API_V1) {
+               res = stlink_usb_xfer(handle, h->databuf, 4);
+               if (res != ERROR_OK)
+                       return res;
                *val = le_to_h_u32(h->databuf);
-       else {
+               return ERROR_OK;
+       } else {
+               res = stlink_cmd_allow_retry(handle, h->databuf, 8);
+               if (res != ERROR_OK)
+                       return res;
                *val = le_to_h_u32(h->databuf + 4);
-               return h->databuf[0] == STLINK_DEBUG_ERR_OK ? ERROR_OK : ERROR_FAIL;
+               return ERROR_OK;
        }
-
-       return ERROR_OK;
 }
 
 /** */
 static int stlink_usb_write_reg(void *handle, int num, uint32_t val)
 {
-       int res;
        struct stlink_usb_handle_s *h = handle;
 
        assert(handle != NULL);
 
-       stlink_usb_init_buffer(handle, STLINK_RX_EP, 2);
+       stlink_usb_init_buffer(handle, h->rx_ep, 2);
 
        h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
        if (h->jtag_api == STLINK_JTAG_API_V1)
@@ -1222,12 +1559,7 @@ static int stlink_usb_write_reg(void *handle, int num, uint32_t val)
        h_u32_to_le(h->cmdbuf+h->cmdidx, val);
        h->cmdidx += 4;
 
-       res = stlink_usb_xfer(handle, h->databuf, 2);
-
-       if (res != ERROR_OK)
-               return res;
-
-       return h->databuf[0] == STLINK_DEBUG_ERR_OK ? ERROR_OK : ERROR_FAIL;
+       return stlink_cmd_allow_retry(handle, h->databuf, 2);
 }
 
 static int stlink_usb_get_rw_status(void *handle)
@@ -1240,7 +1572,7 @@ static int stlink_usb_get_rw_status(void *handle)
        if (h->jtag_api == STLINK_JTAG_API_V1)
                return ERROR_OK;
 
-       stlink_usb_init_buffer(handle, STLINK_RX_EP, 2);
+       stlink_usb_init_buffer(handle, h->rx_ep, 2);
 
        h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
        h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_GETLASTRWSTATUS;
@@ -1250,7 +1582,7 @@ static int stlink_usb_get_rw_status(void *handle)
        if (res != ERROR_OK)
                return res;
 
-       return h->databuf[0] == STLINK_DEBUG_ERR_OK ? ERROR_OK : res;
+       return stlink_usb_error_check(h);
 }
 
 /** */
@@ -1269,7 +1601,7 @@ static int stlink_usb_read_mem8(void *handle, uint32_t addr, uint16_t len,
                return ERROR_FAIL;
        }
 
-       stlink_usb_init_buffer(handle, STLINK_RX_EP, read_len);
+       stlink_usb_init_buffer(handle, h->rx_ep, read_len);
 
        h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
        h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_READMEM_8BIT;
@@ -1307,7 +1639,7 @@ static int stlink_usb_write_mem8(void *handle, uint32_t addr, uint16_t len,
                return ERROR_FAIL;
        }
 
-       stlink_usb_init_buffer(handle, STLINK_TX_EP, len);
+       stlink_usb_init_buffer(handle, h->tx_ep, len);
 
        h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
        h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_WRITEMEM_8BIT;
@@ -1339,7 +1671,7 @@ static int stlink_usb_read_mem32(void *handle, uint32_t addr, uint16_t len,
                return ERROR_TARGET_UNALIGNED_ACCESS;
        }
 
-       stlink_usb_init_buffer(handle, STLINK_RX_EP, len);
+       stlink_usb_init_buffer(handle, h->rx_ep, len);
 
        h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
        h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_READMEM_32BIT;
@@ -1373,7 +1705,7 @@ static int stlink_usb_write_mem32(void *handle, uint32_t addr, uint16_t len,
                return ERROR_TARGET_UNALIGNED_ACCESS;
        }
 
-       stlink_usb_init_buffer(handle, STLINK_TX_EP, len);
+       stlink_usb_init_buffer(handle, h->tx_ep, len);
 
        h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
        h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_WRITEMEM_32BIT;
@@ -1403,6 +1735,7 @@ static int stlink_usb_read_mem(void *handle, uint32_t addr, uint32_t size,
 {
        int retval = ERROR_OK;
        uint32_t bytes_remaining;
+       int retries = 0;
        struct stlink_usb_handle_s *h = handle;
 
        /* calculate byte count */
@@ -1416,6 +1749,11 @@ static int stlink_usb_read_mem(void *handle, uint32_t addr, uint32_t size,
                if (count < bytes_remaining)
                        bytes_remaining = count;
 
+               if (h->transport == HL_TRANSPORT_SWIM) {
+                       retval = stlink_swim_readbytes(handle, addr, bytes_remaining, buffer);
+                       if (retval != ERROR_OK)
+                               return retval;
+               } else
                /* the stlink only supports 8/32bit memory read/writes
                 * honour 32bit, all others will be handled as 8bit access */
                if (size == 4) {
@@ -1433,6 +1771,10 @@ static int stlink_usb_read_mem(void *handle, uint32_t addr, uint32_t size,
 
                                uint32_t head_bytes = 4 - (addr % 4);
                                retval = stlink_usb_read_mem8(handle, addr, head_bytes, buffer);
+                               if (retval == ERROR_WAIT && retries < MAX_WAIT_RETRIES) {
+                                       usleep((1<<retries++) * 1000);
+                                       continue;
+                               }
                                if (retval != ERROR_OK)
                                        return retval;
                                buffer += head_bytes;
@@ -1448,6 +1790,10 @@ static int stlink_usb_read_mem(void *handle, uint32_t addr, uint32_t size,
                } else
                        retval = stlink_usb_read_mem8(handle, addr, bytes_remaining, buffer);
 
+               if (retval == ERROR_WAIT && retries < MAX_WAIT_RETRIES) {
+                       usleep((1<<retries++) * 1000);
+                       continue;
+               }
                if (retval != ERROR_OK)
                        return retval;
 
@@ -1464,6 +1810,7 @@ static int stlink_usb_write_mem(void *handle, uint32_t addr, uint32_t size,
 {
        int retval = ERROR_OK;
        uint32_t bytes_remaining;
+       int retries = 0;
        struct stlink_usb_handle_s *h = handle;
 
        /* calculate byte count */
@@ -1477,6 +1824,11 @@ static int stlink_usb_write_mem(void *handle, uint32_t addr, uint32_t size,
                if (count < bytes_remaining)
                        bytes_remaining = count;
 
+               if (h->transport == HL_TRANSPORT_SWIM) {
+                       retval = stlink_swim_writebytes(handle, addr, bytes_remaining, buffer);
+                       if (retval != ERROR_OK)
+                               return retval;
+               } else
                /* the stlink only supports 8/32bit memory read/writes
                 * honour 32bit, all others will be handled as 8bit access */
                if (size == 4) {
@@ -1494,6 +1846,10 @@ static int stlink_usb_write_mem(void *handle, uint32_t addr, uint32_t size,
 
                                uint32_t head_bytes = 4 - (addr % 4);
                                retval = stlink_usb_write_mem8(handle, addr, head_bytes, buffer);
+                               if (retval == ERROR_WAIT && retries < MAX_WAIT_RETRIES) {
+                                       usleep((1<<retries++) * 1000);
+                                       continue;
+                               }
                                if (retval != ERROR_OK)
                                        return retval;
                                buffer += head_bytes;
@@ -1509,6 +1865,10 @@ static int stlink_usb_write_mem(void *handle, uint32_t addr, uint32_t size,
 
                } else
                        retval = stlink_usb_write_mem8(handle, addr, bytes_remaining, buffer);
+               if (retval == ERROR_WAIT && retries < MAX_WAIT_RETRIES) {
+                       usleep((1<<retries++) * 1000);
+                       continue;
+               }
                if (retval != ERROR_OK)
                        return retval;
 
@@ -1521,14 +1881,121 @@ static int stlink_usb_write_mem(void *handle, uint32_t addr, uint32_t size,
 }
 
 /** */
-static int stlink_usb_close(void *fd)
+static int stlink_usb_override_target(const char *targetname)
 {
-       struct stlink_usb_handle_s *h = fd;
+       return !strcmp(targetname, "cortex_m");
+}
+
+static int stlink_speed(void *handle, int khz, bool query)
+{
+       unsigned i;
+       int speed_index = -1;
+       int speed_diff = INT_MAX;
+       struct stlink_usb_handle_s *h = handle;
+
+       if (h && (h->transport == HL_TRANSPORT_SWIM)) {
+               /*
+                       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;
+       }
+
+       /* only supported by stlink/v2 and for firmware >= 22 */
+       if (h && (h->version.stlink == 1 || h->version.jtag < 22))
+               return khz;
+
+       for (i = 0; i < ARRAY_SIZE(stlink_khz_to_speed_map); i++) {
+               if (khz == stlink_khz_to_speed_map[i].speed) {
+                       speed_index = i;
+                       break;
+               } else {
+                       int current_diff = khz - stlink_khz_to_speed_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) {
+                               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;
+               match = false;
+       } else if (i == ARRAY_SIZE(stlink_khz_to_speed_map))
+               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);
+       }
 
-       if (h->fd)
+       if (h && !query) {
+               int result = stlink_usb_set_swdclk(h, stlink_khz_to_speed_map[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;
+}
+
+/** */
+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)
                jtag_libusb_close(h->fd);
 
-       free(fd);
+       free(h);
 
        return ERROR_OK;
 }
@@ -1551,11 +2018,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 };
-
-       LOG_DEBUG("transport: %d vid: 0x%04x pid: 0x%04x", param->transport,
-               param->vid, param->pid);
+       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)
@@ -1567,7 +2034,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, &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;
                }
@@ -1579,14 +2046,34 @@ static int stlink_usb_open(struct hl_interface_param_s *param, void **fd)
                        goto error_open;
                }
 
+               /* 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) {
-               case 0x3744:
-                       h->version.stlink = 1;
-                       break;
-               default:
-                       h->version.stlink = 2;
-                       break;
+               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_V2_1_PID:
+                               h->version.stlink = 2;
+                               h->tx_ep = STLINK_V2_1_TX_EP;
+                               h->trace_ep = STLINK_V2_1_TRACE_EP;
+                               break;
+                       default:
+                       /* fall through - we assume V2 to be the default version*/
+                       case STLINK_V2_PID:
+                               h->version.stlink = 2;
+                               h->tx_ep = STLINK_TX_EP;
+                               h->trace_ep = STLINK_TRACE_EP;
+                               break;
                }
 
                /* get the device version */
@@ -1621,12 +2108,6 @@ 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;
 
@@ -1657,25 +2138,35 @@ static int stlink_usb_open(struct hl_interface_param_s *param, void **fd)
        /* 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;
        }
 
+       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;
+       }
+
+       /* 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:");
+
+               for (unsigned i = 0; i < ARRAY_SIZE(stlink_khz_to_speed_map); i++)
+                       LOG_DEBUG("%d kHz", stlink_khz_to_speed_map[i].speed);
+
+               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);
@@ -1703,6 +2194,36 @@ error_open:
        return ERROR_FAIL;
 }
 
+int stlink_config_trace(void *handle, bool enabled, enum tpio_pin_protocol pin_protocol,
+                       uint32_t port_size, unsigned int *trace_freq)
+{
+       struct stlink_usb_handle_s *h = handle;
+
+       if (enabled && (h->jtag_api < 2 || 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 = {
        /** */
@@ -1734,5 +2255,13 @@ struct hl_layout_api_s stlink_usb_layout_api = {
        /** */
        .write_mem = stlink_usb_write_mem,
        /** */
-       .write_debug_reg = stlink_usb_write_debug_reg
+       .write_debug_reg = stlink_usb_write_debug_reg,
+       /** */
+       .override_target = stlink_usb_override_target,
+       /** */
+       .speed = stlink_speed,
+       /** */
+       .config_trace = stlink_config_trace,
+       /** */
+       .poll_trace = stlink_usb_trace_read,
 };