X-Git-Url: https://review.openocd.org/gitweb?p=openocd.git;a=blobdiff_plain;f=src%2Fjtag%2Fdrivers%2Fbitbang.c;h=1a0fa1c9d02f5a0121a42b041cb148444adcce75;hp=8a097ca1b165aaac25475ec88090a2b876c656c0;hb=a6c4eb03455f6e97fc25183aae249d6ccdcbfb0f;hpb=de0130a0aad83c1ef692ee4d68ab996a8668424d diff --git a/src/jtag/drivers/bitbang.c b/src/jtag/drivers/bitbang.c index 8a097ca1b1..1a0fa1c9d0 100644 --- a/src/jtag/drivers/bitbang.c +++ b/src/jtag/drivers/bitbang.c @@ -18,9 +18,12 @@ * 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., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + * 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, +};