Cleanup of config/includes.
[fw/openocd] / src / jtag / drivers / cmsis_dap.c
index eb973642ca230d87e3c7db7d242a6bc677e06320..26e576b8edfd0c21fa568f5e653a8811326238cc 100644 (file)
@@ -36,6 +36,7 @@
 #endif
 
 #include <transport/transport.h>
+#include "helper/replacements.h"
 #include <jtag/swd.h>
 #include <jtag/interface.h>
 #include <jtag/commands.h>
@@ -136,6 +137,7 @@ static bool swd_mode;
 
 /* CMSIS-DAP SWD Commands */
 #define CMD_DAP_SWD_CONFIGURE     0x13
+#define CMD_DAP_SWD_SEQUENCE      0x1D
 
 /* CMSIS-DAP JTAG Commands */
 #define CMD_DAP_JTAG_SEQ          0x14
@@ -221,6 +223,8 @@ static uint8_t output_pins = SWJ_PIN_SRST | SWJ_PIN_TRST;
 static struct cmsis_dap *cmsis_dap_handle;
 
 
+static int cmsis_dap_quit(void);
+
 static int cmsis_dap_open(void)
 {
        const struct cmsis_dap_backend *backend = NULL;
@@ -279,6 +283,21 @@ static void cmsis_dap_close(struct cmsis_dap *dap)
        }
 }
 
+static void cmsis_dap_flush_read(struct cmsis_dap *dap)
+{
+       unsigned int i;
+       /* Some CMSIS-DAP adapters keep buffered packets over
+        * USB close/open so we need to flush up to 64 old packets
+        * to be sure all buffers are empty */
+       for (i = 0; i < 64; i++) {
+               int retval = dap->backend->read(dap, 10);
+               if (retval == ERROR_TIMEOUT_REACHED)
+                       break;
+       }
+       if (i)
+               LOG_DEBUG("Flushed %u packets", i);
+}
+
 /* Send a message and receive the reply */
 static int cmsis_dap_xfer(struct cmsis_dap *dap, int txlen)
 {
@@ -292,6 +311,7 @@ static int cmsis_dap_xfer(struct cmsis_dap *dap, int txlen)
                pending_fifo_get_idx = 0;
        }
 
+       uint8_t current_cmd = cmsis_dap_handle->command[0];
        int retval = dap->backend->write(dap, txlen, USB_TIMEOUT);
        if (retval < 0)
                return retval;
@@ -301,6 +321,20 @@ static int cmsis_dap_xfer(struct cmsis_dap *dap, int txlen)
        if (retval < 0)
                return retval;
 
+       uint8_t *resp = cmsis_dap_handle->response;
+       if (resp[0] == DAP_ERROR) {
+               LOG_ERROR("CMSIS-DAP command 0x%" PRIx8 " not implemented", current_cmd);
+               return ERROR_NOT_IMPLEMENTED;
+       }
+
+       if (resp[0] != current_cmd) {
+               LOG_ERROR("CMSIS-DAP command mismatch. Sent 0x%" PRIx8
+                        " received 0x%" PRIx8, current_cmd, resp[0]);
+
+               cmsis_dap_flush_read(dap);
+               return ERROR_FAIL;
+       }
+
        return ERROR_OK;
 }
 
@@ -510,6 +544,45 @@ static int cmsis_dap_cmd_DAP_Delay(uint16_t delay_us)
 }
 #endif
 
+static int cmsis_dap_metacmd_targetsel(uint32_t instance_id)
+{
+       int retval;
+       uint8_t *command = cmsis_dap_handle->command;
+       const uint32_t SEQ_RD = 0x80, SEQ_WR = 0x00;
+
+       /* SWD multi-drop requires a transfer ala CMD_DAP_TFER,
+       but with no expectation of an SWD ACK response.  In
+       CMSIS-DAP v1.20 and v2.00, CMD_DAP_SWD_SEQUENCE was
+       added to allow this special sequence to be generated.
+       The purpose of this operation is to select the target
+       corresponding to the instance_id that is written */
+
+       size_t idx = 0;
+       command[idx++] = CMD_DAP_SWD_SEQUENCE;
+       command[idx++] = 3;     /* sequence count */
+
+       /* sequence 0: packet request for TARGETSEL */
+       command[idx++] = SEQ_WR | 8;
+       command[idx++] = SWD_CMD_START | swd_cmd(false, false, DP_TARGETSEL) | SWD_CMD_STOP | SWD_CMD_PARK;
+
+       /* sequence 1: read Trn ACK Trn, no expectation for target to ACK  */
+       command[idx++] = SEQ_RD | 5;
+
+       /* sequence 2: WDATA plus parity */
+       command[idx++] = SEQ_WR | (32 + 1);
+       h_u32_to_le(command + idx, instance_id);
+       idx += 4;
+       command[idx++] = parity_u32(instance_id);
+
+       retval = cmsis_dap_xfer(cmsis_dap_handle, idx);
+
+       if (retval != ERROR_OK || cmsis_dap_handle->response[1] != DAP_OK) {
+               LOG_ERROR("CMSIS-DAP command SWD_Sequence failed.");
+               return ERROR_JTAG_DEVICE_ERROR;
+       }
+       return ERROR_OK;
+}
+
 static void cmsis_dap_swd_write_from_queue(struct cmsis_dap *dap)
 {
        uint8_t *command = cmsis_dap_handle->command;
@@ -605,6 +678,13 @@ static void cmsis_dap_swd_read_process(struct cmsis_dap *dap, int timeout_ms)
        }
 
        uint8_t *resp = dap->response;
+       if (resp[0] != CMD_DAP_TFER) {
+               LOG_ERROR("CMSIS-DAP command mismatch. Expected 0x%" PRIx8
+                        " received 0x%" PRIx8, CMD_DAP_TFER, resp[0]);
+               queued_retval = ERROR_FAIL;
+               goto skip;
+       }
+
        uint8_t transfer_count = resp[1];
        uint8_t ack = resp[2] & 0x07;
        if (resp[2] & 0x08) {
@@ -616,6 +696,7 @@ static void cmsis_dap_swd_read_process(struct cmsis_dap *dap, int timeout_ms)
                LOG_DEBUG("SWD ack not OK @ %d %s", transfer_count,
                          ack == SWD_ACK_WAIT ? "WAIT" : ack == SWD_ACK_FAULT ? "FAULT" : "JUNK");
                queued_retval = ack == SWD_ACK_WAIT ? ERROR_WAIT : ERROR_FAIL;
+               /* TODO: use results of transfers completed before the error occurred? */
                goto skip;
        }
 
@@ -675,7 +756,10 @@ static int cmsis_dap_swd_run_queue(void)
 
 static void cmsis_dap_swd_queue_cmd(uint8_t cmd, uint32_t *dst, uint32_t data)
 {
-       if (pending_fifo[pending_fifo_put_idx].transfer_count == pending_queue_len) {
+       bool targetsel_cmd = swd_cmd(false, false, DP_TARGETSEL) == cmd;
+
+       if (pending_fifo[pending_fifo_put_idx].transfer_count == pending_queue_len
+                        || targetsel_cmd) {
                if (pending_fifo_block_count)
                        cmsis_dap_swd_read_process(cmsis_dap_handle, 0);
 
@@ -689,6 +773,11 @@ static void cmsis_dap_swd_queue_cmd(uint8_t cmd, uint32_t *dst, uint32_t data)
        if (queued_retval != ERROR_OK)
                return;
 
+       if (targetsel_cmd) {
+               cmsis_dap_metacmd_targetsel(data);
+               return;
+       }
+
        struct pending_request_block *block = &pending_fifo[pending_fifo_put_idx];
        struct pending_transfer_result *transfer = &(block->transfers[block->transfer_count]);
        transfer->data = data;
@@ -806,7 +895,7 @@ static int cmsis_dap_swd_switch_seq(enum swd_special_seq seq)
 
        switch (seq) {
        case LINE_RESET:
-               LOG_DEBUG("SWD line reset");
+               LOG_DEBUG_IO("SWD line reset");
                s = swd_seq_line_reset;
                s_len = swd_seq_line_reset_len;
                break;
@@ -815,11 +904,26 @@ static int cmsis_dap_swd_switch_seq(enum swd_special_seq seq)
                s = swd_seq_jtag_to_swd;
                s_len = swd_seq_jtag_to_swd_len;
                break;
+       case JTAG_TO_DORMANT:
+               LOG_DEBUG("JTAG-to-DORMANT");
+               s = swd_seq_jtag_to_dormant;
+               s_len = swd_seq_jtag_to_dormant_len;
+               break;
        case SWD_TO_JTAG:
                LOG_DEBUG("SWD-to-JTAG");
                s = swd_seq_swd_to_jtag;
                s_len = swd_seq_swd_to_jtag_len;
                break;
+       case SWD_TO_DORMANT:
+               LOG_DEBUG("SWD-to-DORMANT");
+               s = swd_seq_swd_to_dormant;
+               s_len = swd_seq_swd_to_dormant_len;
+               break;
+       case DORMANT_TO_SWD:
+               LOG_DEBUG("DORMANT-to-SWD");
+               s = swd_seq_dormant_to_swd;
+               s_len = swd_seq_dormant_to_swd_len;
+               break;
        default:
                LOG_ERROR("Sequence %d not supported", seq);
                return ERROR_FAIL;
@@ -862,6 +966,8 @@ static int cmsis_dap_init(void)
        if (retval != ERROR_OK)
                return retval;
 
+       cmsis_dap_flush_read(cmsis_dap_handle);
+
        retval = cmsis_dap_get_caps_info();
        if (retval != ERROR_OK)
                return retval;
@@ -900,7 +1006,7 @@ static int cmsis_dap_init(void)
        /* INFO_ID_PKT_SZ - short */
        retval = cmsis_dap_cmd_DAP_Info(INFO_ID_PKT_SZ, &data);
        if (retval != ERROR_OK)
-               return retval;
+               goto init_err;
 
        if (data[0] == 2) {  /* short */
                uint16_t pkt_sz = data[1] + (data[2] << 8);
@@ -914,7 +1020,7 @@ static int cmsis_dap_init(void)
                        free(cmsis_dap_handle->packet_buffer);
                        retval = cmsis_dap_handle->backend->packet_buffer_alloc(cmsis_dap_handle, pkt_sz);
                        if (retval != ERROR_OK)
-                               return retval;
+                               goto init_err;
 
                        LOG_DEBUG("CMSIS-DAP: Packet Size = %" PRIu16, pkt_sz);
                }
@@ -923,7 +1029,7 @@ static int cmsis_dap_init(void)
        /* INFO_ID_PKT_CNT - byte */
        retval = cmsis_dap_cmd_DAP_Info(INFO_ID_PKT_CNT, &data);
        if (retval != ERROR_OK)
-               return retval;
+               goto init_err;
 
        if (data[0] == 1) { /* byte */
                int pkt_cnt = data[1];
@@ -938,43 +1044,40 @@ static int cmsis_dap_init(void)
                pending_fifo[i].transfers = malloc(pending_queue_len * sizeof(struct pending_transfer_result));
                if (!pending_fifo[i].transfers) {
                        LOG_ERROR("Unable to allocate memory for CMSIS-DAP queue");
-                       return ERROR_FAIL;
+                       retval = ERROR_FAIL;
+                       goto init_err;
                }
        }
 
-
-       retval = cmsis_dap_get_status();
-       if (retval != ERROR_OK)
-               return ERROR_FAIL;
+       /* Intentionally not checked for error, just logs an info message
+        * not vital for further debugging */
+       (void)cmsis_dap_get_status();
 
        /* Now try to connect to the target
         * TODO: This is all SWD only @ present */
        retval = cmsis_dap_cmd_DAP_SWJ_Clock(jtag_get_speed_khz());
        if (retval != ERROR_OK)
-               return ERROR_FAIL;
+               goto init_err;
 
        /* Ask CMSIS-DAP to automatically retry on receiving WAIT for
         * up to 64 times. This must be changed to 0 if sticky
         * overrun detection is enabled. */
        retval = cmsis_dap_cmd_DAP_TFER_Configure(0, 64, 0);
        if (retval != ERROR_OK)
-               return ERROR_FAIL;
+               goto init_err;
 
        if (swd_mode) {
                /* Data Phase (bit 2) must be set to 1 if sticky overrun
                 * detection is enabled */
                retval = cmsis_dap_cmd_DAP_SWD_Configure(0);    /* 1 TRN, no Data Phase */
                if (retval != ERROR_OK)
-                       return ERROR_FAIL;
+                       goto init_err;
        }
        /* Both LEDs on */
-       retval = cmsis_dap_cmd_DAP_LED(LED_ID_CONNECT, LED_ON);
-       if (retval != ERROR_OK)
-               return ERROR_FAIL;
-
-       retval = cmsis_dap_cmd_DAP_LED(LED_ID_RUN, LED_ON);
-       if (retval != ERROR_OK)
-               return ERROR_FAIL;
+       /* Intentionally not checked for error, debugging will work
+        * without LEDs */
+       (void)cmsis_dap_cmd_DAP_LED(LED_ID_CONNECT, LED_ON);
+       (void)cmsis_dap_cmd_DAP_LED(LED_ID_RUN, LED_ON);
 
        /* support connecting with srst asserted */
        enum reset_types jtag_reset_config = jtag_get_reset_config();
@@ -983,13 +1086,16 @@ static int cmsis_dap_init(void)
                if (jtag_reset_config & RESET_SRST_NO_GATING) {
                        retval = cmsis_dap_cmd_DAP_SWJ_Pins(0, SWJ_PIN_SRST, 0, NULL);
                        if (retval != ERROR_OK)
-                               return ERROR_FAIL;
+                               goto init_err;
                        LOG_INFO("Connecting under reset");
                }
        }
        LOG_INFO("CMSIS-DAP: Interface ready");
-
        return ERROR_OK;
+
+init_err:
+       cmsis_dap_quit();
+       return retval;
 }
 
 static int cmsis_dap_swd_init(void)