1101 lines
26 KiB
C
1101 lines
26 KiB
C
/*
|
|
* Motorola Background Debug Mode Driver
|
|
* Copyright (C) 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.
|
|
*
|
|
* Coldfire TBLCF USB support by:
|
|
* Chris Johns
|
|
* cjohns@users.sourceforge.net
|
|
*/
|
|
|
|
#include <tblcf.h>
|
|
|
|
static int tblcf_read_sysreg (struct BDM *self,
|
|
struct BDMioctl *ioc, int mode);
|
|
static int tblcf_write_sysreg (struct BDM *self,
|
|
struct BDMioctl *ioc, int mode);
|
|
static int tblcf_reset_chip (struct BDM *self);
|
|
static int tblcf_stop_chip (struct BDM *self);
|
|
|
|
/*
|
|
* Invalidate the cache.
|
|
*/
|
|
|
|
static int
|
|
tblcf_invalidate_cache (struct BDM *self)
|
|
{
|
|
struct BDMioctl cacr_ioc;
|
|
|
|
if (self->debugFlag > 1)
|
|
PRINTF (" tblcf_invalidate_cache\n");
|
|
|
|
cacr_ioc.address = BDM_REG_CACR;
|
|
|
|
if (tblcf_read_sysreg (self, &cacr_ioc, BDM_SYS_REG_MODE_MAPPED) < 0)
|
|
return BDM_TARGETNC;
|
|
|
|
/*
|
|
* Set the invalidate bit.
|
|
*/
|
|
|
|
if (cacr_ioc.value) {
|
|
if (self->cf_debug_ver == CF_REVISION_D)
|
|
cacr_ioc.value |= 0x01040100;
|
|
else
|
|
cacr_ioc.value |= 0x01000100;
|
|
|
|
if (self->debugFlag > 2)
|
|
PRINTF (" tblcf_invalidate_cache -- cacr:0x%08x\n", (int) cacr_ioc.value);
|
|
|
|
if (tblcf_write_sysreg (self, &cacr_ioc, BDM_SYS_REG_MODE_MAPPED) < 0)
|
|
return BDM_TARGETNC;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* PC read check. This is used to see if the processor has halted.
|
|
*/
|
|
|
|
static int
|
|
tblcf_pc_read_check (struct BDM *self)
|
|
{
|
|
struct BDMioctl pc_ioc;
|
|
|
|
pc_ioc.address = BDM_REG_RPC;
|
|
|
|
if (tblcf_read_sysreg (self, &pc_ioc, BDM_SYS_REG_MODE_MAPPED) < 0)
|
|
return BDM_TARGETNC;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Get target status
|
|
*/
|
|
|
|
static int
|
|
tblcf_get_status (struct BDM *self)
|
|
{
|
|
int cf_last_running = self->cf_running;
|
|
bdmcf_status_t bdmcf_status;
|
|
int status = 0;
|
|
unsigned long csr;
|
|
int ret;
|
|
|
|
/*
|
|
* TBLCF does not report target power.
|
|
*/
|
|
|
|
if (tblcf_bdm_sts (self->usbDev, &bdmcf_status))
|
|
status = BDM_TARGETNC;
|
|
else {
|
|
if (bdmcf_status.reset_detection == RESET_DETECTED)
|
|
status |= BDM_TARGETRESET;
|
|
}
|
|
|
|
if (status & (BDM_TARGETRESET | BDM_TARGETNC | BDM_TARGETPOWER)) {
|
|
PRINTF (" tblcf_get_status -- Status:0x%x\n", status);
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* The actual hard work is done when reading the system register. Check this
|
|
* function for the issues in reading the CSR register.
|
|
*/
|
|
if (self->cf_running) {
|
|
struct BDMioctl csr_ioc;
|
|
|
|
csr_ioc.address = BDM_REG_CSR;
|
|
|
|
if (tblcf_read_sysreg (self, &csr_ioc, BDM_SYS_REG_MODE_MAPPED) < 0)
|
|
return BDM_TARGETNC;
|
|
|
|
csr = csr_ioc.value;
|
|
} else
|
|
csr = self->cf_csr;
|
|
|
|
ret = ((csr & 0x0e000000 ? BDM_TARGETHALT : 0) |
|
|
(csr & 0x0f000000 ? BDM_TARGETSTOPPED : 0));
|
|
|
|
/*
|
|
* If we were running, and we have detected we have stopped
|
|
* flush the cache.
|
|
*/
|
|
if (cf_last_running && !self->cf_running)
|
|
tblcf_invalidate_cache (self);
|
|
|
|
if (self->debugFlag > 2)
|
|
PRINTF (" tblcf_get_status -- Status:0x%x, csr:0x%08x, cf_csr:0x%08x\n",
|
|
ret, (int) csr, (int) self->cf_csr);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Hardware initialization. The open.
|
|
*/
|
|
static int
|
|
tblcf_init_hardware (struct BDM *self)
|
|
{
|
|
int status;
|
|
|
|
/*
|
|
* Swap the swap state as the USB pod will swap.
|
|
*
|
|
* The swap of the swap will work on Intel devices but I am
|
|
* not sure of other platforms.
|
|
*/
|
|
mustSwap = mustSwap ? 0 : 1;
|
|
|
|
/*
|
|
* We do not know which version of debug module we have so
|
|
* default to 0 and do not use PST signals. This will cause the
|
|
* CSR register to be read.
|
|
*/
|
|
|
|
self->cf_running = 1;
|
|
self->cf_debug_ver = 0;
|
|
self->cf_use_pst = 0;
|
|
self->address = 0;
|
|
|
|
tblcf_reset_chip (self);
|
|
|
|
status = tblcf_get_status (self);
|
|
|
|
if (self->debugFlag)
|
|
PRINTF (" tblcf_init_hardware: status:%d\n", status);
|
|
|
|
/*
|
|
* Process the result of the CSR read.
|
|
*/
|
|
|
|
self->cf_debug_ver = (self->cf_csr >> 20) & 0x0f;
|
|
|
|
if (self->debugFlag)
|
|
PRINTF (" tblcf_init_hardware: debug version is %d\n", self->cf_debug_ver);
|
|
|
|
return status;
|
|
}
|
|
|
|
static int
|
|
tblcf_serial_clock (struct BDM *self, unsigned short wval, int holdback)
|
|
{
|
|
PRINTF (" tblcf_serial_clock -- should never be called\n");
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Read system register
|
|
*/
|
|
|
|
static int
|
|
tblcf_read_sysreg (struct BDM *self, struct BDMioctl *ioc, int mode)
|
|
{
|
|
int cmd;
|
|
|
|
if (self->debugFlag > 2)
|
|
PRINTF (" tblcf_read_sysreg + Reg(%d):0x%x\n", mode, ioc->address);
|
|
|
|
if ((mode == BDM_SYS_REG_MODE_MAPPED) && (ioc->address >= BDM_MAX_SYSREG))
|
|
return BDM_FAULT_NVC;
|
|
|
|
/*
|
|
* For the Coldfire we need to select the type of command
|
|
* to use.
|
|
*/
|
|
if ((mode == BDM_SYS_REG_MODE_CONTROL) ||
|
|
((mode == BDM_SYS_REG_MODE_MAPPED) && (ioc->address < BDM_REG_CSR))) {
|
|
if (mode == BDM_SYS_REG_MODE_CONTROL)
|
|
cmd = ioc->address & 0xffff;
|
|
else
|
|
cmd = cf_sysreg_map[ioc->address];
|
|
|
|
if (cmd == -1) {
|
|
ioc->value = 0;
|
|
if (self->debugFlag)
|
|
PRINTF (" tblcf_read_sysreg - Reg(%d):0x%x is not mapped; ignored\n",
|
|
mode, ioc->address);
|
|
return 0;
|
|
}
|
|
|
|
if (tblcf_read_creg (self->usbDev, cmd, &ioc->value)) {
|
|
PRINTF (" tblcf_read_sysreg - Reg:0x%x failed with cmd 0x%02x\n",
|
|
ioc->address, cmd);
|
|
return BDM_FAULT_RESPONSE;
|
|
}
|
|
}
|
|
else {
|
|
if (mode == BDM_SYS_REG_MODE_DEBUG) {
|
|
int r;
|
|
cmd = ioc->address & 0xffff;
|
|
/*
|
|
* See if the register is actually mapped and therefore write only.
|
|
*/
|
|
for (r = BDM_REG_CSR; r < BDM_MAX_SYSREG; r++) {
|
|
if (cf_sysreg_map[r] == (ioc->address & 0xffff)) {
|
|
mode = BDM_SYS_REG_MODE_MAPPED;
|
|
ioc->address = r;
|
|
if (self->debugFlag > 2)
|
|
PRINTF (" tblcf_read_sysreg - remapped to Reg:0x%x\n", ioc->address);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
cmd = cf_sysreg_map[ioc->address];
|
|
|
|
if (cmd == -1) {
|
|
ioc->value = 0;
|
|
if (self->debugFlag)
|
|
PRINTF (" tblcf_read_sysreg - Reg(%d):0x%x is not mapped; ignored\n",
|
|
mode, ioc->address);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Are the registers write only ?
|
|
*/
|
|
if ((mode == BDM_SYS_REG_MODE_MAPPED) && (ioc->address != BDM_REG_CSR)) {
|
|
ioc->value = self->shadow_sysreg[ioc->address];
|
|
if (self->debugFlag > 1)
|
|
PRINTF (" tblcf_read_sysreg - Reg:0x%x is write only, 0x%08x\n",
|
|
ioc->address, ioc->value);
|
|
return 0;
|
|
}
|
|
|
|
if (tblcf_read_dreg (self->usbDev, cmd, &ioc->value)) {
|
|
PRINTF (" tblcf_read_sysreg - Reg:0x%x failed with cmd 0x%02x\n",
|
|
ioc->address, cmd);
|
|
return BDM_FAULT_RESPONSE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Watch out here as this function is called from tblcf_get_status.
|
|
*/
|
|
|
|
if (ioc->address == BDM_REG_CSR) {
|
|
if (self->cf_running) {
|
|
if (ioc->value & 0x0f000000) {
|
|
|
|
/*
|
|
* We could false trigger for some reason such as bit
|
|
* errors on the serial stream. To check if we have
|
|
* actually halted we should read the program counter
|
|
* register. If we can then we have halted, else
|
|
* we can only assume we are still running.
|
|
*
|
|
* We check the PC read twice to insure we are ok.
|
|
*
|
|
* This solution is provided by Motorola, more specifically
|
|
* Joe Circello, the chief ColdFire architect, and Sue Cozart
|
|
* for providing access to Joe. Thanks. (CCJ 15-05-2000)
|
|
*/
|
|
|
|
if ((tblcf_pc_read_check (self) == 0) &&
|
|
(tblcf_pc_read_check (self) == 0)) {
|
|
self->cf_csr = ioc->value;
|
|
self->cf_running = 0;
|
|
|
|
/*
|
|
* If we were running, and we have detected we have stopped
|
|
* flush the cache. This should probably be somewhere else
|
|
* if we are using PST, I am not sure yet : davidm
|
|
*/
|
|
|
|
tblcf_invalidate_cache (self);
|
|
}
|
|
else {
|
|
/*
|
|
* We have not halted as the PC is not accessable.
|
|
*/
|
|
ioc->value &= ~0x0f000000;
|
|
}
|
|
}
|
|
} else {
|
|
self->cf_csr = ioc->value = (self->cf_csr & 0x0f000000) | ioc->value;
|
|
}
|
|
}
|
|
|
|
if (ioc->address == BDM_REG_SR)
|
|
ioc->value &= 0xffff;
|
|
|
|
if (self->debugFlag > 1)
|
|
PRINTF (" tblcf_read_sysreg - Reg(%d):0x%x is 0x%08x\n",
|
|
mode, ioc->address, ioc->value);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Write system register
|
|
*/
|
|
static int
|
|
tblcf_write_sysreg (struct BDM *self, struct BDMioctl *ioc, int mode)
|
|
{
|
|
int cmd;
|
|
|
|
if ((mode == BDM_SYS_REG_MODE_MAPPED) && (ioc->address >= BDM_MAX_SYSREG))
|
|
return BDM_FAULT_NVC;
|
|
|
|
if (self->debugFlag)
|
|
PRINTF (" tblcf_write_sysreg - Reg(%d):0x%x is 0x%x\n",
|
|
mode, ioc->address, ioc->value);
|
|
|
|
/*
|
|
* For the Coldfire we need to select the type of command
|
|
* to use.
|
|
*/
|
|
if ((mode == BDM_SYS_REG_MODE_CONTROL) ||
|
|
((mode == BDM_SYS_REG_MODE_MAPPED) && (ioc->address < BDM_REG_CSR))) {
|
|
if (mode == BDM_SYS_REG_MODE_CONTROL)
|
|
cmd = ioc->address & 0xffff;
|
|
else
|
|
cmd = cf_sysreg_map[ioc->address];
|
|
|
|
if (cmd == -1) {
|
|
if (self->debugFlag)
|
|
PRINTF (" tblcf_write_sysreg - Reg(%d):0x%x is not mapped; ignored\n",
|
|
mode, ioc->address);
|
|
return 0;
|
|
}
|
|
|
|
tblcf_write_creg (self->usbDev, cmd, ioc->value);
|
|
}
|
|
else {
|
|
if (mode == BDM_SYS_REG_MODE_DEBUG)
|
|
cmd = ioc->address & 0xffff;
|
|
else
|
|
cmd = cf_sysreg_map[ioc->address];
|
|
|
|
if (cmd == -1) {
|
|
if (self->debugFlag)
|
|
PRINTF (" tblcf_write_sysreg - Reg(%d):0x%x is not mapped; ignored\n",
|
|
mode, ioc->address);
|
|
return 0;
|
|
}
|
|
|
|
if (mode == BDM_SYS_REG_MODE_MAPPED)
|
|
self->shadow_sysreg[ioc->address] = ioc->value;
|
|
|
|
tblcf_write_dreg (self->usbDev, cmd, ioc->value);
|
|
}
|
|
|
|
if (tblcf_get_last_sts (self->usbDev)) {
|
|
if (self->debugFlag)
|
|
PRINTF (" tblcf_write_sysreg - Reg(%d):0x%x write failed\n",
|
|
mode, ioc->address);
|
|
return BDM_FAULT_RESPONSE;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Generate a bus error as the access has failed. This is
|
|
* not supported on the CPU32.
|
|
*/
|
|
|
|
static int
|
|
tblcf_gen_bus_error (struct BDM *self)
|
|
{
|
|
if (self->debugFlag)
|
|
PRINTF (" tblcf_gen_bus_error\n");
|
|
|
|
tblcf_assert_ta (self->usbDev, 100);
|
|
|
|
return BDM_FAULT_FORCED_TA;
|
|
}
|
|
|
|
/*
|
|
* Restart chip and stop on first instruction fetch. Easy for the
|
|
* Coldfire, keep BKPT asserted during the first 16 clocks after
|
|
* reset is negated.
|
|
*/
|
|
static int
|
|
tblcf_restart_chip (struct BDM *self)
|
|
{
|
|
if (self->debugFlag)
|
|
PRINTF (" tblcf_restart_chip\n");
|
|
|
|
tblcf_reset_chip (self);
|
|
|
|
self->cf_running = 1;
|
|
|
|
if (tblcf_get_status (self) & (BDM_TARGETHALT | BDM_TARGETSTOPPED))
|
|
return 0;
|
|
|
|
return BDM_FAULT_RESPONSE;
|
|
}
|
|
|
|
/*
|
|
* Restart chip and disable background debugging mode. On
|
|
* a Coldfire exit reset without the BKPT being asserted.
|
|
*/
|
|
static int
|
|
tblcf_release_chip (struct BDM *self)
|
|
{
|
|
if (self->debugFlag)
|
|
PRINTF (" tblcf_release_chip\n");
|
|
|
|
self->cf_running = 1;
|
|
|
|
tblcf_target_go (self->usbDev);
|
|
tblcf_close(self->usbDev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Restart chip, the coldfire is easier than the CPU32 to get into
|
|
* BMD mode after reset. Just have the BKPT signal active when
|
|
* reset is negated.
|
|
*/
|
|
static int
|
|
tblcf_reset_chip (struct BDM *self)
|
|
{
|
|
if (self->debugFlag)
|
|
PRINTF (" tblcf_reset_chip\n");
|
|
|
|
self->cf_running = 1;
|
|
|
|
if (tblcf_target_reset (self->usbDev, BDM_MODE)) {
|
|
if (self->debugFlag)
|
|
PRINTF (" tblcf_reset_chip: error\n");
|
|
return BDM_FAULT_RESPONSE;
|
|
}
|
|
|
|
self->address = 0;
|
|
self->cf_running = 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Force the target into background debugging mode
|
|
*/
|
|
static int
|
|
tblcf_stop_chip (struct BDM *self)
|
|
{
|
|
int status, retries = 4;
|
|
|
|
if (self->debugFlag)
|
|
PRINTF (" tblcf_stop_chip\n");
|
|
|
|
while (retries--) {
|
|
if (tblcf_target_halt (self->usbDev)) {
|
|
if (self->debugFlag)
|
|
PRINTF (" tblcf_stop_chip: usb error\n");
|
|
}
|
|
|
|
bdm_sleep (10);
|
|
status = tblcf_get_status (self);
|
|
|
|
if (status & (BDM_TARGETHALT | BDM_TARGETSTOPPED)) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return BDM_FAULT_RESPONSE;
|
|
}
|
|
|
|
/*
|
|
* Restart target execution.
|
|
*/
|
|
static int
|
|
tblcf_run_chip (struct BDM *self)
|
|
{
|
|
struct BDMioctl sreg_ioc;
|
|
struct BDMioctl pc_ioc;
|
|
int err;
|
|
|
|
if (self->debugFlag)
|
|
PRINTF (" tblcf_run_chip\n");
|
|
|
|
/*
|
|
* Flush the cache to insure all changed data is read by the
|
|
* processor.
|
|
*/
|
|
tblcf_invalidate_cache (self);
|
|
|
|
/*
|
|
* Change the CSR:4 or the SSM bit to off then issue a go.
|
|
*/
|
|
sreg_ioc.address = BDM_REG_CSR;
|
|
sreg_ioc.value = 0x00000000;
|
|
if ((err = tblcf_write_sysreg (self, &sreg_ioc, BDM_SYS_REG_MODE_MAPPED)) < 0)
|
|
return err;
|
|
|
|
/*
|
|
* Revision D hardware does not have a bit to mask interrupts so
|
|
* we save the SR if we step and need to restore it now.
|
|
*
|
|
* The SR needs to be read back and the mask bits set as the
|
|
* other SR bits may have changed.
|
|
*
|
|
* It appears that the V4 core has some pipelining in effect that
|
|
* causes instruction fetches to already be cached. This could be
|
|
* problem in certain instances for stepping. To resolve this,
|
|
* we read the PC value, and then write it right back out.
|
|
*/
|
|
|
|
if ((self->cf_debug_ver == CF_REVISION_D) && self->cf_sr_masked) {
|
|
sreg_ioc.address = BDM_REG_SR;
|
|
if ((err = tblcf_read_sysreg (self, &sreg_ioc,
|
|
BDM_SYS_REG_MODE_MAPPED)) < 0)
|
|
return err;
|
|
sreg_ioc.value &= ~0x0700;
|
|
sreg_ioc.value |= self->cf_sr_mask_cache;
|
|
if ((err = tblcf_write_sysreg (self, &sreg_ioc,
|
|
BDM_SYS_REG_MODE_MAPPED)) < 0)
|
|
return err;
|
|
self->cf_sr_masked = 0;
|
|
|
|
/*
|
|
* Read the PC value and write it back out
|
|
*/
|
|
pc_ioc.address = BDM_REG_RPC;
|
|
if ((err = tblcf_read_sysreg (self, &pc_ioc,
|
|
BDM_SYS_REG_MODE_MAPPED)) < 0)
|
|
return err;
|
|
if ((err = tblcf_write_sysreg (self, &pc_ioc,
|
|
BDM_SYS_REG_MODE_MAPPED)) < 0)
|
|
return err;
|
|
}
|
|
|
|
/*
|
|
* Now issue a GO command.
|
|
*/
|
|
if (tblcf_target_go (self->usbDev)) {
|
|
PRINTF (" tblcf_run_chip - GO cmd failed, err = %d\n", err);
|
|
return BDM_FAULT_RESPONSE;
|
|
}
|
|
|
|
self->cf_running = 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Make the target execute a single instruction and
|
|
* reenter background debugging mode
|
|
*/
|
|
static int
|
|
tblcf_step_chip (struct BDM *self)
|
|
{
|
|
struct BDMioctl sreg_ioc;
|
|
struct BDMioctl csr_ioc;
|
|
struct BDMioctl pc_ioc;
|
|
int err;
|
|
|
|
if (self->debugFlag)
|
|
PRINTF (" tblcf_step_chip\n");
|
|
|
|
/*
|
|
* Flush the cache to insure all changed data is read by the
|
|
* processor.
|
|
*/
|
|
tblcf_invalidate_cache (self);
|
|
|
|
/*
|
|
* Change the CSR:5 or the IPI bit to on. The SSM bit is handled
|
|
* by the pod.
|
|
*/
|
|
sreg_ioc.address = BDM_REG_CSR;
|
|
sreg_ioc.value = 1 << 5;
|
|
if ((err = tblcf_write_sysreg (self, &sreg_ioc, BDM_SYS_REG_MODE_MAPPED)) < 0)
|
|
return err;
|
|
|
|
/*
|
|
* Revision D hardware does not have a bit to mask interrupts so
|
|
* we save the SR when we step and then restore when we go
|
|
*
|
|
* The SR needs to be read back and the mask bits set as the
|
|
* other SR bits may have changed.
|
|
*
|
|
* It appears that the V4 core has some pipelining in effect that
|
|
* causes instruction fetches to already be cached. This could be
|
|
* problem in certain instances for stepping. To resolve this,
|
|
* we read the PC value, and then write it right back out.
|
|
*/
|
|
if ((self->cf_debug_ver == CF_REVISION_D) && !self->cf_sr_masked) {
|
|
sreg_ioc.address = BDM_REG_SR;
|
|
if ((err = tblcf_read_sysreg (self, &sreg_ioc,
|
|
BDM_SYS_REG_MODE_MAPPED)) < 0)
|
|
return err;
|
|
self->cf_sr_mask_cache = sreg_ioc.value & 0x0700;
|
|
sreg_ioc.value |= 0x0700;
|
|
if ((err = tblcf_write_sysreg (self, &sreg_ioc,
|
|
BDM_SYS_REG_MODE_MAPPED)) < 0)
|
|
return err;
|
|
self->cf_sr_masked = 1;
|
|
|
|
/*
|
|
* Read the PC value and write it back out
|
|
*/
|
|
pc_ioc.address = BDM_REG_RPC;
|
|
if ((err=tblcf_read_sysreg (self, &pc_ioc,
|
|
BDM_SYS_REG_MODE_MAPPED)) < 0)
|
|
return err;
|
|
if ((err=tblcf_write_sysreg (self, &pc_ioc,
|
|
BDM_SYS_REG_MODE_MAPPED)) < 0)
|
|
return err;
|
|
}
|
|
|
|
/*
|
|
* Now issue a GO command.
|
|
*/
|
|
if (tblcf_target_step (self->usbDev)) {
|
|
PRINTF (" tblcf_step_chip - GO cmd failed\n");
|
|
return BDM_FAULT_RESPONSE;
|
|
}
|
|
|
|
self->cf_running = 0;
|
|
self->cf_csr = 0x01000000;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Fill I/O buffer with data from target
|
|
*/
|
|
int
|
|
tblcf_fill_buf (struct BDM *self, int count)
|
|
{
|
|
if (self->debugFlag)
|
|
PRINTF ("tblcf_fill_buf - count:%d\n", count);
|
|
|
|
if (count == 0)
|
|
return 0;
|
|
|
|
/*
|
|
* We need to swap the data if it has already been swapped.
|
|
*/
|
|
|
|
if (tblcf_read_block32 (self->usbDev, self->address, count, self->ioBuffer)) {
|
|
tblcf_gen_bus_error (self);
|
|
if (self->debugFlag)
|
|
PRINTF ("tblcf_fill_buf - failed\n");
|
|
return BDM_FAULT_RESPONSE;
|
|
}
|
|
|
|
self->address += count;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Send contents of I/O buffer to target
|
|
*/
|
|
int
|
|
tblcf_send_buf (struct BDM *self, int count)
|
|
{
|
|
if (self->debugFlag)
|
|
PRINTF ("tblcf_send_buf - count:%d\n", count);
|
|
|
|
if (count == 0)
|
|
return 0;
|
|
|
|
if (tblcf_write_block32 (self->usbDev, self->address, count, self->ioBuffer)) {
|
|
tblcf_gen_bus_error (self);
|
|
if (self->debugFlag)
|
|
PRINTF ("tblcf_send_buf - failed\n");
|
|
return BDM_FAULT_RESPONSE;
|
|
}
|
|
|
|
self->address += count;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Read processor register
|
|
*/
|
|
static int
|
|
tblcf_read_preg (struct BDM *self, struct BDMioctl *ioc)
|
|
{
|
|
if (tblcf_read_reg (self->usbDev, ioc->address & 0xF, &ioc->value)) {
|
|
tblcf_gen_bus_error (self);
|
|
if (self->debugFlag)
|
|
PRINTF ("tblcf_read_preg - reg:0x%02x, failed\n", ioc->address & 0xF);
|
|
return BDM_FAULT_RESPONSE;
|
|
}
|
|
|
|
if (self->debugFlag)
|
|
PRINTF ("tblcf_read_preg - reg:0x%02x = 0x%08x\n",
|
|
ioc->address & 0xF, ioc->value);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Read a long word from memory
|
|
*/
|
|
static int
|
|
tblcf_read_long_word (struct BDM *self, struct BDMioctl *ioc)
|
|
{
|
|
unsigned long int l;
|
|
|
|
if (tblcf_read_mem32 (self->usbDev, ioc->address, &l)) {
|
|
tblcf_gen_bus_error (self);
|
|
if (self->debugFlag)
|
|
PRINTF ("tblcf_read_long_word : *0x%08x failed\n", ioc->address);
|
|
return BDM_FAULT_RESPONSE;
|
|
}
|
|
|
|
ioc->value = l;
|
|
|
|
self->address = ioc->address + 4;
|
|
|
|
if (self->debugFlag)
|
|
PRINTF ("tblcf_read_long_word : *0x%08x = 0x%08x\n", ioc->address, ioc->value);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Read a word from memory
|
|
*/
|
|
static int
|
|
tblcf_read_word (struct BDM *self, struct BDMioctl *ioc)
|
|
{
|
|
unsigned int w;
|
|
|
|
if (tblcf_read_mem16 (self->usbDev, ioc->address, &w)) {
|
|
tblcf_gen_bus_error (self);
|
|
if (self->debugFlag)
|
|
PRINTF ("tblcf_read_word : *0x%08x failed\n", ioc->address);
|
|
return BDM_FAULT_RESPONSE;
|
|
}
|
|
|
|
ioc->value = w;
|
|
|
|
self->address = ioc->address + 2;
|
|
|
|
if (self->debugFlag)
|
|
PRINTF ("tblcf_read_word : *0x%08x = 0x%04x\n", ioc->address, ioc->value);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Read a byte from memory
|
|
*/
|
|
static int
|
|
tblcf_read_byte (struct BDM *self, struct BDMioctl *ioc)
|
|
{
|
|
unsigned char b;
|
|
|
|
if (tblcf_read_mem8 (self->usbDev, ioc->address, &b)) {
|
|
tblcf_gen_bus_error (self);
|
|
if (self->debugFlag)
|
|
PRINTF ("tblcf_read_byte : *0x%08x failed\n", ioc->address);
|
|
return BDM_FAULT_RESPONSE;
|
|
}
|
|
|
|
ioc->value = b;
|
|
|
|
self->address = ioc->address + 1;
|
|
|
|
if (self->debugFlag)
|
|
PRINTF ("tblcf_read_byte : *0x%08x = 0x%02x\n", ioc->address, ioc->value);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Write processor register
|
|
*/
|
|
static int
|
|
tblcf_write_preg (struct BDM *self, struct BDMioctl *ioc)
|
|
{
|
|
if (self->debugFlag)
|
|
PRINTF ("tblcf_write_preg - reg:%d, val:0x%08x\n",
|
|
ioc->address & 0xF, ioc->value);
|
|
|
|
tblcf_write_reg (self->usbDev, ioc->address & 0xF, ioc->value);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Write a long word to memory
|
|
*/
|
|
static int
|
|
tblcf_write_long_word (struct BDM *self, struct BDMioctl *ioc)
|
|
{
|
|
if (self->debugFlag)
|
|
PRINTF ("tblcf_write_long_word : 0x%08x = 0x%08x\n", ioc->address, ioc->value);
|
|
|
|
self->address = ioc->address + 4;
|
|
|
|
tblcf_write_mem32 (self->usbDev, ioc->address, ioc->value);
|
|
|
|
if (tblcf_get_last_sts (self->usbDev)) {
|
|
tblcf_gen_bus_error (self);
|
|
if (self->debugFlag)
|
|
PRINTF ("tblcf_write_long_word : *0x%08x failed\n", ioc->address);
|
|
return BDM_FAULT_RESPONSE;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Write a word to memory
|
|
*/
|
|
static int
|
|
tblcf_write_word (struct BDM *self, struct BDMioctl *ioc)
|
|
{
|
|
if (self->debugFlag)
|
|
PRINTF ("tblcf_write_word : 0x%08x = 0x%04x\n", ioc->address, (ioc->value & 0xffff));
|
|
|
|
self->address = ioc->address + 2;
|
|
|
|
tblcf_write_mem16 (self->usbDev, ioc->address, ioc->value);
|
|
|
|
if (tblcf_get_last_sts (self->usbDev)) {
|
|
tblcf_gen_bus_error (self);
|
|
if (self->debugFlag)
|
|
PRINTF ("tblcf_write_word : *0x%08x failed\n", ioc->address);
|
|
return BDM_FAULT_RESPONSE;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Write a byte to memory
|
|
*/
|
|
static int
|
|
tblcf_write_byte (struct BDM *self, struct BDMioctl *ioc)
|
|
{
|
|
if (self->debugFlag)
|
|
PRINTF ("tblcf_write_byte : 0x%08x = 0x%02x\n", ioc->address, (ioc->value & 0xff));
|
|
|
|
self->address = ioc->address + 1;
|
|
|
|
tblcf_write_mem8 (self->usbDev, ioc->address, ioc->value);
|
|
|
|
if (tblcf_get_last_sts (self->usbDev)) {
|
|
tblcf_gen_bus_error (self);
|
|
if (self->debugFlag)
|
|
PRINTF ("tblcf_write_byte : *0x%08x failed\n", ioc->address);
|
|
return BDM_FAULT_RESPONSE;
|
|
}
|
|
|
|
return 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
|
|
cf_bit_bash (struct BDM *self, unsigned short mask, unsigned short bits)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
/*
|
|
* Initialise the BDM structure for a Coldfire in the P&E interface
|
|
*/
|
|
static int
|
|
tblcf_init_self (struct BDM *self)
|
|
{
|
|
int reg;
|
|
|
|
self->processor = BDM_COLDFIRE;
|
|
self->interface = BDM_COLDFIRE_TBLCF;
|
|
|
|
self->get_status = tblcf_get_status;
|
|
self->init_hardware = tblcf_init_hardware;
|
|
self->serial_clock = tblcf_serial_clock;
|
|
self->gen_bus_error = tblcf_gen_bus_error;
|
|
self->restart_chip = tblcf_restart_chip;
|
|
self->release_chip = tblcf_release_chip;
|
|
self->reset_chip = tblcf_reset_chip;
|
|
self->stop_chip = tblcf_stop_chip;
|
|
self->run_chip = tblcf_run_chip;
|
|
self->step_chip = tblcf_step_chip;
|
|
self->fill_buf = tblcf_fill_buf;
|
|
self->send_buf = tblcf_send_buf;
|
|
self->read_sysreg = tblcf_read_sysreg;
|
|
self->read_proreg = tblcf_read_preg;
|
|
self->read_long_word = tblcf_read_long_word;
|
|
self->read_word = tblcf_read_word;
|
|
self->read_byte = tblcf_read_byte;
|
|
self->write_sysreg = tblcf_write_sysreg;
|
|
self->write_proreg = tblcf_write_preg;
|
|
self->write_long_word = tblcf_write_long_word;
|
|
self->write_word = tblcf_write_word;
|
|
self->write_byte = tblcf_write_byte;
|
|
|
|
for (reg = 0; reg < BDM_MAX_SYSREG; reg++)
|
|
if (reg == BDM_REG_AATR)
|
|
self->shadow_sysreg[reg] = 0x0005;
|
|
else
|
|
self->shadow_sysreg[reg] = 0;
|
|
|
|
self->cf_debug_ver = 0;
|
|
self->cf_use_pst = 1;
|
|
self->cf_running = 1;
|
|
self->cf_csr = 0;
|
|
|
|
self->cf_sr_masked = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Initialise the USB interface to tghe pod.
|
|
*/
|
|
int
|
|
usb_bdm_init (const char *device)
|
|
{
|
|
#if BDM_TBLCF_USB
|
|
struct BDM *self;
|
|
int devs;
|
|
int dev;
|
|
char usb_device[256];
|
|
ssize_t dev_size;
|
|
#if linux
|
|
char udev_usb_device[256];
|
|
#endif
|
|
|
|
/*
|
|
* Initialise the USB layers.
|
|
*/
|
|
devs = tblcf_init ();
|
|
|
|
#ifdef BDM_VER_MESSAGE
|
|
fprintf (stderr, "usb-bdm-init %d.%d, " __DATE__ ", " __TIME__ " devices:%d\n",
|
|
BDM_DRV_VERSION >> 8, BDM_DRV_VERSION & 0xff, devs);
|
|
#endif
|
|
|
|
#if linux
|
|
{
|
|
struct stat sb;
|
|
int result;
|
|
char *p;
|
|
|
|
if (lstat (device, &sb) == 0)
|
|
{
|
|
if (S_ISLNK (sb.st_mode))
|
|
{
|
|
dev_size = readlink (device, udev_usb_device, sizeof (udev_usb_device));
|
|
|
|
if ((dev_size >= 0) || (dev_size < sizeof (udev_usb_device) - 1))
|
|
{
|
|
udev_usb_device[dev_size] = '\0';
|
|
|
|
/*
|
|
* On Linux this is bus/usb/....
|
|
*/
|
|
|
|
if (strncmp (udev_usb_device, "bus/usb", sizeof ("bus/usb") - 1) == 0)
|
|
{
|
|
memmove (udev_usb_device, udev_usb_device + sizeof ("bus/usb"),
|
|
sizeof (udev_usb_device) - sizeof ("bus/usb") - 1);
|
|
|
|
p = strchr (udev_usb_device, '/');
|
|
|
|
*p = '-';
|
|
|
|
device = udev_usb_device;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
for (dev = 0; dev < devs; dev++)
|
|
{
|
|
char* name;
|
|
int length;
|
|
|
|
tblcf_usb_dev_name (dev, usb_device, sizeof (usb_device));
|
|
|
|
name = usb_device;
|
|
length = strlen (usb_device);
|
|
|
|
while (name && length)
|
|
{
|
|
name = strchr (name, device[0]);
|
|
|
|
if (name)
|
|
{
|
|
length = strlen (name);
|
|
|
|
if (strlen (device) <= length)
|
|
{
|
|
if (strncmp (device, name, length) == 0)
|
|
{
|
|
/*
|
|
* Set up the self srructure. Only ever one with ioperm.
|
|
*/
|
|
|
|
self = &bdm_device_info[0];
|
|
|
|
/*
|
|
* Open the USB device.
|
|
*/
|
|
self->usbDev = tblcf_open (usb_device);
|
|
|
|
if (self->usbDev < 0)
|
|
{
|
|
errno = EIO;
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* First set the default debug level.
|
|
*/
|
|
|
|
self->debugFlag = BDM_DEFAULT_DEBUG;
|
|
|
|
/*
|
|
* Force the port to exist.
|
|
*/
|
|
self->exists = 1;
|
|
|
|
self->delayTimer = 0;
|
|
|
|
tblcf_init_self (self);
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
name++;
|
|
length = strlen (name);
|
|
}
|
|
}
|
|
}
|
|
|
|
errno = ENOENT;
|
|
return -2;
|
|
#else
|
|
errno = ENOENT;
|
|
return -1;
|
|
#endif
|
|
}
|