+// SPDX-License-Identifier: GPL-2.0-or-later
+
/***************************************************************************
* Copyright (C) 2005 by Dominic Rath *
* Dominic.Rath@gmx.de *
* *
* Copyright (C) 2007,2008 Øyvind Harboe *
* oyvind.harboe@zylin.com *
- * *
- * This program is free software; you can redistribute it and/or modify *
- * it under the terms of the GNU General Public License as published by *
- * the Free Software Foundation; either version 2 of the License, or *
- * (at your option) any later version. *
- * *
- * This program is distributed in the hope that it will be useful, *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
- * GNU General Public License for more details. *
- * *
- * You should have received a copy of the GNU General Public License *
- * along with this program. If not, see <http://www.gnu.org/licenses/>. *
***************************************************************************/
/* 2014-12: Addition of the SWD protocol support is based on the initial work
return ERROR_OK;
}
+static void bitbang_sleep(unsigned int microseconds)
+{
+ if (bitbang_interface->sleep) {
+ bitbang_interface->sleep(microseconds);
+ } else {
+ jtag_sleep(microseconds);
+ }
+}
+
int bitbang_execute_queue(void)
{
struct jtag_command *cmd = jtag_command_queue; /* currently processed command */
break;
case JTAG_SLEEP:
LOG_DEBUG_IO("sleep %" PRIu32, cmd->cmd.sleep->us);
- jtag_sleep(cmd->cmd.sleep->us);
+ if (bitbang_interface->flush && (bitbang_interface->flush() != ERROR_OK))
+ return ERROR_FAIL;
+ bitbang_sleep(cmd->cmd.sleep->us);
break;
case JTAG_TMS:
retval = bitbang_execute_tms(cmd);
static void bitbang_swd_exchange(bool rnw, uint8_t buf[], unsigned int offset, unsigned int bit_cnt)
{
- LOG_DEBUG("bitbang_swd_exchange");
-
if (bitbang_interface->blink) {
/* FIXME: we should manage errors */
bitbang_interface->blink(1);
static 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");
+ LOG_DEBUG_IO("SWD line reset");
bitbang_swd_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_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;
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) {
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);
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_IO("%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_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));
if (queued_retval != ERROR_OK) {
return;
}
+ /* Devices do not reply to DP_TARGETSEL write cmd, ignore received ack */
+ bool check_ack = swd_cmd_returns_ack(cmd);
+
+ /* init the array to silence scan-build */
+ uint8_t trn_ack_data_parity_trn[DIV_ROUND_UP(4 + 3 + 32 + 1 + 4, 8)] = {0};
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);
- bitbang_swd_exchange(true, trn_ack_data_parity_trn, 0, 1 + 3 + 1);
+ bitbang_swd_exchange(true, trn_ack_data_parity_trn, 0, 1 + 3);
+
+ /* Avoid a glitch on SWDIO when changing the direction to output.
+ * To keep performance penalty minimal, pre-write the first data
+ * bit to SWDIO GPIO output buffer while clocking the turnaround bit.
+ * Following swdio_drive(true) outputs the pre-written value
+ * and the same value is rewritten by the next swd_write()
+ * instead of glitching SWDIO
+ * HiZ/pull-up --------------> 0 -------------> 1
+ * swdio_drive(true) swd_write(0,1)
+ * in case of data bit 0 = 1
+ */
+ bitbang_swd_exchange(false, trn_ack_data_parity_trn, 1 + 3 + 1, 1);
bitbang_interface->swdio_drive(true);
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_IO("%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_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;
}
}
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_swd_exchange(true, NULL, 0, 8);
int retval = queued_retval;
queued_retval = ERROR_OK;
- LOG_DEBUG("SWD queue return value: %02x", retval);
+ LOG_DEBUG_IO("SWD queue return value: %02x", retval);
return retval;
}