/*
 * Copyright (c) 1990 The Regents of the University of California.
 * All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Van Jacobson of Lawrence Berkeley Laboratory.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	@(#)scsi.c      7.5 (Berkeley) 5/4/91
 */

/*
 * AMIGA AMD 33C93 scsi adaptor driver
 */
#include <linux/config.h>

#include <asm/system.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/delay.h>
#include <linux/bootinfo.h>
#include "../block/blk.h"
#include "scsi.h"
#include "hosts.h"

#include "wd33c93dma.h"
#include "wd33c93var.h"
#include "wd33c93reg.h"

#define printf printk

static int sbic_wait (volatile sbic_padded_regmap_t *regs, char until,
		      int timeo, int line);
static void scsiabort (struct Scsi_Host *host,
		       volatile sbic_padded_regmap_t *regs, char *where);
static void scsierror (struct Scsi_Host *host,
		       volatile sbic_padded_regmap_t *regs, u_char csr);
static int issue_select (struct scsi_softc *dev,
			 volatile sbic_padded_regmap_t *regs, u_char target,
			 u_char our_addr);
static int wait_for_select (struct Scsi_Host *host,
			    volatile sbic_padded_regmap_t *regs, int lun);
static int ixfer_start (volatile sbic_padded_regmap_t *regs, int len,
			u_char phase, int wait);
static int ixfer_out (volatile sbic_padded_regmap_t *regs, int len,
		      u_char *buf, int phase);
static void ixfer_in (volatile sbic_padded_regmap_t *regs, int len,
		      u_char *buf);
static int scsiicmd (struct Scsi_Host *host, int target, int lun,
		     u_char *cbuf, int clen, u_char *buf, int len,
		     u_char xferphase);
static void finishxfer (Scsi_Cmnd *cmnd,
			volatile sbic_padded_regmap_t *regs, int target);
static void wd33c93reset(struct Scsi_Host *host);

/*
 * SCSI delays
 * In u-seconds, primarily for state changes on the SPC.
 */
#define SCSI_CMD_WAIT	50000	/* wait per step of 'immediate' cmds */
#define SCSI_DATA_WAIT	50000	/* wait per data in/out step */
#define SCSI_INIT_WAIT	50000	/* wait per step (both) during init */

int scsi_cmd_wait = SCSI_CMD_WAIT;
int scsi_data_wait = SCSI_DATA_WAIT;
int scsi_init_wait = SCSI_INIT_WAIT;

/*
 * Synch xfer parameters, and timing conversions
 */
static int sbic_min_period = SBIC_SYN_MIN_PERIOD;  /* in cycles = f(ICLK,FSn) */
static int sbic_max_offset = SBIC_SYN_MAX_OFFSET;  /* pure number */

static int
sbic_to_scsi_period(struct scsi_softc *dev,
		    volatile sbic_padded_regmap_t *regs, int a)
{
  unsigned int fs;

  /* cycle = DIV / (2*CLK) */
  /* DIV = FS+2 */
  /* best we can do is 200ns at 20Mhz, 2 cycles */

  GET_SBIC_myid(regs,fs);
  fs = (fs >>6) + 2;                              /* DIV */
  fs = (fs * 10000) / (dev->sc_clock_freq<<1);   /* Cycle, in ns */
  if (a < 2) a = 8;                               /* map to Cycles */
  return ((fs*a)>>2);                             /* in 4 ns units */
}

static int
scsi_period_to_sbic(struct scsi_softc *dev,
		    volatile sbic_padded_regmap_t *regs, int p)
{
  unsigned int fs;
  int ret;

  /* Just the inverse of the above */

  GET_SBIC_myid(regs,fs);
  fs = (fs >>6) + 2;                              /* DIV */
  fs = (fs * 10000) / (dev->sc_clock_freq<<1);   /* Cycle, in ns */

  ret = p << 2; 				  /* in ns units */
  ret = ret / fs;				  /* in Cycles */
  if (ret < sbic_min_period)
    return sbic_min_period;
  /* verify rounding */
  if (sbic_to_scsi_period(dev, regs, ret) < p)
    ret++;
  return (ret >= 8) ? 0 : ret;
}

/* default to not inhibit sync negotiation on any drive */
u_char inhibit_sync[8][8] = { {1,1,1,1,1,1,1,1}, }; /* initialize, so patchable */

#define DELAY(num) udelay (num)

#undef DEBUG
#define DEBUG

#ifdef DEBUG
int	scsi_debug = 0;
int	sync_debug = 0;
#define WAITHIST
#define QUASEL
#endif

#ifdef QUASEL
#define QPRINTF(a) if (scsi_debug > 1) printf a
#else
#define QPRINTF
#endif

#ifdef WAITHIST
#define MAXWAIT 1022
u_int	ixstart_wait[MAXWAIT+2];
u_int	ixin_wait[MAXWAIT+2];
u_int	ixout_wait[MAXWAIT+2];
u_int	mxin_wait[MAXWAIT+2];
u_int	mxin2_wait[MAXWAIT+2];
u_int	cxin_wait[MAXWAIT+2];
u_int	fxfr_wait[MAXWAIT+2];
u_int	sgo_wait[MAXWAIT+2];
#define HIST(h,w) (++h[((w)>MAXWAIT? MAXWAIT : ((w) < 0 ? -1 : (w))) + 1]);
#else
#define HIST(h,w)
#endif

#define b_cylin 	b_resid

static int sbic_wait (volatile sbic_padded_regmap_t *regs,
		      char until, int timeo, int line)
{
  unsigned char val;

  if (! timeo)
    timeo = 1000000;	/* some large value.. */

  GET_SBIC_asr (regs,val);
  while ((val & until) == 0)
    {
      if (!timeo--)
	{
	  int csr;
	  GET_SBIC_csr (regs, csr);
	  printf("sbic_wait TIMEO @%d with asr=x%x csr=x%x\n", line, val, csr);
	  break;
	}
      DELAY(1);
      GET_SBIC_asr(regs,val);
    }
  return val;
}

#define SBIC_WAIT(regs, until, timeo) sbic_wait (regs, until, timeo, __LINE__)


void wd33c93abort (Scsi_Cmnd *SCpnt)
{
	struct scsi_softc *dev = HOSTDATA(SCpnt->host);

	scsiabort (SCpnt->host, (sbic_padded_regmap_t *)dev->regs,
		   "wd33c93abort");

	SCpnt->result = DID_ABORT << 16;
}

static void
scsiabort(struct Scsi_Host *host,
	  volatile sbic_padded_regmap_t *regs,
	  char *where)
{
  struct scsi_softc *dev = HOSTDATA(host);
  u_char csr, asr;

  GET_SBIC_asr (regs, asr);
  GET_SBIC_csr (regs, csr);

  printf ("scsi%d: abort %s: csr = 0x%02x, asr = 0x%02x\n",
	  host->host_no, where, csr, asr);

  if (dev->sc_flags & SCSI_SELECTED)
    {
      SET_SBIC_cmd (regs, SBIC_CMD_ABORT);
      WAIT_CIP (regs);

      GET_SBIC_asr (regs, asr);
      if (asr & (SBIC_ASR_BSY|SBIC_ASR_LCI))
	{
	  /* ok, get more drastic.. */

	  SET_SBIC_cmd (regs, SBIC_CMD_RESET);
	  DELAY(25);
	  SBIC_WAIT(regs, SBIC_ASR_INT, 0);
	  GET_SBIC_csr (regs, csr);       /* clears interrupt also */

	  dev->sc_flags &= ~SCSI_SELECTED;
	  return;
	}

      do
	{
	  SBIC_WAIT (regs, SBIC_ASR_INT, 0);
	  GET_SBIC_csr (regs, csr);
	}
      while ((csr != SBIC_CSR_DISC) && (csr != SBIC_CSR_DISC_1)
	      && (csr != SBIC_CSR_CMD_INVALID));

      /* lets just hope it worked.. */
      dev->sc_flags &= ~SCSI_SELECTED;
    }
}

/*
 * XXX Set/reset long delays.
 *
 * if delay == 0, reset default delays
 * if delay < 0,  set both delays to default long initialization values
 * if delay > 0,  set both delays to this value
 *
 * Used when a devices is expected to respond slowly (e.g. during
 * initialization).
 */
void
scsi_delay(int delay)
{
	static int saved_cmd_wait, saved_data_wait;

	if (delay) {
		saved_cmd_wait = scsi_cmd_wait;
		saved_data_wait = scsi_data_wait;
		if (delay > 0)
			scsi_cmd_wait = scsi_data_wait = delay;
		else
			scsi_cmd_wait = scsi_data_wait = scsi_init_wait;
	} else {
		scsi_cmd_wait = saved_cmd_wait;
		scsi_data_wait = saved_data_wait;
	}
}

int scsi_clock_override = 0;

#ifdef CONFIG_A3000_SCSI
int a3000_detect(int host_nr)
{
  struct Scsi_Host *instance;
  struct scsi_softc *dev;
  sbic_padded_regmap_t *regs;
  caddr_t address = (caddr_t)0x00DD0000;

  /* is this machine an A3000? */
  if (boot_info.bi_amiga.model != AMI_3000)
	  return 0;

  instance = scsi_register (host_nr, sizeof (struct scsi_softc));
  dev = HOSTDATA(instance);
  memset (dev, 0, sizeof(*dev));

  /* initialize dma */
  a3000dmainit (instance, address, &dev->dmafree, &dev->dmago, &dev->dmanext,
		&dev->dmastop);

  /* advance ac->amiga_addr to point to the real sbic-registers */
  address = (caddr_t) ((int)address + 0x41);
  regs = (sbic_padded_regmap_t *) address;
  dev->regs = (caddr_t)regs;
  dev->sc_clock_freq = scsi_clock_override ? scsi_clock_override : 143;

  wd33c93reset (instance);

  return(1);
}
#endif

#ifdef CONFIG_A2091_SCSI
int a2091_detect(int host_nr)
{
  struct Scsi_Host *instance;
  struct scsi_softc *dev;
  sbic_padded_regmap_t *regs;
  int i, manuf, product, num_a2091 = 0;
  caddr_t address = 0;

  for (i = 0; i < boot_info.bi_amiga.num_autocon; i++)
  {
	  manuf = boot_info.bi_amiga.autocon[i].cd_Rom.er_Manufacturer;
	  product = boot_info.bi_amiga.autocon[i].cd_Rom.er_Product;
	  if (manuf == MANUF_COMMODORE && product == PROD_A2091) {
		  address = boot_info.bi_amiga.autocon[i].cd_BoardAddr;
		  instance = scsi_register (host_nr,
					    sizeof (struct scsi_softc));
		  dev = HOSTDATA(instance);
		  memset (dev, 0, sizeof(*dev));
		  /* initialize dma */
		  a2091dmainit (instance, address, &dev->dmafree, &dev->dmago,
				&dev->dmanext, &dev->dmastop);
		  /*
		   * advance ac->amiga_addr to point to the real
		   * sbic-registers
		   */
		  address = (caddr_t) ((int)address + 0x91);
		  regs = (sbic_padded_regmap_t *) address;
		  dev->regs = (caddr_t)regs;
		  dev->sc_clock_freq = scsi_clock_override ?
			  scsi_clock_override : 77;
		  wd33c93reset (instance);
		  host_nr++;
		  num_a2091++;
	  }
  }
  return num_a2091;
}
#endif

#ifdef CONFIG_GVP11_SCSI
int gvp11_detect(int host_nr)
{
  struct Scsi_Host *instance;
  struct scsi_softc *dev;
  sbic_padded_regmap_t *regs;
  int i, manuf, product, num_gvp11 = 0;
  caddr_t address = 0;

  for (i = 0; i < boot_info.bi_amiga.num_autocon; i++)
  {
	  manuf = boot_info.bi_amiga.autocon[i].cd_Rom.er_Manufacturer;
	  product = boot_info.bi_amiga.autocon[i].cd_Rom.er_Product;
	  if (manuf == MANUF_GVP && product == PROD_GVPIISCSI) {
		  address = boot_info.bi_amiga.autocon[i].cd_BoardAddr;
		  instance = scsi_register (host_nr,
					    sizeof (struct scsi_softc));
		  dev = HOSTDATA(instance);
		  memset (dev, 0, sizeof(*dev));
		  /* initialize dma */
		  gvp11dmainit (instance, address, &dev->dmafree, &dev->dmago,
				&dev->dmanext, &dev->dmastop);
		  /*
		   * advance ac->amiga_addr to point to the real
		   * sbic-registers
		   */
		  address = (caddr_t) ((int)address + 0x61);
		  regs = (sbic_padded_regmap_t *) address;
		  dev->regs = (caddr_t)regs;
		  dev->sc_clock_freq = scsi_clock_override ?
			  scsi_clock_override : 77;
		  wd33c93reset (instance);
		  host_nr++;
		  num_gvp11++;
	  }
  }
  return num_gvp11;
}
#endif


static void wd33c93reset(struct Scsi_Host *host)
{
  struct scsi_softc *dev = HOSTDATA(host);
  volatile sbic_padded_regmap_t *regs =
				(sbic_padded_regmap_t *)dev->regs;
  u_char  my_id, csr;

  if (dev->sc_flags & SCSI_ALIVE)
    scsiabort(host, regs, "reset");

  printf("scsi%d: ", host->host_no);

  /* preserve our ID for now */
  GET_SBIC_myid (regs, my_id);
  my_id &= SBIC_ID_MASK;

  if (dev->sc_clock_freq < 110)
    my_id |= SBIC_ID_FS_8_10;
  else if (dev->sc_clock_freq < 160)
    my_id |= SBIC_ID_FS_12_15;
  else if (dev->sc_clock_freq < 210)
    my_id |= SBIC_ID_FS_16_20;

  my_id |= SBIC_ID_EAF/* |SBIC_ID_EHP*/;

  SET_SBIC_myid (regs, my_id);

  /*
   * Disable interrupts (in dmainit) then reset the chip
   */
  SET_SBIC_cmd (regs, SBIC_CMD_RESET);
  DELAY(25);
  SBIC_WAIT(regs, SBIC_ASR_INT, 0);
  GET_SBIC_csr (regs, csr);       /* clears interrupt also */

  /*
   * Set up various chip parameters
   */
  SET_SBIC_control (regs, (/*SBIC_CTL_HHP |*/ SBIC_CTL_EDI | SBIC_CTL_IDI |
			   /* | SBIC_CTL_HSP | */ SBIC_MACHINE_DMA_MODE));
  /* don't allow (re)selection (SBIC_RID_ES) until we can handle target mode!! */
  SET_SBIC_rselid (regs, 0 /* | SBIC_RID_ER | SBIC_RID_ES | SBIC_RID_DSP */);
  SET_SBIC_syn (regs, 0);     /* asynch for now */

  /* anything else was zeroed by reset */

#if 0
  /* go async for now */
  dev->sc_sync = 0;
  printf ("async, ");
#endif

  printf("scsi id %d\n", my_id & SBIC_ID_MASK);
  dev->sc_flags |= SCSI_ALIVE;
  dev->sc_flags &= ~SCSI_SELECTED;
}

static void
scsierror (struct Scsi_Host *host, volatile sbic_padded_regmap_t *regs,
	   u_char csr)
{
	printf("scsi%d: ", host->host_no);
#if 0
	if (ints & INTS_RST) {
		DELAY(100);
		if (regs->scsi_aconf & HCONF_SD)
			printf("spurious RST interrupt");
		else
			printf("hardware error - check fuse");
		sep = ", ";
	}
	if ((ints & INTS_HARD_ERR) || regs->scsi_serr) {
		if (regs->scsi_serr & SERR_SCSI_PAR) {
			printf("%sparity err", sep);
			sep = ", ";
		}
		if (regs->scsi_serr & SERR_SPC_PAR) {
			printf("%sSPC parity err", sep);
			sep = ", ";
		}
		if (regs->scsi_serr & SERR_TC_PAR) {
			printf("%sTC parity err", sep);
			sep = ", ";
		}
		if (regs->scsi_serr & SERR_PHASE_ERR) {
			printf("%sphase err", sep);
			sep = ", ";
		}
		if (regs->scsi_serr & SERR_SHORT_XFR) {
			printf("%ssync short transfer err", sep);
			sep = ", ";
		}
		if (regs->scsi_serr & SERR_OFFSET) {
			printf("%ssync offset error", sep);
			sep = ", ";
		}
	}
	if (ints & INTS_TIMEOUT)
		printf("%sSPC select timeout error", sep);
	if (ints & INTS_SRV_REQ)
		printf("%sspurious SRV_REQ interrupt", sep);
	if (ints & INTS_CMD_DONE)
		printf("%sspurious CMD_DONE interrupt", sep);
	if (ints & INTS_DISCON)
		printf("%sspurious disconnect interrupt", sep);
	if (ints & INTS_RESEL)
		printf("%sspurious reselect interrupt", sep);
	if (ints & INTS_SEL)
		printf("%sspurious select interrupt", sep);
#else
	printf ("csr == 0x%02x", csr);  /* XXX */
#endif
	printf("\n");
}

static int
issue_select (struct scsi_softc *dev, volatile sbic_padded_regmap_t *regs,
	      u_char target, u_char our_addr)
{
  QPRINTF (("issue_select %d\n", target));

  /* if we're already selected, return */
  if (dev->sc_flags & SCSI_SELECTED)    /* XXXX */
    return 1;

  SBIC_TC_PUT (regs, 0);
  SET_SBIC_selid (regs, target);
  SET_SBIC_timeo (regs, SBIC_TIMEOUT(250,dev->sc_clock_freq));
  if (dev->sc_sync[target].state == SYNC_DONE)
    {
      /* set to negotiated values */
      SET_SBIC_syn (regs, SBIC_SYN (dev->sc_sync[target].offset,
				    dev->sc_sync[target].period));
    }
  else
    {
      /* set to async */
      SET_SBIC_syn (regs, SBIC_SYN (0, sbic_min_period));
    }
  SET_SBIC_cmd (regs, SBIC_CMD_SEL_ATN);

  return (0);
}

static int
wait_for_select (struct Scsi_Host *host, volatile sbic_padded_regmap_t *regs,
		 int lun)
{
  struct scsi_softc *dev = HOSTDATA(host);
  u_char  csr;

  QPRINTF (("wait_for_select: "));

  WAIT_CIP (regs);
  do
    {
      SBIC_WAIT (regs, SBIC_ASR_INT, 0);
      GET_SBIC_csr (regs, csr);
      QPRINTF (("%02x ", csr));
    }
  while (csr != (SBIC_CSR_MIS_2|MESG_OUT_PHASE)
	 && csr != SBIC_CSR_SEL_TIMEO /*&& csr != SBIC_CSR_INITIATOR*/);

  /* Send identify message (SCSI-2 requires an identify msg (?)) */
  if (csr == (SBIC_CSR_MIS_2|MESG_OUT_PHASE))
    {
      u_char id;
      GET_SBIC_selid (regs, id);

      /* handle drives that don't want to be asked whether to go
	 sync at all. */
      if (inhibit_sync[(int)host->host_no][id]
	  && dev->sc_sync[id].state == SYNC_START)
	{
#ifdef DEBUG
	  if (sync_debug)
	    printf ("Forcing target %d asynchronous.\n", id);
#endif
	  dev->sc_sync[id].offset = 0;
	  dev->sc_sync[id].period = sbic_min_period;
	  dev->sc_sync[id].state = SYNC_DONE;
	}


      if (dev->sc_sync[id].state == SYNC_START)
	{
	  /* then try to initiate a sync transfer.

	     So compose the sync message we're going to send to the target */

#ifdef DEBUG
	  if (sync_debug)
	    printf ("Sending sync request to target %d ... ", id);
#endif
	  dev->sc_msg[0] = MSG_IDENTIFY | (lun & 7); /* no MSG_IDENTIFY_DR yet */
	  dev->sc_msg[1] = MSG_EXT_MESSAGE; /* sync request is extended message */
	  dev->sc_msg[2] = 3;		 /* length */
	  dev->sc_msg[3] = MSG_SYNC_REQ;
	  dev->sc_msg[4] = sbic_to_scsi_period (dev, regs, sbic_min_period);
	  dev->sc_msg[5] = sbic_max_offset;

	  if (ixfer_start (regs, 6, MESG_OUT_PHASE, scsi_cmd_wait))
	    ixfer_out (regs, 6, dev->sc_msg, MESG_OUT_PHASE);

	  dev->sc_sync[id].state = SYNC_SENT;
#ifdef DEBUG
	  if (sync_debug)
	    printf ("sent\n");
#endif
	}
      else
	{
	  /* just send identify then */
	  SEND_BYTE (regs, MSG_IDENTIFY | (lun & 7));
	}

      SBIC_WAIT (regs, SBIC_ASR_INT, 0);
      GET_SBIC_csr (regs, csr);
      QPRINTF (("[%02x]", csr));
#ifdef DEBUG
      if (sync_debug && dev->sc_sync[id].state == SYNC_SENT)
	printf ("csr-result of last msgout: 0x%x\n", csr);
#endif

      if (csr != SBIC_CSR_SEL_TIMEO)
	dev->sc_flags |= SCSI_SELECTED;
    }

  QPRINTF(("\n"));

  return csr == SBIC_CSR_SEL_TIMEO;
}

static int
ixfer_start (volatile sbic_padded_regmap_t *regs, int len, u_char phase,
	     int wait)
{
#if 0
	regs->scsi_tch = len >> 16;
	regs->scsi_tcm = len >> 8;
	regs->scsi_tcl = len;
	regs->scsi_pctl = phase;
	regs->scsi_tmod = 0; /*XXX*/
	regs->scsi_scmd = SCMD_XFR | SCMD_PROG_XFR;

	/* wait for xfer to start or svc_req interrupt */
	while ((regs->scsi_ssts & SSTS_BUSY) == 0) {
		if (regs->scsi_ints || --wait < 0) {
#ifdef DEBUG
			if (scsi_debug)
				printf("ixfer_start fail: i%x, w%d\n",
				       regs->scsi_ints, wait);
#endif
			HIST(ixstart_wait, wait)
			return (0);
		}
		DELAY(1);
	}
	HIST(ixstart_wait, wait)
	return (1);
#else
	if (phase == DATA_IN_PHASE || phase == MESG_IN_PHASE)
	  {
	    u_char id;

	    GET_SBIC_selid (regs, id);
	    id |= SBIC_SID_FROM_SCSI;
	    SET_SBIC_selid (regs, id);

	    SBIC_TC_PUT (regs, (unsigned)len);
	  }
	else if (phase == DATA_OUT_PHASE || phase == MESG_OUT_PHASE
		 || phase == CMD_PHASE )
	  {
	    SBIC_TC_PUT (regs, (unsigned)len);
	  }
	else
	  {
	    SBIC_TC_PUT (regs, 0);
	  }

	QPRINTF(("ixfer_start %d, %d, %d\n", len, phase, wait));

	return 1;
#endif
}

static int
ixfer_out (volatile sbic_padded_regmap_t *regs, int len, u_char *buf,
	   int phase)
{
  int wait = scsi_data_wait;
  u_char orig_csr, asr;

  QPRINTF(("ixfer_out {%d} %02x %02x %02x %02x %02x %02x\n",
	   len, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]));

  GET_SBIC_csr (regs, orig_csr);

  /* sigh.. WD-PROTO strikes again.. sending the command in one go causes
     the chip to lock up if talking to certain (misbehaving?) targets.
     Anyway, this procedure should work for all targets, but it's slightly
     slower due to the overhead */
#if 0
  if (phase == CMD_PHASE)
    {
      WAIT_CIP (regs);
#if 0
      for (; len > 0; len--)
	{
	  /* clear possible last interrupt */
	  GET_SBIC_csr (regs, csr);

	  /* send the byte, and expect an interrupt afterwards */
	  SEND_BYTE (regs, *buf);
	  buf++;
	  SBIC_WAIT (regs, SBIC_ASR_INT, 0);    /* XXX  */
	}
#else
      SET_SBIC_cmd (regs, SBIC_CMD_XFER_INFO);
      for (;len > 0; len--)
	{
	  WAIT_CIP (regs);
	  GET_SBIC_csr (regs, csr);
	  SET_SBIC_cmd (regs, SBIC_CMD_XFER_INFO | SBIC_CMD_SBT);
	  GET_SBIC_asr (regs, asr);
	  while (!(asr & SBIC_ASR_DBR))
	    {
	      DELAY(1);
	      GET_SBIC_asr (regs, asr);
	    }

	  SET_SBIC_data (regs, *buf);
	  buf++;
	  while (!(asr & SBIC_ASR_INT))
	    {
	      DELAY(1);
	      GET_SBIC_asr (regs, asr);
	    }

	}

#endif
    }
  else
#endif
    {
      WAIT_CIP (regs);
      SET_SBIC_cmd (regs, SBIC_CMD_XFER_INFO);
      for (;len > 0; len--)
	{
	  GET_SBIC_asr (regs, asr);
	  while (!(asr & SBIC_ASR_DBR))
	    {
	      if ((asr & SBIC_ASR_INT) || --wait < 0)
		{
#ifdef DEBUG
		  if (scsi_debug)
		    printf("ixfer_out fail: l%d i%x w%d\n",
			   len, asr, wait);
#endif
		  HIST(ixout_wait, wait)
		  return (len);
		}
	      DELAY(1);
	      GET_SBIC_asr (regs, asr);
	    }

	  SET_SBIC_data (regs, *buf);
	  buf++;
	}
    }

  QPRINTF(("ixfer_out done\n"));
  /* this leaves with one csr to be read */
  HIST(ixout_wait, wait)
  return (0);
}

static void
ixfer_in (volatile sbic_padded_regmap_t *regs, int len, u_char *buf)
{
  int wait = scsi_data_wait;
  u_char *obp = buf;
  u_char orig_csr, asr;

  GET_SBIC_csr (regs, orig_csr);

  QPRINTF(("ixfer_in %d, csr=%02x\n", len, orig_csr));

  WAIT_CIP (regs);
  SET_SBIC_cmd (regs, SBIC_CMD_XFER_INFO);
  for (;len > 0; len--)
    {
      GET_SBIC_asr (regs, asr);
      while (!(asr & SBIC_ASR_DBR))
	{
	  if ((asr & SBIC_ASR_INT) || --wait < 0)
	    {
#ifdef DEBUG
	      if (scsi_debug)
		printf("ixfer_in fail: l%d i%x w%d\n",
		       len, asr, wait);
#endif
	      HIST(ixin_wait, wait)
	      return;
	    }

	  DELAY(1);
	  GET_SBIC_asr (regs, asr);
	}

      GET_SBIC_data (regs, *buf);
      buf++;
    }

  QPRINTF(("ixfer_in {%d} %02x %02x %02x %02x %02x %02x\n",
	   len, obp[0], obp[1], obp[2], obp[3], obp[4], obp[5]));

  /* this leaves with one csr to be read */
  HIST(ixin_wait, wait)
}


/*
 * SCSI 'immediate' command:  issue a command to some SCSI device
 * and get back an 'immediate' response (i.e., do programmed xfer
 * to get the response data).  'cbuf' is a buffer containing a scsi
 * command of length clen bytes.  'buf' is a buffer of length 'len'
 * bytes for data.  The transfer direction is determined by the device
 * (i.e., by the scsi bus data xfer phase).  If 'len' is zero, the
 * command must supply no data.  'xferphase' is the bus phase the
 * caller expects to happen after the command is issued.  It should
 * be one of DATA_IN_PHASE, DATA_OUT_PHASE or STATUS_PHASE.
 */
static int
scsiicmd (struct Scsi_Host *host, int target, int lun, u_char *cbuf, int clen,
	  u_char *buf, int len, u_char xferphase)
{
  struct scsi_softc *dev = HOSTDATA(host);
  volatile sbic_padded_regmap_t *regs =
				(sbic_padded_regmap_t *)dev->regs;
  u_char phase, csr, asr;
  int wait;

  /* set the sbic into non-DMA mode */
  SET_SBIC_control (regs, (/*SBIC_CTL_HHP |*/ SBIC_CTL_EDI | SBIC_CTL_IDI |
			   /* | SBIC_CTL_HSP | */ 0));

retry_selection:
  /* select the SCSI bus (it's an error if bus isn't free) */
  if (issue_select (dev, regs, target, dev->sc_scsi_addr))
    return DID_ERROR << 16;
  if (wait_for_select (host, regs, lun))
    return DID_NO_CONNECT << 16;
  /*
   * Wait for a phase change (or error) then let the device
   * sequence us through the various SCSI phases.
   */
  dev->sc_stat[0] = 0xff;
  dev->sc_msg[0] = 0xff;
  phase = CMD_PHASE;
  while (1)
    {
      wait = scsi_cmd_wait;

      GET_SBIC_csr (regs, csr);
      QPRINTF((">CSR:%02x<", csr));

      HIST(cxin_wait, wait);
      if ((csr != 0xff) && (csr & 0xf0) && (csr & 0x08)) /* requesting some new phase */
	phase = csr & PHASE;
      else if ((csr == SBIC_CSR_DISC) || (csr == SBIC_CSR_DISC_1)
	       || (csr == SBIC_CSR_S_XFERRED))
	{
	  dev->sc_flags &= ~SCSI_SELECTED;
	  GET_SBIC_cmd_phase (regs, phase);
	  if (phase == 0x60)
	    GET_SBIC_tlun (regs, dev->sc_stat[0]);
	  else
	    return DID_ERROR << 16;

	  goto out;
	}
      else
	{
	  scsierror(host, regs, csr);
	  goto abort;
	}

      switch (phase)
	{
	case CMD_PHASE:
	  if (ixfer_start (regs, clen, phase, wait))
	    if (ixfer_out (regs, clen, cbuf, phase))
	      goto abort;
	  phase = xferphase;
	  break;

	case DATA_IN_PHASE:
	  if (len <= 0)
	    goto abort;
	  wait = scsi_data_wait;
	  if (ixfer_start (regs, len, phase, wait))
	    ixfer_in (regs, len, buf);
	  phase = STATUS_PHASE;
	  break;

	case MESG_IN_PHASE:
	  if (ixfer_start (regs, sizeof (dev->sc_msg), phase, wait))
	    {
	      dev->sc_msg[0] = 0xff;
	      ixfer_in (regs, sizeof (dev->sc_msg), dev->sc_msg);
	      /* get the command completion interrupt, or we can't send
		 a new command (LCI) */
	      SBIC_WAIT (regs, SBIC_ASR_INT, wait);
	      GET_SBIC_csr (regs, csr);
#ifdef DEBUG
	      if (sync_debug)
		printf ("msgin finished with csr 0x%x\n", csr);
#endif
	      /* test whether this is a reply to our sync request */
	      if (dev->sc_msg[0] == MSG_EXT_MESSAGE
		  && dev->sc_msg[1] == 3
		  && dev->sc_msg[2] == MSG_SYNC_REQ)
		{
		  dev->sc_sync[target].period = scsi_period_to_sbic (dev, regs, dev->sc_msg[3]);
		  dev->sc_sync[target].offset = dev->sc_msg[4];
		  dev->sc_sync[target].state = SYNC_DONE;
		  SET_SBIC_syn (regs, SBIC_SYN (dev->sc_sync[target].offset,
						dev->sc_sync[target].period));
		  /* ACK the message */
		  SET_SBIC_cmd (regs, SBIC_CMD_CLR_ACK);
		  WAIT_CIP (regs);
		  phase = CMD_PHASE;  /* or whatever */
		  printf ("scsi%d: target %d now synchronous, period=%dns, offset=%d.\n",
			  host->host_no, target, dev->sc_msg[3] * 4,
			  dev->sc_msg[4]);
		}
	      else if (dev->sc_msg[0] == MSG_REJECT
		       && dev->sc_sync[target].state == SYNC_SENT)
		{
#ifdef DEBUG
		  if (sync_debug)
		    printf ("target %d rejected sync, going async\n", target);
#endif
		  dev->sc_sync[target].period = sbic_min_period;
		  dev->sc_sync[target].offset = 0;
		  dev->sc_sync[target].state = SYNC_DONE;
		  SET_SBIC_syn (regs, SBIC_SYN (dev->sc_sync[target].offset,
						dev->sc_sync[target].period));
		  /* ACK the message */
		  SET_SBIC_cmd (regs, SBIC_CMD_CLR_ACK);
		  WAIT_CIP (regs);
		  phase = CMD_PHASE;  /* or whatever */
		}
	      else if (dev->sc_msg[0] == MSG_REJECT)
		{
		  /* coming to think of it, we'll never REJECt a REJECT
		     message.. :-) */

		  /* ACK the message */
		  SET_SBIC_cmd (regs, SBIC_CMD_CLR_ACK);
		  WAIT_CIP (regs);
		  phase = CMD_PHASE;  /* or whatever */
		}
	      else if (dev->sc_msg[0] == MSG_CMD_COMPLETE)
		{
		  /* !! KLUDGE ALERT !!

		     quite a few drives don't seem to really like the current
		     way of sending the sync-handshake together with the
		     idend-message, and they react by sending command-complete
		     and disconnecting right after returning the valid sync
		     handshake. So, all I can do is reselect the drive, and
		     hope it won't disconnect again. I don't think this is valid
		     behavior, but I can't help fixing a problem that apparently
		     exists.

		     Note: we should not get here on `normal' command completion,
		     as that condition is handled by the high-level sel&xfer resume
		     command used to walk thru status/cc-phase. */

#ifdef DEBUG
		  if (sync_debug)
		    printf ("GOT CMD-COMPLETE! %d acting weird.. waiting for disconnect...\n", target);
#endif
		  /* ACK the message */
		  SET_SBIC_cmd (regs, SBIC_CMD_CLR_ACK);
		  WAIT_CIP (regs);

		  /* wait for disconnect */
		  while ((csr != SBIC_CSR_DISC) && (csr != SBIC_CSR_DISC_1))
		    {
		      DELAY (1);
		      GET_SBIC_csr (regs, csr);
		    }
#ifdef DEBUG
		  if (sync_debug)
		    printf ("ok.\nRetrying selection.\n");
#endif
		  dev->sc_flags &= ~SCSI_SELECTED;
		  goto retry_selection;
		}
	      else
		{
#ifdef DEBUG
		  if (scsi_debug || sync_debug)
		    printf ("Rejecting message 0x%02x\n", dev->sc_msg[0]);
#endif
		  /* prepare to reject the message, NACK */
		  SET_SBIC_cmd (regs, SBIC_CMD_SET_ATN);
		  WAIT_CIP (regs);
		  SET_SBIC_cmd (regs, SBIC_CMD_CLR_ACK);
		  phase = MESG_OUT_PHASE;
		}
	    }
	  break;

	case MESG_OUT_PHASE:
#ifdef DEBUG
	  if (sync_debug)
	    printf ("Sending REJECT message to last received message.\n");
#endif
	  /* should only get here on reject, since it's always US that
	     initiate a sync transfer */
	  SEND_BYTE (regs, MSG_REJECT);
	  phase = STATUS_PHASE;
	  break;

	case DATA_OUT_PHASE:
	  if (len <= 0)
	    goto abort;
	  wait = scsi_data_wait;
	  if (ixfer_start (regs, len, phase, wait))
	    {
	      if (ixfer_out (regs, len, buf, phase))
		goto abort;
	    }
	  phase = STATUS_PHASE;
	  break;

	case STATUS_PHASE:
	  /* the sbic does the status/cmd-complete reading ok, so do this
	     with its hi-level commands. */
	  SBIC_TC_PUT (regs, 0);
	  SET_SBIC_cmd_phase (regs, 0x46);
	  SET_SBIC_cmd (regs, SBIC_CMD_SEL_ATN_XFER);
	  phase = BUS_FREE_PHASE;
	  break;

	case BUS_FREE_PHASE:
	  goto out;

	default:
	  printf("scsi: unexpected phase %d in icmd from %d\n",
		 phase, target);
	  goto abort;
	}

      /* make sure the last command was taken, ie. we're not hunting after
	 an ignored command.. */
      GET_SBIC_asr (regs, asr);
      if (asr & SBIC_ASR_LCI)
	goto abort;

      /* tapes may take a loooong time.. */
      while (asr & SBIC_ASR_BSY)
	{
	  DELAY(1);
	  GET_SBIC_asr (regs, asr);
	}

#if 0
      if (wait <= 0)
	goto abort;
#endif

      /* wait for last command to complete */
      SBIC_WAIT (regs, SBIC_ASR_INT, wait);
    }

abort:
  scsiabort(host, regs, "icmd");
out:
  if (dev->sc_stat[0] == 0xff)
    return DID_ERROR << 16;
  else
    return (dev->sc_stat[0]);
}

/*
 * Finish SCSI xfer command:  After the completion interrupt from
 * a read/write operation, sequence through the final phases in
 * programmed i/o.  This routine is a lot like scsiicmd except we
 * skip (and don't allow) the select, cmd out and data in/out phases.
 */
static void
finishxfer (Scsi_Cmnd *SCpnt, volatile sbic_padded_regmap_t *regs,
	    int target)
{
  u_char phase, csr;
  unsigned long flags;
  struct scsi_softc *dev = HOSTDATA(SCpnt->host);

  save_flags (flags);
  cli();

  /* have the sbic complete on its own */
  SBIC_TC_PUT (regs, 0);
  SET_SBIC_cmd_phase (regs, 0x46);
  SET_SBIC_cmd (regs, SBIC_CMD_SEL_ATN_XFER);

  do
    {
      SBIC_WAIT (regs, SBIC_ASR_INT, 0);
      GET_SBIC_csr (regs, csr);
    }
  while ((csr != SBIC_CSR_DISC) && (csr != SBIC_CSR_DISC_1)
	 && (csr != SBIC_CSR_S_XFERRED));

  dev->sc_flags &= ~SCSI_SELECTED;
  GET_SBIC_cmd_phase (regs, phase);
  if (phase == 0x60)
    GET_SBIC_tlun (regs, dev->sc_stat[0]);
  else
    scsierror (SCpnt->host, regs, csr);

  restore_flags (flags);
}

#define IS_WRITE(cmd) ((cmd) == CMD_WRITE || (cmd) == CMD_WRITE_EXT)

static int wd33c93go (Scsi_Cmnd *SCpnt)
{
  struct scsi_softc *dev = HOSTDATA(SCpnt->host);
  volatile sbic_padded_regmap_t *regs =
			(sbic_padded_regmap_t *)dev->regs;
  int slave = SCpnt->target, unit = SCpnt->lun;
  int i, dmaflags;
  u_char phase, csr, asr;
  u_char clen = COMMAND_SIZE(SCpnt->cmnd[0]);

  dev->sc_sq = SCpnt;

  /* this should only happen on character devices, and there only if user
     programs have not been written taking care of not paossing odd aligned
     buffers. dd was a bad example of this sin.. */

/* XXXX do all with polled I/O */
  /*
   * RB: This alignment tests seem tobe OK for the A3000
   * GVP11 (and A2091, too, I guess) can DMA to 2-byte-aligned
   * adresses. And old GVP Series I controllers can even do DMA
   * to unaligned memory.
   */
  if ((((int)SCpnt->buffer & 3) || (SCpnt->bufflen & 1)))
    {
      dev->dmafree(SCpnt);

      /* in this case do the transfer with programmed I/O :-( This is
	 probably still faster than doing the transfer with DMA into a
	 buffer and copying it later to its final destination, comments? */
      scsiicmd (SCpnt->host, slave, unit, (u_char *)SCpnt->cmnd, clen,
		(unsigned char *)SCpnt->buffer, SCpnt->bufflen,
		IS_WRITE(SCpnt->cmnd[0]) ? DATA_OUT_PHASE : DATA_IN_PHASE);

      dev->sc_flags &=~ SCSI_IO;
      SCpnt->result = dev->sc_stat[0];
      return 1;
    }

  /* set the sbic into DMA mode */
  SET_SBIC_control (regs, (/*SBIC_CTL_HHP |*/ SBIC_CTL_EDI | SBIC_CTL_IDI |
			   /* | SBIC_CTL_HSP | */ SBIC_MACHINE_DMA_MODE));

  /* select the SCSI bus (it's an error if bus isn't free) */
  if (issue_select(dev, regs, slave, dev->sc_scsi_addr)
      || wait_for_select(SCpnt->host, regs, unit))
    {
      dev->dmafree(SCpnt);
      SCpnt->result = DID_ERROR << 16;
      return 1;
    }
  /*
   * Wait for a phase change (or error) then let the device
   * sequence us through command phase (we may have to take
   * a msg in/out before doing the command).  If the disk has
   * to do a seek, it may be a long time until we get a change
   * to data phase so, in the absense of an explicit phase
   * change, we assume data phase will be coming up and tell
   * the SPC to start a transfer whenever it does.  We'll get
   * a service required interrupt later if this assumption is
   * wrong.  Otherwise we'll get a service required int when
   * the transfer changes to status phase.
   */
  phase = CMD_PHASE;
  while (1)
    {
      int wait = scsi_cmd_wait;

      switch (phase)
	{
	case CMD_PHASE:
	  if (ixfer_start(regs, clen, phase, wait))
	    if (ixfer_out(regs, clen, SCpnt->cmnd, phase))
	      goto abort;
	  break;

	case MESG_IN_PHASE:
	  if (ixfer_start (regs, sizeof (dev->sc_msg), phase, wait))
	    {
	      ixfer_in (regs, sizeof (dev->sc_msg), dev->sc_msg);
	      /* prepare to reject any mesgin, no matter what it might be.. */
	      SET_SBIC_cmd (regs, SBIC_CMD_SET_ATN);
	      WAIT_CIP (regs);
	      SET_SBIC_cmd (regs, SBIC_CMD_CLR_ACK);
	      phase = MESG_OUT_PHASE;
	    }
	  break;

	case MESG_OUT_PHASE:
	  SEND_BYTE (regs, MSG_REJECT);
	  phase = STATUS_PHASE;
	  break;

	case DATA_IN_PHASE:
	case DATA_OUT_PHASE:
	  goto out;

	/* status phase can happen, if the issued read/write command is
	   illegal (for example, reading after EOT on tape) and the device
	   doesn't even go to data in/out phase. So handle this here
	   normally, instead of going thru abort-handling. */
	case STATUS_PHASE:
	  dev->dmafree(SCpnt);
	  finishxfer (SCpnt, regs, slave);
	  SCpnt = dev->sc_sq;
	  dev->sc_flags &=~ SCSI_IO;
	  SCpnt->result = dev->sc_stat[0];
	  return 1;

	default:
	  printf("scsi: unexpected phase %d in go from %d\n",
		 phase, slave);
	  goto abort;
	}

      /* make sure the last command was taken, ie. we're not hunting after
	 an ignored command.. */
      GET_SBIC_asr (regs, asr);
      if (asr & SBIC_ASR_LCI)
	goto abort;

      /* tapes may take a loooong time.. */
      while (asr & SBIC_ASR_BSY)
	{
	  DELAY(1);
	  GET_SBIC_asr (regs, asr);
	}

      if (wait <= 0)
	goto abort;

      /* wait for last command to complete */
      SBIC_WAIT (regs, SBIC_ASR_INT, wait);

      GET_SBIC_csr (regs, csr);
      QPRINTF((">CSR:%02x<", csr));

      HIST(sgo_wait, wait);
      if ((csr != 0xff) && (csr & 0xf0) && (csr & 0x08))        /* requesting some new phase */
	phase = csr & PHASE;
      else
	{
	  scsierror(SCpnt->host, regs, csr);
	  goto abort;
	}
    }

out:
  dmaflags = DMAGO_NOINT;
  if (!IS_WRITE(SCpnt->cmnd[0]))
    dmaflags |= DMAGO_READ;
  if ((int)SCpnt->buffer & 3)
    panic ("not long-aligned buffer address in scsi_go");
  if (SCpnt->bufflen & 1)
    panic ("odd transfer count in scsi_go");

  /* dmago() also enables interrupts for the sbic */
  i = dev->dmago(SCpnt, dmaflags);
/*  i = dev->dmago(SCpnt, (char *)SCpnt->buffer, SCpnt->bufflen, dmaflags);*/

  SBIC_TC_PUT (regs, (unsigned)i);
  SET_SBIC_cmd (regs, SBIC_CMD_XFER_INFO);

  return (0);

abort:
  scsiabort(SCpnt->host, regs, "go");
  dev->dmafree(SCpnt);
  SCpnt->result = DID_ERROR << 16;
  return (1);
}

void
scsidone (int unit)
{
#ifdef DEBUG
  if (scsi_debug)
    printf("scsi%d: done called!\n", unit);
#endif
}

int
wd33c93intr (struct Scsi_Host *host)
{
  struct scsi_softc *dev = HOSTDATA(host);
  volatile sbic_padded_regmap_t *regs =
				(sbic_padded_regmap_t *)dev->regs;
  u_char asr, csr;
  Scsi_Cmnd *SCpnt;
  int i;

  GET_SBIC_asr (regs, asr);
  if (! (asr & SBIC_ASR_INT))
    return 0;

  GET_SBIC_csr (regs, csr);
QPRINTF(("[0x%x]", csr));

  if (csr == (SBIC_CSR_XFERRED|STATUS_PHASE)
      || csr == (SBIC_CSR_MIS|STATUS_PHASE)
      || csr == (SBIC_CSR_MIS_1|STATUS_PHASE)
      || csr == (SBIC_CSR_MIS_2|STATUS_PHASE))
    {
      /*
       * this should be the normal i/o completion case.
       * get the status & cmd complete msg then let the
       * device driver look at what happened.
       */
      SCpnt = dev->sc_sq;
      finishxfer(SCpnt, regs, SCpnt->target);
      dev->sc_flags &=~ SCSI_IO;
      dev->dmafree (SCpnt);
      SCpnt->result = dev->sc_stat[0];
      (SCpnt->scsi_done)(SCpnt);
    }
  else if (csr == (SBIC_CSR_XFERRED|DATA_OUT_PHASE) || csr == (SBIC_CSR_XFERRED|DATA_IN_PHASE)
	   || csr == (SBIC_CSR_MIS|DATA_OUT_PHASE) || csr == (SBIC_CSR_MIS|DATA_IN_PHASE)
	   || csr == (SBIC_CSR_MIS_1|DATA_OUT_PHASE) || csr == (SBIC_CSR_MIS_1|DATA_IN_PHASE)
	   || csr == (SBIC_CSR_MIS_2|DATA_OUT_PHASE) || csr == (SBIC_CSR_MIS_2|DATA_IN_PHASE))
    {
      /* do scatter-gather dma hacking the controller chip, ouch.. */
      i = dev->dmanext (host);
      SBIC_TC_PUT (regs, (unsigned)i);
      SET_SBIC_cmd (regs, SBIC_CMD_XFER_INFO);
    }
  else
    {
      /* Something unexpected happened -- deal with it. */
      dev->dmastop (host);
      scsierror(host, regs, csr);
      scsiabort(host, regs, "intr");
      if (dev->sc_flags & SCSI_IO)
	{
	  dev->sc_flags &=~ SCSI_IO;
	  SCpnt = dev->sc_sq;
	  dev->dmafree (SCpnt);
	  SCpnt->result = DID_ERROR;
	  (SCpnt->scsi_done)(SCpnt);
	}
    }

  return 1;
}

const char *wd33c93_info (void)
{
    static const char string[]="";
    return string;
}

int wd33c93_queuecommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
{
	int xferphase;
	int result;

	SCpnt->scsi_done = done;

	switch (SCpnt->cmnd[0]) {
	    case CMD_TEST_UNIT_READY:
		xferphase = STATUS_PHASE;
		break;
	    case CMD_WRITE:
		xferphase = DATA_OUT_PHASE;
		break;
	    default:
		xferphase = DATA_IN_PHASE;
	}

	switch (SCpnt->cmnd[0]) {
	    case CMD_TEST_UNIT_READY:
	    case CMD_INQUIRY:
	    case CMD_READ_CAPACITY:
	    case CMD_REQUEST_SENSE:
		SCpnt->result = scsiicmd(SCpnt->host, SCpnt->target,
					 SCpnt->lun, SCpnt->cmnd,
					 COMMAND_SIZE (SCpnt->cmnd[0]),
					 (unsigned char *)SCpnt->request_buffer,
					 SCpnt->request_bufflen, xferphase);

		(SCpnt->scsi_done)(SCpnt);
		break;
	    default:
		result = wd33c93go (SCpnt);
		if (result)
			(SCpnt->scsi_done)(SCpnt);
		break;
	}

       return 0;
}


int wd33c93_command (Scsi_Cmnd *SCpnt)
{
	int xferphase;
	int result;

	switch (SCpnt->cmnd[0]) {
	    case CMD_TEST_UNIT_READY:
		xferphase = STATUS_PHASE;
		break;
	    case CMD_WRITE:
		xferphase = DATA_OUT_PHASE;
		break;
	    default:
		xferphase = DATA_IN_PHASE;
	}

	result = scsiicmd(SCpnt->host, SCpnt->target, SCpnt->lun, SCpnt->cmnd,
			  COMMAND_SIZE (SCpnt->cmnd[0]),
			  (unsigned char *)SCpnt->request_buffer,
			  SCpnt->request_bufflen, xferphase);

	return result;
}

int wd33c93_abort (Scsi_Cmnd *SCpnt, int code)
{
	if (code == 0)
		SCpnt->result = DID_ABORT;
	else
		SCpnt->result = DID_TIME_OUT;

	wd33c93abort (SCpnt);

	return 0;
}

int wd33c93_reset (Scsi_Cmnd *SCpnt)
{
	wd33c93reset (SCpnt->host);

	return 0;
}
