ftdi: allow selecting device by usb bus location
[fw/openocd] / src / jtag / drivers / bitbang.c
index 795764a4bb420652410daea56f07e5f735d0578f..1a0fa1c9d02f5a0121a42b041cb148444adcce75 100644 (file)
@@ -21,6 +21,9 @@
  *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.           *
  ***************************************************************************/
 
+/* 2014-12: Addition of the SWD protocol support is based on the initial work
+ * by Paul Fertser and modifications by Jean-Christian de Rivaz. */
+
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
@@ -29,6 +32,9 @@
 #include <jtag/interface.h>
 #include <jtag/commands.h>
 
+/* YUK! - but this is currently a global.... */
+extern struct jtag_interface *jtag_interface;
+
 /**
  * Function bitbang_stableclocks
  * issues a number of clock cycles while staying in a stable state.
@@ -39,6 +45,8 @@
  */
 static void bitbang_stableclocks(int num_cycles);
 
+static void bitbang_swd_write_reg(uint8_t cmd, uint32_t value, uint32_t ap_delay_clk);
+
 struct bitbang_interface *bitbang_interface;
 
 /* DANGER!!!! clock absolutely *MUST* be 0 in idle or reset won't work!
@@ -337,3 +345,206 @@ int bitbang_execute_queue(void)
 
        return retval;
 }
+
+
+bool swd_mode;
+static int queued_retval;
+
+static int bitbang_swd_init(void)
+{
+       LOG_DEBUG("bitbang_swd_init");
+       swd_mode = true;
+       return ERROR_OK;
+}
+
+static void bitbang_exchange(bool rnw, uint8_t buf[], unsigned int offset, unsigned int bit_cnt)
+{
+       LOG_DEBUG("bitbang_exchange");
+       int tdi;
+
+       for (unsigned int i = offset; i < bit_cnt + offset; i++) {
+               int bytec = i/8;
+               int bcval = 1 << (i % 8);
+               tdi = !rnw && (buf[bytec] & bcval);
+
+               bitbang_interface->write(0, 0, tdi);
+
+               if (rnw && buf) {
+                       if (bitbang_interface->swdio_read())
+                               buf[bytec] |= bcval;
+                       else
+                               buf[bytec] &= ~bcval;
+               }
+
+               bitbang_interface->write(1, 0, tdi);
+       }
+}
+
+int bitbang_swd_switch_seq(enum swd_special_seq seq)
+{
+       LOG_DEBUG("bitbang_swd_switch_seq");
+
+       switch (seq) {
+       case LINE_RESET:
+               LOG_DEBUG("SWD line reset");
+               bitbang_exchange(false, (uint8_t *)swd_seq_line_reset, 0, swd_seq_line_reset_len);
+               break;
+       case JTAG_TO_SWD:
+               LOG_DEBUG("JTAG-to-SWD");
+               bitbang_exchange(false, (uint8_t *)swd_seq_jtag_to_swd, 0, swd_seq_jtag_to_swd_len);
+               break;
+       case SWD_TO_JTAG:
+               LOG_DEBUG("SWD-to-JTAG");
+               bitbang_exchange(false, (uint8_t *)swd_seq_swd_to_jtag, 0, swd_seq_swd_to_jtag_len);
+               break;
+       default:
+               LOG_ERROR("Sequence %d not supported", seq);
+               return ERROR_FAIL;
+       }
+
+       return ERROR_OK;
+}
+
+void bitbang_switch_to_swd(void)
+{
+       LOG_DEBUG("bitbang_switch_to_swd");
+       bitbang_exchange(false, (uint8_t *)swd_seq_jtag_to_swd, 0, swd_seq_jtag_to_swd_len);
+}
+
+static void swd_clear_sticky_errors(void)
+{
+       bitbang_swd_write_reg(swd_cmd(false,  false, DP_ABORT),
+               STKCMPCLR | STKERRCLR | WDERRCLR | ORUNERRCLR, 0);
+}
+
+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);
+
+       if (queued_retval != ERROR_OK) {
+               LOG_DEBUG("Skip bitbang_swd_read_reg because queued_retval=%d", queued_retval);
+               return;
+       }
+
+       for (;;) {
+               uint8_t trn_ack_data_parity_trn[DIV_ROUND_UP(4 + 3 + 32 + 1 + 4, 8)];
+
+               cmd |= SWD_CMD_START | (1 << 7);
+               bitbang_exchange(false, &cmd, 0, 8);
+
+               bitbang_interface->swdio_drive(false);
+               bitbang_exchange(true, trn_ack_data_parity_trn, 0, 1 + 3 + 32 + 1 + 1);
+               bitbang_interface->swdio_drive(true);
+
+               int ack = buf_get_u32(trn_ack_data_parity_trn, 1, 3);
+               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,
+                         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_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_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;
+               }
+       }
+}
+
+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));
+
+       if (queued_retval != ERROR_OK) {
+               LOG_DEBUG("Skip bitbang_swd_write_reg because queued_retval=%d", queued_retval);
+               return;
+       }
+
+       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);
+               bitbang_exchange(false, &cmd, 0, 8);
+
+               bitbang_interface->swdio_drive(false);
+               bitbang_exchange(true, trn_ack_data_parity_trn, 0, 1 + 3 + 1);
+               bitbang_interface->swdio_drive(true);
+               bitbang_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,
+                         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_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_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;
+               }
+       }
+}
+
+static int bitbang_swd_run_queue(void)
+{
+       LOG_DEBUG("bitbang_swd_run_queue");
+       /* A transaction must be followed by another transaction or at least 8 idle cycles to
+        * ensure that data is clocked through the AP. */
+       bitbang_exchange(true, NULL, 0, 8);
+
+       int retval = queued_retval;
+       queued_retval = ERROR_OK;
+       LOG_DEBUG("SWD queue return value: %02x", retval);
+       return retval;
+}
+
+const struct swd_driver bitbang_swd = {
+       .init = bitbang_swd_init,
+       .switch_seq = bitbang_swd_switch_seq,
+       .read_reg = bitbang_swd_read_reg,
+       .write_reg = bitbang_swd_write_reg,
+       .run = bitbang_swd_run_queue,
+};