/*
  Giant Slalom device driver

  Copyright TiVo Inc., 2002.   The terms of the GNU Public License (version 2) 
  apply to this code.

  Derived in part from g_NCR5380 and other Linux SCSI and IDE drivers.
  My thanks to the numerous authors of that code, who should in no way
  be held at all responsible for the various horrors I've visited upon
  the code thus abstracted.

  The Giant Slalom is a rather peculiar chip, made slightly more so by
  the techniques being used to hook it up to a TiVo board
  architecture.  The chip has an IDE/ATAPI interface (two of them,
  actually) with the registers mapped at nonstandard offsets in the
  chip's memory space, a whole bunch of miscellaneous peripherals such
  as I2C and VFD drivers (we use the latter but not the former), and
  an on-chip DMA controller which channels data between various I/Os
  and a large off-chip SDRAM buffer pool.  A TiVo-specific PLD bridges
  the GS's CPU interface (which has multiple chip selects and a really
  huge addressing range) into a modest amount of TiVo localbus address
  space.  Accesses to the GS registers must go through the bridge's
  address-mapping protocol.  The PLD must be initialized (using some
  application-level support) before the GS itself can be used.

  This driver models the GS as a SCSI host adapter, with multiple
  devices attached.

  0 - PLD loader and administrative interface.  This "device" is
      always available and can be accessed via the SCSI Generic interface.
      It responds to the IDENTIFY DEVICE command, and to WRITE DATA (loads
      the PLD).  Also responds to diagnostic ioctl() calls to read and
      write specific GS registers; these ioctl() calls are available only
      after the WRITE DATA command successfully configures the bridge.

  1 - GS SDRAM.  A block device with a 512-byte sector size,
      accessible via SCSI generic or /dev/sd*, available once the PLD
      is loaded and the SDRAM probing has been performed.  Responds to
      IDENTIFY DEVICE, READ DATA, WRITE DATA, and READ CAPACITY
      commands.  This driver is intended for diagnostic purposes only;
      it's a Bad Idea to actually use it while the driver is in use
      by anything/anyone else, as you'll have no way of knowing which
      portions of the GS SDRAM are being used for other purposes and
      you'll probably step on somebody's data.

  2 - GS FL/VFD driver.  Device protocol not yet defined... probably a
      block or character device.  Available once the PLD is loaded and the
      SDRAM probing has been performed.  Responds to IDENTIFY DEVICE and
      WRITE DATA commands at least.

  3 - CD- or DVD-whatever ATAPI drive, master device on the GS primary IDE
      interface.    Available once the PLD is loaded and the SDRAM probing 
      has been performed.  Responds to whatever commands the drive itself
      supports.

  No attempt has been made to support legacy IDE hard drives... ATAPI
  only.  Disconnect/reselect and tagged command queueing are neither
  supported nor emulated... each device-type handler will handle only
  one command at a time.  This might be an issue if we ever want to
  support two DVD-RW drives, and be able to finalize one disc while
  starting the burn on another.

*/

#include <linux/modversions.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/mman.h>
#include <linux/pci.h>
#include <linux/linkage.h>
#include <linux/time.h>
#include <linux/wait.h>
#include <linux/blkdev.h>
#include <linux/blk.h>
#include <linux/spinlock.h>
#include <linux/tivoconfig.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/hdreg.h>
#include <linux/tqueue.h>
#include <linux/interrupt.h>
#include <linux/reboot.h>

#include <asm/atomic.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
#include <asm/byteorder.h>
#include <asm/bcm7020.h>
#include <asm/px_hw.h>

#include <scsi/giantslalom.h>

#include "scsi.h"
#include "hosts.h"
#include "constants.h"
#include "sd.h"

#include "gs.h"

#if (NDEBUG & NDEBUG_LISTS)
#define LIST(x,y) {printk("LINE:%d   Adding %p to %p\n", __LINE__, (void*)(x), (void*)(y)); if ((x)==(y)) udelay(5); }
#define REMOVE(w,x,y,z) {printk("LINE:%d   Removing: %p->%p  %p->%p \n", __LINE__, (void*)(w), (void*)(x), (void*)(y), (void*)(z)); if ((x)==(y)) udelay(5); }
#else
#define LIST(x,y)
#define REMOVE(w,x,y,z)
#endif

#define KERN_DPLATT KERN_DEBUG

#define TAG_NEXT	-1
#define TAG_NONE	-2

#define USLEEP_SLEEP (20*HZ/1000)
#define USLEEP_WAITLONG USLEEP_SLEEP

const static char version[] = BUILDDATE;

enum FakeCheckCondition {
  NothingPending=0,
  UnitAttention=1,
  IllegalRequest=2,
  NotReady=3,
  IllegalAddress=4,
};


#define XMIT_GS_SPI_SUCCESS 0
#define XMIT_GS_SPI_FAILURE -1

int xmit_gs_spi (unsigned char *data, int len, void (*callback)(int));

static int gs_mem_autoprobe (void);
static int gs_abort(Scsi_Cmnd *);
static int gs_detect(Scsi_Host_Template *);
static int gs_release_resources(struct Scsi_Host *);
static int gs_queue_command(Scsi_Cmnd *, void (*done) (Scsi_Cmnd *));
static int gs_reset(Scsi_Cmnd *, unsigned int);
static const char *gs_info(struct Scsi_Host *);
static int gs_proc_info(char *buffer, char **start, off_t offset, int length, int hostno, int inout);
static int gs_ioctl(Scsi_Device *dev, int cmd, void *arg);
static void gsWorkerDoneCallback (struct gs_worker *worker);
static void gsXmitDoneCallback (struct gs_worker *worker);


static void gs_intr(int irq, void *dev_id, struct pt_regs *regs);
static void do_gs_intr(int irq, void *dev_id, struct pt_regs *regs);
static void broadcom_intr(int irq, void *dev_id, struct pt_regs *regs);

static void gs_wait (int mscount);

static int gsCheckHostDMARules (Scsi_Cmnd * cmd);
static int gsCheckMoreHostDMA (Scsi_Cmnd * cmd);
static int gsTransferToUser (Scsi_Cmnd * cmd, unsigned char *buffer, unsigned int length);
static int gsTransferFromUser (Scsi_Cmnd * cmd, unsigned char *buffer, unsigned int length);
static int gsInquiry (Scsi_Cmnd * cmd, int device, int deviceType, char *vendor, char *model, char *version);
static void gsCheckCondition (struct Scsi_Host *instance,
			      Scsi_Cmnd * cmd, enum FakeCheckCondition cond);
static void gsRequestSense(struct Scsi_Host *instance,
			   Scsi_Cmnd * cmd);

static void run_main(void);
static void gs_main(void);
static int gs_probe_bridge(void);
static void gs_bridge_is_alive(struct Scsi_Host *instance);
static void gs_init_broadcom(void);
static void initialize_SCp(Scsi_Cmnd * cmd);
static int gs_run_workers(struct Scsi_Host *instance);
static unsigned int gs_gated_requests(struct Scsi_Host *instance);

static unsigned long gs_interrupt_for_workers(struct Scsi_Host *instance,
					      unsigned long interruptsFired);

static void gs_queue_worker_for_interrupt (struct gs_worker *worker,
					   unsigned long interruptsNeeded,
					   enum gs_worker_state nextState);

static void gs_queue_worker_for_timer (struct gs_worker *worker,
				       int timerTicks,
				       enum gs_worker_state nextState);

static void gs_worker_next_state (struct gs_worker *worker,
			      enum gs_worker_state nextState);

static void gs_worker_disarm_timer (struct gs_worker *worker);

static void gs_worker_set_deadman (struct gs_worker *worker,
				   int timerTicks);

static void gs_worker_disarm_deadman (struct gs_worker *worker);

static struct Scsi_Host *first_instance = NULL;
static Scsi_Host_Template *the_template = NULL;

static int gsBridgeDispatchFunction (struct Scsi_Host *instance,
				     Scsi_Cmnd * cmd, int device);
static int gsRamDispatchFunction (struct Scsi_Host *instance,
				  Scsi_Cmnd * cmd, int device);
static int gsFlDispatchFunction (struct Scsi_Host *instance,
				 Scsi_Cmnd * cmd, int device);
static int gsDriveDispatchFunction (struct Scsi_Host *instance,
				    Scsi_Cmnd * cmd, int device);
static int gsDacDispatchFunction (struct Scsi_Host *instance,
				  Scsi_Cmnd * cmd, int device);

static int gsBridgeIoctlFunction (struct Scsi_Host *instance,
				  Scsi_Device *dev, int cmd, void *arg);

static void gs_worker_setup(struct Scsi_Host *instance,
			    struct gs_worker *worker);
static void gs_worker_dma_to_sdram(struct Scsi_Host *instance,
				   struct gs_worker *worker);
static void gs_worker_do_psw_dma_burst(struct Scsi_Host *instance,
					struct gs_worker *worker);
static void gs_worker_detach_command(struct Scsi_Host *instance,
				     struct gs_worker *worker);
static void gs_worker_queue_for_atapi(struct Scsi_Host *instance,
				      struct gs_worker *worker);
static void gs_worker_queue_for_gate(struct Scsi_Host *instance,
				      struct gs_worker *worker);
static void gs_worker_init_atapi(struct Scsi_Host *instance,
				 struct gs_worker *worker);
static void gs_worker_send_packet(struct Scsi_Host *instance,
				  struct gs_worker *worker);
static void gs_worker_dma_data(struct Scsi_Host *instance,
			       struct gs_worker *worker);
static void gs_worker_check_dma_results(struct Scsi_Host *instance,
					struct gs_worker *worker);
static void gs_worker_pio_data(struct Scsi_Host *instance,
			       struct gs_worker *worker);
static void gs_worker_check_pio_results(struct Scsi_Host *instance,
					struct gs_worker *worker);
static void gs_worker_init_for_request_sense(struct Scsi_Host *instance,
					     struct gs_worker *worker);
static void gs_worker_send_request_sense(struct Scsi_Host *instance,
					 struct gs_worker *worker);
static void gs_worker_transfer_sense_data(struct Scsi_Host *instance,
					  struct gs_worker *worker);
static void gs_worker_dma_from_sdram(struct Scsi_Host *instance,
				     struct gs_worker *worker);
static void gs_worker_do_psr_dma_burst(struct Scsi_Host *instance,
					struct gs_worker *worker);
static void gs_worker_loopback_dma_done(struct Scsi_Host *instance,
					struct gs_worker *worker);
static void gs_worker_postprocess(struct Scsi_Host *instance,
				  struct gs_worker *worker);

static void gs_worker_setup_reset(struct Scsi_Host *instance,
				  struct gs_worker *worker);
static void gs_worker_reset_done(struct Scsi_Host *instance,
				 struct gs_worker *worker);

static void gs_worker_setup_fl(struct Scsi_Host *instance,
			       struct gs_worker *worker);
static void gs_worker_fl_data_burst(struct Scsi_Host *instance,
				    struct gs_worker *worker);

static void gs_worker_setup_dac(struct Scsi_Host *instance,
				struct gs_worker *worker);
static void gs_worker_dac_data_burst(struct Scsi_Host *instance,
				     struct gs_worker *worker);

static void gs_worker_done(struct Scsi_Host *instance,
			   struct gs_worker *worker);


static void gs_release_chip_resources (struct Scsi_Host *instance,
				       unsigned int resourceMask);

static void gs_task (void *whatever);
static void gs_timer_task (void *whatever);

static void show_atapi_status(void);
static void show_gs_intr(void);

#define GS_DEVICES 5

#define GS_DEVICE_BRIDGE 0
#define GS_DEVICE_RAM    1
#define GS_DEVICE_FL     2
#define GS_DEVICE_DRIVE  3
#define GS_DEVICE_DAC    4

#define GS_HANDLERS 5

#define GS_HANDLER_BRIDGE 0
#define GS_HANDLER_RAM    1
#define GS_HANDLER_FL     2
#define GS_HANDLER_DRIVE  3
#define GS_HANDLER_DAC    4

typedef int (*GSCommandDispatchFunction) (struct Scsi_Host *instance,
					  Scsi_Cmnd * cmd, int device);

typedef int (*GSIoctlFunction) (struct Scsi_Host *instance,
				Scsi_Device *dev, int cmd, void *arg);

static const GSCommandDispatchFunction dispatchers[GS_HANDLERS] = 
  {
    gsBridgeDispatchFunction,
    gsRamDispatchFunction,
    gsFlDispatchFunction,
    gsDriveDispatchFunction,
    gsDacDispatchFunction
  };

static const GSIoctlFunction ioctlHandlers[GS_HANDLERS] = 
  {
    gsBridgeIoctlFunction,
    NULL, NULL, NULL, NULL
  };

static const int deviceDispatcher[GS_DEVICES] = {0, 1, 2, 3, 4};

#define GIANT_SLALOM {						\
	proc_info:      gs_proc_info,			\
	name:           "Giant Slalom Scsi Driver",	\
	detect:         gs_detect,				\
	release:        gs_release_resources,		\
	info:           (void *)gs_info,			\
        ioctl:          gs_ioctl,				\
	queuecommand:   gs_queue_command,			\
	abort:          gs_abort,				\
	reset:          gs_reset, 				\
	bios_param:     NULL,				\
	can_queue:      8,				\
	cmd_per_lun:    1,				\
        this_id:        7,						\
        sg_tablesize:   SG_ALL,						\
        use_clustering: DISABLE_CLUSTERING}

struct BroadcomDMADescriptor {
  int transferWordCount_7_0   :  8;
  int memoryAddr_25_2         : 24;
  int direction               :  1;
  int last                    :  1;
  int reserved                :  4;
  int ioAddr_25_2             : 24;
  int transferWordCount_9_8   :  2;
};

struct gs_hostdata {
  unsigned char id_mask, id_higher_mask;	/* 1 << id, all bits greater */
  unsigned char targets_present;		/* targets we have connected
						   to, so we can call a select
						   failure a retryable condition */
  volatile unsigned char busy[GS_HANDLERS];	/* index = target, bit = lun */
  volatile unsigned char pending[GS_HANDLERS];	/* index = target, bit = lun */
  volatile unsigned char last_message;	/* last message OUT */
  volatile Scsi_Cmnd *connected[GS_HANDLERS];	/* currently connected command */
  volatile enum FakeCheckCondition condition[8]; /* pending conditions */
  volatile Scsi_Cmnd *issue_queue;	/* waiting to be issued */
  volatile unsigned aborted:1;		/* flag, says aborted */
  volatile unsigned pldLoaded:1;        /* flag, bridge is up */
  volatile unsigned initDone:1;         /* flag, initialization done */
  volatile unsigned atapiOK:1;          /* flag, ATAPI usable */
  volatile unsigned wasReset:1;         /* flag, ATAPI bus was reset, need to settle... */
  volatile unsigned mustReset:1;        /* flag, ATAPI reset is required */
  int flags;
  rwlock_t queueLock;                   /* lock on the issue queue */
  unsigned long time_expires;		/* in jiffies, set prior to sleeping */
  struct Scsi_Host *next_timer;
  int select_time;			/* timer in select for target response */
  volatile Scsi_Cmnd *selecting;
  int workerSN;
  struct list_head workers;
  volatile unsigned long resourcesBusy; /* On-chip resources */
  unsigned int atapiSectorSize;
  unsigned int atapiBufferModulus;
  int diagMode;
  struct BroadcomDMADescriptor *dmaDescriptors;
  unsigned char deferred_sense_buffer[SCSI_SENSE_BUFFERSIZE];
  int haveDeferredSense;
#ifdef GS_STATS
  unsigned timebase;			/* Base for time calcs */
  long time_read[8];			/* time to do reads */
  long time_write[8];			/* time to do writes */
  unsigned long bytes_read[8];		/* bytes read */
  unsigned long bytes_write[8];		/* bytes written */
  unsigned pendingr;
  unsigned pendingw;
#endif
};


int gs_mem_lists_initialized = 0;
int gs_probed_memsize = 0;

static int gs_broadcom_enabled = 0;

static struct list_head gs_free_blocks;
static struct list_head gs_busy_blocks;

static struct tq_struct gs_task_struct;
static struct tq_struct gs_timer_task_struct;

#if defined(MODULE)
MODULE_AUTHOR("Dave Platt, TiVo Inc.");
MODULE_DESCRIPTION("Giant Slalom device driver");
MODULE_LICENSE("GPL");

MODULE_PARM(debug, "1-1i");
MODULE_PARM_DESC(debug,"enable debug logs");
static int debug = 0;

MODULE_PARM(jam, "1-10i");
MODULE_PARM_DESC(jam,"jam in a DMA failure occasionally");
static int jam = 0;

MODULE_PARM(autodebug, "0-600i");
MODULE_PARM_DESC(autodebug,"enable debug after DMA failure for N seconds");
static int autodebug = 0;

MODULE_PARM(autodebugcount, "0-999i");
MODULE_PARM_DESC(autodebugcount,"enable debug after DMA failure N times");
static int autodebugcount = 0;

static int autodebugExpires = 0;
static int autodebugRestore = -1;

#endif

static void gs_fl_burst(const unsigned short *array, unsigned byteCount);
static int gs_reboot(struct notifier_block *self, unsigned long, void *);

static struct notifier_block my_notifier = { gs_reboot, NULL, 0 };

static int notifier_installed = 0;

// Reboot-time data bursts to send to the FL display

static const unsigned short RebootPattern1[12] =
{
  0xC632, 0x13C9, 0x3123, 0xCC00, 0x00CF, 0xD223, 0xD251, 0x21D5,
  0x3A03, 0xD831, 0x23DB, 0xD023
};

static const unsigned short RebootPattern2[2] =
{
  0xE140, 0x0200
};

static void gs_autodebug_on(void) {
  if (autodebug == 0 || debug > 1 || autodebugcount <= 0) {
    return;
  }
  if (autodebugRestore < 0) {
    autodebugRestore = debug;
  }
  debug = 1;
  autodebugExpires = jiffies + autodebug * HZ;
  autodebugcount --;
  printk(KERN_INFO "gs: autodebug mode enabled\n");
}

static void gs_autodebug_off(void) {
  if (autodebugExpires > 0 && autodebugExpires < jiffies) {
    debug = autodebugRestore;
    autodebugRestore = -1;
    autodebugExpires = 0;
    printk(KERN_INFO "gs: autodebug mode expired\n");
  }
}

static void gs_wait( int  mscount )
{
    int delay = ( mscount * HZ ) / 1000;

    wait_queue_head_t wait;

    init_waitqueue_head( &wait );

    if( delay <= 0 ) delay = 1;

    interruptible_sleep_on_timeout( &wait, delay );
}

#ifdef NOTDEF
static void dump_bridge(void)
{
  int i;
  unsigned short value;
  printk(KERN_INFO "Bridge registers:");
  for (i = 0 ; i <= 32; i += 2) {
    value = *(volatile unsigned short *) KSEG1ADDR(GS_BRIDGE_BASE+i);
    printk(" %04X", value);
  }
  printk("\n");
  scsi_sleep(2);
}
#endif

static unsigned short GS_READ (unsigned long offset)
{
  unsigned long flags;
  unsigned long oldIndirect;
  unsigned short value;
  save_and_cli(flags);
  oldIndirect = GS_BRIDGE->gsIndirectRegister;
  barrier();
  GS_BRIDGE->gsIndirectRegister = offset >> 24;
  barrier();
  value = *(volatile unsigned short *) KSEG1ADDR(GS_WINDOW_BASE + (offset & 0xFFFFFF));
  barrier();
  GS_BRIDGE->gsIndirectRegister = oldIndirect;
  barrier();
  restore_flags(flags);
  if (debug > 1) {
    printk(KERN_INFO "gs: peek %04X from %08lX\n", value, offset);
  }
  return value;
}

static void GS_READ_FIFO (unsigned long offset, long count,
			  unsigned short *buf)
{
  unsigned long flags;
  unsigned long oldIndirect;
  long origCount;
  unsigned short val;
  unsigned long cksum;
  if (debug > 2) printk(KERN_INFO "gs: read_fifo offset 0x%08lX count %ld\n", offset, count);
   
  save_and_cli(flags);
  oldIndirect = GS_BRIDGE->gsIndirectRegister;
  barrier();
  GS_BRIDGE->gsIndirectRegister = offset >> 24;
  barrier();
  if (debug > 3) printk(KERN_INFO "gs: bridge indirect register is %08X\n",   GS_BRIDGE->gsIndirectRegister);
  origCount = count;
  cksum = 0;
  while (--count >= 0) {
    if (debug > 4) printk(KERN_INFO "gs: read word %ld\n", origCount - count - 1);
     
    val = *(volatile unsigned short *) KSEG1ADDR(GS_WINDOW_BASE + (offset & 0xFFFFFF));
    *buf++ = val;
    cksum += val;
  }
  barrier();
  GS_BRIDGE->gsIndirectRegister = oldIndirect;
  barrier();
  if (debug > 2) printk(KERN_INFO "gs: FIFO read completed, cksum %08lX\n", cksum);
  restore_flags(flags);
}

static void GS_READ_MULTI (unsigned long offset, long count,
			  unsigned short *buf)
{
  unsigned long flags;
  unsigned long oldIndirect;
  save_and_cli(flags);
  oldIndirect = GS_BRIDGE->gsIndirectRegister;
  barrier();
  GS_BRIDGE->gsIndirectRegister = offset >> 24;
  barrier();
  while (--count >= 0) {
    *buf++ = *(volatile unsigned short *) KSEG1ADDR(GS_WINDOW_BASE + (offset & 0xFFFFFF));
    offset += 2;
  }
  barrier();
  GS_BRIDGE->gsIndirectRegister = oldIndirect;
  barrier();
  restore_flags(flags);
}

static unsigned char GS_READ8 (unsigned long offset)
{
  return GS_READ(offset) >> 8;
}

static void GS_WRITE (unsigned long offset, unsigned short value)
{
  unsigned long flags;
  unsigned long oldIndirect;
  if (debug > 1) {
    printk(KERN_INFO "gs: poke %04X at %08lX\n", value, offset);
  }
  save_and_cli(flags);
  oldIndirect = GS_BRIDGE->gsIndirectRegister;
  barrier();
  GS_BRIDGE->gsIndirectRegister = offset >> 24;
  barrier();
  *(volatile unsigned short *) KSEG1ADDR(GS_WINDOW_BASE + (offset & 0xFFFFFF)) = value;
  barrier();
  GS_BRIDGE->gsIndirectRegister = oldIndirect;
  barrier();
  restore_flags(flags);
}

static void GS_WRITE_FIFO (unsigned long offset, long count,
			   unsigned short *buf)
{
  unsigned long flags;
  unsigned long oldIndirect;
  save_and_cli(flags);
  oldIndirect = GS_BRIDGE->gsIndirectRegister;
  barrier();
  GS_BRIDGE->gsIndirectRegister = offset >> 24;
  barrier();
  while (--count >= 0) {
    *(volatile unsigned short *) KSEG1ADDR(GS_WINDOW_BASE + (offset & 0xFFFFFF)) = *buf++;
    barrier();
  }
  GS_BRIDGE->gsIndirectRegister = oldIndirect;
  barrier();
  restore_flags(flags);
}

static void GS_WRITE_MULTI (unsigned long offset, long count,
			    const unsigned short *buf)
{
  unsigned long flags;
  unsigned long oldIndirect;
  save_and_cli(flags);
  oldIndirect = GS_BRIDGE->gsIndirectRegister;
  barrier();
  GS_BRIDGE->gsIndirectRegister = offset >> 24;
  barrier();
  while (--count >= 0) {
    *(volatile unsigned short *) KSEG1ADDR(GS_WINDOW_BASE + (offset & 0xFFFFFF)) = *buf++;
    offset += 2;
    barrier();
  }
  GS_BRIDGE->gsIndirectRegister = oldIndirect;
  barrier();
  restore_flags(flags);
}

static void GS_WRITE8 (unsigned long offset, unsigned char value)
{
  GS_WRITE(offset, ((unsigned short) value) << 8);
}

/*
  The Giant Slalom has an attached SDRAM, which is used for double-
  buffering data requests being sent to/from the ATAPI interface.
  This SDRAM is _not_ part of the host's unified memory space...
  it's a separate address space managed through one of the GS's
  Chip Select lines.  Addressing it is both [1] necessary, and
  [2] more than a bit convoluted.  

  Treating a GS SDRAM offset as if it were a host address would have
  horrendous consequences.  So, rather than try to [ab]use the
  kernel's memory-allocation/management routines to maintain the
  free- and busy-block lists, I'm just reimplementing a basic
  memory manager here, using the linux/list.h package... seems
  less dangerous.
*/

/*
  Probe the GS SDRAM, figure out how big it is, and create a GS free
  block which frames all of the SDRAM.

  Returns 0 if memory was set up OK (or was previously set up),
  -1 if the probe failed or the freelists couldn't be set up.
*/

int gs_mem_autoprobe ()
{
  unsigned long gs_memsize = 2;
  unsigned long old1, old2;
  int continueScanning = 1;
  struct gs_sdram *allSDRAM;
  if (gs_mem_lists_initialized) {
    return 0;
  }
  INIT_LIST_HEAD(&gs_free_blocks);
  INIT_LIST_HEAD(&gs_busy_blocks);
  if (debug) printk(KERN_INFO "gs: probing base of SDRAM\n");
  old1 = GS_READ(GS_OFFSET_SDRAM);
  old2 = GS_READ(GS_OFFSET_SDRAM+2);
  GS_WRITE(GS_OFFSET_SDRAM, 0x1234);
  GS_WRITE(GS_OFFSET_SDRAM+2, 0x5678);
  if (GS_READ(GS_OFFSET_SDRAM) != 0x1234 ||
      GS_READ(GS_OFFSET_SDRAM+2) != 0x5678) {
    printk(KERN_INFO "gs: Initial SDRAM probe failed - bad bridge?\n");
    scsi_sleep (HZ / 4);
    return -1;
  }
  GS_WRITE(GS_OFFSET_SDRAM+2, old2);
  while (gs_memsize < GS_BANK_SIZE && continueScanning) {
    if (debug) printk(KERN_INFO "gs: testing memsize of %ld\n", gs_memsize);
    old2 = GS_READ(GS_OFFSET_SDRAM+gs_memsize);
    GS_WRITE(GS_OFFSET_SDRAM+gs_memsize, 0xABCD);
    if (GS_READ(GS_OFFSET_SDRAM) != 0x1234 ||
	GS_READ(GS_OFFSET_SDRAM+gs_memsize) != 0xABCD) {
      GS_WRITE(GS_OFFSET_SDRAM+gs_memsize, old2);
      continueScanning = 0;
    } else {
      GS_WRITE(GS_OFFSET_SDRAM+gs_memsize, old2);
      gs_memsize <<= 1;
    }
  }
  GS_WRITE(GS_OFFSET_SDRAM, old1);
  printk(KERN_INFO "gs: SDRAM size is %ld (0x%lX)\n", gs_memsize, gs_memsize);
    scsi_sleep (HZ / 4);
  allSDRAM = (struct gs_sdram *) kmalloc(sizeof (struct gs_sdram),
					 GFP_KERNEL);
  if (!allSDRAM) {
    printk("gs: kmalloc failed\n");
    return -1;
  }
  allSDRAM->worker = NULL;
  allSDRAM->gsOffset = 0;
  allSDRAM->sizeRequested = 0;
  allSDRAM->sizeAllocated = gs_memsize;
  list_add((struct list_head *) allSDRAM, &gs_free_blocks);
  gs_mem_lists_initialized = 1;
  gs_probed_memsize = gs_memsize;
  return 0;
}

struct gs_sdram * gs_sdram_alloc (struct gs_worker *worker,
				  unsigned long size)
{
  struct list_head *this;
  struct gs_sdram *thisBlock = NULL, *newBlock;
  unsigned long roundedSize;
  int foundOne = 0;
  int split = 0;
  unsigned int flags;
  roundedSize = (size + GS_SDRAM_ALLOC_MODULUS - 1) &
    ~(GS_SDRAM_ALLOC_MODULUS - 1);
  if (debug) printk(KERN_DEBUG "gs: request a block of %ld bytes, rounded to %ld\n",
	 size, roundedSize);
  save_and_cli(flags);
  list_for_each(this,&gs_free_blocks) {
    thisBlock = (struct gs_sdram *) this;
    if (thisBlock->sizeAllocated >= roundedSize) {
      foundOne = 1;
      if (debug) printk(KERN_DEBUG "gs: found a block %p offset %ld, size  %ld\n",
	     thisBlock, thisBlock->gsOffset, thisBlock->sizeAllocated);
      break;
    }
  }
  if (!foundOne) {
    restore_flags(flags);
    return NULL;
  }
  /*
    If the block we found is larger than what we need (rounded up),
    create a new block descriptor which frames the portion that we
    need and update this one in place to point to the unused portion.
    Otherwise, grab this whole block.
  */
  if (thisBlock->sizeAllocated >  roundedSize) {
    if (debug) printk(KERN_DEBUG "gs: splitting this block\n");
    newBlock = (struct gs_sdram *) kmalloc(sizeof (struct gs_sdram),
					   GFP_ATOMIC);
    if (!newBlock) {
      printk(KERN_ERR "gs: kmalloc failed during gs_sdram_alloc\n");
      /*
	In this case we'll fall through and hand out the whole big
	block.
      */
    } else {
      newBlock->gsOffset = thisBlock->gsOffset;
      newBlock->sizeAllocated = roundedSize;
      if (debug) printk(KERN_DEBUG "gs: new block %p offset %ld size %ld\n",
	     newBlock, newBlock->gsOffset, newBlock->sizeAllocated);
      thisBlock->gsOffset += roundedSize;
      thisBlock->sizeAllocated -= roundedSize;
      if (debug) printk(KERN_DEBUG "gs: old block %p now offset %ld size %ld\n",
	     thisBlock, thisBlock->gsOffset, thisBlock->sizeAllocated);
      split = 1;
      thisBlock = newBlock;
    }
  }
  thisBlock->sizeRequested = size;
  thisBlock->worker = worker;
  /*
    If we grabbed the whole block, take it off of the free list
  */
  if (!split) {
    if (debug) printk(KERN_DEBUG "gs: took block %p from free list\n", thisBlock);
    list_del_init((struct list_head *) thisBlock);
  }
  /*
    Put it on the in-use list
  */
  list_add((struct list_head *) thisBlock, &gs_busy_blocks);
  if (debug) printk(KERN_DEBUG "gs: block %p now on busy list\n", thisBlock);
  /*
    Fill the first word with a DEAD code as a debug check
  */
  if (debug) GS_WRITE(GS_OFFSET_SDRAM + thisBlock->gsOffset, 0xDEAD);
  restore_flags(flags);
  return thisBlock;
}

void gs_sdram_free (struct gs_sdram *thisBlock)
{
  struct list_head *this, *next;
  unsigned int flags;
  struct gs_sdram *nextBlock, *prevBlock;
  int foundSuccessor = 0;
  if (debug) printk(KERN_DEBUG "gs: release block %p offset %ld size (%ld,%ld)\n",
	 thisBlock, thisBlock->gsOffset, thisBlock->sizeRequested,
	 thisBlock->sizeAllocated);
  save_and_cli(flags);
  /*
    Clear out the worker record and allocation history
  */
  thisBlock->worker = NULL;
  thisBlock->sizeRequested = 0;
  /*
    Take this block off of the in-use list
  */
  this = (struct list_head *) thisBlock;
  list_del_init(this);
  /*
    Fill the first word with a BEEF code as a debug check
  */
  if (debug) GS_WRITE(GS_OFFSET_SDRAM + thisBlock->gsOffset, 0xBEEF);
  /*
    Scan the free list, looking for the successor to this
    block.
  */
  prevBlock = NULL;
  nextBlock = NULL;
  list_for_each(next,&gs_free_blocks) {
    nextBlock = (struct gs_sdram *) next;
    if (nextBlock->gsOffset > thisBlock->gsOffset) {
      foundSuccessor = 1;
      break;
    }
    prevBlock = nextBlock;
  }
  if (!foundSuccessor) {
    if (debug) printk(KERN_DEBUG "gs: no successor\n");
    nextBlock = NULL;
  }
  /*
    If there is a successor, and it starts immediately after this
    one, merge the space owned by this block into the successor
  */
  if (nextBlock &&
      nextBlock->gsOffset == thisBlock->gsOffset + thisBlock->sizeAllocated) {
    if (debug) printk(KERN_DEBUG "gs: merge with successor %p offset %ld size %ld\n",
	   nextBlock, nextBlock->gsOffset, nextBlock->sizeAllocated);
    nextBlock->gsOffset = thisBlock->gsOffset;
    nextBlock->sizeAllocated += thisBlock->sizeAllocated;
    kfree(thisBlock);
    thisBlock = NULL;
    /*
      Check to see if we just "grew" the successor backwards so that it
      abuts the predecessor.  If so, merge the two.
    */
    if (prevBlock && nextBlock->gsOffset ==
	prevBlock->gsOffset + prevBlock->sizeAllocated) {
      if (debug) printk(KERN_DEBUG "gs: also merge with predecessor %p offset %ld size %ld\n",
	     prevBlock, prevBlock->gsOffset, prevBlock->sizeAllocated);
      prevBlock->sizeAllocated += nextBlock->sizeAllocated;
      list_del((struct list_head *) next);
      kfree(nextBlock);
    }
  } else if (prevBlock && thisBlock->gsOffset ==
	     prevBlock->gsOffset + prevBlock->sizeAllocated) {
    /*
      We didn't merge with successor, but there's a predecessor with
      whom we can merge
    */
    if (debug) printk(KERN_DEBUG "gs: merge with predecessor %p offset %ld size %ld\n",
	   prevBlock, prevBlock->gsOffset, prevBlock->sizeAllocated);
    prevBlock->sizeAllocated += thisBlock->sizeAllocated;
    kfree(thisBlock);
  } else {
    /*
      We can't merge at all, so just insert it in the free list.
      May need to add either at the beginning, or right after its predecessor.
    */
    if (debug) printk(KERN_DEBUG "gs: no merge, add to list\n");
    list_add(this, prevBlock ? ((struct list_head *) prevBlock) : &gs_free_blocks);
  }
  restore_flags(flags);
}

/**
 *	gs_init	-	initialise the Giant Slalom adapter isntance
 *	@instance: adapter to configure
 *
 *	Initializes *instance
 *
 *	Notes : I assume that the host, hostno, and id bits have been
 *      set correctly.  I don't care about the irq and other fields. 
 *
 *	Locks: interrupts must be enabled when we are called 
 *
 *      XXX Pretty much all of this needs to be redone for Giant Slalom!
 */

static void __init gs_init(struct Scsi_Host *instance)
{
  int i;
  struct gs_hostdata *hostdata = (struct gs_hostdata *) instance->hostdata;
  
  if(in_interrupt())
    printk(KERN_ERR "gs_init called with interrupts off!\n");
  
  hostdata->aborted = 0;
  hostdata->pldLoaded = hostdata->atapiOK = 0;
  hostdata->id_mask = 1 << instance->this_id;
  for (i = hostdata->id_mask; i <= 0x80; i <<= 1)
    if (i > hostdata->id_mask)
      hostdata->id_higher_mask |= i;
  for (i = 0; i < GS_HANDLERS; ++i) {
    hostdata->busy[i] = 0;
    hostdata->pending[i] = 0;
    hostdata->connected[i] = NULL;
  }
  for (i = 0; i < 8; ++i) {
    hostdata->condition[i] = NothingPending;
  }
  hostdata->targets_present = 0;
  hostdata->issue_queue = NULL;
  
  if (!the_template) {
    the_template = instance->hostt;
    first_instance = instance;
  }

  hostdata->atapiSectorSize = 2048;
  hostdata->atapiBufferModulus = 2048;
  hostdata->time_expires = 0;
  hostdata->resourcesBusy = 0;
  hostdata->next_timer = NULL;
  hostdata->queueLock = RW_LOCK_UNLOCKED;
  INIT_LIST_HEAD(&hostdata->workers);

  hostdata->dmaDescriptors = kmalloc(PAGE_SIZE, GFP_KERNEL);

  printk(KERN_INFO "gs: allocated DMA buffer at 0x%p, %ld bytes / %d = %ld descriptors\n",
	 hostdata->dmaDescriptors, PAGE_SIZE,
	 sizeof (struct BroadcomDMADescriptor),
	 PAGE_SIZE / sizeof (struct BroadcomDMADescriptor));
}

/*
  gs_probe_bridge - see if the bridge PLD has been programmed.

  Check the bridge PLD to see if it's functional.  This is done by putting
  the Broadcom localbus into 16-bit 8-wait-state mode (so it won't
  hang if the PLD doesn't respond), writing a couple of scratch locations
  in the bridge and then reading them back.  If the values read back
  correctly, the bridge is assumed to be functional, and further GS
  probing and initialization can be initiated.

  Do NOT call this function after the GS has been initialized!  Doing
  so will probably yngvi up the bridge.

  Returns 1 if the bridge is alive, 0 if not.

*/

static int gs_probe_bridge(void)
{
  unsigned int nullTerm;

  unsigned short revision, revisionInverted;

  unsigned short iradc, ipadc, iwadc;

  // Setup the 7020 local bus so that Chip Select 3 
  // is a 16-bit device with 15 clocks of waitstate.
  // This allows us to attempt to access the bridge's
  // revision registers, without hanging the system
  // if the bridge is non copos mentus.

  printk(KERN_INFO "gs: probing for bridge\n");

  scsi_sleep(1);

  nullTerm = *BCM_IO_NULL_TERM;

  *BCM_IO_NULL_TERM = (nullTerm & ~0x0C00F000) | 0x0400F000;

  barrier();

  revision = GS_BRIDGE->revision;
  barrier();
  revisionInverted = GS_BRIDGE->revisionInverted;
  barrier();

  if ((revision ^ revisionInverted) == 0xFFFF) {
    printk(KERN_INFO "gs: detected bridge, revision %04X\n", revision);
    scsi_sleep(1);
    /*
      Set the 7020 CS3 to 16-bit mode, let the PLD terminate dsack.
      Set the 7020 CS4 to 16-bit mode, let the PLD terminate dsack.
    */
    *BCM_IO_NULL_TERM = (nullTerm & ~0x3C0FF000) | 0x14000000;
    barrier();
  } else {
    printk(KERN_INFO "gs: bridge not detected (not yet initialized?)\n");
    scsi_sleep(1);
    /* Leave the 7020 CS3 set for 8-wait-state dsack termination */
    return -1;
  }

  // set bus timeout cycles to x20
  *(volatile unsigned short *) KSEG1ADDR(GS_BRIDGE_BASE+6) = 0xF0;
  barrier();

  // reset the GS, wait a moment, then take it out of reset

  GS_BRIDGE->control &= ~GS_CONTROL_GS_nRESET;
  scsi_sleep(1);
  
  // take GS out of reset, enable bus termination.

  GS_BRIDGE->control |= (GS_CONTROL_GS_nRESET |
			 GS_CONTROL_BT_ENABLE);
  scsi_sleep(1);

  if (debug) printk(KERN_INFO "gs: initializing GS SDRAM controller\n");
  scsi_sleep(1);
  
  // initialize GS SDRAM

  GS_WRITE(GS_OFFSET_CORE + 0x0132, 0x8002);  // SDRAM CBR=64M(*16)
  GS_WRITE(GS_OFFSET_CORE + 0x0122, 0x0101);  // SDRAM init

#ifdef NOTDEF
  // Set SDRAM DMA timings to conservative values for now...

  GS_WRITE(GS_DMA_BANK0_CTL, 0x0001);
  GS_WRITE(GS_DMA_BANK1_CTL, 0x0001);
  GS_WRITE(GS_DMA_BANK2_CTL, 0x0001);
  GS_WRITE(GS_DMA_BANK3_CTL, 0x0001);
  GS_WRITE(GS_DMA_BANK4_CTL, 0x0001);
  GS_WRITE(GS_DMA_BANK5_CTL, 0x0001);
  GS_WRITE(GS_DMA_BANK6_CTL, 0x0001);
  GS_WRITE(GS_DMA_BANK7_CTL, 0x0001);
#endif

  // Initialize DMA engines

  if (debug) printk(KERN_INFO "gs: initializing GS DMA engines\n");
  scsi_sleep(1);
  
  iradc = GS_READ(GS_READ_DMA_IRADC);
  iradc &= ~0x8000;
  GS_WRITE(GS_READ_DMA_IRADC, iradc);
  iradc |= 0x8000;
  GS_WRITE(GS_READ_DMA_IRADC, iradc);

  iwadc = GS_READ(GS_WRITE_DMA_IWADC);
  iwadc &= ~0x8000;
  GS_WRITE(GS_WRITE_DMA_IWADC, iwadc);
  iwadc |= 0x8000;
  GS_WRITE(GS_WRITE_DMA_IWADC, iwadc);

  ipadc = GS_READ(GS_ATA_DMA_IAPDC);
  ipadc &= ~0x8000;
  GS_WRITE(GS_ATA_DMA_IAPDC, ipadc);
  ipadc |= 0x8000;
  GS_WRITE(GS_ATA_DMA_IAPDC, ipadc);

  /*
    Initialize serial I/O controller
  */

  GS_WRITE(GS_SERIAL_IODIR2, 0x00FE);
  GS_WRITE(GS_SERIAL_IODATA2, 0x0000);
  GS_WRITE(GS_SERIAL_SELSER, 0x00FE);

  // Enable interrupts from GS and bridge.  Don't enable CS8420 yet

  if (debug) printk(KERN_INFO "gs: initializing GS bridge interrupts\n");

  disable_irq(GS_IRQ);

  scsi_sleep(1);
  
  GS_BRIDGE->imr =
    GS_BRIDGE_INTR_GS_STALL       |
    GS_BRIDGE_INTR_GS_XIRQS;

  if (debug) printk(KERN_INFO "gs: bridge initialization completed\n");
  scsi_sleep(1);
  
  return 0;

}

static void gs_bridge_is_alive(struct Scsi_Host *instance)
{
  int delays;
  unsigned char status;
  ((struct gs_hostdata *) instance->hostdata)->pldLoaded = 1;
  printk(KERN_INFO "gs: resetting ATAPI interface\n");
  GS_WRITE8(GS_ATAPI_CONTROL_REGISTER, 0xC0);
  scsi_sleep(1);
  GS_WRITE8(GS_ATAPI_CONTROL_REGISTER, 0x80);
  delays = 0;
  do {
    scsi_sleep(50);
    status = GS_READ8(GS_ATAPI_ALTSTATUS_REGISTER);
    show_atapi_status();
    delays ++;
  } while (delays < 20 && (status & BUSY_STAT) && status != 0xFF);
  if (status == 0xFF) {
    printk(KERN_INFO "gs: No device attached to ATAPI bus\n");
  } else {
    ((struct gs_hostdata *) instance->hostdata)->atapiOK = 1;
  }
  (void) register_reboot_notifier(&my_notifier);
  notifier_installed = 1;
  if (gs_mem_autoprobe() != 0) {
    printk(KERN_ERR "gs: Giant Slalom SDRAM initialization FAILED\n");
  }
  enable_irq(GS_IRQ);
}

/*
 *	gs_init_broadcom - set up the Broadcom CS3/CS4 chip-select address maps.
 */

static void gs_init_broadcom (void)
{
  *BCM_IO_CS3_ADDR = 0x07ff0400;
  *BCM_IO_CS4_ADDR = 0xbfff8000;
  *BCM_IO_NULL_TERM = (*BCM_IO_NULL_TERM & 0xC3F00FFF) | 0x14000000;
}

/**
 * 	gs_detect	-	look for Giant Slalom controller
 *	@tpnt: the scsi template
 *
 *	Scan for the present of a Giant Slalom ASIC.
 *
 *	Locks: none
 */

int __init gs_detect(Scsi_Host_Template * tpnt)
{
  struct Scsi_Host *instance;

  printk(KERN_INFO "gs: version %s loaded\n", version);

  tpnt->proc_name = "gs";

    if( MsGetBase() == 0 )
    {
      printk( KERN_WARNING "gs: No media switch detected, bailing out\n" );
      return 0;
    }

    /*
     * skanky hack to tunr off I2S interface. This should be done in the kernel
     */
    {
      volatile unsigned long * brcmaddr =  ((unsigned long*)KSEG1ADDR(0x100000f0));
      
      *brcmaddr = *brcmaddr & ~0x00000400;
    }

  printk(KERN_INFO "gs: trying to request bridge IRQ %d\n", GS_IRQ);
  
  if (request_irq(GS_IRQ, do_gs_intr, SA_INTERRUPT, "Giant Slalom", NULL)) {
    printk(KERN_INFO "gs: failed to acquire IRQ %d\n", GS_IRQ);
    return 0;
  }

  printk(KERN_INFO "gs: trying to request DMA IRQ %d\n", DMA_IRQ);
  
  if (request_irq(DMA_IRQ, broadcom_intr, SA_INTERRUPT, "Giant Slalom", NULL)) {
    printk(KERN_INFO "gs: failed to acquire IRQ %d\n", DMA_IRQ);
    return 0;
  }

  printk(KERN_INFO "gs: trying to register adapter\n");
  
  instance = scsi_register(tpnt, sizeof(struct gs_hostdata));
  
  if (instance == NULL) {
    printk(KERN_INFO "gs: failed to register adapter\n");
    free_irq(GS_IRQ, NULL);
    return 0;
  }
  
  printk(KERN_INFO "scsi%d : Giant Slalom bridge at 0x%x mem at 0x%0x irq %d", instance->host_no,
	 GS_BRIDGE_BASE, GS_WINDOW_BASE, GS_IRQ);
  printk("\n");

  scsi_sleep(HZ / 4);

  gs_init(instance);

  gs_init_broadcom();

  printk(KERN_INFO "gs: initializing tasks\n");
  gs_task_struct.routine = gs_task;
  gs_task_struct.data = NULL;
  gs_timer_task_struct.routine = gs_timer_task;
  gs_timer_task_struct.data = NULL;

  if (!gs_probe_bridge()) {
    gs_bridge_is_alive(instance);
  }

  return 1;
}

/**
 *	gs_release_resources	-	free resources
 *	@instance: host adapter to clean up (There Can Be Only One)
 *
 *	Free the generic interface resources from this adapter.
 *
 *	Locks: none
 */
 
int gs_release_resources(struct Scsi_Host *instance)
{
  if (notifier_installed) {
    notifier_installed = 0;
    (void) unregister_reboot_notifier(&my_notifier);
  }
  free_irq(GS_IRQ, NULL);
  free_irq(DMA_IRQ, NULL);
  return 0;
}

static void __inline__ gs_worker_next_state (struct gs_worker *worker,
					 enum gs_worker_state nextState) {
  worker->state = nextState;
  worker->go = 1;
  worker->dwell = 0;
}

static void initialize_SCp(Scsi_Cmnd * cmd)
{
	/* 
	 * Initialize the Scsi Pointer field so that all of the commands in the 
	 * various queues are valid.
	 */

	if (cmd->use_sg) {
		cmd->SCp.buffer = (struct scatterlist *) cmd->buffer;
		cmd->SCp.buffers_residual = cmd->use_sg - 1;
		cmd->SCp.ptr = (char *) cmd->SCp.buffer->address;
		cmd->SCp.this_residual = cmd->SCp.buffer->length;
	} else {
		cmd->SCp.buffer = NULL;
		cmd->SCp.buffers_residual = 0;
		cmd->SCp.ptr = (char *) cmd->request_buffer;
		cmd->SCp.this_residual = cmd->request_bufflen;
	}
}

/*
  Check the buffer(s) pointed to by a command, and see if all of them
  are compatible with the alignment and size rules for a Broadcom
  DMA transfer.  Every buffer must be 4-byte aligned, and a multiple of
  4 bytes.
*/

static int gsCheckHostDMARules (Scsi_Cmnd * cmd)
{
  int i;
  if (((long int) cmd->SCp.ptr) % 4 != 0 ||
      cmd->SCp.this_residual % 4 != 0) {
    return 0;
  }
  for (i = 1; i <= cmd->SCp.buffers_residual; i++) {
    if (((long int) cmd->SCp.buffer[i].address) % 4 != 0 ||
	cmd->SCp.buffer[i].length % 4 != 0) {
      return 0;
    }
  }
  return 1;
}

/*
  See if there is any more data to be transferred via DMA -
  return 0 if done, or residual size of next buffer if not done
*/

static int gsCheckMoreHostDMA (Scsi_Cmnd * cmd)
{
  while (cmd->SCp.this_residual == 0 &&
	 cmd->SCp.buffers_residual > 0) {
    ++cmd->SCp.buffer;
    --cmd->SCp.buffers_residual;
    cmd->SCp.this_residual = cmd->SCp.buffer->length;
    cmd->SCp.ptr = cmd->SCp.buffer->address;
  }
  return cmd->SCp.this_residual;
}

/*
  Set up the Broadcom ROMbus DMA descriptor array.  Each descriptor
  is limited to [1,1024] words of word-aligned data transfer.
*/

#define ONEDMABURST 1020

static void gsSetupHostDMA (struct Scsi_Host *instance, Scsi_Cmnd * cmd,
			    int direction)
{
  struct gs_hostdata *hostdata;
  const int numDescriptors = PAGE_SIZE / sizeof (struct BroadcomDMADescriptor);
  int currentDescriptor;
  int waterline;
  int thisChunk, transferWordCount;
  unsigned long ioAddress;
  unsigned long consumed;
  hostdata = (struct gs_hostdata *) (instance->hostdata);
  currentDescriptor = 0;
  waterline = 256 * 1024; /* size of bridge's PSR region */
  memset(hostdata->dmaDescriptors, 0, PAGE_SIZE);
  if (debug) printk(KERN_INFO "gs: setting up Broadcom descriptors\n");
  if (direction) {
    ioAddress = ((unsigned long) GS_WRITE_WINDOW) & 0x3FFFFFFC;
  } else {
    ioAddress = ((unsigned long) GS_READ_WINDOW) & 0x3FFFFFFC;
  }
  consumed = 0;
  while (waterline > 0 &&
	 currentDescriptor < numDescriptors &&
	 (thisChunk = gsCheckMoreHostDMA(cmd)) > 0) {
    if (debug > 1) printk(KERN_INFO "gs: want a chunk of %d bytes\n", thisChunk);
    if (thisChunk > waterline) {
      thisChunk = waterline;
      if (debug > 1) printk(KERN_INFO "gs: clipped chunk to waterline: %d bytes\n", thisChunk);
    }
    if (thisChunk > ONEDMABURST) {
      thisChunk = ONEDMABURST;
      if (debug > 1) printk(KERN_INFO "gs: clipped chunk to %d bytes\n", ONEDMABURST);
    }
    transferWordCount = thisChunk / 4;
    consumed += thisChunk;
    if (debug > 1) printk(KERN_INFO "gs: want to transfer %d words from 0x%08lX\n",
			  transferWordCount, (unsigned long) cmd->SCp.ptr);
    hostdata->dmaDescriptors[currentDescriptor].transferWordCount_7_0 =
      transferWordCount;
    hostdata->dmaDescriptors[currentDescriptor].transferWordCount_9_8 =
      transferWordCount >> 8;
    hostdata->dmaDescriptors[currentDescriptor].direction = direction;
    hostdata->dmaDescriptors[currentDescriptor].memoryAddr_25_2 =
      (0x03FFFFFC & (unsigned int) cmd->SCp.ptr) >> 2;
    hostdata->dmaDescriptors[currentDescriptor].ioAddr_25_2 = ioAddress >> 2;
    if (debug > 1) printk(KERN_INFO "gs: Broadcom DMA descriptor is %08lX %08lX\n",
			  *(unsigned long *) (&hostdata->dmaDescriptors[currentDescriptor]),
			  *(1 + (unsigned long *) (&hostdata->dmaDescriptors[currentDescriptor])));
    dma_cache_wback_inv((unsigned long) phys_to_virt((unsigned long) cmd->SCp.ptr), thisChunk);
    cmd->SCp.ptr           += thisChunk;
    cmd->SCp.this_residual -= thisChunk;
    waterline              -= thisChunk;
    currentDescriptor ++;
  }
  if (currentDescriptor == 0) {
    printk(KERN_ERR "gs: AIEE!  Null ROMbus DMA!\n");
    panic("Null ROMbus DMA");
  }
  hostdata->dmaDescriptors[currentDescriptor - 1].last = 1;
  if (debug) printk(KERN_INFO "gs: broadcom descriptors from %p to %p, consume %ld bytes\n",
		    &hostdata->dmaDescriptors[0],
		    &hostdata->dmaDescriptors[currentDescriptor - 1],
		    consumed);
#if 0
  {
    int i;
    if (debug) printk(KERN_INFO "gs: byteswapping %d Broadcom descriptors\n", currentDescriptor);
    for (i = 0; i < currentDescriptor * sizeof (struct BroadcomDMADescriptor) / sizeof (unsigned long);
	 i++) {
      *(i + (unsigned long *) hostdata->dmaDescriptors) =
	__cpu_to_le32(*(i + (unsigned long *) hostdata->dmaDescriptors));
    }
  }
#endif
  dma_cache_wback_inv((unsigned long) phys_to_virt((unsigned long) hostdata->dmaDescriptors), PAGE_SIZE);
  /*
    Flush the whole dcache.  Unfortunately, this seems to be necessary to ensure that
    SCSI Generic direct I/O works correctly.  The dma_cache_wback_inv() calls above *ought*
    to be sufficient, I think, and they work fine for non-direct I/O, but are not
    reliable for direct I/O.  I suspect that any "dirty" cache lines in user-virtual
    don't get flushed out correctly if direct I/O is used, because of the fact that
    the memory lines are multiply mapped (once for the user, and once for the kernel
    kiobuf).

    Ought to figure out a better way to fix this.
  */
  flush_dcache();
  if (debug > 1) printk(KERN_INFO "gs: set up %d Broadcom descriptors\n", currentDescriptor);
}

/*
  Transfer data to the caller's buffer(s)
*/

static int gsTransferToUser (Scsi_Cmnd * cmd, unsigned char *buffer, unsigned int length)
{
  int xferred = 0;
  int xfer;
  while (length > 0) {
    if (!cmd->SCp.this_residual) {
      if (cmd->SCp.buffers_residual) {
	++cmd->SCp.buffer;
	--cmd->SCp.buffers_residual;
	cmd->SCp.this_residual = cmd->SCp.buffer->length;
	cmd->SCp.ptr = cmd->SCp.buffer->address;
      } else {
	break;  /* ran out of space */
      }
    }
    xfer = length;
    if (xfer > cmd->SCp.this_residual) {
      xfer = cmd->SCp.this_residual;
    }
    memcpy(cmd->SCp.ptr, buffer, xfer);
    cmd->SCp.ptr += xfer;
    cmd->SCp.this_residual -= xfer;
    buffer += xfer;
    xferred += xfer;
    length -= xfer;
  }
  return xferred;
}

static int gsTransferFromUser (Scsi_Cmnd * cmd, unsigned char *buffer, unsigned int length)
{
  int xferred = 0;
  int xfer;
  while (length > 0) {
    if (!cmd->SCp.this_residual) {
      if (cmd->SCp.buffers_residual) {
	++cmd->SCp.buffer;
	--cmd->SCp.buffers_residual;
	cmd->SCp.this_residual = cmd->SCp.buffer->length;
	cmd->SCp.ptr = cmd->SCp.buffer->address;
      } else {
	break;  /* ran out of space */
      }
    }
    xfer = length;
    if (xfer > cmd->SCp.this_residual) {
      xfer = cmd->SCp.this_residual;
    }
    memcpy(buffer, cmd->SCp.ptr, xfer);
    cmd->SCp.ptr += xfer;
    cmd->SCp.this_residual -= xfer;
    buffer += xfer;
    xferred += xfer;
    length -= xfer;
  }
  return xferred;
}

/*
  Fake up an INQUIRY response
*/

static int gsInquiry (Scsi_Cmnd * cmd, int device,
		      int deviceType,
		      char *vendor,
		      char *model,
		      char *version)
{
  unsigned char inquiryData[36];
  int l;
  memset(inquiryData, 0, 16);
  memset(inquiryData+20, ' ', 20);
  inquiryData[0] = deviceType;
  inquiryData[3] = 2; /* response data format */
  inquiryData[4] = 36 - 4;
  l = strlen(vendor);
  if (l > 8) l = 8;
  strncpy(inquiryData+8, vendor, l);
  l = strlen(model);
  if (l > 16) l = 16;
  strncpy(inquiryData+16, model, l);
  l = strlen(version);
  if (l > 4) l = 4;
  strncpy(inquiryData+32, version, l);
  (void) gsTransferToUser(cmd, inquiryData, 36);
  return 0;
}

/*
  Simulate a CHECK CONDITION error on an emulated device.
  XXX We need to cache the condition somewhere associated with the
  device so we can interpret it when we simulate REQUEST SENSE!
*/

static void gsCheckCondition (struct Scsi_Host *instance,
			      Scsi_Cmnd * cmd, enum FakeCheckCondition cond)
{
  int device = cmd->target;
  printk(KERN_INFO "gs: simulating Check Condition, code %d\n", cond);
  ((struct gs_hostdata *) (instance->hostdata))->condition[device] = cond;
  cmd->SCp.Status = CHECK_CONDITION;
  cmd->result |= (CHECK_CONDITION << 1);
}

/*
  Fake up a Request Sense.  For now make it an
  Illegal Command Code in all cases.
*/

static void gsRequestSense(struct Scsi_Host *instance,
			   Scsi_Cmnd * cmd)
{
  unsigned char senseData[8];
  int device = cmd->target;
  printk(KERN_INFO "gs: simulating Request Sense, code %d\n", ((struct gs_hostdata *) (instance->hostdata))->condition[device]);
  memset(senseData, 0, sizeof senseData);
  senseData[0] = 0x72; /* current error, descriptor sense data format */;
  switch (((struct gs_hostdata *) (instance->hostdata))->condition[device]) {
  case NothingPending:
    senseData[1] = NO_SENSE;
    break;
  case UnitAttention:
    senseData[1] = UNIT_ATTENTION;
    senseData[2] = 0x28; /* NOT READY -> READY, MEDIUM MAY HAVE CHANGED */
    senseData[3] = 0x00; /* NOT READY -> READY, MEDIUM MAY HAVE CHANGED */
    break;
  case IllegalRequest:
    senseData[1] = ILLEGAL_REQUEST;
    senseData[2] = 0x20; /* INVALID COMMAND OPERATION CODE */
    senseData[3] = 0x00; /* INVALID COMMAND OPERATION CODE */
    break;
  case NotReady:
    senseData[1] = NOT_READY;
    senseData[2] = 0x67; /* CONFIGURATION FAILURE */
    senseData[3] = 0x00; /* CONFIGURATION FAILURE */
    break;
  case IllegalAddress:
    senseData[1] = ILLEGAL_REQUEST;
    senseData[2] = 0x24; /* INVALID FIELD IN CDB */
    senseData[3] = 0x00; /* INVALID FIELD IN CDB */
    break;
  default:
    senseData[1] = NOT_READY;
    senseData[2] = 0x20; /* ACCESS DENIED - NO ACCESS RIGHTS */
    senseData[3] = 0x02; /* ACCESS DENIED - NO ACCESS RIGHTS */
    break;
  }
  (void) gsTransferToUser(cmd, senseData, 8);
  ((struct gs_hostdata *) (instance->hostdata))->condition[device] =
    NothingPending;
}

static int gsBridgeDispatchFunction (struct Scsi_Host *instance,
				     Scsi_Cmnd * cmd, int device) 
{
  switch (cmd->cmnd[0]) {
  case TEST_UNIT_READY:
    cmd->result = 0; /* it's OK */;
    return 1;
  case INQUIRY:
    (void) gsInquiry(cmd, device, TYPE_COMM, "Pioneer", "GS bridge", "0001");
    cmd->result = 0; /* it's OK */;
    return 1;
  case REQUEST_SENSE:
    gsRequestSense(instance, cmd);
    cmd->result = 0; /* it's OK */;
    return 1;
  default:
    gsCheckCondition(instance, cmd, IllegalRequest);
    return 1;
  }    
}
     
static int gsRamDispatchFunction (struct Scsi_Host *instance,
				  Scsi_Cmnd * cmd, int device)
{
  unsigned char capacityData[8];
  unsigned int capacityBlocks;
  unsigned int lba = 0, transferLength = 0, direction = 0;
  unsigned short sector[256];
  unsigned int offset;
  unsigned int i;
  if (!((struct gs_hostdata *) (instance->hostdata))->pldLoaded) {
    cmd->result = DID_BAD_TARGET << 16;
    return 1;
  }
  switch (cmd->cmnd[0]) {
  case TEST_UNIT_READY:
    cmd->result = 0; /* it's OK */;
    return 1;
  case INQUIRY:
    (void) gsInquiry(cmd, device, TYPE_DISK, "Pioneer", "GS SDRAM", "0001");
    cmd->result = 0; /* it's OK */;
    return 1;
  case REQUEST_SENSE:
    gsRequestSense(instance, cmd);
    cmd->result = 0; /* it's OK */;
    return 1;
  case READ_CAPACITY:
    capacityBlocks = gs_probed_memsize / 512;
    capacityData[0] = (unsigned char) (capacityBlocks >> 24);
    capacityData[1] = (unsigned char) (capacityBlocks >> 16);
    capacityData[2] = (unsigned char) (capacityBlocks >>  8);
    capacityData[3] = (unsigned char) (capacityBlocks      );
    capacityData[4] = 0;
    capacityData[5] = 0;
    capacityData[6] = 2; /* 512 byte sectors */
    capacityData[7] = 0;
    (void) gsTransferToUser(cmd, capacityData, 8);
    cmd->result = 0; /* it's OK */;
    return 1;
  case READ_6:
    lba = ((cmd->cmnd[1] & 0x1F) << 16) + (cmd->cmnd[2] << 8) + cmd->cmnd[3];
    transferLength = cmd->cmnd[4];
    direction = 0;
    goto transfer;
  case WRITE_6:
    lba = ((cmd->cmnd[1] & 0x1F) << 16) + (cmd->cmnd[2] << 8) + cmd->cmnd[3];
    transferLength = cmd->cmnd[4];
    direction = 01;
    goto transfer;
  case READ_10:
    lba = (cmd->cmnd[2] << 24) + (cmd->cmnd[3] << 16) + (cmd->cmnd[4] << 8) + cmd->cmnd[5];
    transferLength = (cmd->cmnd[7] << 8) + cmd->cmnd[8];
    direction = 0;
    goto transfer;
  case WRITE_10:
    lba = (cmd->cmnd[2] << 24) + (cmd->cmnd[3] << 16) + (cmd->cmnd[4] << 8) + cmd->cmnd[5];
    transferLength = (cmd->cmnd[7] << 8) + cmd->cmnd[8];
    direction = 1;
    transfer:      capacityBlocks = gs_probed_memsize / 512;
    if (capacityBlocks < lba + transferLength) {
      gsCheckCondition(instance, cmd, IllegalAddress);
      return 1;
    }
    offset = lba * 512;
    while (transferLength-- > 0) {
      if (direction == 1) {
	if (gsTransferFromUser(cmd, (unsigned char *) sector, 512) != 512) {
	  transferLength = 0;
	  continue;
	}
      }
      for (i = 0; i < 256; i ++) {
	if (direction == 0) {
	  sector[i] = GS_READ(GS_OFFSET_SDRAM+offset);
	} else {
	  GS_WRITE(GS_OFFSET_SDRAM+offset, sector[i]);
	}
	offset += 2;
      }
      if (direction == 0) {
	if (gsTransferToUser(cmd, (unsigned char *) sector, 512) != 512) {
	  transferLength = 0;
	  continue;
	}
      }
    }
    return 1;
  default:
    gsCheckCondition(instance, cmd, IllegalRequest);
    return 1;
  }    
  return 0;
}
     
static int gsFlDispatchFunction (struct Scsi_Host *instance,
				 Scsi_Cmnd * cmd, int device)
{
  struct gs_worker *worker;
  struct gs_hostdata *hostdata = (struct gs_hostdata *) (instance->hostdata);
  unsigned long length;
  unsigned long commandLength = 0;
  unsigned long flags;
  int i;
  (void) device;
  if (!((struct gs_hostdata *) (instance->hostdata))->pldLoaded) {
    cmd->result = DID_BAD_TARGET << 16;
    return 1;
  }
  switch (cmd->cmnd[0]) {
  case TEST_UNIT_READY:
    cmd->result = 0; /* it's OK */;
    return 1;
  case INQUIRY:
    (void) gsInquiry(cmd, device, TYPE_COMM, "Pioneer", "GS FL SPI", "0001");
    cmd->result = 0; /* it's OK */;
    return 1;
  case REQUEST_SENSE:
    gsRequestSense(instance, cmd);
    cmd->result = 0; /* it's OK */;
    return 1;
  case WRITE_6:
  case WRITE_10:
  case WRITE_12:
    length = cmd->SCp.this_residual;
    for (i = 0; i < cmd->SCp.buffers_residual; i++) {
      length += cmd->SCp.buffer[i].length;
    }
    switch (cmd->cmnd[0]) {
    case WRITE_6:
      commandLength = cmd->cmnd[4];
      break;
    case WRITE_10:
      commandLength = (cmd->cmnd[7] << 8) + cmd->cmnd[8];
      break;
    case WRITE_12:
      commandLength = (cmd->cmnd[6] << 24) + (cmd->cmnd[7] << 16) +
	(cmd->cmnd[8] << 8) + cmd->cmnd[9];
      break;
    }
    if (length < commandLength) {
      gsCheckCondition(instance, cmd, IllegalAddress);
      return 0;
    }
    /*
      Create a Worker for this FL write request and queue it up...
    */
    worker = (struct gs_worker *) kmalloc(sizeof (struct gs_worker),
					  GFP_ATOMIC);
    if (!worker) {
      return -1; /* requeue it for later */
    }
    memset(worker, 0, sizeof *worker);
    worker->sn = ++hostdata->workerSN;
    worker->scsiRequest = cmd;
    worker->transferSize = commandLength;
    gs_worker_next_state(worker, GS_WORKER_SETUP_FL);
    save_and_cli(flags);
    list_add_tail((struct list_head *) worker, &hostdata->workers);
    restore_flags(flags);
    hostdata->connected[GS_HANDLER_FL] = NULL;
    hostdata->busy[GS_HANDLER_FL] = 0;
    return 0;
  default:
    gsCheckCondition(instance, cmd, IllegalRequest);
    return 1;
  }    
  return 0;
}
     
static int gsDacDispatchFunction (struct Scsi_Host *instance,
				 Scsi_Cmnd * cmd, int device)
{
  struct gs_worker *worker;
  struct gs_hostdata *hostdata = (struct gs_hostdata *) (instance->hostdata);
  unsigned long length;
  unsigned long commandLength = 0;
  unsigned long flags;
  int i;
  (void) device;
  if (!((struct gs_hostdata *) (instance->hostdata))->pldLoaded) {
    cmd->result = DID_BAD_TARGET << 16;
    return 1;
  }
  switch (cmd->cmnd[0]) {
  case TEST_UNIT_READY:
    cmd->result = 0; /* it's OK */;
    return 1;
  case INQUIRY:
    (void) gsInquiry(cmd, device, TYPE_COMM, "Pioneer", "GS DAC SPI", "0001");
    cmd->result = 0; /* it's OK */;
    return 1;
  case REQUEST_SENSE:
    gsRequestSense(instance, cmd);
    cmd->result = 0; /* it's OK */;
    return 1;
  case WRITE_6:
  case WRITE_10:
  case WRITE_12:
    length = cmd->SCp.this_residual;
    for (i = 0; i < cmd->SCp.buffers_residual; i++) {
      length += cmd->SCp.buffer[i].length;
    }
    switch (cmd->cmnd[0]) {
    case WRITE_6:
      commandLength = cmd->cmnd[4];
      break;
    case WRITE_10:
      commandLength = (cmd->cmnd[7] << 8) + cmd->cmnd[8];
      break;
    case WRITE_12:
      commandLength = (cmd->cmnd[6] << 24) + (cmd->cmnd[7] << 16) +
	(cmd->cmnd[8] << 8) + cmd->cmnd[9];
      break;
    }
    if (length < commandLength) {
      gsCheckCondition(instance, cmd, IllegalAddress);
      return 0;
    }
    /*
      Create a Worker for this FL write request and queue it up...
    */
    worker = (struct gs_worker *) kmalloc(sizeof (struct gs_worker),
					  GFP_ATOMIC);
    if (!worker) {
      return -1; /* requeue it for later */
    }
    memset(worker, 0, sizeof *worker);
    worker->sn = ++hostdata->workerSN;
    worker->scsiRequest = cmd;
    worker->transferSize = commandLength;
    gs_worker_next_state(worker, GS_WORKER_SETUP_DAC);
    save_and_cli(flags);
    list_add_tail((struct list_head *) worker, &hostdata->workers);
    restore_flags(flags);
    hostdata->connected[GS_HANDLER_DAC] = NULL;
    hostdata->busy[GS_HANDLER_DAC] = 0;
    return 0;
  default:
    gsCheckCondition(instance, cmd, IllegalRequest);
    return 1;
  }    
  return 0;
}
     
static int gsDriveDispatchFunction (struct Scsi_Host *instance,
				    Scsi_Cmnd * cmd, int device)
{
  struct gs_worker *worker;
  struct gs_hostdata *hostdata = (struct gs_hostdata *) (instance->hostdata);
  unsigned long flags;
  (void) device;
  if (debug) printk(KERN_INFO "gs: drive dispatch function for cmd %p\n", cmd);
  if (!hostdata->pldLoaded) {
    cmd->result = DID_BAD_TARGET << 16;
    return 1;
  }
  if (!hostdata->atapiOK) {
    cmd->result = DID_BAD_TARGET << 16;
    return 1;
  }
  /*
    Create a Worker for this ATAPI request and queue it up...
  */
  if (debug > 1) printk(KERN_INFO "gs: allocating worker\n");
  worker = (struct gs_worker *) kmalloc(sizeof (struct gs_worker),
				       GFP_ATOMIC);
  if (debug > 1) printk(KERN_INFO "gs: got worker at %p\n", worker);
  if (!worker) {
    return -1; /* requeue it for later */
  }
  memset(worker, 0, sizeof *worker);
  worker->sn = ++hostdata->workerSN;
  worker->scsiRequest = cmd;
  if (debug) printk(KERN_INFO "gs: worker is #%d\n", worker->sn);
  gs_worker_next_state(worker, GS_WORKER_SETUP);
  save_and_cli(flags);
  list_add_tail((struct list_head *) worker, &hostdata->workers);
  /*
    Take it out of the Connected slot and mark us non-busy
  */
  hostdata->connected[GS_HANDLER_DRIVE] = NULL;
  hostdata->busy[GS_HANDLER_DRIVE] = 0;
  restore_flags(flags);
  return 0;
}

static int gsBridgeIoctlFunction (struct Scsi_Host *instance,
				  Scsi_Device *dev, int cmd, void *arg)
{
  int result = 0;
  unsigned long flags;
  struct gs_hostdata *hostdata = (struct gs_hostdata *) (instance->hostdata);

  (void) dev;

  if (cmd != kGSDownloadPLD && !hostdata->pldLoaded) {
    printk(KERN_INFO "gs: access ioctl() on a dead bridge!\n");
    return -EINVAL;
  }
    
  switch( cmd )
    {
    case kGSReadBridge:
      {
	GSIODescriptor descriptor;

	copy_from_user( &descriptor, (void*)arg, sizeof( descriptor ) );

	{
	  short *dest = (short *)(descriptor.data);
	  volatile short *src = GetGSBridgeAddress( descriptor.address );
	  int i;

	  for( i = 0; i < descriptor.length; i += 2 )
	    {
	      short value = *src;
	      barrier();
	      put_user( value, dest );
	      if (debug > 1) {
		printk(KERN_INFO "gs: peek bridge %08lx has  %04x\n",
		       (unsigned long) src, value);
	      }
	      ++src; ++dest;
	    }
	}
	break;
      }
    case kGSWriteBridge:
      {
	GSIODescriptor descriptor;

	copy_from_user( &descriptor, (void*)arg, sizeof( descriptor ) );

	{
	  short *src = (short *)(descriptor.data);
	  volatile short *dest = GetGSBridgeAddress( descriptor.address );
	  int i;

	  for( i = 0; i < descriptor.length; i += 2 )
	    {
	      short value;
	      get_user( value, src );
	      if (debug > 1) {
		printk(KERN_INFO "gs: poke bridge %08lx with %04x\n",
		       (unsigned long) dest, value);
	      }
	      *dest = value;
	      barrier();
	      ++src; ++dest;
	    }
	}
	break;
      }

    case kGSWriteBridgeMasked:
      {
	GSMaskDescriptor descriptor;
	volatile short *dest;

	copy_from_user( &descriptor, (void*)arg, sizeof( descriptor ) );
	dest = GetGSBridgeAddress( descriptor.address );

	save_and_cli(flags);

	*dest = (*dest & ~descriptor.mask) |
	  (descriptor.bits & descriptor.mask);

	restore_flags(flags);

	break;
      }

    case kGSReadWindow:
      {
	GSIODescriptor descriptor;

	copy_from_user( &descriptor, (void*)arg, sizeof( descriptor ) );

	{
	  short *dest = (short *)(descriptor.data);
	  int i;

	  for( i = 0; i < descriptor.length; i += 2 )
	    {
	      short value = GS_READ(descriptor.address);
	      put_user( value, dest );

	      if (debug > 0) {
		printk(KERN_INFO "gs: peek window %08lx has  %04x\n",
		       descriptor.address, value & 0x0000FFFF);
	      }
	      descriptor.address += 2;
	      ++dest;
	    }
	}
	break;
      }
    case kGSWriteWindow:
      {
	GSIODescriptor descriptor;

	copy_from_user( &descriptor, (void*)arg, sizeof( descriptor ) );

	{
	  short *src = (short *)(descriptor.data);
	  int i;

	  for( i = 0; i < descriptor.length; i += 2 )
	    {
	      short value;
	      get_user( value, src );
	      if (debug > 0 &&
		  hostdata->resourcesBusy & (GS_RESOURCE_ATAPI |
					     GS_RESOURCE_HOSTDMA)) {
		printk(KERN_INFO "gs: Caution: poke window %08lx with %04x while in use!\n",
		       descriptor.address, value & 0x0000FFFF);
	      }
	      if (debug > 0) {
		printk(KERN_INFO "gs: poke window %08lx with %04x\n",
		       descriptor.address, value & 0x0000FFFF);
	      }
	      GS_WRITE(descriptor.address, value);
	      ++src; 
	      descriptor.address += 2;
	    }
	}
	break;
      }
    case kGSDownloadPLD:
      {
	GSPLDDescriptor descriptor;
	int i;
	
	if (((struct gs_hostdata *) (instance->hostdata))->pldLoaded) {
	  printk(KERN_INFO "gs: WARNING - reprogramming a live bridge!\n");
	  ((struct gs_hostdata *) (instance->hostdata))->pldLoaded = 0;
	}

	copy_from_user( &descriptor, (void*)arg, sizeof( descriptor ) );

	{

	  unsigned char *src = (unsigned char *)(descriptor.data);

	  volatile unsigned char *dest = 0xA + (unsigned char *) GS_BRIDGE;

	  unsigned long nullTerm = *BCM_IO_NULL_TERM;

	  unsigned long gpioCtl = MsRead( MS_GPIOOBE_REG );

	  unsigned long gpioData = MsRead( MS_GPIOOB_REG );

	  // Setup the 7020 local bus so that Chip Select 3 
	  // is an 8-bit device with ~ 8 clocks of waitstate.

	  nullTerm = *BCM_IO_NULL_TERM;

	  *BCM_IO_NULL_TERM = (nullTerm & ~0x0C00F000) | 0x08008000;

	  barrier();

		// Pulse the "Program" signal to the FPGA low and then high. 
		// This signal is connected to the MS GPIO6 pin.
		// Set the "Chip Select" and "Write" signals to the FPGA low. 
		// These signals are connected to the MS GPIO9 pin.

	  MsWrite( MS_GPIOOB_REG, ( gpioData & ~0x00000240 ) | 0x00000240 );
	  MsWrite( MS_GPIOOBE_REG, ( gpioCtl & ~0x00000240 ) | 0x00000240 );

	  MsWrite( MS_GPIOOB_REG, ( gpioData & ~0x000000240 ) | 0x00000200 );
	  gs_wait ( 1 );
	  MsWrite( MS_GPIOOB_REG, ( gpioData & ~0x00000240 ) | 0x00000240 );
	  gs_wait ( 1 );
	  MsWrite( MS_GPIOOB_REG, ( gpioData & ~0x00000240 ) | 0x00000040 );

	  // Load each byte of configuration data into the FPGA by doing byte
	  // a writes to the Chip Select 3 address space (0x1c100000).

	  for( i = 0; i < descriptor.length; ++i )
	    {
	      unsigned char value;
	      get_user( value, src );
	      *dest = value;
	      ++src;
	    }

	  // When all the data is written, set the "Chip Select" and "Write"
	  // signals to the FPGA high (MS GPIO9 pin).
	  gs_wait ( 1 );
	  MsWrite( MS_GPIOOB_REG, gpioData | 0x00000240 );

	  // Restore the 7020 local bus chip select 3 settings for 16-bit device

	  *BCM_IO_NULL_TERM = nullTerm;
	  barrier();

	  // Probe the bridge to see if it responds.

	  if (!gs_probe_bridge()) {
	    gs_bridge_is_alive(instance);
	  }

	}
	break;
      }
    case kGSInitDone:
      {
	// Diagnostics/self-test software has finished running, has reset
	// pretty much everything inside the chip.  Reinitialize the
	// necessary subcomponents...
	
	// ... SDRAM controller

	GS_WRITE(GS_OFFSET_CORE + 0x0132, 0x8002);  // SDRAM CBR=64M(*16)
	GS_WRITE(GS_OFFSET_CORE + 0x0122, 0x0101);  // SDRAM init

	// ... RA DMA
	
	GS_WRITE(GS_READ_DMA_RADC,  0x0005); /* RA DMA TRACON REQ stop */
	GS_WRITE(GS_READ_DMA_IRADC, 0x8132); /* RA DMA stop */
	GS_WRITE(GS_READ_DMA_IRADC, 0x0132); /* RA DMA module reset */
	GS_WRITE(GS_READ_DMA_IRADC, 0x8132); /* RA DMA module reset release */
	GS_WRITE(GS_READ_DMA_RADC,  0x0004); /* RA DMA FIFO reset */
	GS_WRITE(GS_READ_DMA_RADC,  0x0005); /* RA DMA FIFO reset release */

	// ... WA DMA

	GS_WRITE(GS_WRITE_DMA_WADC,  0x0005); /* WA DMA TRACON REQ stop */
	GS_WRITE(GS_WRITE_DMA_IWADC, 0x8132); /* WA DMA stop */
	GS_WRITE(GS_WRITE_DMA_IWADC, 0x0132); /* WA DMA module reset */
	GS_WRITE(GS_WRITE_DMA_IWADC, 0x8132); /* WA DMA module reset release */
	GS_WRITE(GS_WRITE_DMA_WADC,  0x0004); /* WA DMA FIFO reset */
	GS_WRITE(GS_WRITE_DMA_WADC,  0x0005); /* WA DMA FIFO reset release */

	// ... serial controller

	GS_WRITE(GS_SERIAL_IODIR2, 0x00FE);
	GS_WRITE(GS_SERIAL_IODATA2, 0x0000);
	GS_WRITE(GS_SERIAL_SELSER, 0x00FE);

	hostdata->initDone = 1;
	hostdata->diagMode = 0;

	if (debug) printk("gs: kGSInitDone\n");
	break;
      }
    case kGSSetDebugMode:
      {
	debug = (int) arg;
	printk(KERN_INFO "gs: debug level set to %d\n", debug);
	break;
      }
    case kGSResetFIFOs:
      {
	struct gs_worker *worker;
	unsigned long *doneFlag;
	/*
	  Caller has requested a FIFO/DMA reset.  This is implemented
	  via a command-less worker and a couple of special states in
	  the FSM.  As there is no SCSI command associated with this
	  request, we use a simple "done" flag set by the worker's
	  completion callback, and poll it every 10 ms.
	*/
	worker = (struct gs_worker *) kmalloc(sizeof (struct gs_worker),
					      GFP_ATOMIC);
	if (!worker) {
	  return -ENOMEM;
	}
	memset(worker, 0, sizeof *worker);
	worker->sn = ++hostdata->workerSN;
	doneFlag = kmalloc(sizeof (unsigned long), GFP_ATOMIC);
	if (!doneFlag) {
	  kfree(worker);
	  return -ENOMEM;
	}
	*doneFlag = 0;
	worker->endactionToken = doneFlag;
	worker->endaction = gsWorkerDoneCallback;
	gs_worker_next_state(worker, GS_WORKER_SETUP_RESET);
	save_and_cli(flags);
	list_add_tail((struct list_head *) worker, &hostdata->workers);
	restore_flags(flags);
	queue_task (&gs_task_struct, &tq_immediate);
	mark_bh (IMMEDIATE_BH);
	while (!*doneFlag) {
	  scsi_sleep(HZ / 100);
	}
	kfree(doneFlag);
	break;
      }
    case kGSReserve:
      {
	break; /* not yet implemented, treat as a NO-OP */
      }
    case kGSRelease:
      {
	break; /* not yet implemented, treat as a NO-OP */
      }
    case kGSReadFifoReset:
      {
	/*
	  Diagnostic code says that it has finished pulling data
	  out of the test FIFO.  Since the DMA transfer which feeds
	  data into this FIFO cannot generate a completion
	  interrupt, we must find the Worker holding the transfer
	  open, and awaken it so that it will shut down gracefully
	  and release the DMA channel and SDRAM.

	  Someday, I'd just love to work on a project where the
	  hardware guys consult with the software guys _before_
	  the silicon tapes out.  It'd save *so* much grottiness in the
	  driver code...
	*/
	struct gs_worker *worker;
	struct list_head *this;
	save_and_cli(flags);
	list_for_each(this,&hostdata->workers) {
	  worker = (struct gs_worker *) this;
	  if (worker->backgroundDMA) {
	    worker->go = 1;
	  }
	}
	restore_flags(flags);
	queue_task (&gs_task_struct, &tq_immediate);
	mark_bh (IMMEDIATE_BH);
	break;
      }
    case kGSReportDiagMode:
      {
	hostdata->diagMode = (int) arg;
	break;
      }
    default:
      {
	result = -EINVAL;
      }
    }
  return result;
}


static void gs_release_chip_resources (struct Scsi_Host *instance, unsigned int resourceMask)
{
  struct list_head *this;
  struct gs_worker *worker;
  struct gs_hostdata *hostdata;
  unsigned long flags;
  unsigned int releaseMask;
  unsigned int intersectMask;
  hostdata = (struct gs_hostdata *) instance->hostdata;
  releaseMask = resourceMask;
  hostdata->resourcesBusy &= ~resourceMask;
  save_and_cli(flags);
  list_for_each(this,&hostdata->workers) {
    worker = (struct gs_worker *) this;
    if (worker->gsResourcesUsed & resourceMask) {
      worker->gsResourcesUsed &= ~resourceMask;
    }
    intersectMask = worker->gsResourcesNeeded & releaseMask;
    if (intersectMask) {
      worker->gsResourcesNeeded &= ~intersectMask;
      worker->go = 1;
      intersectMask &= ~GS_MULTIPLE_GRABBERS;
      releaseMask &= ~intersectMask;
    }
  }
  restore_flags(flags);
}

/*
  Specially-exported routine for the FL driver to use.
  Generates a Worker which pushes data out to the FL display's
  SPI interface.
*/

int xmit_gs_spi (unsigned char *data, int len, void (*callback)(int))
{
  struct gs_hostdata *hostdata;
  struct gs_worker *worker;
  unsigned long flags;
  if (!first_instance) {
    return XMIT_GS_SPI_FAILURE;
  }
  hostdata = (struct gs_hostdata *) first_instance->hostdata;
  if (!hostdata->pldLoaded) {
    return XMIT_GS_SPI_FAILURE;
  }
  worker = (struct gs_worker *) kmalloc(sizeof (struct gs_worker),
					GFP_ATOMIC);
  if (!worker) {
    return XMIT_GS_SPI_FAILURE;
  }
  memset(worker, 0, sizeof *worker);
  worker->sn = ++hostdata->workerSN;
  worker->writeback.transformBuffer = kmalloc(len, GFP_ATOMIC);
  if (!worker->writeback.transformBuffer) {
    kfree(worker);
    return XMIT_GS_SPI_FAILURE;
  }
  memcpy(worker->writeback.transformBuffer, data, len);
  worker->transferSize = worker->transferRemaining = len;  
  worker->endaction = gsXmitDoneCallback;
  worker->endactionToken = (void *) callback;
  gs_worker_next_state(worker, GS_WORKER_SETUP_FL);
  save_and_cli(flags);
  list_add_tail((struct list_head *) worker, &hostdata->workers);
  restore_flags(flags);
  queue_task (&gs_task_struct, &tq_immediate);
  mark_bh (IMMEDIATE_BH);
  return XMIT_GS_SPI_SUCCESS;
}

/*
  Other exported routines, for other drivers which must manipulate
  the GS bridge registers.
*/

int GSReadBridge(unsigned long address, unsigned long length, void *data )
{
  struct gs_hostdata *hostdata;
  unsigned long flags;
  unsigned long *dataWord;
  if (!first_instance) {
    return -ENODEV;
  }
  hostdata = (struct gs_hostdata *) first_instance->hostdata;
  if (!hostdata->pldLoaded) {
    return -ENODEV;
  }
  dataWord = (unsigned long *) data;
  save_and_cli(flags);
  while (length > 0) {
    *dataWord = *((unsigned long *) (GS_BRIDGE_BASE + address));
    length -= sizeof (unsigned long);
    address += sizeof (unsigned long);
    dataWord += sizeof (unsigned long);
  }
  restore_flags(flags);
  return 0;
}

int GSWriteBridge(unsigned long address, unsigned long length, void *data )
{
  struct gs_hostdata *hostdata;
  unsigned long flags;
  unsigned long *dataWord;
  if (!first_instance) {
    return -ENODEV;
  }
  hostdata = (struct gs_hostdata *) first_instance->hostdata;
  if (!hostdata->pldLoaded) {
    return -ENODEV;
  }
  dataWord = (unsigned long *) data;
  save_and_cli(flags);
  while (length > 0) {
    *((unsigned long *) (GS_BRIDGE_BASE + address)) = *dataWord;
    length -= sizeof (unsigned long);
    address += sizeof (unsigned long);
    dataWord += sizeof (unsigned long);
  }
  restore_flags(flags);
  return 0;
}

int GSWriteBridgeMasked(unsigned long address, unsigned short mask,
			unsigned short bits)
{
  struct gs_hostdata *hostdata;
  unsigned long flags;
  if (!first_instance) {
    return -ENODEV;
  }
  hostdata = (struct gs_hostdata *) first_instance->hostdata;
  if (!hostdata->pldLoaded) {
    return -ENODEV;
  }
  save_and_cli(flags);
  *((unsigned long *) (GS_BRIDGE_BASE + address)) =
    (~mask & *((unsigned long *) (GS_BRIDGE_BASE + address))) |
    ( mask & bits);
  restore_flags(flags);
  return 0;
}

static void show_atapi_status(void)
{
  const char* bits[8] = {"BSY ", "DRDY ", "FAULT ", "SeekComplete ",
			 "DRQ ", "obs2 ", "obs1 ", "ERR "};
  int i;
  unsigned short stat = GS_READ8(GS_ATAPI_ALTSTATUS_REGISTER);
  printk(KERN_INFO "gs: ATAPI bus status is 0x%02X: ", stat);
  for (i = 0; i < 8; i++) {
    if (stat & (0x80 >> i)) {
      printk(bits[i]);
    }
  }
  printk("\n");
}

#ifdef NOTDEF

static void show_pci_intr_status(void)
{
  printk(KERN_INFO "gs: PCI interrupt enable %08lX status %08lX\n",
	 *BCM_PCI_INTR_REG, *BCM_PCI_INTR_STAT);
}

#endif

  
/*
  State-machine nodes for the ATAPI device driver.
*/

static void gs_worker_setup(struct Scsi_Host *instance, struct gs_worker *worker)
{
  unsigned long int length, i;
  Scsi_Cmnd *cmd;  
  struct gs_sdram *sdram = NULL;
  unsigned int flags;
  unsigned int diagByte;
  struct gs_hostdata *hostdata;
  hostdata = (struct gs_hostdata *) instance->hostdata;
  cmd = (Scsi_Cmnd *) worker->scsiRequest;
  if (cmd->timeout_per_command) {
    worker->potentialDeadman = cmd->timeout_per_command; /* convert to ticks */
  }
  memset(cmd->sense_buffer, 0, sizeof cmd->sense_buffer);
  length = cmd->SCp.this_residual;
  if (debug > 1) printk(KERN_INFO "gs: buffer %p(%ld)\n", cmd->SCp.ptr, length);
  for (i = 1; i <= cmd->SCp.buffers_residual; i++) {
    if (debug > 1) printk(KERN_INFO "gs: %p(%d)\n", cmd->SCp.buffer[i].address,
		      cmd->SCp.buffer[i].length);
    length += cmd->SCp.buffer[i].length;
  }
  if (debug) printk(KERN_INFO "gs: setup request, command 0x%02X for %ld bytes\n", cmd->cmnd[0],
	 length);
  memcpy(worker->writeback.cmd, cmd->cmnd, 12);
  worker->sc_data_direction = cmd->sc_data_direction;
  /*
    Transform several older SCSI commands into the newer forms recognized
    by ATAPI devices.  The command transform occurs in the writeback area,
    with the original command being left intact in the SCSI command packet.

    XXX Need to also monitor MODE SELECT commands (here) and MODE SENSE
    (in postprocessing) to track the sector size in use, as it's
    important for handling ATAPI and host DMA buffering.
  */
  diagByte = 0;
  switch (worker->writeback.cmd[0]) {
  case INQUIRY:
    worker->writeback.transformBufferLength = length;
    worker->writeback.transformBuffer = kmalloc(length, GFP_ATOMIC);
    break;
  case READ_10:
  case WRITE_10:
    diagByte = worker->writeback.cmd[6];
    worker->writeback.cmd[6] = 0;
    break;
  case READ_12:
  case WRITE_12:
    diagByte = worker->writeback.cmd[10];
    worker->writeback.cmd[10] = 0;
    break;
  case READ_6:
  case WRITE_6:
    worker->writeback.cmd[8] = worker->writeback.cmd[4];
    worker->writeback.cmd[6] = 0;
    worker->writeback.cmd[5] = worker->writeback.cmd[3];
    worker->writeback.cmd[4] = worker->writeback.cmd[2];
    worker->writeback.cmd[3] = worker->writeback.cmd[1] & 0x1f;	
    worker->writeback.cmd[2] = 0;
    worker->writeback.cmd[1] &= 0xe0;
    worker->writeback.cmd[0] += (READ_10 - READ_6);
    break;
  case MODE_SENSE:
  case MODE_SELECT:
    if (length == 0) {
      break;
    }
    length += 4;
    if (debug) printk(KERN_INFO "gs: bumped transfer size to %ld for new MODE SENSE/SELECT\n", length);
    worker->writeback.transformBufferLength = length;
    worker->writeback.transformBuffer = kmalloc(length, GFP_ATOMIC);
    /* XXX should do something if this fails, albeit it's unlikely */
    memset(worker->writeback.transformBuffer, 0, length);
    memset(worker->writeback.cmd, 0, 12);
    worker->writeback.cmd[0] = cmd->cmnd[0] | 0x40;
    worker->writeback.cmd[1] = cmd->cmnd[1];
    worker->writeback.cmd[2] = cmd->cmnd[2];
    worker->writeback.cmd[8] = cmd->cmnd[4] + 4;
    worker->writeback.cmd[9] = cmd->cmnd[5];
    if (cmd->cmnd[4] + 4 > 255) {
      worker->writeback.cmd[7] = cmd->cmnd[4] + 4 - 255;
    }
    if (worker->writeback.cmd[0] == MODE_SELECT_10) {
      unsigned long took;
      unsigned char firstFour[4];
      if (debug) printk(KERN_INFO "gs: transforming data from MODE SELECT to MODE SELECT 10\n");
      took = gsTransferFromUser(cmd, firstFour, 4);
      if (took != 4) {
	printk(KERN_ERR "gs: unexpected error during MODE SELECT transformation A\n");
      }
      took = gsTransferFromUser(cmd, worker->writeback.transformBuffer + 8, length - 8);
      if (took != length - 8) {
	printk(KERN_ERR "gs: unexpected error during MODE SELECT transformation B\n");
      }
      worker->writeback.transformBuffer[1] = firstFour[0];	/* Mode data length */
      worker->writeback.transformBuffer[2] = firstFour[1];	/* Medium type */
      worker->writeback.transformBuffer[3] = firstFour[2];	/* Device specific parameter */
      worker->writeback.transformBuffer[7] = firstFour[3];	/* Block descriptor length */
    }
    break;
  default:
    break;
  }
  worker->diagByte = diagByte;
  if ((worker->writeback.cmd[0] == READ_10 ||
       worker->writeback.cmd[0] == READ_12 ||
       worker->writeback.cmd[0] == WRITE_10 ||
       worker->writeback.cmd[0] == WRITE_12) &&
      length > 0 &&
      (length % hostdata->atapiSectorSize) == 0 &&
      worker->writeback.transformBufferLength == 0) {
    worker->useAtapiDMA = 1;
    worker->useHostDMA = 0;
    if (debug) printk(KERN_INFO "gs: initDone %d   diagByte %d  dmaRules %d\n",
		      hostdata->initDone, diagByte, gsCheckHostDMARules(cmd));
    if (diagByte != 0) {
      if (!hostdata->initDone) {
	printk(KERN_ERR "gs: DMA requested but chip not fully initialized!\n");
	worker->diagByte = 0;
      } else if (hostdata->diagMode == 0) {
	printk(KERN_ERR "gs: DMA requested but mode not set!\n");
	worker->diagByte = 0;
      } else if (!gsCheckHostDMARules(cmd)) {
	printk(KERN_ERR "gs: DMA requested but data size/alignment forbids!\n");
	worker->diagByte = 0;
      } else {
	if (debug) printk(KERN_INFO "gs: using host DMA\n");
	worker->useHostDMA = diagByte;
      }
    } else {
      if (debug) printk(KERN_INFO "gs: not using host DMA\n");
    }
    save_and_cli(flags);
    sdram = gs_sdram_alloc (worker, length);
    if (!sdram) {
      worker->gsResourcesNeeded |= GS_RESOURCE_SDRAM;
      restore_flags(flags);
      printk(KERN_INFO "gs: sdram alloc failed, opening gate\n");
      gs_release_chip_resources(instance, GS_RESOURCE_GATE);
      return;
    }
    restore_flags(flags);
    worker->writeback.buffer = sdram;
    if (debug) printk(KERN_INFO "gs: sdram allocated %ld bytes at offset 0x%0lX\n",
	   sdram->sizeAllocated, sdram->gsOffset);
  }
  worker->transferSize = length;
  worker->transferRemaining = length;
  /*
    If we're doing a write, record the amount of memory we're
    buffering up, so that we can do intelligent burst-writing at a
    later time via the write gate.  We'll make the decision as to
    whether to release the write gate when we're done DMA'ing into
    SDRAM.

    If we're doing any non-gated command, and there's anything gated
    ahead of us, release the gate immediately!
  */
  if (worker->writeback.cmd[0] == WRITE_10 ||
      worker->writeback.cmd[0] == WRITE_12) {
    worker->gatedSize = worker->transferSize;
  } else if (gs_gated_requests(instance) > 0) {
    if (debug) printk(KERN_INFO "gs: worker %d opening the gate!\n", worker->sn);
    gs_release_chip_resources(instance, GS_RESOURCE_GATE);
  }
  if (worker->useAtapiDMA &&
      worker->sc_data_direction == SCSI_DATA_WRITE) {
    if (worker->useHostDMA &&
	worker->useHostDMA != 3 &&
	worker->useHostDMA != 4 &&
	worker->useHostDMA != 44) {
      gs_worker_next_state(worker, GS_WORKER_DMA_TO_SDRAM);
    } else {
      gs_worker_next_state(worker, GS_WORKER_PIO_TO_SDRAM);
    }
  } else if (worker->useAtapiDMA &&
	     worker->useHostDMA == 42 &&
	     worker->sc_data_direction == SCSI_DATA_READ) {
    /*
      Disgusting debug hack, remove before ship!  Bypass the ATAPI
      read operation entirely... fill the GS SDRAM buffer with a
      deterministic pattern, and then enter the DMA-to-the-host
      phase without actually reading anything from the drive.
    */
    if (debug) printk(KERN_INFO "gs: filling %ld bytes at 0x%lX\n",
		      sdram->sizeAllocated, sdram->gsOffset);
    for (i = 0; i < sdram->sizeAllocated; i += 2) {
      GS_WRITE(GS_OFFSET_SDRAM + sdram->gsOffset + i, (unsigned short) i);
      {
	int j;
	j = GS_READ(GS_OFFSET_SDRAM + sdram->gsOffset + i);
	if ((unsigned short) i != (unsigned short) j) {
	  printk(KERN_INFO "gs: write %04X to offset %ld, read back %04X\n",
		 (unsigned short) i, i, (unsigned short) j);
	}
      }
    }
    if (debug) {
      printk(KERN_INFO "gs: bypass ATAPI entirely!\n");
      printk(KERN_INFO "gs: faked data at 0x%lX is",
	     worker->writeback.buffer->gsOffset);
      for (i = 0; i < 32; i += 2) {
	printk(" %04X", GS_READ(GS_OFFSET_SDRAM +
				worker->writeback.buffer->gsOffset + i));
      }
      printk("\n");
    }
    gs_worker_next_state(worker, GS_WORKER_DMA_FROM_SDRAM);
  } else {
    gs_worker_next_state(worker, GS_WORKER_QUEUE_FOR_ATAPI);
  }
}

static void gs_worker_pio_to_sdram(struct Scsi_Host *instance,
				  struct gs_worker *worker)
{
  (void) instance;
  unsigned short tempbuf[32];
  int bcount, chomp, words, took;
  unsigned long offset;
  Scsi_Cmnd *cmd;  
  struct gs_hostdata *hostdata;
  hostdata = (struct gs_hostdata *) instance->hostdata;
  cmd = (Scsi_Cmnd *) worker->scsiRequest;
  bcount = worker->transferSize;
  offset = worker->writeback.buffer->gsOffset;
  if (debug) printk(KERN_INFO "gs: PIO %d bytes to the SDRAM at 0x%0lX\n",
	 bcount, offset);
  while (bcount > 0) {
    chomp = bcount;
    if (chomp > sizeof tempbuf) {
      chomp = sizeof tempbuf;
    }
    bcount -= chomp;
    words = chomp / 2;
    memset(tempbuf, 0, sizeof tempbuf);
    took = gsTransferFromUser(cmd, (unsigned char *) tempbuf, chomp);
    GS_WRITE_MULTI(GS_OFFSET_SDRAM+offset, words, tempbuf);
    offset += chomp;
  }
  if (worker->useHostDMA == 44) {
    int i;
    int sum1, sum2;
    sum1 = 0;
    sum2 = 0;
    for (i = 0; i < worker->transferSize; i += 2) {
      sum1 += GS_READ(GS_OFFSET_SDRAM +
		      worker->writeback.buffer->gsOffset + i);
      sum2 += sum1;
    }
    sum1 ^= sum2;
    printk(KERN_INFO "gs: PIO transfer, data hash is 0x%08X\n",
	   sum1);
    gs_worker_next_state(worker, GS_WORKER_POSTPROCESS);
    return;
  }
  if (worker->useHostDMA == 3 || worker->useHostDMA == 4) {
    gs_worker_next_state(worker, GS_WORKER_DMA_FROM_SDRAM);
  } else {
    gs_worker_next_state(worker, GS_WORKER_DETACH_COMMAND);
  }
}

static void gs_worker_dma_to_sdram(struct Scsi_Host *instance,
				   struct gs_worker *worker)
{
  struct gs_hostdata *hostdata;
  unsigned short wadc, iwadc, wama, wact;
  unsigned long flags;
  unsigned long offset, sectors;
  hostdata = (struct gs_hostdata *) instance->hostdata;
  if (worker->aborting) {
    gs_worker_next_state(worker, GS_WORKER_DONE);
    return ;
  }
  if (hostdata->resourcesBusy & GS_RESOURCE_HOSTDMA) {
    worker->gsResourcesNeeded |= GS_RESOURCE_HOSTDMA;
    return;
  }
  worker->gsResourcesUsed |= GS_RESOURCE_HOSTDMA;
  hostdata->resourcesBusy |= GS_RESOURCE_HOSTDMA;
  if (debug) printk(KERN_INFO "gs: we own the host DMA bus (-> SDRAM)\n");
  wadc = GS_READ(GS_WRITE_DMA_WADC);
  iwadc = GS_READ(GS_WRITE_DMA_IWADC);
  wama = GS_READ(GS_WRITE_DMA_WAMA);
  /*
    Reset the write DMA pathway
  */
  if (debug) printk(KERN_INFO "gs: WADC %04X IWADC %04X WAMA %04X\n", wadc, iwadc, wama);
  wadc |= 0x0004;      
  GS_WRITE(GS_WRITE_DMA_WADC, wadc);  /* DMA TRACON REQ off */
  iwadc &= ~0x0001;
  GS_WRITE(GS_WRITE_DMA_IWADC, iwadc);   /* stop the DMA */
  iwadc &= ~0x8000;
  GS_WRITE(GS_WRITE_DMA_IWADC, iwadc);   /* DMA module reset */
  iwadc |= 0x8000;
  GS_WRITE(GS_WRITE_DMA_IWADC, iwadc);   /* DMA module reset release*/
  wadc &= ~0x01;
  GS_WRITE(GS_WRITE_DMA_WADC, wadc);   /* Reset write FIFO */
  wadc |= 0x01;
  GS_WRITE(GS_WRITE_DMA_WADC, wadc);   /* release reset on write FIFO*/
  if (debug) printk(KERN_INFO "gs: WADC %04X IWADC %04X WAMA %04X\n", wadc, iwadc, wama);
  offset = worker->writeback.buffer->gsOffset;
  sectors = worker->transferSize / hostdata->atapiSectorSize;
  if (hostdata->atapiBufferModulus == 2048) {
    wama = (wama & ~0x0007) | 0x02;
    wact = 0;
  } else {
    wama = (wama & ~0x0007) | 0x04;
    wact = (hostdata->atapiSectorSize / 2) - 1;
  }
  if (debug) printk(KERN_INFO "gs: WADC %04X IWADC %04X WAMA %04X\n", wadc, iwadc, wama);
  GS_WRITE(GS_WRITE_DMA_WADB, offset / hostdata->atapiBufferModulus);
  GS_WRITE(GS_WRITE_DMA_WANB, sectors - 1);
  GS_WRITE(GS_WRITE_DMA_WAMA, wama);   /* set per-sector storage modulus */
  GS_WRITE(GS_WRITE_DMA_WACT, wact);
  if (debug) printk(KERN_INFO "gs: xfersize %d index %ld sectors %ld\n",
	 (hostdata->atapiSectorSize / 2) - 1,
	 offset / hostdata->atapiBufferModulus, sectors);
  if (debug) printk(KERN_INFO "gs: WACT %04X WADB %04X WANB %04X\n",
	 GS_READ(GS_WRITE_DMA_WACT),
	 GS_READ(GS_WRITE_DMA_WADB),
	 GS_READ(GS_WRITE_DMA_WANB));
  save_and_cli(flags);
  wadc &= ~0x04;
  GS_WRITE(GS_WRITE_DMA_WADC, wadc);   /* Set up DREQ */
  iwadc &= ~0x10;
  GS_WRITE(GS_WRITE_DMA_IWADC, iwadc);   /* unmask DMA-done interrupt */
  iwadc |= 0x01;
  GS_WRITE(GS_WRITE_DMA_IWADC, iwadc);   /* start the DMA */
  if (debug) printk(KERN_INFO "gs: WADC %04X IWADC %04X WAMA %04X\n", wadc, iwadc, wama);
  if (debug) printk(KERN_INFO "gs: GS write DMA initiated\n");
  gs_queue_worker_for_interrupt (worker, GS_INT_HOST_DMA | 
				 GS_INT_PSW_STALL,
				 GS_WORKER_DO_PSW_DMA_BURST);
  worker->go = 1;
  restore_flags(flags);
}

static void gs_worker_do_psw_dma_burst(struct Scsi_Host *instance,
					struct gs_worker *worker)
{
  Scsi_Cmnd *cmd;  
  struct gs_hostdata *hostdata;
  unsigned short wadc, iwadc, wama;
  unsigned long flags;
  int bailout;
  hostdata = (struct gs_hostdata *) instance->hostdata;
  if (debug) printk(KERN_INFO "gs: in do_psw_dma_burst\n");
  cmd = (Scsi_Cmnd *) worker->scsiRequest;
  wadc = GS_READ(GS_WRITE_DMA_WADC);
  iwadc = GS_READ(GS_WRITE_DMA_IWADC);
  wama = GS_READ(GS_WRITE_DMA_WAMA);
  /*
    If we hit a PSW stall, or if the midlayer says that we have
    timed out, it's BAD.  Shut down both DMA engines and
    bail out.
  */
  bailout = 0;
  if (worker->aborting) {
    printk(KERN_ERR "gs: DMA-from-host is being aborted due to timeout!\n");
    bailout = 1;
  }
  if (!(worker->gsInterruptsPending & GS_INT_PSW_STALL)) {
    printk(KERN_ERR "gs: PSW stall detected, aborting DMA!\n");
    bailout = 1;
  }
  if (bailout) {
    save_and_cli(flags);
    if (gs_broadcom_enabled) {
      disable_irq(DMA_IRQ);
      gs_broadcom_enabled = 0;
    }
    printk(KERN_INFO "gs: Broadcom DMA debug register says 0x%08X\n",
	   *BCM_IO_DMA0_DEB);
    printk(KERN_INFO "gs: Broadcom DMA cntrl register says 0x%08X\n",
	   *BCM_IO_DMA0_CTL);
    iwadc |= 0x10;
    GS_WRITE(GS_WRITE_DMA_IWADC, iwadc);   /* mask DMA-done interrupt */
    wadc |= 0x0004;      
    GS_WRITE(GS_WRITE_DMA_WADC, wadc);  /* DMA TRACON REQ off */
    iwadc &= ~0x0001;
    GS_WRITE(GS_WRITE_DMA_IWADC, iwadc);   /* stop the DMA */
    iwadc &= ~0x8000;
    GS_WRITE(GS_WRITE_DMA_IWADC, iwadc);   /* DMA module reset */
    iwadc |= 0x8000;
    GS_WRITE(GS_WRITE_DMA_IWADC, iwadc);   /* DMA module reset release*/
    wadc &= ~0x01;
    GS_WRITE(GS_WRITE_DMA_WADC, wadc);   /* Reset write FIFO */
    wadc |= 0x01;
    GS_WRITE(GS_WRITE_DMA_WADC, wadc);   /* release reset on write FIFO*/
#ifdef NOTDEF
    GS_WRITE(GS_OFFSET_TRACON+0x2F6, 0x0000); /* reset DMA engine */
    GS_WRITE(GS_OFFSET_TRACON+0x2F6, 0x0001); /* release reset */
#endif
    *BCM_IO_DMA0_CTL = 0x02;  /* halt Broadcom DMA, force into reset */
    barrier();
    *BCM_IO_DMA0_REC = 0x00;  /* disable Broadcom DMA interrupt */
    barrier();
    restore_flags(flags);
    if (debug) {
      int i;
      printk(KERN_INFO "gs: DMA'ed data is");
      for (i = 0; i < 32; i += 2) {
	printk(" %04X", GS_READ(GS_OFFSET_SDRAM +
				worker->writeback.buffer->gsOffset + i));
      }
      printk("\n");
    }
    worker->reportError = 1;
    worker->retries ++; /* pretend we retried, 'cause we can't restart... */
    gs_release_chip_resources(instance, GS_RESOURCE_HOSTDMA);
    gs_worker_next_state(worker, GS_WORKER_POSTPROCESS);
    gs_autodebug_on();
    return;
  }
  /*
    OK... things are healthy.  See whether the host side of the DMA is
    done yet, or if we have more yet to do.
  */
  if (gsCheckMoreHostDMA(cmd) == 0) {
    /*
      Host side is done.  If we haven't gotten the DMA-done signal from
      the GS as well, go back to sleep and wait until it arrives.
    */
    if (worker->gsInterruptsPending & GS_INT_HOST_DMA) {
      if (debug) printk(KERN_INFO "gs: awaiting final interrupt from GS!\n");
      return;
    }
    /*
      OK, we're in good shape.  Release the DMA resources and continue.
      Clear out the PSW-stall interrupt from our mask, as we are no
      longer interested in it.
    */
    gs_release_chip_resources(instance, GS_RESOURCE_HOSTDMA);
    worker->gsInterruptsPending &= ~GS_INT_PSW_STALL;
    /*
      Yet another cheezoid debug hack.
    */
    if (worker->useHostDMA == 42) {
      int i;
      int sum1, sum2;
      sum1 = 0;
      sum2 = 0;
      for (i = 0; i < worker->transferSize; i += 2) {
	sum1 += GS_READ(GS_OFFSET_SDRAM +
			worker->writeback.buffer->gsOffset + i);
	sum2 += sum1;
      }
      sum1 ^= sum2;
      printk(KERN_INFO "gs: DMA transfer, data hash is 0x%08X\n",
	     sum1);
      gs_worker_next_state(worker, GS_WORKER_POSTPROCESS);
      return;
    }
    gs_worker_next_state(worker, GS_WORKER_DETACH_COMMAND);
    return;
  }
  /*
    We have more data to be written to the GS.
  */
  save_and_cli(flags);
  /*
    Set up the Broadcom DMA descriptors and start the engine.
  */
  gsSetupHostDMA (instance, cmd, 1);
  if (debug) printk(KERN_INFO "gs: starting Broadcom write-DMA burst\n");
  save_and_cli(flags);
  *BCM_IO_DMA0_CTL = 0x02;  /* force Broadcom into reset */
  barrier();
  *BCM_IO_DMA0_CTL = 0x00;  /* release reset */
  barrier();
  *BCM_IO_DMA0_REC = (1 << 26) |                         /* interrupt enable */
    (((unsigned long) hostdata->dmaDescriptors) & 0x03FFFFFF); /* desc. addr */
  barrier();
  GS_BRIDGE->imr |= GS_BRIDGE_INTR_PSW_STALL;
  worker->gsInterruptsPending |= GS_INT_BROADCOM_DMA | GS_INT_PSW_STALL;
  enable_irq(DMA_IRQ);
  gs_broadcom_enabled = 1;
  if (debug) printk(KERN_INFO "gs: DMA rec register is 0x%08X\n",
		    *BCM_IO_DMA0_REC);
  *BCM_IO_DMA0_CTL = 0x01; /* start the DMA, non-wraparound */
  restore_flags(flags);
  if (debug) printk(KERN_INFO "gs: write-DMA burst started, IRQ enabled\n");
}

static void gs_worker_detach_command(struct Scsi_Host *instance, struct gs_worker *worker)
{
  struct gs_hostdata *hostdata;
  Scsi_Cmnd *cmd;  
  cmd = (Scsi_Cmnd *) worker->scsiRequest;
  unsigned int totalGated;
  hostdata = (struct gs_hostdata *) instance->hostdata;
  if (worker->aborting) {
    gs_worker_next_state(worker, GS_WORKER_DONE);
    return ;
  }
  if (hostdata->haveDeferredSense) {
    if (debug) printk(KERN_INFO "gs: killing worker %d with deferred sense data\n", worker->sn);
    memcpy(cmd->sense_buffer, hostdata->deferred_sense_buffer, SCSI_SENSE_BUFFERSIZE);
    if (cmd->sense_buffer[0] == 0x70 || cmd->sense_buffer[0] == 0x72) {
      cmd->sense_buffer[0] ++; /* change from current to deferred */
    }
    if (debug) print_sense("gs", cmd);
    hostdata->haveDeferredSense = 0;
    worker->retries = 1;
    gs_worker_next_state(worker, GS_WORKER_DONE);
    return ;
  }
  if (worker->gatedSize == 0) {
    gs_worker_next_state(worker, GS_WORKER_QUEUE_FOR_ATAPI);
    return;
  }
  if (debug) printk(KERN_INFO "gs: %d bytes are gated\n", gs_gated_requests(instance));
  totalGated = gs_gated_requests(instance);
  if (totalGated > GS_SDRAM_GATE_BARRIER) {
    if (debug) printk(KERN_INFO "gs: pausing worker %d, SDRAM too full\n",
		      worker->sn);
    worker->gsResourcesNeeded |= GS_RESOURCE_SDRAM;
    return;
  }
  else if (totalGated >= GS_SDRAM_GATE_THRESHOLD) {
    if (debug) printk(KERN_INFO "gs: worker %d opening the gate - threshold!\n", worker->sn);
    gs_release_chip_resources(instance, GS_RESOURCE_GATE);
  }
  if (debug) printk(KERN_INFO "gs: worker %d running detached!\n", worker->sn);
  worker->scsiRequest = NULL;
  if (debug) printk(KERN_INFO "gs: gs_main calls done on %p\n", cmd);
  cmd->scsi_done(cmd);
  gs_worker_next_state(worker, GS_WORKER_QUEUE_FOR_ATAPI);
}

static void gs_worker_queue_for_atapi(struct Scsi_Host *instance, struct gs_worker *worker)
{
  unsigned int flags;
  struct gs_hostdata *hostdata;
  hostdata = (struct gs_hostdata *) instance->hostdata;
  if (worker->aborting) {
    gs_worker_next_state(worker, GS_WORKER_DONE);
    return ;
  }
  if (debug) printk(KERN_INFO "gs: worker %d will try to acquire rights to the ATAPI bus\n", worker->sn);
  save_and_cli(flags);
  if (!(hostdata->resourcesBusy & GS_RESOURCE_ATAPI)) {
    worker->gsResourcesUsed |= GS_RESOURCE_ATAPI;
    hostdata->resourcesBusy |= GS_RESOURCE_ATAPI;
    if (debug) printk(KERN_INFO "gs: worker %d owns the ATAPI bus\n", worker->sn);
    if (worker->gatedSize != 0) {
      gs_worker_next_state(worker, GS_WORKER_QUEUE_FOR_GATE);
    } else {
      gs_worker_next_state(worker, GS_WORKER_INIT_ATAPI);
    }
    restore_flags(flags);
    return;
  }
  worker->gsResourcesNeeded |= GS_RESOURCE_ATAPI;
  if (debug) printk(KERN_INFO "gs: the bus was busy\n");
  if (worker->gatedSize == 0) {
    if (debug) printk(KERN_INFO "gs: opening the gate\n");
    gs_release_chip_resources(instance, GS_RESOURCE_GATE);
  }
  restore_flags(flags);
}

static void gs_worker_queue_for_gate(struct Scsi_Host *instance, struct gs_worker *worker)
{
  unsigned int flags;
  struct gs_hostdata *hostdata;
  hostdata = (struct gs_hostdata *) instance->hostdata;
  if (worker->aborting) {
    gs_worker_next_state(worker, GS_WORKER_DONE);
    return ;
  }
  if (debug) printk(KERN_INFO "gs: worker %d will try to acquire the write gate\n", worker->sn);
  save_and_cli(flags);
  if (!(hostdata->resourcesBusy & GS_RESOURCE_GATE)) {
    worker->gsResourcesUsed |= GS_RESOURCE_GATE;
    hostdata->resourcesBusy |= GS_RESOURCE_GATE;
    gs_worker_next_state(worker, GS_WORKER_INIT_ATAPI);
    if (debug) printk(KERN_INFO "gs: worker %d owns the write gate\n", worker->sn);
    restore_flags(flags);
    return;
  }
  worker->gsResourcesNeeded |= GS_RESOURCE_GATE;
  if (debug) printk(KERN_INFO "gs: the gate was busy or closed\n");
  restore_flags(flags);
}

static void gs_worker_init_atapi(struct Scsi_Host *instance, struct gs_worker *worker)
{
  (void) instance;
  unsigned long flags;
  unsigned long bcount;
  unsigned short foo;
  unsigned short status;
  int delays;
  struct gs_hostdata *hostdata;
  Scsi_Cmnd *cmd;  
  cmd = (Scsi_Cmnd *) worker->scsiRequest;
  hostdata = (struct gs_hostdata *) instance->hostdata;
  if (worker->aborting) {
    gs_release_chip_resources(instance, GS_RESOURCE_ATAPI);
    gs_worker_next_state(worker, GS_WORKER_DONE);
    return ;
  }
  if (hostdata->haveDeferredSense) {
    if (cmd) {
      if (debug) printk(KERN_INFO "gs: killing worker %d with deferred sense data\n", worker->sn);
      memcpy(cmd->sense_buffer, hostdata->deferred_sense_buffer, SCSI_SENSE_BUFFERSIZE);
      if (cmd->sense_buffer[0] == 0x70 || cmd->sense_buffer[0] == 0x72) {
	cmd->sense_buffer[0] ++; /* change from current to deferred */
      }
      if (debug) print_sense("gs", cmd);
      hostdata->haveDeferredSense = 0;
      worker->retries = 1;
    } else {
      if (debug) printk(KERN_INFO "gs: flushing worker %d due to deferred sense data\n", worker->sn);
    }
    gs_worker_next_state(worker, GS_WORKER_DONE);
    return ;
  }
  if (hostdata->wasReset) {
    printk(KERN_INFO "gs: making sure ATAPI bus has settled after being reset...\n");
    hostdata->wasReset = 0;
    gs_queue_worker_for_timer(worker, HZ * 2, GS_WORKER_INIT_ATAPI);
    return;
  }
  if (hostdata->mustReset) {
    printk(KERN_INFO "gs: resetting ATAPI interface as was requested...\n");
    hostdata->mustReset = 0;
    GS_WRITE8(GS_ATAPI_CONTROL_REGISTER, 0xC0);
    GS_WRITE8(GS_ATAPI_CONTROL_REGISTER, 0x80);
    gs_queue_worker_for_timer(worker, HZ, GS_WORKER_INIT_ATAPI);
    return;
  }
  if (!cmd) {
    if (debug) printk(KERN_INFO "gs: setting worker %d deadman timer to %d ticks\n",
		      worker->sn, worker->potentialDeadman);
    gs_worker_set_deadman(worker, worker->potentialDeadman);
  }
  if (worker->diagByte == 43) {
    int sleeptime = worker->transferSize / 2000 + (jiffies % 3);
    int ticks = sleeptime * HZ / 1000;
    if (debug) printk(KERN_INFO "gs: worker %d simulates a %d-msec %d-tick ATAPI op\n",
		      worker->sn, sleeptime, ticks);
    gs_queue_worker_for_timer(worker, ticks, GS_WORKER_POSTPROCESS);
    if (worker->sc_data_direction == SCSI_DATA_READ) {
      if (worker->useHostDMA) {
	worker->state = GS_WORKER_DMA_FROM_SDRAM;
      } else if (worker->useAtapiDMA) {
	worker->state = GS_WORKER_PIO_FROM_SDRAM;
      }
    }
    return;
  }
  bcount = worker->transferSize;
  if (bcount > 63 * 1024) {
    bcount = 63 * 1024;
  }
  worker->transferBurst = bcount;
  worker->transferRemaining -= bcount;
  if (debug) printk(KERN_INFO "gs: prep ATAPI for %s packet, %ld bytes\n", 
	 worker->useAtapiDMA ? "DMA" : "PIO", bcount);
  save_and_cli(flags);
  /*
    Clear and then enable the ATA interrupt in the GS before we start
    poking...
  */
  status = GS_READ8(GS_ATAPI_STATUS_REGISTER);
  foo = GS_READ(GS_ATA_INTERRUPT_MASK_REGISTER);
  foo |= GS_ATA_INTERRUPT_CLEAR | GS_ATA_INTERRUPT_MASK;
  GS_WRITE(GS_ATA_INTERRUPT_MASK_REGISTER, foo);
  foo &= ~GS_ATA_INTERRUPT_CLEAR;
  GS_WRITE(GS_ATA_INTERRUPT_MASK_REGISTER, foo);
  foo &= ~GS_ATA_INTERRUPT_MASK;
  GS_WRITE(GS_ATA_INTERRUPT_MASK_REGISTER, foo);
  GS_WRITE8(GS_ATAPI_SELECT_REGISTER, 0xA0);
  GS_WRITE8(GS_ATAPI_CONTROL_REGISTER, 0x08);
  GS_WRITE8(GS_ATAPI_FEATURE_REGISTER,
	    worker->useAtapiDMA ? 0x01 : 0x00);
  GS_WRITE8(GS_ATAPI_BCOUNTH_REGISTER, bcount >> 8);
  GS_WRITE8(GS_ATAPI_BCOUNTL_REGISTER, bcount & 0xFF);
  /*
    Put the data interface into PIO mode by default
  */
  foo = GS_READ(GS_ATA_DMA_APDC);
  foo &= ~0x04;
  GS_WRITE(GS_ATA_DMA_APDC, foo);
  /*
    Initiate the packet command
  */
  GS_WRITE8(GS_ATAPI_COMMAND_REGISTER, WIN_PACKETCMD);
  
  /*
    Do a null read to the alt-status register to guarantee the
    proper timings.  Then, read the status register and wait for the
    drive to go not-busy.  If it takes more than a few hundred microseconds,
    bail.
  */
  (void) GS_READ8(GS_ATAPI_ALTSTATUS_REGISTER);
  delays = 0;
  status = GS_READ8(GS_ATAPI_STATUS_REGISTER);
  while (status & BUSY_STAT) {
    if (delays++ > 400) {
      printk(KERN_INFO "gs: device is stuck BUSY!\n");
      hostdata->mustReset = 1;
      worker->reportError = 1;
      gs_worker_next_state(worker, GS_WORKER_DONE);
      return;
    }
    status = GS_READ8(GS_ATAPI_STATUS_REGISTER);
  } 
  /*
    Go on and send the packet
  */
  gs_worker_next_state(worker, GS_WORKER_SEND_PACKET);
  if (debug) printk(KERN_INFO "gs: WIN_PACKETCMD written\n");
}

static void gs_worker_send_packet(struct Scsi_Host *instance, struct gs_worker *worker)
{
  Scsi_Cmnd *cmd;  
  unsigned long flags;
  int i;
  unsigned short bytes;
  (void) instance;
  cmd = (Scsi_Cmnd *) worker->scsiRequest;
  if (!GS_READ8(GS_ATAPI_STATUS_REGISTER) & DRQ_STAT) {
    printk(KERN_ERR "gs: sent WIN_PACKETCMD but DRQ isn't set\n");
    worker->reportError = 1;
    gs_worker_next_state(worker, GS_WORKER_DONE);
    return;
  }
  if (debug) printk(KERN_INFO "gs: WIN_PACKETCMD accepted, sending ATAPI packet %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n",
	 worker->writeback.cmd[0], worker->writeback.cmd[1],
	 worker->writeback.cmd[2], worker->writeback.cmd[3], 
	 worker->writeback.cmd[4], worker->writeback.cmd[5],
	 worker->writeback.cmd[6], worker->writeback.cmd[7], 
	 worker->writeback.cmd[8], worker->writeback.cmd[9],
	 worker->writeback.cmd[10], worker->writeback.cmd[11]);
  save_and_cli(flags);
  for (i = 0; i < 12; i += 2) {
    bytes = (worker->writeback.cmd[i] << 8) | worker->writeback.cmd[i+1];
    GS_WRITE(GS_ATAPI_DATA_REGISTER, bytes);
  }
  /*
    Packet sent.  If doing DMA, go start up the engine.  If doing PIO,
    wait for an interrupt.

    There's a potential race in the DMA case, as we might get an
    interrupt from the drive while we're in the midst of setting up the
    data transfer.  This could occur if the device immediately generates
    an interrupt with an ERR.  We queue up for the ATAPI interrupt here,
    before we re-enable interrupts, so that we don't lose the ATAPI
    interrupt if this occurs.
  */
  if (debug) printk(KERN_INFO "gs: packet sent!\n");
  if (worker->useAtapiDMA) {
    worker->gsInterruptsFired = 0;
    gs_queue_worker_for_interrupt (worker, GS_INT_ATAPI, GS_WORKER_DMA_DATA);
    worker->go = 1;
  } else {
    gs_queue_worker_for_interrupt (worker, GS_INT_ATAPI, GS_WORKER_PIO_DATA);
  }
  restore_flags(flags);
}

static void gs_worker_dma_data(struct Scsi_Host *instance, struct gs_worker *worker)
{
  (void) instance;
  unsigned char stat;
  Scsi_Cmnd *cmd;  
  struct gs_hostdata *hostdata;
  unsigned short apdc, iapdc, apma;
  unsigned long flags;
  unsigned long offset, sectors;
  if (debug) printk(KERN_INFO "gs: here we set up DMA data transfer...\n");
  hostdata = (struct gs_hostdata *) instance->hostdata;
  cmd = (Scsi_Cmnd *) worker->scsiRequest;
  save_and_cli(flags);
  stat = GS_READ8(GS_ATAPI_ALTSTATUS_REGISTER);
  if (stat & ERR_STAT) {
    restore_flags(flags);
    if (debug) printk(KERN_INFO "gs: drive responded to command 0x%02X with an ERR\n",
	   worker->writeback.cmd[0]);
    if (debug && worker->retries) {
      printk(KERN_INFO "gs: I guess the error is persistent\n");
      gs_worker_next_state(worker, GS_WORKER_DONE);
    } else {
      gs_worker_next_state(worker, GS_WORKER_INIT_FOR_REQUEST_SENSE);
    }
    return;
  }
  if (!stat & DRQ_STAT) {
    gs_queue_worker_for_interrupt (worker, GS_INT_ATAPI, GS_WORKER_DMA_DATA);
    restore_flags(flags);
    printk(KERN_ERR "gs: send packet, no ERR, no DRQ!\n");
    return;
  }
  restore_flags(flags);
  apdc = GS_READ(GS_ATA_DMA_APDC);
  iapdc = GS_READ(GS_ATA_DMA_IAPDC);
  apma = GS_READ(GS_ATA_DMA_APMA);
  if (debug) printk(KERN_INFO "gs: APDC %04X IAPDC %04X APMA %04X\n", apdc, iapdc, apma);
  iapdc &= ~ 0x8001;
  GS_WRITE(GS_ATA_DMA_IAPDC, iapdc); /* mask interrupt, assert DMA reset */
  if (debug) printk(KERN_INFO "gs: APDC %04X IAPDC %04X APMA %04X\n", apdc, iapdc, apma);
  iapdc |=  0x8000;
  GS_WRITE(GS_ATA_DMA_IAPDC, iapdc); /* release DMA reset */
  if (debug) printk(KERN_INFO "gs: APDC %04X IAPDC %04X APMA %04X\n", apdc, iapdc, apma);
  apdc &= ~0x01;
  GS_WRITE(GS_ATA_DMA_APDC, apdc);   /* FIFO reset */
  if (debug) printk(KERN_INFO "gs: APDC %04X IAPDC %04X APMA %04X\n", apdc, iapdc, apma);
  if (worker->sc_data_direction == SCSI_DATA_WRITE) {
    if (debug) printk(KERN_INFO "gs: setting APDC direction for WRITE (SDRAM->ATAPI)\n");
    apdc &= ~0x02;
  } else {
    if (debug) printk(KERN_INFO "gs: setting APDC direction for READ (ATAPI->SDRAM)\n");
    apdc |= 0x02;
  }
  GS_WRITE(GS_ATA_DMA_APDC, apdc);   /* set DMA direction */
  if (debug) printk(KERN_INFO "gs: APDC %04X IAPDC %04X APMA %04X\n", apdc, iapdc, apma);
  apdc |= 0x01;
  GS_WRITE(GS_ATA_DMA_APDC, apdc);   /* release FIFO reset */
  if (debug) printk(KERN_INFO "gs: APDC %04X IAPDC %04X APMA %04X\n", apdc, iapdc, apma);
  offset = worker->writeback.buffer->gsOffset;
  sectors = worker->transferSize / hostdata->atapiSectorSize;
  if (hostdata->atapiBufferModulus == 2048) {
    apma = (apma & ~0x0007) | 0x02;
  } else {
    apma = (apma & ~0x0007) | 0x04;
  }
  GS_WRITE(GS_ATA_DMA_APMA, apma);   /* set per-sector storage modulus */
  if (debug) printk(KERN_INFO "gs: APDC %04X IAPDC %04X APMA %04X\n", apdc, iapdc, apma);
  GS_WRITE(GS_ATA_DMA_APCT, (hostdata->atapiSectorSize / 2) - 1);
  GS_WRITE(GS_ATA_DMA_APDB, offset / hostdata->atapiBufferModulus);
  GS_WRITE(GS_ATA_DMA_APNB, sectors - 1);
  if (debug) printk(KERN_INFO "gs: xfersize %d index %ld sectors %ld\n",
	 (hostdata->atapiSectorSize / 2) - 1,
	 offset / hostdata->atapiBufferModulus, sectors);
  if (debug) printk(KERN_INFO "gs: APCT %04X APDB %04X APNB %04X\n",
	 GS_READ(GS_ATA_DMA_APCT),
	 GS_READ(GS_ATA_DMA_APDB),
	 GS_READ(GS_ATA_DMA_APNB));
  save_and_cli(flags);
  apdc |= 0x04; /* enable DMA mode on the data interface */
  GS_WRITE(GS_ATA_DMA_APDC, apdc);   /* enable DMA on the interface */
  iapdc &= ~0x10;
  GS_WRITE(GS_ATA_DMA_IAPDC, iapdc);   /* unmask DMA-done interrupt */
  if (debug) printk(KERN_INFO "gs: APDC %04X IAPDC %04X APMA %04X\n", apdc, iapdc, apma);
  iapdc |= 0x01;
  GS_WRITE(GS_ATA_DMA_IAPDC, iapdc);   /* start the DMA */
  if (debug) printk(KERN_INFO "gs: APDC %04X IAPDC %04X APMA %04X\n", apdc, iapdc, apma);
  if (debug) printk(KERN_INFO "gs: DMA initiated, wait for interrupt\n");
  if (worker->gsInterruptsFired & GS_INT_ATAPI) {
    /*
      In this case, the ATAPI interrupt fired sometime between when we
      finished sending the packet, and now.  Queue up for the end-of
      DMA interrupt, and force us into the check-DMA-results state
      immediately to start the dwell countdown.
    */
    gs_queue_worker_for_interrupt (worker, GS_INT_ATAPI_DMA,
				   GS_WORKER_CHECK_DMA_RESULTS);
    worker->go = 1;
  } else {
    gs_queue_worker_for_interrupt (worker, GS_INT_ATAPI | GS_INT_ATAPI_DMA,
				   GS_WORKER_CHECK_DMA_RESULTS);
  }
  restore_flags(flags);
}

/*
  This one's a bit tricky, as we may be waiting on either or both
  of two interrupts (drive-done and DMA-done) and these can trigger
  in either order.  In the no-error path we wait until we've gotten
  both.  The only odd case is "drive done, DMA not done, and the
  drive says ERR" - in this case we forcibly abort the DMA and then
  proceed as if the DMA had terminated with an interrupt.
*/

static void gs_worker_check_dma_results(struct Scsi_Host *instance,
				    struct gs_worker *worker)
{
  unsigned char stat;
  unsigned long flags;
  unsigned short foo;
  struct gs_hostdata *hostdata;
  int bail = 1;
  int dwellExpired = 0;
  int stillBusy = 0;
  int settle = 0;
  unsigned short apdc, iapdc;
  Scsi_Cmnd *cmd;
  hostdata = (struct gs_hostdata *) instance->hostdata;
  cmd = (Scsi_Cmnd *) worker->scsiRequest;
  (void) instance;
  if (debug) printk(KERN_INFO "gs: here we check DMA results\n");
  save_and_cli(flags);
  if (worker->aborting) {
    printk(KERN_ERR "gs: aborting during ATAPI DMA due to timeout or software reset\n");
    /* 
       mask and clear ATA interrupt  
    */
    foo = GS_READ(GS_ATA_INTERRUPT_MASK_REGISTER);
    foo &= ~(GS_ATA_INTERRUPT_CLEAR | GS_ATA_INTERRUPT_MASK);
    worker->gsInterruptsPending &= ~GS_INT_ATAPI;
    /* 
       Do a null settle-down, which will abort and reset the ATAPI
       DMA core
    */
    settle = 1;
    worker->dwell = 1;
    /*
      If we hadn't already done a reset (e.g. in gs_reset()), do one
      now.
    */
    if (!hostdata->wasReset) {
      /* Do a hard reset on the ATAPI bus */
      printk(KERN_INFO "gs: resetting ATAPI interface\n");
      apdc = GS_READ(GS_ATA_DMA_APDC);
      apdc &= ~0x04;
      GS_WRITE(GS_ATA_DMA_APDC, apdc);
      GS_WRITE8(GS_ATAPI_CONTROL_REGISTER, 0xC0);
      GS_WRITE8(GS_ATAPI_CONTROL_REGISTER, 0x80);
      hostdata->wasReset = 1;
    }
  }
  switch (worker->gsInterruptsPending) {
  case GS_INT_ATAPI:
    if (debug) printk(KERN_INFO "gs: end-of-DMA interrupt but drive still busy!\n");
    break;
  case GS_INT_ATAPI | GS_INT_ATAPI_DMA:
    if (debug) printk(KERN_INFO "gs: neither drive nor DMA is done, why am I here?\n");
    break;
  case GS_INT_ATAPI_DMA:
    if (debug) printk(KERN_INFO "gs: drive done, DMA not!\n");
    /*
      This condition usually indicates an error - the drive has
      asserted INTR before all of the data was DMA'ed.  We'll use the
      settle-for-a-few-milliseconds path to make sure that it's
      really a Badness and not just a race condition.  We can also end
      up here if the drive stalled, the upper SCSI layers called
      scsi_do_abort, and we reset the drive... in this case we
      don't bother waiting around.
    */
    if (!worker->aborting) {
      settle = 1;
    }
    break;
  case 0:
    if (debug) printk(KERN_INFO "gs: DMA done, drive done!\n");
    apdc = GS_READ(GS_ATA_DMA_APDC);
    iapdc = GS_READ(GS_ATA_DMA_IAPDC);
    if (apdc & 0x0040) {
      if (debug) printk(KERN_INFO "gs: ATA/FIFO still busy!\n");
      stillBusy = 1;
    }
    if (iapdc & 0x0004) {
      if (debug) printk(KERN_INFO "gs: FIFO/SDRAM still busy!\n");
      stillBusy = 1;
    }
    if (stillBusy) {
      settle = 1;
    } else {
      bail = 0;
    }
    break;
  default:
    printk(KERN_ERR "gs: worker %d has implausible interrupts-pending 0x%08lX\n", 
	   worker->sn, worker->gsInterruptsPending);
    bail = 0;
  }
  /*
    Provide a way to delay for up to 50 milliseconds in a couple of
    cases, to work around possible or definite race conditions in the
    chip.  If we wait too long, bail out... and, if the DMA engine
    is running, kill it.
  */
  if (settle) {
    if (worker->dwell == 0) {
      worker->dwell = 10;
      gs_queue_worker_for_timer(worker, 5, GS_WORKER_CHECK_DMA_RESULTS);
    } else if (--worker->dwell > 0) {
      gs_queue_worker_for_timer(worker, 5, GS_WORKER_CHECK_DMA_RESULTS);
    } else {
      bail = 0;
      dwellExpired = 1;
      apdc = GS_READ(GS_ATA_DMA_APDC);
      iapdc = GS_READ(GS_ATA_DMA_IAPDC);
      iapdc |= 0x0010;
      GS_WRITE(GS_ATA_DMA_IAPDC, iapdc);   /* mask DMA-done interrupt */
      iapdc &= ~0x0001;
      GS_WRITE(GS_ATA_DMA_IAPDC, iapdc);   /* DMA is ended */
      iapdc &= ~0x8000;
      GS_WRITE(GS_ATA_DMA_IAPDC, iapdc);   /* Put DMA module into reset */
      iapdc |= 0x8000;
      GS_WRITE(GS_ATA_DMA_IAPDC, iapdc);   /* Bring module out of reset */
      apdc &= ~0x01;
      GS_WRITE(GS_ATA_DMA_APDC, apdc);   /* FIFO reset */
      apdc |= 0x01;
      GS_WRITE(GS_ATA_DMA_APDC, apdc);   /* release FIFO reset */
      worker->gsInterruptsPending &= ~GS_INT_ATAPI_DMA;
    }
  }
  restore_flags(flags);
  if (bail) {
    return;
  }
  /*
    Put the interface back into PIO mode, get status...
  */
  apdc = GS_READ(GS_ATA_DMA_APDC);
  apdc &= ~0x04;
  GS_WRITE(GS_ATA_DMA_APDC, apdc);
  stat = GS_READ8(GS_ATAPI_ALTSTATUS_REGISTER);
  if (worker->aborting) {
    gs_worker_next_state(worker, GS_WORKER_DONE);
  } else if (stat & ERR_STAT) {
    printk(KERN_INFO "gs: data transfer ended with an ERR\n");
    worker->reportError = 1;
    worker->retries ++; /* pretend we retries, 'cause we can't restart... */
    gs_worker_next_state(worker, GS_WORKER_INIT_FOR_REQUEST_SENSE);
  } else {
    if (dwellExpired) {
      printk(KERN_ERR "gs: DMA dwell expired but ERR was not set?!?\n");
      worker->reportHang = 1;
    }
    gs_worker_next_state(worker, GS_WORKER_POSTPROCESS);
    if (worker->sc_data_direction == SCSI_DATA_READ) {
      if (worker->useHostDMA) {
	gs_worker_next_state(worker, GS_WORKER_DMA_FROM_SDRAM);
      } else if (worker->useAtapiDMA) {
	gs_worker_next_state(worker, GS_WORKER_PIO_FROM_SDRAM);
      }
    }
  }
}

static void gs_worker_pio_data(struct Scsi_Host *instance, struct gs_worker *worker)
{
  (void) instance;
  unsigned char stat, ireason;
  int bcount, chomp, words, took;
  int transformOffset;
  unsigned short tempbuf[32];
  unsigned short foo;
  Scsi_Cmnd *cmd;  
  struct gs_hostdata *hostdata;
  if (debug) printk(KERN_INFO "gs: here we do PIO data transfer...\n");
  hostdata = (struct gs_hostdata *) instance->hostdata;
  cmd = (Scsi_Cmnd *) worker->scsiRequest;
  stat = GS_READ8(GS_ATAPI_ALTSTATUS_REGISTER);
  if (stat & ERR_STAT) {
    if (debug) printk(KERN_INFO "gs: drive responded to command 0x%02X with an ERR\n",
	   worker->writeback.cmd[0]);
    if (debug && worker->retries) {
      printk(KERN_INFO "gs: I guess the error is persistent\n");
      gs_worker_next_state(worker, GS_WORKER_DONE);
    } else {
      gs_worker_next_state(worker, GS_WORKER_INIT_FOR_REQUEST_SENSE);
    }
    return;
  }
  if (!(stat & DRQ_STAT)) {
    if (debug) printk(KERN_INFO "gs: drive has deasserted DRQ... must be done\n");
    gs_worker_next_state(worker, GS_WORKER_CHECK_PIO_RESULTS);
    return;
  }
  bcount = (GS_READ8(GS_ATAPI_BCOUNTH_REGISTER) << 8) |
    GS_READ8(GS_ATAPI_BCOUNTL_REGISTER);
  ireason = GS_READ8(GS_ATAPI_IREASON_REGISTER);
  if (debug) printk(KERN_INFO "gs: ireason 0x%0X, byte count is %d\n", ireason, bcount);
  if (ireason & GS_ATAPI_IREASON_COD) {
    printk (KERN_ERR "gs: CoD != 0 in gs_worker_transfer_data\n");
    worker->reportError = 1;
    gs_worker_next_state(worker, GS_WORKER_CHECK_PIO_RESULTS);
    return;
  }
  if (worker->aborting) {
    printk(KERN_ERR "gs: aborting during ATAPI PIO due to timeout or software reset\n");
    /* 
       mask and clear ATA interrupt  
    */
    foo = GS_READ(GS_ATA_INTERRUPT_MASK_REGISTER);
    foo &= ~(GS_ATA_INTERRUPT_CLEAR | GS_ATA_INTERRUPT_MASK);
    worker->gsInterruptsPending &= ~GS_INT_ATAPI;
    /*
      If we hadn't already done a reset (e.g. in gs_reset()), do one
      now.
    */
    if (!hostdata->wasReset) {
      /* Do a hard reset on the ATAPI bus */
      printk(KERN_INFO "gs: resetting ATAPI interface\n");
      GS_WRITE8(GS_ATAPI_CONTROL_REGISTER, 0xC0);
      GS_WRITE8(GS_ATAPI_CONTROL_REGISTER, 0x80);
      hostdata->wasReset = 1;
    }
    gs_worker_next_state(worker, GS_WORKER_DONE);
    return;
  }
  /*
    Re-queue us now so that we avoid a race condition after pulling
    out 'bcount' bytes from the drive.
  */
  gs_queue_worker_for_interrupt (worker, GS_INT_ATAPI,
				 GS_WORKER_PIO_DATA);
  /*
    PIO the data from the drive, 64 bytes at a time.  Yah, it's inelegant.
  */
  transformOffset = 0;
  while (bcount > 0) {
    chomp = bcount;
    if (chomp > sizeof tempbuf) {
      chomp = sizeof tempbuf;
    }
    bcount -= chomp;
    words = chomp / 2;
    if (ireason & GS_ATAPI_IREASON_IO) {
      if (debug > 2) printk(KERN_INFO "gs: we will now read %d bytes\n", chomp);
      GS_READ_FIFO(GS_ATAPI_DATA_REGISTER, words, tempbuf);
      if (worker->writeback.transformBuffer) {
	if (chomp > worker->writeback.transformBufferLength) {
	  chomp = worker->writeback.transformBufferLength;
	}
	if (debug > 2) printk(KERN_INFO "gs: we will now transfer %d bytes to the transform buffer\n", chomp);
	memcpy(worker->writeback.transformBuffer + transformOffset, (unsigned char *) tempbuf, chomp);
	took = chomp;
	transformOffset += chomp;
      } else {
	if (debug > 2) printk(KERN_INFO "gs: we will now transfer %d bytes to the user\n", chomp);
	took = gsTransferToUser(cmd, (unsigned char *) tempbuf, chomp);
      }
      worker->transferRemaining -= took;
      if (took != chomp) {
	if (debug) printk(KERN_INFO "gs: discarded excess data from ATAPI device\n");
      }
    } else {
      memset(tempbuf, 0, sizeof tempbuf);
      if (worker->writeback.transformBuffer) {
	if (chomp > worker->writeback.transformBufferLength) {
	  chomp = worker->writeback.transformBufferLength;
	}
	if (debug > 2) printk(KERN_INFO "gs: we will now transfer %d bytes from the transform buffer\n", chomp);
	memcpy((unsigned char *) tempbuf,
	       worker->writeback.transformBuffer + transformOffset, chomp);
	took = chomp;
	transformOffset += chomp;
      } else {
	if (debug > 2) printk(KERN_INFO "gs: we will now write %d bytes\n", chomp);
	took = gsTransferFromUser(cmd, (unsigned char *) tempbuf, chomp);
      }
      worker->transferRemaining -= took;
      if (took != chomp) {
	if (debug) printk(KERN_INFO "gs: padding extra data for ATAPI device\n");
      }
      GS_WRITE_FIFO(GS_ATAPI_DATA_REGISTER, words, tempbuf);
    }      
  }
  if (debug) printk(KERN_INFO "gs: end of transfer burst, wait for next interrupt\n");
}

static void gs_worker_check_pio_results(struct Scsi_Host *instance,
				    struct gs_worker *worker)
{
  unsigned char stat;
  Scsi_Cmnd *cmd;  
  cmd = (Scsi_Cmnd *) worker->scsiRequest;
  (void) instance;
  if (debug) printk(KERN_INFO "gs: check PIO results\n");
  stat = GS_READ8(GS_ATAPI_ALTSTATUS_REGISTER);
  if (stat & ERR_STAT) {
    printk(KERN_INFO "gs: data transfer ended with an ERR\n");
    worker->reportError = 1;
    worker->retries ++; /* pretend we retries, 'cause we can't restart... */
    gs_worker_next_state(worker, GS_WORKER_INIT_FOR_REQUEST_SENSE);
  } else {
    gs_worker_next_state(worker, GS_WORKER_POSTPROCESS);
  }
}

static void gs_worker_pio_from_sdram(struct Scsi_Host *instance,
				    struct gs_worker *worker)
{
  (void) instance;
  unsigned short tempbuf[32];
  int bcount, chomp, words, took;
  unsigned long offset;
  Scsi_Cmnd *cmd;  
  struct gs_hostdata *hostdata;
  hostdata = (struct gs_hostdata *) instance->hostdata;
  cmd = (Scsi_Cmnd *) worker->scsiRequest;
  bcount = worker->transferSize;
  offset = worker->writeback.buffer->gsOffset;
  if (debug) printk(KERN_INFO "gs: PIO %d bytes from the SDRAM at 0x%0lX\n",
	 bcount, offset);
  while (bcount > 0) {
    chomp = bcount;
    if (chomp > sizeof tempbuf) {
      chomp = sizeof tempbuf;
    }
    bcount -= chomp;
    words = chomp / 2;
    memset(tempbuf, 0, sizeof tempbuf);
    GS_READ_MULTI(GS_OFFSET_SDRAM+offset, words, tempbuf);
    took = gsTransferToUser(cmd, (unsigned char *) tempbuf, chomp);
    offset += chomp;
  }
  gs_worker_next_state(worker, GS_WORKER_POSTPROCESS);
}

static void gs_worker_dma_from_sdram(struct Scsi_Host *instance,
				     struct gs_worker *worker)
{
  Scsi_Cmnd *cmd;  
  struct gs_hostdata *hostdata;
  unsigned short radc, iradc, rama, ract;
  unsigned long flags;
  unsigned long offset, sectors;
  hostdata = (struct gs_hostdata *) instance->hostdata;
  if (worker->aborting) {
    gs_worker_next_state(worker, GS_WORKER_DONE);
  }
  if (hostdata->resourcesBusy & GS_RESOURCE_HOSTDMA) {
    worker->gsResourcesNeeded |= GS_RESOURCE_HOSTDMA;
    return;
  }
  worker->gsResourcesUsed |= GS_RESOURCE_HOSTDMA;
  hostdata->resourcesBusy |= GS_RESOURCE_HOSTDMA;
  cmd = (Scsi_Cmnd *) worker->scsiRequest;
  if (debug) printk(KERN_INFO "gs: we own the host DMA bus (SDRAM -> host)\n");
  radc = GS_READ(GS_READ_DMA_RADC);
  iradc = GS_READ(GS_READ_DMA_IRADC);
  rama = GS_READ(GS_READ_DMA_RAMA);
  if (debug) printk(KERN_INFO "gs: RADC %04X IRADC %04X RAMA %04X\n", radc, iradc, rama);
  if (worker->useHostDMA != 3 && worker->useHostDMA != 4) {
    radc |= 0x0004;      
    GS_WRITE(GS_READ_DMA_RADC, radc);  /* DMA TRACON REQ off */
    iradc &= ~0x0001;
    GS_WRITE(GS_READ_DMA_IRADC, iradc);   /* stop the DMA */
    iradc &= ~0x8000;
    GS_WRITE(GS_READ_DMA_IRADC, iradc);   /* DMA module reset */
    iradc |= 0x8000;
    GS_WRITE(GS_READ_DMA_IRADC, iradc);   /* DMA module reset release*/
    radc &= ~0x01;
    GS_WRITE(GS_READ_DMA_RADC, radc);   /* Reset read FIFO */
    radc |= 0x01;
    GS_WRITE(GS_READ_DMA_RADC, radc);   /* release reset on read FIFO*/
  }
  if (worker->useHostDMA != 4) {
    iradc &= ~0x10;                       /* interrupt not masked */
  } else {
    iradc |= 0x0010;                      /* interrupt is masked */
  }
  GS_WRITE(GS_READ_DMA_IRADC, iradc);   /* set/clear interrupt mask */
  if (debug) printk(KERN_INFO "gs: RADC %04X IRADC %04X RAMA %04X\n", radc, iradc, rama);
  offset = worker->writeback.buffer->gsOffset;
  sectors = worker->transferSize / hostdata->atapiSectorSize;
  if (hostdata->atapiBufferModulus == 2048) {
    rama = (rama & ~0x0007) | 0x02;
    ract = 0;
  } else {
    rama = (rama & ~0x0007) | 0x04;
    ract = (hostdata->atapiSectorSize / 2) - 1;
  }
  if (debug) printk(KERN_INFO "gs: RADC %04X IRADC %04X RAMA %04X\n", radc, iradc, rama);
  GS_WRITE(GS_READ_DMA_RADB, offset / hostdata->atapiBufferModulus);
  GS_WRITE(GS_READ_DMA_RANB, sectors - 1);
  GS_WRITE(GS_READ_DMA_RAMA, rama);   /* set per-sector storage modulus */
  GS_WRITE(GS_READ_DMA_RACT, ract);   /* set modulus mode */
  if (debug) printk(KERN_INFO "gs: RACT %04X RADB %04X RANB %04X\n",
		    GS_READ(GS_READ_DMA_RACT),
		    GS_READ(GS_READ_DMA_RADB),
		    GS_READ(GS_READ_DMA_RANB));
  radc &= ~0x0004;      
  GS_WRITE(GS_READ_DMA_RADC, radc);  /* DMA TRACON REQ on */
  save_and_cli(flags);
  GS_BRIDGE->control |= GS_CONTROL_PSR_RESET;
  barrier();
  GS_BRIDGE->control &= ~GS_CONTROL_PSR_RESET;
  barrier();
  switch (worker->useHostDMA) {
  case 4:
    /*
      Specialized diagnostic DMA.  We start the DMA, don't expect an
      interrupt (it'll never occur), and then detach the SCSI command
      from the worker and mark it done.  The worker remains alive,
      holding onto the DMA channel and SDRAM block, while the app
      polls the test data out of the chip.  When the app is done, it'll
      call the reset IOCTL, and we'll hunt down and terminate the worker.

      We'll set a 15-second timer to make sure that we eventually
      unwedge, even if the diagnostic app fails to clean up properly.
    */
    iradc |= 0x01;
    GS_WRITE(GS_READ_DMA_IRADC, iradc);   /* start the DMA */
    gs_queue_worker_for_timer(worker, 15 * HZ, GS_WORKER_LOOPBACK_DMA_DONE);
    worker->backgroundDMA = 1;
    worker->scsiRequest = NULL;
    if (cmd) {
      cmd->scsi_done(cmd);
    } else {
      panic("No command during DMA type 4\n");
    }
    break;
  case 3:
    /*
      Load boundary-scan test data using DMA.   Should be done within
      500 msec.  PSR inteface not used.
    */
    gs_queue_worker_for_interrupt (worker, GS_INT_HOST_DMA,
				   GS_WORKER_DO_PSR_DMA_BURST);
    gs_queue_worker_for_timer(worker, 500, GS_WORKER_DO_PSR_DMA_BURST);
    iradc |= 0x01;
    GS_WRITE(GS_READ_DMA_IRADC, iradc);   /* start the DMA */
    break;
  default:
    /*
      DMA engine sometimes doesn't start up properly.  Set a 50-
      millisecond timeout before initiation.  If the timer runs out
      and we don't have any data arriving at the bridge, declare a
      Bad Thing and treat it like a stall.
    */
    worker->awaitingPSR = 1;
    GS_BRIDGE->imr |= GS_BRIDGE_INTR_PSR_DATA_VALID;
    GS_BRIDGE->control |= GS_CONTROL_PSR_ENABLE;
    iradc |= 0x01;
    GS_WRITE(GS_READ_DMA_IRADC, iradc);   /* start the DMA */
    gs_queue_worker_for_interrupt (worker, GS_INT_HOST_DMA | GS_INT_PSR_VALID |
				   GS_INT_PSR_STALL,
				   GS_WORKER_DO_PSR_DMA_BURST);
    gs_queue_worker_for_timer(worker, 50, GS_WORKER_DO_PSR_DMA_BURST);
    break;
  }
  if (debug) printk(KERN_INFO "gs: RADC %04X IRADC %04X RAMA %04X\n", radc, iradc, rama);
  if (debug) printk(KERN_INFO "gs: GS read DMA initiated\n");
  restore_flags(flags);
}

static void gs_worker_do_psr_dma_burst(struct Scsi_Host *instance,
					struct gs_worker *worker)
{
  Scsi_Cmnd *cmd;  
  struct gs_hostdata *hostdata;
  unsigned short radc, iradc, rama;
  unsigned long flags;
  int bailout;
  hostdata = (struct gs_hostdata *) instance->hostdata;
  if (debug) printk(KERN_INFO "gs: in do_psr_dma_burst\n");
  cmd = (Scsi_Cmnd *) worker->scsiRequest;
  radc = GS_READ(GS_READ_DMA_RADC);
  iradc = GS_READ(GS_READ_DMA_IRADC);
  rama = GS_READ(GS_READ_DMA_RAMA);
  /*
    If we hit a PSR stall, or if the midlayer says that we have
    timed out, it's BAD.  Shut down both DMA engines and
    bail out.
  */
  bailout = 0;
  if (worker->aborting) {
    printk(KERN_ERR "gs: DMA-to-host timeout, IRADC=0x%04X, aborting DMA!\n",
	   	     GS_READ(GS_READ_DMA_IRADC));
    bailout = 1;
    worker->reportHang = 1;
  }
  if (worker->useHostDMA != 3 &&
      !(worker->gsInterruptsPending & GS_INT_PSR_STALL)) {
    printk(KERN_ERR "gs: PSR stall detected, IRADC=0x%04X, aborting DMA!\n",
	   	     GS_READ(GS_READ_DMA_IRADC));
    bailout = 1;
    worker->reportHang = 1;
  }
  if (worker->awaitingPSR) {
    worker->awaitingPSR = 0;
    if (jam != 0 && (jiffies % jam) == 0) {
      printk(KERN_ERR "gs: simulating a no-PSR error, aborting DMA!\n");
      bailout = 1;
      worker->reportHang = 1;
    } else if (!(GS_BRIDGE->isr & GS_BRIDGE_INTR_PSR_DATA_VALID)) {
      printk(KERN_ERR "gs: PSR did not arrive, IRADC=0x%04X, aborting DMA!\n",
	     GS_READ(GS_READ_DMA_IRADC));
      bailout = 1;
      worker->reportHang = 1;
    }
  }
  if (worker->useHostDMA == 3 &&
      (worker->gsInterruptsPending & GS_INT_HOST_DMA)) {
    printk(KERN_ERR "gs: boundary-scan data load failure\n");
    bailout = 1;
    worker->reportHang = 1;
  }
  if (bailout) {
    save_and_cli(flags);
    if (gs_broadcom_enabled) {
      disable_irq(DMA_IRQ);
      gs_broadcom_enabled = 0;
    }
    printk(KERN_ERR "gs: Broadcom DMA debug register says 0x%08X\n",
	   *BCM_IO_DMA0_DEB);
    printk(KERN_ERR "gs: Broadcom DMA cntrl register says 0x%08X\n",
	   *BCM_IO_DMA0_CTL);
    printk(KERN_ERR "gs: Broadcom interrupt status register says 0x%08X\n",
	   *BCM_INTR_STATUS);
    printk(KERN_ERR "gs: Broadcom interrupt enable register says 0x%08X\n",
	   *BCM_INTR_ENA);
    printk(KERN_ERR "gs: reg 0x60-0x6C %04X %04X %04X %04X %04X %04X %04X\n",
	   GS_READ(GS_OFFSET_CORE+0x60), GS_READ(GS_OFFSET_CORE+0x62), 
	   GS_READ(GS_OFFSET_CORE+0x64), GS_READ(GS_OFFSET_CORE+0x66), 
	   GS_READ(GS_OFFSET_CORE+0x68), GS_READ(GS_OFFSET_CORE+0x6A), 
	   GS_READ(GS_OFFSET_CORE+0x6C));
    printk(KERN_ERR "gs: reg 0x70-0x7C %04X %04X %04X %04X %04X %04X %04X\n",
	   GS_READ(GS_OFFSET_CORE+0x70), GS_READ(GS_OFFSET_CORE+0x72), 
	   GS_READ(GS_OFFSET_CORE+0x74), GS_READ(GS_OFFSET_CORE+0x76), 
	   GS_READ(GS_OFFSET_CORE+0x78), GS_READ(GS_OFFSET_CORE+0x7A), 
	   GS_READ(GS_OFFSET_CORE+0x7C));
    printk(KERN_ERR "gs: reg 0x80-0x8C %04X %04X %04X %04X %04X %04X %04X\n",
	   GS_READ(GS_OFFSET_CORE+0x80), GS_READ(GS_OFFSET_CORE+0x82), 
	   GS_READ(GS_OFFSET_CORE+0x84), GS_READ(GS_OFFSET_CORE+0x86), 
	   GS_READ(GS_OFFSET_CORE+0x88), GS_READ(GS_OFFSET_CORE+0x8A), 
	   GS_READ(GS_OFFSET_CORE+0x8C));
    printk(KERN_ERR "gs: reg 0xB0-0xBC %04X %04X %04X %04X %04X %04X %04X\n",
	   GS_READ(GS_OFFSET_CORE+0xB0), GS_READ(GS_OFFSET_CORE+0xB2), 
	   GS_READ(GS_OFFSET_CORE+0xB4), GS_READ(GS_OFFSET_CORE+0xB6), 
	   GS_READ(GS_OFFSET_CORE+0xB8), GS_READ(GS_OFFSET_CORE+0xBA), 
	   GS_READ(GS_OFFSET_CORE+0xBC));
    printk(KERN_ERR "gs: reg 0x130-0x132 %04X %04x\n",
	   GS_READ(GS_OFFSET_CORE+0x130), GS_READ(GS_OFFSET_CORE+0x132));
    printk(KERN_ERR "gs: tracon 0x2F0     %04X\n",
	   GS_READ(GS_OFFSET_TRACON+0x2F0));
    printk(KERN_ERR "gs: tracon 0x20c %04X  0x2dc %04X  0x2f6 %04X  0x2f8 %04X\n",
	   GS_READ(GS_OFFSET_TRACON+0x20C), GS_READ(GS_OFFSET_TRACON+0x2DC),
	   GS_READ(GS_OFFSET_TRACON+0x2F6), GS_READ(GS_OFFSET_TRACON+0x2F8));
    printk(KERN_ERR "gs: tracon 0x308 %04X  0x320 %04X  0x380 %04X  0x382 %04X\n",
	   GS_READ(GS_OFFSET_TRACON+0x308), GS_READ(GS_OFFSET_TRACON+0x320),
	   GS_READ(GS_OFFSET_TRACON+0x380), GS_READ(GS_OFFSET_TRACON+0x382));
    printk(KERN_ERR "gs: tracon 0x384 %04X  0x386 %04X  0x388 %04X  0x38A %04X\n",
	   GS_READ(GS_OFFSET_TRACON+0x384), GS_READ(GS_OFFSET_TRACON+0x386),
	   GS_READ(GS_OFFSET_TRACON+0x388), GS_READ(GS_OFFSET_TRACON+0x38A));
    printk(KERN_ERR "gs: tracon 0x014 %04X  0x050 %04X  0x0d6 %04X  0x11C %04X\n",
	   GS_READ(GS_OFFSET_TRACON+0x014), GS_READ(GS_OFFSET_TRACON+0x050),
	   GS_READ(GS_OFFSET_TRACON+0x0d6), GS_READ(GS_OFFSET_TRACON+0x11c));
    iradc |= 0x10;
    GS_WRITE(GS_READ_DMA_IRADC, iradc);   /* mask DMA-done interrupt */
    radc |= 0x0004;      
    GS_WRITE(GS_READ_DMA_RADC, radc);  /* DMA TRACON REQ off */
    iradc &= ~0x0001;
    GS_WRITE(GS_READ_DMA_IRADC, iradc);   /* stop the DMA */
    iradc &= ~0x8000;
    GS_WRITE(GS_READ_DMA_IRADC, iradc);   /* DMA module reset */
    iradc |= 0x8000;
    GS_WRITE(GS_READ_DMA_IRADC, iradc);   /* DMA module reset release*/
    radc &= ~0x01;
    GS_WRITE(GS_READ_DMA_RADC, radc);   /* Reset read FIFO */
    radc |= 0x01;
    GS_WRITE(GS_READ_DMA_RADC, radc);   /* release reset on read FIFO*/
    *BCM_IO_DMA0_CTL = 0x02;  /* halt Broadcom DMA, force into reset */
    barrier();
    *BCM_IO_DMA0_REC = 0x00;  /* disable Broadcom DMA interrupt */
    barrier();
    GS_BRIDGE->control &= ~GS_CONTROL_PSR_ENABLE;
    restore_flags(flags);
    worker->reportError = 1;
    worker->retries ++; /* pretend we retried, 'cause we can't restart... */
    gs_release_chip_resources(instance, GS_RESOURCE_HOSTDMA);
    gs_worker_next_state(worker, GS_WORKER_POSTPROCESS);
    gs_autodebug_on();
    return;
  }
  /*
    If we're still waiting for the Broadcom localbus DMA to finish up,
    just go back to sleep.
  */
  if (worker->gsInterruptsPending & GS_INT_BROADCOM_DMA) {
    if (debug) printk(KERN_INFO "gs: awaiting final interrupt from Broadcom!\n");
    return;
  }
  /*
    OK... things are healthy and there's no data actually in flight.
    See if we have more host-side DMA to do.
  */
  if (worker->useHostDMA == 3 || worker->useHostDMA == 4) {
    if (debug) printk(KERN_INFO "gs: diagnostic DMA completed\n");
    gs_release_chip_resources(instance, GS_RESOURCE_HOSTDMA);
    gs_worker_next_state(worker, GS_WORKER_POSTPROCESS);
    return;
  }
  if (gsCheckMoreHostDMA(cmd) == 0) {
    /*
      Host side is done.  We should have gotten the end-of-DMA
      signal from the GS, too.  If not, error out and shut down
      its DMA engine - we've got a handshake problem somewhere.
    */
    if (worker->gsInterruptsPending & GS_INT_HOST_DMA) {
      printk(KERN_ERR "gs: Host says DMA read done, GS says not!\n");
      save_and_cli(flags);
      iradc |= 0x10;
      GS_WRITE(GS_READ_DMA_IRADC, iradc);   /* mask DMA-done interrupt */
      GS_WRITE(GS_OFFSET_TRACON+0x2F6, 0x0000); /* reset DMA engine */
      GS_WRITE(GS_OFFSET_TRACON+0x2F6, 0x0001); /* release reset */
      restore_flags(flags);
      worker->reportError = 1;
      worker->retries ++; /* pretend we retried, 'cause we can't restart... */
      gs_release_chip_resources(instance, GS_RESOURCE_HOSTDMA);
      gs_worker_next_state(worker, GS_WORKER_POSTPROCESS);
      return;
    }
    /*
      OK, we're in good shape.  Release the DMA resources and continue.
    */
    save_and_cli(flags);
    GS_BRIDGE->control &= ~GS_CONTROL_PSR_ENABLE;
    restore_flags(flags);
    gs_release_chip_resources(instance, GS_RESOURCE_HOSTDMA);
    gs_worker_next_state(worker, GS_WORKER_POSTPROCESS);
    return;
  }
  /*
    We have more reading to do.  Check for PSR valid... if it's not
    set at the moment, set the no-flow timer and go back to sleep.
  */
  save_and_cli(flags);
  if (!(GS_BRIDGE->isr & GS_BRIDGE_INTR_PSR_DATA_VALID)) {
    gs_queue_worker_for_timer(worker, 50, GS_WORKER_DO_PSR_DMA_BURST);
    worker->awaitingPSR = 1;
    worker->gsInterruptsPending |= GS_INT_PSR_VALID;
    restore_flags(flags);
    if (debug) printk(KERN_INFO "gs: another read DMA to do, PSR not valid yet\n");
    return;
  }
  /*
    We have more reading to do, and PSR-valid tells us that there's
    data to read in the FIFO.  Set up the Broadcom DMA descriptors
    and start the engine.
  */
  gsSetupHostDMA (instance, cmd, 0);
  if (debug) printk(KERN_INFO "gs: starting Broadcom read-DMA burst\n");
  save_and_cli(flags);
  *BCM_IO_DMA0_CTL = 0x02;  /* force Broadcom into reset */
  barrier();
  *BCM_IO_DMA0_CTL = 0x00;  /* release reset */
  barrier();
  *BCM_IO_DMA0_REC = (1 << 26) |                         /* interrupt enable */
    (((unsigned long) hostdata->dmaDescriptors) & 0x03FFFFFF); /* desc. addr */
  barrier();
  GS_BRIDGE->imr |= GS_BRIDGE_INTR_PSR_STALL;
  worker->gsInterruptsPending |= GS_INT_BROADCOM_DMA | GS_INT_PSR_STALL;
  enable_irq(DMA_IRQ);
  gs_broadcom_enabled = 1;
  if (debug) printk(KERN_INFO "gs: DMA rec register is 0x%08X\n",
		    *BCM_IO_DMA0_REC);
  *BCM_IO_DMA0_CTL = 0x01; /* start the DMA, non-wraparound */
  restore_flags(flags);
  if (debug) printk(KERN_INFO "gs: read-DMA burst started, IRQ enabled\n");
}

static void gs_worker_loopback_dma_done (struct Scsi_Host *instance,
					 struct gs_worker *worker)
{
  unsigned short radc, iradc, rama;
  if (debug) printk(KERN_INFO "gs: in loopback_dma_done\n");
  radc = GS_READ(GS_READ_DMA_RADC);
  iradc = GS_READ(GS_READ_DMA_IRADC);
  rama = GS_READ(GS_READ_DMA_RAMA);
  if (!(worker->gsInterruptsPending & GS_INT_TIMER)) {
    printk(KERN_ERR "gs: loopback DMA not cleared, timed out!\n");
    iradc |= 0x10;
    GS_WRITE(GS_READ_DMA_IRADC, iradc);   /* mask DMA-done interrupt */
    radc |= 0x0004;      
    GS_WRITE(GS_READ_DMA_RADC, radc);  /* DMA TRACON REQ off */
    iradc &= ~0x0001;
    GS_WRITE(GS_READ_DMA_IRADC, iradc);   /* stop the DMA */
    iradc &= ~0x8000;
    GS_WRITE(GS_READ_DMA_IRADC, iradc);   /* DMA module reset */
    iradc |= 0x8000;
    GS_WRITE(GS_READ_DMA_IRADC, iradc);   /* DMA module reset release*/
    radc &= ~0x01;
    GS_WRITE(GS_READ_DMA_RADC, radc);   /* Reset read FIFO */
    radc |= 0x01;
    GS_WRITE(GS_READ_DMA_RADC, radc);   /* release reset on read FIFO*/
  } else {
    if (debug) printk(KERN_INFO "gs: loopback DMA cleared by app\n");
  }
  gs_worker_next_state(worker, GS_WORKER_DONE);
}

static void gs_worker_postprocess(struct Scsi_Host *instance,
				 struct gs_worker *worker)
{
  (void) instance;
  Scsi_Cmnd *cmd;  
  unsigned long took;
  cmd = (Scsi_Cmnd *) worker->scsiRequest;
  if (debug) printk(KERN_INFO "gs: here we can postprocess the command\n");
  gs_worker_disarm_deadman(worker);
  if (worker->writeback.cmd[0] == MODE_SENSE_10 &&
      cmd &&
      cmd->cmnd[0] == MODE_SENSE) {
    worker->writeback.transformBuffer[0] =
      worker->writeback.transformBuffer[1];  /* Mode data length */
    worker->writeback.transformBuffer[1] =
      worker->writeback.transformBuffer[2];  /* Medium type */
    worker->writeback.transformBuffer[2] =
      worker->writeback.transformBuffer[3];  /* Device specific parameter */
    worker->writeback.transformBuffer[3] =
      worker->writeback.transformBuffer[7];  /* Block descriptor length */
    memmove(worker->writeback.transformBuffer + 4,
	    worker->writeback.transformBuffer + 8,
	    worker->writeback.transformBufferLength - 8);
  }
  if (worker->writeback.cmd[0] == INQUIRY) {
    worker->writeback.transformBuffer[2] |= 2;	/* ansi_revision */
    worker->writeback.transformBuffer[3] =
      (worker->writeback.transformBuffer[3] & 0xf0) | 2; /* data format */
  }
  if (worker->writeback.transformBufferLength > 0) {
    switch (worker->sc_data_direction) {
    case SCSI_DATA_UNKNOWN:
      printk(KERN_INFO "gs: whoops!  SCSI_DATA_UNKNOWN with a transform buffer!\n");
      break;
    case SCSI_DATA_NONE:
      printk(KERN_INFO "gs: whoops!  SCSI_DATA_NONE with a transform buffer!\n");
      break;
    case SCSI_DATA_WRITE:
      break;
    case SCSI_DATA_READ:
      took = gsTransferToUser(cmd, worker->writeback.transformBuffer,
			      worker->writeback.transformBufferLength);
      if (debug) printk(KERN_INFO "gs: sent %ld bytes to user from transform buffer!\n",
	     worker->writeback.transformBufferLength);
      break;
    default:
      break;
    }
  }
  gs_worker_next_state(worker, GS_WORKER_DONE);
}

static void gs_worker_init_for_request_sense(struct Scsi_Host *instance,
					    struct gs_worker *worker)
{
  unsigned long flags;
  unsigned long bcount;
  unsigned short foo;
  int delays;
  unsigned short status;
  struct gs_hostdata *hostdata;
  Scsi_Cmnd *cmd;  
  cmd = (Scsi_Cmnd *) worker->scsiRequest;
  hostdata = (struct gs_hostdata *) instance->hostdata;
  bcount = SCSI_SENSE_BUFFERSIZE;
  if (debug) printk(KERN_INFO "gs: prep ATAPI bus for REQUEST SENSE packet command\n");
  save_and_cli(flags);
  GS_WRITE8(GS_ATAPI_SELECT_REGISTER, 0xA0);
  GS_WRITE8(GS_ATAPI_CONTROL_REGISTER, 0x08);
  GS_WRITE8(GS_ATAPI_FEATURE_REGISTER, 0x00); /* don't enable DMA yet */
  GS_WRITE8(GS_ATAPI_BCOUNTH_REGISTER, bcount >> 8);
  GS_WRITE8(GS_ATAPI_BCOUNTL_REGISTER, bcount & 0xFF);
  /*
    Switch the data interface [back] into PIO mode
  */
  foo = GS_READ(GS_ATA_DMA_APDC);
  foo &= ~0x04;
  GS_WRITE(GS_ATA_DMA_APDC, foo);
  /*
    Start the packet
  */
  GS_WRITE8(GS_ATAPI_COMMAND_REGISTER, WIN_PACKETCMD);
  /*
    Read-and-discard from the alt status register in order to
    generate the proper ATAPI bus timing.  Then, wait for the
    drive to go not-BUSY.
  */
  (void) GS_READ8(GS_ATAPI_ALTSTATUS_REGISTER);
  delays = 0;
  status = GS_READ8(GS_ATAPI_STATUS_REGISTER);
  while (status & BUSY_STAT) {
    if (delays++ > 400) {
      printk(KERN_INFO "gs: device is stuck BUSY!\n");
      hostdata->mustReset = 1;
      worker->reportError = 1;
      gs_worker_next_state(worker, GS_WORKER_DONE);
      return;
    }
    status = GS_READ8(GS_ATAPI_STATUS_REGISTER);
  } 
  gs_worker_next_state(worker, GS_WORKER_SEND_REQUEST_SENSE);
  restore_flags(flags);
  if (debug) printk(KERN_INFO "gs: WIN_PACKETCMD written\n");
}

static void gs_worker_send_request_sense(struct Scsi_Host *instance,
					struct gs_worker *worker)
{
  Scsi_Cmnd *cmd;  
  unsigned long flags;
  int i;
  unsigned short bytes;
  unsigned char senseCommand[12];
  (void) instance;
  cmd = (Scsi_Cmnd *) worker->scsiRequest;
  if (!GS_READ8(GS_ATAPI_ALTSTATUS_REGISTER) & DRQ_STAT) {
    printk(KERN_ERR "gs: sent WIN_PACKETCMD but DRQ isn't set\n");
    worker->reportError = 1;
    worker->state = GS_WORKER_DONE;
    worker->go = 1;
    return;
  }
  memset(senseCommand, 0, sizeof senseCommand);
  senseCommand[0] = REQUEST_SENSE;
  senseCommand[4] = SCSI_SENSE_BUFFERSIZE;
  if (debug) printk(KERN_INFO "gs: WIN_PACKETCMD accepted, sending REQUEST SENSE\n");
  save_and_cli(flags);
  for (i = 0; i < 12; i += 2) {
    bytes = (senseCommand[i] << 8) | senseCommand[i+1];
    GS_WRITE(GS_ATAPI_DATA_REGISTER, bytes);
  }
  gs_queue_worker_for_interrupt(worker, GS_INT_ATAPI,
				GS_WORKER_TRANSFER_SENSE_DATA);
  restore_flags(flags);
}

static void gs_worker_transfer_sense_data(struct Scsi_Host *instance,
					 struct gs_worker *worker)
{
  (void) instance;
  unsigned char stat, ireason;
  int bcount, i;
  unsigned short tempbuf;
  struct gs_hostdata *hostdata;
  Scsi_Cmnd *cmd;  
  if (debug) printk(KERN_INFO "gs: here we do sense data transfer...\n");
  hostdata = (struct gs_hostdata *) instance->hostdata;
  cmd = (Scsi_Cmnd *) worker->scsiRequest;
  stat = GS_READ8(GS_ATAPI_ALTSTATUS_REGISTER);
  if (stat & ERR_STAT) {
    printk(KERN_INFO "gs: drive responded to REQUEST SENSE with an ERR\n");
    worker->reportError = 1;
    gs_worker_next_state(worker, GS_WORKER_DONE);
    return;
  }
  if (!(stat & DRQ_STAT)) {
    if (debug) printk(KERN_INFO "gs: no sense data!\n");
    gs_worker_next_state(worker, GS_WORKER_DMA_FROM_SDRAM);
    return;
  }
  bcount = (GS_READ8(GS_ATAPI_BCOUNTH_REGISTER) << 8) |
    GS_READ8(GS_ATAPI_BCOUNTL_REGISTER);
  ireason = GS_READ8(GS_ATAPI_IREASON_REGISTER);
  if (debug) printk(KERN_INFO "gs: ireason 0x%0X, byte count is %d\n", ireason, bcount);
  if (ireason & GS_ATAPI_IREASON_COD) {
    printk (KERN_ERR "gs: CoD != 0 in gs_worker_transfer_sense_data\n");
    worker->reportError = 1;
    gs_worker_next_state(worker, GS_WORKER_DONE);
    return;
  }
  /*
    PIO the sense data from the drive.
  */
  memset(worker->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);
  for (i = 0; i < bcount; i += 2) {
    tempbuf = GS_READ(GS_ATAPI_DATA_REGISTER);
    if (i < SCSI_SENSE_BUFFERSIZE) {
      worker->sense_buffer[i] = tempbuf >> 8;
      worker->sense_buffer[i+1] = tempbuf & 0xFF;
    } else if (i == SCSI_SENSE_BUFFERSIZE) {
      printk(KERN_INFO "gs: drive sent too much sense data!\n");
    }
  }
  if (cmd) {
    memcpy(cmd->sense_buffer, worker->sense_buffer, SCSI_SENSE_BUFFERSIZE);
    if (debug) print_sense("gs", cmd);
  } else {
    memcpy(hostdata->deferred_sense_buffer, worker->sense_buffer,
	   SCSI_SENSE_BUFFERSIZE);
    hostdata->haveDeferredSense = 1;
  }
  worker->retries ++;
  /*
    Add a brief holdoff after completing a REQUEST SENSE - helps the drive
    settle its tummy in some cases, it seems.
  */
  gs_queue_worker_for_timer(worker, 5, GS_WORKER_POSTPROCESS);
}

static void gs_worker_done(struct Scsi_Host *instance, struct gs_worker *worker)
{
  Scsi_Cmnd *cmd;  
  struct gs_hostdata *hostdata;
  hostdata = (struct gs_hostdata *) instance->hostdata;
  int device, handler;
  if (debug) printk(KERN_INFO "gs: worker %d is done\n", worker->sn);
  if (worker->gsResourcesUsed & GS_RESOURCE_GATE) {
    worker->gatedSize = 0;
    if (gs_gated_requests(instance) == 0) {
      worker->gsResourcesUsed &= ~GS_RESOURCE_GATE;
      if (debug) printk(KERN_INFO "gs: closing the gate\n");
    } else {
      if (debug) printk(KERN_INFO "gs: leaving the gate open\n");
    }
  }
  if (worker->writeback.transformBuffer) {
    kfree(worker->writeback.transformBuffer);
    worker->writeback.transformBuffer = NULL;
  }
  if (worker->writeback.buffer) {
    gs_sdram_free(worker->writeback.buffer);
    worker->writeback.buffer = NULL;
    gs_release_chip_resources(instance, GS_RESOURCE_SDRAM);
  }
  gs_release_chip_resources(instance, worker->gsResourcesUsed);
  cmd = (Scsi_Cmnd *) worker->scsiRequest;
  if (cmd) {
    if (worker->reportHang) {
      /*
	DMA hung during diagnostic read, a condition which needs to be cleared
	by the diag software after interrogating the hardware state.  Fake
	up a "Hardware error: data path failure" error.
      */
      memset(cmd->sense_buffer, 0, sizeof cmd->sense_buffer);
      cmd->sense_buffer[0] = 0x72;
      cmd->sense_buffer[1] = HARDWARE_ERROR;
      cmd->sense_buffer[2] = 0x41; /* DATA PATH FAILURE */
      cmd->sense_buffer[3] = 0x00; /* DATA_PATH_FAILURE */
      cmd->result |= (DRIVER_SENSE << 24) | (CHECK_CONDITION << 1);
    } else if (worker->reportError) {
      cmd->result |= DID_ERROR << 16;
    }
    if (worker->retries) {
      cmd->result |= (DRIVER_SENSE << 24) | (CHECK_CONDITION << 1);
    }
    if (worker->aborting) {
      cmd->result |= DID_ABORT << 16;
    }
    if (debug) printk(KERN_INFO "gs: cmd result is %08X\n", cmd->result);
    device = cmd->target;
    handler = deviceDispatcher[device];
    if (hostdata->connected[handler] == cmd) {
      hostdata->connected[handler] = NULL;
      hostdata->busy[handler] = 0;
    }
    cmd->scsi_done(cmd);
  } else if (worker->endaction) {
    (*worker->endaction)(worker);
  }
  gs_autodebug_off();  /* turn off autodebug if it has timed out */
}

static void gs_worker_setup_reset(struct Scsi_Host *instance, struct gs_worker *worker)
{
  struct gs_sdram *sdram = NULL;
  unsigned int flags;
  unsigned int offset;
  unsigned short radc, iradc, rama;
  struct gs_hostdata *hostdata;
  hostdata = (struct gs_hostdata *) instance->hostdata;
  if (!worker->writeback.buffer) {
    save_and_cli(flags);
    sdram = gs_sdram_alloc (worker, 2048);
    if (!sdram) {
      worker->gsResourcesNeeded |= GS_RESOURCE_SDRAM;
      restore_flags(flags);
      printk(KERN_INFO "gs: sdram alloc failed, opening gate\n");
      gs_release_chip_resources(instance, GS_RESOURCE_GATE);
      return;
    }
    restore_flags(flags);
    worker->writeback.buffer = sdram;
#ifdef NOTDEF
    {
      unsigned long int i;
      for (i = 0; i < 2048; i += 2) {
	GS_WRITE(GS_OFFSET_SDRAM + sdram->gsOffset + i, 0);
      }
    }
#endif
  }
  if (hostdata->resourcesBusy & GS_RESOURCE_HOSTDMA) {
    worker->gsResourcesNeeded |= GS_RESOURCE_HOSTDMA;
    return;
  }
  worker->gsResourcesUsed |= GS_RESOURCE_HOSTDMA;
  hostdata->resourcesBusy |= GS_RESOURCE_HOSTDMA;
  radc = GS_READ(GS_READ_DMA_RADC);
  iradc = GS_READ(GS_READ_DMA_IRADC);
  rama = GS_READ(GS_READ_DMA_RAMA);
  offset = worker->writeback.buffer->gsOffset;
  GS_WRITE(GS_READ_DMA_RADB, offset / 2048);
  GS_WRITE(GS_READ_DMA_RANB, 0);                /* 1 sector */
  rama = (rama & ~0x0007) | 0x0002;   /* data align is 2048 bytes */
  GS_WRITE(GS_READ_DMA_RAMA, rama);   /* set per-sector storage modulus */
  GS_WRITE(GS_READ_DMA_RACT, 0);      /* large-transfer not relevant */
  save_and_cli(flags);
  radc &= ~0x04;
  GS_WRITE(GS_READ_DMA_RADC, radc);   /* Set up DREQ */
  iradc |= 0x01;
  GS_WRITE(GS_READ_DMA_IRADC, iradc);   /* start the DMA-flush */
  if (debug) printk(KERN_INFO "gs: FIFO-flush DMA initiated, wait 5 ms\n");
  gs_queue_worker_for_timer(worker, 5, GS_WORKER_RESET_DONE);
  restore_flags(flags);
}

static void gs_worker_reset_done(struct Scsi_Host *instance, struct gs_worker *worker)
{
  unsigned short radc, iradc;
  struct gs_hostdata *hostdata;
  hostdata = (struct gs_hostdata *) instance->hostdata;
  if (debug) printk(KERN_INFO "gs: timer expired, stopping DMA\n");
  radc = GS_READ(GS_READ_DMA_RADC);
  iradc = GS_READ(GS_READ_DMA_IRADC);
  radc |= 0x0004;      
  GS_WRITE(GS_READ_DMA_RADC, radc);  /* DMA TRACON REQ */
  iradc &= ~0x0001;
  GS_WRITE(GS_READ_DMA_IRADC, iradc);   /* stop the DMA */
  iradc &= ~0x8000;
  GS_WRITE(GS_READ_DMA_IRADC, iradc);   /* DMA module reset */
  iradc |= 0x8000;
  GS_WRITE(GS_READ_DMA_IRADC, iradc);   /* DMA module reset release*/
  radc &= ~0x01;
  GS_WRITE(GS_READ_DMA_RADC, radc);   /* Reset read FIFO */
  radc |= 0x01;
  GS_WRITE(GS_READ_DMA_RADC, radc);   /* release reset on read FIFO*/
  gs_worker_next_state(worker, GS_WORKER_DONE);
}

static void gs_worker_setup_fl(struct Scsi_Host *instance, struct gs_worker *worker)
{
  Scsi_Cmnd *cmd;
  struct gs_hostdata *hostdata;
  hostdata = (struct gs_hostdata *) instance->hostdata;
  cmd = (Scsi_Cmnd *) worker->scsiRequest;
  if (hostdata->resourcesBusy & GS_RESOURCE_FL) {
    worker->gsResourcesNeeded |= GS_RESOURCE_FL;
    return;
  }
  worker->gsResourcesUsed |= GS_RESOURCE_FL;
  hostdata->resourcesBusy |= GS_RESOURCE_FL;
  if (debug) printk(KERN_INFO "gs: we now own the FL interface\n");
  worker->transferRemaining = worker->transferSize;
  gs_queue_worker_for_timer(worker, 2, GS_WORKER_FL_DATA_BURST);
}

static void gs_worker_fl_data_burst(struct Scsi_Host *instance, struct gs_worker *worker)
{
  unsigned int flags;
  unsigned short cint, cctl1;
  unsigned long bcount;
  unsigned short tempbuf[16];
  int i;
  Scsi_Cmnd *cmd;
  struct gs_hostdata *hostdata;
  hostdata = (struct gs_hostdata *) instance->hostdata;
  cmd = (Scsi_Cmnd *) worker->scsiRequest;
  save_and_cli(flags);
  /*
    If the FL interrupt is still pending, we timed out.  Mask off the
    interrupt and bail out.
  */
  if (worker->gsInterruptsPending & GS_INT_FL) {
    cint = GS_READ(GS_FL_CINT);
    cint |= 0x88;
    GS_WRITE(GS_FL_CINT, cint);
    if (cmd) {
      cmd->result = DID_ABORT << 16;
    }
    worker->aborting = 1;
    restore_flags(flags);
    printk(KERN_INFO "gs: FL interface timed out\n");
    gs_worker_next_state(worker, GS_WORKER_DONE);
    return;
  }
  restore_flags(flags);
  bcount = worker->transferRemaining;
  if (bcount == 0) {
    gs_worker_next_state(worker, GS_WORKER_DONE);
    return;
  }
  if (bcount > 32) {
    bcount = 32;
  }
  if (debug) printk(KERN_INFO "gs: do an FL data burst of %ld bytes\n", bcount);
  worker->transferBurst = bcount;
  memset(tempbuf, 0, sizeof tempbuf);
  if (cmd) {
    (void) gsTransferFromUser(cmd, (unsigned char *) tempbuf, bcount);
  } else {
    memcpy(tempbuf, worker->writeback.transformBuffer + worker->transferSize -
	   worker->transferRemaining, bcount);
  }
  worker->transferRemaining -= bcount;
  GS_WRITE_MULTI(GS_FL_CDATA, (bcount + 1) / 2, tempbuf);
  if (debug) {
    printk(KERN_INFO "gs: FL count %ld data", bcount);
    for (i = 0; i < (bcount + 1) / 2; i++) {
      printk(" 0x%04X", tempbuf[i]);
    }
    printk("\n");
  }
  GS_WRITE(GS_FL_CBYTNM, (unsigned short) bcount - 1);
  GS_WRITE(GS_FL_CCTL2, 0x00C2); /* enable clock, LSB first, rising edge */
  GS_WRITE(GS_FL_CLATCH, 0x0001); /* type 1 latching, H>L>H, enable */
  GS_WRITE(GS_FL_CCKSEL, 0x0005); /* serial clock is VCLK / 128 */
  GS_WRITE(GS_FL_CINTSL, 0x0000); /* internal setting */
  /*
    Queue us up for the interrupt and for a timeout, then start
    the Bubble Machine.
  */
  save_and_cli(flags);
  gs_queue_worker_for_interrupt(worker, GS_INT_FL,
				GS_WORKER_FL_DATA_BURST);
  gs_queue_worker_for_timer(worker, 59, GS_WORKER_FL_DATA_BURST);
  cint = GS_READ(GS_FL_CINT);
  cctl1 = GS_READ(GS_FL_CCTL1);
  cint &= ~0x0080;
  GS_WRITE(GS_FL_CINT, cint); /* un-mask interrupt */
  cctl1 |= 0x0080;
  GS_WRITE(GS_FL_CCTL1, cctl1); /* start the transceiver */
  restore_flags(flags);
  return;
}

static void gs_worker_setup_dac(struct Scsi_Host *instance, struct gs_worker *worker)
{
  Scsi_Cmnd *cmd;
  struct gs_hostdata *hostdata;
  hostdata = (struct gs_hostdata *) instance->hostdata;
  cmd = (Scsi_Cmnd *) worker->scsiRequest;
  if (hostdata->resourcesBusy & GS_RESOURCE_DAC) {
    worker->gsResourcesNeeded |= GS_RESOURCE_DAC;
    return;
  }
  worker->gsResourcesUsed |= GS_RESOURCE_DAC;
  hostdata->resourcesBusy |= GS_RESOURCE_DAC;
  if (debug) printk(KERN_INFO "gs: we now own the DAC interface\n");
  worker->transferRemaining = worker->transferSize;
  gs_queue_worker_for_timer(worker, 2, GS_WORKER_DAC_DATA_BURST);
}

static void gs_worker_dac_data_burst(struct Scsi_Host *instance, struct gs_worker *worker)
{
  unsigned int flags;
  unsigned short dint, dctl1;
  unsigned long bcount;
  unsigned short tempbuf[16];
  Scsi_Cmnd *cmd;
  struct gs_hostdata *hostdata;
  hostdata = (struct gs_hostdata *) instance->hostdata;
  cmd = (Scsi_Cmnd *) worker->scsiRequest;
  save_and_cli(flags);
  /*
    If the DAC interrupt is still pending, we timed out.  Mask off the
    interrupt and bail out.
  */
  if (worker->gsInterruptsPending & GS_INT_DAC) {
    dint = GS_READ(GS_DAC_DINT);
    dint |= 0x88;
    GS_WRITE(GS_DAC_DINT, dint);
    if (cmd) {
      cmd->result = DID_ABORT << 16;
    }
    worker->aborting = 1;
    restore_flags(flags);
    printk(KERN_INFO "gs: DAC interface timed out\n");
    gs_worker_next_state(worker, GS_WORKER_DONE);
    return;
  }
  restore_flags(flags);
  bcount = worker->transferRemaining;
  if (bcount == 0) {
    gs_worker_next_state(worker, GS_WORKER_DONE);
    return;
  }
  if (bcount > 32) {
    bcount = 32;
  }
  if (debug) printk(KERN_INFO "gs: do a DAC data burst of %ld bytes\n", bcount);
  worker->transferBurst = bcount;
  memset(tempbuf, 0, sizeof tempbuf);
  if (cmd) {
    (void) gsTransferFromUser(cmd, (unsigned char *) tempbuf, bcount);
  } else {
    memcpy(tempbuf, worker->writeback.buffer + worker->transferSize -
	   worker->transferRemaining, bcount);
  }
  worker->transferRemaining -= bcount;
  GS_WRITE_MULTI(GS_DAC_DDATA, (bcount + 1) / 2, tempbuf);
  GS_WRITE(GS_DAC_DBYTNM, (unsigned short) bcount - 1);
  GS_WRITE(GS_DAC_DCTL2, 0x0082); /* enable clock, MSB first, rising edge */
  GS_WRITE(GS_DAC_DLATCH, 0x0001); /* type 1 latching, H>L>H, enable */
  GS_WRITE(GS_DAC_DCKSEL, 0x0000); /* serial clock is 6.75 MHz */
  GS_WRITE(GS_DAC_DINTSL, 0x0000); /* internal setting */
  /*
    Queue us up for the interrupt and for a timeout, then start
    the Bubble Machine.
  */
  save_and_cli(flags);
  gs_queue_worker_for_interrupt(worker, GS_INT_DAC,
				GS_WORKER_DAC_DATA_BURST);
  gs_queue_worker_for_timer(worker, 59, GS_WORKER_DAC_DATA_BURST);
  dint = GS_READ(GS_DAC_DINT);
  dctl1 = GS_READ(GS_DAC_DCTL1);
  dint &= ~0x0080;
  GS_WRITE(GS_DAC_DINT, dint); /* un-mask interrupt */
  dctl1 |= 0x0080;
  GS_WRITE(GS_DAC_DCTL1, dctl1); /* start the transceiver */
  restore_flags(flags);
  return;
}

static void gs_queue_worker_for_interrupt (struct gs_worker *worker,
					  unsigned long interruptsNeeded,
					  enum gs_worker_state nextState)
{
  unsigned long flags;
  unsigned short atim;
  save_and_cli(flags);
  if (worker->gsInterruptsPending & ~interruptsNeeded) {
    printk(KERN_ERR "gs: worker %d new state %d old int %08lX add int %08lX\n",
	   worker->sn, nextState, worker->gsInterruptsPending,
	   interruptsNeeded);
  }
  worker->gsInterruptsPending |= interruptsNeeded;
  worker->state = nextState;
  if (interruptsNeeded & GS_INT_ATAPI) {
    atim = GS_READ(GS_ATA_INTERRUPT_MASK_REGISTER);
    GS_WRITE(GS_ATA_INTERRUPT_MASK_REGISTER, atim & ~GS_ATA_INTERRUPT_CLEAR);
  }
  restore_flags(flags);
}

static void gs_queue_worker_for_timer (struct gs_worker *worker,
				       int timerTicks,
				       enum gs_worker_state nextState)
{
  unsigned long flags;
  save_and_cli(flags);
  worker->gsInterruptsPending |= GS_INT_TIMER;
  worker->timeout = timerTicks ? timerTicks : 1;
  worker->state = nextState;
  restore_flags(flags);
  queue_task(&gs_timer_task_struct, &tq_timer);
}

static void gs_worker_disarm_timer (struct gs_worker *worker) {
  unsigned long flags;
  save_and_cli(flags);
  worker->gsInterruptsPending &= ~GS_INT_TIMER;
  worker->timeout = 0;
  restore_flags(flags);
}

static void gs_worker_set_deadman (struct gs_worker *worker,
					 int timerTicks)
{
  unsigned long flags;
  save_and_cli(flags);
  worker->deadman = timerTicks ? timerTicks : 20*HZ;
  restore_flags(flags);
  queue_task(&gs_timer_task_struct, &tq_timer);
}

static void gs_worker_disarm_deadman (struct gs_worker *worker)
{
  worker->deadman = 0;
}

static unsigned long gs_interrupt_for_workers(struct Scsi_Host *instance,
					      unsigned long interruptsFired)
{
  unsigned long flags;
  unsigned long intersection;
  unsigned long interruptsTaken;
  struct list_head *this;
  struct gs_worker *worker;
  struct gs_hostdata *hostdata;
  hostdata = (struct gs_hostdata *) instance->hostdata;
  save_and_cli(flags);
  interruptsTaken = 0;
  if (debug) printk(KERN_INFO "gs: process virtual interrupt 0x%08lX\n",
	 interruptsFired);
  list_for_each(this,&hostdata->workers) {
    worker = (struct gs_worker *) this;
    if (worker->gsInterruptsPending) {
      if (debug) printk(KERN_INFO "gs: worker %d pending on 0x%08lX\n",
			worker->sn, worker->gsInterruptsPending);
      intersection = worker->gsInterruptsPending & interruptsFired;
      if (intersection) {
	interruptsTaken |= intersection;
	worker->gsInterruptsFired = intersection;
	worker->gsInterruptsPending &= ~intersection;
	worker->go = 1;
	gs_worker_disarm_timer(worker);
	if (debug) printk(KERN_INFO "gs: woke up the worker\n");
      }
    }
  }
  return interruptsTaken;
}  

/*
 *	gs_run_workers - run the individual state machines for the
 *	ATAPI requests on the queue.
 *
 */

static int gs_run_workers(struct Scsi_Host *instance)
{
  int done = 1;
  struct list_head *this, *next;
  struct gs_worker *worker;
  struct gs_hostdata *hostdata;
  hostdata = (struct gs_hostdata *) instance->hostdata;
  list_for_each_safe(this,next,&hostdata->workers) {
    worker = (struct gs_worker *) this;
    if (worker->go) {
      worker->go = 0;
      switch (worker->state) {
      case GS_WORKER_SETUP:
	gs_worker_setup(instance, worker);
	break;
      case GS_WORKER_PIO_TO_SDRAM:
	gs_worker_pio_to_sdram(instance, worker);
	break;
      case GS_WORKER_DMA_TO_SDRAM:
	gs_worker_dma_to_sdram(instance, worker);
	break;
      case GS_WORKER_DO_PSW_DMA_BURST:
	gs_worker_do_psw_dma_burst(instance, worker);
	break;
      case GS_WORKER_DETACH_COMMAND:
	gs_worker_detach_command(instance, worker);
	break;
      case GS_WORKER_QUEUE_FOR_ATAPI:
	gs_worker_queue_for_atapi(instance, worker);
	break;
      case GS_WORKER_QUEUE_FOR_GATE:
	gs_worker_queue_for_gate(instance, worker);
	break;
      case GS_WORKER_INIT_ATAPI:
	gs_worker_init_atapi(instance, worker);
	break;
      case GS_WORKER_SEND_PACKET:
	gs_worker_send_packet(instance, worker);
	break;
      case GS_WORKER_DMA_DATA:
	gs_worker_dma_data(instance, worker);
	break;
      case GS_WORKER_CHECK_DMA_RESULTS:
	gs_worker_check_dma_results(instance, worker);
	break;
      case GS_WORKER_PIO_DATA:
	gs_worker_pio_data(instance, worker);
	break;
      case GS_WORKER_CHECK_PIO_RESULTS:
	gs_worker_check_pio_results(instance, worker);
	break;
      case GS_WORKER_PIO_FROM_SDRAM:
	gs_worker_pio_from_sdram(instance, worker);
	break;
      case GS_WORKER_DMA_FROM_SDRAM:
	gs_worker_dma_from_sdram(instance, worker);
	break;
      case GS_WORKER_DO_PSR_DMA_BURST:
	gs_worker_do_psr_dma_burst(instance, worker);
	break;
      case GS_WORKER_LOOPBACK_DMA_DONE:
	gs_worker_loopback_dma_done(instance, worker);
	break;
      case GS_WORKER_POSTPROCESS:
	gs_worker_postprocess(instance, worker);
	break;
      case GS_WORKER_INIT_FOR_REQUEST_SENSE:
	gs_worker_init_for_request_sense(instance, worker);
	break;
      case GS_WORKER_SEND_REQUEST_SENSE:
	gs_worker_send_request_sense(instance, worker);
	break;
      case GS_WORKER_TRANSFER_SENSE_DATA:
	gs_worker_transfer_sense_data(instance, worker);
	break;
      case GS_WORKER_DONE:
	gs_worker_done(instance, worker);
	list_del((struct list_head *) worker);
	kfree(worker);
	worker = NULL;
	break;
      case GS_WORKER_SETUP_RESET:
	gs_worker_setup_reset(instance, worker);
	break;
      case GS_WORKER_RESET_DONE:
	gs_worker_reset_done(instance, worker);
	break;
      case GS_WORKER_SETUP_FL:
	gs_worker_setup_fl(instance, worker);
	break;
      case GS_WORKER_FL_DATA_BURST:
	gs_worker_fl_data_burst(instance, worker);
	break;
      case GS_WORKER_SETUP_DAC:
	gs_worker_setup_dac(instance, worker);
	break;
      case GS_WORKER_DAC_DATA_BURST:
	gs_worker_dac_data_burst(instance, worker);
	break;
      default:
	break;
      }
      if (worker) {
	done &= !worker->go;
      }
    }
  }
  if (list_empty(&hostdata->workers)) {
    hostdata->resourcesBusy |= GS_RESOURCE_GATE;
  }
  return done;
}

/*
 *	gs_gated_requests: returns the total amount of SDRAM
 *         reserved by gated WRITE requests.
 */

static unsigned int gs_gated_requests(struct Scsi_Host *instance)
{
  struct list_head *this, *next;
  struct gs_worker *worker;
  struct gs_hostdata *hostdata;
  unsigned int totalGated = 0;
  hostdata = (struct gs_hostdata *) instance->hostdata;
  list_for_each_safe(this,next,&hostdata->workers) {
    worker = (struct gs_worker *) this;
    totalGated += worker->gatedSize;
  }
  return totalGated;
}


/*
  The coroutine code has been gently and lovingly lifted from
  NCR5380 - go see the comments there.
*/

static unsigned long main_running = 0;

static __inline__ void run_main(void)
{
  if (!test_and_set_bit(0, &main_running)) {
    gs_main();
  }
}

/*
  Coroutine runner task
*/

static void gs_task (void *whatever)
{
  (void) whatever;
  run_main();
}

/*
  Timer task.  Run through all known workers, and kick awake any of
  them who were waiting for a timer countdown and whose countdown
  has expired.  Also, abort any workers which had a deadman timer
  (these are usually background/buffered write commands, whose
  SCSI request/command has already been returned as 'completed'
  and which thus no longer have a SCSI-layer timer watching over
  them.
*/

static void gs_timer_task (void *whatever)
{
   struct Scsi_Host *instance;
   struct gs_hostdata *hostdata;
   struct list_head *this;
   struct gs_worker *worker;
   int wakeup = 0;
   int rearm = 0;
   (void) whatever;
   for (instance = first_instance;
	instance && instance->hostt == the_template;
	instance = instance->next) {
     hostdata = (struct gs_hostdata *) instance->hostdata;
     list_for_each(this,&hostdata->workers) {
       worker = (struct gs_worker *) this;
       if (worker->deadman != 0) {
	 worker->deadman --;
	 if (worker->deadman == 0) {
	   worker->aborting = 1;
	   worker->go = 1;
	   printk(KERN_ERR "gs: deadman timer aborting worker #%d\n", worker->sn);
	 } else {
	   rearm = 1;
	 }
       }
       if (worker->timeout > 0 &&
	   (worker->gsInterruptsPending & GS_INT_TIMER)) {
	 worker->timeout --;
	 if (worker->timeout == 0) {
	   worker->gsInterruptsPending &= ~GS_INT_TIMER;
	   worker->gsInterruptsFired |= GS_INT_TIMER;
	   worker->go = 1;
	   wakeup = 1;
	 } else {
	   rearm = 1;
	 }
       }
     }
   }
   if (wakeup) {
     queue_task (&gs_task_struct, &tq_immediate);
     mark_bh (IMMEDIATE_BH);
   }
   if (rearm) {
     queue_task(&gs_timer_task_struct, &tq_timer);
   }
}


/**
 *	gs_main	-	Giant Slalom state machines
 *
 *	gs_main is a coroutine that runs as long as more work can 
 *      be done on the Giant Slalom host adapter in a system.  Both 
 *      gs_queue_command() and gs_intr() will try to start it 
 *      in case it is not running.
 * 
 *	Locks; The caller must hold the io_request_lock. The lock will still be
 *	held on return but may be dropped while running. Called functions take
 *	the DMA lock.
 */

static void gs_main(void) {
   Scsi_Cmnd *tmp, *prev, *next;
   struct Scsi_Host *instance;
   struct gs_hostdata *hostdata;
   int done;
   unsigned int iflags, handler;

   /*
    * We run (with interrupts disabled) until we're sure that none of
    * the host adapters have anything that can be done, at which point 
    * we set main_running to 0 and exit.
    *
    * Interrupts are enabled before doing various other internal 
    * instructions, after we've decided that we need to run through
    * the loop again.
    *
    * this should prevent any race conditions.
    *
    * XXX the Giant Slalom adaptation of this process would not be
    * safe if two or more GS adapters were present, and one adapter
    * were unloaded while the main loop was running... unlikely but
    * possible.
    */

   do {
     /* Lock held here */
     done = 1;
     for (instance = first_instance; instance && instance->hostt == the_template; instance = instance->next) {
       /*
	* Search through the issue_queue for a command destined
	* for a target that's not busy.
	*/
       hostdata = (struct gs_hostdata *) instance->hostdata;
       write_lock_irqsave(&hostdata->queueLock, iflags);
       for (tmp = (Scsi_Cmnd *) hostdata->issue_queue, prev = NULL;
	    tmp;
	    prev = tmp, tmp = next) 
	 {
	   int device = tmp->target;
	   next = (Scsi_Cmnd *) tmp->host_scribble;
	   /*  When we find one, remove it from the issue queue. */
	   if (device >= GS_DEVICES || !(hostdata->busy[deviceDispatcher[device]])) {
	     if (prev) {
	       REMOVE(prev, prev->host_scribble, tmp, tmp->host_scribble);
	       prev->host_scribble = tmp->host_scribble;
	     } else {
	       REMOVE(-1, hostdata->issue_queue, tmp, tmp->host_scribble);
	       hostdata->issue_queue = (Scsi_Cmnd *) tmp->host_scribble;
	     }
	     tmp->host_scribble = NULL;
	     if (device >= GS_DEVICES) {
	       if (debug) printk(KERN_DEBUG "Rejecting command for device %d, bad target\n", device);
	       tmp->result = DID_BAD_TARGET << 16;
	       tmp->scsi_done(tmp);
	       done = 0;
	       tmp = prev;                        
	       continue;
	     }
	     /*
	       Initialize the buffer pointers
	     */
	     initialize_SCp(tmp);
	     /*
	       Connect this request to the handler and mark the handler busy with
	       a request not yet successfully initiated.
	     */
	     hostdata->connected[deviceDispatcher[device]] = tmp;
	     hostdata->busy[deviceDispatcher[device]] = 1;
	     hostdata->pending[deviceDispatcher[device]] = 1;
                    
	     /* When removing an item, we want to hook up the next item to our
	        prev item (not ourself).  At loop iteration time, this will
	        happen and prevents the removed item from being
	        unintentionally left in the list.
	     */
	     tmp = prev;
	   }
	 }
       /*
	 OK, we've walked the queue, and connected up any requests for
	 dispatchers which were previously idle.  Now, kick off any
	 dispatchers which have uninitiated requests.
       */
       write_unlock_irqrestore(&hostdata->queueLock, iflags);
       for (handler = 0; handler < GS_HANDLERS; handler++) {
	 /*
	   Call the handler for this [pseudo]target
	 */
	 if (hostdata->pending[handler]) {
	   int result;
	   hostdata->pending[handler] = 0;
	   tmp = (Scsi_Cmnd *) hostdata->connected[handler];
	   result = (*dispatchers[handler])(instance, tmp, tmp->target);
	   /*
	     Dispatchers return 0 if request accepted and in progress, 1 if 
	     request was completed immediately and should be marked as done,
	     and -1 if the request couldn't be accepted and should be requeued
	     on the head of the list.
	   */
	   if (result != 0) {
	     hostdata->connected[handler] = NULL;
	     hostdata->busy[handler] = 0;
	     if (result < 0) {
	       write_lock_irqsave(&hostdata->queueLock, iflags);
	       LIST(tmp, hostdata->issue_queue);
	       tmp->host_scribble = (unsigned char *) hostdata->issue_queue;
	       hostdata->issue_queue = tmp;
	       write_unlock_irqrestore(&hostdata->queueLock, iflags);
	     } else {
	       if (debug) printk(KERN_INFO "gs: gs_main calls done on %p\n", tmp);
	       tmp->scsi_done(tmp);
	       done = 0;
	     }
	   }
	 }
       }
       done &= gs_run_workers(instance);
     }
   } while (!done);
   /* Exit lock held */
   clear_bit(0, &main_running);

}

static void show_gs_intr(void)
{
  const int numIntrs = 7;
  const char *intrs[] = { " PsrDataValid",
			 " PswStallTimeout",
			 " PsrStallTimeout",
			 " GiantSlalomStallTimeout",
			 " CS8420Interrupt",
			 " XirqInterrupt",
			 " XirqsInterrupt" };
  int i;
  printk(KERN_INFO "gs: interrupt ");
  unsigned short isr = GS_BRIDGE->isr;
  for (i = numIntrs - 1; i >= 0; i--) {
    if (isr & (1 << i)) {
      printk(intrs[i]);
    }
  }
  printk("\n");
}

/**
 *
 *      broadcom_intr - Broadcom "ROM-bus DMA done" interrupt handler
 *
 */

static void broadcom_intr(int irq, void *dev_id, struct pt_regs *regs)
{
  unsigned long flags;
  struct Scsi_Host *instance;
  unsigned long interruptsTaken;

  save_and_cli(flags);

  if (debug) printk(KERN_INFO "gs: Broadcom DMA interrupt triggered!\n");

  disable_irq(DMA_IRQ);
  gs_broadcom_enabled = 0;

  interruptsTaken = 0;
  for (instance = first_instance;
       instance && instance->hostt == the_template;
       instance = instance->next) {
    interruptsTaken |= gs_interrupt_for_workers(instance, GS_INT_BROADCOM_DMA);
  }

  queue_task (&gs_task_struct, &tq_immediate);
  mark_bh (IMMEDIATE_BH);

  restore_flags(flags);
  
  if (!interruptsTaken) {
    printk(KERN_ERR "Oops!  No worker took the Broadcom DMA interrupt!\n");
  }

}

/**
 * 	gs_intr	-	Giant Slalom interrupt handler
 *
 *	Handle interrupts.
 *
 *	Locks: caller must hold the io_request lock.
 */

static void gs_intr(int irq, void *dev_id, struct pt_regs *regs) {
  struct Scsi_Host *instance;

  unsigned long interruptS, unclearedInterrupts;
  unsigned long interruptsTaken;
  unsigned long wakeups;
  unsigned short foo;
  unsigned short bridgeISR;
  unsigned short apdc, iapdc;
  unsigned short cint, dint;
  unsigned long flags;

  if (debug) {
    show_gs_intr();
  }

  /*
    Do the magic here.
  */

  bridgeISR = GS_BRIDGE->isr;
  barrier();
  GS_BRIDGE->isr = bridgeISR; /* clear everything we can... */
  barrier();

  wakeups = 0;

  if ((bridgeISR & GS_BRIDGE_INTR_PSR_DATA_VALID) &&
      (GS_BRIDGE->imr & GS_BRIDGE_INTR_PSR_DATA_VALID)) {
    wakeups |= GS_INT_PSR_VALID;
    save_and_cli(flags);
    GS_BRIDGE->imr &= ~GS_BRIDGE_INTR_PSR_DATA_VALID;
    restore_flags(flags);
  }

  if (bridgeISR & GS_BRIDGE_INTR_PSW_STALL) {
    wakeups |= GS_INT_PSW_STALL;
    save_and_cli(flags);
    GS_BRIDGE->imr &= ~GS_BRIDGE_INTR_PSW_STALL;
    restore_flags(flags);
  }

  if (bridgeISR & GS_BRIDGE_INTR_PSR_STALL) {
    wakeups |= GS_INT_PSR_STALL;
    save_and_cli(flags);
    GS_BRIDGE->imr &= ~GS_BRIDGE_INTR_PSR_STALL;
    restore_flags(flags);
  }

  if (bridgeISR & GS_BRIDGE_INTR_GS_XIRQS) {

    interruptS = (GS_READ(GS_INTERRUPT_S_FACTOR_A) << 16) |
      GS_READ(GS_INTERRUPT_S_FACTOR_B);
    if (debug) printk(KERN_INFO "gs: interrupt factor is 0x%08lX\n", interruptS);
    unclearedInterrupts = interruptS;

    /*
      We use a single virtual interrupt for both GS->host and host->GS DMA,
      and we only ever have one worker pending on this virtual interrupt.
      When either (real) interrupt fires, disarm both real interrupts
      and trigger the virtual interrupt
    */

    if (interruptS & (GS_INTERRUPT_S_READ_DMA | GS_INTERRUPT_S_WRITE_DMA)) {
      unclearedInterrupts &= ~(GS_INTERRUPT_S_READ_DMA | GS_INTERRUPT_S_WRITE_DMA);
      wakeups |= GS_INT_HOST_DMA;
      foo = GS_READ(GS_READ_DMA_IRADC);
      foo |= 0x08; /* acknowledge/clear interrupt */
      GS_WRITE(GS_READ_DMA_IRADC, foo);
      foo |= 0x10; /* disable the interrupt */
      GS_WRITE(GS_READ_DMA_IRADC, foo);
      foo = GS_READ(GS_WRITE_DMA_IWADC);
      foo |= 0x08; /* acknowledge/clear interrupt */
      GS_WRITE(GS_WRITE_DMA_IWADC, foo);
      foo |= 0x10; /* disable the interrupt */
      GS_WRITE(GS_WRITE_DMA_IWADC, foo);
    }

    if (interruptS & GS_INTERRUPT_S_ATA_PRIMARY) {
      /*
	Read the ATA status register to clear the drive interrupt,
	then clear the triggered interrupt in the GS.
      */
      apdc = GS_READ(GS_ATA_DMA_APDC);
      iapdc = GS_READ(GS_ATA_DMA_IAPDC);
      if (debug) printk(KERN_DEBUG "gs: ATAPI interrupt, apdc %04X iapdc %04X\n",
	     apdc, iapdc);
      foo = GS_READ(GS_ATAPI_STATUS_REGISTER);
      barrier();
      foo = GS_READ(GS_ATA_INTERRUPT_MASK_REGISTER);
      GS_WRITE(GS_ATA_INTERRUPT_MASK_REGISTER, foo | GS_ATA_INTERRUPT_CLEAR);
      /*
	Mask off ATAPI interrupt.  Normally, reading the status register
	is supposed to persuade the drive to stop interrupting, but
	this doesn't always seem to work?

	Interrupt will be un-masked when a worker queues for it.
      */
      unclearedInterrupts &= ~GS_INTERRUPT_S_ATA_PRIMARY;
      wakeups |= GS_INT_ATAPI;
    }

    if (interruptS & GS_INTERRUPT_S_ATA_PRIMARY_DMA) {
      foo = GS_READ(GS_ATA_DMA_IAPDC);
      if (debug) printk(KERN_DEBUG "gs: ATAPI DMA interrupt, iapdc = %04X\n", foo);
      GS_WRITE(GS_ATA_DMA_IAPDC, foo | 0x04); /* acknowledge and... */
      GS_WRITE(GS_ATA_DMA_IAPDC, foo | 0x10); /*    ... mask off    */
      unclearedInterrupts &= ~GS_INTERRUPT_S_ATA_PRIMARY_DMA;
      wakeups |= GS_INT_ATAPI_DMA;
    }

    if (interruptS & GS_INTERRUPT_S_SERIAL_C) {
      cint = GS_READ(GS_FL_CINT);
      cint |= 0x0088;
      GS_WRITE(GS_FL_CINT, cint); /* acknowledge & mask interrupt */
      unclearedInterrupts &= ~GS_INTERRUPT_S_SERIAL_C;
      wakeups |= GS_INT_FL;
    }

    if (interruptS & GS_INTERRUPT_S_SERIAL_D) {
      dint = GS_READ(GS_DAC_DINT);
      dint |= 0x0088;
      GS_WRITE(GS_DAC_DINT, dint); /* acknowledge & mask interrupt */
      unclearedInterrupts &= ~GS_INTERRUPT_S_SERIAL_D;
      wakeups |= GS_INT_DAC;
    }

    if (unclearedInterrupts) {
      printk(KERN_ERR "gs: uncleared interrupts %08lx - UGH!\n",
	     unclearedInterrupts);
    }

  }

  /*
    Kick awake any workers who were waiting for any of the interrupts
    that we just noticed, and then tickle the bottom-half task
  */

  if (wakeups) {

    interruptsTaken = 0;

    for (instance = first_instance;
	 instance && instance->hostt == the_template;
	 instance = instance->next) {
      interruptsTaken |= gs_interrupt_for_workers(instance, wakeups);
    }

    queue_task (&gs_task_struct, &tq_immediate);
    mark_bh (IMMEDIATE_BH);

    if (interruptsTaken != wakeups) {
      printk(KERN_ERR "gs: OOPS:  Unclaimed virtual interrupt 0x%08lX\n",
	     wakeups & ~interruptsTaken);
    }
  }
	   
}

/**
 *	do_gs_intr
 *	@irq: interrupt number
 *	@dev_id: device info
 *	@regs: registers (unused)
 *
 *	Takes the io_request_lock and invokes the Giant Slalom interrupt
 *	handler code
 *
 *	Locks: takes and releases the io_request lock
 */

static void do_gs_intr(int irq, void *dev_id, struct pt_regs *regs) {
  unsigned long flags;

  spin_lock_irqsave(&io_request_lock, flags);
  gs_intr(irq, dev_id, regs);
  spin_unlock_irqrestore(&io_request_lock, flags);
}

/**
   gs_abort - abort a command.
 */

static int gs_abort(Scsi_Cmnd * cmd)
{
   struct Scsi_Host *instance;
   struct gs_hostdata *hostdata;
   struct list_head *this;
   struct gs_worker *worker;
   Scsi_Cmnd *tmp, *prev, *next;
   int handler;
   unsigned long iflags;
   /*
     Scan the driver instances (should only be one, really).
     For each instance, check for four cases:
     [1] Command is on the issue queue.  Easy case... dequeue it and
         it's aborted.
     [2] Command is connected to a device, and pending (i.e. hasn't
         actually been dispatched yet).  Just as easy... disconnect it,
         un-pend the device, and it's aborted.
     [3] Command is connected to the device and not pending.  Can't
         abort it in this case - return a SCSI_ABORT_NOT_RUNNING.
     [4] Command isn't in the issue queue, isn't connected, and there's
         a worker which owns it.  We tell the worker to abort the
         command, kick the worker awake, and return SCSI_ABORT_PENDING.
   */

   printk(KERN_ERR "gs: in gs_abort()\n");

   gs_autodebug_on();

   printk(KERN_ERR "gs:   bISR   %04X\n", GS_BRIDGE->isr);

   if (((struct gs_hostdata *) first_instance->hostdata)->pldLoaded) {
     printk(KERN_ERR "gs:   IntSA  %04X\n", GS_READ(GS_INTERRUPT_S_FACTOR_A));
     printk(KERN_ERR "gs:   IntSB  %04X\n", GS_READ(GS_INTERRUPT_S_FACTOR_B));
     printk(KERN_ERR "gs:   IntA   %04X\n", GS_READ(GS_INTERRUPT_FACTOR_A));
     printk(KERN_ERR "gs:   IntB   %04X\n", GS_READ(GS_INTERRUPT_FACTOR_B));
     printk(KERN_ERR "gs:   IWADC  %04X\n", GS_READ(GS_WRITE_DMA_IWADC));
     printk(KERN_ERR "gs:   WADC   %04X\n", GS_READ(GS_WRITE_DMA_WADC));
     printk(KERN_ERR "gs:   IRADC  %04X\n", GS_READ(GS_READ_DMA_IRADC));
     printk(KERN_ERR "gs:   RADC   %04X\n", GS_READ(GS_READ_DMA_RADC));
     printk(KERN_ERR "gs:   IAPDC  %04X\n", GS_READ(GS_ATA_DMA_IAPDC));
     printk(KERN_ERR "gs:   APDC   %04X\n", GS_READ(GS_ATA_DMA_APDC));
   }

   for (instance = first_instance;
	instance && instance->hostt == the_template;
	instance = instance->next) {
     hostdata = (struct gs_hostdata *) instance->hostdata;
     write_lock_irqsave(&hostdata->queueLock, iflags);
     for (tmp = (Scsi_Cmnd *) hostdata->issue_queue, prev = NULL;
	  tmp;
	  prev = tmp, tmp = next) {
       next = (Scsi_Cmnd *) tmp->host_scribble;
       if (tmp == cmd) {
	 /* found it in the issue queue.  Dequeue and abort. */
	 if (prev) {
	   REMOVE(prev, prev->host_scribble, tmp, tmp->host_scribble);
	   prev->host_scribble = tmp->host_scribble;
	 } else {
	   REMOVE(-1, hostdata->issue_queue, tmp, tmp->host_scribble);
	   hostdata->issue_queue = (Scsi_Cmnd *) tmp->host_scribble;
	 }
	 tmp->host_scribble = NULL;
	 tmp->result = DID_ABORT << 16;
	 write_unlock_irqrestore(&hostdata->queueLock, iflags);
	 printk(KERN_INFO "gs: aborted a command in the issue queue\n");
	 return SCSI_ABORT_SUCCESS;
       }
     }
     for (handler = 0; handler < GS_HANDLERS; handler++) {
       tmp = (Scsi_Cmnd *) hostdata->connected[handler];
       if (tmp == cmd) {
	 if (hostdata->pending[handler]) {
	   hostdata->connected[handler] = NULL;
	   hostdata->pending[handler] = 0;
	   tmp->result = DID_ABORT << 16;
	   write_unlock_irqrestore(&hostdata->queueLock, iflags);
	   printk(KERN_INFO "gs: aborted a command which was pending\n");
	   return SCSI_ABORT_SUCCESS;
	 } else {
	   write_unlock_irqrestore(&hostdata->queueLock, iflags);
	   printk(KERN_INFO "gs: can't abort a connected command\n");
	   return SCSI_ABORT_NOT_RUNNING;
	 }
       }
     }
     list_for_each(this,&hostdata->workers) {
       worker = (struct gs_worker *) this;
       if (worker->scsiRequest == cmd) {
	 worker->aborting = 1;
	 worker->go = 1;
	 gs_worker_disarm_timer(worker);
	 queue_task (&gs_task_struct, &tq_immediate);
	 mark_bh (IMMEDIATE_BH);
	 write_unlock_irqrestore(&hostdata->queueLock, iflags);
	 printk(KERN_INFO "gs: aborting a command in progress\n");
	 return SCSI_ABORT_PENDING;
       }
     }
     write_unlock_irqrestore(&hostdata->queueLock, iflags);
   }
   printk(KERN_INFO "gs: didn't find that command, can't abort it\n");
   return SCSI_ABORT_NOT_RUNNING;
}

/**
   gs_reset - reset device or adapter.  The command passed in is a
   dummy (usually) used only to identify the device we wish
   to reset.

   The reset functionality here is as quirky as most of the
   other stuff in this driver.  We implement the reset function
   only for the ATAPI DVD-RW drive - for all other devices it's
   a no-op.  We locate whichever worker is currently holding
   the ATAPI bus, mark it aborted-with-timeout, and wake it up
   (if no worker holds the bus, nobody gets aborted).  We also
   force a hard reset on the ATAPI bus itself.  We treat
   device-only, bus, and host resets in the same fashion.
 */

static int gs_reset(Scsi_Cmnd * cmd, unsigned int reset_flags)
{
   struct Scsi_Host *instance;
   struct gs_hostdata *hostdata;
   struct list_head *this;
   struct gs_worker *worker;
   unsigned long iflags;
   unsigned short apdc;

   (void) reset_flags;
   printk(KERN_ERR "gs: in gs_reset()\n");

   if (cmd->target != GS_DEVICE_DRIVE) {
     printk(KERN_INFO "gs: reset function not implemented for device %d\n", cmd->target);
     return SCSI_RESET_SUCCESS;
   }

   instance = cmd->host;
   hostdata = (struct gs_hostdata *) instance->hostdata;

   write_lock_irqsave(&hostdata->queueLock, iflags);
   list_for_each(this,&hostdata->workers) {
     worker = (struct gs_worker *) this;
     if (worker->gsResourcesUsed & GS_RESOURCE_ATAPI) {
       printk(KERN_ERR "gs: killing worker %d during reset\n", worker->sn);
       worker->aborting = 1;
       worker->go = 1;
       gs_worker_disarm_timer(worker);
     }
   }
   write_unlock_irqrestore(&hostdata->queueLock, iflags);
   printk(KERN_ERR "gs: initiating hard reset of ATAPI bus\n");
   save_and_cli(iflags);
   apdc = GS_READ(GS_ATA_DMA_APDC);
   apdc &= ~0x04;
   GS_WRITE(GS_ATA_DMA_APDC, apdc);
   GS_WRITE8(GS_ATAPI_CONTROL_REGISTER, 0xC0);
   GS_WRITE8(GS_ATAPI_CONTROL_REGISTER, 0x80);
   hostdata->wasReset = 1;
   queue_task (&gs_task_struct, &tq_immediate);
   mark_bh (IMMEDIATE_BH);
   restore_flags(iflags);
   return SCSI_RESET_SUCCESS;
}

/**
   gs_info - get info on a controller.
 */

static const char *gs_info(struct Scsi_Host *instance)
{
  (void) instance;
  if (debug) printk(KERN_DEBUG "In gs_info()\n");
  return "GS controller";
}

/**
   gs_proc_info - get info on a controller.
 */

#define PRINTP(x) len += sprintf(buffer+len, x)
#define ANDP ,

static int gs_proc_info(char *buffer, char **start, off_t offset,
			int length, int hostno, int inout)
{
  int len = 0;
  struct Scsi_Host *scsi_ptr;
  struct gs_hostdata *hostdata;
  unsigned long flags;
  (void) start;
  (void) offset;
  (void) length;
  (void) hostno;
  (void) inout;
  if (debug) printk(KERN_DEBUG "In gs_proc_info()\n");
  save_and_cli(flags);
  for (scsi_ptr = first_instance; scsi_ptr; scsi_ptr = scsi_ptr->next) {
    if (scsi_ptr->host_no == hostno) {
      break;
    }
  }
  hostdata = (struct gs_hostdata *) scsi_ptr->hostdata;
  PRINTP("SCSI host number %d : %s\n" ANDP scsi_ptr->host_no ANDP scsi_ptr->hostt->name);
  PRINTP("Bridge : %s\n" ANDP hostdata->pldLoaded ? "UP" : "DOWN");
  PRINTP("ATAPI : %s\n" ANDP hostdata->atapiOK ? "ATTACHED" : "DETACHED");
  PRINTP("SDRAM : %d\n" ANDP gs_probed_memsize);

  *start = buffer + offset;
  len -= offset;
  if (len > length)
    len = length;
  restore_flags(flags);
  return len;
}


/**
 *	gs_queue_command 		-	queue a command
 *	@cmd: SCSI command
 *	@done: completion handler
 *
 *      cmd is added to the per instance issue_queue, with minor 
 *      twiddling done to the host specific fields of cmd.  If the 
 *      main coroutine is not running, it is restarted.
 *
 *	Locks: io_request lock held by caller. Called functions drop and
 *	retake this lock. Called functions take dma lock.
 */

int gs_queue_command(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *)) {
	struct Scsi_Host *instance = cmd->host;
	struct gs_hostdata *hostdata = (struct gs_hostdata *) instance->hostdata;
	Scsi_Cmnd *tmp;
	unsigned long iflags;


	/* 
	 * We use the host_scribble field as a pointer to the next command  
	 * in a queue 
	 */

	cmd->host_scribble = NULL;
	cmd->scsi_done = done;
	cmd->result = 0;

	if (debug) printk(KERN_DEBUG "gs: queue_command %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n",
	 cmd->cmnd[0], cmd->cmnd[1], cmd->cmnd[2], cmd->cmnd[3], 
	 cmd->cmnd[4], cmd->cmnd[5], cmd->cmnd[6], cmd->cmnd[7], 
	 cmd->cmnd[8], cmd->cmnd[9], cmd->cmnd[10], cmd->cmnd[11]);

	/* 
	 * Insert the cmd into the issue queue. Note that REQUEST SENSE 
	 * commands are added to the head of the queue since any command will
	 * clear the contingent allegiance condition that exists and the 
	 * sense data is only guaranteed to be valid while the condition exists.
	 */

	write_lock_irqsave(&hostdata->queueLock, iflags);

	if (!(hostdata->issue_queue) || (cmd->cmnd[0] == REQUEST_SENSE)) {
		LIST(cmd, hostdata->issue_queue);
		cmd->host_scribble = (unsigned char *) hostdata->issue_queue;
		hostdata->issue_queue = cmd;
	} else {
		for (tmp = (Scsi_Cmnd *) hostdata->issue_queue; tmp->host_scribble; tmp = (Scsi_Cmnd *) tmp->host_scribble);
		LIST(cmd, tmp);
		tmp->host_scribble = (unsigned char *) cmd;
	}

	write_unlock_irqrestore(&hostdata->queueLock, iflags);

	/*
	  Kick the runner task awake if it's not already so...
	*/
	queue_task (&gs_task_struct, &tq_immediate);
	mark_bh (IMMEDIATE_BH);

	return 0;
}

/*
 * Ioctl handler
 */

static int gs_ioctl(Scsi_Device *dev, int cmd, void *arg)
{
   struct Scsi_Host *host = dev->host;
   unsigned int id = dev->id;
   if (debug) printk(KERN_DEBUG "In gs_ioctl()\n");
   if (ioctlHandlers[id]) {
     return (*ioctlHandlers[id])(host, dev, cmd, arg);
   } 
   return -EINVAL;
}

static void gsWorkerDoneCallback (struct gs_worker *worker)
{
  *((unsigned long *) worker->endactionToken) = 1;
}

static void gsXmitDoneCallback (struct gs_worker *worker)
{
  void (*callback)(int);
  if (worker->endactionToken) {
    callback = (void (*) (int)) worker->endactionToken;
    (*callback)(worker->aborting ? XMIT_GS_SPI_FAILURE : XMIT_GS_SPI_SUCCESS);
  }
}

static void gs_fl_burst(const unsigned short *array, unsigned byteCount)
{
  unsigned long flags;
  unsigned short cint, cctl1;
  int i;
  save_and_cli(flags);
  /* wait a while for transceiver to finish previous operation */
  i = 0;
  do {
    cint = GS_READ(GS_FL_CINT);
  } while ((cint & 0x8) == 0 && ++i < 100000);
  cint  |= 0x0008;
  GS_WRITE(GS_FL_CINT, cint);
  udelay(10000);
  GS_WRITE_MULTI(GS_FL_CDATA, (byteCount + 1) / 2, array);
  GS_WRITE(GS_FL_CBYTNM, (unsigned short) byteCount - 1);
  GS_WRITE(GS_FL_CCTL2, 0x00C2); /* enable clock, LSB first, rising edge */
  GS_WRITE(GS_FL_CLATCH, 0x0001); /* type 1 latching, H>L>H, enable */
  GS_WRITE(GS_FL_CCKSEL, 0x0005); /* serial clock is VCLK / 128 */
  GS_WRITE(GS_FL_CINTSL, 0x0000); /* internal setting */
  cint |= 0x0080;
  GS_WRITE(GS_FL_CINT, cint); /* mask interrupt */
  cctl1 = GS_READ(GS_FL_CCTL1);
  cctl1 |= 0x0080;
  GS_WRITE(GS_FL_CCTL1, cctl1); /* start the transceiver */
  /* wait a while for transceiver to finish this operation */
  i = 0;
  do {
    cint = GS_READ(GS_FL_CINT);
  } while ((cint & 0x8) == 0 && ++i < 100000);
  restore_flags(flags);
}

static int gs_reboot(struct notifier_block *self, unsigned long p1, void *p2)
{
  (void) self;
  (void) p1;
  (void) p2;
  int i;
  unsigned short cmd[15];
  printk(KERN_INFO "gs: clearing FL display\n");
  memset(cmd, 0, sizeof cmd);
  for (i = 0; i < 12; i+=4) {
    cmd[0] = (0xc0 + (i*3)) << 8;
    gs_fl_burst(cmd, sizeof cmd);
  }
  gs_fl_burst(RebootPattern1, sizeof RebootPattern1);
  gs_fl_burst(RebootPattern2, sizeof RebootPattern2);
  return 0;
}

#ifdef MODULE

static Scsi_Host_Template driver_template = GIANT_SLALOM;

#include <linux/module.h>
#include "scsi_module.c"

#endif

EXPORT_SYMBOL(xmit_gs_spi);
EXPORT_SYMBOL(GSReadBridge);
EXPORT_SYMBOL(GSWriteBridge);
EXPORT_SYMBOL(GSWriteBridgeMasked);
