Files
Bernd Mueller adfd70813f initial push
2026-06-17 13:44:30 +02:00

1968 lines
54 KiB
C

/*
* $Id: bdm.c,v 1.8 2006/12/28 13:04:58 ppisa Exp $
*
* Linux Device Driver BDM Interface
* based on the PD driver package by Scott Howard, Feb 93
* PD version ported to Linux by M.Schraut, Feb 95
* enhancements from W. Eric Norum (eric@skatter.usask.ca), who did
* a Next-Step-Port in Jun 95
* tested for kernel version 1.3.57
* (C) 1995, 1996 Technische Universitaet Muenchen, L. f. Prozessrechner
* (C) 1997 Gunter Magin, Muenchen
*
* Modified by Pavel Pisa pisa@cmp.felk.cvut.cz 1997 - 2003
* tested for kernel version 2.2.x and 2.4.x with ICD and PD cables
* new k_compat.h for kernels up to 2.4.0
*
* 2003/04/27 modified by Juergen Eder <Juergen.Eder@gmx.de>:
* Compatible with parport driver
* define in makefile: -DWITH_PARPORT_SUPPORT
*
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, 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; see the file COPYING. If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
*/
#undef REALLY_SLOW_IO
#include <linux/module.h>
#include <linux/version.h>
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17)
#include <linux/config.h>
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,60)
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17)
#include <linux/devfs_fs_kernel.h>
#endif
#else /* 2.5.60 */
#ifdef MODVERSIONS
#include <linux/modversions.h>
#endif /* MODVERSIONS */
#endif /* 2.5.60 */
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/ioport.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) // fixme ?
#include <linux/slab.h> /* instead of malloc.h */
#else
#include <linux/malloc.h>
#endif
#include <linux/sys.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/delay.h>
#ifdef WITH_PARPORT_SUPPORT
#include <linux/parport.h>
#endif
#include <asm/segment.h>
#include <asm/system.h>
#include <asm/io.h>
#include "bdm.h"
#include "k_compat.h"
#if (LINUX_VERSION_CODE >= VERSION(2,4,0))
#define BDM_WITH_DEVFS
#include <linux/miscdevice.h>
kc_devfs_handle_t bdm_devfs_handle=NULL;
#endif
#define BDM_DEVFS_DIR_NAME "m683xx-bdm"
MODULE_SUPPORTED_DEVICE("m683xx-bdm");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Motorola m683xx BDM debugging interface");
#define BDM_DRIVER_VERSION 4
#if !defined PD_INTERFACE && !defined ICD_INTERFACE
#error You have to decide for the PD interface or the ICD interface
#error Check the Makefile and uncomment the right line
#endif
#if !defined ICD_INTERFACE && defined EFIICDISP
#error It makes no sense to enable ISP programming support in a driver not
#error for ICD compatible devices
#error Check the Makefile and either disable EFIICDISP or enable ICD_INTERFACE
#endif
#define BYTECOUNT_FOR_BEING_NICE 64
/* no of bytes to transfer before giving
* other tasks opportunity being scheduled */
#define BDM_WAITCNT 0xffff /* no of loops to wait for response */
#define BITS 15 /* shift 16bit into 32 bit by 15 */
/* -.1.Bit always 0 (Statusbit) */
/* declaration of local variables */
#define BDM_DLY_DEFAULT 30
#define BDM_LONG_DLY(d) (2*d/3 == 0) ? 1 : (2*d/3)
#define BDM_HALF_DLY(d) (d/2 == 0) ? 1 : (d/2)
#define BDM_SHORT_DLY(d) (d/3 == 0) ? 1 : (d/3)
#define BDM_MIN_DLY(d) (d/30 == 0) ? 1 : (d/30)
static int bdm_norm_dly = BDM_DLY_DEFAULT;
static int bdm_long_dly = BDM_LONG_DLY(BDM_DLY_DEFAULT);
static int bdm_half_dly = BDM_HALF_DLY(BDM_DLY_DEFAULT);
static int bdm_short_dly = BDM_SHORT_DLY(BDM_DLY_DEFAULT);
static int bdm_min_dly = BDM_MIN_DLY(BDM_DLY_DEFAULT);
static int bdm_reset_dly = 0;
static int bdm_no_ser_dly_mode = 0; /* mode for maximal speed */
#define SER_CLK_DSO_WAIT 0x100 /* in fast mode, delay only before */
/* explicit serial transfers */
static unsigned int bdm_debug_level = BDM_DEBUG_NONE;
static int bdm_version = BDM_DRIVER_VERSION;
static int bdm_sense = 1; /* do sense power & connected */
typedef unsigned int status_bitset;
#define BDM_DESCR_MAGIC 0x42444daa
typedef struct _bdm_descr_t bdm_descr_t;
struct _bdm_descr_t {
unsigned int magic;
#ifndef WITH_PARPORT_SUPPORT
unsigned int port_adr;
#else /* WITH_PARPORT_SUPPORT */
unsigned int parport_nr;
#endif /* WITH_PARPORT_SUPPORT */
unsigned int minor;
status_bitset (*init) (bdm_descr_t *);
int (*deinit) (bdm_descr_t *);
status_bitset (*getstatus) (bdm_descr_t *);
int (*restart_chip) (bdm_descr_t *);
int (*release_chip) (bdm_descr_t *);
int (*stop_chip) (bdm_descr_t *);
int (*step_chip) (bdm_descr_t *);
int (*reset_chip) (bdm_descr_t *);
int (*ser_clk) (bdm_descr_t *, short, int);
int (*set_led) (bdm_descr_t *, int);
status_bitset (*resynchro) (bdm_descr_t *);
char *name;
unsigned int old_ctl; /* save old control port settings */
unsigned int shadow_ctl; /* shadow reg. of control port for read */
};
/* generic helper functions */
#ifndef WITH_PARPORT_SUPPORT
#define PORT_ADDRESSE_LP1 0x378 /* LPT1 */
#define PORT_ADDRESSE_LP2 0x278 /* LPT2 */
#define PORT_ADDRESSE_LP3 0x3bc /* LPT3 */
#define BDM_DATA(d) (d->port_adr)
#define BDM_STAT(d) (d->port_adr + 1)
#define BDM_CTRL(d) (d->port_adr + 2)
static unsigned char
bdm_in_ctl(bdm_descr_t * descr)
{
return inb(BDM_CTRL(descr));
}
static void
bdm_out_ctl(int data, bdm_descr_t * descr)
{
outb(data,BDM_CTRL(descr));
descr->shadow_ctl=data;
}
static unsigned char
bdm_in_st(bdm_descr_t * descr)
{
return inb(BDM_STAT(descr));
}
static void
bdm_out_data(int data, bdm_descr_t * descr)
{
outb(data,BDM_DATA(descr));
}
static unsigned char
bdm_in_data(bdm_descr_t * descr)
{
return inb(BDM_DATA(descr));
}
#else /* WITH_PARPORT_SUPPORT */
#define PORT_ADDRESSE_LP1 0
#define PORT_ADDRESSE_LP2 1
#define PORT_ADDRESSE_LP3 2
#define MAX_BDM_PORTS 3
static struct pardevice *bdm_parports[MAX_BDM_PORTS];
static struct pardevice *
bdm_get_parport(bdm_descr_t * descr)
{
if((unsigned)descr->parport_nr>=MAX_BDM_PORTS)
return NULL;
return bdm_parports[descr->parport_nr];
}
/*static int
bdm_get_parport_num(bdm_descr_t * descr)
{
return descr->parport_nr;
}*/
static void
bdm_out_ctl(int data, bdm_descr_t * descr)
{
struct pardevice *p;
p = bdm_get_parport(descr);
if(!p)
return;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
parport_write_control(p->port,data & ~0x20);
if(data & 0x20)
parport_data_reverse(p->port);
else
parport_data_forward(p->port);
#else
parport_write_control(p->port,data);
#endif
descr->shadow_ctl=data;
}
static unsigned char
bdm_in_ctl(bdm_descr_t * descr)
{
struct pardevice *p;
p = bdm_get_parport(descr);
if(!p)
return 0;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
/* There is no 'parport_read_control()' function -> use my own variable */
return descr->shadow_ctl;
#else
return parport_read_control(p->port);
#endif
}
static unsigned char
bdm_in_st(bdm_descr_t * descr)
{
struct pardevice *p;
p = bdm_get_parport(descr);
if(!p)
return 0;
return parport_read_status(p->port);
}
static void
bdm_out_data(int data, bdm_descr_t * descr)
{
struct pardevice *p;
p = bdm_get_parport(descr);
if(!p)
return;
parport_write_data(p->port,data);
}
static unsigned char
bdm_in_data(bdm_descr_t * descr)
{
struct pardevice *p;
p = bdm_get_parport(descr);
if(!p)
return 0;
return parport_read_data(p->port);
}
#endif /* WITH_PARPORT_SUPPORT */
/* slow down host for not overrunning target */
static void
bdm_delay(int counter)
{
if (counter < (1000000/HZ)) {
while (counter > 1000) {
udelay(1000); counter -= 1000;
}
udelay(counter);
} else {
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(counter/(1000000/HZ));
}
#if NEVER
while (counter--) {
asm volatile ("nop");
}
#endif /* NEVER */
}
static void
bdm_error(int bdm_type)
{
if (bdm_debug_level >= BDM_DEBUG_SOME) {
switch (bdm_type) {
case -BDM_FAULT_UNKNOWN:
printk("Unknown Error - check speed\n");
break;
case -BDM_FAULT_POWER:
printk("Power failed on Target MCU\n");
break;
case -BDM_FAULT_CABLE:
printk("Cable disconnected on Target MCU\n");
break;
case -BDM_FAULT_RESPONSE:
printk("No response from Target MCU\n");
break;
case -BDM_FAULT_RESET:
printk("Can t clock Target MCU while on Reset\n");
break;
case -BDM_FAULT_PORT:
printk("Wrong Port\n");
break;
case -BDM_FAULT_BERR:
printk("Buserror\n");
break;
}
}
}
#define do_case(x) case x: ret = #x; break;
static char *
bdm_get_ioctl_name(int ioctl)
{
char *ret;
switch (ioctl) {
do_case(BDM_INIT);
do_case(BDM_DEINIT);
do_case(BDM_RESET_CHIP);
do_case(BDM_RESTART_CHIP);
do_case(BDM_STOP_CHIP);
do_case(BDM_STEP_CHIP);
do_case(BDM_GET_STATUS);
do_case(BDM_SPEED);
do_case(BDM_RELEASE_CHIP);
do_case(BDM_DEBUG_LEVEL);
do_case(BDM_GET_VERSION);
do_case(BDM_SENSECABLE);
do_case(BDM_RESPW);
#ifdef EFIICDISP
do_case(BDM_ISPSET);
do_case(BDM_ISPGET);
#endif
default:
ret = "Unknown ioctl";
}
return ret;
}
#ifdef PD_INTERFACE
/*
* Parallel port bit assignments for the PD interface (minor-numbers 0-2)
*
* 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 - Drive BKPT* /DSCLK line HIGH
* | | |
* | | +------- 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 BDMPD_DSCLK 1 /* data shift clock / breakpoint pin */
#define BDMPD_RST_OUT 2 /* set high to force reset on MCU */
#define BDMPD_DSI 4 /* data shift input Host->MCU */
#define BDMPD_STEP_OUT 8 /* set low to gate IFETCH onto BKPT */
#define BDMPD_FREEZE 8 /* FREEZE asserted when MCU stopped */
#define BDMPD_DSO 0x20 /* data shift output MCU-.Host */
#define BDMPD_PWR_DWN 0x40 /* power down - low when Vcc failed */
#define BDMPD_NC 0x80 /* not connected - low when unplugged */
static status_bitset
bdm_pd_init(bdm_descr_t *descr)
{
status_bitset status;
bdm_out_ctl(BDMPD_STEP_OUT | BDMPD_DSCLK, descr);
/* force breakpoint */
bdm_delay(bdm_min_dly);
status = descr->getstatus(descr); /* eg connected, power etc. */
if (bdm_debug_level >= BDM_DEBUG_SOME) {
printk("bdm_pd_init minor %d status %d\n",
descr->minor, status);
}
return status;
}
static int
bdm_pd_deinit(bdm_descr_t * descr)
{
#if LP_PORT_RESTORE
if (bdm_debug_level >= BDM_DEBUG_SOME) {
printk("bdm_deinit: restoring %#x\n", descr->old_ctl);
}
bdm_out_ctl(descr->old_ctl, descr);
/* restore old control port settings */
#endif /* LP_PORT_RESTORE */
return BDM_FAULT_NOERROR;
}
/* read status byte from statusport and translate into bitset */
static status_bitset
bdm_pd_getstatus(bdm_descr_t *descr)
{
unsigned char temp = bdm_in_st(descr);
if (bdm_debug_level >= BDM_DEBUG_ALL) {
printk("bdm_pd_getstatus minor=%d res=%#x\n", descr->minor, temp);
}
if (bdm_sense) {
if (!(temp & BDMPD_NC))
return BDM_TARGETNC;
if (!(temp & BDMPD_PWR_DWN))
return BDM_TARGETPOWER;
}
return (temp & BDMPD_FREEZE ? BDM_TARGETSTOPPED : 0) |
(bdm_in_ctl(descr) & BDMPD_RST_OUT ? BDM_TARGETRESET : 0);
}
/* this routine is last resort one, it is able resyncronize with MCU,
when some spike on DSCLK has disturbed MCU */
static status_bitset
bdm_pd_resynchro(bdm_descr_t *descr)
{
int i;
descr->ser_clk(descr, BDM_NOP_CMD, 0); /* send NOP command */
descr->ser_clk(descr, BDM_NOP_CMD, 0); /* send NOP command */
descr->ser_clk(descr, BDM_NOP_CMD, 0); /* send NOP command */
i=17; /* maximal number disturb in bit shift */
while(i--)
{
if((bdm_in_st(descr) & BDMPD_DSO))
return BDM_FAULT_NOERROR; /* DSO becomes 0 => OK */
bdm_out_ctl( BDMPD_STEP_OUT | BDMPD_DSCLK, descr); /* DSCLK=0 */
bdm_delay(bdm_norm_dly);
bdm_out_ctl( BDMPD_STEP_OUT , descr); /* DSCLK=1 */
bdm_delay(bdm_norm_dly);
}
return -BDM_FAULT_UNKNOWN;
}
/* restart chip and stop on first execution fetch */
static int
bdm_pd_restart_chip(bdm_descr_t * descr)
{
unsigned bdm_Loop_Count = 0;
if (bdm_debug_level >= BDM_DEBUG_SOME) {
printk("bdm_pd_restart_chip minor=%d\n", descr->minor);
}
bdm_out_ctl(BDMPD_DSCLK, descr);
bdm_out_ctl(BDMPD_RST_OUT | BDMPD_DSCLK, descr);
bdm_delay(bdm_norm_dly+bdm_reset_dly);
bdm_out_ctl(BDMPD_DSCLK, descr);
bdm_delay(bdm_norm_dly);
bdm_out_ctl(BDMPD_STEP_OUT | BDMPD_DSCLK, descr);
for (bdm_Loop_Count = 0xffff; bdm_Loop_Count; bdm_Loop_Count--)
if (bdm_in_st(descr) & BDMPD_FREEZE)
break;
if (!bdm_Loop_Count) {
bdm_error(-BDM_FAULT_RESPONSE);
return -BDM_FAULT_RESPONSE;
} else {
return BDM_FAULT_NOERROR;
}
}
/* stop running target */
static int
bdm_pd_stop_chip(bdm_descr_t * descr)
{
unsigned int bdm_ctr;
int return_value;
if (bdm_debug_level >= BDM_DEBUG_SOME) {
printk("bdm_pd_stop_chip minor=%d\n", descr->minor);
}
if (bdm_in_st(descr) & BDMPD_FREEZE) {
if (bdm_debug_level >= BDM_DEBUG_SOME) {
printk("bdm_pd_stop_chip: already frozen..\n");
};
/* ensures no spike on BKPT/DSCLK in bdm_ser_clk */
bdm_out_ctl(BDMPD_DSCLK, descr);
bdm_delay(bdm_short_dly);
bdm_out_ctl(BDMPD_DSCLK | BDMPD_STEP_OUT, descr);
return BDM_FAULT_NOERROR; /* target was already halted */
}
if (bdm_debug_level >= BDM_DEBUG_SOME) {
printk("bdm_pd_stop_chip: was not frozen..\n");
}
return_value = -BDM_FAULT_UNKNOWN;
/* bdm_out_ctl(BDMPD_DSCLK | BDMPD_STEP_OUT, descr); */
bdm_out_ctl(BDMPD_DSCLK*0, descr);
if (bdm_debug_level >= BDM_DEBUG_SOME) {
printk("bdm_pd_stop_chip: armed stop\n");
}
for (bdm_ctr = BDM_WAITCNT; bdm_ctr; bdm_ctr--) {
if ((bdm_in_st(descr)) & BDMPD_FREEZE) {
return_value = BDM_FAULT_NOERROR; /* target is now halted */
break;
}
bdm_delay(bdm_min_dly);
}
if (bdm_debug_level >= BDM_DEBUG_SOME) {
printk("bdm_pd_stop_chip: after wait-for-freeze: cntr = %d\n",
BDM_WAITCNT-bdm_ctr);
}
bdm_out_ctl(BDMPD_DSCLK, descr);
bdm_delay(bdm_norm_dly);
bdm_out_ctl(BDMPD_DSCLK | BDMPD_STEP_OUT, descr);
if (!bdm_ctr) {
bdm_error(-BDM_FAULT_RESPONSE);
return -BDM_FAULT_RESPONSE;
}
#ifdef BDM_TRY_RESYNCHRO
descr->resynchro(descr); /* tries to find correct 17 bit boundary */
#endif /* BDM_TRY_RESYNCHRO */
return return_value;
}
/* single stepping target mcu */
static int
bdm_pd_step_chip(bdm_descr_t * descr)
{
int tester;
unsigned short DataOut = (BDM_GO_CMD & 1 ? BDMPD_DSI : 0);
status_bitset bdm_stat = descr->getstatus(descr);
if (bdm_debug_level >= BDM_DEBUG_SOME) {
printk("bdm_pd_step_chip minor=%d\n", descr->minor);
}
if (bdm_stat & BDM_TARGETRESET) {
bdm_error(-BDM_FAULT_RESET);
return -BDM_FAULT_RESET;
}
if (bdm_stat & BDM_TARGETNC) {
bdm_error(-BDM_FAULT_CABLE);
return -BDM_FAULT_CABLE;
}
if (bdm_stat & BDM_TARGETPOWER) {
bdm_error(-BDM_FAULT_POWER);
return -BDM_FAULT_POWER;
}
tester = descr->ser_clk(descr, BDM_GO_CMD, 1);
switch (tester) {
case -BDM_FAULT_RESET:
case -BDM_FAULT_CABLE:
case -BDM_FAULT_POWER:
return tester;
}
if (bdm_debug_level >= BDM_DEBUG_ALL) {
printk("stepchip sent: %x got : %x\n",
BDM_GO_CMD, (unsigned int) tester);
printk("last answerbit : %x\n", !(bdm_in_st(descr) & BDMPD_DSO));
}
/*
* this is the place the inb() should take place, but on step_chip, we
* don't care for the reply of CPU32, coming in when sending the cmd
*/
bdm_out_ctl(BDMPD_DSCLK | BDMPD_STEP_OUT | DataOut, descr);
bdm_delay(bdm_norm_dly);
bdm_out_ctl(BDMPD_DSCLK | DataOut, descr);
bdm_delay(bdm_long_dly);
/* leave more time to avoid a glitch on DSCLK*/
bdm_out_ctl(DataOut, descr);
/* FREEZE goes low after */
/* 10 CPU32 clock cycles, it is about 700ns @ 16 MHz */
bdm_delay(bdm_long_dly);
#ifdef BDM_TRY_RESYNCHRO
descr->stop_chip(descr);
return descr->resynchro(descr);
#else /* BDM_TRY_RESYNCHRO */
return descr->stop_chip(descr);
#endif /* BDM_TRY_RESYNCHRO */
}
/* reset target chip without asserting bkpt/!dsclk */
static int
bdm_pd_release_chip(bdm_descr_t * descr)
{
if (bdm_debug_level >= BDM_DEBUG_SOME) {
printk("bdm_pd_release_chip minor=%d\n", descr->minor);
}
bdm_out_ctl(BDMPD_DSCLK | BDMPD_STEP_OUT, descr);
bdm_delay(bdm_short_dly);
bdm_out_ctl(BDMPD_DSCLK | BDMPD_RST_OUT | BDMPD_STEP_OUT, descr);
bdm_delay(bdm_norm_dly);
bdm_out_ctl(BDMPD_STEP_OUT, descr);
bdm_delay(bdm_short_dly);
return BDM_FAULT_NOERROR;
}
/*
* 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
bdm_pd_reset_chip(bdm_descr_t * descr)
{
if (bdm_debug_level >= BDM_DEBUG_SOME) {
printk("bdm_pd_reset_chip minor=%d\n", descr->minor);
}
#if NEVER
/* this is the original Howard code */
bdm_out_ctl(BDMPD_RST_OUT | BDMPD_STEP_OUT, descr);
bdm_delay(bdm_norm_dly);
bdm_out_ctl(BDMPD_STEP_OUT, descr);
#endif /* NEVER */
/* this is suggested by Eric Norum */
bdm_out_ctl(BDMPD_DSCLK | BDMPD_RST_OUT, descr);
bdm_delay(bdm_norm_dly+bdm_reset_dly);
bdm_out_ctl(BDMPD_DSCLK, descr);
bdm_delay(bdm_norm_dly+bdm_reset_dly);
bdm_out_ctl(BDMPD_DSCLK | BDMPD_STEP_OUT, descr);
return BDM_FAULT_NOERROR;
}
/* serial software protokoll for bdm-pd-interface */
static int
bdm_pd_ser_clk(bdm_descr_t * descr, short bdm_value, int delay_bits)
{
unsigned int ShiftRegister;
unsigned char DataOut;
unsigned counter;
unsigned bdm_stat = descr->getstatus(descr);
ShiftRegister = bdm_value;
ShiftRegister <<= BITS;
if (bdm_stat & BDM_TARGETRESET) { /*error checking */
bdm_error(-BDM_FAULT_RESET);
return -BDM_FAULT_RESET;
}
if (bdm_stat & BDM_TARGETNC) {
bdm_error(-BDM_FAULT_CABLE);
return -BDM_FAULT_CABLE;
}
if (bdm_stat & BDM_TARGETPOWER) {
bdm_error(-BDM_FAULT_POWER);
return -BDM_FAULT_POWER;
}
if(!(bdm_stat & BDM_TARGETSTOPPED))
{ if (bdm_debug_level >= BDM_DEBUG_SOME)
printk("bdm_ser_clk stop target first minor=%d\n", descr->minor);
if(descr->stop_chip(descr)!=BDM_FAULT_NOERROR)
{ printk("bdm_ser_clk can't stop it minor=%d\n", descr->minor);
return -BDM_FAULT_RESPONSE;
}
}
/*
* We can get there with DSCLK high or low
* but next code is OK for both cases
* first ensures DSCLK low => it frozes transmit shift
* register against CPU32 updates until 17 bits are sent
* and then clocks transmit shift register by DSCLK high
*/
if (bdm_debug_level >= BDM_DEBUG_ALL) {
printk("bdm_pd_ser_clk: writing %5x\n", bdm_value);
}
if(bdm_no_ser_dly_mode && (delay_bits&SER_CLK_DSO_WAIT))
{ /* Wait for DSO to become 0 to indicate ready
* according CPU32UM, DSO is updated as long as
* DSCLK is high
*/
int wait_cnt;
for(wait_cnt=0;wait_cnt<100;wait_cnt++)
{ if (bdm_in_st(descr) & BDMPD_DSO)
break; /* DSO becomed zerro already */
bdm_delay(bdm_min_dly);
}
if(wait_cnt && (bdm_debug_level >= BDM_DEBUG_SOME))
printk("bdm_pd_ser_clk: ready after %d cycles\n",
wait_cnt);
}
counter = 32 - BITS - (delay_bits&0xff);
while (counter--) {
DataOut = ((ShiftRegister & 0x80000000) ? BDMPD_DSI : 0);
/*if 1 then dsi=1*/
ShiftRegister <<= 1; /*shift one bit left*/
bdm_out_ctl(DataOut | BDMPD_STEP_OUT | BDMPD_DSCLK, descr);
if(!bdm_no_ser_dly_mode)
bdm_delay(bdm_half_dly);
if (!(bdm_in_st(descr) & BDMPD_DSO))
ShiftRegister |= 1; /*put 1 on 0.bit */
bdm_out_ctl(DataOut | BDMPD_STEP_OUT, descr);
if(!bdm_no_ser_dly_mode)
bdm_delay(bdm_norm_dly);
}
if (bdm_debug_level >= BDM_DEBUG_ALL) {
printk("bdm_pd_ser_clk: read %5x bits %d\n",
ShiftRegister, delay_bits);
}
#ifdef FIXME
if (!(delay_bits&0xff) && (ShiftRegister & 0x10000)) switch (ShiftRegister) {
case 0x1ffff: /* no valid command return */
return -BDM_FAULT_NVC;
case 0x10001:
return -BDM_FAULT_BERR;
case 0x10000:
return BDM_NOTE_NOTREADY;
default: ;
}
#endif /* FIXME */
return ShiftRegister;
}
/* switch LED if available */
static int
bdm_pd_setled(bdm_descr_t * descr, int switchstate)
{
return 0;
}
#endif
#ifdef ICD_INTERFACE
/*
* Parallel port bit assignments for the ICD interface (minor-numbers 4-6)
*
* Status register
* +---+---+---+---+---+---+---+---+
* | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
* +---+---+---+---+---+---+---+---+
* | | | | |
* | | | | +--- ICD detection
* | | | | must follow LED level
* | | | |
* | | | +------- Power Detection
* | | | 1 - Power available
* | | | 0 - no Power
* | | |
* | | +----------- Jumper State
* | | 1 - Jumper closed
* | | 0 - Jumper open
* | |
* | +--------------- 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 (not implemented everywhere..)
* | | Write 1 - turn on LED
* | | Write 0 - turn off LED
* | |
* | +----------- ERROR
* | Write 0 - BERR output is tristated
* | Write 1 - BERR is pulled low
* |
* +--------------- spare
*/
/* data register bit definitions */
#define BDMICD_DSI 0x01 /* data shift input Host->MCU */
#define BDMICD_DSCLK 0x02 /* data shift clock / breakpoint pin */
#define BDMICD_STEP_OUT 0x04 /* set low to force breakpoint */
#define BDMICD_RST_OUT 0x08 /* set low to force reset on MCU */
#define BDMICD_OE 0x10 /* set to a 1 to enable DSI */
#define BDMICD_LED 0x20 /* set to 1 to turn on LED */
#define BDMICD_FORCE_BERR 0x40 /* set to a 1 to force BERR on target */
/* status register bit definitions */
#define BDMICD_DSO 0x80 /* serial data from target */
#define BDMICD_FREEZE 0x40 /* 1 if target is frozen */
#define BDMICD_JMPR 0x20 /* 1 if jumper is set */
#define BDMICD_PWR 0x10 /* 1 if power available */
#define BDMICD_ICDDETECT 0x08 /* must follow LED */
/* control register bit definitions */
#define BDMICD_EFIDET 0x04 /* setting to 1 will pull LED feedback low */
#define BDMICD_DCR_DIR 0x20 /* 0 -> drive data buffers, 1 -> data port tristate */
static int bdm_icd_led_status;
/*
* Jumper seems to be responsible for SW generated breakpoints according to
* P&E's user manual
*
* we assume a Rev. A cable, so ignore jumper setting
*
* Power detection is instable: depends heavily on PP internal
* pull-ups/downs to get low level when there is no power
*
*/
static status_bitset
bdm_icd_init(bdm_descr_t * descr)
{
status_bitset status;
unsigned char temp1, temp2;
bdm_icd_led_status = BDMICD_LED;
/* ensure data outputs are driven for BPP port configuration */
bdm_out_ctl(BDMICD_DCR_DIR*0,descr);
/* perform icd check */
bdm_out_data(BDMICD_STEP_OUT | BDMICD_DSCLK | BDMICD_RST_OUT, descr);
bdm_delay(bdm_min_dly);
temp1 = bdm_in_st(descr);
bdm_out_data(BDMICD_STEP_OUT | BDMICD_DSCLK | BDMICD_RST_OUT |
bdm_icd_led_status, descr);
bdm_delay(bdm_min_dly);
temp2 = bdm_in_st(descr);
if ((temp1 ^ temp2) & BDMICD_ICDDETECT) {
printk("bdm_icd_init: detected ICD interface\n");
}
/* force breakpoint */
bdm_delay(bdm_min_dly);
status = descr->getstatus(descr);
if (bdm_debug_level >= BDM_DEBUG_SOME) {
printk("bdm_icd_init minor %d status %d\n",
descr->minor, status);
}
return status;
}
static int
bdm_icd_deinit(bdm_descr_t * descr)
{
#if LP_PORT_RESTORE
if (bdm_debug_level >= BDM_DEBUG_SOME) {
printk("bdm_icd_deinit: restoring %#x\n", descr->bdm_old_ctl);
}
bdm_out_ctl(descr->bdm_old_ctl, descr);
/* restore old control port settings */
#endif /* LP_PORT_RESTORE */
return BDM_FAULT_NOERROR;
}
/* read status byte from statusport and translate into bitset */
static status_bitset
bdm_icd_getstatus(bdm_descr_t * descr)
{
unsigned char temp = bdm_in_st(descr);
if (bdm_debug_level > BDM_DEBUG_SOME) {
printk("bdm_icd_getstatus minor=%d res=%#x\n", descr->minor, temp);
}
if (bdm_sense) {
if (!(temp & BDMICD_PWR))
return BDM_TARGETPOWER;
}
return (temp & BDMICD_FREEZE ? BDM_TARGETSTOPPED : 0);
}
/* this routine is last resort one, it is able resyncronize with MCU,
when some spike on DSCLK has disturbed MCU */
static status_bitset
bdm_icd_resynchro(bdm_descr_t *descr)
{
int i;
descr->ser_clk(descr, BDM_NOP_CMD, 0); /* send NOP command */
descr->ser_clk(descr, BDM_NOP_CMD, 0); /* send NOP command */
descr->ser_clk(descr, BDM_NOP_CMD, 0); /* send NOP command */
i=17; /* maximal number disturb in bit shift */
while(i--)
{
if((bdm_in_st(descr) & BDMICD_DSO)) {
if (bdm_debug_level >= BDM_DEBUG_SOME) {
printk("bdm_icd_resynchro minor=%d shift=%d\n",
descr->minor,16-i);
}
return BDM_FAULT_NOERROR; /* DSO becomes 0 => OK */
}
bdm_out_data(BDMICD_RST_OUT | BDMICD_OE | BDMICD_STEP_OUT,
descr); /* DSCLK=0 */
bdm_delay(bdm_norm_dly);
bdm_out_data(BDMICD_RST_OUT | BDMICD_OE | BDMICD_STEP_OUT |
BDMICD_DSCLK, descr); /* DSCLK=1 */
bdm_delay(bdm_norm_dly);
}
if (bdm_debug_level >= BDM_DEBUG_SOME)
printk("bdm_icd_resynchro failed\n");
return -BDM_FAULT_UNKNOWN;
}
/* restart chip and stop on first execution fetch */
static int
bdm_icd_restart_chip(bdm_descr_t * descr)
{
if (bdm_debug_level >= BDM_DEBUG_SOME) {
printk("bdm_icd_restart_chip minor=%d\n", descr->minor);
}
bdm_out_data(BDMICD_DSCLK, descr);
bdm_delay(bdm_norm_dly+bdm_reset_dly);
return descr->stop_chip(descr);
}
/* stop running target */
static int
bdm_icd_stop_chip(bdm_descr_t * descr)
{
unsigned int bdm_ctr;
int return_value;
if (bdm_debug_level >= BDM_DEBUG_SOME) {
printk("bdm_icd_stop_chip minor=%d\n", descr->minor);
}
if ((bdm_in_st(descr)) & BDMICD_FREEZE)
{ /* already frozen, ensure no spikes on BKPT/DSCLK in bdm_ser_clk */
bdm_out_data(BDMICD_RST_OUT, descr);
bdm_delay(bdm_short_dly);
bdm_out_data(BDMICD_STEP_OUT | BDMICD_RST_OUT, descr);
return BDM_FAULT_NOERROR;
}
return_value = -BDM_FAULT_UNKNOWN;
bdm_delay(bdm_min_dly+100);
#if 1 /* stop imediately */
bdm_out_data(BDMICD_RST_OUT, descr);
#else /* stop after this instruction */
bdm_out_data(BDMICD_DSCLK | BDMICD_RST_OUT, descr);
#endif
bdm_delay(bdm_min_dly+300);
for (bdm_ctr = BDM_WAITCNT; bdm_ctr; bdm_ctr--) {
if ((bdm_in_st(descr)) & BDMICD_FREEZE) {
return_value = BDM_FAULT_NOERROR; /* target is now halted */
break;
}
bdm_delay(bdm_min_dly);
}
#ifdef DEB_TIM
printk("bdm_icd_stop_chip: having waited for FREEZE %d loops\n",
BDM_WAITCNT-bdm_ctr);
#endif
if (!bdm_ctr) {
#if 1
bdm_out_data(BDMICD_RST_OUT | BDMICD_FORCE_BERR, descr);
#else
bdm_out_data(BDMICD_DSCLK | BDMICD_RST_OUT | BDMICD_FORCE_BERR, descr);
#endif
bdm_delay(bdm_min_dly);
for (bdm_ctr = BDM_WAITCNT; bdm_ctr; bdm_ctr--) {
if ((bdm_in_st(descr)) & BDMICD_FREEZE) {
return_value = BDM_FAULT_NOERROR; /* target is now halted */
break;
}
bdm_delay(bdm_min_dly);
}
#ifdef DEB_TIM
printk("bdm_icd_stop_chip: berr has been forced; wait time was: %d\n",
BDM_WAITCNT-bdm_ctr);
#endif
}
#if 1
bdm_out_data(BDMICD_RST_OUT, descr);
bdm_delay(bdm_short_dly);
bdm_out_data(BDMICD_RST_OUT | BDMICD_STEP_OUT, descr);
#else
bdm_out_data(BDMICD_DSCLK | BDMICD_RST_OUT | BDMICD_STEP_OUT, descr);
#endif
#ifdef DEB_TIM
printk("bdm_icd_stop_chip: forcing DSCLK RST STEP\n");
#endif
if (!bdm_ctr) {
bdm_error(-BDM_FAULT_RESPONSE);
return -BDM_FAULT_RESPONSE;
}
#ifdef BDM_TRY_RESYNCHRO
descr->resynchro(descr); /* tries to find correct 17 bit boundary */
#endif /* BDM_TRY_RESYNCHRO */
return return_value;
}
/* single stepping target mcu */
static int
bdm_icd_step_chip(bdm_descr_t * descr)
{
int tester;
unsigned short DataOut = (BDM_GO_CMD & 1 ? BDMICD_DSI : 0);
status_bitset bdm_stat = descr->getstatus(descr);
if (bdm_debug_level >= BDM_DEBUG_SOME) {
printk("bdm_icd_step_chip minor=%d\n", descr->minor);
}
/* first two conditions will not be detected on ICD... */
if (bdm_stat & BDM_TARGETRESET) {
bdm_error(-BDM_FAULT_RESET);
return -BDM_FAULT_RESET;
}
if (bdm_stat & BDM_TARGETNC) {
bdm_error(-BDM_FAULT_CABLE);
return -BDM_FAULT_CABLE;
}
if (bdm_stat & BDM_TARGETPOWER) {
bdm_error(-BDM_FAULT_POWER);
return -BDM_FAULT_POWER;
}
tester = descr->ser_clk(descr, BDM_GO_CMD, 1);
switch (tester) {
case -BDM_FAULT_RESET:
case -BDM_FAULT_CABLE:
case -BDM_FAULT_POWER:
return tester;
}
if (bdm_debug_level >= BDM_DEBUG_ALL) {
printk("stepchip sent: %x got : %x\n",
BDM_GO_CMD, (unsigned int) tester);
printk("last answerbit : %x\n", !(bdm_in_st(descr) & BDMICD_DSO));
}
bdm_out_data(BDMICD_OE | BDMICD_STEP_OUT | DataOut | BDMICD_RST_OUT, descr);
bdm_delay(bdm_norm_dly);
bdm_out_data(BDMICD_OE | DataOut | BDMICD_RST_OUT, descr);
bdm_delay(bdm_min_dly);
bdm_out_data(BDMICD_DSCLK | DataOut | BDMICD_RST_OUT, descr);
#ifdef BDM_TRY_RESYNCHRO
descr->stop_chip(descr);
return descr->resynchro(descr);
#else /* BDM_TRY_RESYNCHRO */
return descr->stop_chip(descr);
#endif /* BDM_TRY_RESYNCHRO */
}
/* reset target chip without asserting freeze */
static int
bdm_icd_release_chip(bdm_descr_t * descr)
{
if (bdm_debug_level >= BDM_DEBUG_SOME) {
printk("bdm_icd_release_chip minor=%d\n", descr->minor);
}
bdm_out_data(BDMICD_DSCLK | BDMICD_STEP_OUT, descr);
bdm_delay(bdm_norm_dly);
bdm_out_data(BDMICD_DSCLK | BDMICD_RST_OUT | BDMICD_STEP_OUT, descr);
bdm_delay(bdm_short_dly);
return BDM_FAULT_NOERROR;
}
/*
* 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
bdm_icd_reset_chip(bdm_descr_t * descr)
{
if (bdm_debug_level >= BDM_DEBUG_SOME) {
printk("bdm_icd_reset_chip minor=%d\n", descr->minor);
}
/* this is according to Scott Howard */
bdm_out_data(BDMICD_DSCLK | BDMICD_STEP_OUT, descr);
bdm_delay(bdm_norm_dly+bdm_reset_dly);
bdm_out_data(BDMICD_DSCLK | BDMICD_STEP_OUT | BDMICD_RST_OUT, descr);
bdm_delay(bdm_norm_dly+bdm_reset_dly);
/*
*Tom Hoover's Error or Optimization? has to be checked
* FIXME... with scope; kick out if unnecessary.
*/
bdm_out_data(BDMICD_DSCLK, descr);
bdm_delay(bdm_norm_dly+1000);
/*
descr->stop_chip(descr);
*/
return BDM_FAULT_NOERROR;
}
/* serial software protokoll for bdm-icd-interface */
static int
bdm_icd_ser_clk(bdm_descr_t * descr, short bdm_value, int delay_bits)
{
unsigned int ShiftRegister;
unsigned char DataOut;
unsigned counter;
unsigned bdm_stat = descr->getstatus(descr);
if (bdm_debug_level >= BDM_DEBUG_ALL) {
printk("icd_ser_clk: in-val %x delay %d\n",
(unsigned short) bdm_value, delay_bits);
}
ShiftRegister = bdm_value;
ShiftRegister <<= BITS;
/* first two conditions will not be detected on ICD... */
if (bdm_stat & BDM_TARGETRESET) { /*error checking */
bdm_error(-BDM_FAULT_RESET);
return -BDM_FAULT_RESET;
}
if (bdm_stat & BDM_TARGETNC) {
bdm_error(-BDM_FAULT_CABLE);
return -BDM_FAULT_CABLE;
}
if (bdm_stat & BDM_TARGETPOWER) {
bdm_error(-BDM_FAULT_POWER);
return -BDM_FAULT_POWER;
}
if(!(bdm_stat & BDM_TARGETSTOPPED))
{ if (bdm_debug_level >= BDM_DEBUG_SOME)
printk("bdm_ser_clk stop target first minor=%d\n", descr->minor);
if(descr->stop_chip(descr)==-BDM_FAULT_RESPONSE)
{ printk("bdm_ser_clk can't stop it minor=%d\n", descr->minor);
return -BDM_FAULT_RESPONSE;
}
}
if(bdm_no_ser_dly_mode && (delay_bits&SER_CLK_DSO_WAIT))
{ /* Wait for DSO to become 0 to indicate ready
* according CPU32UM, DSO is updated as long as
* DSCLK is high
*/
int wait_cnt;
for(wait_cnt=0;wait_cnt<100;wait_cnt++)
{ if (bdm_in_st(descr) & BDMICD_DSO)
break; /* DSO becomed zerro already */
bdm_delay(bdm_min_dly);
}
if(wait_cnt && (bdm_debug_level >= BDM_DEBUG_SOME))
printk("bdm_icd_ser_clk: ready after %d cycles\n",
wait_cnt);
}
counter = 32 - BITS - (delay_bits&0xff);
if (bdm_debug_level >= BDM_DEBUG_ALL) {
printk("bdm_icd_ser_clk: writing %x\n", bdm_value);
}
while (counter--) {
DataOut = ((ShiftRegister & 0x80000000) ? BDMICD_DSI : 0);
/*if 1 then DSI=1*/
bdm_out_data(DataOut | BDMICD_RST_OUT | BDMICD_OE | BDMICD_STEP_OUT |
BDMICD_LED, descr); /* DSCLK=0 */
if(!bdm_no_ser_dly_mode)
bdm_delay(bdm_norm_dly);
ShiftRegister <<= 1; /*shift one bit left*/
if (!(bdm_in_st(descr) & BDMICD_DSO))
ShiftRegister |= 1; /*put 1 on 0.bit */
bdm_out_data(DataOut | BDMICD_RST_OUT | BDMICD_OE | BDMICD_STEP_OUT |
BDMICD_DSCLK | BDMICD_LED, descr); /* DSCLK=1 */
if(!bdm_no_ser_dly_mode)
bdm_delay(bdm_half_dly);
}
if ((delay_bits&0xff) == 0) {
bdm_out_data(BDMICD_DSCLK | BDMICD_STEP_OUT | BDMICD_RST_OUT,
descr);
bdm_delay(bdm_min_dly);
}
if (bdm_debug_level >= BDM_DEBUG_ALL) {
printk("icd_ser_clk: out-val %x delay %d\n",
(unsigned short) ShiftRegister, delay_bits);
}
#ifdef FIXME
if (!(delay_bits&0xff) && (ShiftRegister & 0x10000)) switch (ShiftRegister) {
case 0x1ffff: /* no valid command return */
return -BDM_FAULT_NVC;
case 0x10001:
return -BDM_FAULT_BERR;
case 0x10000:
return BDM_NOTE_NOTREADY;
default: ;
}
#endif /* FIXME */
return ShiftRegister;
}
/* switch LED if available */
static int
bdm_icd_setled(bdm_descr_t * descr, int switchstate)
{
bdm_icd_led_status = (switchstate ? BDMICD_LED : 0);
bdm_out_data(BDMICD_STEP_OUT | BDMICD_DSCLK | BDMICD_RST_OUT |
bdm_icd_led_status, descr);
return BDM_FAULT_NOERROR;
}
#endif
static int bdm_minor_index[] = {
#ifdef PD_INTERFACE
0, 1, 2,
#else
-1, -1, -1,
#endif
-1,
#ifdef ICD_INTERFACE
3, 4, 5,
#else
-1, -1, -1,
#endif
-1,
#ifdef AS_INTERFACE
-1, -1, -1,
#else
-1, -1, -1,
#endif
-1
};
const int bdm_minor_index_size =
sizeof(bdm_minor_index)/sizeof(*bdm_minor_index);
#define PD_DESCRIPTOR(adr,minor,name) \
BDM_DESCR_MAGIC, adr, minor, \
bdm_pd_init, bdm_pd_deinit, bdm_pd_getstatus,\
bdm_pd_restart_chip, bdm_pd_release_chip, bdm_pd_stop_chip,\
bdm_pd_step_chip, bdm_pd_reset_chip, bdm_pd_ser_clk,\
bdm_pd_setled, bdm_pd_resynchro , name
#define ICD_DESCRIPTOR(adr,minor,name) \
BDM_DESCR_MAGIC, adr, minor, \
bdm_icd_init, bdm_icd_deinit, bdm_icd_getstatus,\
bdm_icd_restart_chip, bdm_icd_release_chip, bdm_icd_stop_chip,\
bdm_icd_step_chip, bdm_icd_reset_chip, bdm_icd_ser_clk,\
bdm_icd_setled, bdm_icd_resynchro , name
#define EMPTY_DESCRIPTOR(adr,minor) \
0, adr, minor, \
NULL, NULL, NULL, \
NULL, NULL, NULL, \
NULL, NULL, NULL, \
NULL, NULL, NULL
static bdm_descr_t bdm_driver_descriptors[] = {
#ifdef PD_INTERFACE
{ PD_DESCRIPTOR(PORT_ADDRESSE_LP1,0,"pd0") },
{ PD_DESCRIPTOR(PORT_ADDRESSE_LP2,1,"pd1") },
{ PD_DESCRIPTOR(PORT_ADDRESSE_LP3,2,"pd2") },
#else
{ EMPTY_DESCRIPTOR(0,0) },
{ EMPTY_DESCRIPTOR(0,1) },
{ EMPTY_DESCRIPTOR(0,2) },
#endif
#ifdef ICD_INTERFACE
{ ICD_DESCRIPTOR(PORT_ADDRESSE_LP1,4,"icd0") },
{ ICD_DESCRIPTOR(PORT_ADDRESSE_LP2,5,"icd1") },
{ ICD_DESCRIPTOR(PORT_ADDRESSE_LP3,6,"icd2") },
#else
{ EMPTY_DESCRIPTOR(0,4) },
{ EMPTY_DESCRIPTOR(0,5) },
{ EMPTY_DESCRIPTOR(0,6) },
#endif
{ EMPTY_DESCRIPTOR(0,8) },
{ EMPTY_DESCRIPTOR(0,9) },
{ EMPTY_DESCRIPTOR(0,10) }
};
/* preparation macros to get minor descriptor */
#define check_descriptor(rdev, descr) \
{\
unsigned int minor; int descr_indx;\
if ((minor = kc_dev2minor(rdev)) > bdm_minor_index_size) {\
return -ENODEV;\
} \
if ((descr_indx = bdm_minor_index[minor]) < 0) {\
return -ENODEV;\
}\
descr = &bdm_driver_descriptors[descr_indx];\
if (descr->minor != minor) {\
printk("Inconsistency: descr->minor %d, minor %d\n", descr->minor,\
minor);\
return -ENODEV;\
}\
}
#define minor2descriptor(minor) \
(&bdm_driver_descriptors[bdm_minor_index[(minor)]])
static bdm_descr_t *bdm_file2descr(const struct file *file)
{
bdm_descr_t *descr = (bdm_descr_t *)(file->private_data);
if(descr==NULL){
printk(KERN_CRIT "BDM no descriptor!!!\n");
return NULL;
}
if(descr->magic!=BDM_DESCR_MAGIC){
printk(KERN_CRIT "BDM wrong MAGIC number!!!\n");
return NULL;
}
return descr;
}
static int
bdm_open(struct inode *inode, struct file *file)
{
status_bitset bdm_okay;
bdm_descr_t *descr;
int val;
#ifdef WITH_PARPORT_SUPPORT
static struct pardevice *parport;
#endif /* WITH_PARPORT_SUPPORT */
check_descriptor(inode->i_rdev, descr);
#ifndef WITH_PARPORT_SUPPORT
if (check_region(BDM_DATA(descr), 3)) {
printk("bdm_open: address space in use (conflict with PARPORT ?)...\n");
return -EBUSY;
request_region(BDM_DATA(descr), 3, "bdm");
}
#else /* WITH_PARPORT_SUPPORT */
parport=bdm_get_parport(descr);
if(!parport)
return -ENODEV;
if(parport_claim(parport)) {
printk("bdm_open: no access to PARPORT device");
return -EBUSY;
}
#endif /* WITH_PARPORT_SUPPORT */
/* stupid test if required port is really available */
val=bdm_in_data(descr);
val&=~0x21;
bdm_out_data(val, descr);
if (bdm_in_data(descr) != val) {
printk("bdm_open: physical device not detected...\n");
#ifdef WITH_PARPORT_SUPPORT
parport_release(bdm_get_parport(descr));
#else
release_region(BDM_DATA(descr), 3);
#endif
return -ENODEV;
}
kc_MOD_INC_USE_COUNT;
file->private_data=descr;
if (bdm_debug_level >= BDM_DEBUG_SOME) {
printk("bdm_open\n");
}
bdm_okay = descr->init(descr);
if (bdm_debug_level >= BDM_DEBUG_SOME) {
if (!(bdm_okay & BDM_TARGETNC)) {
printk("BDM detected !!\n");
} else {
printk("Target not connected ! Error %d \n", bdm_okay);
}
}
if (bdm_debug_level >= BDM_DEBUG_SOME) {
printk("bdm_open successful\n");
}
return BDM_FAULT_NOERROR;
}
static CLOSERET
bdm_release(struct inode *inode, struct file *file)
{
bdm_descr_t *descr = bdm_file2descr(file);
if(!descr) return -ENODEV;
descr->deinit(descr);
if (bdm_debug_level >= BDM_DEBUG_SOME) {
printk("bdm_release\n");
}
file->private_data=NULL;
kc_MOD_DEC_USE_COUNT;
#ifdef WITH_PARPORT_SUPPORT
parport_release(bdm_get_parport(descr));
#else
release_region(BDM_DATA(descr), 3);
#endif
return (CLOSERET)0;
}
static int
bdm_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
unsigned retval = BDM_FAULT_NOERROR;
status_bitset status;
bdm_descr_t *descr = bdm_file2descr(file);
if(!descr) return -ENODEV;
if (bdm_debug_level >= BDM_DEBUG_SOME) {
printk("bdm_ioctl minor=%d cmd=%d (%s) arg=0x%lx\n",
descr->minor, cmd, bdm_get_ioctl_name(cmd), arg);
}
switch (cmd) {
case BDM_INIT:
status = descr->init(descr);
if (status & BDM_TARGETNC)
retval = -BDM_FAULT_CABLE;
else if (status & BDM_TARGETPOWER)
retval = -BDM_FAULT_POWER;
break;
case BDM_DEINIT:
retval = descr->deinit(descr);
break;
case BDM_RESET_CHIP:
retval = descr->reset_chip(descr);
/* hardware reset on MCU - running state */
break;
case BDM_RESTART_CHIP:
/* reset target and stops execution on first instruction fetch */
retval = descr->restart_chip(descr);
break;
case BDM_STOP_CHIP:
/* stop running target (FREEZE) */
retval = descr->stop_chip(descr);
break;
case BDM_STEP_CHIP:
/* one step on target */
descr->step_chip(descr);
retval = BDM_FAULT_NOERROR;
break;
case BDM_GET_STATUS:
retval = descr->getstatus(descr);
break;
case BDM_SPEED:
/* Change speed value */
if ((int) arg <= 0) {
if(bdm_debug_level >= BDM_DEBUG_SOME)
printk("bdm_ioctl BDM_SPEED: no_ser_dly_mode=1\n");
bdm_no_ser_dly_mode = 1;
bdm_norm_dly = 1;
} else {
bdm_no_ser_dly_mode = 0;
bdm_norm_dly = (int) arg;
}
bdm_long_dly = BDM_LONG_DLY(bdm_norm_dly);
bdm_half_dly = BDM_HALF_DLY(bdm_norm_dly);
bdm_short_dly = BDM_SHORT_DLY(bdm_norm_dly);
bdm_min_dly = BDM_MIN_DLY(bdm_norm_dly);
if (bdm_debug_level >= BDM_DEBUG_SOME) {
printk("bdm_ioctl SPEED: "
"norm %d long %d half %d short %d min %d\n",
bdm_norm_dly, bdm_long_dly, bdm_half_dly, bdm_short_dly,
bdm_min_dly);
}
break;
case BDM_RELEASE_CHIP:
/* reset without bdm, so quit bdm mode */
retval = descr->release_chip(descr);
break;
case BDM_DEBUG_LEVEL:
/* change debug level 0,1,2 */
if (arg < 3) {
bdm_debug_level = (unsigned int) arg;
} else {
retval = -EINVAL;
}
break;
case BDM_GET_VERSION:
/* read counter and return it to *arg */
if(kc_put_user_long(bdm_version, arg))
retval = -EINVAL;
break;
case BDM_SENSECABLE:
bdm_sense = arg;
if (bdm_debug_level >= BDM_DEBUG_SOME) {
printk("bdm_ioctl: setting bdm_sense to %d\n", bdm_sense);
}
break;
case BDM_RESPW:
/* reset additional delay */
bdm_reset_dly = (unsigned int) arg;
if (bdm_debug_level >= BDM_DEBUG_SOME) {
printk("bdm_ioctl: setting reset_pw to %d\n", bdm_reset_dly);
}
break;
case BDM_SETLED:
retval = descr->set_led(descr,arg);
break;
#ifdef EFIICDISP
#define BDMICD_ispSCLK 1 /* ctrl-Register; clock data to isp */
#define BDMICD_ispSDI 8 /* ctrl-Register; data line to isp */
#define BDMICD_ispMODE 0x80 /* data-Register; mode for ips */
#define BDMICD_ispSDO 0x20 /* stat-Register; data line from isp */
case BDM_ISPSET:
{
static char ispcmd=0, ispdata=0;
switch(arg & 0xf) {
case BDM_ispSDI:
/* #define ISPVERBOSE 1 */
if (bdm_debug_level >= BDM_DEBUG_ALL) {
printk("ispSDI ");
}
if (arg & BDM_isp_LEVEL) { /* neg. output */
ispcmd &= ~BDMICD_ispSDI;
if (bdm_debug_level >= BDM_DEBUG_ALL) {
printk("1\n");
}
} else {
ispcmd |= BDMICD_ispSDI;
if (bdm_debug_level >= BDM_DEBUG_ALL) {
printk("0\n");
}
}
bdm_out_ctl(ispcmd, descr);
break;
case BDM_ispSCLK:
if (bdm_debug_level >= BDM_DEBUG_ALL) {
printk("ispSCLK ");
}
if (arg & BDM_isp_LEVEL) { /* neg. output */
ispcmd &= ~BDMICD_ispSCLK;
if (bdm_debug_level >= BDM_DEBUG_ALL) {
printk("1\n");
}
} else {
ispcmd |= BDMICD_ispSCLK;
if (bdm_debug_level >= BDM_DEBUG_ALL) {
printk("0\n");
}
}
bdm_out_ctl(ispcmd, descr);
break;
case BDM_ispMODE:
if (bdm_debug_level >= BDM_DEBUG_ALL) {
printk("ispMODE ");
}
if (arg & BDM_isp_LEVEL) {
ispdata |= BDMICD_ispMODE;
if (bdm_debug_level >= BDM_DEBUG_ALL) {
printk("1\n");
}
} else {
ispdata &= ~BDMICD_ispMODE;
if (bdm_debug_level >= BDM_DEBUG_ALL) {
printk("0\n");
}
}
bdm_out_data(ispdata, descr);
break;
case BDM_ispISP:
/* ignore */
break;
}
udelay(100);
/* this is necessary to mimic the bdm_delay factor for isp */
break;
}
case BDM_ISPGET:
if (bdm_in_st(descr) & BDMICD_ispSDO) {
retval = 1;
} else {
retval = 0;
}
if (bdm_debug_level >= BDM_DEBUG_ALL) {
printk("ispSDO %d\n", retval);
}
break;
#endif
default:
retval = -EINVAL;
}
return retval;
}
static RWRET
bdm_write(WRITE_PARAMETERS)
{
short bdm_word;
int ret;
int written = 0;
bdm_descr_t *descr = bdm_file2descr(file);
if(!descr) return -ENODEV;
if (bdm_debug_level >= BDM_DEBUG_SOME) {
printk("bdm_write minor %d len %d\n", descr->minor, (int)count);
}
if (!access_ok(VERIFY_READ, buf, count))
return -EINVAL;
while (count > 0) {
bdm_word = 0;
kc_get_user_word(bdm_word, buf);
ret = descr->ser_clk(descr, bdm_word, 0);
if (bdm_debug_level >= BDM_DEBUG_ALL) {
printk("bdm_write sent: %x received: %x\n",
bdm_word, (unsigned int) ret);
}
if (written) {
switch (ret) {
case 0x1ffff: /* no valid command return */
return -BDM_FAULT_NVC;
case 0x10001: /* Buserror */
return -BDM_FAULT_BERR;
case -BDM_FAULT_RESET:
case -BDM_FAULT_CABLE:
case -BDM_FAULT_POWER:
return ret;
}
}
count -= 2;
buf += 2;
written += 2;
if (!(written % BYTECOUNT_FOR_BEING_NICE)) {
schedule();
}
}
return written;
}
static RWRET
bdm_read(READ_PARAMETERS)
{
short bdm_word = BDM_NOP_CMD, value;
int ret;
int read = 0, timeout = 0;
bdm_descr_t *descr = bdm_file2descr(file);
if(!descr) return -ENODEV;
if (bdm_debug_level >= BDM_DEBUG_SOME) {
printk("bdm_read minor=%d len=%d\n", descr->minor, (int)count);
}
if (!access_ok(VERIFY_WRITE, buf, count))
return -EINVAL;
while (count > 0) {
ret = descr->ser_clk(descr, bdm_word, SER_CLK_DSO_WAIT);
if (bdm_debug_level >= BDM_DEBUG_ALL) {
printk("\n read sent: %x received: %x\n",
bdm_word, (unsigned int) ret);
}
switch (ret) {
case 0x10001: /* test if BERR*/
return -BDM_FAULT_BERR;
case 0x1ffff: /* no valid command return */
return -BDM_FAULT_NVC;
case 0x10000:
timeout++;
if (timeout - read / 2 > 4) {
/*
* we assume the FAULT_RESPONSE error, if number of
* failed attempts is by 4 higher than number of
* successful attempts
*/
return -BDM_FAULT_RESPONSE;
}
break;
case -BDM_FAULT_RESET:
case -BDM_FAULT_CABLE:
case -BDM_FAULT_POWER:
return ret;
default:
value = ret;
kc_put_user_word(value, buf);
buf += 2;
count -= 2;
read += 2;
if (!(read % BYTECOUNT_FOR_BEING_NICE)) {
schedule();
}
}
}
return read;
}
/* static struct file_operations bdm_fops */
KC_CHRDEV_FOPS_BEG(bdm_fops)
KC_FOPS_LSEEK(NULL) /* lseek */
read:bdm_read, /* read */
write:bdm_write, /* write */
readdir:NULL, /* readdir */
poll:NULL, /* poll/select */
ioctl:bdm_ioctl, /* ioctl */
mmap:NULL, /* mmap */
open:bdm_open, /* open */
KC_FOPS_FLUSH(NULL) /* flush */
KC_FOPS_RELEASE(bdm_release) /* release */
fsync:NULL, /* fsync */
fasync:NULL, /* fasync */
NULL, /* check_media_change */
NULL, /* revalidate */
KC_CHRDEV_FOPS_END;
struct kc_class *bdm_class;
#ifdef WITH_PARPORT_SUPPORT
static int
bdm_preempt(void *handle)
{
return 1; // 1=don't release parport
}
#endif
int
init_module(void)
{
int i;
#ifdef WITH_PARPORT_SUPPORT
int num;
struct parport *port;
int num_offset=0;
#endif
printk("BDM init_module\n %s\n %s\n %s\n",
"$RCSfile: bdm.c,v $", "$Revision: 1.8 $", "$Date: 2006/12/28 13:04:58 $");
/*"$Id: bdm.c,v 1.8 2006/12/28 13:04:58 ppisa Exp $", */
printk(" Version %s\n Compiled at %s %s\n",
#ifdef PD_INTERFACE
"PD "
#endif
#ifdef ICD_INTERFACE
"ICD "
#ifdef EFIICDISP
"+ISP"
#endif
" "
#endif
"", __DATE__, __TIME__);
#ifdef WITH_PARPORT_SUPPORT
printk("BDM init_module: compiled with PARPORT support\n");
for(num=0; num < MAX_BDM_PORTS; num++)
{
port=NULL;
do{
if(num+num_offset >= PARPORT_MAX) break;
port=parport_find_number(num+num_offset);
if(port) break;
num_offset++;
}while(1);
if(!port) break;
bdm_parports[num] = parport_register_device(port, "bdm", bdm_preempt, NULL, NULL,0,NULL);
if (!bdm_parports[num]){
printk("BDM init_module: Can't register device no.: %d",num);
}
parport_put_port(port);
}
#endif
#ifndef BDM_WITH_DEVFS
if (register_chrdev(BDM_MAJOR_NUMBER, "bdm", &bdm_fops)) {
printk("Unable to get major number for BDM driver\n");
return -EIO;
}
#else /* BDM_WITH_DEVFS */
if (devfs_register_chrdev(BDM_MAJOR_NUMBER, "bdm", &bdm_fops)) {
printk("Unable to get major number for BDM driver\n");
return -EIO;
}
/* I hate next conditional compilation, but k_compat.h can not handle
latest changes to devfs done by Adam J. Richter <adam@yggdrasil.com>. */
#if (LINUX_VERSION_CODE >= VERSION(2,5,60))
#if (LINUX_VERSION_CODE < VERSION(2,6,17))
devfs_mk_dir (BDM_DEVFS_DIR_NAME);
#endif /* < 2.6.17 */
bdm_class = kc_class_create(THIS_MODULE, BDM_DEVFS_DIR_NAME);
for(i=0;i<bdm_minor_index_size;i++){
/*char dev_name[32];*/
if(bdm_minor_index[i]<0) continue;
if(!minor2descriptor(i)->name) continue;
/*sprintf(dev_name, "%s/%s", BDM_DEVFS_DIR_NAME, minor2descriptor(i)->name);*/
#if (LINUX_VERSION_CODE < VERSION(2,6,17))
devfs_mk_cdev(MKDEV(BDM_MAJOR_NUMBER, i), S_IFCHR | S_IRUGO | S_IWUGO,
"%s/%s", BDM_DEVFS_DIR_NAME, minor2descriptor(i)->name);
#endif /* < 2.6.17 */
kc_class_device_create(bdm_class, NULL, MKDEV(BDM_MAJOR_NUMBER, i), NULL, minor2descriptor(i)->name);
}
#else /* < 2.5.60 */
bdm_devfs_handle = devfs_mk_dir (NULL, BDM_DEVFS_DIR_NAME, NULL);
for(i=0;i<bdm_minor_index_size;i++){
if(bdm_minor_index[i]<0) continue;
if(!minor2descriptor(i)->name) continue;
devfs_register(bdm_devfs_handle, minor2descriptor(i)->name,
DEVFS_FL_DEFAULT, BDM_MAJOR_NUMBER, i,
S_IFCHR | S_IRUGO | S_IWUGO ,
&bdm_fops, NULL);
}
#endif /* 2.5.60 */
#endif /* BDM_WITH_DEVFS */
printk("BDM driver succesfully registered !!\n");
return 0;
}
void
cleanup_module(void)
{
#ifdef WITH_PARPORT_SUPPORT
int num;
#endif
#ifdef WITH_PARPORT_SUPPORT
printk("BDM init_module: compiled with PARPORT support\n");
for(num=0; num < MAX_BDM_PORTS; num++)
{
if (bdm_parports[num])
parport_unregister_device(bdm_parports[num]);
bdm_parports[num] = NULL;
}
#endif
#ifdef BDM_WITH_DEVFS
/* I hate next conditional compilation, but k_compat.h can not handle
latest changes to devfs done by Adam J. Richter <adam@yggdrasil.com>. */
#if (LINUX_VERSION_CODE >= VERSION(2,5,60))
{
int i;
for(i=0;i<bdm_minor_index_size;i++){
if(bdm_minor_index[i]<0) continue;
if(!minor2descriptor(i)->name) continue;
kc_class_device_destroy(bdm_class, MKDEV(BDM_MAJOR_NUMBER, i));
#if (LINUX_VERSION_CODE < VERSION(2,6,17))
devfs_remove("%s/%s", BDM_DEVFS_DIR_NAME, minor2descriptor(i)->name);
#endif /* < 2.6.17 */
}
}
#if (LINUX_VERSION_CODE < VERSION(2,6,17))
devfs_remove(BDM_DEVFS_DIR_NAME);
#endif /* < 2.6.17 */
kc_class_destroy(bdm_class);
#else /* < 2.5.60 */
if(bdm_devfs_handle)
devfs_unregister (bdm_devfs_handle);
#endif /* < 2.5.60 */
if (devfs_unregister_chrdev(BDM_MAJOR_NUMBER, "bdm") != 0)
#else /* BDM_WITH_DEVFS */
if (unregister_chrdev(BDM_MAJOR_NUMBER, "bdm") != 0)
#endif /* BDM_WITH_DEVFS */
printk("BDM cleanup: Unregister failed\n");
else
printk("BDM cleanup: Unregister O.K.\n");
}