/*
 * Motorola Background Debug Mode Driver
 * Copyright (C) 1995  W. Eric Norum
 * Copyright (C) 1998-2007  Chris Johns
 *
 * Based on:
 *  1. `A Background Debug Mode Driver Package for Motorola's
 *     16- and 32-Bit Microcontrollers', Scott Howard, Motorola
 *     Canada, 1993.
 *  2. `Linux device driver for public domain BDM Interface',
 *     M. Schraut, Technische Universitaet Muenchen, Lehrstuhl
 *     fuer Prozessrechner, 1995.
 * 
 * Extended to support the ColdFire BDM interface using the P&E
 * module which comes with the EVB. Currently only tested with the
 * 5206 (5V) device.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 * 
 * W. Eric Norum
 * Saskatchewan Accelerator Laboratory
 * University of Saskatchewan
 * 107 North Road
 * Saskatoon, Saskatchewan, CANADA
 * S7N 5C6
 * 
 * eric@skatter.usask.ca
 *
 * Coldfire support by:
 * Chris Johns
 * chris@users.sourceforge.net
 *
 * ccj@acm.org
 *
 * 19/1/1999 -- Greg Ungerer (gerg@moreton.com.au)
 *     Added support for more recent LINUX kernels 2.2.0+
 *
 * 22/10/1999 -- CCJ (ccj@acm.org)
 *     Remove the OS specific parts to allow for easier support for
 *     different OS's. This is based on the work done by
 *     David McCullough (davidm@stallion.oz.au) for the SCO port.
 *
 * 31/05/2000 - CCJ (ccj@acm.org)
 *     Merged in the ICD interface, and renamed the Eric interface
 *     to the PD interface.
 *
 * 01/09/2000 - ASA (aaganichev@hypercom.com)
 *     Fixed ICD and PD support basing on original driver code, fix ICD name
 *     misspelling.
 *
 * 14/11/2000 - James Housley <jim@thehousleys.net>
 *              Greg Tunnock (gtunnock@redfernnetworks.com)
 *     Port to FreeBSD.
 *
 * 02/02/2001 - KJO (vac4050@cae597.rsc.raytheon.com)
 *     Reduced time delays in some cpu32_icd_xxx() functions to improve
 *     performance of cpu32 ICD driver and speed up downloads.
 *
 * 12/12/2001 - Thomas Andrew (tandrews@mindspring.co.za)
 *     Change to stop getting "Invalid instruction (0x1FFFF) back from the
 *     ICD interface.
 */

/*
 * Declarations common to kernel and user code
 */
#include "bdm.h"

/*
 ************************************************************************
 *     BDM driver control structure                                     *
 ************************************************************************
 */

struct BDM {
  /*
   * Device name, processor and interface type
   */
  char name[20];
  int  processor;
  int  interface;

  /*
   * Device I/O ports
   */
  int  exists;
  int  portsAreMine;
  int  portBase;
  int  dataPort;
  int  statusPort;
  int  controlPort;
  int  usbDev;

  /*
   * Control debugging messages
   */
  int  debugFlag;

  /*
   * Device is exclusive-use
   */
  int  isOpen;

  /*
   * Serial I/O delay timer
   */
  int  delayTimer;

  /*
   * I/O buffer
   */
  char         ioBuffer[512];
  unsigned int readValue;
  
  /*
   * Interface specific handlers
   */
  int (*get_status)(struct BDM *self);
  int (*init_hardware) (struct BDM *self);
  int (*serial_clock) (struct BDM *self, unsigned short wval, int holdback);
  int (*gen_bus_error) (struct BDM *self);
  int (*restart_chip) (struct BDM *self);
  int (*release_chip) (struct BDM *self);
  int (*reset_chip) (struct BDM *self);
  int (*stop_chip) (struct BDM *self);
  int (*run_chip) (struct BDM *self);
  int (*step_chip) (struct BDM *self);
  int (*fill_buf) (struct BDM *self, int count);
  int (*send_buf) (struct BDM *self, int count);
  int (*read_sysreg) (struct BDM *self, struct BDMioctl *ioc, int mode);
  int (*read_proreg) (struct BDM *self, struct BDMioctl *ioc);
  int (*read_long_word) (struct BDM *self, struct BDMioctl *ioc);
  int (*read_word) (struct BDM *self, struct BDMioctl *ioc);
  int (*read_byte) (struct BDM *self, struct BDMioctl *ioc);
  int (*write_sysreg) (struct BDM *self, struct BDMioctl *ioc, int mode);
  int (*write_proreg) (struct BDM *self, struct BDMioctl *ioc);
  int (*write_long_word) (struct BDM *self, struct BDMioctl *ioc);
  int (*write_word) (struct BDM *self, struct BDMioctl *ioc);
  int (*write_byte) (struct BDM *self, struct BDMioctl *ioc);

  /*
   * Some system registers are write only so we need to shadow them
   */
  unsigned long shadow_sysreg[BDM_MAX_SYSREG];

  /*
   * Coldfire processors have different versions of the debug module.
   * These also require special operations to occur.
   */
  int           cf_debug_ver;
  int           cf_use_pst;
  
  /*
   * Need to get the status from the csr, how-ever the status bits are a
   * once read then clear so we need logic to store this state to know
   * the status.
   */
  int           cf_running;
  unsigned long cf_csr;
  
  /*
   * Revision D BDM hardware does not have a bit to mask interrupts so
   * we must save the SR when we step and then restore when we go.
   */
  unsigned long cf_sr_mask_cache;
  int           cf_sr_masked;

  /*
   * Address for the current transfer. The TBLCF pod always
   * takes the address rather than streaming. This should be changed.
   */
  unsigned long address;

#ifdef BDM_BIT_BASH_PORT
  
  int (*bit_bash) (struct BDM *self, unsigned short mask, unsigned short value);

  /*
   * The current state of the bit bash bits. Easier than figuring out
   * if the bit should be on or off by reading the PC port. I am sure
   * this could be done, but I am not a PC parallel port expert.
   */

  unsigned short bit_bash_bits;

#endif
};

/*
 ************************************************************************
 *     System Register Modes                                            *
 ************************************************************************
 */
#define BDM_SYS_REG_MODE_MAPPED   (0)
#define BDM_SYS_REG_MODE_CONTROL  (1)
#define BDM_SYS_REG_MODE_DEBUG    (2)

/*
 ************************************************************************
 *     Common Functions                                                 *
 ************************************************************************
 */

static int bdmDrvGetStatus (struct BDM *self);
static int bdmDrvInitHardware (struct BDM *self);
static int bdmDrvRestartChip (struct BDM *self);
static int bdmDrvReleaseChip (struct BDM *self);
static int bdmDrvResetChip (struct BDM *self);
static int bdmDrvStopChip (struct BDM *self);
static int bdmDrvSerialClock (struct BDM *self, unsigned short wval, int holdback);
static int bdmDrvGenerateBusError (struct BDM *self);
static int bdmDrvStepChip (struct BDM *self);
static int bdmDrvSendCommandTillTargetReady (struct BDM *self,
                                          unsigned short command);
static int bdmDrvFillBuf (struct BDM *self, int count);
static int bdmDrvSendBuf (struct BDM *self, int count);
static int bdmDrvGo (struct BDM *self);
static int bdmDrvReadSystemRegister (struct BDM *self, struct BDMioctl *ioc, int mode);
static int bdmDrvReadProcessorRegister (struct BDM *self, struct BDMioctl *ioc);
static int bdmDrvReadLongWord (struct BDM *self, struct BDMioctl *ioc);
static int bdmDrvReadWord (struct BDM *self, struct BDMioctl *ioc);
static int bdmDrvReadByte (struct BDM *self, struct BDMioctl *ioc);
static int bdmDrvWriteSystemRegister (struct BDM *self, struct BDMioctl *ioc, int mode);
static int bdmDrvWriteProcessorRegister (struct BDM *self, struct BDMioctl *ioc);
static int bdmDrvWriteLongWord (struct BDM *self, struct BDMioctl *ioc);
static int bdmDrvWriteWord (struct BDM *self, struct BDMioctl *ioc);
static int bdmDrvWriteByte (struct BDM *self, struct BDMioctl *ioc);

/*
 * Common processor defines.
 */

/*
 * CPU Generated Messages.
 */

#define CGM_FAILURE         1       /* bit 16, s/c bit */
#define CGM_CMD_OK          0xffff
#define CGM_NOT_READY       0x0000
#define CGM_BUS_ERROR       0x0001
#define CGM_ILLEGAL_CMD     0xffff

/*
 * Command codes for BDM interface
 */

#define BDM_RREG_CMD        0x2180  /* CPU32, Coldfire */
#define BDM_WREG_CMD        0x2080  /* CPU32, Coldfire */
#define BDM_RSREG_CMD       0x2580  /* CPU32, Coldfire */
#define BDM_WSREG_CMD       0x2480  /* CPU32, Coldfire */
#define BDM_READ_CMD        0x1900  /* CPU32, Coldfire */
#define BDM_WRITE_CMD       0x1800  /* CPU32, Coldfire */
#define BDM_DUMP_CMD        0x1d00  /* CPU32, Coldfire */
#define BDM_FILL_CMD        0x1c00  /* CPU32, Coldfire */
#define BDM_GO_CMD          0x0c00  /* CPU32, Coldfire */
#define BDM_CALL_CMD        0x0800  /* CPU32 */
#define BDM_RST_CMD         0x0400  /* CPU32, Coldfire */
#define BDM_NOP_CMD         0x0000  /* CPU32, Coldfire */
#define BDM_RCREG_CMD       0x2980  /* Coldfire */
#define BDM_WCREG_CMD       0x2880  /* Coldfire */
#define BDM_RDMREG_CMD      0x2d80  /* Coldfire */
#define BDM_WDMREG_CMD      0x2c80  /* Coldfire */
#define BDM_FORCED_TA_CMD   0x0002  /* Coldfire version D and higher */

/*
 * Operand size for BDM_READ_CMD/BDM_WRITE_CMD
 */

#define BDM_SIZE_BYTE       0x0000
#define BDM_SIZE_WORD       0x0040
#define BDM_SIZE_LONG       0x0080

/*
 * Address/Data field
 */

#define BDM_ADDR_NOT_DATA   0x0004

/*
 * Per-device data.
 * Allocate a struct for each port for each interface.
 */
static struct BDM bdm_device_info[BDM_NUM_OF_MINORS];

/*
 * Does this host require swapping.
 */
static int mustSwap;

/*
 ************************************************************************
 *     Genric Bit Bash Functions Decls                                  *
 ************************************************************************
 */

static int bdmBitBashSendCommandTillTargetReady (struct BDM *self,
                                                 unsigned short command);
static int bdmBitBashFillBuf (struct BDM *self, int count);
static int bdmBitBashSendBuf (struct BDM *self, int count);
static int bdmbitBashFetchWord (struct BDM *self, unsigned short *sp);
static int bdmBitBashReadProcessorRegister (struct BDM *self,
                                            struct BDMioctl *ioc);
static int bdmBitBashReadLongWord (struct BDM *self, struct BDMioctl *ioc);
static int bdmBitBashReadWord (struct BDM *self, struct BDMioctl *ioc);
static int bdmBitBashReadByte (struct BDM *self, struct BDMioctl *ioc);
static int bdmBitBashWriteProcessorRegister (struct BDM *self,
                                             struct BDMioctl *ioc);
static int bdmBitBashWriteLongWord (struct BDM *self,
                                    struct BDMioctl *ioc);
static int bdmBitBashWriteWord (struct BDM *self, struct BDMioctl *ioc);
static int bdmBitBashWriteByte (struct BDM *self, struct BDMioctl *ioc);


/*
 ************************************************************************
 *     CPU32 for PD/ICD interface support routines                      *
 ************************************************************************
 */

#include "bdm-cpu32.c"

/*
 ************************************************************************
 *     Coldfire P&E support routines                                    *
 ************************************************************************
 */

#include "bdm-cf-pe.c"

/*
 ************************************************************************
 *     Coldfire P&E support routines                                    *
 ************************************************************************
 */

#if BDM_TBLCF_USB
#include "bdm-tblcf.c"
#endif

/*
 ************************************************************************
 *     Genric Bit Bash Functions                                        *
 ************************************************************************
 */

/*
 * Loop till target I/O operation completes
 */
static int
bdmBitBashSendCommandTillTargetReady (struct BDM *self, unsigned short command)
{
  int err;
  int timeout = 0;

  /*
   * If no response is returned hit the bus error line and
   * then wait for the bus error response.
   */
  for (;;) {
    err = bdmDrvSerialClock (self, command, 0);
    if (err)
      return err;
    if ((self->readValue & 0x10000) == 0)
      return 0;
    ++timeout;
    if (timeout == 5) {
      /*
       * Should get a bus error response back.
       */
      bdmDrvGenerateBusError (self);
    }
    if (timeout > 6) {
      return BDM_FAULT_RESPONSE;
    }
  }
}

/*
 * Fill I/O buffer with data from target
 */
static int
bdmBitBashFillBuf (struct BDM *self, int count)
{
  unsigned short *sp = (unsigned short *)self->ioBuffer;
  int cmd;
  int err;

  if (self->debugFlag)
    PRINTF ("bdmBitBashFillBuf - count:%d\n", count);
  
  if (count == 0)
    return 0;
  
  if (count >= 4)
    cmd = BDM_DUMP_CMD | BDM_SIZE_LONG;
  else if (count >= 2)
    cmd = BDM_DUMP_CMD | BDM_SIZE_WORD;
  else
    cmd = BDM_DUMP_CMD | BDM_SIZE_BYTE;
  
  err = bdmDrvSerialClock (self, cmd, 0);
  if (err)
    return err;

  for (;;) {
    switch (count) {
      case 1:
        err = bdmBitBashSendCommandTillTargetReady (self, BDM_NOP_CMD);
        if (err)
          return err;
        *(unsigned char *)sp = self->readValue;
        return 0;

      case 2:
        err = bdmBitBashSendCommandTillTargetReady (self, BDM_NOP_CMD);
        if (err)
          return err;
        *sp = self->readValue;
        return 0;

      case 3:
        err = bdmBitBashSendCommandTillTargetReady (self, BDM_DUMP_CMD | BDM_SIZE_BYTE);
        if (err)
          return err;
        *sp++ = self->readValue;
        count -= 2;
        break;

      case 4:
        err = bdmBitBashSendCommandTillTargetReady (self, BDM_NOP_CMD);
        if (err)
          return err;
        *sp++ = self->readValue;
        err = bdmDrvSerialClock (self, BDM_NOP_CMD, 0);
        if (err)
          return err;
        *sp = self->readValue;
        return 0;

      case 5:
        err = bdmBitBashSendCommandTillTargetReady (self, BDM_NOP_CMD);
        if (err)
          return err;
        *sp++ = self->readValue;
        err = bdmBitBashSendCommandTillTargetReady (self, BDM_DUMP_CMD | BDM_SIZE_BYTE);
        if (err)
          return err;
        *sp++ = self->readValue;
        count -= 4;
        break;

      case 6:
      case 7:
        err = bdmBitBashSendCommandTillTargetReady (self, BDM_NOP_CMD);
        if (err)
          return err;
        *sp++ = self->readValue;
        err = bdmBitBashSendCommandTillTargetReady (self, BDM_DUMP_CMD | BDM_SIZE_WORD);
        if (err)
          return err;
        *sp++ = self->readValue;
        count -= 4;
        break;

      default:
        err = bdmBitBashSendCommandTillTargetReady (self, BDM_NOP_CMD);
        if (err)
          return err;
        *sp++ = self->readValue;
        err = bdmBitBashSendCommandTillTargetReady (self, BDM_DUMP_CMD | BDM_SIZE_LONG);
        if (err)
          return err;
        *sp++ = self->readValue;
        count -= 4;
        break;
    }
  }
}

/*
 * Send contents of I/O buffer to target
 */
static int
bdmBitBashSendBuf (struct BDM *self, int count)
{
  unsigned short *sp = (unsigned short *)self->ioBuffer;
  int cmd;
  int err;

  if (self->debugFlag)
    PRINTF ("bdmBitBashSendBuf - count:%d\n", count);

  if (count == 0)
    return 0;
  
  for (;;) {
    if (count >= 4)
      cmd = BDM_FILL_CMD | BDM_SIZE_LONG;
    else if (count >= 2)
      cmd = BDM_FILL_CMD | BDM_SIZE_WORD;
    else if (count >= 1)
      cmd = BDM_FILL_CMD | BDM_SIZE_BYTE;
    else
      cmd = BDM_NOP_CMD;
    err = bdmBitBashSendCommandTillTargetReady (self, cmd);
    if (err)
      return err;
    if (count >= 4) {
      err = bdmDrvSerialClock (self, *sp++, 0);
      if (err)
        return err;
      err = bdmDrvSerialClock (self, *sp++, 0);
      if (err)
        return err;
      count -= 4;
    }
    else if (count >= 2) {
      err = bdmDrvSerialClock (self, *sp++, 0);
      if (err)
        return err;
      count -= 2;
    }
    else if (count >= 1) {
      err = bdmDrvSerialClock (self, *(unsigned char *)sp, 0);
      if (err)
        return err;
      count -= 1;
    }
    else {
      return 0;
    }
  }
}

/*
 * Get a word from the target
 */
int
bdmBitBashFetchWord (struct BDM *self, unsigned short *sp)
{
  int err;

  err = bdmBitBashSendCommandTillTargetReady (self, BDM_NOP_CMD);
  if (err)
    return err;
  *sp = self->readValue;
  return 0;
}

/*
 * Read processor register
 */
static int
bdmBitBashReadProcessorRegister (struct BDM *self, struct BDMioctl *ioc)
{
  int err;
  unsigned short msw, lsw;

  if (((err = bdmDrvSerialClock (self, BDM_RREG_CMD | (ioc->address & 0xF), 0)) != 0) ||
      ((err = bdmBitBashFetchWord (self, &msw)) != 0) ||
      ((err = bdmBitBashFetchWord (self, &lsw)) != 0)) {
    if (self->debugFlag)
      PRINTF ("bdmBitBashReadProcessorRegister - reg:0x%02x, failed err=%d\n",
              ioc->address & 0xF, err);
    return err;
  }
  
  ioc->value = (msw << 16) | lsw;
  
  if (self->debugFlag)
    PRINTF ("bdmBitBashReadProcessorRegister - reg:0x%02x = 0x%08x\n",
            ioc->address & 0xF, ioc->value);
  return 0;
}

/*
 * Read a long word from memory
 */
static int
bdmBitBashReadLongWord (struct BDM *self, struct BDMioctl *ioc)
{
  int err;
  unsigned short msw, lsw;

  if (((err = bdmDrvSerialClock (self, BDM_READ_CMD | BDM_SIZE_LONG, 0)) != 0) ||
      ((err = bdmDrvSerialClock (self, ioc->address >> 16, 0)) != 0) ||
      ((err = bdmDrvSerialClock (self, ioc->address, 0)) != 0) ||
      ((err = bdmBitBashFetchWord (self, &msw)) != 0) ||
      ((err = bdmBitBashFetchWord (self, &lsw)) != 0)) {
    if (self->debugFlag)
      PRINTF ("bdmBitBashReadLongWord : *0x%08x failed, err=%d\n", ioc->address, err);
    return err;
  }
  
  ioc->value = (msw << 16) | lsw;

  if (self->debugFlag)
    PRINTF ("bdmBitBashReadLongWord : *0x%08x = 0x%08x\n", ioc->address, ioc->value);

  return 0;
}

/*
 * Read a word from memory
 */
static int
bdmBitBashReadWord (struct BDM *self, struct BDMioctl *ioc)
{
  int            err;
  unsigned short w;

  if (((err = bdmDrvSerialClock (self, BDM_READ_CMD | BDM_SIZE_WORD, 0)) != 0) ||
      ((err = bdmDrvSerialClock (self, ioc->address >> 16, 0)) != 0) ||
      ((err = bdmDrvSerialClock (self, ioc->address, 0)) != 0) ||
      ((err = bdmBitBashFetchWord (self, &w)) != 0)) {
    if (self->debugFlag)
      PRINTF ("bdmBitBashReadWord : *0x%08x failed, err=%d\n", ioc->address, err);
    return err;
  }
  
  ioc->value = w;
  
  if (self->debugFlag)
    PRINTF ("bdmBitBashReadWord : *0x%08x = 0x%04x\n", ioc->address, (ioc->value & 0xffff));

  return 0;
}

/*
 * Read a byte from memory
 */
static int
bdmBitBashReadByte (struct BDM *self, struct BDMioctl *ioc)
{
  int            err;
  unsigned short w;

  if (((err = bdmDrvSerialClock (self, BDM_READ_CMD | BDM_SIZE_BYTE, 0)) != 0) ||
      ((err = bdmDrvSerialClock (self, ioc->address >> 16, 0)) != 0) ||
      ((err = bdmDrvSerialClock (self, ioc->address, 0)) != 0) ||
      ((err = bdmBitBashFetchWord (self, &w)) != 0)){
  if (self->debugFlag)
    PRINTF ("bdmBitBashReadByte : *0x%08x failed, err=%d\n", ioc->address, err);
    
  return err;
  }
  
  ioc->value = w;
  
  if (self->debugFlag)
    PRINTF ("bdmBitBashReadByte : *0x%08x = 0x%02x\n", ioc->address, (ioc->value & 0xff));
  return 0;
}

/*
 * Write processor register
 */
static int
bdmBitBashWriteProcessorRegister (struct BDM *self, struct BDMioctl *ioc)
{
  int err;

  if (self->debugFlag)
    PRINTF ("bdmBitBashWriteProcessorRegister - reg:%d, val:0x%08x\n",
            ioc->address & 0xF, ioc->value);

  if (((err = bdmDrvSerialClock (self, BDM_WREG_CMD | (ioc->address & 0xF), 0)) != 0) ||
      ((err = bdmDrvSerialClock (self, ioc->value >> 16, 0)) != 0) ||
      ((err = bdmDrvSerialClock (self, ioc->value, 0)) != 0))
    return err;
  return 0;
}

/*
 * Write a long word to memory
 */
static int
bdmBitBashWriteLongWord (struct BDM *self, struct BDMioctl *ioc)
{
  int err;
  unsigned short w;

  if (self->debugFlag)
    PRINTF ("bdmBitBashWriteLongWord : 0x%08x = 0x%08x\n", ioc->address, ioc->value);

  if (((err = bdmDrvSerialClock (self, BDM_WRITE_CMD | BDM_SIZE_LONG, 0)) != 0) ||
      ((err = bdmDrvSerialClock (self, ioc->address >> 16, 0)) != 0) ||
      ((err = bdmDrvSerialClock (self, ioc->address, 0)) != 0) ||
      ((err = bdmDrvSerialClock (self, ioc->value >> 16, 0)) != 0) ||
      ((err = bdmDrvSerialClock (self, ioc->value, 0)) != 0) ||
      ((err = bdmBitBashFetchWord (self, &w)) != 0))
    return err;
  return 0;
}

/*
 * Write a word to memory
 */
static int
bdmBitBashWriteWord (struct BDM *self, struct BDMioctl *ioc)
{
  int err;
  unsigned short w;

  if (self->debugFlag)
    PRINTF ("bdmBitBashWriteWord : 0x%08x = 0x%04x\n", ioc->address, (ioc->value & 0xffff));

  if (((err = bdmDrvSerialClock (self, BDM_WRITE_CMD | BDM_SIZE_WORD, 0)) != 0) ||
      ((err = bdmDrvSerialClock (self, ioc->address >> 16, 0)) != 0) ||
      ((err = bdmDrvSerialClock (self, ioc->address, 0)) != 0) ||
      ((err = bdmDrvSerialClock (self, ioc->value, 0)) != 0) ||
      ((err = bdmBitBashFetchWord (self, &w)) != 0))
    return err;
  return 0;
}

/*
 * Write a byte to memory
 */
static int
bdmBitBashWriteByte (struct BDM *self, struct BDMioctl *ioc)
{
  int err;
  unsigned short w;

  if (self->debugFlag)
    PRINTF ("bdmBitBashWriteByte : 0x%08x = 0x%02x\n", ioc->address, (ioc->value & 0xff));

  if (((err = bdmDrvSerialClock (self, BDM_WRITE_CMD | BDM_SIZE_BYTE, 0)) != 0) ||
      ((err = bdmDrvSerialClock (self, ioc->address >> 16, 0)) != 0) ||
      ((err = bdmDrvSerialClock (self, ioc->address, 0)) != 0) ||
      ((err = bdmDrvSerialClock (self, ioc->value, 0)) != 0) ||
      ((err = bdmBitBashFetchWord (self, &w)) != 0))
    return err;
  return 0;
}

/*
 ************************************************************************
 *     Common Functions                                                 *
 ************************************************************************
 */

/*
 * Get target status
 */
static int
bdmDrvGetStatus (struct BDM *self)
{
  return (self->get_status) (self);
}

/*
 * Hardware initialization
 */
static int
bdmDrvInitHardware (struct BDM *self)
{
  return (self->init_hardware) (self);
}

/*
 * Clock a word to/from the target
 */
static int
bdmDrvSerialClock (struct BDM *self, unsigned short wval, int holdback)
{
  return (self->serial_clock) (self, wval, holdback);
}

/*
 * Generate a bus error on the target.
 */
static int
bdmDrvGenerateBusError (struct BDM *self)
{
  return (self->gen_bus_error) (self);
}
  
/*
 * Restart chip and stop on the first instruction fetch
 */
static int
bdmDrvRestartChip (struct BDM *self)
{
  return (self->restart_chip) (self);
}
  
/*
 * Restart chip and disable background debugging mode
 */
static int
bdmDrvReleaseChip (struct BDM *self)
{
  return (self->release_chip) (self);
}

/*
 * Reset chip, enable background debugging mode, halt on first fetch.
 *
 */
static int
bdmDrvResetChip (struct BDM *self)
{
  return (self->reset_chip) (self);
}

/*
 * Force the target into background debugging mode
 */
static int
bdmDrvStopChip (struct BDM *self)
{
  return (self->stop_chip) (self);
}

/*
 * Restart target execution
 */
static int
bdmDrvGo (struct BDM *self)
{
  return (self->run_chip) (self);
}

/*
 * Make the target execute a single instruction and
 * reenter background debugging mode
 */
static int
bdmDrvStepChip (struct BDM *self)
{
  return (self->step_chip) (self);
}
  
/*
 * Fill I/O buffer with data from target
 */
static int
bdmDrvFillBuf (struct BDM *self, int count)
{
  int err = (self->fill_buf) (self, count);
  if (mustSwap)
  {
    char *p = self->ioBuffer;
    int  i;
    char c;
    for (i = 0; i < (count - 1); i += 2)
    {
      c = *p;
      *p = *(p + 1);
      p++;
      *p = c;
      p++;
    }
  }
  return err;
}

/*
 * Send contents of I/O buffer to target
 */
static int
bdmDrvSendBuf (struct BDM *self, int count)
{
  if (mustSwap)
  {
    char *p = self->ioBuffer;
    int  i;
    char c;
    for (i = 0; i < (count - 1); i += 2)
    {
      c = *p;
      *p = *(p + 1);
      p++;
      *p = c;
      p++;
    }
  }
  return (self->send_buf) (self, count);
}

/*
 * Read system register
 */
static int
bdmDrvReadSystemRegister (struct BDM *self, struct BDMioctl *ioc, int mode)
{
  return (self->read_sysreg) (self, ioc, mode);
}

/*
 * Read processor register
 */
static int
bdmDrvReadProcessorRegister (struct BDM *self, struct BDMioctl *ioc)
{
  return (self->read_proreg) (self, ioc);
}

/*
 * Read a long word from memory
 */
static int
bdmDrvReadLongWord (struct BDM *self, struct BDMioctl *ioc)
{
  return (self->read_long_word) (self, ioc);
}

/*
 * Read a word from memory
 */
static int
bdmDrvReadWord (struct BDM *self, struct BDMioctl *ioc)
{
  return (self->read_word) (self, ioc);
}

/*
 * Read a byte from memory
 */
static int
bdmDrvReadByte (struct BDM *self, struct BDMioctl *ioc)
{
  return (self->read_byte) (self, ioc);
}

/*
 * Write system register
 */
static int
bdmDrvWriteSystemRegister (struct BDM *self, struct BDMioctl *ioc, int mode)
{
  return (self->write_sysreg) (self, ioc, mode);
}

/*
 * Write processor register
 */
static int
bdmDrvWriteProcessorRegister (struct BDM *self, struct BDMioctl *ioc)
{
  return (self->write_proreg) (self, ioc);
}

/*
 * Write a long word to memory
 */
static int
bdmDrvWriteLongWord (struct BDM *self, struct BDMioctl *ioc)
{
  return (self->write_long_word) (self, ioc);
}

/*
 * Write a word to memory
 */
static int
bdmDrvWriteWord (struct BDM *self, struct BDMioctl *ioc)
{
  return (self->write_word) (self, ioc);
}

/*
 * Write a byte to memory
 */
static int
bdmDrvWriteByte (struct BDM *self, struct BDMioctl *ioc)
{
  return (self->write_byte) (self, ioc);
}

#ifdef BDM_BIT_BASH_PORT

/*
 * Bit Bash the BDM signals. This is provided to allow low level
 * bit bashing support for getting new BDM or target hardware
 * working.
 */
static int
bdmDrvBitBash (struct BDM *self, struct BDMioctl *ioc)
{
  if (self->bit_bash)
    return (self->bit_bash) (self, ioc->value >> 16, ioc->value & 0xffff);
  return -1;
}

#endif

/*
 ************************************************************************
 *      Driver Functions which are common to the different OS's         *
 ************************************************************************
 */

/*
 * Requires the OS to define `os_claim_io_ports' and
 * `os_release_io_ports'. If your OS does not support this,
 * do nothing and return an error code of 0.
 */

static int
bdm_open (unsigned int minor)
{  
  struct BDM *self;
  int status, err = 0;
  
  union {
    char     c[4];
    uint32_t l;
  } un;

  if (minor >= (sizeof bdm_device_info / sizeof bdm_device_info[0]))
    return ENODEV;

  /*
   * Determine what byte-swapping we'll need to do
   */
  if (sizeof un.l != sizeof un.c) {
    PRINTF ("bdm_open -- host machine sizeof (uint32_t) != sizeof (char[4])");
    return ENODEV;
  }
  if ((sizeof (uint32_t) != 4) ||
      (sizeof (short) != 2) ||
      (sizeof (char) != 1)) {
    PRINTF ("bdm_open -- host machine sizeof not appropriate");
    return ENODEV;
  }
  un.c[0] = 0x01;
  un.c[1] = 0x02;
  un.c[2] = 0x03;
  un.c[3] = 0x04;
  if (un.l == 0x01020304) {
    mustSwap = 0;
  }
  else if (un.l == 0x04030201) {
    mustSwap = 1;
  }
  else {
    PRINTF ("bdm_open -- host machine has peculiar byte ordering");
    return ENODEV;
  }
  
  self = &bdm_device_info[minor];

  if (self->debugFlag > 0)
    PRINTF ("bdm_open -- minor %d\n", minor);

  if (!self->exists)
    return ENODEV;
  if (self->isOpen)
    return EBUSY;

  /*
   * Ask the OS to try and claim the port.
   */

  err = os_claim_io_ports (self->name, self->portBase, 4);

  if (err)
    return err;
  
  self->portsAreMine = 1;
  
  if (self->debugFlag)
    PRINTF ("bdm_open -- %s using port 0x%x.\n", self->name, self->portBase);
  
  /*
   * Set up the driver.
   */

  status = bdmDrvInitHardware (self);
  
  if (status & BDM_TARGETNC)
    err = BDM_FAULT_CABLE;
  else if (status & BDM_TARGETPOWER)
    err = BDM_FAULT_POWER;

  if (err) {
    os_release_io_ports (self->portBase, 4);
    self->portsAreMine = 0;
  }
  else {
    os_lock_module ();
    self->isOpen = 1;
  }

  if (self->debugFlag)
    PRINTF ("BDMopen return %d, delayTimer %d\n", err, self->delayTimer);
  
  return err;
}

/*
 * The old Linux driver set the control port back to the value it
 * had when the device was opened.  This is good when the port is
 * being shared between the BDM interface and a printer, but has
 * the unfortunate side effect of freezing the target.  This makes
 * it inconvenient to use the debugger, or a downloader program,
 * since the target freezes as soon as the debugger, or downloader,
 * exits.
 */
static int
bdm_close (unsigned int minor)
{
  struct BDM *self = &bdm_device_info[minor];
  
  if (self->isOpen) {
    bdmDrvReleaseChip (self);
    if (self->exists && self->portsAreMine)
      os_release_io_ports (self->portBase, 4);
    self->portsAreMine = 0;
    self->isOpen = 0;
    os_unlock_module ();
  }

  if (self->debugFlag)
    PRINTF ("BDM released\n");

  return 0;
}

static int
bdm_ioctl (unsigned int minor, unsigned int cmd, unsigned long arg)
{
  struct BDM      *self = &bdm_device_info[minor];
  struct BDMioctl ioc;
  int             iarg;
  int             err = 0;

  /*
   * Pick up the argument
   */
  if (self->debugFlag > 3)
    PRINTF ("BDMioctl cmd:0x%x\n", cmd);
  
  switch (cmd) {
    case BDM_READ_REG:
    case BDM_READ_CTLREG:
    case BDM_READ_DBREG:
    case BDM_READ_SYSREG:
    case BDM_READ_LONGWORD:
    case BDM_READ_WORD:
    case BDM_READ_BYTE:
    case BDM_WRITE_REG:
    case BDM_WRITE_CTLREG:
    case BDM_WRITE_DBREG:
    case BDM_WRITE_SYSREG:
    case BDM_WRITE_LONGWORD:
    case BDM_WRITE_WORD:
    case BDM_WRITE_BYTE:
      err = os_copy_in ((void*) &ioc, (void*) arg, sizeof ioc);
      break;

    case BDM_SPEED:
    case BDM_DEBUG:      
    case BDM_SET_CF_PST:      
      err = os_copy_in ((void*) &iarg, (void*) arg, sizeof iarg);
      if (self->debugFlag > 3)
        PRINTF ("BDMioctl cmd->iarg:0x%x\n", iarg);
      break;
  }

  if (err)
    return err;

  /*
   * Handle the request
   */
  switch (cmd) {
    case BDM_INIT:
      iarg = bdmDrvInitHardware (self);
      if (iarg & BDM_TARGETNC)
        err = BDM_FAULT_CABLE;
      else if (iarg & BDM_TARGETPOWER)
        err = BDM_FAULT_POWER;
      break;

    case BDM_RESET_CHIP:
      err = bdmDrvResetChip (self);
      break;

    case BDM_RESTART_CHIP:
      err = bdmDrvRestartChip (self);
      break;

    case BDM_STOP_CHIP:
      err = bdmDrvStopChip (self);
      break;

    case BDM_STEP_CHIP:
      err = bdmDrvStepChip (self);
      break;

    case BDM_GO:
      err = bdmDrvGo (self);
      break;

    case BDM_GET_STATUS:
      iarg = bdmDrvGetStatus (self);
      break;

    case BDM_SPEED:
      self->delayTimer = iarg;
      if (self->debugFlag)
        PRINTF ("delayTimer now %d\n", self->delayTimer);
      break;

    case BDM_DEBUG:
      self->debugFlag = iarg;
      if (self->debugFlag)
        PRINTF ("debugFlag now %d\n", self->debugFlag);
      break;

    case BDM_RELEASE_CHIP:
      err = bdmDrvReleaseChip (self);
      break;

    case BDM_READ_CTLREG:
      err = bdmDrvReadSystemRegister (self, &ioc, BDM_SYS_REG_MODE_CONTROL);
      break;

    case BDM_READ_DBREG:
      err = bdmDrvReadSystemRegister (self, &ioc, BDM_SYS_REG_MODE_DEBUG);
      break;

    case BDM_READ_REG:
      err = bdmDrvReadProcessorRegister (self, &ioc);
      break;

    case BDM_READ_SYSREG:
      err = bdmDrvReadSystemRegister (self, &ioc, BDM_SYS_REG_MODE_MAPPED);
      break;

    case BDM_READ_LONGWORD:
      err = bdmDrvReadLongWord (self, &ioc);
      break;

    case BDM_READ_WORD:
      err = bdmDrvReadWord (self, &ioc);
      break;

    case BDM_READ_BYTE:
      err = bdmDrvReadByte (self, &ioc);
      break;

    case BDM_WRITE_CTLREG:
      err = bdmDrvWriteSystemRegister (self, &ioc, BDM_SYS_REG_MODE_CONTROL);
      break;

    case BDM_WRITE_DBREG:
      err = bdmDrvWriteSystemRegister (self, &ioc, BDM_SYS_REG_MODE_DEBUG);
      break;

    case BDM_WRITE_SYSREG:
      err = bdmDrvWriteSystemRegister (self, &ioc, BDM_SYS_REG_MODE_MAPPED);
      break;

    case BDM_WRITE_REG:
      err = bdmDrvWriteProcessorRegister (self, &ioc);
      break;

    case BDM_WRITE_LONGWORD:
      err = bdmDrvWriteLongWord (self, &ioc);
      break;

    case BDM_WRITE_WORD:
      err = bdmDrvWriteWord (self, &ioc);
      break;

    case BDM_WRITE_BYTE:
      err = bdmDrvWriteByte (self, &ioc);
      break;

    case BDM_GET_CPU_TYPE:
      iarg = self->processor;
      break;

    case BDM_GET_IF_TYPE:
      iarg = self->interface;
      break;

    case BDM_GET_DRV_VER:
      iarg = BDM_DRV_VERSION;
      break;

    case BDM_GET_CF_PST:
      iarg = self->cf_use_pst;
      break;

    case BDM_SET_CF_PST:
      self->cf_use_pst = iarg;
      break;

    default:
      err = EINVAL;
      break;
  }

  if (err)
    return err;

  /*
   * Return the result if no error was found.
   */
  switch (cmd) {
    case BDM_READ_REG:
    case BDM_READ_CTLREG:
    case BDM_READ_DBREG:
    case BDM_READ_SYSREG:
    case BDM_READ_LONGWORD:
    case BDM_READ_WORD:
    case BDM_READ_BYTE:
      err = os_copy_out ((void*) arg, (void*) &ioc, sizeof ioc);
      break;

    case BDM_GET_STATUS:
    case BDM_GET_CPU_TYPE:
    case BDM_GET_IF_TYPE:
    case BDM_GET_DRV_VER:
    case BDM_GET_CF_PST:
      err = os_copy_out ((void*) arg, (void*) &iarg, sizeof iarg);
  }

  return err;
}

static int
bdm_read (unsigned int minor, char *buf, int count)
{
  struct BDM *self = &bdm_device_info[minor];
  int        nleft, ncopy;
  int        err;
  
  nleft = count;
  while (nleft) {
    if (nleft > sizeof self->ioBuffer)
      ncopy = sizeof self->ioBuffer;
    else
      ncopy = nleft;
    err = bdmDrvFillBuf (self, ncopy);
    if (err)
      return err;
    err = os_move_out (buf, self->ioBuffer, ncopy);
    if (err)
      return err;
    nleft -= ncopy;
#ifndef BUF_INCREMENTED_BY_MOVE_OUT
    buf += ncopy;
#endif
  }
  return 0;
}

static int
bdm_write (unsigned int minor, char *buf, int count)
{  
  struct BDM *self = &bdm_device_info[minor];
  int        nleft, ncopy;
  int        err;

  nleft = count;
  while (nleft) {
    if (nleft > sizeof self->ioBuffer)
      ncopy = sizeof self->ioBuffer;
    else
      ncopy = nleft;
    err = os_move_in (self->ioBuffer, (void*) buf, ncopy);
    if (err)
      return err;
    err = bdmDrvSendBuf (self, ncopy);
    if (err)
      return err;
    nleft -= ncopy;
#ifndef BUF_INCREMENTED_BY_MOVE_IN
    buf += ncopy;
#endif
  }
  return 0;
}
