X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=src%2Ftarget%2Fadi_v5_swd.c;h=a21bf25b9174c4e87b706470765bdb515068c848;hb=48d74f97114a76563753918da8708d45098fbf1d;hp=d099f4e710021153b179c8bc4dcb0bb9d510010f;hpb=420a692e0fae34a59eb3c33932362812e33f5583;p=fw%2Fopenocd diff --git a/src/target/adi_v5_swd.c b/src/target/adi_v5_swd.c index d099f4e71..a21bf25b9 100644 --- a/src/target/adi_v5_swd.c +++ b/src/target/adi_v5_swd.c @@ -58,7 +58,7 @@ static bool do_sync; static void swd_finish_read(struct adiv5_dap *dap) { const struct swd_driver *swd = adiv5_dap_swd_driver(dap); - if (dap->last_read != NULL) { + if (dap->last_read) { swd->read_reg(swd_cmd(true, false, DP_RDBUFF), dap->last_read, 0); dap->last_read = NULL; } @@ -74,7 +74,7 @@ static void swd_clear_sticky_errors(struct adiv5_dap *dap) const struct swd_driver *swd = adiv5_dap_swd_driver(dap); assert(swd); - swd->write_reg(swd_cmd(false, false, DP_ABORT), + swd->write_reg(swd_cmd(false, false, DP_ABORT), STKCMPCLR | STKERRCLR | WDERRCLR | ORUNERRCLR, 0); } @@ -96,7 +96,7 @@ static int swd_run_inner(struct adiv5_dap *dap) static int swd_connect(struct adiv5_dap *dap) { const struct swd_driver *swd = adiv5_dap_swd_driver(dap); - uint32_t dpidr; + uint32_t dpidr = 0xdeadbeef; int status; /* FIXME validate transport config ... is the @@ -112,36 +112,87 @@ static int swd_connect(struct adiv5_dap *dap) if (jtag_reset_config & RESET_CNCT_UNDER_SRST) { if (jtag_reset_config & RESET_SRST_NO_GATING) - swd_add_reset(1); + adapter_assert_reset(); else LOG_WARNING("\'srst_nogate\' reset_config option is required"); } } - /* Note, debugport_init() does setup too */ - swd->switch_seq(JTAG_TO_SWD); - /* Clear link state, including the SELECT cache. */ - dap->do_reconnect = false; - dap_invalidate_cache(dap); + int64_t timeout = timeval_ms() + 500; + + do { + /* Note, debugport_init() does setup too */ + swd->switch_seq(JTAG_TO_SWD); + + /* Clear link state, including the SELECT cache. */ + dap->do_reconnect = false; + dap_invalidate_cache(dap); + + status = swd_queue_dp_read(dap, DP_DPIDR, &dpidr); + if (status == ERROR_OK) { + status = swd_run_inner(dap); + if (status == ERROR_OK) + break; + } + + alive_sleep(1); + + } while (timeval_ms() < timeout); + + if (status != ERROR_OK) { + LOG_ERROR("Error connecting DP: cannot read IDR"); + return status; + } + + LOG_INFO("SWD DPIDR %#8.8" PRIx32, dpidr); + + do { + dap->do_reconnect = false; + + /* force clear all sticky faults */ + swd_clear_sticky_errors(dap); + + status = swd_run_inner(dap); + if (status != ERROR_WAIT) + break; - swd_queue_dp_read(dap, DP_DPIDR, &dpidr); + alive_sleep(10); - /* force clear all sticky faults */ - swd_clear_sticky_errors(dap); + } while (timeval_ms() < timeout); - status = swd_run_inner(dap); + /* IHI 0031E B4.3.2: + * "A WAIT response must not be issued to the ... + * ... writes to the ABORT register" + * swd_clear_sticky_errors() writes to the ABORT register only. + * + * Unfortunately at least Microchip SAMD51/E53/E54 returns WAIT + * in a corner case. Just try if ABORT resolves the problem. + */ + if (status == ERROR_WAIT) { + LOG_WARNING("Connecting DP: stalled AP operation, issuing ABORT"); - if (status == ERROR_OK) { - LOG_INFO("SWD DPIDR %#8.8" PRIx32, dpidr); dap->do_reconnect = false; + + swd->write_reg(swd_cmd(false, false, DP_ABORT), + DAPABORT | STKCMPCLR | STKERRCLR | WDERRCLR | ORUNERRCLR, 0); + status = swd_run_inner(dap); + } + + if (status == ERROR_OK) status = dap_dp_init(dap); - } else - dap->do_reconnect = true; return status; } +static int swd_send_sequence(struct adiv5_dap *dap, enum swd_special_seq seq) +{ + const struct swd_driver *swd = adiv5_dap_swd_driver(dap); + assert(swd); + + return swd->switch_seq(seq); +} + static inline int check_sync(struct adiv5_dap *dap) { return do_sync ? swd_run_inner(dap) : ERROR_OK; @@ -166,22 +217,26 @@ static int swd_queue_ap_abort(struct adiv5_dap *dap, uint8_t *ack) } /** Select the DP register bank matching bits 7:4 of reg. */ -static void swd_queue_dp_bankselect(struct adiv5_dap *dap, unsigned reg) +static int swd_queue_dp_bankselect(struct adiv5_dap *dap, unsigned reg) { /* Only register address 4 is banked. */ if ((reg & 0xf) != 4) - return; + return ERROR_OK; uint32_t select_dp_bank = (reg & 0x000000F0) >> 4; uint32_t sel = select_dp_bank | (dap->select & (DP_SELECT_APSEL | DP_SELECT_APBANK)); if (sel == dap->select) - return; + return ERROR_OK; dap->select = sel; - swd_queue_dp_write(dap, DP_SELECT, sel); + int retval = swd_queue_dp_write(dap, DP_SELECT, sel); + if (retval != ERROR_OK) + dap->select = DP_SELECT_INVALID; + + return retval; } static int swd_queue_dp_read(struct adiv5_dap *dap, unsigned reg, @@ -194,7 +249,10 @@ static int swd_queue_dp_read(struct adiv5_dap *dap, unsigned reg, if (retval != ERROR_OK) return retval; - swd_queue_dp_bankselect(dap, reg); + retval = swd_queue_dp_bankselect(dap, reg); + if (retval != ERROR_OK) + return retval; + swd->read_reg(swd_cmd(true, false, reg), data, 0); return check_sync(dap); @@ -211,17 +269,29 @@ static int swd_queue_dp_write(struct adiv5_dap *dap, unsigned reg, return retval; swd_finish_read(dap); - if (reg == DP_SELECT) + if (reg == DP_SELECT) { dap->select = data & (DP_SELECT_APSEL | DP_SELECT_APBANK | DP_SELECT_DPBANK); - else - swd_queue_dp_bankselect(dap, reg); + + swd->write_reg(swd_cmd(false, false, reg), data, 0); + + retval = check_sync(dap); + if (retval != ERROR_OK) + dap->select = DP_SELECT_INVALID; + + return retval; + } + + retval = swd_queue_dp_bankselect(dap, reg); + if (retval != ERROR_OK) + return retval; + swd->write_reg(swd_cmd(false, false, reg), data, 0); return check_sync(dap); } /** Select the AP register bank matching bits 7:4 of reg. */ -static void swd_queue_ap_bankselect(struct adiv5_ap *ap, unsigned reg) +static int swd_queue_ap_bankselect(struct adiv5_ap *ap, unsigned reg) { struct adiv5_dap *dap = ap->dap; uint32_t sel = ((uint32_t)ap->ap_num << 24) @@ -229,11 +299,15 @@ static void swd_queue_ap_bankselect(struct adiv5_ap *ap, unsigned reg) | (dap->select & DP_SELECT_DPBANK); if (sel == dap->select) - return; + return ERROR_OK; dap->select = sel; - swd_queue_dp_write(dap, DP_SELECT, sel); + int retval = swd_queue_dp_write(dap, DP_SELECT, sel); + if (retval != ERROR_OK) + dap->select = DP_SELECT_INVALID; + + return retval; } static int swd_queue_ap_read(struct adiv5_ap *ap, unsigned reg, @@ -247,8 +321,11 @@ static int swd_queue_ap_read(struct adiv5_ap *ap, unsigned reg, if (retval != ERROR_OK) return retval; - swd_queue_ap_bankselect(ap, reg); - swd->read_reg(swd_cmd(true, true, reg), dap->last_read, ap->memaccess_tck); + retval = swd_queue_ap_bankselect(ap, reg); + if (retval != ERROR_OK) + return retval; + + swd->read_reg(swd_cmd(true, true, reg), dap->last_read, ap->memaccess_tck); dap->last_read = data; return check_sync(dap); @@ -266,8 +343,11 @@ static int swd_queue_ap_write(struct adiv5_ap *ap, unsigned reg, return retval; swd_finish_read(dap); - swd_queue_ap_bankselect(ap, reg); - swd->write_reg(swd_cmd(false, true, reg), data, ap->memaccess_tck); + retval = swd_queue_ap_bankselect(ap, reg); + if (retval != ERROR_OK) + return retval; + + swd->write_reg(swd_cmd(false, true, reg), data, ap->memaccess_tck); return check_sync(dap); } @@ -291,6 +371,7 @@ static void swd_quit(struct adiv5_dap *dap) const struct dap_ops swd_dap_ops = { .connect = swd_connect, + .send_sequence = swd_send_sequence, .queue_dp_read = swd_queue_dp_read, .queue_dp_write = swd_queue_dp_write, .queue_ap_read = swd_queue_ap_read, @@ -323,15 +404,16 @@ static const struct command_registration swd_handlers[] = { .mode = COMMAND_ANY, .help = "SWD command group", .chain = swd_commands, + .usage = "", }, COMMAND_REGISTRATION_DONE }; static int swd_select(struct command_context *ctx) { - /* FIXME: only place where global 'jtag_interface' is still needed */ - extern struct jtag_interface *jtag_interface; - const struct swd_driver *swd = jtag_interface->swd; + /* FIXME: only place where global 'adapter_driver' is still needed */ + extern struct adapter_driver *adapter_driver; + const struct swd_driver *swd = adapter_driver->swd_ops; int retval; retval = register_commands(ctx, NULL, swd_handlers);