947 lines
27 KiB
C
947 lines
27 KiB
C
/*
|
|
* Motorola Background Debug Mode Driver
|
|
* Copyright (C) 1995 W. Eric Norum
|
|
* Copyright (C) 1998-2008 Chris Johns
|
|
*
|
|
* 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
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*
|
|
* W. Eric Norum
|
|
* Saskatchewan Accelerator Laboratory
|
|
* University of Saskatchewan
|
|
* 107 North Road
|
|
* Saskatoon, Saskatchewan, CANADA
|
|
* S7N 5C6
|
|
*
|
|
* eric@skatter.usask.ca
|
|
*
|
|
* Coldfire support by:
|
|
* Chris Johns
|
|
* chris@contemporary.net.au
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* PD (Eric's) CPU32 Interface
|
|
*
|
|
* Parallel port bit assignments
|
|
*
|
|
* Status register (bits 0-2 not used):
|
|
* +---+---+---+---+---+---+---+---+
|
|
* | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|
|
* +---+---+---+---+---+---+---+---+
|
|
* | | | | |
|
|
* | | | | +--- Target FREEZE line
|
|
* | | | | 1 - Target is in background mode
|
|
* | | | | 0 - Target is not background mode
|
|
* | | | |
|
|
* | | | +------- Not used
|
|
* | | |
|
|
* | | +----------- Serial data from target
|
|
* | | 1 - `0' from target
|
|
* | | 0 - `1' from target
|
|
* | |
|
|
* | +--------------- Target power
|
|
* | 1 - Target power is ON
|
|
* | 0 - Target power is OFF
|
|
* |
|
|
* +------------------- Target connected
|
|
* 1 - Target is connected
|
|
* 0 - Target is not connected
|
|
*
|
|
* Control register (bits 4-7 not used):
|
|
* +---+---+---+---+
|
|
* | 3 | 2 | 1 | 0 |
|
|
* +---+---+---+---+
|
|
* | | | |
|
|
* | | | +--- Target BKPT* /DSCLK line
|
|
* | | | Write 1 - Drive BKPT* /DSCLK line LOW
|
|
* | | | Write 0 - Allow BKPT* /DSCLK line to go HIGH
|
|
* | | | Allow flip-flop to control BKPT* /DSCLK line
|
|
* | | |
|
|
* | | +------- Target RESET* line
|
|
* | | Write 1 - Force RESET* LOW
|
|
* | | Write 0 - Allow monitoring of RESET*
|
|
* | | Read 1 - RESET* is LOW
|
|
* | | Read 0 - RESET* is HIGH
|
|
* | |
|
|
* | +----------- Serial data to target
|
|
* | Write 0 - Send `0' to target
|
|
* | Write 1 - Send `1' to target
|
|
* |
|
|
* +--------------- Control single-step flip-flop
|
|
* Write 1 - Clear flip-flop
|
|
* BKPT* /DSCLK is controlled by bit 0.
|
|
* Write 0 - Allow flip-flop operation
|
|
* Falling edge of IFETCH* /DSI clocks a `1'
|
|
* into the flip-flop and drive BKPT* /DSCLK
|
|
* LOW, causing a breakpoint.
|
|
*/
|
|
#define CPU32_PD_SR_CONNECTED (0x80)
|
|
#define CPU32_PD_SR_POWERED (0x40)
|
|
#define CPU32_PD_SR_DATA_BAR (0x20)
|
|
#define CPU32_PD_SR_FROZEN (0x08)
|
|
|
|
#define CPU32_PD_CR_NOT_SINGLESTEP (0x08)
|
|
#define CPU32_PD_CR_DATA (0x04)
|
|
#define CPU32_PD_CR_FORCE_RESET (0x02)
|
|
#define CPU32_PD_CR_RESET_STATUS (0x02)
|
|
#define CPU32_PD_CR_CLOCKBAR_BKPT (0x01)
|
|
|
|
/*
|
|
* ICD interface.
|
|
*
|
|
* Parallel port bit assignments
|
|
*
|
|
* Status register
|
|
* +---+---+---+---+---+---+---+---+
|
|
* | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|
|
* +---+---+---+---+---+---+---+---+
|
|
* | | | | |
|
|
* | | | | +--- Not used
|
|
* | | | +------- Not used
|
|
* | | +----------- Not used
|
|
* | |
|
|
* | +--------------- Target FREEZE line
|
|
* | 1 - Target is in background mode
|
|
* | 0 - Target is not background mode
|
|
* |
|
|
* +------------------- Serial data from target
|
|
* 1 - `0' from target
|
|
* 0 - `1' from target
|
|
*
|
|
* Data register
|
|
* +---+---+---+---+---+---+---+---+
|
|
* | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|
|
* +---+---+---+---+---+---+---+---+
|
|
* | | | | | | | |
|
|
* | | | | | | | +--- Serial data to target
|
|
* | | | | | | | Write 1: Send 1 to target
|
|
* | | | | | | | Write 0: Send 0 to target
|
|
* | | | | | | | Signal gets to target, if OE is 1
|
|
* | | | | | | | and target is in FREEZE mode
|
|
* | | | | | | |
|
|
* | | | | | | +------- Clock
|
|
* | | | | | | if target in freeze mode, then:
|
|
* | | | | | | Write 1: drive BKPT* /DSCLK 1
|
|
* | | | | | | Write 0: drive BKPT* /DSCLK 0
|
|
* | | | | | |
|
|
* | | | | | +----------- BREAK
|
|
* | | | | | if target not in freeze mode, then:
|
|
* | | | | | Write 0: drive BKPT* /DSCLK 0
|
|
* | | | | | line determines single stepping
|
|
* | | | | | on leaving BGND mode:
|
|
* | | | | | Write 0: do single step
|
|
* | | | | | Write 1: continue normally
|
|
* | | | | |
|
|
* | | | | +--------------- RESET
|
|
* | | | | Write 0: pull reset low
|
|
* | | | | Write 1: release reset line
|
|
* | | | |
|
|
* | | | +--- OE
|
|
* | | | Write 0 - DSI is tristated
|
|
* | | | Write 1 - DSI pin is forced to level of serial data
|
|
* | | |
|
|
* | | +------- LED
|
|
* | | Write 1 - turn on LED
|
|
* | | Write 0 - turn off LED
|
|
* | |
|
|
* | +----------- ERROR
|
|
* | Write 0 - BERR output is tristated
|
|
* | Write 1 - BERR is pulled low
|
|
* |
|
|
* +--------------- spare
|
|
*/
|
|
|
|
#define CPU32_ICD_DSI (1 << 0) /* data shift input Host->MCU */
|
|
#define CPU32_ICD_DSCLK (1 << 1) /* data shift clock / breakpoint pin */
|
|
#define CPU32_ICD_STEP_OUT (1 << 2) /* set low to force breakpoint */
|
|
#define CPU32_ICD_RST_OUT (1 << 3) /* set low to force reset on MCU */
|
|
#define CPU32_ICD_OE (1 << 4) /* set to a 1 to enable DSI */
|
|
#define CPU32_ICD_FORCE_BERR (1 << 6) /* set to a 1 to force BERR on target */
|
|
#define CPU32_ICD_FREEZE (1 << 6) /* */
|
|
#define CPU32_ICD_DSO (1 << 7) /* */
|
|
|
|
/*
|
|
************************************************************************
|
|
* CPU32 for PD/ICD interface support routines *
|
|
************************************************************************
|
|
*/
|
|
|
|
/*
|
|
* CPU32 system register mapping. See bdm.h for the user values.
|
|
*/
|
|
|
|
static int cpu32_sysreg_map[BDM_REG_VBR + 1] =
|
|
{ 0x0, /* BDM_REG_RPC */
|
|
0x1, /* BDM_REG_PCC */
|
|
0xb, /* BDM_REG_SR */
|
|
0xc, /* BDM_REG_USP */
|
|
0xd, /* BDM_REG_SSP */
|
|
0xe, /* BDM_REG_SFC */
|
|
0xf, /* BDM_REG_DFC */
|
|
0x8, /* BDM_REG_ATEMP */
|
|
0x9, /* BDM_REG_FAR */
|
|
0xa /* BDM_REG_VBR */
|
|
};
|
|
|
|
/* need by cpu32_read_sysreg() */
|
|
static int cpu32_write_sysreg (struct BDM *self, struct BDMioctl *ioc, int mode);
|
|
static int cpu32_icd_stop_chip (struct BDM *self);
|
|
|
|
/*
|
|
* Clock a word to/from the target
|
|
*/
|
|
|
|
static int
|
|
cpu32_serial_clock (struct BDM *self, unsigned short wval, int holdback)
|
|
{
|
|
return (self->serial_clock) (self, wval, holdback);
|
|
}
|
|
|
|
/*
|
|
* Get target status
|
|
*/
|
|
static int
|
|
cpu32_pd_get_status (struct BDM *self)
|
|
{
|
|
unsigned char sr = inb (self->statusPort);
|
|
int ret;
|
|
|
|
if (!(sr & CPU32_PD_SR_CONNECTED))
|
|
ret = BDM_TARGETNC;
|
|
else if (!(sr & CPU32_PD_SR_POWERED))
|
|
ret = BDM_TARGETPOWER;
|
|
else
|
|
ret = (sr & CPU32_PD_SR_FROZEN ? BDM_TARGETSTOPPED : 0) |
|
|
(inb (self->controlPort) & CPU32_PD_CR_RESET_STATUS ? BDM_TARGETRESET : 0);
|
|
if (self->debugFlag > 1)
|
|
PRINTF (" cpu32_pd_get_status -- Status Port:0x%02x Status:0x%04x\n",
|
|
sr, ret);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Hardware initialization
|
|
*/
|
|
static int
|
|
cpu32_pd_init_hardware (struct BDM *self)
|
|
{
|
|
int status;
|
|
|
|
/*
|
|
* Force breakpoint
|
|
*/
|
|
outb (CPU32_PD_CR_NOT_SINGLESTEP | CPU32_PD_CR_CLOCKBAR_BKPT, self->controlPort);
|
|
udelay (100);
|
|
outb (CPU32_PD_CR_FORCE_RESET | CPU32_PD_CR_CLOCKBAR_BKPT, self->controlPort);
|
|
bdm_sleep (HZ / 100);
|
|
outb (CPU32_PD_CR_CLOCKBAR_BKPT, self->controlPort);
|
|
udelay (10);
|
|
outb (CPU32_PD_CR_NOT_SINGLESTEP | CPU32_PD_CR_CLOCKBAR_BKPT, self->controlPort);
|
|
udelay (100);
|
|
status = cpu32_pd_get_status (self);
|
|
if (self->debugFlag)
|
|
PRINTF (" cpu32_pd_init_hardware: status:0x%02x control port:0x%02x\n",
|
|
status, inb (self->controlPort));
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* Clock a word to/from the target
|
|
*/
|
|
static int
|
|
cpu32_pd_serial_clock (struct BDM *self, unsigned short wval, int holdback)
|
|
{
|
|
unsigned long shiftRegister;
|
|
unsigned char dataBit;
|
|
unsigned int counter;
|
|
unsigned int status = cpu32_pd_get_status (self);
|
|
|
|
if (status & BDM_TARGETRESET)
|
|
return BDM_FAULT_RESET;
|
|
if (status & BDM_TARGETNC)
|
|
return BDM_FAULT_CABLE;
|
|
if (status & BDM_TARGETPOWER)
|
|
return BDM_FAULT_POWER;
|
|
shiftRegister = wval;
|
|
counter = 17 - holdback;
|
|
while (counter--) {
|
|
dataBit = ((shiftRegister & 0x10000) ? CPU32_PD_CR_DATA : 0);
|
|
shiftRegister <<= 1;
|
|
outb (dataBit | CPU32_PD_CR_NOT_SINGLESTEP | CPU32_PD_CR_CLOCKBAR_BKPT,
|
|
self->controlPort);
|
|
bdm_delay (self->delayTimer + 1);
|
|
if ((inb (self->statusPort) & CPU32_PD_SR_DATA_BAR) == 0)
|
|
shiftRegister |= 1;
|
|
outb (dataBit | CPU32_PD_CR_NOT_SINGLESTEP, self->controlPort);
|
|
bdm_delay ((self->delayTimer >> 1) + 1);
|
|
}
|
|
self->readValue = shiftRegister & 0x1FFFF;
|
|
if (self->debugFlag)
|
|
PRINTF (" cpu32_pd_serial_clock -- send 0x%05x receive 0x%05x\n",
|
|
wval, self->readValue);
|
|
if (self->readValue & 0x10000) {
|
|
if (self->readValue == 0x10001)
|
|
return BDM_FAULT_BERR;
|
|
else if (self->readValue != 0x10000)
|
|
return BDM_FAULT_NVC;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Restart chip and stop on first instruction fetch
|
|
*/
|
|
static int
|
|
cpu32_pd_restart_chip (struct BDM *self)
|
|
{
|
|
int check;
|
|
|
|
if (self->debugFlag)
|
|
PRINTF (" cpu32_pd_restart_chip\n");
|
|
outb (CPU32_PD_CR_FORCE_RESET | CPU32_PD_CR_CLOCKBAR_BKPT, self->controlPort);
|
|
udelay (10);
|
|
outb (CPU32_PD_CR_CLOCKBAR_BKPT, self->controlPort);
|
|
udelay (10);
|
|
outb (CPU32_PD_CR_NOT_SINGLESTEP | CPU32_PD_CR_CLOCKBAR_BKPT, self->controlPort);
|
|
for (check = 0 ; check < 1000 ; check++) {
|
|
if (inb (self->statusPort) & CPU32_PD_SR_FROZEN)
|
|
return 0;
|
|
}
|
|
return BDM_FAULT_RESPONSE;
|
|
}
|
|
|
|
/*
|
|
* Restart chip and disable background debugging mode
|
|
*/
|
|
static int
|
|
cpu32_pd_release_chip (struct BDM *self)
|
|
{
|
|
if (self->debugFlag)
|
|
PRINTF (" cpu32_pd_release_chip\n");
|
|
outb (CPU32_PD_CR_NOT_SINGLESTEP | CPU32_PD_CR_FORCE_RESET, self->controlPort);
|
|
udelay (10);
|
|
outb (CPU32_PD_CR_NOT_SINGLESTEP, self->controlPort);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Restart chip, enable background debugging mode, halt on first fetch
|
|
*
|
|
* The software from the Motorola BBS tries to have the target
|
|
* chip begin execution, but that doesn't work very reliably.
|
|
* The RESETH* line rises rather slowly, so sometimes the BKPT* / DSCLK
|
|
* would be seen low, and sometimes it wouldn't.
|
|
*/
|
|
static int
|
|
cpu32_pd_reset_chip (struct BDM *self)
|
|
{
|
|
if (self->debugFlag)
|
|
PRINTF (" cpu32_pd_reset_chip\n");
|
|
outb (CPU32_PD_CR_CLOCKBAR_BKPT | CPU32_PD_CR_FORCE_RESET,
|
|
self->controlPort);
|
|
udelay (10);
|
|
outb (CPU32_PD_CR_CLOCKBAR_BKPT, self->controlPort);
|
|
udelay (10);
|
|
outb (CPU32_PD_CR_CLOCKBAR_BKPT | CPU32_PD_CR_NOT_SINGLESTEP,
|
|
self->controlPort);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Force the target into background debugging mode
|
|
*/
|
|
static int
|
|
cpu32_pd_stop_chip (struct BDM *self)
|
|
{
|
|
int check;
|
|
|
|
if (self->debugFlag)
|
|
PRINTF (" cpu32_pd_stop_chip\n");
|
|
if (inb (self->statusPort) & CPU32_PD_SR_FROZEN)
|
|
return 0;
|
|
outb (CPU32_PD_CR_NOT_SINGLESTEP | CPU32_PD_CR_CLOCKBAR_BKPT,
|
|
self->controlPort);
|
|
for (check = 0 ; check < 1000 ; check++) {
|
|
if (inb (self->statusPort) & CPU32_PD_SR_FROZEN)
|
|
return 0;
|
|
}
|
|
return BDM_FAULT_RESPONSE;
|
|
}
|
|
|
|
/*
|
|
* Make the target execute a single instruction and
|
|
* reenter background debugging mode
|
|
*/
|
|
static int
|
|
cpu32_pd_step_chip (struct BDM *self)
|
|
{
|
|
int check;
|
|
unsigned char dataBit;
|
|
int err;
|
|
|
|
if (self->debugFlag)
|
|
PRINTF (" cpu32_pd_step_chip\n");
|
|
err = cpu32_serial_clock (self, BDM_GO_CMD, 1);
|
|
if (err)
|
|
return err;
|
|
|
|
/*
|
|
* Send the last bit of the command
|
|
*/
|
|
dataBit = (BDM_GO_CMD & 0x1) ? CPU32_PD_CR_DATA : 0;
|
|
outb (dataBit | CPU32_PD_CR_NOT_SINGLESTEP | CPU32_PD_CR_CLOCKBAR_BKPT,
|
|
self->controlPort);
|
|
bdm_delay (self->delayTimer + 1);
|
|
|
|
/*
|
|
* Enable single-step
|
|
*/
|
|
outb (dataBit | CPU32_PD_CR_CLOCKBAR_BKPT, self->controlPort);
|
|
bdm_delay (1);
|
|
outb (dataBit, self->controlPort);
|
|
|
|
/*
|
|
* Wait for step to complete
|
|
* The software from the Motorola BBS doesn't do this, but
|
|
* omitting the `outb' operation leaves a race condition the
|
|
* next time cpu32_serial_clock is called.
|
|
*
|
|
* The first output operation in bdmSerialClock sends
|
|
* `dataBit | CPU32_CR_NOT_SINGLESTEP | CPU32_CR_CLOCKBAR_BKPT' to the
|
|
* control port. If the flip flop in the external circuit
|
|
* clears before the `CPU32_CR_CLOCKBAR_BKPT' pin of the '132 goes
|
|
* low, there is a narrow glitch on the BKPT* / DSCLK pin, which
|
|
* clocks a garbage bit into the target chip.
|
|
*/
|
|
for (check = 0 ; check < 1000 ; check++) {
|
|
if (inb (self->statusPort) & CPU32_PD_SR_FROZEN) {
|
|
outb (CPU32_PD_CR_CLOCKBAR_BKPT, self->controlPort);
|
|
return 0;
|
|
}
|
|
}
|
|
return BDM_FAULT_RESPONSE;
|
|
}
|
|
|
|
/*
|
|
* Get target status
|
|
*/
|
|
static int
|
|
cpu32_icd_get_status (struct BDM *self)
|
|
{
|
|
unsigned char sr = inb (self->statusPort);
|
|
int ret;
|
|
|
|
ret = sr & CPU32_ICD_FREEZE ? BDM_TARGETSTOPPED : 0;
|
|
if (self->debugFlag > 1)
|
|
PRINTF (" cpu32_icd_get_status -- Status Port:0x%02x Status:0x%04x\n",
|
|
sr, ret);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Hardware initialization
|
|
*/
|
|
static int
|
|
cpu32_icd_init_hardware (struct BDM *self)
|
|
{
|
|
int status;
|
|
|
|
/*
|
|
* Force breakpoint
|
|
*/
|
|
outb (CPU32_ICD_STEP_OUT | CPU32_ICD_DSCLK | CPU32_ICD_RST_OUT, self->dataPort);
|
|
udelay (10);
|
|
status = cpu32_icd_get_status (self);
|
|
if (self->debugFlag)
|
|
PRINTF (" cpu32_icd_init_hardware: status:0x%02x, data port:0x%02x\n",
|
|
status, inb (self->dataPort));
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* Clock a word to/from the target
|
|
*/
|
|
static int
|
|
cpu32_icd_serial_clock (struct BDM *self, unsigned short wval, int holdback)
|
|
{
|
|
unsigned long shiftRegister;
|
|
unsigned char dataBit;
|
|
unsigned int counter;
|
|
unsigned int status = cpu32_icd_get_status (self);
|
|
|
|
if (status & BDM_TARGETRESET)
|
|
return BDM_FAULT_RESET;
|
|
if (status & BDM_TARGETNC)
|
|
return BDM_FAULT_CABLE;
|
|
if (status & BDM_TARGETPOWER)
|
|
return BDM_FAULT_POWER;
|
|
if(!(status & BDM_TARGETSTOPPED)) {
|
|
if (self->debugFlag)
|
|
PRINTF (" cpu32_icd_serial_clock -- stop target first\n");
|
|
if (cpu32_icd_stop_chip (self) == BDM_FAULT_RESPONSE) {
|
|
if (self->debugFlag)
|
|
PRINTF (" cpu32_icd_serial_clock -- can\'t stop it\n");
|
|
return BDM_FAULT_RESPONSE;
|
|
}
|
|
}
|
|
shiftRegister = wval;
|
|
counter = 17 - holdback;
|
|
while (counter--) {
|
|
dataBit = ((shiftRegister & 0x10000) ? CPU32_ICD_DSI : 0);
|
|
shiftRegister <<= 1;
|
|
outb (dataBit | CPU32_ICD_RST_OUT | CPU32_ICD_OE | CPU32_ICD_STEP_OUT,
|
|
self->dataPort);
|
|
bdm_delay (self->delayTimer + 1);
|
|
if ((inb (self->statusPort) & CPU32_ICD_DSO) == 0)
|
|
shiftRegister |= 1;
|
|
outb (dataBit | CPU32_ICD_RST_OUT | CPU32_ICD_OE | CPU32_ICD_STEP_OUT | CPU32_ICD_DSCLK,
|
|
self->dataPort);
|
|
bdm_delay ((self->delayTimer >> 1) + 1);
|
|
}
|
|
if (holdback == 0) {
|
|
outb (CPU32_ICD_RST_OUT | CPU32_ICD_STEP_OUT | CPU32_ICD_DSCLK,
|
|
self->dataPort);
|
|
bdm_delay (self->delayTimer + 1);
|
|
}
|
|
outb (CPU32_ICD_RST_OUT | CPU32_ICD_STEP_OUT | CPU32_ICD_DSCLK,
|
|
self->dataPort);
|
|
self->readValue = shiftRegister & 0x1FFFF;
|
|
if (self->debugFlag)
|
|
PRINTF (" cpu32_icd_serial_clock -- send 0x%05x, receive 0x%05x\n",
|
|
wval, self->readValue);
|
|
if (self->readValue & 0x10000) {
|
|
if (self->readValue == 0x10001)
|
|
return BDM_FAULT_BERR;
|
|
else if (self->readValue != 0x10000)
|
|
return BDM_FAULT_NVC;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Force the target into background debugging mode
|
|
*/
|
|
static int
|
|
cpu32_icd_stop_chip (struct BDM *self)
|
|
{
|
|
int check;
|
|
int pass;
|
|
|
|
if (self->debugFlag)
|
|
PRINTF (" cpu32_icd_stop_chip: ");
|
|
/* if FREEZE is already high, we're stopped and we're done here */
|
|
if (inb (self->statusPort) & CPU32_ICD_FREEZE) {
|
|
if (self->debugFlag)
|
|
PRINTF ("already stopped\n");
|
|
return 0;
|
|
}
|
|
|
|
/* try multiple times... */
|
|
for (pass = 0; pass < 14; pass++) {
|
|
|
|
/* even times, simply assert DSCLK and RESET */
|
|
if (pass%2 == 0) {
|
|
outb (CPU32_ICD_DSCLK | CPU32_ICD_RST_OUT, self->dataPort);
|
|
}
|
|
/* odd times, yank BERR as well, in case the target is wedged */
|
|
else {
|
|
outb (CPU32_ICD_DSCLK | CPU32_ICD_RST_OUT | CPU32_ICD_FORCE_BERR,
|
|
self->dataPort);
|
|
}
|
|
|
|
/* now hang around and wait for the freeze line to come up
|
|
* XXX we're depending on a nop loop for timing? arrrgh!
|
|
*/
|
|
for (check = 0 ; check < (1000 + ((pass+1)%2) * 9000) ; check++) {
|
|
if (inb (self->statusPort) & CPU32_ICD_FREEZE) {
|
|
/* if freeze line is high we're OK
|
|
* XXX let reset go too?
|
|
*/
|
|
if (self->debugFlag)
|
|
PRINTF("stopped after %d bdm_delays\n", check);
|
|
outb (CPU32_ICD_RST_OUT, self->dataPort);
|
|
return 0;
|
|
}
|
|
bdm_delay (10);
|
|
}
|
|
|
|
}
|
|
/* we've failed... */
|
|
outb (CPU32_ICD_RST_OUT, self->dataPort);
|
|
if (self->debugFlag)
|
|
PRINTF("failed!\n");
|
|
return BDM_FAULT_RESPONSE;
|
|
}
|
|
|
|
/*
|
|
* Restart chip and stop on first instruction fetch
|
|
*/
|
|
static int
|
|
cpu32_icd_restart_chip (struct BDM *self)
|
|
{
|
|
if (self->debugFlag)
|
|
PRINTF (" cpu32_icd_restart_chip\n");
|
|
outb (CPU32_ICD_DSCLK, self->dataPort);
|
|
udelay (1);
|
|
return cpu32_icd_stop_chip (self);
|
|
}
|
|
|
|
/*
|
|
* Restart chip and disable background debugging mode
|
|
*/
|
|
static int
|
|
cpu32_icd_release_chip (struct BDM *self)
|
|
{
|
|
if (self->debugFlag)
|
|
PRINTF (" cpu32_icd_release_chip\n");
|
|
outb (CPU32_ICD_DSCLK | CPU32_ICD_STEP_OUT, self->dataPort);
|
|
udelay (10);
|
|
outb (CPU32_ICD_DSCLK | CPU32_ICD_RST_OUT | CPU32_ICD_STEP_OUT, self->dataPort);
|
|
udelay (10);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Restart chip, enable background debugging mode, halt on first fetch
|
|
*
|
|
* The software from the Motorola BBS tries to have the target
|
|
* chip begin execution, but that doesn't work very reliably.
|
|
* The RESETH* line rises rather slowly, so sometimes the BKPT* / DSCLK
|
|
* would be seen low, and sometimes it wouldn't.
|
|
*/
|
|
static int
|
|
cpu32_icd_reset_chip (struct BDM *self)
|
|
{
|
|
if (self->debugFlag)
|
|
PRINTF (" cpu32_icd_reset_chip\n");
|
|
|
|
/*
|
|
* Assert RESET*, BKPT*, and BREAK*
|
|
*/
|
|
outb (0, self->dataPort);
|
|
udelay (100);
|
|
|
|
/*
|
|
* Deassert RESET (CPU must see BKPT* asserted at rising edge of RESET*)
|
|
* Leaving BKPT* and BREAK* asserted gets us ready for first data txfer
|
|
* as per Figure 7-8 in CPU32RM/AD
|
|
*/
|
|
outb (CPU32_ICD_RST_OUT, self->dataPort);
|
|
udelay (100);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Make the target execute a single instruction and
|
|
* reenter background debugging mode
|
|
*/
|
|
static int
|
|
cpu32_icd_step_chip (struct BDM *self)
|
|
{
|
|
unsigned char dataBit;
|
|
int err;
|
|
|
|
if (self->debugFlag)
|
|
PRINTF (" cpu32_step_chip\n");
|
|
err = cpu32_serial_clock (self, BDM_GO_CMD, 1);
|
|
if (err)
|
|
return err;
|
|
|
|
/*
|
|
* Send the last bit of the command
|
|
*/
|
|
dataBit = (BDM_GO_CMD & 0x1) ? CPU32_ICD_DSI : 0;
|
|
outb (dataBit | CPU32_ICD_OE | CPU32_ICD_STEP_OUT | CPU32_ICD_RST_OUT,
|
|
self->dataPort);
|
|
bdm_delay (self->delayTimer + 1);
|
|
outb (dataBit | CPU32_ICD_OE | CPU32_ICD_RST_OUT, self->dataPort);
|
|
bdm_delay (1);
|
|
/* Raise CPU32_ICD_DSCLK before dropping CPU32_ICD_OEA */
|
|
outb (CPU32_ICD_DSCLK | CPU32_ICD_OE | CPU32_ICD_RST_OUT, self->dataPort);
|
|
bdm_delay (1);
|
|
outb (CPU32_ICD_DSCLK | CPU32_ICD_RST_OUT, self->dataPort);
|
|
|
|
return cpu32_icd_stop_chip (self);
|
|
}
|
|
|
|
/*
|
|
* Read system register
|
|
*/
|
|
|
|
static int
|
|
cpu32_read_sysreg (struct BDM *self, struct BDMioctl *ioc, int mode)
|
|
{
|
|
int err, cmd;
|
|
unsigned short msw, lsw;
|
|
|
|
/*
|
|
* CPU32 MBAR require sfc support, make it look like
|
|
* a register.
|
|
*/
|
|
if (ioc->address == BDM_REG_MBAR) {
|
|
struct BDMioctl mbar_ioc;
|
|
unsigned long sfc;
|
|
|
|
mbar_ioc.address = BDM_REG_SFC;
|
|
if ((err = cpu32_read_sysreg (self, &mbar_ioc,
|
|
BDM_SYS_REG_MODE_MAPPED)) < 0)
|
|
return err;
|
|
sfc = mbar_ioc.value;
|
|
mbar_ioc.address = BDM_REG_SFC;
|
|
mbar_ioc.value = 7;
|
|
if ((err = cpu32_write_sysreg (self, &mbar_ioc,
|
|
BDM_SYS_REG_MODE_MAPPED)) < 0)
|
|
return err;
|
|
mbar_ioc.address = 0x3FF00;
|
|
if ((err = bdmDrvReadLongWord (self, &mbar_ioc)) < 0)
|
|
return err;
|
|
ioc->value = mbar_ioc.value;
|
|
mbar_ioc.address = BDM_REG_SFC;
|
|
mbar_ioc.value = sfc;
|
|
if ((err = cpu32_write_sysreg (self, &mbar_ioc,
|
|
BDM_SYS_REG_MODE_MAPPED)) < 0)
|
|
return err;
|
|
return 0;
|
|
}
|
|
|
|
if (ioc->address > BDM_REG_VBR)
|
|
return BDM_FAULT_NVC;
|
|
|
|
if (mode != BDM_SYS_REG_MODE_MAPPED)
|
|
cmd = ioc->address & 0xffff;
|
|
else
|
|
cmd = cpu32_sysreg_map[ioc->address];
|
|
|
|
if (cmd == -1) {
|
|
ioc->value = 0;
|
|
if (self->debugFlag)
|
|
PRINTF (" cpu32_read_sysreg - Reg(%d):0x%x is not mapped; ignored\n",
|
|
mode, ioc->address);
|
|
return 0;
|
|
}
|
|
|
|
cmd |= BDM_RSREG_CMD;
|
|
|
|
if (((err = cpu32_serial_clock (self, cmd, 0)) != 0) ||
|
|
((err = bdmBitBashFetchWord (self, &msw)) != 0) ||
|
|
((err = bdmBitBashFetchWord (self, &lsw)) != 0))
|
|
return err;
|
|
ioc->value = (msw << 16) | lsw;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Write system register
|
|
*/
|
|
static int
|
|
cpu32_write_sysreg (struct BDM *self, struct BDMioctl *ioc, int mode)
|
|
{
|
|
int err, cmd;
|
|
|
|
/*
|
|
* CPU32 MBAR require dfc support, make it look like
|
|
* a register.
|
|
*/
|
|
if (ioc->address == BDM_REG_MBAR) {
|
|
struct BDMioctl mbar_ioc;
|
|
unsigned long dfc;
|
|
|
|
mbar_ioc.address = BDM_REG_DFC;
|
|
if ((err = cpu32_read_sysreg (self, &mbar_ioc,
|
|
BDM_SYS_REG_MODE_MAPPED)) < 0)
|
|
return err;
|
|
dfc = mbar_ioc.value;
|
|
mbar_ioc.address = BDM_REG_DFC;
|
|
mbar_ioc.value = 7;
|
|
if ((err = cpu32_write_sysreg (self, &mbar_ioc,
|
|
BDM_SYS_REG_MODE_MAPPED)) < 0)
|
|
return err;
|
|
mbar_ioc.address = 0x3FF00;
|
|
mbar_ioc.value = ioc->value;
|
|
if ((err = bdmDrvWriteLongWord (self, &mbar_ioc)) < 0)
|
|
return err;
|
|
mbar_ioc.address = BDM_REG_DFC;
|
|
mbar_ioc.value = dfc;
|
|
if ((err = cpu32_write_sysreg (self, &mbar_ioc,
|
|
BDM_SYS_REG_MODE_MAPPED)) < 0)
|
|
return err;
|
|
return 0;
|
|
}
|
|
|
|
if (ioc->address > BDM_REG_VBR)
|
|
return BDM_FAULT_NVC;
|
|
|
|
if (mode != BDM_SYS_REG_MODE_MAPPED)
|
|
cmd = ioc->address & 0xffff;
|
|
else
|
|
cmd = cpu32_sysreg_map[ioc->address];
|
|
|
|
cmd = BDM_WSREG_CMD;
|
|
|
|
if (((err = cpu32_serial_clock (self, cmd, 0)) != 0) ||
|
|
((err = cpu32_serial_clock (self, ioc->value >> 16, 0)) != 0) ||
|
|
((err = cpu32_serial_clock (self, ioc->value, 0)) != 0))
|
|
return err;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Generate a bus error for the ICD interface
|
|
*/
|
|
|
|
static int
|
|
cpu32_icd_gen_bus_error (struct BDM *self)
|
|
{
|
|
if (self->debugFlag)
|
|
PRINTF(" cpu32_icd_gen_bus_error\n");
|
|
|
|
outb (CPU32_ICD_FORCE_BERR | CPU32_ICD_RST_OUT, self->dataPort);
|
|
udelay (400);
|
|
outb (CPU32_ICD_RST_OUT, self->dataPort);
|
|
|
|
return BDM_FAULT_BERR;
|
|
}
|
|
|
|
/*
|
|
* Generate a bus error as the access has failed. This is
|
|
* not supported on the CPU32 with PD interface.
|
|
* (the 7-chip PD interface generates it automatically in hardware
|
|
*/
|
|
static int
|
|
cpu32_gen_bus_error (struct BDM *self)
|
|
{
|
|
if (self->debugFlag > 1)
|
|
PRINTF(" cpu32_gen_bus_error\n");
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Restart target execution
|
|
*/
|
|
static int
|
|
cpu32_run_chip (struct BDM *self)
|
|
{
|
|
return cpu32_serial_clock (self, BDM_GO_CMD, 0);
|
|
}
|
|
|
|
#ifdef BDM_BIT_BASH_PORT
|
|
|
|
/*
|
|
* Bit Bash the BDM port. No status checks. I assume you know what is happening at
|
|
* a low level with the BDM hardware if you are using this interface.
|
|
*/
|
|
static int
|
|
cpu32_bit_bash (struct BDM *self, unsigned short mask, unsigned short bits)
|
|
{
|
|
unsigned char ctrl_port = 0;
|
|
|
|
if (self->debugFlag)
|
|
PRINTF (" cpu32_bit_bash: mask=%04x, bits=%04x\n", mask, bits);
|
|
|
|
self->bit_bash_bits &= ~mask;
|
|
self->bit_bash_bits |= bits;
|
|
|
|
if (self->bit_bash_bits & BDM_BB_RESET)
|
|
ctrl_port |= CPU32_CR_FORCE_RESET;
|
|
|
|
if ((self->bit_bash_bits & BDM_BB_BKPT) == 0)
|
|
ctrl_port |= CPU32_CR_CLOCKBAR_BKPT;
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
/*
|
|
* Initialise the BDM structure for a CPU32
|
|
*/
|
|
static int
|
|
cpu32_pd_init_self (struct BDM *self)
|
|
{
|
|
int reg;
|
|
|
|
self->processor = BDM_CPU32;
|
|
self->interface = BDM_CPU32_ERIC;
|
|
|
|
self->get_status = cpu32_pd_get_status;
|
|
self->init_hardware = cpu32_pd_init_hardware;
|
|
self->serial_clock = cpu32_pd_serial_clock;
|
|
self->gen_bus_error = cpu32_gen_bus_error;
|
|
self->read_sysreg = cpu32_read_sysreg;
|
|
self->write_sysreg = cpu32_write_sysreg;
|
|
self->restart_chip = cpu32_pd_restart_chip;
|
|
self->release_chip = cpu32_pd_release_chip;
|
|
self->reset_chip = cpu32_pd_reset_chip;
|
|
self->stop_chip = cpu32_pd_stop_chip;
|
|
self->run_chip = cpu32_run_chip;
|
|
self->step_chip = cpu32_pd_step_chip;
|
|
|
|
#ifdef BDM_BIT_BASH_PORT
|
|
self->bit_bash = cpu32_bit_bash;
|
|
self->bit_bash_bits = 0;
|
|
#endif
|
|
|
|
for (reg = 0; reg < BDM_MAX_SYSREG; reg++)
|
|
self->shadow_sysreg[reg] = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
cpu32_icd_init_self (struct BDM *self)
|
|
{
|
|
int reg;
|
|
|
|
self->processor = BDM_CPU32;
|
|
self->interface = BDM_CPU32_ICD;
|
|
|
|
self->get_status = cpu32_icd_get_status;
|
|
self->init_hardware = cpu32_icd_init_hardware;
|
|
self->serial_clock = cpu32_icd_serial_clock;
|
|
self->gen_bus_error = cpu32_icd_gen_bus_error;
|
|
self->restart_chip = cpu32_icd_restart_chip;
|
|
self->release_chip = cpu32_icd_release_chip;
|
|
self->reset_chip = cpu32_icd_reset_chip;
|
|
self->stop_chip = cpu32_icd_stop_chip;
|
|
self->run_chip = cpu32_run_chip;
|
|
self->step_chip = cpu32_icd_step_chip;
|
|
self->fill_buf = bdmBitBashFillBuf;
|
|
self->send_buf = bdmBitBashSendBuf;
|
|
self->read_sysreg = cpu32_read_sysreg;
|
|
self->read_proreg = bdmBitBashReadProcessorRegister;
|
|
self->read_long_word = bdmBitBashReadLongWord;
|
|
self->read_word = bdmBitBashReadWord;
|
|
self->read_byte = bdmBitBashReadByte;
|
|
self->write_sysreg = cpu32_write_sysreg;
|
|
self->write_proreg = bdmBitBashWriteProcessorRegister;
|
|
self->write_long_word = bdmBitBashWriteLongWord;
|
|
self->write_word = bdmBitBashWriteWord;
|
|
self->write_byte = bdmBitBashWriteByte;
|
|
|
|
#ifdef BDM_BIT_BASH_PORT
|
|
self->bit_bash = cpu32_bit_bash;
|
|
self->bit_bash_bits = 0;
|
|
#endif
|
|
|
|
for (reg = 0; reg < BDM_MAX_SYSREG; reg++)
|
|
self->shadow_sysreg[reg] = 0;
|
|
|
|
return 0;
|
|
}
|
|
|