From b2b514be5bcca8b84736ed2abb8687477ade583c Mon Sep 17 00:00:00 2001 From: Ian Thompson Date: Thu, 25 Aug 2022 08:45:14 -0700 Subject: [PATCH] target/xtensa: enable DAP/SWD for generic xtensa - Enable ADIv5 DAP systems via JTAG or SWD transport - Select correct PWRCTL/PWRSTAT bits for XDM/APB Signed-off-by: Ian Thompson Change-Id: I5894210c804f85075da868d0cfc6fb20b589d99f Reviewed-on: https://review.openocd.org/c/openocd/+/7144 Tested-by: jenkins Reviewed-by: Antonio Borneo --- src/target/xtensa/xtensa.c | 31 ++++---- src/target/xtensa/xtensa_chip.c | 36 ++++++++-- src/target/xtensa/xtensa_debug_module.c | 96 +++++++++++++++++++++++++ src/target/xtensa/xtensa_debug_module.h | 65 ++++++++++++----- 4 files changed, 191 insertions(+), 37 deletions(-) diff --git a/src/target/xtensa/xtensa.c b/src/target/xtensa/xtensa.c index a90cd14684..0c975a4ea6 100644 --- a/src/target/xtensa/xtensa.c +++ b/src/target/xtensa/xtensa.c @@ -776,7 +776,7 @@ static inline bool xtensa_is_stopped(struct target *target) int xtensa_examine(struct target *target) { struct xtensa *xtensa = target_to_xtensa(target); - unsigned int cmd = PWRCTL_DEBUGWAKEUP | PWRCTL_MEMWAKEUP | PWRCTL_COREWAKEUP; + unsigned int cmd = PWRCTL_DEBUGWAKEUP(xtensa) | PWRCTL_MEMWAKEUP(xtensa) | PWRCTL_COREWAKEUP(xtensa); LOG_DEBUG("coreid = %d", target->coreid); @@ -786,7 +786,7 @@ int xtensa_examine(struct target *target) } xtensa_queue_pwr_reg_write(xtensa, XDMREG_PWRCTL, cmd); - xtensa_queue_pwr_reg_write(xtensa, XDMREG_PWRCTL, cmd | PWRCTL_JTAGDEBUGUSE); + xtensa_queue_pwr_reg_write(xtensa, XDMREG_PWRCTL, cmd | PWRCTL_JTAGDEBUGUSE(xtensa)); xtensa_dm_queue_enable(&xtensa->dbg_mod); xtensa_dm_queue_tdi_idle(&xtensa->dbg_mod); int res = xtensa_dm_queue_execute(&xtensa->dbg_mod); @@ -806,13 +806,13 @@ int xtensa_examine(struct target *target) int xtensa_wakeup(struct target *target) { struct xtensa *xtensa = target_to_xtensa(target); - unsigned int cmd = PWRCTL_DEBUGWAKEUP | PWRCTL_MEMWAKEUP | PWRCTL_COREWAKEUP; + unsigned int cmd = PWRCTL_DEBUGWAKEUP(xtensa) | PWRCTL_MEMWAKEUP(xtensa) | PWRCTL_COREWAKEUP(xtensa); if (xtensa->reset_asserted) - cmd |= PWRCTL_CORERESET; + cmd |= PWRCTL_CORERESET(xtensa); xtensa_queue_pwr_reg_write(xtensa, XDMREG_PWRCTL, cmd); /* TODO: can we join this with the write above? */ - xtensa_queue_pwr_reg_write(xtensa, XDMREG_PWRCTL, cmd | PWRCTL_JTAGDEBUGUSE); + xtensa_queue_pwr_reg_write(xtensa, XDMREG_PWRCTL, cmd | PWRCTL_JTAGDEBUGUSE(xtensa)); xtensa_dm_queue_tdi_idle(&xtensa->dbg_mod); return xtensa_dm_queue_execute(&xtensa->dbg_mod); } @@ -959,8 +959,8 @@ int xtensa_assert_reset(struct target *target) target->state = TARGET_RESET; xtensa_queue_pwr_reg_write(xtensa, XDMREG_PWRCTL, - PWRCTL_JTAGDEBUGUSE | PWRCTL_DEBUGWAKEUP | PWRCTL_MEMWAKEUP | - PWRCTL_COREWAKEUP | PWRCTL_CORERESET); + PWRCTL_JTAGDEBUGUSE(xtensa) | PWRCTL_DEBUGWAKEUP(xtensa) | PWRCTL_MEMWAKEUP(xtensa) | + PWRCTL_COREWAKEUP(xtensa) | PWRCTL_CORERESET(xtensa)); xtensa_dm_queue_tdi_idle(&xtensa->dbg_mod); int res = xtensa_dm_queue_execute(&xtensa->dbg_mod); if (res != ERROR_OK) @@ -980,8 +980,8 @@ int xtensa_deassert_reset(struct target *target) OCDDCR_ENABLEOCD | OCDDCR_DEBUGINTERRUPT); xtensa_queue_pwr_reg_write(xtensa, XDMREG_PWRCTL, - PWRCTL_JTAGDEBUGUSE | PWRCTL_DEBUGWAKEUP | PWRCTL_MEMWAKEUP | - PWRCTL_COREWAKEUP); + PWRCTL_JTAGDEBUGUSE(xtensa) | PWRCTL_DEBUGWAKEUP(xtensa) | PWRCTL_MEMWAKEUP(xtensa) | + PWRCTL_COREWAKEUP(xtensa)); xtensa_dm_queue_tdi_idle(&xtensa->dbg_mod); int res = xtensa_dm_queue_execute(&xtensa->dbg_mod); if (res != ERROR_OK) @@ -2013,13 +2013,17 @@ int xtensa_checksum_memory(struct target *target, target_addr_t address, uint32_ int xtensa_poll(struct target *target) { struct xtensa *xtensa = target_to_xtensa(target); + if (xtensa_dm_poll(&xtensa->dbg_mod) != ERROR_OK) { + target->state = TARGET_UNKNOWN; + return ERROR_TARGET_NOT_EXAMINED; + } - int res = xtensa_dm_power_status_read(&xtensa->dbg_mod, PWRSTAT_DEBUGWASRESET | - PWRSTAT_COREWASRESET); + int res = xtensa_dm_power_status_read(&xtensa->dbg_mod, PWRSTAT_DEBUGWASRESET(xtensa) | + PWRSTAT_COREWASRESET(xtensa)); if (xtensa->dbg_mod.power_status.stat != xtensa->dbg_mod.power_status.stath) LOG_TARGET_DEBUG(target, "PWRSTAT: read 0x%08" PRIx32 ", clear 0x%08lx, reread 0x%08" PRIx32, xtensa->dbg_mod.power_status.stat, - PWRSTAT_DEBUGWASRESET | PWRSTAT_COREWASRESET, + PWRSTAT_DEBUGWASRESET(xtensa) | PWRSTAT_COREWASRESET(xtensa), xtensa->dbg_mod.power_status.stath); if (res != ERROR_OK) return res; @@ -2047,7 +2051,7 @@ int xtensa_poll(struct target *target) "DSR has changed: was 0x%08" PRIx32 " now 0x%08" PRIx32, prev_dsr, xtensa->dbg_mod.core_status.dsr); - if (xtensa->dbg_mod.power_status.stath & PWRSTAT_COREWASRESET) { + if (xtensa->dbg_mod.power_status.stath & PWRSTAT_COREWASRESET(xtensa)) { /* if RESET state is persitent */ target->state = TARGET_RESET; } else if (!xtensa_dm_is_powered(&xtensa->dbg_mod)) { @@ -2957,6 +2961,7 @@ void xtensa_target_deinit(struct target *target) LOG_ERROR("Failed to clear OCDDCR_ENABLEOCD!"); return; } + xtensa_dm_deinit(&xtensa->dbg_mod); } xtensa_free_reg_cache(target); free(xtensa->hw_brps); diff --git a/src/target/xtensa/xtensa_chip.c b/src/target/xtensa/xtensa_chip.c index 79aa3e48e8..c609245a2d 100644 --- a/src/target/xtensa/xtensa_chip.c +++ b/src/target/xtensa/xtensa_chip.c @@ -83,10 +83,23 @@ static int xtensa_chip_target_create(struct target *target, Jim_Interp *interp) .tap = NULL, .queue_tdi_idle = NULL, .queue_tdi_idle_arg = NULL, + .dap = NULL, + .debug_ap = NULL, + .debug_apsel = DP_APSEL_INVALID, + .ap_offset = 0, }; - xtensa_chip_dm_cfg.tap = target->tap; - LOG_DEBUG("JTAG: %s:%s pos %d", target->tap->chip, target->tap->tapname, target->tap->abs_chain_position); + struct adiv5_private_config *pc = target->private_config; + if (adiv5_verify_config(pc) == ERROR_OK) { + xtensa_chip_dm_cfg.dap = pc->dap; + xtensa_chip_dm_cfg.debug_apsel = pc->ap_num; + xtensa_chip_dm_cfg.ap_offset = target->dbgbase; + LOG_DEBUG("DAP: ap_num %" PRId64 " DAP %p\n", pc->ap_num, pc->dap); + } else { + xtensa_chip_dm_cfg.tap = target->tap; + LOG_DEBUG("JTAG: %s:%s pos %d", target->tap->chip, target->tap->tapname, + target->tap->abs_chain_position); + } struct xtensa_chip_common *xtensa_chip = calloc(1, sizeof(struct xtensa_chip_common)); if (!xtensa_chip) { @@ -116,13 +129,26 @@ void xtensa_chip_target_deinit(struct target *target) static int xtensa_chip_examine(struct target *target) { - return xtensa_examine(target); + struct xtensa *xtensa = target_to_xtensa(target); + int retval = xtensa_dm_examine(&xtensa->dbg_mod); + if (retval == ERROR_OK) + retval = xtensa_examine(target); + return retval; } int xtensa_chip_jim_configure(struct target *target, struct jim_getopt_info *goi) { - target->has_dap = false; - return JIM_CONTINUE; + static bool dap_configured; + int ret = adiv5_jim_configure(target, goi); + if (ret == JIM_OK) { + LOG_DEBUG("xtensa '-dap' target option found"); + dap_configured = true; + } + if (!dap_configured) { + LOG_DEBUG("xtensa '-dap' target option not yet found, assuming JTAG..."); + target->has_dap = false; + } + return ret; } /** Methods for generic example of Xtensa-based chip-level targets. */ diff --git a/src/target/xtensa/xtensa_debug_module.c b/src/target/xtensa/xtensa_debug_module.c index 7f8a75bc66..8753b863cd 100644 --- a/src/target/xtensa/xtensa_debug_module.c +++ b/src/target/xtensa/xtensa_debug_module.c @@ -10,6 +10,7 @@ #include #endif +#include #include "xtensa_debug_module.h" #define TAPINS_PWRCTL 0x08 @@ -25,6 +26,10 @@ #define TAPINS_IDCODE_LEN 32 #define TAPINS_BYPASS_LEN 1 +/* Table of power register offsets for APB space */ +static const struct xtensa_dm_pwr_reg_offsets xdm_pwr_regs[XDMREG_PWRNUM] = + XTENSA_DM_PWR_REG_OFFSETS; + /* Table of debug register offsets for Nexus and APB space */ static const struct xtensa_dm_reg_offsets xdm_regs[XDMREG_NUM] = XTENSA_DM_REG_OFFSETS; @@ -60,15 +65,85 @@ int xtensa_dm_init(struct xtensa_debug_module *dm, const struct xtensa_debug_mod { if (!dm || !cfg) return ERROR_FAIL; + if (!IS_ALIGNED(cfg->ap_offset, XTENSA_DM_APB_ALIGN)) { + LOG_ERROR("Xtensa DM APB offset must be aligned to a %dKB multiple", + XTENSA_DM_APB_ALIGN / 1024); + return ERROR_FAIL; + } dm->pwr_ops = cfg->pwr_ops; dm->dbg_ops = cfg->dbg_ops; dm->tap = cfg->tap; dm->queue_tdi_idle = cfg->queue_tdi_idle; dm->queue_tdi_idle_arg = cfg->queue_tdi_idle_arg; + dm->dap = cfg->dap; + dm->debug_ap = cfg->debug_ap; + dm->debug_apsel = cfg->debug_apsel; + dm->ap_offset = cfg->ap_offset; return ERROR_OK; } +void xtensa_dm_deinit(struct xtensa_debug_module *dm) +{ + if (dm->debug_ap) { + dap_put_ap(dm->debug_ap); + dm->debug_ap = NULL; + } +} + +int xtensa_dm_poll(struct xtensa_debug_module *dm) +{ + /* Check if debug_ap is available to prevent segmentation fault. + * If the re-examination after an error does not find a MEM-AP + * (e.g. the target stopped communicating), debug_ap pointer + * can suddenly become NULL. + */ + return (!dm || (dm->dap && !dm->debug_ap)) ? ERROR_FAIL : ERROR_OK; +} + +int xtensa_dm_examine(struct xtensa_debug_module *dm) +{ + struct adiv5_dap *swjdp = dm->dap; + int retval = ERROR_OK; + + if (swjdp) { + LOG_DEBUG("DM examine: DAP AP select %d", dm->debug_apsel); + if (dm->debug_ap) { + dap_put_ap(dm->debug_ap); + dm->debug_ap = NULL; + } + if (dm->debug_apsel == DP_APSEL_INVALID) { + LOG_DEBUG("DM examine: search for APB-type MEM-AP..."); + /* TODO: Determine whether AP_TYPE_AXI_AP APs can be supported... */ + retval = dap_find_get_ap(swjdp, AP_TYPE_APB_AP, &dm->debug_ap); + if (retval != ERROR_OK) { + LOG_ERROR("Could not find MEM-AP to control the core"); + return retval; + } + } else { + dm->debug_ap = dap_get_ap(swjdp, dm->debug_apsel); + } + + /* TODO: Allow a user-specified AP instead of relying on AP_TYPE_APB_AP */ + dm->debug_apsel = dm->debug_ap->ap_num; + LOG_DEBUG("DM examine: Setting apsel to %d", dm->debug_apsel); + + /* Leave (only) generic DAP stuff for debugport_init(); */ + dm->debug_ap->memaccess_tck = 8; + + retval = mem_ap_init(dm->debug_ap); + if (retval != ERROR_OK) { + LOG_ERROR("MEM-AP init failed: %d", retval); + return retval; + } + + /* TODO: how to set autoincrement range? Hard-code it to 1024 bytes for now */ + dm->debug_ap->tar_autoincr_block = (1 << 10); + } + + return retval; +} + int xtensa_dm_queue_enable(struct xtensa_debug_module *dm) { return dm->dbg_ops->queue_reg_write(dm, XDMREG_DCRSET, OCDDCR_ENABLEOCD); @@ -80,6 +155,11 @@ int xtensa_dm_queue_reg_read(struct xtensa_debug_module *dm, enum xtensa_dm_reg LOG_ERROR("Invalid DBG reg ID %d!", reg); return ERROR_FAIL; } + if (dm->dap) + /* NOTE: Future optimization: mem_ap_read_u32() offers higher performance with + * queued reads, but requires an API change to pass value as a 32-bit pointer. + */ + return mem_ap_read_buf(dm->debug_ap, value, 4, 1, xdm_regs[reg].apb + dm->ap_offset); uint8_t regdata = (xdm_regs[reg].nar << 1) | 0; uint8_t dummy[4] = { 0, 0, 0, 0 }; xtensa_dm_add_set_ir(dm, TAPINS_NARSEL); @@ -94,6 +174,8 @@ int xtensa_dm_queue_reg_write(struct xtensa_debug_module *dm, enum xtensa_dm_reg LOG_ERROR("Invalid DBG reg ID %d!", reg); return ERROR_FAIL; } + if (dm->dap) + return mem_ap_write_u32(dm->debug_ap, xdm_regs[reg].apb + dm->ap_offset, value); uint8_t regdata = (xdm_regs[reg].nar << 1) | 1; uint8_t valdata[] = { value, value >> 8, value >> 16, value >> 24 }; xtensa_dm_add_set_ir(dm, TAPINS_NARSEL); @@ -111,6 +193,16 @@ int xtensa_dm_queue_pwr_reg_read(struct xtensa_debug_module *dm, LOG_ERROR("Invalid PWR reg ID %d!", reg); return ERROR_FAIL; } + if (dm->dap) { + /* NOTE: Future optimization: mem_ap_read_u32() offers higher performance with + * queued reads, but requires an API change to pass value as a 32-bit pointer. + */ + uint32_t apbreg = xdm_pwr_regs[reg].apb + dm->ap_offset; + int retval = mem_ap_read_buf(dm->debug_ap, data, 4, 1, apbreg); + if (retval == ERROR_OK) + retval = mem_ap_write_u32(dm->debug_ap, apbreg, clear); + return retval; + } uint8_t value_clr = (uint8_t)clear; uint8_t tap_insn = (reg == XDMREG_PWRCTL) ? TAPINS_PWRCTL : TAPINS_PWRSTAT; int tap_insn_sz = (reg == XDMREG_PWRCTL) ? TAPINS_PWRCTL_LEN : TAPINS_PWRSTAT_LEN; @@ -127,6 +219,10 @@ int xtensa_dm_queue_pwr_reg_write(struct xtensa_debug_module *dm, LOG_ERROR("Invalid PWR reg ID %d!", reg); return ERROR_FAIL; } + if (dm->dap) { + uint32_t apbreg = xdm_pwr_regs[reg].apb + dm->ap_offset; + return mem_ap_write_u32(dm->debug_ap, apbreg, data); + } uint8_t tap_insn = (reg == XDMREG_PWRCTL) ? TAPINS_PWRCTL : TAPINS_PWRSTAT; int tap_insn_sz = (reg == XDMREG_PWRCTL) ? TAPINS_PWRCTL_LEN : TAPINS_PWRSTAT_LEN; uint8_t value = (uint8_t)data; diff --git a/src/target/xtensa/xtensa_debug_module.h b/src/target/xtensa/xtensa_debug_module.h index b4bb886823..b382e03db6 100644 --- a/src/target/xtensa/xtensa_debug_module.h +++ b/src/target/xtensa/xtensa_debug_module.h @@ -12,6 +12,7 @@ #define OPENOCD_TARGET_XTENSA_DEBUG_MODULE_H #include +#include #include #include @@ -45,19 +46,22 @@ struct xtensa_dm_pwr_reg_offsets { Module to happen correctly. When it is set, any write to this bit clears it. Either don't access it, or re-write it to 1 so JTAG accesses continue. */ -#define PWRCTL_JTAGDEBUGUSE BIT(7) -#define PWRCTL_DEBUGRESET BIT(6) -#define PWRCTL_CORERESET BIT(4) -#define PWRCTL_DEBUGWAKEUP BIT(2) -#define PWRCTL_MEMWAKEUP BIT(1) -#define PWRCTL_COREWAKEUP BIT(0) - -#define PWRSTAT_DEBUGWASRESET BIT(6) -#define PWRSTAT_COREWASRESET BIT(4) -#define PWRSTAT_CORESTILLNEEDED BIT(3) -#define PWRSTAT_DEBUGDOMAINON BIT(2) -#define PWRSTAT_MEMDOMAINON BIT(1) -#define PWRSTAT_COREDOMAINON BIT(0) +#define PWRCTL_JTAGDEBUGUSE(x) (((x)->dbg_mod.dap) ? (0) : BIT(7)) +#define PWRCTL_DEBUGRESET(x) (((x)->dbg_mod.dap) ? BIT(28) : BIT(6)) +#define PWRCTL_CORERESET(x) (((x)->dbg_mod.dap) ? BIT(16) : BIT(4)) +#define PWRCTL_DEBUGWAKEUP(x) (((x)->dbg_mod.dap) ? BIT(12) : BIT(2)) +#define PWRCTL_MEMWAKEUP(x) (((x)->dbg_mod.dap) ? BIT(8) : BIT(1)) +#define PWRCTL_COREWAKEUP(x) (((x)->dbg_mod.dap) ? BIT(0) : BIT(0)) + +#define PWRSTAT_DEBUGWASRESET_DM(d) (((d)->dap) ? BIT(28) : BIT(6)) +#define PWRSTAT_COREWASRESET_DM(d) (((d)->dap) ? BIT(16) : BIT(4)) +#define PWRSTAT_DEBUGWASRESET(x) (PWRSTAT_DEBUGWASRESET_DM(&((x)->dbg_mod))) +#define PWRSTAT_COREWASRESET(x) (PWRSTAT_COREWASRESET_DM(&((x)->dbg_mod))) +#define PWRSTAT_CORESTILLNEEDED(x) (((x)->dbg_mod.dap) ? BIT(4) : BIT(3)) +#define PWRSTAT_DEBUGDOMAINON(x) (((x)->dbg_mod.dap) ? BIT(12) : BIT(2)) +#define PWRSTAT_MEMDOMAINON(x) (((x)->dbg_mod.dap) ? BIT(8) : BIT(1)) +#define PWRSTAT_COREDOMAINON(x) (((x)->dbg_mod.dap) ? BIT(0) : BIT(0)) + /* Virtual IDs for using with xtensa_debug_ops API */ enum xtensa_dm_reg { /* TRAX Registers */ @@ -236,7 +240,7 @@ struct xtensa_dm_reg_offsets { { .nar = 0x7f, .apb = 0x3ffc }, /* XDMREG_COMPID3 */ \ } -#define XTENSA_DM_APB_MASK (0x3fff) +#define XTENSA_DM_APB_ALIGN 0x4000 /* OCD registers, bit definitions */ #define OCDDCR_ENABLEOCD BIT(0) @@ -408,24 +412,47 @@ struct xtensa_perfmon_result { struct xtensa_debug_module_config { const struct xtensa_power_ops *pwr_ops; const struct xtensa_debug_ops *dbg_ops; + + /* Either JTAG or DAP structures will be populated */ struct jtag_tap *tap; void (*queue_tdi_idle)(struct target *target); void *queue_tdi_idle_arg; + + /* For targets conforming to ARM Debug Interface v5, + * "dap" references the Debug Access Port (DAP) + * used to make requests to the target; + * "debug_ap" is AP instance connected to processor + */ + struct adiv5_dap *dap; + struct adiv5_ap *debug_ap; + int debug_apsel; + uint32_t ap_offset; }; struct xtensa_debug_module { const struct xtensa_power_ops *pwr_ops; const struct xtensa_debug_ops *dbg_ops; + + /* Either JTAG or DAP structures will be populated */ struct jtag_tap *tap; void (*queue_tdi_idle)(struct target *target); void *queue_tdi_idle_arg; + /* DAP struct; AP instance connected to processor */ + struct adiv5_dap *dap; + struct adiv5_ap *debug_ap; + int debug_apsel; + struct xtensa_power_status power_status; struct xtensa_core_status core_status; xtensa_ocdid_t device_id; + uint32_t ap_offset; }; int xtensa_dm_init(struct xtensa_debug_module *dm, const struct xtensa_debug_module_config *cfg); +void xtensa_dm_deinit(struct xtensa_debug_module *dm); +int xtensa_dm_poll(struct xtensa_debug_module *dm); +int xtensa_dm_examine(struct xtensa_debug_module *dm); int xtensa_dm_queue_enable(struct xtensa_debug_module *dm); int xtensa_dm_queue_reg_read(struct xtensa_debug_module *dm, enum xtensa_dm_reg reg, uint8_t *value); int xtensa_dm_queue_reg_write(struct xtensa_debug_module *dm, enum xtensa_dm_reg reg, uint32_t value); @@ -439,7 +466,7 @@ int xtensa_dm_queue_pwr_reg_write(struct xtensa_debug_module *dm, static inline int xtensa_dm_queue_execute(struct xtensa_debug_module *dm) { - return jtag_execute_queue(); + return dm->dap ? dap_run(dm->dap) : jtag_execute_queue(); } static inline void xtensa_dm_queue_tdi_idle(struct xtensa_debug_module *dm) @@ -492,14 +519,14 @@ static inline bool xtensa_dm_is_online(struct xtensa_debug_module *dm) static inline bool xtensa_dm_tap_was_reset(struct xtensa_debug_module *dm) { - return !(dm->power_status.prev_stat & PWRSTAT_DEBUGWASRESET) && - dm->power_status.stat & PWRSTAT_DEBUGWASRESET; + return !(dm->power_status.prev_stat & PWRSTAT_DEBUGWASRESET_DM(dm)) && + dm->power_status.stat & PWRSTAT_DEBUGWASRESET_DM(dm); } static inline bool xtensa_dm_core_was_reset(struct xtensa_debug_module *dm) { - return !(dm->power_status.prev_stat & PWRSTAT_COREWASRESET) && - dm->power_status.stat & PWRSTAT_COREWASRESET; + return !(dm->power_status.prev_stat & PWRSTAT_COREWASRESET_DM(dm)) && + dm->power_status.stat & PWRSTAT_COREWASRESET_DM(dm); } static inline bool xtensa_dm_core_is_stalled(struct xtensa_debug_module *dm) -- 2.30.2