drivers/bitbang: add support for SWD multidrop
[fw/openocd] / src / jtag / drivers / bitbang.c
index 4417e1258f03decb1bd6fc431dae2d6cc6e5b62f..898d6d3df9c5d482d167348d8f6b8a8e45416023 100644 (file)
@@ -358,11 +358,10 @@ int bitbang_execute_queue(void)
                                        return ERROR_FAIL;
                                if (jtag_read_buffer(buffer, cmd->cmd.scan) != ERROR_OK)
                                        retval = ERROR_JTAG_QUEUE_FAILED;
-                               if (buffer)
-                                       free(buffer);
+                               free(buffer);
                                break;
                        case JTAG_SLEEP:
-                               LOG_DEBUG_IO("sleep %" PRIi32, cmd->cmd.sleep->us);
+                               LOG_DEBUG_IO("sleep %" PRIu32, cmd->cmd.sleep->us);
                                jtag_sleep(cmd->cmd.sleep->us);
                                break;
                        case JTAG_TMS:
@@ -394,6 +393,11 @@ static void bitbang_swd_exchange(bool rnw, uint8_t buf[], unsigned int offset, u
 {
        LOG_DEBUG("bitbang_swd_exchange");
 
+       if (bitbang_interface->blink) {
+               /* FIXME: we should manage errors */
+               bitbang_interface->blink(1);
+       }
+
        for (unsigned int i = offset; i < bit_cnt + offset; i++) {
                int bytec = i/8;
                int bcval = 1 << (i % 8);
@@ -410,6 +414,11 @@ static void bitbang_swd_exchange(bool rnw, uint8_t buf[], unsigned int offset, u
 
                bitbang_interface->swd_write(1, swdio);
        }
+
+       if (bitbang_interface->blink) {
+               /* FIXME: we should manage errors */
+               bitbang_interface->blink(0);
+       }
 }
 
 static int bitbang_swd_switch_seq(enum swd_special_seq seq)
@@ -425,10 +434,26 @@ static int bitbang_swd_switch_seq(enum swd_special_seq seq)
                LOG_DEBUG("JTAG-to-SWD");
                bitbang_swd_exchange(false, (uint8_t *)swd_seq_jtag_to_swd, 0, swd_seq_jtag_to_swd_len);
                break;
+       case JTAG_TO_DORMANT:
+               LOG_DEBUG("JTAG-to-DORMANT");
+               bitbang_swd_exchange(false, (uint8_t *)swd_seq_jtag_to_dormant, 0, swd_seq_jtag_to_dormant_len);
+               break;
        case SWD_TO_JTAG:
                LOG_DEBUG("SWD-to-JTAG");
                bitbang_swd_exchange(false, (uint8_t *)swd_seq_swd_to_jtag, 0, swd_seq_swd_to_jtag_len);
                break;
+       case SWD_TO_DORMANT:
+               LOG_DEBUG("SWD-to-DORMANT");
+               bitbang_swd_exchange(false, (uint8_t *)swd_seq_swd_to_dormant, 0, swd_seq_swd_to_dormant_len);
+               break;
+       case DORMANT_TO_SWD:
+               LOG_DEBUG("DORMANT-to-SWD");
+               bitbang_swd_exchange(false, (uint8_t *)swd_seq_dormant_to_swd, 0, swd_seq_dormant_to_swd_len);
+               break;
+       case DORMANT_TO_JTAG:
+               LOG_DEBUG("DORMANT-to-JTAG");
+               bitbang_swd_exchange(false, (uint8_t *)swd_seq_dormant_to_jtag, 0, swd_seq_dormant_to_jtag_len);
+               break;
        default:
                LOG_ERROR("Sequence %d not supported", seq);
                return ERROR_FAIL;
@@ -446,7 +471,7 @@ static void swd_clear_sticky_errors(void)
 static void bitbang_swd_read_reg(uint8_t cmd, uint32_t *value, uint32_t ap_delay_clk)
 {
        LOG_DEBUG("bitbang_swd_read_reg");
-       assert(cmd & SWD_CMD_RnW);
+       assert(cmd & SWD_CMD_RNW);
 
        if (queued_retval != ERROR_OK) {
                LOG_DEBUG("Skip bitbang_swd_read_reg because queued_retval=%d", queued_retval);
@@ -456,7 +481,7 @@ static void bitbang_swd_read_reg(uint8_t cmd, uint32_t *value, uint32_t ap_delay
        for (;;) {
                uint8_t trn_ack_data_parity_trn[DIV_ROUND_UP(4 + 3 + 32 + 1 + 4, 8)];
 
-               cmd |= SWD_CMD_START | (1 << 7);
+               cmd |= SWD_CMD_START | SWD_CMD_PARK;
                bitbang_swd_exchange(false, &cmd, 0, 8);
 
                bitbang_interface->swdio_drive(false);
@@ -467,57 +492,52 @@ static void bitbang_swd_read_reg(uint8_t cmd, uint32_t *value, uint32_t ap_delay
                uint32_t data = buf_get_u32(trn_ack_data_parity_trn, 1 + 3, 32);
                int parity = buf_get_u32(trn_ack_data_parity_trn, 1 + 3 + 32, 1);
 
-               LOG_DEBUG("%s %s %s reg %X = %08"PRIx32,
+               LOG_DEBUG("%s %s read reg %X = %08"PRIx32,
                          ack == SWD_ACK_OK ? "OK" : ack == SWD_ACK_WAIT ? "WAIT" : ack == SWD_ACK_FAULT ? "FAULT" : "JUNK",
-                         cmd & SWD_CMD_APnDP ? "AP" : "DP",
-                         cmd & SWD_CMD_RnW ? "read" : "write",
+                         cmd & SWD_CMD_APNDP ? "AP" : "DP",
                          (cmd & SWD_CMD_A32) >> 1,
                          data);
 
-               switch (ack) {
-                case SWD_ACK_OK:
-                       if (parity != parity_u32(data)) {
-                               LOG_DEBUG("Wrong parity detected");
-                               queued_retval = ERROR_FAIL;
-                               return;
-                       }
-                       if (value)
-                               *value = data;
-                       if (cmd & SWD_CMD_APnDP)
-                               bitbang_swd_exchange(true, NULL, 0, ap_delay_clk);
-                       return;
-                case SWD_ACK_WAIT:
-                       LOG_DEBUG("SWD_ACK_WAIT");
+               if (ack == SWD_ACK_WAIT) {
                        swd_clear_sticky_errors();
-                       break;
-                case SWD_ACK_FAULT:
-                       LOG_DEBUG("SWD_ACK_FAULT");
-                       queued_retval = ack;
+                       continue;
+               } else if (ack != SWD_ACK_OK) {
+                       queued_retval = swd_ack_to_error_code(ack);
                        return;
-                default:
-                       LOG_DEBUG("No valid acknowledge: ack=%d", ack);
-                       queued_retval = ack;
+               }
+
+               if (parity != parity_u32(data)) {
+                       LOG_ERROR("Wrong parity detected");
+                       queued_retval = ERROR_FAIL;
                        return;
                }
+               if (value)
+                       *value = data;
+               if (cmd & SWD_CMD_APNDP)
+                       bitbang_swd_exchange(true, NULL, 0, ap_delay_clk);
+               return;
        }
 }
 
 static void bitbang_swd_write_reg(uint8_t cmd, uint32_t value, uint32_t ap_delay_clk)
 {
        LOG_DEBUG("bitbang_swd_write_reg");
-       assert(!(cmd & SWD_CMD_RnW));
+       assert(!(cmd & SWD_CMD_RNW));
 
        if (queued_retval != ERROR_OK) {
                LOG_DEBUG("Skip bitbang_swd_write_reg because queued_retval=%d", queued_retval);
                return;
        }
 
+       /* Devices do not reply to DP_TARGETSEL write cmd, ignore received ack */
+       bool check_ack = swd_cmd_returns_ack(cmd);
+
        for (;;) {
                uint8_t trn_ack_data_parity_trn[DIV_ROUND_UP(4 + 3 + 32 + 1 + 4, 8)];
                buf_set_u32(trn_ack_data_parity_trn, 1 + 3 + 1, 32, value);
                buf_set_u32(trn_ack_data_parity_trn, 1 + 3 + 1 + 32, 1, parity_u32(value));
 
-               cmd |= SWD_CMD_START | (1 << 7);
+               cmd |= SWD_CMD_START | SWD_CMD_PARK;
                bitbang_swd_exchange(false, &cmd, 0, 8);
 
                bitbang_interface->swdio_drive(false);
@@ -526,31 +546,27 @@ static void bitbang_swd_write_reg(uint8_t cmd, uint32_t value, uint32_t ap_delay
                bitbang_swd_exchange(false, trn_ack_data_parity_trn, 1 + 3 + 1, 32 + 1);
 
                int ack = buf_get_u32(trn_ack_data_parity_trn, 1, 3);
-               LOG_DEBUG("%s %s %s reg %X = %08"PRIx32,
+
+               LOG_DEBUG("%s%s %s write reg %X = %08"PRIx32,
+                         check_ack ? "" : "ack ignored ",
                          ack == SWD_ACK_OK ? "OK" : ack == SWD_ACK_WAIT ? "WAIT" : ack == SWD_ACK_FAULT ? "FAULT" : "JUNK",
-                         cmd & SWD_CMD_APnDP ? "AP" : "DP",
-                         cmd & SWD_CMD_RnW ? "read" : "write",
+                         cmd & SWD_CMD_APNDP ? "AP" : "DP",
                          (cmd & SWD_CMD_A32) >> 1,
                          buf_get_u32(trn_ack_data_parity_trn, 1 + 3 + 1, 32));
 
-               switch (ack) {
-                case SWD_ACK_OK:
-                       if (cmd & SWD_CMD_APnDP)
-                               bitbang_swd_exchange(true, NULL, 0, ap_delay_clk);
-                       return;
-                case SWD_ACK_WAIT:
-                       LOG_DEBUG("SWD_ACK_WAIT");
-                       swd_clear_sticky_errors();
-                       break;
-                case SWD_ACK_FAULT:
-                       LOG_DEBUG("SWD_ACK_FAULT");
-                       queued_retval = ack;
-                       return;
-                default:
-                       LOG_DEBUG("No valid acknowledge: ack=%d", ack);
-                       queued_retval = ack;
-                       return;
+               if (check_ack) {
+                       if (ack == SWD_ACK_WAIT) {
+                               swd_clear_sticky_errors();
+                               continue;
+                       } else if (ack != SWD_ACK_OK) {
+                               queued_retval = swd_ack_to_error_code(ack);
+                               return;
+                       }
                }
+
+               if (cmd & SWD_CMD_APNDP)
+                       bitbang_swd_exchange(true, NULL, 0, ap_delay_clk);
+               return;
        }
 }