X-Git-Url: https://review.openocd.org/gitweb?p=openocd.git;a=blobdiff_plain;f=src%2Ftarget%2Fadi_v5_swd.c;h=d099f4e710021153b179c8bc4dcb0bb9d510010f;hp=f103e4b089ea17077830a36bbc81fc388c556709;hb=420a692e0fae34a59eb3c33932362812e33f5583;hpb=6f8b8593d63bc9781435270a54b6f7d245eecd8e diff --git a/src/target/adi_v5_swd.c b/src/target/adi_v5_swd.c index f103e4b089..d099f4e710 100644 --- a/src/target/adi_v5_swd.c +++ b/src/target/adi_v5_swd.c @@ -13,15 +13,31 @@ * 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, write to the - * Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * along with this program. If not, see . ***************************************************************************/ /** * @file - * This file implements SWD transport support for cores implementing - the ARM Debug Interface version 5 (ADIv5). + * Utilities to support ARM "Serial Wire Debug" (SWD), a low pin-count debug + * link protocol used in cases where JTAG is not wanted. This is coupled to + * recent versions of ARM's "CoreSight" debug framework. This specific code + * is a transport level interface, with "target/arm_adi_v5.[hc]" code + * understanding operation semantics, shared with the JTAG transport. + * + * Single-DAP support only. + * + * for details, see "ARM IHI 0031A" + * ARM Debug Interface v5 Architecture Specification + * especially section 5.3 for SWD protocol + * + * On many chips (most current Cortex-M3 parts) SWD is a run-time alternative + * to JTAG. Boards may support one or both. There are also SWD-only chips, + * (using SW-DP not SWJ-DP). + * + * Even boards that also support JTAG can benefit from SWD support, because + * usually there's no way to access the SWO trace view mechanism in JTAG mode. + * That is, trace access may require SWD support. + * */ #ifdef HAVE_CONFIG_H @@ -32,61 +48,336 @@ #include "arm_adi_v5.h" #include -/* - * This represents the bits which must be sent out on TMS/SWDIO to - * switch a DAP implemented using an SWJ-DP module into SWD mode. - * These bits are stored (and transmitted) LSB-first. - * - * See the DAP-Lite specification, section 2.2.5 for information - * about making the debug link select SWD or JTAG. (Similar info - * is in a few other ARM documents.) - */ -static const uint8_t jtag2swd_bitseq[] = { - /* More than 50 TCK/SWCLK cycles with TMS/SWDIO high, - * putting both JTAG and SWD logic into reset state. - */ - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /* Switching sequence enables SWD and disables JTAG - * NOTE: bits in the DP's IDCODE may expose the need for - * an old/deprecated sequence (0xb6 0xed). - */ - 0x9e, 0xe7, - /* More than 50 TCK/SWCLK cycles with TMS/SWDIO high, - * putting both JTAG and SWD logic into reset state. - */ - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, -}; +#include +#include -/** - * Put the debug link into SWD mode, if the target supports it. - * The link's initial mode may be either JTAG (for example, - * with SWJ-DP after reset) or SWD. - * - * @param target Enters SWD mode (if possible). - * - * Note that targets using the JTAG-DP do not support SWD, and that - * some targets which could otherwise support it may have have been - * configured to disable SWD signaling - * - * @return ERROR_OK or else a fault code. - */ -int dap_to_swd(struct target *target) +#include + +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) { + swd->read_reg(swd_cmd(true, false, DP_RDBUFF), dap->last_read, 0); + dap->last_read = NULL; + } +} + +static int swd_queue_dp_write(struct adiv5_dap *dap, unsigned reg, + uint32_t data); +static int swd_queue_dp_read(struct adiv5_dap *dap, unsigned reg, + uint32_t *data); + +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), + STKCMPCLR | STKERRCLR | WDERRCLR | ORUNERRCLR, 0); +} + +static int swd_run_inner(struct adiv5_dap *dap) +{ + const struct swd_driver *swd = adiv5_dap_swd_driver(dap); int retval; - LOG_DEBUG("Enter SWD mode"); + retval = swd->run(); + + if (retval != ERROR_OK) { + /* fault response */ + dap->do_reconnect = true; + } + + return retval; +} - /* REVISIT it's nasty to need to make calls to a "jtag" - * subsystem if the link isn't in JTAG mode... +static int swd_connect(struct adiv5_dap *dap) +{ + const struct swd_driver *swd = adiv5_dap_swd_driver(dap); + uint32_t dpidr; + int status; + + /* FIXME validate transport config ... is the + * configured DAP present (check IDCODE)? + * Is *only* one DAP configured? + * + * MUST READ DPIDR */ - retval = jtag_add_tms_seq(8 * sizeof(jtag2swd_bitseq), - jtag2swd_bitseq, TAP_INVALID); - if (retval == ERROR_OK) - retval = jtag_execute_queue(); + /* Check if we should reset srst already when connecting, but not if reconnecting. */ + if (!dap->do_reconnect) { + enum reset_types jtag_reset_config = jtag_get_reset_config(); + + if (jtag_reset_config & RESET_CNCT_UNDER_SRST) { + if (jtag_reset_config & RESET_SRST_NO_GATING) + swd_add_reset(1); + 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); + + swd_queue_dp_read(dap, DP_DPIDR, &dpidr); + + /* force clear all sticky faults */ + swd_clear_sticky_errors(dap); + + status = swd_run_inner(dap); + + if (status == ERROR_OK) { + LOG_INFO("SWD DPIDR %#8.8" PRIx32, dpidr); + dap->do_reconnect = false; + status = dap_dp_init(dap); + } else + dap->do_reconnect = true; + + return status; +} + +static inline int check_sync(struct adiv5_dap *dap) +{ + return do_sync ? swd_run_inner(dap) : ERROR_OK; +} + +static int swd_check_reconnect(struct adiv5_dap *dap) +{ + if (dap->do_reconnect) + return swd_connect(dap); + + return ERROR_OK; +} + +static int swd_queue_ap_abort(struct adiv5_dap *dap, uint8_t *ack) +{ + const struct swd_driver *swd = adiv5_dap_swd_driver(dap); + assert(swd); + + swd->write_reg(swd_cmd(false, false, DP_ABORT), + DAPABORT | STKCMPCLR | STKERRCLR | WDERRCLR | ORUNERRCLR, 0); + return check_sync(dap); +} + +/** Select the DP register bank matching bits 7:4 of reg. */ +static void swd_queue_dp_bankselect(struct adiv5_dap *dap, unsigned reg) +{ + /* Only register address 4 is banked. */ + if ((reg & 0xf) != 4) + return; + + 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; + + dap->select = sel; + + swd_queue_dp_write(dap, DP_SELECT, sel); +} + +static int swd_queue_dp_read(struct adiv5_dap *dap, unsigned reg, + uint32_t *data) +{ + const struct swd_driver *swd = adiv5_dap_swd_driver(dap); + assert(swd); + + int retval = swd_check_reconnect(dap); + if (retval != ERROR_OK) + return retval; + + swd_queue_dp_bankselect(dap, reg); + swd->read_reg(swd_cmd(true, false, reg), data, 0); + + return check_sync(dap); +} + +static int swd_queue_dp_write(struct adiv5_dap *dap, unsigned reg, + uint32_t data) +{ + const struct swd_driver *swd = adiv5_dap_swd_driver(dap); + assert(swd); + + int retval = swd_check_reconnect(dap); + if (retval != ERROR_OK) + return retval; + + swd_finish_read(dap); + 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); + + 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) +{ + struct adiv5_dap *dap = ap->dap; + uint32_t sel = ((uint32_t)ap->ap_num << 24) + | (reg & 0x000000F0) + | (dap->select & DP_SELECT_DPBANK); + + if (sel == dap->select) + return; - /* REVISIT set up the DAP's ops vector for SWD mode. */ + dap->select = sel; + + swd_queue_dp_write(dap, DP_SELECT, sel); +} + +static int swd_queue_ap_read(struct adiv5_ap *ap, unsigned reg, + uint32_t *data) +{ + struct adiv5_dap *dap = ap->dap; + const struct swd_driver *swd = adiv5_dap_swd_driver(dap); + assert(swd); + + int retval = swd_check_reconnect(dap); + 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); + dap->last_read = data; + + return check_sync(dap); +} + +static int swd_queue_ap_write(struct adiv5_ap *ap, unsigned reg, + uint32_t data) +{ + struct adiv5_dap *dap = ap->dap; + const struct swd_driver *swd = adiv5_dap_swd_driver(dap); + assert(swd); + + int retval = swd_check_reconnect(dap); + if (retval != ERROR_OK) + return retval; + + swd_finish_read(dap); + swd_queue_ap_bankselect(ap, reg); + swd->write_reg(swd_cmd(false, true, reg), data, ap->memaccess_tck); + + return check_sync(dap); +} + +/** Executes all queued DAP operations. */ +static int swd_run(struct adiv5_dap *dap) +{ + swd_finish_read(dap); + return swd_run_inner(dap); +} + +/** Put the SWJ-DP back to JTAG mode */ +static void swd_quit(struct adiv5_dap *dap) +{ + const struct swd_driver *swd = adiv5_dap_swd_driver(dap); + + swd->switch_seq(SWD_TO_JTAG); + /* flush the queue before exit */ + swd->run(); +} + +const struct dap_ops swd_dap_ops = { + .connect = swd_connect, + .queue_dp_read = swd_queue_dp_read, + .queue_dp_write = swd_queue_dp_write, + .queue_ap_read = swd_queue_ap_read, + .queue_ap_write = swd_queue_ap_write, + .queue_ap_abort = swd_queue_ap_abort, + .run = swd_run, + .quit = swd_quit, +}; + +static const struct command_registration swd_commands[] = { + { + /* + * Set up SWD and JTAG targets identically, unless/until + * infrastructure improves ... meanwhile, ignore all + * JTAG-specific stuff like IR length for SWD. + * + * REVISIT can we verify "just one SWD DAP" here/early? + */ + .name = "newdap", + .jim_handler = jim_jtag_newtap, + .mode = COMMAND_CONFIG, + .help = "declare a new SWD DAP" + }, + COMMAND_REGISTRATION_DONE +}; + +static const struct command_registration swd_handlers[] = { + { + .name = "swd", + .mode = COMMAND_ANY, + .help = "SWD command group", + .chain = swd_commands, + }, + 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; + int retval; + + retval = register_commands(ctx, NULL, swd_handlers); + if (retval != ERROR_OK) + return retval; + + /* be sure driver is in SWD mode; start + * with hardware default TRN (1), it can be changed later + */ + if (!swd || !swd->read_reg || !swd->write_reg || !swd->init) { + LOG_DEBUG("no SWD driver?"); + return ERROR_FAIL; + } + + retval = swd->init(); + if (retval != ERROR_OK) { + LOG_DEBUG("can't init SWD driver"); + return retval; + } return retval; } +static int swd_init(struct command_context *ctx) +{ + /* nothing done here, SWD is initialized + * together with the DAP */ + return ERROR_OK; +} + +static struct transport swd_transport = { + .name = "swd", + .select = swd_select, + .init = swd_init, +}; + +static void swd_constructor(void) __attribute__((constructor)); +static void swd_constructor(void) +{ + transport_register(&swd_transport); +} + +/** Returns true if the current debug session + * is using SWD as its transport. + */ +bool transport_is_swd(void) +{ + return get_current_transport() == &swd_transport; +}