/* 
 * $Id: bdm.c,v 1.21 1996/07/21 12:16:08 magin 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, 
 * Lehrstuhl fuer Prozessrechner

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.

 */


#include <linux/config.h>
#include <linux/module.h>
/* #include <linux/modversions.h> */
#include <linux/version.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/ioport.h>
#include <linux/malloc.h>
#include <linux/sys.h>
#include <linux/fs.h>
#include <asm/segment.h>
#include <asm/system.h>
#include <asm/io.h>
#include "bdm.h"

#define	BDM_DRIVER_VERSION	2
/*
 * 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.
 */

/*
 * Parallel port bit assignments for the ICD interface (minor-numbers 4-6)
 *
 * 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
 */

#if !defined MODVERSIONS
#undef MOD_INC_USE_COUNT
#define MOD_INC_USE_COUNT
#undef MOD_DEC_USE_COUNT
#define MOD_DEC_USE_COUNT
#endif

#ifdef PD_INTERFACE

#define BDMPD_DSCLK      1		/* data shift clock / breakpoint pin  */
#define BDMPD_RST_OUT    2		/* set high to force reset on MCU     */
#define BDM_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 */
#endif

#ifdef IDC_INTERFACE
#define BDM_DSI        1		/* data shift input  Host->MCU        */
#define BDM_DSCLK      2		/* data shift clock / breakpoint pin  */
#define BDM_STEP_OUT   4		/* set low to force breakpoint        */
#define BDM_RST_OUT    8		/* set low to force reset on MCU      */
#define BDM_OE         0x10		/* set to a 1 to enable DSI           */
#define BDM_FORCE_BERR 0x40		/* set to a 1 to force BEER on target */
#define BDM_FREEZE     0x40     /* */
#define BDM_DSO        0x80		/* */
#endif

#if !defined PD_INTERFACE && !defined IDC_INTERFACE
#error	You have to decide for the PD interface or the IDC interface
#error	Check the Makefile and uncomment the right line
#endif

#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 */
#if LP_PORT_RESTORE
static int bdm_old_ctl;			/* save old control port settings     */
#endif
static int bdm_speed = 30;		/* delay for fast CPU like 486        */
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 */

/* different debug levels             */

static int pport_adr[] =
{
	0x378,						/* lpt0 */
	0x278,						/* lpt1 */
	0x3bc,						/* lpt2 */
};
static int pport_adr_len = sizeof(pport_adr) / sizeof(int);

#define	BDM_DATA(minor)	(pport_adr[(minor)])
#define	BDM_STAT(minor)	(pport_adr[(minor)] + 1)
#define	BDM_CTRL(minor)	(pport_adr[(minor)] + 2)

typedef unsigned int status_bitset;

/* declare all functions */
static int bdm_deinit(unsigned int);
static status_bitset bdm_getstatus(unsigned int);
static int bdm_restart_chip(unsigned int);
static int bdm_release_chip(unsigned int);
static int bdm_stop_chip(unsigned int);
static int bdm_step_chip(unsigned int);
static int bdm_reset_chip(unsigned int);
static void bdm_delay(int);
static long bdm_ser_clk(unsigned int, short, int);
static void bdm_error(int);



static status_bitset
bdm_init_hardware(unsigned int minor)
{
	status_bitset status;

#ifdef PD_INTERFACE
	outb(BDMPD_STEP_OUT | BDMPD_DSCLK, BDM_CTRL(minor));	
										/* force breakpoint         */
#endif
#ifdef IDC_INTERFACE
	outb(BDM_STEP_OUT | BDM_DSCLK | BDM_RST_OUT, BDM_DATA(minor));	
										/* force breakpoint         */
#endif
	status = bdm_getstatus(minor);		/* eg connected, power etc. */
	if (bdm_debug_level >= BDM_DEBUG_SOME) {
		printk("bdm_init_hardware status %d, port: %#x\n", 
				status, BDM_DATA(minor));
	}
	return status;
}


static int
bdm_deinit(unsigned int minor)
{
#if LP_PORT_RESTORE
	if (bdm_debug_level >= BDM_DEBUG_SOME) {
		printk("bdm_deinit: restoring %#x\n", bdm_old_ctl);
	}
	outb(bdm_old_ctl, BDM_CTRL(minor));	/* restore old control port settings */
#endif /* LP_PORT_RESTORE */
	return BDM_NORETURN;
}

static int
bdm_open(struct inode *inode, struct file *file)
{
	status_bitset bdm_okay;
	unsigned int minor = MINOR(inode->i_rdev);

	if (minor >= pport_adr_len) {
		return -ENODEV;
	}

/*
	if (check_region(BDM_DATA(minor), 3)) {
		return -EBUSY;
	} else {
		request_region(BDM_DATA(minor), 3, "bdm");
	}
*/

	MOD_INC_USE_COUNT;

	if (bdm_debug_level >= BDM_DEBUG_SOME) {
		printk("bdm_open\n");
	}

	bdm_okay = bdm_init_hardware(minor);

printk("bdm_open: init_hardware returns 0x%x\n", bdm_okay);
	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 xxNEVER
	/* we dont care on open... */
	if (bdm_okay & BDM_TARGETNC) {
		MOD_DEC_USE_COUNT;
		return BDM_FAULT_CABLE;
	} else if (bdm_okay & BDM_TARGETPOWER) {
		MOD_DEC_USE_COUNT;
		return BDM_FAULT_POWER;
	}
#endif /* NEVER */

	if (bdm_debug_level >= BDM_DEBUG_SOME) {
		printk("bdm_open successful\n");
	}
printk("bdm_open returning %d\n", BDM_NORETURN);
	return BDM_NORETURN;
}


static void
bdm_release(struct inode *inode, struct file *file)
{
	unsigned int minor = MINOR(inode->i_rdev);
	bdm_deinit(minor);
	if (bdm_debug_level >= BDM_DEBUG_SOME) {
		printk("bdm_release\n");
	}
	MOD_DEC_USE_COUNT;
	release_region(BDM_DATA(minor), 3);
}

#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);
		default:
			ret = "Unknown ioctl";
	}
	return ret;
}

static int
bdm_ioctl(struct inode *inode, struct file *file, 
		unsigned int cmd, unsigned long arg)
{
	unsigned retval = BDM_NORETURN;
	status_bitset status; 
	unsigned int minor = MINOR(inode->i_rdev);

	if (bdm_debug_level >= BDM_DEBUG_SOME) {
		printk("bdm_ioctl minor=%d cmd=%d (%s)  arg=0x%lx\n", 
			minor, cmd, bdm_get_ioctl_name(cmd), arg);
	}
	switch (cmd) {
	  case BDM_INIT:
		status = bdm_init_hardware(minor);
		if (status & BDM_TARGETNC) 
			retval = BDM_FAULT_CABLE;
		else if (status & BDM_TARGETPOWER) 
			retval = BDM_FAULT_POWER;
		break;
	  case BDM_DEINIT:
		retval = bdm_deinit(minor);
		break;
	  case BDM_RESET_CHIP:
		retval = bdm_reset_chip(minor);		
		/* hardware reset on MCU - running state */
		break;
	  case BDM_RESTART_CHIP:	
	  	/* reset target and stops execution on first instruction fetch */
		retval = bdm_restart_chip(minor);
		break;
	  case BDM_STOP_CHIP:		
	  	/* stop running target (FREEZE) */
		retval = bdm_stop_chip(minor);
		break;
	  case BDM_STEP_CHIP:
		/* one step on target */
		bdm_step_chip(minor);	
		retval = BDM_NORETURN;
		break;
	  case BDM_GET_STATUS:
		retval = bdm_getstatus(minor);
		break;
	  case BDM_SPEED:			
		/* Change speed value */
		bdm_speed = (int) arg;
		retval = BDM_NORETURN;
		break;
	  case BDM_RELEASE_CHIP:	
		/* reset without bdm, so quit bdm mode */
		retval = bdm_release_chip(minor);
		break;
	  case BDM_DEBUG_LEVEL:
		/* change debug level 0,1,2 */
		if (arg < 3) {
			bdm_debug_level = (unsigned int) arg;	
			retval = BDM_NORETURN;
		} else {
			retval = -EINVAL;
		}
		break;
	  case BDM_GET_VERSION:
		/* read counter and return it to *arg */
		if (verify_area(VERIFY_WRITE, (int *) arg, sizeof(int))) {
			retval = -EINVAL;
		} else {
			put_fs_long(bdm_version, arg);
		}
		break;
	  case BDM_SENSECABLE:
		bdm_sense = arg;
		printk("bdm_ioctl: setting bdm_sense to %d\n", bdm_sense);
	    break;
	  default:
		retval = -EINVAL;
	}
	return retval;
}



static int
bdm_write(struct inode *inode, struct file *file, const char *buf, int count)
{
	unsigned int minor = MINOR(inode->i_rdev);
	short bdm_word;
	long ret;
	int written = 0;

	if (bdm_debug_level >= BDM_DEBUG_SOME) {
		printk("bdm_write minor %d len %d\n", minor, count);	 
	}
	if (verify_area(VERIFY_READ, buf, count))
		return -EINVAL;

	while (count > 0) {
		bdm_word = get_fs_word(buf);
		ret = bdm_ser_clk(minor, bdm_word, 0);
		if (bdm_debug_level >= BDM_DEBUG_ALL) {
			printk("\n write gesendet:  %x bekommen: %lx\n", bdm_word, 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;
				default:
			}
		}
		count -= 2;
		buf += 2;
		written += 2;
		if (!(written % 64)) {
			schedule();
		}
	}
	return written;
}

static int
bdm_read(struct inode *inode, struct file *file, char *buf, int count)
{
	unsigned int minor = MINOR(inode->i_rdev);
	short bdm_word = 0x0, wert;
	long ret;
	int read = 0, timeout = 0;

	if (bdm_debug_level >= BDM_DEBUG_SOME) {
		printk("bdm_read minor=%d len=%d\n", minor, count);	 
	}
	if (verify_area(VERIFY_WRITE, buf, count))
		return -EINVAL;

	while (count > 0) {
		ret = bdm_ser_clk(minor, bdm_word, 0);

		if (bdm_debug_level >= BDM_DEBUG_ALL) {
			printk("\n read gesendet:  %x bekommen: %lx\n", bdm_word, 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)
					return BDM_FAULT_RESPONSE;
			case BDM_FAULT_RESET:
			case BDM_FAULT_CABLE:
			case BDM_FAULT_POWER:
				return ret;
			default:
				wert = ret;
				put_fs_word(wert, buf);
				buf += 2;
				count -= 2;
				read += 2;
		}

	}
	return read;
}

static struct file_operations bdm_fops =
{
	NULL,						/* lseek               */
	bdm_read,					/* read                */
	bdm_write,					/* write               */
	NULL,						/* readdir             */
	NULL,						/* select              */
	bdm_ioctl,					/* ioctl               */
	NULL,						/* mmap                */
	bdm_open,					/* open                */
	bdm_release,				/* release             */
	NULL,						/* fsync               */
	NULL,						/* fasync              */
	NULL,						/* check_media_change  */
	NULL						/* revalidate          */
};

/* read status byte from statusport and translate into bitset */
static status_bitset
bdm_getstatus(unsigned int minor)
{
	unsigned char temp = inb(BDM_STAT(minor));

	if (bdm_debug_level >= BDM_DEBUG_SOME) {
		printk("bdm_getstatus minor=%d res=%#x\n", minor, temp);	 
	}
#ifdef PD_INTERFACE
	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) |
	(inb(BDM_CTRL(minor)) & BDMPD_RST_OUT ? BDM_TARGETRESET : 0);
#endif
#ifdef IDC_INTERFACE
	return (temp & BDM_FREEZE ? BDM_TARGETSTOPPED : 0);
#endif
}


/* restart chip and stop on first execution fetch */
static int
bdm_restart_chip(unsigned int minor)
{
#ifdef PD_INTERFACE
	unsigned bdm_Loop_Count = 0;
#endif

	if (bdm_debug_level >= BDM_DEBUG_SOME) {
		printk("bdm_restart_chip minor=%d\n", minor);
	}

#ifdef PD_INTERFACE
	outb(BDMPD_RST_OUT | BDMPD_DSCLK, BDM_CTRL(minor));
	bdm_delay(BDM_WAITCNT);
	outb(BDMPD_DSCLK, BDM_CTRL(minor));
	bdm_delay(BDM_WAITCNT);
	outb(BDMPD_STEP_OUT | BDMPD_DSCLK, BDM_CTRL(minor));
	for (bdm_Loop_Count = 0xffff; bdm_Loop_Count; bdm_Loop_Count--)
		if (inb(BDM_STAT(minor)) & BDMPD_FREEZE)
			break;
	if (!bdm_Loop_Count) {
		bdm_error(BDM_FAULT_RESPONSE);
		return BDM_FAULT_RESPONSE;
	} else {
		return BDM_NORETURN;
	}
#endif
#ifdef IDC_INTERFACE
   	outb(BDM_DSCLK, BDM_DATA(minor));
	bdm_delay(BDM_WAITCNT);
	return bdm_stop_chip(minor);
#endif

}


/* stop running target */
static int
bdm_stop_chip(unsigned int minor)
{
	short bdm_ctr;
	int return_value;

	if (bdm_debug_level >= BDM_DEBUG_SOME) {
		printk("bdm_stop_chip minor=%d\n", minor);
	}

	if (inb(BDM_STAT(minor)) & BDMPD_FREEZE) /* FIXME */
		return BDM_NORETURN;	/* target was already halted */

	return_value = BDM_FAULT_UNKNOWN;

#ifdef PD_INTERFACE
	outb(BDMPD_DSCLK | BDMPD_STEP_OUT, BDM_CTRL(minor));
#endif
#ifdef IDC_INTERFACE
	outb(BDM_DSCLK | BDM_RST_OUT, BDM_DATA(minor));
#endif

	for (bdm_ctr = BDM_WAITCNT; bdm_ctr; bdm_ctr--) {
		if ((inb(BDM_STAT(minor))) & BDMPD_FREEZE) { /* FIXME */
			return_value = BDM_NORETURN;	/* target is now halted      */
			break;
		}
		bdm_delay(1);
	}

#ifdef IDC_INTERFACE
	if (!bdm_ctr) {
		outb(BDM_DSCLK | BDM_RST_OUT | BDM_FORCE_BERR, BDM_DATA(minor));
 		bdm_delay(1);
 		for (bdm_ctr = BDM_WAITCNT; bdm_ctr; bdm_ctr--) {
 			if ((inb(BDM_STAT(minor))) & BDM_FREEZE) {
 				return_value = BDM_NORETURN;	/* target is now halted      */
 				break;
 			}
 			bdm_delay(1);
 		}
 	}
	outb(BDM_DSCLK | BDM_RST_OUT | BDM_STEP_OUT, BDM_DATA(minor));
#endif

	if (!bdm_ctr) {
		bdm_error(BDM_FAULT_RESPONSE);
		return BDM_FAULT_RESPONSE;
	}
	return return_value;
}



#define bdm_DataOut (BDM_GO_CMD & 1 ? BDM_DSI : 0)	/*FIXME*/


/* single stepping target mcu */
static int
bdm_step_chip(unsigned int minor)
{
	unsigned long tester;
	status_bitset bdm_stat = bdm_getstatus(minor);

	if (bdm_debug_level >= BDM_DEBUG_SOME) {
		printk("bdm_step_chip minor=%d\n", 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 = bdm_ser_clk(minor, BDM_GO_CMD, 1);
	switch (tester) {
		case BDM_FAULT_RESET:
		case BDM_FAULT_CABLE:
		case BDM_FAULT_POWER:
			return tester;
		default:
	}
	if (bdm_debug_level >= BDM_DEBUG_ALL) {
		printk("stepchip sent: %x got : %lx\n", BDM_GO_CMD, tester);
		printk("last answerbit : %x\n", !(inb(BDM_STAT(minor)) & BDMPD_DSO));
		/* FIXME */
	}
#ifdef PD_INTERFACE
	outb(BDMPD_DSCLK | BDMPD_STEP_OUT | bdm_DataOut, BDM_CTRL(minor));
	bdm_delay(bdm_speed + 1);
	outb(BDMPD_DSCLK | bdm_DataOut, BDM_CTRL(minor));
	bdm_delay(1);
	outb(bdm_DataOut, BDM_CTRL(minor));
	return BDM_NORETURN;
#endif
#ifdef IDC_INTERFACE
	outb(BDM_OE | BDM_STEP_OUT | bdm_DataOut | BDM_RST_OUT, BDM_DATA(minor));
	bdm_delay(bdm_speed + 1);
	outb(BDM_OE | bdm_DataOut | BDM_RST_OUT, BDM_DATA(minor));
	outb(BDM_DSCLK | BDM_RST_OUT, BDM_DATA(minor));
	return bdm_stop_chip(minor);
#endif
}

/* reset target chip without asserting freeze */
static int
bdm_release_chip(unsigned int minor)
{
	if (bdm_debug_level >= BDM_DEBUG_SOME) {
		printk("bdm_release_chip minor=%d\n", minor);
	}

#ifdef PD_INTERFACE
	outb(BDMPD_DSCLK | BDMPD_STEP_OUT, BDM_CTRL(minor));
	bdm_delay(10);
	outb(BDMPD_DSCLK | BDMPD_RST_OUT | BDMPD_STEP_OUT, BDM_CTRL(minor));
	bdm_delay(BDM_WAITCNT);
	outb(BDMPD_STEP_OUT, BDM_CTRL(minor));
	bdm_delay(10);
#endif
#ifdef IDC_INTERFACE
	outb(BDM_DSCLK | BDM_STEP_OUT, BDM_DATA(minor));
	bdm_delay(BDM_WAITCNT);
	outb(BDM_DSCLK | BDM_RST_OUT | BDM_STEP_OUT, BDM_DATA(minor));
	bdm_delay(10);
#endif

	return BDM_NORETURN;
}

/*
 * 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_reset_chip(unsigned int minor)
{
	if (bdm_debug_level >= BDM_DEBUG_SOME) {
		printk("bdm_reset_chip minor=%d\n", minor);
	}

#ifdef PD_INTERFACE
	/* this is suggested by Eric Norum */
	outb(BDMPD_DSCLK | BDMPD_RST_OUT, BDM_CTRL(minor));
	bdm_delay(BDM_WAITCNT);
	outb(BDMPD_DSCLK, BDM_CTRL(minor));
	bdm_delay(BDM_WAITCNT);
	outb(BDMPD_DSCLK | BDMPD_STEP_OUT, BDM_CTRL(minor));
#endif
#ifdef IDC_INTERFACE
	/* this is according to Scott Howard */
    outb(BDM_DSCLK | BDM_STEP_OUT, BDM_DATA(minor));
	bdm_delay(BDM_WAITCNT);
	outb(BDM_DSCLK | BDM_STEP_OUT | BDM_RST_OUT, BDM_DATA(minor));

	/*
	 *Tom Hoover's Error or Optimization? has to be checked
	 */
	/*
	outb(BDM_DSCLK, BDM_DATA(minor));
	bdm_delay(BDM_WAITCNT);
	bdm_stop_chip(minor);
	 */
#endif
	return BDM_NORETURN;
}


/* slow down host for not overrunning target */
static void
bdm_delay(int counter)
{
	while (counter--) {
		asm volatile ("nop");
	}
}


/* serial software protokoll for bdm-pd-interface */
static long
bdm_ser_clk(unsigned int minor, short bdm_value, int delay_bits)
{
	unsigned long ShiftRegister;
	unsigned char DataOut;
	unsigned counter;
	unsigned bdm_stat = bdm_getstatus(minor);

	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;
	}
	counter = 32 - BITS - delay_bits;
	while (counter--) {
		DataOut = ((ShiftRegister & 0x80000000) ? BDM_DSI : 0);
		/*if 1 then dsi=1*/

		if (bdm_debug_level >= BDM_DEBUG_ALL) {
			printk("%c", DataOut ? '1' : '0');
		}
		ShiftRegister <<= 1;	/*shift one bit left*/
		if (!(inb(BDM_STAT(minor)) & BDMPD_DSO)) { /* FIXME */
			ShiftRegister |= 1;	/*put 1 on 0.bit */
			if (bdm_debug_level >= BDM_DEBUG_ALL) {
				printk("1-");
			}
		} else {
			if (bdm_debug_level >= BDM_DEBUG_ALL) {
				printk("0-");
			}
		}
#ifdef PD_INTERFACE
		outb(DataOut | BDMPD_STEP_OUT | BDMPD_DSCLK, BDM_CTRL(minor));
		bdm_delay(bdm_speed + 1);
		outb(DataOut | BDMPD_STEP_OUT, BDM_CTRL(minor));
		bdm_delay(2 * bdm_speed / 3 + 1);
#endif
#ifdef IDC_INTERFACE
		outb(DataOut | BDM_RST_OUT | BDM_OE | BDM_STEP_OUT, 
			BDM_DATA(minor));
		bdm_delay(bdm_speed + 1);
		outb(DataOut | BDM_RST_OUT | BDM_OE | BDM_STEP_OUT | BDM_DSCLK, 
			BDM_DATA(minor));
		bdm_delay((bdm_speed >> 1) + 1);
#endif
	}
#ifdef IDC_INTERFACE
	outb(BDM_DSCLK | BDM_STEP_OUT | BDM_RST_OUT, BDM_DATA(minor));
#endif
	return ShiftRegister;
}



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;
		}
	}
}

int
init_module(void)
{

	printk("BDM init_module\n   %s\n   %s\n   %s\n",
		   "$RCSfile: bdm.c,v $", "$Revision: 1.21 $", "$Date: 1996/07/21 12:16:08 $");
		   /*"$Id: bdm.c,v 1.21 1996/07/21 12:16:08 magin Exp $", */
	printk("   Version %s\n   Compiled at %s %s\n",
#ifdef PD_INTERFACE
		   "PD-interface",
#endif
#ifdef IDC_INTERFACE
		   "ICD-interface with P&E-Cable", 
#endif
		   __DATE__, __TIME__);
	if (register_chrdev(BDM_MAJOR_NUMBER, "bdm", &bdm_fops)) {
		printk("Unable to get major number for BDM driver\n");
		return -EIO;
	}
	printk("BDM driver succesfully registered !!\n");
	return 0;
}

void
cleanup_module(void)
{
	if (unregister_chrdev(BDM_MAJOR_NUMBER, "bdm") != 0)
		printk("BDM cleanup: Unregister failed\n");
	else
		printk("BDM cleanup: Unregister  O.K.\n");
}
