X-Git-Url: https://review.openocd.org/gitweb?p=openocd.git;a=blobdiff_plain;f=src%2Fjtag%2Fdrivers%2Fbitbang.c;h=1a0fa1c9d02f5a0121a42b041cb148444adcce75;hp=795764a4bb420652410daea56f07e5f735d0578f;hb=4d4d22e9d204960d570e82165b647ea0e3a727e9;hpb=08d4411b59dd8bd0e7d8009003b71d23acbf6eee diff --git a/src/jtag/drivers/bitbang.c b/src/jtag/drivers/bitbang.c index 795764a4bb..1a0fa1c9d0 100644 --- a/src/jtag/drivers/bitbang.c +++ b/src/jtag/drivers/bitbang.c @@ -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 #include +/* 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, +};