From c008d30fe85a674842632e32d732e22e0a91b95d Mon Sep 17 00:00:00 2001 From: David Brownell Date: Tue, 24 Nov 2009 21:24:44 -0800 Subject: [PATCH] Cortex-A8: implement DPM This implements the DPM interface for Cortex-A8 cores. It also adds a synchronization operation to the DPM framework, which is needed by the Cortex-A8 after CPSR writes. Signed-off-by: David Brownell --- src/target/arm_dpm.c | 13 +--- src/target/arm_dpm.h | 3 + src/target/armv7a.h | 2 + src/target/cortex_a8.c | 167 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 176 insertions(+), 9 deletions(-) diff --git a/src/target/arm_dpm.c b/src/target/arm_dpm.c index 18a9dcc74..b8107d785 100644 --- a/src/target/arm_dpm.c +++ b/src/target/arm_dpm.c @@ -52,10 +52,8 @@ static int dpm_modeswitch(struct arm_dpm *dpm, enum armv4_5_mode mode) retval = dpm->instr_write_data_r0(dpm, ARMV4_5_MSR_GP(0, 0xf, 0), cpsr); - /* REVISIT on Cortex-A8, we need a Prefetch Flush operation too ... - cortex_a8_exec_opcode(target, - ARMV4_5_MCR(15, 0, 0, 7, 5, 4)); - */ + if (dpm->instr_cpsr_sync) + retval = dpm->instr_cpsr_sync(dpm); return retval; } @@ -142,11 +140,8 @@ static int dpm_write_reg(struct arm_dpm *dpm, struct reg *r, unsigned regnum) ARMV4_5_MSR_GP(0, 0xf, regnum & 1), value); - /* REVISIT on Cortex-A8, we need a Prefetch Flush operation - * after writing CPSR ... - cortex_a8_exec_opcode(target, - ARMV4_5_MCR(15, 0, 0, 7, 5, 4)); - */ + if (regnum == 16 && dpm->instr_cpsr_sync) + retval = dpm->instr_cpsr_sync(dpm); break; } diff --git a/src/target/arm_dpm.h b/src/target/arm_dpm.h index 06b548ecc..67ce2180e 100644 --- a/src/target/arm_dpm.h +++ b/src/target/arm_dpm.h @@ -61,6 +61,9 @@ struct arm_dpm { int (*instr_write_data_r0)(struct arm_dpm *, uint32_t opcode, uint32_t data); + /** Optional core-specific operation invoked after CPSR writes. */ + int (*instr_cpsr_sync)(struct arm_dpm *dpm); + /* READ FROM CPU */ /** Runs one instruction, reading data from dcc after execution. */ diff --git a/src/target/armv7a.h b/src/target/armv7a.h index 51f7b45aa..942bf8bff 100644 --- a/src/target/armv7a.h +++ b/src/target/armv7a.h @@ -23,6 +23,7 @@ #include "armv4_5.h" #include "armv4_5_mmu.h" #include "armv4_5_cache.h" +#include "arm_dpm.h" enum { @@ -53,6 +54,7 @@ struct armv7a_common struct swjdp_common swjdp_info; /* Core Debug Unit */ + struct arm_dpm dpm; uint32_t debug_base; uint8_t debug_ap; uint8_t memory_ap; diff --git a/src/target/cortex_a8.c b/src/target/cortex_a8.c index f549fb394..28c75b591 100644 --- a/src/target/cortex_a8.c +++ b/src/target/cortex_a8.c @@ -351,6 +351,173 @@ static int cortex_a8_dap_write_memap_register_u32(struct target *target, uint32_ return retval; } +/* + * Cortex-A8 implementation of Debug Programmer's Model + * + * NOTE that in several of these cases the "stall" mode might be useful. + * It'd let us queue a few operations together... prepare/finish might + * be the places to enable/disable that mode. + */ + +static inline struct cortex_a8_common *dpm_to_a8(struct arm_dpm *dpm) +{ + return container_of(dpm, struct cortex_a8_common, armv7a_common.dpm); +} + +static int cortex_a8_write_dcc(struct cortex_a8_common *a8, uint32_t data) +{ + LOG_DEBUG("write DCC 0x%08" PRIx32, data); + return mem_ap_write_u32(&a8->armv7a_common.swjdp_info, + a8->armv7a_common.debug_base + CPUDBG_DTRRX, data); +} + +static int cortex_a8_read_dcc(struct cortex_a8_common *a8, uint32_t *data) +{ + struct swjdp_common *swjdp = &a8->armv7a_common.swjdp_info; + uint32_t dscr; + int retval; + + /* Wait for DTRRXfull */ + do { + retval = mem_ap_read_atomic_u32(swjdp, + a8->armv7a_common.debug_base + CPUDBG_DSCR, + &dscr); + } while ((dscr & (1 << DSCR_DTR_TX_FULL)) == 0); + + retval = mem_ap_read_atomic_u32(swjdp, + a8->armv7a_common.debug_base + CPUDBG_DTRTX, data); + LOG_DEBUG("read DCC 0x%08" PRIx32, *data); + + return retval; +} + +static int cortex_a8_dpm_prepare(struct arm_dpm *dpm) +{ + struct cortex_a8_common *a8 = dpm_to_a8(dpm); + struct swjdp_common *swjdp = &a8->armv7a_common.swjdp_info; + uint32_t dscr; + int retval; + + retval = mem_ap_read_atomic_u32(swjdp, + a8->armv7a_common.debug_base + CPUDBG_DSCR, + &dscr); + + /* this "should never happen" ... */ + if (dscr & (1 << DSCR_DTR_RX_FULL)) { + LOG_ERROR("DSCR_DTR_RX_FULL, dscr 0x%08" PRIx32, dscr); + /* Clear DCCRX */ + retval = cortex_a8_exec_opcode( + a8->armv7a_common.armv4_5_common.target, + ARMV4_5_MRC(14, 0, 0, 0, 5, 0)); + } + + return retval; +} + +static int cortex_a8_dpm_finish(struct arm_dpm *dpm) +{ + /* REVISIT what could be done here? */ + return ERROR_OK; +} + +static int cortex_a8_instr_write_data_dcc(struct arm_dpm *dpm, + uint32_t opcode, uint32_t data) +{ + struct cortex_a8_common *a8 = dpm_to_a8(dpm); + int retval; + + retval = cortex_a8_write_dcc(a8, data); + + return cortex_a8_exec_opcode( + a8->armv7a_common.armv4_5_common.target, + opcode); +} + +static int cortex_a8_instr_write_data_r0(struct arm_dpm *dpm, + uint32_t opcode, uint32_t data) +{ + struct cortex_a8_common *a8 = dpm_to_a8(dpm); + int retval; + + retval = cortex_a8_write_dcc(a8, data); + + /* DCCRX to R0, "MCR p14, 0, R0, c0, c5, 0", 0xEE000E15 */ + retval = cortex_a8_exec_opcode( + a8->armv7a_common.armv4_5_common.target, + ARMV4_5_MRC(14, 0, 0, 0, 5, 0)); + + /* then the opcode, taking data from R0 */ + retval = cortex_a8_exec_opcode( + a8->armv7a_common.armv4_5_common.target, + opcode); + + return retval; +} + +static int cortex_a8_instr_cpsr_sync(struct arm_dpm *dpm) +{ + struct target *target = dpm->arm->target; + + /* "Prefetch flush" after modifying execution status in CPSR */ + return cortex_a8_exec_opcode(target, ARMV4_5_MCR(15, 0, 0, 7, 5, 4)); +} + +static int cortex_a8_instr_read_data_dcc(struct arm_dpm *dpm, + uint32_t opcode, uint32_t *data) +{ + struct cortex_a8_common *a8 = dpm_to_a8(dpm); + int retval; + + /* the opcode, writing data to DCC */ + retval = cortex_a8_exec_opcode( + a8->armv7a_common.armv4_5_common.target, + opcode); + + return cortex_a8_read_dcc(a8, data); +} + + +static int cortex_a8_instr_read_data_r0(struct arm_dpm *dpm, + uint32_t opcode, uint32_t *data) +{ + struct cortex_a8_common *a8 = dpm_to_a8(dpm); + int retval; + + /* the opcode, writing data to R0 */ + retval = cortex_a8_exec_opcode( + a8->armv7a_common.armv4_5_common.target, + opcode); + + /* write R0 to DCC */ + retval = cortex_a8_exec_opcode( + a8->armv7a_common.armv4_5_common.target, + ARMV4_5_MCR(14, 0, 0, 0, 5, 0)); + + return cortex_a8_read_dcc(a8, data); +} + +// static +int cortex_a8_dpm_setup(struct cortex_a8_common *a8, uint32_t didr) +{ + struct arm_dpm *dpm = &a8->armv7a_common.dpm; + + dpm->arm = &a8->armv7a_common.armv4_5_common; + dpm->didr = didr; + + dpm->prepare = cortex_a8_dpm_prepare; + dpm->finish = cortex_a8_dpm_finish; + + dpm->instr_write_data_dcc = cortex_a8_instr_write_data_dcc; + dpm->instr_write_data_r0 = cortex_a8_instr_write_data_r0; + dpm->instr_cpsr_sync = cortex_a8_instr_cpsr_sync; + + dpm->instr_read_data_dcc = cortex_a8_instr_read_data_dcc; + dpm->instr_read_data_r0 = cortex_a8_instr_read_data_r0; + + return arm_dpm_setup(dpm); +} + + /* * Cortex-A8 Run control */