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 <dbrownell@users.sourceforge.net>
This commit is contained in:
parent
991daa03eb
commit
c008d30fe8
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue