This is an automated email from Gerrit. Daniel Goehring ([email protected]) just uploaded a new patch set to Gerrit, which you can find at http://openocd.zylin.com/6077
-- gerrit commit b2e0bfd3a50a1e80364c46b111e3cd3dc1e7ba3f Author: Kevin Burke <[email protected]> Date: Tue Feb 9 17:32:51 2021 -0500 Target/ARM: Add ADIv6 Support Includes Class 9 ROM Table, additonal -adiv5 and -adiv6 configuration overrides in case default IDR reads fail to identify the correct ADI version, additonal -apbase address configuration setting for ADIv6 targets, and minimal code changes by making use of existing adiv5 functions where possible. Tested on Ampere emulation and Quicksilver silicon. Change-Id: Ie948a8ab4fcd647a174400cc5d9fcd58909ca2fd Signed-off-by: Kevin Burke <[email protected]> Signed-off-by: Daniel Goehring <[email protected]> diff --git a/src/target/adi_v5_jtag.c b/src/target/adi_v5_jtag.c index 6dede97..ac2f860 100644 --- a/src/target/adi_v5_jtag.c +++ b/src/target/adi_v5_jtag.c @@ -10,6 +10,8 @@ * * Copyright (C) 2009-2010 by David Brownell * + * Copyright (C) 2020-2021, Ampere Computing LLC * + * * 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 @@ -27,7 +29,7 @@ /** * @file * This file implements JTAG transport support for cores implementing - the ARM Debug Interface version 5 (ADIv5). + the ARM Debug Interface version 5 (ADIv5) and version 6 (ADIv6). */ #ifdef HAVE_CONFIG_H @@ -49,13 +51,15 @@ #define JTAG_DP_IDCODE 0xE /* three-bit ACK values for DPACC and APACC reads */ -#define JTAG_ACK_OK_FAULT 0x2 -#define JTAG_ACK_WAIT 0x1 +#define JTAG_ACK_OK_FAULT 0x2 /* ADIv5 */ +#define JTAG_ACK_WAIT 0x1 /* ADIv5 and ADIv6 */ +#define JTAG_ACK_FAULT 0x2 /* ADIv6 */ +#define JTAG_ACK_OK 0x4 /* ADIV6 */ static int jtag_ap_q_abort(struct adiv5_dap *dap, uint8_t *ack); #ifdef DEBUG_WAIT -static const char *dap_reg_name(int instr, int reg_addr) +static const char *dap_reg_name(uint32_t adi_ap_reg_offset, int instr, int reg_addr) { char *reg_name = "UNK"; @@ -83,6 +87,9 @@ static const char *dap_reg_name(int instr, int reg_addr) } if (instr == JTAG_DP_APACC) { + /* ADIv6 reg addresses below 0xE00 must be adjusted */ + if (reg_addr < 0xE00) + reg_addr = reg_addr - adi_ap_reg_offset; switch (reg_addr) { case MEM_AP_REG_CSW: reg_name = "CSW"; @@ -90,6 +97,9 @@ static const char *dap_reg_name(int instr, int reg_addr) case MEM_AP_REG_TAR: reg_name = "TAR"; break; + case MEM_AP_REG_TAR64: + reg_name = "TAR64"; + break; case MEM_AP_REG_DRW: reg_name = "DRW"; break; @@ -111,9 +121,30 @@ static const char *dap_reg_name(int instr, int reg_addr) case MEM_AP_REG_BASE: reg_name = "BASE"; break; + case MEM_AP_REG_BASE64: + reg_name = "BASE64"; + break; case AP_REG_IDR: reg_name = "IDR"; break; + case AP_REG_DEVARCH: + reg_name = "DEVARCH"; + break; + case AP_REG_DEVID: + reg_name = "DEVID"; + break; + case AP_REG_CIDR0: + reg_name = "CIDR0"; + break; + case AP_REG_CIDR1: + reg_name = "CIDR1"; + break; + case AP_REG_CIDR2: + reg_name = "CIDR2"; + break; + case AP_REG_CIDR3: + reg_name = "CIDR3"; + break; default: reg_name = "UNK"; break; @@ -127,12 +158,12 @@ static const char *dap_reg_name(int instr, int reg_addr) struct dap_cmd { struct list_head lh; uint8_t instr; - uint8_t reg_addr; + uint16_t reg_addr; uint8_t RnW; uint8_t *invalue; uint8_t ack; uint32_t memaccess_tck; - uint32_t dp_select; + uint64_t dp_select; struct scan_field fields[2]; uint8_t out_addr_buf; @@ -147,17 +178,30 @@ struct dap_cmd_pool { struct dap_cmd cmd; }; -static void log_dap_cmd(const char *header, struct dap_cmd *el) +static void log_dap_cmd(uint32_t adi_ap_reg_offset, const char *header, struct dap_cmd *el) { #ifdef DEBUG_WAIT - LOG_DEBUG("%s: %2s %6s %5s 0x%08x 0x%08x %2s", header, - el->instr == JTAG_DP_APACC ? "AP" : "DP", - dap_reg_name(el->instr, el->reg_addr), - el->RnW == DPAP_READ ? "READ" : "WRITE", - buf_get_u32(el->outvalue_buf, 0, 32), - buf_get_u32(el->invalue, 0, 32), - el->ack == JTAG_ACK_OK_FAULT ? "OK" : - (el->ack == JTAG_ACK_WAIT ? "WAIT" : "INVAL")); + if (adi_ap_reg_offset == ADIV6_REG_DELTA) { + /* We are connected to ADIv6 */ + LOG_DEBUG("%s: %2s %6s %5s 0x%08x 0x%08x %2s", header, + el->instr == JTAG_DP_APACC ? "AP" : "DP", + dap_reg_name(adi_ap_reg_offset, el->instr, el->reg_addr), + el->RnW == DPAP_READ ? "READ" : "WRITE", + buf_get_u32(el->outvalue_buf, 0, 32), + buf_get_u32(el->invalue, 0, 32), + el->ack == JTAG_ACK_OK ? "OK" : + (el->ack == JTAG_ACK_WAIT ? "WAIT" : el->ack == JTAG_ACK_FAULT ? "FAULT" : "INVAL")); + } else { + /* We are connected to ADIv5 */ + LOG_DEBUG("%s: %2s %6s %5s 0x%08x 0x%08x %2s", header, + el->instr == JTAG_DP_APACC ? "AP" : "DP", + dap_reg_name(adi_ap_reg_offset, el->instr, el->reg_addr), + el->RnW == DPAP_READ ? "READ" : "WRITE", + buf_get_u32(el->outvalue_buf, 0, 32), + buf_get_u32(el->invalue, 0, 32), + el->ack == JTAG_ACK_OK_FAULT ? "OK" : + (el->ack == JTAG_ACK_WAIT ? "WAIT" : "INVAL")); + } #endif } @@ -170,7 +214,7 @@ static int jtag_limit_queue_size(struct adiv5_dap *dap) } static struct dap_cmd *dap_cmd_new(struct adiv5_dap *dap, uint8_t instr, - uint8_t reg_addr, uint8_t RnW, + uint16_t reg_addr, uint8_t RnW, uint8_t *outvalue, uint8_t *invalue, uint32_t memaccess_tck) { @@ -271,12 +315,13 @@ static int adi_jtag_dp_scan_cmd(struct adiv5_dap *dap, struct dap_cmd *cmd, uint /* Add specified number of tck clocks after starting memory bus * access, giving the hardware time to complete the access. * They provide more time for the (MEM) AP to complete the read ... - * See "Minimum Response Time" for JTAG-DP, in the ADIv5 spec. + * See "Minimum Response Time" for JTAG-DP, in the ADIv5/ADIv6 spec. */ - if (cmd->instr == JTAG_DP_APACC) { - if (((cmd->reg_addr == MEM_AP_REG_DRW) - || ((cmd->reg_addr & 0xF0) == MEM_AP_REG_BD0)) - && (cmd->memaccess_tck != 0)) + if ((cmd->instr == JTAG_DP_APACC) && (cmd->memaccess_tck != 0)) { + if (dap->adi_version == 6) + jtag_add_runtest(cmd->memaccess_tck, TAP_IDLE); + else if ((cmd->reg_addr == MEM_AP_REG_DRW) + || ((cmd->reg_addr & 0xFFF0) == MEM_AP_REG_BD0)) jtag_add_runtest(cmd->memaccess_tck, TAP_IDLE); } @@ -296,7 +341,7 @@ static int adi_jtag_dp_scan_cmd_sync(struct adiv5_dap *dap, struct dap_cmd *cmd, /** * Scan DPACC or APACC using target ordered uint8_t buffers. No endianness - * conversions are performed. See section 4.4.3 of the ADIv5 spec, which + * conversions are performed. See section 4.4.3 of the ADIv5/ADIv6 spec, which * discusses operations which access these registers. * * Note that only one scan is performed. If RnW is set, a separate scan @@ -315,7 +360,7 @@ static int adi_jtag_dp_scan_cmd_sync(struct adiv5_dap *dap, struct dap_cmd *cmd, */ static int adi_jtag_dp_scan(struct adiv5_dap *dap, - uint8_t instr, uint8_t reg_addr, uint8_t RnW, + uint8_t instr, uint16_t reg_addr, uint8_t RnW, uint8_t *outvalue, uint8_t *invalue, uint32_t memaccess_tck, uint8_t *ack) { @@ -342,13 +387,28 @@ static int adi_jtag_dp_scan(struct adiv5_dap *dap, * must be different). */ static int adi_jtag_dp_scan_u32(struct adiv5_dap *dap, - uint8_t instr, uint8_t reg_addr, uint8_t RnW, + uint8_t instr, uint16_t reg_addr, uint8_t RnW, uint32_t outvalue, uint32_t *invalue, uint32_t memaccess_tck, uint8_t *ack) { uint8_t out_value_buf[4]; int retval; - + uint64_t sel = (reg_addr >> 4) & 0xf; + + /* No need to change SELECT or RDBUFF as they are not banked */ + /* Must target DP */ + if (instr == JTAG_DP_DPACC && reg_addr != DP_SELECT && reg_addr != DP_RDBUFF + && sel != (dap->select & 0xf)) { + if (dap->select != DP_SELECT_INVALID) + sel |= dap->select & ~0xfull; + dap->select = sel; + LOG_DEBUG("DP BANKSEL: %x", (uint32_t) sel); + buf_set_u32(out_value_buf, 0, 32, (uint32_t) sel); + retval = adi_jtag_dp_scan(dap, JTAG_DP_DPACC, + DP_SELECT, DPAP_WRITE, out_value_buf, NULL, 0, NULL); + if (retval != ERROR_OK) + return retval; + } buf_set_u32(out_value_buf, 0, 32, outvalue); retval = adi_jtag_dp_scan(dap, instr, reg_addr, RnW, @@ -377,7 +437,7 @@ static int adi_jtag_finish_read(struct adiv5_dap *dap) } static int adi_jtag_scan_inout_check_u32(struct adiv5_dap *dap, - uint8_t instr, uint8_t reg_addr, uint8_t RnW, + uint8_t instr, uint16_t reg_addr, uint8_t RnW, uint32_t outvalue, uint32_t *invalue, uint32_t memaccess_tck) { int retval; @@ -416,14 +476,19 @@ static int jtagdp_overrun_check(struct adiv5_dap *dap) /* skip all completed transactions up to the first WAIT */ list_for_each_entry(el, &dap->cmd_journal, lh) { - if (el->ack == JTAG_ACK_OK_FAULT) { - log_dap_cmd("LOG", el); + /**********************************************************************/ + /* JTAG_ACK_OK_FAULT (ADIv5) and JTAG_ACK_FAULT (ADIv6) are equal so */ + /* the following statement is cheching to see if an acknowledgement of*/ + /* OK or FAULT is generated for ADIv5 or ADIv6 */ + /**********************************************************************/ + if ((el->ack == JTAG_ACK_OK_FAULT) || ((dap->adi_version == 6) && (el->ack == JTAG_ACK_OK))) { + log_dap_cmd(dap->adi_ap_reg_offset, "LOG", el); } else if (el->ack == JTAG_ACK_WAIT) { found_wait = 1; break; } else { LOG_ERROR("Invalid ACK (%1x) in DAP response", el->ack); - log_dap_cmd("ERR", el); + log_dap_cmd(dap->adi_ap_reg_offset, "ERR", el); retval = ERROR_JTAG_DEVICE_ERROR; goto done; } @@ -436,14 +501,15 @@ static int jtagdp_overrun_check(struct adiv5_dap *dap) if (found_wait && el != list_first_entry(&dap->cmd_journal, struct dap_cmd, lh)) { prev = list_entry(el->lh.prev, struct dap_cmd, lh); if (prev->RnW == DPAP_READ) { - log_dap_cmd("PND", prev); + log_dap_cmd(dap->adi_ap_reg_offset, "PND", prev); /* search for the next OK transaction, it contains * the result of the previous READ */ tmp = el; list_for_each_entry_from(tmp, &dap->cmd_journal, lh) { - if (tmp->ack == JTAG_ACK_OK_FAULT) { + /* The following check covers OK and FAULT ACKs for both ADIv5 and ADIv6 */ + if ((tmp->ack == JTAG_ACK_OK_FAULT) || ((dap->adi_version == 6) && (tmp->ack == JTAG_ACK_OK))) { /* recover the read value */ - log_dap_cmd("FND", tmp); + log_dap_cmd(dap->adi_ap_reg_offset, "FND", tmp); if (el->invalue != el->invalue_buf) { uint32_t invalue = le_to_h_u32(tmp->invalue); memcpy(el->invalue, &invalue, sizeof(uint32_t)); @@ -454,7 +520,7 @@ static int jtagdp_overrun_check(struct adiv5_dap *dap) } if (prev != NULL) { - log_dap_cmd("LST", el); + log_dap_cmd(dap->adi_ap_reg_offset, "LST", el); /* * At this point we're sure that no previous @@ -476,8 +542,9 @@ static int jtagdp_overrun_check(struct adiv5_dap *dap) retval = adi_jtag_dp_scan_cmd_sync(dap, tmp, NULL); if (retval != ERROR_OK) break; - if (tmp->ack == JTAG_ACK_OK_FAULT) { - log_dap_cmd("FND", tmp); + /* The following check covers OK and FAULT ACKs for both ADIv5 and ADIv6 */ + if ((tmp->ack == JTAG_ACK_OK_FAULT) || ((dap->adi_version == 6) && (tmp->ack == JTAG_ACK_OK))) { + log_dap_cmd(dap->adi_ap_reg_offset, "FND", tmp); if (el->invalue != el->invalue_buf) { uint32_t invalue = le_to_h_u32(tmp->invalue); memcpy(el->invalue, &invalue, sizeof(uint32_t)); @@ -486,7 +553,7 @@ static int jtagdp_overrun_check(struct adiv5_dap *dap) } if (tmp->ack != JTAG_ACK_WAIT) { LOG_ERROR("Invalid ACK (%1x) in DAP response", tmp->ack); - log_dap_cmd("ERR", tmp); + log_dap_cmd(dap->adi_ap_reg_offset, "ERR", tmp); retval = ERROR_JTAG_DEVICE_ERROR; break; } @@ -495,7 +562,7 @@ static int jtagdp_overrun_check(struct adiv5_dap *dap) if (retval == ERROR_OK) { /* timeout happened */ - if (tmp->ack != JTAG_ACK_OK_FAULT) { + if (tmp->ack == JTAG_ACK_WAIT) { LOG_ERROR("Timeout during WAIT recovery"); dap->select = DP_SELECT_INVALID; jtag_ap_q_abort(dap, NULL); @@ -523,7 +590,7 @@ static int jtagdp_overrun_check(struct adiv5_dap *dap) /* move all remaining transactions over to the replay list */ list_for_each_entry_safe_from(el, tmp, &dap->cmd_journal, lh) { - log_dap_cmd("REP", el); + log_dap_cmd(dap->adi_ap_reg_offset, "REP", el); list_move_tail(&el->lh, &replay_list); } @@ -560,8 +627,8 @@ static int jtagdp_overrun_check(struct adiv5_dap *dap) retval = adi_jtag_dp_scan_cmd_sync(dap, el, NULL); if (retval != ERROR_OK) break; - log_dap_cmd("REC", el); - if (el->ack == JTAG_ACK_OK_FAULT) { + log_dap_cmd(dap->adi_ap_reg_offset, "REC", el); + if ((el->ack == JTAG_ACK_OK_FAULT) || ((dap->adi_version == 6) && (el->ack == JTAG_ACK_OK))) { if (el->invalue != el->invalue_buf) { uint32_t invalue = le_to_h_u32(el->invalue); memcpy(el->invalue, &invalue, sizeof(uint32_t)); @@ -570,14 +637,14 @@ static int jtagdp_overrun_check(struct adiv5_dap *dap) } if (el->ack != JTAG_ACK_WAIT) { LOG_ERROR("Invalid ACK (%1x) in DAP response", el->ack); - log_dap_cmd("ERR", el); + log_dap_cmd(dap->adi_ap_reg_offset, "ERR", el); retval = ERROR_JTAG_DEVICE_ERROR; break; } } while (timeval_ms() - time_now < 1000); if (retval == ERROR_OK) { - if (el->ack != JTAG_ACK_OK_FAULT) { + if (el->ack == JTAG_ACK_WAIT) { LOG_ERROR("Timeout during WAIT recovery"); dap->select = DP_SELECT_INVALID; jtag_ap_q_abort(dap, NULL); @@ -715,15 +782,37 @@ static int jtag_dp_q_write(struct adiv5_dap *dap, unsigned reg, /** Select the AP register bank matching bits 7:4 of reg. */ static int jtag_ap_q_bankselect(struct adiv5_ap *ap, unsigned reg) { + int retval; struct adiv5_dap *dap = ap->dap; - uint32_t sel = ((uint32_t)ap->ap_num << 24) | (reg & 0x000000F0); + uint64_t sel; - if (sel == dap->select) - return ERROR_OK; + if (dap->adi_version == 5) { + sel = (ap->ap_num << 24) | (reg & 0x000000F0); - dap->select = sel; + if (sel == dap->select) + return ERROR_OK; + + dap->select = sel; + + return jtag_dp_q_write(dap, DP_SELECT, (uint32_t) sel); + } else { + sel = ap->base_addr | (reg & 0x0000FFF0); - return jtag_dp_q_write(dap, DP_SELECT, sel); + if (sel == (dap->select & ~0xfull)) + return ERROR_OK; + + if (dap->select != DP_SELECT_INVALID) + sel |= dap->select & 0xf; + dap->select = sel; + LOG_DEBUG("AP BANKSEL: %x", (uint32_t) sel); + retval = jtag_dp_q_write(dap, DP_SELECT, (uint32_t) sel); + if (retval != ERROR_OK) + return retval; + if (dap->asize > 32) + retval = jtag_dp_q_write(dap, DP_SELECT1, (uint32_t) (sel>>32)); + + return retval; + } } static int jtag_ap_q_read(struct adiv5_ap *ap, unsigned reg, diff --git a/src/target/arm_adi_v5.c b/src/target/arm_adi_v5.c index 02b14da..74fcf85 100644 --- a/src/target/arm_adi_v5.c +++ b/src/target/arm_adi_v5.c @@ -82,6 +82,17 @@ #include <helper/list.h> #include <helper/jim-nvp.h> +static int dap_devtype_display(struct command_invocation *cmd, + struct adiv5_ap *ap, target_addr_t base_addr); +static int adiv6_dap_rom_display(struct command_invocation *cmd, + struct adiv5_ap *ap, target_addr_t dbgbase, int depth); +static int adiv6_dap_lookup_cs_component(struct adiv5_ap *ap, + target_addr_t dbgbase, uint8_t type, target_addr_t *addr, int32_t *idx); +static int adiv6_dap_lookup_cs_component_root(struct adiv5_ap *ap, + target_addr_t dbgbase, uint8_t type, target_addr_t *addr, int32_t *idx); +static int adiv6_dap_get_debugbase(struct adiv5_ap *ap, target_addr_t *dbgbase, uint32_t *apid); +static int adiv6_dap_info_command(struct command_invocation *cmd, struct adiv5_ap *ap); + /* ARM ADI Specification requires at least 10 bits used for TAR autoincrement */ /* @@ -105,7 +116,7 @@ static int mem_ap_setup_csw(struct adiv5_ap *ap, uint32_t csw) if (csw != ap->csw_value) { /* LOG_DEBUG("DAP: Set CSW %x",csw); */ - int retval = dap_queue_ap_write(ap, MEM_AP_REG_CSW, csw); + int retval = dap_queue_ap_write(ap, (MEM_AP_REG_CSW + ap->dap->adi_ap_reg_offset), csw); if (retval != ERROR_OK) { ap->csw_value = 0; return retval; @@ -119,11 +130,11 @@ static int mem_ap_setup_tar(struct adiv5_ap *ap, target_addr_t tar) { if (!ap->tar_valid || tar != ap->tar_value) { /* LOG_DEBUG("DAP: Set TAR %x",tar); */ - int retval = dap_queue_ap_write(ap, MEM_AP_REG_TAR, tar); + int retval = dap_queue_ap_write(ap, MEM_AP_REG_TAR + ap->dap->adi_ap_reg_offset, tar); if (retval == ERROR_OK && (ap->cfg_reg & 2)) { /* See if bits 63:32 of tar is different from last setting */ if ((ap->tar_value >> 32) != (tar >> 32)) - retval = dap_queue_ap_write(ap, MEM_AP_REG_TAR64, (uint32_t) (tar >> 32)); + retval = dap_queue_ap_write(ap, MEM_AP_REG_TAR64 + ap->dap->adi_ap_reg_offset, (uint32_t) (tar >> 32)); } if (retval != ERROR_OK) { ap->tar_valid = false; @@ -140,9 +151,9 @@ static int mem_ap_read_tar(struct adiv5_ap *ap, target_addr_t *tar) uint32_t lower; uint32_t upper = 0; - int retval = dap_queue_ap_read(ap, MEM_AP_REG_TAR, &lower); + int retval = dap_queue_ap_read(ap, MEM_AP_REG_TAR + ap->dap->adi_ap_reg_offset, &lower); if (retval == ERROR_OK && (ap->cfg_reg & 2)) /* See if large physical addressing is enabled */ - retval = dap_queue_ap_read(ap, MEM_AP_REG_TAR64, &upper); + retval = dap_queue_ap_read(ap, MEM_AP_REG_TAR64 + ap->dap->adi_ap_reg_offset, &upper); if (retval != ERROR_OK) { ap->tar_valid = false; @@ -250,7 +261,7 @@ int mem_ap_read_u32(struct adiv5_ap *ap, target_addr_t address, if (retval != ERROR_OK) return retval; - return dap_queue_ap_read(ap, MEM_AP_REG_BD0 | (address & 0xC), value); + return dap_queue_ap_read(ap, (MEM_AP_REG_BD0 | (address & 0xC)) + ap->dap->adi_ap_reg_offset, value); } /** @@ -302,7 +313,7 @@ int mem_ap_write_u32(struct adiv5_ap *ap, target_addr_t address, if (retval != ERROR_OK) return retval; - return dap_queue_ap_write(ap, MEM_AP_REG_BD0 | (address & 0xC), + return dap_queue_ap_write(ap, (MEM_AP_REG_BD0 | (address & 0xC)) + ap->dap->adi_ap_reg_offset, value); } @@ -434,7 +445,7 @@ static int mem_ap_write(struct adiv5_ap *ap, const uint8_t *buffer, uint32_t siz nbytes -= this_size; - retval = dap_queue_ap_write(ap, MEM_AP_REG_DRW, outvalue); + retval = dap_queue_ap_write(ap, MEM_AP_REG_DRW + ap->dap->adi_ap_reg_offset, outvalue); if (retval != ERROR_OK) break; @@ -531,7 +542,7 @@ static int mem_ap_read(struct adiv5_ap *ap, uint8_t *buffer, uint32_t size, uint if (retval != ERROR_OK) break; - retval = dap_queue_ap_read(ap, MEM_AP_REG_DRW, read_ptr++); + retval = dap_queue_ap_read(ap, MEM_AP_REG_DRW + dap->adi_ap_reg_offset, read_ptr++); if (retval != ERROR_OK) break; @@ -782,11 +793,11 @@ int mem_ap_init(struct adiv5_ap *ap) if (retval != ERROR_OK) return retval; - retval = dap_queue_ap_read(ap, MEM_AP_REG_CSW, &csw); + retval = dap_queue_ap_read(ap, MEM_AP_REG_CSW + dap->adi_ap_reg_offset, &csw); if (retval != ERROR_OK) return retval; - retval = dap_queue_ap_read(ap, MEM_AP_REG_CFG, &cfg); + retval = dap_queue_ap_read(ap, MEM_AP_REG_CFG + dap->adi_ap_reg_offset, &cfg); if (retval != ERROR_OK) return retval; @@ -820,7 +831,7 @@ int mem_ap_init(struct adiv5_ap *ap) ap->cfg_reg = cfg; if (cfg & 0x02) { /* See if large physical addressing is enabled */ - retval = dap_queue_ap_write(ap, MEM_AP_REG_TAR64, 0); + retval = dap_queue_ap_write(ap, MEM_AP_REG_TAR64 + ap->dap->adi_ap_reg_offset, 0); if (retval == ERROR_OK) retval = dap_run(dap); if (retval != ERROR_OK) { @@ -878,6 +889,15 @@ static const char *class_description[16] = { "Reserved", "OptimoDE DESS", "Generic IP component", "PrimeCell or System component" }; +static const char *addr_description[2] = { + "32-bit Physical Addressing", "Large Physical Addressing" +}; +static const char *class9rom_description[2] = { + "32-bit ROM Entries", "64-bit ROM Table Entries" +}; +static const char *archid_description[5] = { + "MEM_AP", "JTAG_AP", "ROM Table", "ARCHID Unknown", "" +}; static bool is_dap_cid_ok(uint32_t cid) { @@ -897,7 +917,7 @@ int dap_find_ap(struct adiv5_dap *dap, enum ap_type type_to_find, struct adiv5_a /* read the IDR register of the Access Port */ uint32_t id_val = 0; - int retval = dap_queue_ap_read(dap_ap(dap, ap_num), AP_REG_IDR, &id_val); + int retval = dap_queue_ap_read(dap_ap(dap, ap_num), AP_REG_IDR + dap->adi_ap_reg_offset, &id_val); if (retval != ERROR_OK) return retval; @@ -918,7 +938,9 @@ int dap_find_ap(struct adiv5_dap *dap, enum ap_type type_to_find, struct adiv5_a */ if ((retval == ERROR_OK) && /* Register read success */ ((id_val & IDR_JEP106) == IDR_JEP106_ARM) && /* Jedec codes match */ - ((id_val & IDR_TYPE) == type_to_find)) { /* type matches*/ + ((id_val & IDR_TYPE) == type_to_find || + (type_to_find == AP_TYPE_APB_AP ? (id_val & IDR_TYPE) == AP_TYPE_APB4_AP : 0)) /* match both APB4 and APB*/ + ) { /* type matches*/ LOG_DEBUG("Found %s at AP index: %d (IDR=0x%08" PRIX32 ")", (type_to_find == AP_TYPE_AHB3_AP) ? "AHB3-AP" : @@ -949,19 +971,22 @@ int dap_get_debugbase(struct adiv5_ap *ap, int retval; uint32_t baseptr_upper, baseptr_lower; + if (dap->adi_version == 6) + return adiv6_dap_get_debugbase(ap, dbgbase, apid); + baseptr_upper = 0; if (ap->cfg_reg & 2) { /* large physical addressing enabled */ /* Read higher order 32-bits of base address */ - retval = dap_queue_ap_read(ap, MEM_AP_REG_BASE64, &baseptr_upper); + retval = dap_queue_ap_read(ap, MEM_AP_REG_BASE64 + dap->adi_ap_reg_offset, &baseptr_upper); if (retval != ERROR_OK) return retval; } - retval = dap_queue_ap_read(ap, MEM_AP_REG_BASE, &baseptr_lower); + retval = dap_queue_ap_read(ap, MEM_AP_REG_BASE + dap->adi_ap_reg_offset, &baseptr_lower); if (retval != ERROR_OK) return retval; - retval = dap_queue_ap_read(ap, AP_REG_IDR, apid); + retval = dap_queue_ap_read(ap, AP_REG_IDR + dap->adi_ap_reg_offset, apid); if (retval != ERROR_OK) return retval; retval = dap_run(dap); @@ -980,6 +1005,10 @@ int dap_lookup_cs_component(struct adiv5_ap *ap, target_addr_t component_base; int retval; + struct adiv5_dap *dap = ap->dap; + if (dap->adi_version == 6) + return adiv6_dap_lookup_cs_component_root(ap, dbgbase, type, addr, idx); + dbgbase &= 0xFFFFFFFFFFFFF000ull; *addr = 0; @@ -1141,6 +1170,7 @@ static const struct { { ARM_ID, 0x4c7, "Cortex-M7 PPB ROM", "(Private Peripheral Bus ROM Table)", }, { ARM_ID, 0x4c8, "Cortex-M7 ROM", "(ROM Table)", }, { ARM_ID, 0x4e0, "Cortex-A35 ROM", "(v7 Memory Map ROM Table)", }, + { ARM_ID, 0x4e4, "DSU ROM v8 Debug", "(ROM Table)", }, { ARM_ID, 0x906, "CoreSight CTI", "(Cross Trigger)", }, { ARM_ID, 0x907, "CoreSight ETB", "(Trace Buffer)", }, { ARM_ID, 0x908, "CoreSight CSTF", "(Trace Funnel)", }, @@ -1184,6 +1214,10 @@ static const struct { { ARM_ID, 0x9d7, "Cortex-A57 PMU", "(Performance Monitor Unit)", }, { ARM_ID, 0x9d8, "Cortex-A72 PMU", "(Performance Monitor Unit)", }, { ARM_ID, 0x9da, "Cortex-A35 PMU/CTI/ETM", "(Performance Monitor Unit/Cross Trigger/ETM)", }, + { ARM_ID, 0x9e8, "SOC-600 ETR", "(Embedded Trace Router)", }, + { ARM_ID, 0x9ea, "SOC-600 ETF", "(Embedded Trace FIFO)", }, + { ARM_ID, 0x9ed, "SOC-600 CTI", "(Cross Trigger)", }, + { ARM_ID, 0x9ee, "SOC-600 CATU", "(Address Translation Unit)", }, { ARM_ID, 0xc05, "Cortex-A5 Debug", "(Debug Unit)", }, { ARM_ID, 0xc07, "Cortex-A7 Debug", "(Debug Unit)", }, { ARM_ID, 0xc08, "Cortex-A8 Debug", "(Debug Unit)", }, @@ -1197,6 +1231,7 @@ static const struct { { ARM_ID, 0xd04, "Cortex-A35 Debug", "(Debug Unit)", }, { ARM_ID, 0xd07, "Cortex-A57 Debug", "(Debug Unit)", }, { ARM_ID, 0xd08, "Cortex-A72 Debug", "(Debug Unit)", }, + { ARM_ID, 0xd0c, "Neoverse N1", "(Debug Unit)", }, { 0x097, 0x9af, "MSP432 ROM", "(ROM Table)" }, { 0x09f, 0xcd0, "Atmel CPU with DSU", "(CPU)" }, { 0x0c1, 0x1db, "XMC4500 ROM", "(ROM Table)" }, @@ -1214,6 +1249,34 @@ static const struct { { ANY_ID, 0x343, "TI DAPCTL", "", }, /* from OMAP3 memmap */ }; +static const struct { + uint16_t archid; + const char *full; +} dap_archid[] = { + {0x0A00, "RAS architecture"}, + {0x1A01, "Instrumentation Trace Macrocell (ITM) architecture"}, + {0x1A02, "DWT architecture"}, + {0x1A03, "Flash Patch and Breakpoint unit (FPB) architecture"}, + {0x2A04, "Processor debug architecture (ARMv8-M)"}, + {0x6A05, "Processor debug architecture (ARMv8-R)"}, + {0x0A10, "PC sample-based profiling"}, + {0x4A13, "Embedded Trace Macrocell (ETM) architecture."}, + {0x1A14, "Cross Trigger Interface (CTI) architecture"}, + {0x6A15, "Processor debug architecture (v8.0-A)"}, + {0x7A15, "Processor debug architecture (v8.1-A)"}, + {0x8A15, "Processor debug architecture (v8.2-A)"}, + {0x2A16, "Processor Performance Monitor (PMU) architecture"}, + {0x0A17, "Memory Access Port v2 architecture"}, + {0x0A27, "JTAG Access Port v2 architecture"}, + {0x0A31, "Basic trace router"}, + {0x0A37, "Power requestor"}, + {0x0A47, "Unknown Access Port v2 architecture"}, + {0x0A50, "HSSTP architecture"}, + {0x0A63, "System Trace Macrocell (STM) architecture"}, + {0x0A75, "CoreSight ELA architecture"}, + {0x0AF7, "CoreSight ROM architecture"}, +}; + static int dap_rom_display(struct command_invocation *cmd, struct adiv5_ap *ap, target_addr_t dbgbase, int depth) { @@ -1221,6 +1284,10 @@ static int dap_rom_display(struct command_invocation *cmd, uint64_t pid; uint32_t cid; char tabs[16] = ""; + struct adiv5_dap *dap = ap->dap; + + if (dap->adi_version == 6) + return adiv6_dap_rom_display(cmd, ap, dbgbase, depth); if (depth > 16) { command_print(cmd, "\tTables too deep"); @@ -1319,150 +1386,8 @@ static int dap_rom_display(struct command_invocation *cmd, } } } else if (class == 9) { /* CoreSight component */ - const char *major = "Reserved", *subtype = "Reserved"; - - uint32_t devtype; - retval = mem_ap_read_atomic_u32(ap, base_addr | 0xFCC, &devtype); - if (retval != ERROR_OK) - return retval; - unsigned minor = (devtype >> 4) & 0x0f; - switch (devtype & 0x0f) { - case 0: - major = "Miscellaneous"; - switch (minor) { - case 0: - subtype = "other"; - break; - case 4: - subtype = "Validation component"; - break; - } - break; - case 1: - major = "Trace Sink"; - switch (minor) { - case 0: - subtype = "other"; - break; - case 1: - subtype = "Port"; - break; - case 2: - subtype = "Buffer"; - break; - case 3: - subtype = "Router"; - break; - } - break; - case 2: - major = "Trace Link"; - switch (minor) { - case 0: - subtype = "other"; - break; - case 1: - subtype = "Funnel, router"; - break; - case 2: - subtype = "Filter"; - break; - case 3: - subtype = "FIFO, buffer"; - break; - } - break; - case 3: - major = "Trace Source"; - switch (minor) { - case 0: - subtype = "other"; - break; - case 1: - subtype = "Processor"; - break; - case 2: - subtype = "DSP"; - break; - case 3: - subtype = "Engine/Coprocessor"; - break; - case 4: - subtype = "Bus"; - break; - case 6: - subtype = "Software"; - break; - } - break; - case 4: - major = "Debug Control"; - switch (minor) { - case 0: - subtype = "other"; - break; - case 1: - subtype = "Trigger Matrix"; - break; - case 2: - subtype = "Debug Auth"; - break; - case 3: - subtype = "Power Requestor"; - break; - } - break; - case 5: - major = "Debug Logic"; - switch (minor) { - case 0: - subtype = "other"; - break; - case 1: - subtype = "Processor"; - break; - case 2: - subtype = "DSP"; - break; - case 3: - subtype = "Engine/Coprocessor"; - break; - case 4: - subtype = "Bus"; - break; - case 5: - subtype = "Memory"; - break; - } - break; - case 6: - major = "Performance Monitor"; - switch (minor) { - case 0: - subtype = "other"; - break; - case 1: - subtype = "Processor"; - break; - case 2: - subtype = "DSP"; - break; - case 3: - subtype = "Engine/Coprocessor"; - break; - case 4: - subtype = "Bus"; - break; - case 5: - subtype = "Memory"; - break; - } - break; - } - command_print(cmd, "\t\tType is 0x%02" PRIx8 ", %s, %s", - (uint8_t)(devtype & 0xff), - major, subtype); - /* REVISIT also show 0xfc8 DevId */ + /* call common routine for both ADIv5 and ADIv6 */ + return dap_devtype_display(cmd, ap, base_addr); } return ERROR_OK; @@ -1475,6 +1400,10 @@ int dap_info_command(struct command_invocation *cmd, uint32_t apid; target_addr_t dbgbase; uint8_t mem_ap; + struct adiv5_dap *dap = ap->dap; + + if (dap->adi_version == 6) + return adiv6_dap_info_command(cmd, ap); /* Now we read ROM table ID registers, ref. ARM IHI 0029B sec */ retval = dap_get_debugbase(ap, &dbgbase, &apid); @@ -1533,6 +1462,7 @@ int dap_info_command(struct command_invocation *cmd, enum adiv5_cfg_param { CFG_DAP, CFG_AP_NUM, + CFG_AP_BASE, CFG_BASEADDR, CFG_CTIBASE, /* DEPRECATED */ }; @@ -1540,6 +1470,7 @@ enum adiv5_cfg_param { static const Jim_Nvp nvp_config_opts[] = { { .name = "-dap", .value = CFG_DAP }, { .name = "-ap-num", .value = CFG_AP_NUM }, + { .name = "-apbase", .value = CFG_AP_BASE }, { .name = "-baseaddr", .value = CFG_BASEADDR }, { .name = "-ctibase", .value = CFG_CTIBASE }, /* DEPRECATED */ { .name = NULL, .value = -1 } @@ -1619,6 +1550,35 @@ static int adiv5_jim_spot_configure(Jim_GetOptInfo *goi, } break; + case CFG_AP_BASE: + if (goi->isconfigure) { + jim_wide ap_base; + e = Jim_GetOpt_Wide(goi, &ap_base); + if (e != JIM_OK) + return e; + if (ap_base & 0xffful) { + Jim_SetResultString(goi->interp, "Invalid AP Base Address!", -1); + return JIM_ERR; + } + if (*ap_num_p == DP_APSEL_INVALID) { + Jim_SetResultString(goi->interp, "config -ap-num must precede -apbase!", -1); + return JIM_ERR; + } + (*dap_p)->ap[*ap_num_p].base_addr = ap_base; + } else { + if (goi->argc != 0) { + Jim_WrongNumArgs(goi->interp, goi->argc, goi->argv, "NO PARAMS"); + return JIM_ERR; + } + + if (*ap_num_p == DP_APSEL_INVALID) { + Jim_SetResultString(goi->interp, "AP number not configured", -1); + return JIM_ERR; + } + Jim_SetResult(goi->interp, Jim_NewIntObj(goi->interp, *ap_num_p)); + } + break; + case CFG_CTIBASE: LOG_WARNING("DEPRECATED! use \'-baseaddr' not \'-ctibase\'"); /* fall through */ @@ -1757,21 +1717,31 @@ COMMAND_HANDLER(dap_baseaddr_command) */ ap = dap_ap(dap, apsel); - retval = dap_queue_ap_read(ap, MEM_AP_REG_BASE, &baseaddr_lower); - /* See if large physical addressing is enabled */ - if ((ap->cfg_reg & 2) && retval == ERROR_OK) - retval = dap_queue_ap_read(ap, MEM_AP_REG_BASE64, &baseaddr_upper); - if (retval != ERROR_OK) - return retval; - retval = dap_run(dap); - if (retval != ERROR_OK) - return retval; - if (ap->cfg_reg & 2) { - baseaddr = (((target_addr_t) baseaddr_upper) << 32) | baseaddr_lower; - command_print(CMD, "0x%016" PRIx64, baseaddr); - } else - command_print(CMD, "0x%08" PRIx32, baseaddr_lower); + if (dap->adi_version == 6) { + if (ap->cfg_reg != ADI_BAD_CFG) + command_print(CMD, "0x%016" PRIx64, dap_ap(dap, apsel)->base_addr); + else { + command_print(CMD, "Access Port %d not determined to be a mem_ap", ap->ap_num); + return ERROR_FAIL; + } + } else { + retval = dap_queue_ap_read(ap, MEM_AP_REG_BASE, &baseaddr_lower); + + /* See if large physical addressing is enabled */ + if ((ap->cfg_reg & 2) && retval == ERROR_OK) + retval = dap_queue_ap_read(ap, MEM_AP_REG_BASE64, &baseaddr_upper); + if (retval != ERROR_OK) + return retval; + retval = dap_run(dap); + if (retval != ERROR_OK) + return retval; + if (ap->cfg_reg & 2) { /* See if large physical addressing is enabled */ + baseaddr = (((target_addr_t) baseaddr_upper) << 32) | baseaddr_lower; + command_print(CMD, "0x%016" PRIx64, baseaddr); + } else + command_print(CMD, "0x%08" PRIx32, baseaddr_lower); + } return retval; } @@ -1888,7 +1858,7 @@ COMMAND_HANDLER(dap_apid_command) return ERROR_COMMAND_SYNTAX_ERROR; } - retval = dap_queue_ap_read(dap_ap(dap, apsel), AP_REG_IDR, &apid); + retval = dap_queue_ap_read(dap_ap(dap, apsel), AP_REG_IDR + dap->adi_ap_reg_offset, &apid); if (retval != ERROR_OK) return retval; retval = dap_run(dap); @@ -1920,14 +1890,22 @@ COMMAND_HANDLER(dap_apreg_command) ap = dap_ap(dap, apsel); COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], reg); - if (reg >= 256 || (reg & 3)) { - command_print(CMD, "Invalid reg value (should be less than 256 and 4 bytes aligned)"); - return ERROR_COMMAND_ARGUMENT_INVALID; + if (dap->adi_version == 6) { + if (reg >= 4096 || (reg & 3)) { + command_print(CMD, "Invalid reg value (should be less than 4096 and 4 bytes aligned)"); + return ERROR_COMMAND_ARGUMENT_INVALID; + } + } else { /* ADI version 5 */ + if (reg >= 256 || (reg & 3)) { + command_print(CMD, "Invalid reg value (should be less than 256 and 4 bytes aligned)"); + return ERROR_COMMAND_ARGUMENT_INVALID; + } } if (CMD_ARGC == 3) { COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], value); - switch (reg) { + /* see if user supplied register address is a match for the CSW or TAR register */ + switch (reg - dap->adi_ap_reg_offset) { case MEM_AP_REG_CSW: ap->csw_value = 0; /* invalid, in case write fails */ retval = dap_queue_ap_write(ap, reg, value); @@ -2077,3 +2055,787 @@ const struct command_registration dap_instance_commands[] = { }, COMMAND_REGISTRATION_DONE }; + +static void dap_archid_display(struct command_invocation *cmd, + struct adiv5_ap *ap, uint32_t archid) { + archid &= 0xffff; + for (unsigned entry = 0; entry < ARRAY_SIZE(dap_archid); entry++) { + if (dap_archid[entry].archid == archid) { + command_print(cmd, "\t\tArchID is 0x%" PRIx16", %s", archid, dap_archid[entry].full); + break; + } + } +} + +static int dap_devtype_display(struct command_invocation *cmd, + struct adiv5_ap *ap, target_addr_t base_addr) +{ + const char *major = "Reserved", *subtype = "Reserved"; + + uint32_t devtype; + int retval = mem_ap_read_atomic_u32(ap, base_addr | 0xFCC, &devtype); + if (retval != ERROR_OK) + return retval; + unsigned minor = (devtype >> 4) & 0x0f; + switch (devtype & 0x0f) { + case 0: + major = "Miscellaneous"; + switch (minor) { + case 0: + subtype = "other"; + break; + case 4: + subtype = "Validation component"; + break; + } + break; + case 1: + major = "Trace Sink"; + switch (minor) { + case 0: + subtype = "other"; + break; + case 1: + subtype = "Port"; + break; + case 2: + subtype = "Buffer"; + break; + case 3: + subtype = "Router"; + break; + } + break; + case 2: + major = "Trace Link"; + switch (minor) { + case 0: + subtype = "other"; + break; + case 1: + subtype = "Funnel, router"; + break; + case 2: + subtype = "Filter"; + break; + case 3: + subtype = "FIFO, buffer"; + break; + } + break; + case 3: + major = "Trace Source"; + switch (minor) { + case 0: + subtype = "other"; + break; + case 1: + subtype = "Processor"; + break; + case 2: + subtype = "DSP"; + break; + case 3: + subtype = "Engine/Coprocessor"; + break; + case 4: + subtype = "Bus"; + break; + case 6: + subtype = "Software"; + break; + } + break; + case 4: + major = "Debug Control"; + switch (minor) { + case 0: + subtype = "other"; + break; + case 1: + subtype = "Trigger Matrix"; + break; + case 2: + subtype = "Debug Auth"; + break; + case 3: + subtype = "Power Requestor"; + break; + } + break; + case 5: + major = "Debug Logic"; + switch (minor) { + case 0: + subtype = "other"; + break; + case 1: + subtype = "Processor"; + break; + case 2: + subtype = "DSP"; + break; + case 3: + subtype = "Engine/Coprocessor"; + break; + case 4: + subtype = "Bus"; + break; + case 5: + subtype = "Memory"; + break; + } + break; + case 6: + major = "Performance Monitor"; + switch (minor) { + case 0: + subtype = "other"; + break; + case 1: + subtype = "Processor"; + break; + case 2: + subtype = "DSP"; + break; + case 3: + subtype = "Engine/Coprocessor"; + break; + case 4: + subtype = "Bus"; + break; + case 5: + subtype = "Memory"; + break; + } + break; + } + command_print(cmd, "\t\tType is 0x%02" PRIx8 ", %s, %s", + (uint8_t)(devtype & 0xff), + major, subtype); + /* REVISIT also show 0xfc8 DevId */ + return ERROR_OK; +} + +static int adiv6_dap_get_debugbase(struct adiv5_ap *ap, + target_addr_t *dbgbase, uint32_t *apid) +{ + struct adiv5_dap *dap = ap->dap; + int retval = ERROR_OK; + uint32_t baseptr_upper, baseptr_lower; + + baseptr_upper = 0; + + if (ap->ap_num == 0) { /* Top level ROM table is always ap[0] */ + if (dap->asize > 32) { + /* Read higher order 32-bits of base address */ + retval = dap_dp_read_atomic(ap->dap, DP_BASEPTR1, &baseptr_upper); + if (retval == ERROR_OK) { + /* read low order 32-bits of base address first */ + retval = dap_dp_read_atomic(ap->dap, DP_BASEPTR0, &baseptr_lower); + } + } else { + /* asize field indicates only a max of 32 bits needs to be read */ + retval = dap_dp_read_atomic(ap->dap, DP_BASEPTR0, &baseptr_lower); + } + } else { + if (ap->cfg_reg == ADI_BAD_CFG) + return ERROR_FAIL; + else if (ap->cfg_reg & 2) { /* large physical addressing enabled */ + /* Read higher order 32-bits of base address */ + retval = dap_queue_ap_read(ap, MEM_AP_REG_BASE64 + dap->adi_ap_reg_offset, &baseptr_upper); + if (retval == ERROR_OK) + retval = dap_queue_ap_read(ap, MEM_AP_REG_BASE + dap->adi_ap_reg_offset, &baseptr_lower); + } else + retval = dap_queue_ap_read(ap, MEM_AP_REG_BASE + dap->adi_ap_reg_offset, &baseptr_lower); + } + + if (retval != ERROR_OK) + return retval; + retval = dap_queue_ap_read(ap, AP_REG_IDR + dap->adi_ap_reg_offset, apid); + if (retval != ERROR_OK) + return retval; + retval = dap_run(dap); + if (retval != ERROR_OK) + return retval; + + *dbgbase = (((target_addr_t) baseptr_upper) << 32) | baseptr_lower; + + return retval; +} + +static int adiv6_dap_lookup_cs_component_root(struct adiv5_ap *ap, + target_addr_t dbgbase, uint8_t type, target_addr_t *addr, int32_t *idx) { + /* Root ROM table is in a separate AP at address dbgbase */ + int retval; + uint16_t max_entry_offset, entry_offset; + uint32_t devid_reg, cidr1, rom_entry_64bit; + struct adiv5_ap rom_ap = ap->dap->ap[0]; + struct adiv5_dap *dap = ap->dap; + /* Point the AP address to dbgbase */ + rom_ap.base_addr = dbgbase; + *addr = 0; + uint32_t entry_lower; + uint32_t entry_upper = 0; + + retval = mem_ap_read_atomic_u32(&rom_ap, AP_REG_DEVID, &devid_reg); + if (retval == ERROR_OK) + retval = mem_ap_read_atomic_u32(&rom_ap, AP_REG_CIDR1, &cidr1); + if (retval != ERROR_OK) + return retval; + + rom_entry_64bit = devid_reg & 0x1; + if (((cidr1 & 0xf0) >> 4) == 9) /* is this a class 9 ROM table */ + max_entry_offset = 512; /* class 9 entry maximum count 512 */ + else + max_entry_offset = 960; /* class 1 entry maximum count 960 */ + + /* Read primary ROM table */ + for (entry_offset = 0; entry_offset < max_entry_offset; entry_offset += 4) { + target_addr_t entry; + if (rom_entry_64bit) { + retval = dap_queue_ap_read(&rom_ap, entry_offset, &entry_upper); + if (retval != ERROR_OK) + return retval; + retval = dap_queue_ap_read(&rom_ap, (entry_offset + 4), &entry_lower); + if (retval != ERROR_OK) + return retval; + + retval = dap_run(ap->dap); + if (retval != ERROR_OK) + return retval; + + entry = (((target_addr_t) entry_upper) << 32) | entry_lower; + LOG_DEBUG("ROM entry[0x%x]: 0x%16.16" PRIx64, entry_offset, entry); + entry_offset += 4; + + } else { + retval = dap_queue_ap_read(&rom_ap, entry_offset, &entry_lower); + if (retval != ERROR_OK) + return retval; + + retval = dap_run(ap->dap); + if (retval != ERROR_OK) + return retval; + /* typecast to take care of 2's complement offsets */ + entry = (target_addr_t) ((int32_t) entry_lower); + LOG_DEBUG("ROM entry[0x%x]: 0x%16.16" PRIx64, entry_offset, entry); + } + + if (entry == 0) + break; + /* Not sure how to differientiate DAP internal APB v.s. Debug APB addresses in the table*/ + if ((entry & 3) == 3) { + entry &= 0xFFFFFFFFFFFFF000ull; + rom_ap.base_addr = dbgbase + entry; + retval = mem_ap_read_atomic_u32(&rom_ap, AP_REG_IDR + dap->adi_ap_reg_offset, &cidr1); + rom_ap.base_addr = dbgbase; + if (retval != ERROR_OK) { + LOG_ERROR("ROM Table looks wrong; assumming target mem_ap for Top Level ROM entry"); + retval = adiv6_dap_lookup_cs_component(ap, entry, type, addr, idx); + if (retval == ERROR_OK) + break; + if (retval != ERROR_TARGET_RESOURCE_NOT_AVAILABLE) + return retval; + } else { + retval = adiv6_dap_lookup_cs_component(&rom_ap, entry, type, addr, idx); + if (retval == ERROR_OK) + break; + if (retval != ERROR_TARGET_RESOURCE_NOT_AVAILABLE) + return retval; + } + } + } + if (!*addr) + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + return ERROR_OK; +} + +static int adiv6_dap_lookup_cs_component(struct adiv5_ap *ap, + target_addr_t dbgbase, uint8_t type, target_addr_t *addr, int32_t *idx) +{ + uint32_t devid_reg, cidr1, entry_offset = 0, devtype; + target_addr_t component_base, romentry; + uint32_t rom_entry_64bit, romentry_lower, romentry_upper; + uint16_t max_entry_offset; + int retval; + + *addr = 0; + retval = mem_ap_read_atomic_u32(ap, AP_REG_DEVID, &devid_reg); + if (retval == ERROR_OK) + retval = mem_ap_read_atomic_u32(ap, AP_REG_CIDR1, &cidr1); + if (retval != ERROR_OK) + return retval; + + rom_entry_64bit = devid_reg & 0x1; + if (((cidr1 & 0xf0) >> 4) == 9) /* is this a class 9 ROM table */ + max_entry_offset = 0x800; /* class 9 entry maximum count 512 */ + else + max_entry_offset = 0xF00; /* class 1 entry maximum count 960 */ + + rom_entry_64bit = devid_reg & 0x1; + dbgbase &= 0xFFFFFFFFFFFFF000ull; + + do { + if (rom_entry_64bit) { + retval = mem_ap_read_atomic_u32(ap, dbgbase | + entry_offset, &romentry_upper); + if (retval != ERROR_OK) + return retval; + entry_offset += 4; + retval = mem_ap_read_atomic_u32(ap, dbgbase | + entry_offset, &romentry_lower); + if (retval != ERROR_OK) + return retval; + romentry = (((target_addr_t) romentry_upper) << 32) | romentry_lower; + + } else { + retval = mem_ap_read_atomic_u32(ap, dbgbase | + entry_offset, &romentry_lower); + if (retval != ERROR_OK) + return retval; + /* typecast to take care of 2's complement offsets */ + romentry = (target_addr_t) ((int32_t) romentry_lower); + } + + if (retval != ERROR_OK) + return retval; + + component_base = dbgbase + (romentry & (0xFFFFFFFFFFFFF000ull)); + + if (romentry & 0x1) { + uint32_t c_cid1; + bool class9_rom = false; + + retval = mem_ap_read_atomic_u32(ap, component_base | 0xff4, &c_cid1); + if (retval != ERROR_OK) { + LOG_ERROR("Can't read component with base address 0x%" PRIx64 + ", the corresponding core might be turned off", component_base); + return retval; + } + /* Class 9 CS */ + if (((c_cid1 >> 4) & 0x0f) == 9) { + uint32_t devarch; + retval = mem_ap_read_atomic_u32(ap, component_base | 0xFBC, &devarch); + if (retval != ERROR_OK) + return retval; + if ((devarch & 0xffff) == 0x0AF7) + class9_rom = true; + } + + /* Class 9 ROM or class 1 ROM */ + if (class9_rom || ((c_cid1 >> 4) & 0x0f) == 1) { + retval = adiv6_dap_lookup_cs_component(ap, component_base, + type, addr, idx); + if (retval == ERROR_OK) + break; + if (retval != ERROR_TARGET_RESOURCE_NOT_AVAILABLE) + return retval; + } + retval = mem_ap_read_atomic_u32(ap, + (component_base & (0xFFFFFFFFFFFFF000ull)) | 0xfcc, + &devtype); + if (retval != ERROR_OK) + return retval; + if ((devtype & 0xff) == type) { + if (!*idx) { + *addr = component_base; + break; + } else + (*idx)--; + } + } + entry_offset += 4; + } while ((romentry > 0) && (entry_offset < max_entry_offset)); + + if (!*addr) + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + + return ERROR_OK; +} + +static int adiv6_dap_rom_display_table(struct command_invocation *cmd, + struct adiv5_ap *ap, target_addr_t base_addr, int depth) +{ + int retval; + uint32_t memtype, devid, cidr1; + char tabs[16] = ""; + int rom_entry_64bit; + uint16_t max_entry_offset, entry_offset; + + if (depth) + snprintf(tabs, sizeof(tabs), "[L%02d] ", depth); + + retval = mem_ap_read_atomic_u32(ap, base_addr | 0xFCC, &memtype); + if (retval != ERROR_OK) + return retval; + + if (memtype & 0x01) + command_print(cmd, "\t\tMEMTYPE system memory present on bus"); + else + command_print(cmd, "\t\tMEMTYPE system memory not present: dedicated debug bus"); + + retval = mem_ap_read_atomic_u32(ap, base_addr | AP_REG_DEVID, &devid); + if (retval == ERROR_OK) + retval = mem_ap_read_atomic_u32(ap, base_addr | AP_REG_CIDR1, &cidr1); + if (retval != ERROR_OK) + return retval; + + rom_entry_64bit = devid & 0x1; + if (((cidr1 & 0xf0) >> 4) == 9) /* is this a class 9 ROM table */ + max_entry_offset = 0x800; + else + max_entry_offset = 0xF00; /* class 1 entry maximum count */ + + + /* Read ROM table entries from base address until we get 0x00000000 or reach the reserved area */ + for (entry_offset = 0; entry_offset < max_entry_offset; entry_offset += 4) { + target_addr_t romentry; + uint32_t romentry_upper = 0; + uint32_t romentry_lower; + + if (rom_entry_64bit) { + retval = mem_ap_read_atomic_u32(ap, base_addr | entry_offset, &romentry_upper); + if (retval != ERROR_OK) + return retval; + retval = mem_ap_read_atomic_u32(ap, base_addr | (entry_offset + 4), &romentry_lower); + if (retval != ERROR_OK) + return retval; + romentry = (((target_addr_t) romentry_upper) << 32) | romentry_lower; + + command_print(cmd, "\t%sROMTABLE[0x%x] = 0x16.16%" PRIx64 "", + tabs, entry_offset, romentry); + entry_offset += 4; + } else { + retval = mem_ap_read_atomic_u32(ap, base_addr | entry_offset, &romentry_lower); + if (retval != ERROR_OK) + return retval; + romentry = (target_addr_t) ((int32_t) romentry_lower); + + command_print(cmd, "\t%sROMTABLE[0x%x] = 0x%" PRIx32 "", + tabs, entry_offset, romentry_lower); + } + if (romentry & 0x01) { + /* Recurse */ + retval = adiv6_dap_rom_display(cmd, ap, base_addr + (romentry & 0xFFFFFFFFFFFFF000ull), depth + 1); + if (retval != ERROR_OK) + return retval; + } else if (romentry != 0) { + command_print(cmd, "\t\tComponent not present"); + } else { + command_print(cmd, "\t%s\tEnd of ROM table", tabs); + break; + } + } + return ERROR_OK; +} + +static int adiv6_dap_rom_display(struct command_invocation *cmd, + struct adiv5_ap *ap, target_addr_t dbgbase, int depth) +{ + int retval; + uint64_t pid; + uint32_t cid; + char tabs[16] = ""; + + if (depth > 16) { + command_print(cmd, "\tTables too deep"); + return ERROR_FAIL; + } + + if (depth) + snprintf(tabs, sizeof(tabs), "[L%02d] ", depth); + + target_addr_t base_addr = dbgbase & 0xFFFFFFFFFFFFF000ull; + command_print(cmd, "\t\tComponent base address 0x%16.16" PRIx64, base_addr); + + retval = dap_read_part_id(ap, base_addr, &cid, &pid); + if (retval != ERROR_OK) { + command_print(cmd, "\t\tCan't read component, the corresponding core might be turned off"); + return ERROR_OK; /* Don't abort recursion */ + } + + if (!is_dap_cid_ok(cid)) { + command_print(cmd, "\t\tInvalid CID 0x%08" PRIx32, cid); + return ERROR_OK; /* Don't abort recursion */ + } + + /* component may take multiple 4K pages */ + uint32_t size = (pid >> 36) & 0xf; + if (size > 0) + command_print(cmd, "\t\tStart address 0x%16.16" PRIx64, (base_addr - 0x1000 * size)); + + command_print(cmd, "\t\tPeripheral ID 0x%010" PRIx64, pid); + + uint8_t class = (cid >> 12) & 0xf; + uint16_t part_num = pid & 0xfff; + uint16_t designer_id = ((pid >> 32) & 0xf) << 8 | ((pid >> 12) & 0xff); + + if (designer_id & 0x80) { + /* JEP106 code */ + command_print(cmd, "\t\tDesigner is 0x%03" PRIx16 ", %s", + designer_id, jep106_manufacturer(designer_id >> 8, designer_id & 0x7f)); + } else { + /* Legacy ASCII ID, clear invalid bits */ + designer_id &= 0x7f; + command_print(cmd, "\t\tDesigner ASCII code 0x%02" PRIx16 ", %s", + designer_id, designer_id == 0x41 ? "ARM" : "<unknown>"); + } + + /* default values to be overwritten upon finding a match */ + const char *type = "Unrecognized"; + const char *full = ""; + + /* search dap_partnums[] array for a match */ + for (unsigned entry = 0; entry < ARRAY_SIZE(dap_partnums); entry++) { + + if ((dap_partnums[entry].designer_id != designer_id) && (dap_partnums[entry].designer_id != ANY_ID)) + continue; + + if (dap_partnums[entry].part_num != part_num) + continue; + + type = dap_partnums[entry].type; + full = dap_partnums[entry].full; + break; + } + + command_print(cmd, "\t\tPart is 0x%" PRIx16", %s %s", part_num, type, full); + command_print(cmd, "\t\tComponent class is 0x%" PRIx8 ", %s", class, class_description[class]); + + if (class == 1) { /* ROM Table */ + retval = adiv6_dap_rom_display_table(cmd, ap, base_addr, depth); + if (retval != ERROR_OK) + return retval; + } else if (class == 9) { /* CoreSight component */ + /* call common routine for both ADIv5 and ADIv6 */ + uint32_t devarch; + retval = dap_devtype_display(cmd, ap, base_addr); + if (retval != ERROR_OK) + return retval; + + retval = mem_ap_read_atomic_u32(ap, base_addr | 0xFBC, &devarch); + if (retval != ERROR_OK) + return retval; + dap_archid_display(cmd, ap, devarch & 0xffff); + /* If Class 9 CS, ARCHID 0x0AF7 is ROM */ + if ((devarch & 0xffff) == 0x0AF7) { + /* Recurse */ + retval = adiv6_dap_rom_display_table(cmd, ap, base_addr, depth); + if (retval != ERROR_OK) + return retval; + } + } + + return ERROR_OK; +} + +static int adiv6_get_dp_rom_info(struct command_invocation *cmd, struct adiv5_ap *ap, uint32_t *cidr1_reg, + uint32_t *cfg_reg, uint32_t *devid_reg, uint32_t *devarch_reg, uint32_t *id_reg) +{ + int retval; + uint32_t class = 0; + int archid_index; + int rom_entry_64bit; + + retval = ERROR_OK; + + /* Gather DP ROM table information for correct traversing */ + if (id_reg != NULL) + retval = dap_queue_ap_read(ap, AP_REG_IDR + ap->dap->adi_ap_reg_offset, id_reg); + if (retval == ERROR_OK) + retval = dap_run(ap->dap); /* read this first reg to see if you get an error */ + + if (retval == ERROR_OK && cidr1_reg != NULL) + retval = dap_queue_ap_read(ap, AP_REG_CIDR1, cidr1_reg); + if (retval == ERROR_OK && cfg_reg != NULL) + retval = dap_queue_ap_read(ap, MEM_AP_REG_CFG + ap->dap->adi_ap_reg_offset, cfg_reg); + if (retval == ERROR_OK && devid_reg != NULL) + retval = dap_queue_ap_read(ap, AP_REG_DEVID, devid_reg); + if (retval == ERROR_OK && devarch_reg != NULL) + retval = dap_queue_ap_read(ap, AP_REG_DEVARCH, devarch_reg); + if (retval == ERROR_OK) + retval = dap_run(ap->dap); + if (retval != ERROR_OK) { + command_print(cmd, "\t\tFailure encountered reading AP configuration registers"); + return retval; + } + + if (cmd != NULL) { + class = (*cidr1_reg >> 4) & 0xf; + switch (*devarch_reg & 0x0ffff) { + case 0xA17: + archid_index = 0; + break; + case 0xA27: + archid_index = 1; + break; + case 0xAF7: + archid_index = 2; + class = 1; /* identify this component as a ROM Table */ + break; + default: + archid_index = 3; + break; + } + + rom_entry_64bit = *devid_reg & 0x1; + + command_print(cmd, "\t\tCIDR1:0x%8.8" PRIx32 ", CFG:0x%8.8" PRIx32, *cidr1_reg, *cfg_reg); + command_print(cmd, "\t\tDEVID:0x%8.8" PRIx32 ",DEVARCH:0x%8.8" PRIx32, *devid_reg, *devarch_reg); + + if (class == 1) { /* One of the 2 ROM Table formats */ + command_print(cmd, "\t\tARCHID:%s", archid_description[archid_index]); + command_print(cmd, "\t\t%s", addr_description[(*cfg_reg >> 1) & 0x1]); + command_print(cmd, "\t\t%s", class9rom_description[rom_entry_64bit]); + + } else { + command_print(cmd, "\t\tARCHID:%s", archid_description[archid_index]); + command_print(cmd, "\t\t%s", addr_description[(*cfg_reg >> 1) & 0x1]); + command_print(cmd, "\t\tClass:%s", class_description[class]); + } + } + + return ERROR_OK; + +} + +static int adiv6_dap_info_command(struct command_invocation *cmd, + struct adiv5_ap *ap) +{ + int retval; + uint32_t baseptr_upper; + uint32_t baseptr_lower; + target_addr_t baseptr = 0; + target_addr_t dbgbase = 0; + uint32_t cidr1 = 0; + uint32_t devid = 0; + uint32_t devarch = 0; + uint32_t cfg = 0; + uint32_t idr; + uint32_t id_val = 0; + uint16_t max_entry_offset, entry_offset; + int rom_entry_64bit; + + struct adiv5_ap rom_ap = ap->dap->ap[0]; /* ap[0] is reserved for the DP ROM Table */ + + if (ap->dap->asize > 32) { + /* Read higher order 32-bits of base address */ + retval = dap_dp_read_atomic(ap->dap, DP_BASEPTR1, &baseptr_upper); + if (retval == ERROR_OK) { + /* read low order 32-bits of base address first */ + dap_dp_read_atomic(ap->dap, DP_BASEPTR0, &baseptr_lower); + if (retval == ERROR_OK) { + baseptr = (((uint64_t) baseptr_upper) << 32) | baseptr_lower; + command_print(cmd, "DP ROM Table BASEPTR: 0x%16.16" PRIx64, baseptr); + } + } + } else { + /* asize field indicates only a max of 32 bits needs to be read */ + retval = dap_dp_read_atomic(ap->dap, DP_BASEPTR0, &baseptr_lower); + if (retval == ERROR_OK) { + baseptr = baseptr_lower; + command_print(cmd, "DP ROM Table BASEPTR0: 0x%8.8" PRIx32, baseptr_lower); + } + } + + if (retval != ERROR_OK) + return retval; + if ((baseptr & 1) == 0) /* valid bit not set */ + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + + baseptr &= 0xfffffffffffff000ull; + + /* Point the AP address to BASEPTR */ + rom_ap.base_addr = baseptr; + + retval = adiv6_get_dp_rom_info(cmd, &rom_ap, &cidr1, &cfg, &devid, &devarch, &idr); + if (retval != ERROR_OK) + return retval; + + rom_entry_64bit = devid & 0x1; + if (((cidr1 & 0xf0) >> 4) == 9) /* is this a class 9 ROM table */ + max_entry_offset = 0x800; + else + max_entry_offset = 0xF00; /* class 1 entry maximum count */ + + + /* Read ROM table entries from base address until we get 0x00000000 or reach the reserved area */ + for (entry_offset = 0; entry_offset < max_entry_offset; entry_offset += 4) { + target_addr_t entry; + uint32_t entry_upper; + uint32_t entry_lower; + + if (rom_entry_64bit) { + retval = dap_queue_ap_read(&rom_ap, entry_offset, &entry_upper); + if (retval != ERROR_OK) + return retval; + retval = dap_queue_ap_read(&rom_ap, entry_offset + 4, &entry_lower); + /* Read apbrom through the register interface */ + if (retval != ERROR_OK) + return retval; + retval = dap_run(ap->dap); + if (retval != ERROR_OK) + return retval; + entry = (((uint64_t) entry_upper) << 32) | entry_lower; + + command_print(cmd, "ROM entry[0x%x]: 0x%16.16" PRIx64, entry_offset, entry); + entry_offset += 4; + } else { + retval = dap_queue_ap_read(&rom_ap, entry_offset, &entry_lower); + if (retval != ERROR_OK) + return retval; + retval = dap_run(ap->dap); + if (retval != ERROR_OK) + continue; + entry = (target_addr_t) ((int32_t) entry_lower); + command_print(cmd, "ROM entry[0x%x]: 0x%8.8" PRIx32, entry_offset, (uint32_t) entry); + } + + if (entry == 0) + break; + if ((entry & 3) == 3) { + rom_ap.base_addr = baseptr + entry; + retval = adiv6_get_dp_rom_info(cmd, &rom_ap, &cidr1, &cfg, &devid, &devarch, &idr); + rom_ap.base_addr = baseptr; + if (retval != ERROR_OK) { + command_print(cmd, "\t\tROM Table looks wrong; will try using target mem_ap for this entry"); + if (ap->cfg_reg == ADI_BAD_CFG) { + command_print(CMD, "\t\tAccess Port %d not determined to be a mem_ap", ap->ap_num); + continue; + } + + retval = dap_queue_ap_read(ap, AP_REG_IDR + ap->dap->adi_ap_reg_offset, &id_val); + if (retval == ERROR_OK) + retval = dap_run(ap->dap); + if (((id_val & IDR_TYPE) != AP_TYPE_APB_AP) && ((id_val & IDR_TYPE) != AP_TYPE_APB4_AP)) { + command_print(CMD, "\t\tAccess Port %d not determined to be an APB", ap->ap_num); + continue; + } + entry &= 0xFFFFFFFFFFFFF000ull; + /* read ROM table through MEM-AP */ + dap_rom_display(cmd, ap, entry, 0); + } + } + } + + retval = adiv6_dap_get_debugbase(ap, &dbgbase, &id_val); + if (retval != ERROR_OK) + return retval; + if (((id_val & IDR_TYPE) == AP_TYPE_APB_AP) || ((id_val & IDR_TYPE) == AP_TYPE_APB4_AP)) { + command_print(cmd, "MEM-AP BASE 0x%16.16" PRIx64, dbgbase); + + if (dbgbase == 0xFFFFFFFFFFFFFFFFull || (dbgbase & 0x3) == 0x2) { + command_print(cmd, "\tNo ROM table present"); + } else { + if (dbgbase & 0x01) + command_print(cmd, "\tValid ROM table present"); + else + command_print(cmd, "\tROM table in legacy format"); + + dap_rom_display(cmd, ap, dbgbase & 0xFFFFFFFFFFFFF000ull, 0); + } + } + + return ERROR_OK; +} diff --git a/src/target/arm_adi_v5.h b/src/target/arm_adi_v5.h index a9f59e8..00681c2 100644 --- a/src/target/arm_adi_v5.h +++ b/src/target/arm_adi_v5.h @@ -5,6 +5,8 @@ * Copyright (C) 2008 by Spencer Oliver * * [email protected] * * * + * Copyright (C) 2019-2021, Ampere Computing LLC * + * * * 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 * @@ -50,10 +52,14 @@ #define DP_DPIDR BANK_REG(0x0, 0x0) /* DPv1+: ro */ #define DP_ABORT BANK_REG(0x0, 0x0) /* DPv1+: SWD: wo */ #define DP_CTRL_STAT BANK_REG(0x0, 0x4) /* DPv0+: rw */ +#define DP_DPIDR1 BANK_REG(0x1, 0x0) /* DPv3: ro */ #define DP_DLCR BANK_REG(0x1, 0x4) /* DPv1+: SWD: rw */ +#define DP_BASEPTR0 BANK_REG(0x2, 0x0) /* DPv3: ro */ #define DP_TARGETID BANK_REG(0x2, 0x4) /* DPv2: ro */ +#define DP_BASEPTR1 BANK_REG(0x3, 0x0) /* DPv3: ro */ #define DP_DLPIDR BANK_REG(0x3, 0x4) /* DPv2: ro */ #define DP_EVENTSTAT BANK_REG(0x4, 0x4) /* DPv2: ro */ +#define DP_SELECT1 BANK_REG(0x5, 0x4) /* DPv3: ro */ #define DP_RESEND BANK_REG(0x0, 0x8) /* DPv1+: SWD: ro */ #define DP_SELECT BANK_REG(0x0, 0x8) /* DPv0+: JTAG: rw; SWD: wo */ #define DP_RDBUFF BANK_REG(0x0, 0xC) /* DPv0+: ro */ @@ -85,6 +91,12 @@ #define CSYSPWRUPREQ (1UL << 30) #define CSYSPWRUPACK (1UL << 31) +/****************************************************************/ +/* The MEM_AP_REG ADIv5 addresses from 0x00 (CSW) to 0xFC (IDR) */ +/* must all be incremented by 0xD00 if ADIV6 is being used */ +/****************************************************************/ +#define ADIV6_REG_DELTA 0xD00 /* Value to add to MEM_AP_REG if using ADIV6 */ +#define ADIV5_REG_DELTA 0x000 /* Value to add to MEM_AP_REG if using ADIV5 */ /* MEM-AP register addresses */ #define MEM_AP_REG_CSW 0x00 #define MEM_AP_REG_TAR 0x04 @@ -101,6 +113,13 @@ /* Generic AP register address */ #define AP_REG_IDR 0xFC /* RO: Identification Register */ +#define AP_REG_DEVARCH 0xFBC /* RO: Present bit 20 should be on and ARCHID (bits 15:0) */ +#define AP_REG_DEVID 0xFC8 /* RO: Device ID Format (bits 3:0) indicates ROM table entry size */ +#define AP_REG_CIDR0 0xFF0 /* RO: Component ID0 */ +#define AP_REG_CIDR1 0xFF4 /* RO: Component ID1 with Class identification */ +#define AP_REG_CIDR2 0xFF8 /* RO: Component ID2 */ +#define AP_REG_CIDR3 0xFFC /* RO: Component ID3 */ + /* Fields of the MEM-AP's CSW register */ #define CSW_SIZE_MASK 7 #define CSW_8BIT 0 @@ -158,6 +177,9 @@ #define DP_APSEL_MAX (255) #define DP_APSEL_INVALID (-1) +/* default configuration register setting indicating a read must be performed */ +#define ADI_BAD_CFG 0xBAD00000 + /* FIXME: not SWD specific; should be renamed, e.g. adiv5_special_seq */ enum swd_special_seq { LINE_RESET, @@ -219,6 +241,9 @@ struct adiv5_ap { /* true if tar_value is in sync with TAR register */ bool tar_valid; + /* Base address for ADIv6 APs */ + uint64_t base_addr; + /* MEM AP configuration register indicating LPAE support */ uint32_t cfg_reg; }; @@ -265,7 +290,7 @@ struct adiv5_dap { * Cache for DP_SELECT register. A value of DP_SELECT_INVALID * indicates no cached value and forces rewrite of the register. */ - uint32_t select; + uint64_t select; /* information about current pending SWjDP-AHBAP transaction */ uint8_t ack; @@ -297,6 +322,15 @@ struct adiv5_dap { /** Flag saying whether to ignore the syspwrupack flag in DAP. Some devices * do not set this bit until later in the bringup sequence */ bool ignore_syspwrupack; + + /* Indicates ADI version (5, 6 or 0 for unknown) being used */ + uint32_t adi_version; + + /* Indicates ADI AP register address delta (0 for adiv5, 0xD00 for adiv6) */ + uint32_t adi_ap_reg_offset; + + /* ADI-v6 only field indicating ROM Table address size */ + uint32_t asize; }; /** @@ -358,6 +392,7 @@ enum ap_type { AP_TYPE_APB_AP = 0x2, /* APB Memory-AP */ AP_TYPE_AXI_AP = 0x4, /* AXI Memory-AP */ AP_TYPE_AHB5_AP = 0x5, /* AHB5 Memory-AP. */ + AP_TYPE_APB4_AP = 0x6, /* APB4 Memory-AP */ }; /** diff --git a/src/target/arm_dap.c b/src/target/arm_dap.c index 4728442..f54329e 100644 --- a/src/target/arm_dap.c +++ b/src/target/arm_dap.c @@ -1,6 +1,8 @@ /*************************************************************************** * Copyright (C) 2016 by Matthias Welwarsky * * * + * Copyright (C) 2020-2021, Ampere Computing LLC * + * * * 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 * @@ -36,8 +38,6 @@ extern const struct dap_ops swd_dap_ops; extern const struct dap_ops jtag_dp_ops; extern struct adapter_driver *adapter_driver; -#define ADI_BAD_CFG 0xBAD00000 - /* DAP command support */ struct arm_dap_object { struct list_head lh; @@ -53,6 +53,8 @@ static void dap_instance_init(struct adiv5_dap *dap) for (i = 0; i <= DP_APSEL_MAX; i++) { dap->ap[i].dap = dap; dap->ap[i].ap_num = i; + /* by default init base address used for adiv6 at 16-bit granularity */ + dap->ap[i].base_addr = i << 16; /* memaccess_tck max is 255 */ dap->ap[i].memaccess_tck = 255; /* Number of bits for tar autoincrement, impl. dep. at least 10 */ @@ -105,6 +107,7 @@ static int dap_init_all(void) { struct arm_dap_object *obj; int retval; + uint32_t dpidr; LOG_DEBUG("Initializing all DAPs ..."); @@ -129,9 +132,77 @@ static int dap_init_all(void) } else dap->ops = &jtag_dp_ops; - retval = dap->ops->connect(dap); - if (retval != ERROR_OK) - return retval; + dap_instance_init(dap); + + if (dap->adi_version == 6) { + dap->adi_ap_reg_offset = ADIV6_REG_DELTA; + LOG_INFO("DAP %s configured to use ADIv6 protocol by user cfg file", jtag_tap_name(dap->tap)); + retval = dap->ops->connect(dap); + if (retval != ERROR_OK) + return retval; + } else if (dap->adi_version != 5) { + /***************************************************/ + /* User did not specify ADIv5 or ADIv6 override */ + /* so read DPIDR and switch ADI version if need be.*/ + /* Note the initial read is via an ADIv6 connection*/ + /* since that connection can handle all ADIv5 ACK */ + /* responses. An ADIv5 connection will not */ + /* recognize an ADIv6 ACK response of '4' (OK) */ + /***************************************************/ + dap->adi_ap_reg_offset = ADIV6_REG_DELTA; + dap->adi_version = 6; + retval = dap->ops->connect(dap); + if (retval != ERROR_OK) + return retval; + retval = dap->ops->queue_dp_read(dap, DP_DPIDR, &dpidr); + if (retval != ERROR_OK) { + LOG_ERROR("DAP read of DPIDR failed..."); + return retval; + } + retval = dap_run(dap); + if (retval != ERROR_OK) { + LOG_ERROR("DAP read of DPIDR failed..."); + return retval; + } + + if (((dpidr & 0x0000F000) >> 12) < 3) { + LOG_INFO("DAP %s DPIDR indicates ADIv5 protocol is being used", jtag_tap_name(dap->tap)); + dap->adi_version = 5; + dap->adi_ap_reg_offset = ADIV5_REG_DELTA; /* MEM AP register address delta to apply */ + retval = dap->ops->connect(dap); + if (retval != ERROR_OK) + return retval; + } else { + /* target is using an ADI v6 DAP which has already been set up */ + LOG_INFO("DAP %s DPIDR indicates ADIv6 protocol is being used", jtag_tap_name(dap->tap)); + } + } else { + /**************************************************/ + /* User configuration wants to force use of ADI-v5*/ + /* This may be required on DPv0 parts that do not */ + /* have a DPIDR register value indicating ADI-v5 */ + /**************************************************/ + LOG_INFO("DAP %s configured to use ADIv5 protocol by user cfg file", jtag_tap_name(dap->tap)); + dap->adi_ap_reg_offset = ADIV5_REG_DELTA; /* MEM AP register address delta to apply */ + retval = dap->ops->connect(dap); + if (retval != ERROR_OK) + return retval; + } + /* see if address size of ROM Table is greater than 32-bits */ + if (dap->adi_version == 6) { + retval = dap->ops->queue_dp_read(dap, DP_DPIDR1, &dpidr); + if (retval != ERROR_OK) { + LOG_ERROR("DAP read of DPIDR1 failed..."); + return retval; + } + retval = dap_run(dap); + if (retval != ERROR_OK) { + LOG_ERROR("DAP read of DPIDR1 failed..."); + return retval; + } + dap->asize = dpidr & 0x0000007F; + } else + dap->asize = 32; /* ADIv5 only supports one select reg */ } return ERROR_OK; @@ -157,11 +228,15 @@ int dap_cleanup_all(void) enum dap_cfg_param { CFG_CHAIN_POSITION, CFG_IGNORE_SYSPWRUPACK, + CFG_ADIV6, + CFG_ADIV5, }; static const Jim_Nvp nvp_config_opts[] = { { .name = "-chain-position", .value = CFG_CHAIN_POSITION }, { .name = "-ignore-syspwrupack", .value = CFG_IGNORE_SYSPWRUPACK }, + { .name = "-adiv6", .value = CFG_ADIV6 }, + { .name = "-adiv5", .value = CFG_ADIV5 }, { .name = NULL, .value = -1 } }; @@ -197,6 +272,12 @@ static int dap_configure(Jim_GetOptInfo *goi, struct arm_dap_object *dap) case CFG_IGNORE_SYSPWRUPACK: dap->dap.ignore_syspwrupack = true; break; + case CFG_ADIV6: + dap->dap.adi_version = 6; + break; + case CFG_ADIV5: + dap->dap.adi_version = 5; + break; default: break; } -- _______________________________________________ OpenOCD-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/openocd-devel
