/* aha152x.c -- Adaptec AHA-152x driver
 * Author: Juergen E. Fischer, fischer@server.et-inf.fho-emden.de
 * Copyright 1993, 1994 Juergen E. Fischer
 *
 *
 * This driver is based on
 *   fdomain.c -- Future Domain TMC-16x0 driver
 * which is
 *   Copyright 1992, 1993 Rickard E. Faith (faith@cs.unc.edu)
 *

 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2, or (at your option) any
 * later version.

 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 
 *
 * $Id: aha152x.c,v 1.9 1995/03/18 09:20:24 root Exp root $
 *

 * $Log: aha152x.c,v $
 * Revision 1.9  1995/03/18  09:20:24  root
 * - patches for PCMCIA and modules
 *
 * Revision 1.8  1995/01/21  22:07:19  root
 * - snarf_region => request_region
 * - aha152x_intr interface change
 *
 * Revision 1.7  1995/01/02  23:19:36  root
 * - updated COMMAND_SIZE to cmd_len
 * - changed sti() to restore_flags()
 * - fixed some #ifdef which generated warnings
 *
 * Revision 1.6  1994/11/24  20:35:27  root
 * - problem with odd number of bytes in fifo fixed
 *
 * Revision 1.5  1994/10/30  14:39:56  root
 * - abort code fixed
 * - debugging improved
 *
 * Revision 1.4  1994/09/12  11:33:01  root
 * - irqaction to request_irq
 * - abortion updated
 *
 * Revision 1.3  1994/08/04  13:53:05  root
 * - updates for mid-level-driver changes
 * - accept unexpected BUSFREE phase as error condition
 * - parity check now configurable
 *
 * Revision 1.2  1994/07/03  12:56:36  root
 * - cleaned up debugging code
 * - more tweaking on reset delays
 * - updated abort/reset code (pretty untested...)
 *
 * Revision 1.1  1994/05/28  21:18:49  root
 * - update for mid-level interface change (abort-reset)
 * - delays after resets adjusted for some slow devices
 *
 * Revision 1.0  1994/03/25  12:52:00  root
 * - Fixed "more data than expected" problem
 * - added new BIOS signatures
 *
 * Revision 0.102  1994/01/31  20:44:12  root
 * - minor changes in insw/outsw handling
 *
 * Revision 0.101  1993/12/13  01:16:27  root
 * - fixed STATUS phase (non-GOOD stati were dropped sometimes;
 *   fixes problems with CD-ROM sector size detection & media change)
 *
 * Revision 0.100  1993/12/10  16:58:47  root
 * - fix for unsuccessful selections in case of non-continuous id assignments
 *   on the scsi bus.
 *
 * Revision 0.99  1993/10/24  16:19:59  root
 * - fixed DATA IN (rare read errors gone)
 *
 * Revision 0.98  1993/10/17  12:54:44  root
 * - fixed some recent fixes (shame on me)
 * - moved initialization of scratch area to aha152x_queue
 *
 * Revision 0.97  1993/10/09  18:53:53  root
 * - DATA IN fixed. Rarely left data in the fifo.
 *
 * Revision 0.96  1993/10/03  00:53:59  root
 * - minor changes on DATA IN
 *
 * Revision 0.95  1993/09/24  10:36:01  root
 * - change handling of MSGI after reselection
 * - fixed sti/cli
 * - minor changes
 *
 * Revision 0.94  1993/09/18  14:08:22  root
 * - fixed bug in multiple outstanding command code
 * - changed detection
 * - support for kernel command line configuration
 * - reset corrected
 * - changed message handling
 *
 * Revision 0.93  1993/09/15  20:41:19  root
 * - fixed bugs with multiple outstanding commands
 *
 * Revision 0.92  1993/09/13  02:46:33  root
 * - multiple outstanding commands work (no problems with IBM drive)
 *
 * Revision 0.91  1993/09/12  20:51:46  root
 * added multiple outstanding commands
 * (some problem with this $%&? IBM device remain)
 *
 * Revision 0.9  1993/09/12  11:11:22  root
 * - corrected auto-configuration
 * - changed the auto-configuration (added some '#define's)
 * - added support for dis-/reconnection
 *
 * Revision 0.8  1993/09/06  23:09:39  root
 * - added support for the drive activity light
 * - minor changes
 *
 * Revision 0.7  1993/09/05  14:30:15  root
 * - improved phase detection
 * - now using the new snarf_region code of 0.99pl13
 *
 * Revision 0.6  1993/09/02  11:01:38  root
 * first public release; added some signatures and biosparam()
 *
 * Revision 0.5  1993/08/30  10:23:30  root
 * fixed timing problems with my IBM drive
 *
 * Revision 0.4  1993/08/29  14:06:52  root
 * fixed some problems with timeouts due incomplete commands
 *
 * Revision 0.3  1993/08/28  15:55:03  root
 * writing data works too.  mounted and worked on a dos partition
 *
 * Revision 0.2  1993/08/27  22:42:07  root
 * reading data works.  Mounted a msdos partition.
 *
 * Revision 0.1  1993/08/25  13:38:30  root
 * first "damn thing doesn't work" version
 *
 * Revision 0.0  1993/08/14  19:54:25  root
 * empty function bodies; detect() works.
 *

 **************************************************************************


 
 DESCRIPTION:

 This is the Linux low-level SCSI driver for Adaptec AHA-1520/1522
 SCSI host adapters.


 PER-DEFINE CONFIGURABLE OPTIONS:

 AUTOCONF       : use configuration the controller reports (only 152x)
 IRQ            : override interrupt channel (9,10,11 or 12) (default 11)
 SCSI_ID        : override scsiid of AIC-6260 (0-7) (default 7)
 RECONNECT      : override target dis-/reconnection/multiple outstanding commands (default on)
 PARITY		: override parity check (default on)
 SKIP_BIOSTEST  : Don't test for BIOS signature (AHA-1510 or disabled BIOS)
 PORTBASE       : Force port base. Don't try to probe


 LILO COMMAND LINE OPTIONS:

 aha152x=<PORTBASE>[,<IRQ>[,<SCSI-ID>[,<RECONNECT>[,<PARITY>]]]]

 The normal configuration can be overridden by specifying a command line.
 When you do this, the BIOS test is skipped. Entered values have to be
 valid (known). Don't use values that aren't support under normal operation.
 If you think that you need other values: contact me.


 REFERENCES USED:

 "AIC-6260 SCSI Chip Specification", Adaptec Corporation.

 "SCSI COMPUTER SYSTEM INTERFACE - 2 (SCSI-2)", X3T9.2/86-109 rev. 10h

 "Writing a SCSI device driver for Linux", Rik Faith (faith@cs.unc.edu)

 "Kernel Hacker's Guide", Michael K. Johnson (johnsonm@sunsite.unc.edu)

 "Adaptec 1520/1522 User's Guide", Adaptec Corporation.
 
 Michael K. Johnson (johnsonm@sunsite.unc.edu)

 Drew Eckhardt (drew@cs.colorado.edu)

 Eric Youngdale (ericy@cais.com) 

 special thanks to Eric Youngdale for the free(!) supplying the
 documentation on the chip.

 **************************************************************************/

#ifdef MODULE
#include <linux/config.h>
#include <linux/module.h>
#endif

#include <linux/sched.h>
#include <asm/io.h>
#include "../block/blk.h"
#include "scsi.h"
#include "sd.h"
#include "hosts.h"
#include "constants.h"
#include <asm/system.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/wait.h>
#include <linux/ioport.h>

#include "aha152x.h"

/* DEFINES */

/* For PCMCIA cards, always use AUTOCONF */
#if defined(PCMCIA) || defined(MODULE)
#define AUTOCONF
#endif

/* If auto configuration is disabled, IRQ, SCSI_ID and RECONNECT have to
   be predefined */
#if !defined(AUTOCONF)
#if !defined(IRQ)
#error undefined IRQ; define AUTOCONF or IRQ
#endif
#if !defined(SCSI_ID)
#error undefined SCSI_ID; define AUTOCONF or SCSI_ID
#endif
#if !defined(RECONNECT)
#error undefined RECONNECT; define AUTOCONF or RECONNECT
#endif
#if !defined(PARITY)
#error undefined PARITY; define AUTOCONF or PARITY
#endif
#endif

/* I use this when I'm looking for weird bugs */
#define DEBUG_TIMING 

#if defined(DEBUG_AHA152X)

#undef  SKIP_PORTS              /* don't display ports */

#undef  DEBUG_QUEUE             /* debug queue() */
#undef  DEBUG_RESET             /* debug reset() */
#undef  DEBUG_INTR              /* debug intr() */
#undef  DEBUG_SELECTION         /* debug selection part in intr() */
#undef  DEBUG_MSGO              /* debug message out phase in intr() */
#undef  DEBUG_MSGI              /* debug message in phase in intr() */
#undef  DEBUG_STATUS            /* debug status phase in intr() */
#undef  DEBUG_CMD               /* debug command phase in intr() */
#undef  DEBUG_DATAI             /* debug data in phase in intr() */
#undef  DEBUG_DATAO             /* debug data out phase in intr() */
#undef  DEBUG_ABORT             /* debug abort() */
#undef  DEBUG_DONE              /* debug done() */
#undef  DEBUG_BIOSPARAM         /* debug biosparam() */

#undef  DEBUG_RACE              /* debug race conditions */
#undef  DEBUG_PHASES            /* debug phases (useful to trace) */
#undef  DEBUG_QUEUES            /* debug reselection */

/* recently used for debugging */
#if 0
#endif

#define DEBUG_PHASES
#define DEBUG_RESET
#define DEBUG_ABORT

#define DEBUG_DEFAULT (debug_reset|debug_abort)

#endif

/* END OF DEFINES */

/* some additional "phases" for getphase() */
#define P_BUSFREE  1
#define P_PARITY   2

static int port_base      = 0;
static int this_host      = 0;
static int can_disconnect = 0;
static int can_doparity   = 0;
static int commands       = 0;

#ifdef DEBUG_AHA152X
unsigned int aha152x_debug  = DEBUG_DEFAULT;
#endif

/* set by aha152x_setup according to the command line */
static int setup_called    = 0;
static int setup_portbase  = 0;
static int setup_irq       = 0;
static int setup_scsiid    = 0;
static int setup_reconnect = 0;
static int setup_doparity  = 0;

#ifdef DEBUG_AHA152X
static int setup_debug     = 0;
#endif

static char *setup_str = (char *)NULL;

enum {
   not_issued   = 0x01,
   in_selection = 0x02,
   disconnected = 0x04,
   aborted      = 0x08,
   sent_ident   = 0x10,
   in_other     = 0x20,
};
 
/*
 * Command queues:
 * issue_SC        : commands that are queued to be issued 
 * current_SC      : command that's currently using the bus
 * disconnected_SC : commands that that have been disconnected 
 */
static Scsi_Cmnd            *issue_SC        = NULL;
static Scsi_Cmnd            *current_SC      = NULL;
static Scsi_Cmnd            *disconnected_SC = NULL;

static int aborting=0, abortion_complete=0, abort_result;

void aha152x_intr( int irq, struct pt_regs * );
void aha152x_done( int error );
void aha152x_setup( char *str, int *ints );

static void aha152x_reset_ports(void);
static void aha152x_panic(char *msg);

static void disp_ports(void);
static void show_command(Scsi_Cmnd *ptr);
static void show_queues(void);
static void disp_enintr(void);

#if defined(DEBUG_RACE)
static void enter_driver(const char *);
static void leave_driver(const char *);
#endif

/* possible i/o addresses for the AIC-6260 */
static unsigned short ports[] =
{
  0x340,      /* default first */
  0x140
};
#define PORT_COUNT (sizeof( ports ) / sizeof( unsigned short ))

/* possible interrupt channels */
static unsigned short irqs[] = { 9, 10, 11, 12, 0 };

#if !defined(SKIP_BIOSTEST)
/* possible locations for the Adaptec BIOS */
static void *addresses[] =
{
  (void *) 0xdc000,   /* default first */
  (void *) 0xc8000,
  (void *) 0xcc000,
  (void *) 0xd0000,
  (void *) 0xd4000,
  (void *) 0xd8000,
  (void *) 0xe0000,
  (void *) 0xf0000,
  (void *) 0xeb800,   /* VTech Platinum SMP */
};
#define ADDRESS_COUNT (sizeof( addresses ) / sizeof( void * ))

/* signatures for various AIC-6[23]60 based controllers.
   The point in detecting signatures is to avoid useless
   and maybe harmful probes on ports. I'm not sure that
   all listed boards pass auto-configuration. For those
   which fail the BIOS signature is obsolete, because
   user intervention to supply the configuration is 
   needed anyway. */
static struct signature {
  char *signature;
  int  sig_offset;
  int  sig_length;
} signatures[] =
{
  { "Adaptec AHA-1520 BIOS",      0x102e, 21 },  /* Adaptec 152x */
  { "Adaptec ASW-B626 BIOS",      0x1029, 21 },  /* on-board controller */
  { "Adaptec BIOS: ASW-B626",       0x0f, 22 },  /* on-board controller */
  { "Adaptec ASW-B626 S2",        0x2e6c, 19 },  /* on-board controller */
  { "Adaptec BIOS:AIC-6360",         0xc, 21 },  /* on-board controller */
  { "ScsiPro SP-360 BIOS",        0x2873, 19 },  /* ScsiPro-Controller with AIC-6360 */
  { "GA-400 LOCAL BUS SCSI BIOS", 0x102e, 26 },  /* Gigabyte Local-Bus-SCSI */
};
#define SIGNATURE_COUNT (sizeof( signatures ) / sizeof( struct signature ))
#endif


static void do_pause( unsigned amount ) /* Pause for amount*10 milliseconds */
{
   unsigned long the_time = jiffies + amount; /* 0.01 seconds per jiffy */

   while (jiffies < the_time)
     ;
}

/*
 *  queue services:
 */
static inline void append_SC( Scsi_Cmnd **SC, Scsi_Cmnd *new_SC)
{
  Scsi_Cmnd *end;

  new_SC->host_scribble = (unsigned char *) NULL;
  if(!*SC)
    *SC=new_SC;
  else
    {
      for( end=*SC;
           end->host_scribble;
           end = (Scsi_Cmnd *) end->host_scribble )
        ;
      end->host_scribble = (unsigned char *) new_SC;
    }
}

static inline Scsi_Cmnd *remove_first_SC( Scsi_Cmnd **SC )
{
  Scsi_Cmnd *ptr;

  ptr=*SC;
  if(ptr)
    *SC= (Scsi_Cmnd *) (*SC)->host_scribble;
  return ptr;
}

static inline Scsi_Cmnd *remove_SC( Scsi_Cmnd **SC, int target, int lun )
{
  Scsi_Cmnd *ptr, *prev;

  for( ptr=*SC, prev=NULL;
       ptr && ((ptr->target!=target) || (ptr->lun!=lun));
       prev = ptr, ptr = (Scsi_Cmnd *) ptr->host_scribble )
    ;

  if(ptr)
    if(prev)
      prev->host_scribble = ptr->host_scribble;
    else
      *SC= (Scsi_Cmnd *) ptr->host_scribble;
  return ptr;
}

/*
 * read inbound byte and wait for ACK to get low
 */
static void make_acklow(void)
{
  SETPORT( SXFRCTL0, CH1|SPIOEN );
  GETPORT(SCSIDAT);
  SETPORT( SXFRCTL0, CH1 );

  while( TESTHI( SCSISIG, ACKI ) )
    ;
}

/*
 * detect current phase more reliable:
 * phase is valid, when the target asserts REQ after we've deasserted ACK.
 *
 * return value is a valid phase or an error code.
 *
 * errorcodes:
 *   P_BUSFREE   BUS FREE phase detected
 *   P_PARITY    parity error in DATA phase
 */
static int getphase(void)
{
  int phase, sstat1;
  
  while( 1 )
    {
      do
        {
          while( !( ( sstat1 = GETPORT( SSTAT1 ) ) & (BUSFREE|SCSIRSTI|REQINIT ) ) )
            ;
          if( sstat1 & BUSFREE )
            return P_BUSFREE;
          if( sstat1 & SCSIRSTI )
            {
              /* IBM drive responds with RSTI to RSTO */
              printk("aha152x: RESET IN\n");
              SETPORT( SSTAT1, SCSIRSTI );
            }
        }
      while( TESTHI( SCSISIG, ACKI ) || TESTLO( SSTAT1, REQINIT ) );

      SETPORT( SSTAT1, CLRSCSIPERR );
  
      phase = GETPORT( SCSISIG ) & P_MASK ;

      if( TESTHI( SSTAT1, SCSIPERR ) )
        {
          if( (phase & (CDO|MSGO))==0 )                         /* DATA phase */
            return P_PARITY;

          make_acklow();
        }
      else
        return phase;
    }
}

/* called from init/main.c */
void aha152x_setup( char *str, int *ints)
{
  if(setup_called)
    panic("aha152x: aha152x_setup called twice.\n");

  setup_called=ints[0];
  setup_str=str;

  setup_portbase  = ints[0] >= 1 ? ints[1] : 0x340;
  setup_irq       = ints[0] >= 2 ? ints[2] : 11;
  setup_scsiid	  = ints[0] >= 3 ? ints[3] : 7;
  setup_reconnect = ints[0] >= 4 ? ints[4] : 1;
  setup_doparity  = ints[0] >= 5 ? ints[5] : 1;
#ifdef DEBUG_AHA152X
  setup_debug     = ints[0] >= 6 ? ints[6] : DEBUG_DEFAULT;
#endif
}

/*
   Test, if port_base is valid.
 */
static int aha152x_porttest(int port_base)
{
  int i;

  if(check_region(port_base, 0x20))
    return 0;

  SETPORT( DMACNTRL1, 0 );          /* reset stack pointer */
  for(i=0; i<16; i++)
    SETPORT( STACK, i );

  SETPORT( DMACNTRL1, 0 );          /* reset stack pointer */
  for(i=0; i<16 && GETPORT(STACK)==i; i++)
    ;

  return(i==16);
}

int aha152x_detect(Scsi_Host_Template * tpnt)
{
  int                 i, ok;
#if defined(AUTOCONF)
  aha152x_config      conf;
#endif
  int                 interrupt_level;
  struct Scsi_Host    *hreg;
  
  if(setup_called)
    {
      printk("aha152x: processing commandline: ");
   
#ifdef DEBUG_AHA152X
      if(setup_called>6)
#else
      if(setup_called>5)
#endif
        {
          printk("\naha152x: %s\n", setup_str );
#ifdef DEBUG_AHA152X
          printk("aha152x: usage: aha152x=<PORTBASE>[,<IRQ>[,<SCSI ID>[,<RECONNECT>[,<PARITY>[,<DEBUG>]]]]]\n");
#else
          printk("aha152x: usage: aha152x=<PORTBASE>[,<IRQ>[,<SCSI ID>[,<RECONNECT>[,<PARITY>]]]]\n");
#endif
          panic("aha152x panics in line %d", __LINE__);
        }

      port_base       = setup_portbase;
      interrupt_level = setup_irq;
      this_host       = setup_scsiid;
      can_disconnect  = setup_reconnect;
      can_doparity    = setup_doparity;
#ifdef DEBUG_AHA152X
      aha152x_debug   = setup_debug;
#endif

#ifndef PCMCIA
      for( i=0; i<PORT_COUNT && (port_base != ports[i]); i++)
        ;

      if(i==PORT_COUNT)
        {
          printk("unknown portbase 0x%03x\n", port_base);
          panic("aha152x panics in line %d", __LINE__);
        }
#endif

      if(!aha152x_porttest(port_base))
        {
          printk("portbase 0x%03x fails probe\n", port_base);
#ifdef PCMCIA
          return 0;
#else
          panic("aha152x panics in line %d", __LINE__);
#endif
        }

#ifndef PCMCIA
      i=0;
      while(irqs[i] && (interrupt_level!=irqs[i]))
        i++;
      if(!irqs[i])
        {
          printk("illegal IRQ %d\n", interrupt_level);
          panic("aha152x panics in line %d", __LINE__);
        }
#endif
      
      if( (this_host < 0) || (this_host > 7) )
        {
          printk("illegal SCSI ID %d\n", this_host);
          panic("aha152x panics in line %d", __LINE__);
        }

      if( (can_disconnect < 0) || (can_disconnect > 1) )
        {
          printk("reconnect %d should be 0 or 1\n", can_disconnect);
          panic("aha152x panics in line %d", __LINE__);
        }

      if( (can_doparity < 0) || (can_doparity > 1) )
        {
          printk("parity %d should be 0 or 1\n", can_doparity);
          panic("aha152x panics in line %d", __LINE__);
        }
      printk("ok\n");
    }
  else
    {
#if !defined(SKIP_BIOSTEST)
      int j;

      ok=0;
      for( i=0; i < ADDRESS_COUNT && !ok; i++)
        for( j=0; (j < SIGNATURE_COUNT) && !ok; j++)
          ok=!memcmp((void *) addresses[i]+signatures[j].sig_offset,
                     (void *) signatures[j].signature,
                     (int) signatures[j].sig_length);

      if(!ok)
        return 0;

      printk("aha152x: BIOS test: passed, ");
#else
      printk("aha152x: ");
#endif /* !SKIP_BIOSTEST */
 
#if !defined(PORTBASE)
      printk("porttest: ");
      for( i=0; i<PORT_COUNT && !aha152x_porttest(ports[i]); i++)
        ;

      if(i==PORT_COUNT)
        {
          printk("failed\n");
          return 0;
        }
      else
        port_base=ports[i];
      printk("ok, ");
#else
      port_base=PORTBASE;
#endif /* !PORTBASE */

#if defined(AUTOCONF)

      conf.cf_port = (GETPORT(PORTA)<<8) + GETPORT(PORTB);

      interrupt_level = irqs[conf.cf_irq];
      this_host       = conf.cf_id;
      can_disconnect  = conf.cf_tardisc;
      can_doparity    = !conf.cf_parity;

      printk("auto configuration: ok, ");

#endif /* AUTOCONF */

#if defined(IRQ)
      interrupt_level = IRQ; 
#endif

#if defined(SCSI_ID)
      this_host = SCSI_ID;
#endif

#if defined(RECONNECT)
      can_disconnect=RECONNECT;
#endif

#if defined(PARITY)
      can_doparity=PARITY;
#endif
    }

  printk("detection complete\n");
 
  ok = request_irq(interrupt_level, aha152x_intr, SA_INTERRUPT, "aha152x");
  
  if(ok<0)
    {
      if(ok == -EINVAL)
        {
           printk("aha152x: bad IRQ %d.\n", interrupt_level);
           printk("         Contact author.\n");
        }
      else
        if( ok == -EBUSY)
          printk( "aha152x: IRQ %d already in use. Configure another.\n",
                  interrupt_level);
        else
          {
            printk( "\naha152x: Unexpected error code on requesting IRQ %d.\n",
                    interrupt_level);
            printk("         Contact author.\n");
          }
      panic("aha152x: driver needs an IRQ.\n");
    }

  SETPORT( SCSIID, this_host << 4 );
  tpnt->this_id=this_host;
  
  if(can_disconnect)
    tpnt->can_queue=AHA152X_MAXQUEUE;

  /* RESET OUT */
  SETBITS(SCSISEQ, SCSIRSTO );
  do_pause(30);
  CLRBITS(SCSISEQ, SCSIRSTO );
  do_pause(60);

  aha152x_reset(NULL);

  printk("aha152x: vital data: PORTBASE=0x%03x, IRQ=%d, SCSI ID=%d, reconnect=%s, parity=%s\n",
         port_base,
         interrupt_level,
         this_host,
         can_disconnect ? "enabled" : "disabled",
         can_doparity ? "enabled" : "disabled");

  request_region(port_base, 0x20, "aha152x");        /* Register */

  hreg = scsi_register(tpnt, 0);
  hreg->io_port = port_base;
  hreg->n_io_port = 0x20;
  hreg->irq = interrupt_level;
  
  /* not expecting any interrupts */
  SETPORT(SIMODE0, 0);
  SETPORT(SIMODE1, 0);

  SETBITS( DMACNTRL0, INTEN);
  return 1;
}

/* 
 *  Queue a command and setup interrupts for a free bus.
 */
int aha152x_queue( Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
{
  unsigned long flags;

#if defined(DEBUG_RACE)
  enter_driver("queue");
#else
#if defined(DEBUG_QUEUE)
  if(aha152x_debug & debug_queue)
    printk("aha152x: queue(), ");
#endif
#endif

#if defined(DEBUG_QUEUE)
  if(aha152x_debug & debug_queue)
  {
    printk( "SCpnt (target = %d lun = %d cmnd = ", SCpnt->target, SCpnt->lun);
    print_command(SCpnt->cmnd);
    printk( ", cmd_len=%d, pieces = %d size = %u), ",
            SCpnt->cmd_len, SCpnt->use_sg, SCpnt->request_bufflen );
    disp_ports();
  }
#endif

  SCpnt->scsi_done =       done;

  /* setup scratch area
     SCp.ptr              : buffer pointer
     SCp.this_residual    : buffer length
     SCp.buffer           : next buffer
     SCp.buffers_residual : left buffers in list
     SCp.phase            : current state of the command */
  SCpnt->SCp.phase = not_issued;
  if (SCpnt->use_sg)
    {
      SCpnt->SCp.buffer           = (struct scatterlist *)SCpnt->request_buffer;
      SCpnt->SCp.ptr              = SCpnt->SCp.buffer->address;
      SCpnt->SCp.this_residual    = SCpnt->SCp.buffer->length;
      SCpnt->SCp.buffers_residual = SCpnt->use_sg - 1;
    }
  else
    {
      SCpnt->SCp.ptr              = (char *)SCpnt->request_buffer;
      SCpnt->SCp.this_residual    = SCpnt->request_bufflen;
      SCpnt->SCp.buffer           = NULL;
      SCpnt->SCp.buffers_residual = 0;
    }
          
  SCpnt->SCp.Status              = CHECK_CONDITION;
  SCpnt->SCp.Message             = 0;
  SCpnt->SCp.have_data_in        = 0;
  SCpnt->SCp.sent_command        = 0;

  /* Turn led on, when this is the first command. */
  save_flags(flags);
  cli();
  commands++;
  if(commands==1)
    SETPORT( PORTA, 1 );

#if defined(DEBUG_QUEUES)
  if(aha152x_debug & debug_queues)
    printk("i+ (%d), ", commands );
#endif
  append_SC( &issue_SC, SCpnt);
  
  /* Enable bus free interrupt, when we aren't currently on the bus */
  if(!current_SC)
    {
      SETPORT(SIMODE0, disconnected_SC ? ENSELDI : 0 );
      SETPORT(SIMODE1, issue_SC ? ENBUSFREE : 0);
    }
  restore_flags(flags);

#if defined(DEBUG_RACE)
  leave_driver("queue");
#endif

  return 0;
}

/*
 *  We only support command in interrupt-driven fashion
 */
int aha152x_command( Scsi_Cmnd *SCpnt )
{
  printk( "aha152x: interrupt driven driver; use aha152x_queue()\n" );
  return -1;
}

/*
 *  Abort a queued command
 *  (commands that are on the bus can't be aborted easily)
 */
int aha152x_abort( Scsi_Cmnd *SCpnt)
{
  unsigned long flags;
  Scsi_Cmnd *ptr, *prev;

  save_flags(flags);
  cli();

#if defined(DEBUG_ABORT)
  if(aha152x_debug & debug_abort)
  { 
    printk("aha152x: abort(), SCpnt=0x%08x, ", (unsigned int) SCpnt );

    show_queues();
  }
#endif

  /* look for command in issue queue */
  for( ptr=issue_SC, prev=NULL;
       ptr && ptr!=SCpnt;
       prev=ptr, ptr=(Scsi_Cmnd *) ptr->host_scribble)
    ;

  if(ptr)
    {
      /* dequeue */
      if(prev)
        prev->host_scribble = ptr->host_scribble;
      else
        issue_SC = (Scsi_Cmnd *) ptr->host_scribble;
      restore_flags(flags);

      ptr->host_scribble = NULL;
      ptr->result = DID_ABORT << 16;
      ptr->done(ptr);
      return SCSI_ABORT_SUCCESS;
    }

  /* if the bus is busy or a command is currently processed,
     we can't do anything more */
  if ( TESTLO(SSTAT1, BUSFREE) || (current_SC && current_SC!=SCpnt))
    {
      /* fail abortion, if bus is busy */

      if(!current_SC)
        printk("bus busy w/o current command, ");
 
      restore_flags(flags);
      return SCSI_ABORT_BUSY;
    }

  /* bus is free */

  if(current_SC)
  { 
    /* target entered bus free before COMMAND COMPLETE, nothing to abort */
    restore_flags(flags);
    current_SC->result = DID_ERROR << 16;
    current_SC->done(current_SC);
    current_SC = (Scsi_Cmnd *) NULL;
    return SCSI_ABORT_SUCCESS;
  }

  /* look for command in disconnected queue */
  for( ptr=disconnected_SC, prev=NULL;
       ptr && ptr!=SCpnt;
       prev=ptr, ptr=(Scsi_Cmnd *) ptr->host_scribble)
    ;

  if(ptr)
    if(!aborting)
      {
        /* dequeue */
        if(prev)
          prev->host_scribble = ptr->host_scribble;
        else
          disconnected_SC = (Scsi_Cmnd *) ptr->host_scribble;
  
        /* set command current and initiate selection,
           let the interrupt routine take care of the abortion */
        current_SC     = ptr;
        ptr->SCp.phase = in_selection|aborted;
        SETPORT( SCSIID, (this_host << OID_) | current_SC->target );
  
        /* enable interrupts for SELECTION OUT DONE and SELECTION TIME OUT */
        SETPORT( SIMODE0, ENSELDO | (disconnected_SC ? ENSELDI : 0) );
        SETPORT( SIMODE1, ENSELTIMO );
  
        /* Enable SELECTION OUT sequence */
        SETBITS(SCSISEQ, ENSELO | ENAUTOATNO );
  
        SETBITS( DMACNTRL0, INTEN );
        abort_result=SCSI_ABORT_SUCCESS;
        aborting++;
        abortion_complete=0;

        sti();  /* Hi Eric, guess what ;-) */
  
        /* sleep until the abortion is complete */
        while(!abortion_complete)
	  barrier();
        aborting=0;
        return abort_result;
      }
    else
      {
        /* we're already aborting a command */
        restore_flags(flags);
        return SCSI_ABORT_BUSY;
      }

  /* command wasn't found */
  printk("command not found\n");
  restore_flags(flags);
  return SCSI_ABORT_NOT_RUNNING;
}

/*
 *  Restore default values to the AIC-6260 registers and reset the fifos
 */
static void aha152x_reset_ports(void)
{
  /* disable interrupts */
  SETPORT(DMACNTRL0, RSTFIFO);

  SETPORT(SCSISEQ, 0);

  SETPORT(SXFRCTL1, 0);
  SETPORT( SCSISIG, 0);
  SETPORT(SCSIRATE, 0);

  /* clear all interrupt conditions */
  SETPORT(SSTAT0, 0x7f);
  SETPORT(SSTAT1, 0xef);

  SETPORT(SSTAT4, SYNCERR|FWERR|FRERR);

  SETPORT(DMACNTRL0, 0);
  SETPORT(DMACNTRL1, 0);

  SETPORT(BRSTCNTRL, 0xf1);

  /* clear SCSI fifo and transfer count */
  SETPORT(SXFRCTL0, CH1|CLRCH1|CLRSTCNT);
  SETPORT(SXFRCTL0, CH1);

  /* enable interrupts */
  SETPORT(SIMODE0, disconnected_SC ? ENSELDI : 0 );
  SETPORT(SIMODE1, issue_SC ? ENBUSFREE : 0);
}

/*
 *  Reset registers, reset a hanging bus and
 *  kill active and disconnected commands for target w/o soft reset
 */
int aha152x_reset(Scsi_Cmnd * __unused)
{
  unsigned long flags;
  Scsi_Cmnd *ptr, *prev, *next;

  aha152x_reset_ports();

  /* Reset, if bus hangs */
  if( TESTLO( SSTAT1, BUSFREE ) )
    {
       CLRBITS( DMACNTRL0, INTEN );

#if defined(DEBUG_RESET)
  if(aha152x_debug & debug_reset)
  {
       printk("aha152x: reset(), bus not free: SCSI RESET OUT\n");
       show_queues();
  }
#endif

       if(current_SC && !current_SC->device->soft_reset)
         {
           current_SC->host_scribble = NULL;
           current_SC->result = DID_RESET << 16;
           current_SC->done(current_SC);
           current_SC=NULL;
         }

       save_flags(flags);
       cli();
       prev=NULL; ptr=disconnected_SC;
       while(ptr)
	 {
	   if(!ptr->device->soft_reset)
	     {
	       if(prev)
		 prev->host_scribble = ptr->host_scribble;
	       else
		 disconnected_SC = (Scsi_Cmnd *) ptr->host_scribble;

	       next = (Scsi_Cmnd *) ptr->host_scribble;
  
	       ptr->host_scribble = NULL;
	       ptr->result        = DID_RESET << 16;
	       ptr->done(ptr);
  
	       ptr = next; 
	     }
	   else
	     {
	       prev=ptr;
	       ptr = (Scsi_Cmnd *) ptr->host_scribble;
	     }
	 }
       restore_flags(flags);

#if defined( DEBUG_RESET )
       if(aha152x_debug & debug_reset)
       {
         printk("commands on targets w/ soft-resets:\n");
         show_queues();
       }
#endif

       /* RESET OUT */
       SETPORT(SCSISEQ, SCSIRSTO);
       do_pause(30);
       SETPORT(SCSISEQ, 0);
       do_pause(60);

       SETPORT(SIMODE0, disconnected_SC ? ENSELDI : 0 );
       SETPORT(SIMODE1, issue_SC ? ENBUSFREE : 0);

       SETPORT( DMACNTRL0, INTEN );
    }

  return SCSI_RESET_SUCCESS;
}

/*
 * Return the "logical geometry"
 */
int aha152x_biosparam(Scsi_Disk * disk, int dev, int *info_array )
{
  int size = disk->capacity;

#if defined(DEBUG_BIOSPARAM)
  if(aha152x_debug & debug_biosparam)
    printk("aha152x_biosparam: dev=%x, size=%d, ", dev, size);
#endif
  
/* I took this from other SCSI drivers, since it provides
   the correct data for my devices. */
  info_array[0]=64;
  info_array[1]=32;
  info_array[2]=size>>11;

#if defined(DEBUG_BIOSPARAM)
  if(aha152x_debug & debug_biosparam)
  {
    printk("bios geometry: head=%d, sec=%d, cyl=%d\n",
           info_array[0], info_array[1], info_array[2]);
    printk("WARNING: check, if the bios geometry is correct.\n");
  }
#endif

  return 0;
}

/*
 *  Internal done function
 */
void aha152x_done( int error )
{
  unsigned long flags;
  Scsi_Cmnd *done_SC;

#if defined(DEBUG_DONE)
  if(aha152x_debug & debug_done)
  {
    printk("\naha152x: done(), ");
    disp_ports();
  }
#endif

  if (current_SC)
    {
#if defined(DEBUG_DONE)
      if(aha152x_debug & debug_done)
        printk("done(%x), ", error);
#endif

      save_flags(flags);
      cli();

      done_SC = current_SC;
      current_SC = NULL;

      /* turn led off, when no commands are in the driver */
      commands--;
      if(!commands)
        SETPORT( PORTA, 0 );                                  /* turn led off */

#if defined(DEBUG_QUEUES)
      if(aha152x_debug & debug_queues) 
        printk("ok (%d), ", commands);
#endif
      restore_flags(flags);

      SETPORT(SIMODE0, disconnected_SC ? ENSELDI : 0 );
      SETPORT(SIMODE1, issue_SC ? ENBUSFREE : 0);

#if defined(DEBUG_PHASES)
      if(aha152x_debug & debug_phases)
        printk("BUS FREE loop, ");
#endif
      while( TESTLO( SSTAT1, BUSFREE ) )
        ;
#if defined(DEBUG_PHASES)
      if(aha152x_debug & debug_phases)
        printk("BUS FREE\n");
#endif

      done_SC->result = error;
      if(done_SC->scsi_done)
        {
#if defined(DEBUG_DONE)
          if(aha152x_debug & debug_done)
            printk("calling scsi_done, ");
#endif
          done_SC->scsi_done( done_SC );
#if defined(DEBUG_DONE)
          if(aha152x_debug & debug_done)
            printk("done returned, ");
#endif
        }
      else
        panic( "aha152x: current_SC->scsi_done() == NULL" );
    }
  else
    aha152x_panic( "done() called outside of command" );
}

/*
 * Interrupts handler (main routine of the driver)
 */
void aha152x_intr( int irqno, struct pt_regs * regs )
{
  unsigned int flags;
  int done=0, phase;

#if defined(DEBUG_RACE)
  enter_driver("intr");
#else
#if defined(DEBUG_INTR)
  if(aha152x_debug & debug_intr)
    printk("\naha152x: intr(), ");
#endif
#endif

  /* no more interrupts from the controller, while we busy.
     INTEN has to be restored, when we're ready to leave
     intr(). To avoid race conditions we have to return
     immediately afterwards. */
  CLRBITS( DMACNTRL0, INTEN);
  sti();  /* Yes, sti() really needs to be here */

  /* disconnected target is trying to reconnect.
     Only possible, if we have disconnected nexuses and
     nothing is occupying the bus.
  */
  if( TESTHI( SSTAT0, SELDI ) &&
      disconnected_SC &&
      ( !current_SC || ( current_SC->SCp.phase & in_selection ) )
    )
    {
      int identify_msg, target, i;

      /* Avoid conflicts when a target reconnects
         while we are trying to connect to another. */
      if(current_SC)
        {
#if defined(DEBUG_QUEUES)
	  if(aha152x_debug & debug_queues)
          printk("i+, ");
#endif
	  save_flags(flags);
          cli();
          append_SC( &issue_SC, current_SC);
          current_SC=NULL;
          restore_flags(flags);
        }

      /* disable sequences */
      SETPORT( SCSISEQ, 0 );
      SETPORT( SSTAT0, CLRSELDI );
      SETPORT( SSTAT1, CLRBUSFREE );

#if defined(DEBUG_QUEUES) || defined(DEBUG_PHASES)
      if(aha152x_debug & (debug_queues|debug_phases))
        printk("reselected, ");
#endif

      i = GETPORT(SELID) & ~(1 << this_host);
      target=0;
      if(i)
        for( ; (i & 1)==0; target++, i>>=1)
          ;
      else
        aha152x_panic("reconnecting target unknown");

#if defined(DEBUG_QUEUES)
      if(aha152x_debug & debug_queues)
        printk("SELID=%02x, target=%d, ", GETPORT(SELID), target );
#endif
      SETPORT( SCSIID, (this_host << OID_) | target );
      SETPORT( SCSISEQ, ENRESELI );

      if(TESTLO( SSTAT0, SELDI ))
        aha152x_panic("RESELI failed");

      SETPORT( SCSISIG, P_MSGI );

      /* Get identify message */
      if((i=getphase())!=P_MSGI)
        {
          printk("target doesn't enter MSGI to identify (phase=%02x)\n", i);
          aha152x_panic("unknown lun");
        }
      SETPORT( SCSISEQ, 0 );

      SETPORT( SXFRCTL0, CH1);

      identify_msg = GETPORT(SCSIBUS);

      if(!(identify_msg & IDENTIFY_BASE))
        {
          printk("target=%d, inbound message (%02x) != IDENTIFY\n",
                 target, identify_msg);
          aha152x_panic("unknown lun");
        }

      make_acklow();
      getphase();

#if defined(DEBUG_QUEUES)
      if(aha152x_debug & debug_queues)
        printk("identify=%02x, lun=%d, ", identify_msg, identify_msg & 0x3f );
#endif

      save_flags(flags);
      cli();

#if defined(DEBUG_QUEUES)
      if(aha152x_debug & debug_queues)
        printk("d-, ");
#endif
      current_SC = remove_SC( &disconnected_SC,
                              target,
                              identify_msg & 0x3f );

      if(!current_SC)
        {
          printk("lun=%d, ", identify_msg & 0x3f );
          aha152x_panic("no disconnected command for that lun");
        }

      current_SC->SCp.phase &= ~disconnected;
      restore_flags(flags);

      SETPORT( SIMODE0, 0 );
      SETPORT( SIMODE1, ENPHASEMIS|ENBUSFREE );
#if defined(DEBUG_RACE)
      leave_driver("(reselected) intr");
#endif
      SETBITS( DMACNTRL0, INTEN);
      return;
    }
  
  /* Check, if we aren't busy with a command */
  if(!current_SC)
    {
      /* bus is free to issue a queued command */
      if(TESTHI( SSTAT1, BUSFREE) && issue_SC)
        {
          save_flags(flags);
          cli();
#if defined(DEBUG_QUEUES)
          if(aha152x_debug & debug_queues)
            printk("i-, ");
#endif
          current_SC = remove_first_SC( &issue_SC );
          restore_flags(flags);

#if defined(DEBUG_INTR) || defined(DEBUG_SELECTION) || defined(DEBUG_PHASES)
	  if(aha152x_debug & (debug_intr|debug_selection|debug_phases))
            printk("issuing command, ");
#endif
          current_SC->SCp.phase = in_selection;

  #if defined(DEBUG_INTR) || defined(DEBUG_SELECTION) || defined(DEBUG_PHASES)
	  if(aha152x_debug & (debug_intr|debug_selection|debug_phases))
            printk("selecting %d, ", current_SC->target); 
  #endif
          SETPORT( SCSIID, (this_host << OID_) | current_SC->target );

          /* Enable interrupts for SELECTION OUT DONE and SELECTION OUT INITIATED */
          SETPORT( SXFRCTL1, can_doparity ? (ENSPCHK|ENSTIMER) : ENSTIMER);

          /* enable interrupts for SELECTION OUT DONE and SELECTION TIME OUT */
          SETPORT( SIMODE0, ENSELDO | (disconnected_SC ? ENSELDI : 0) );
          SETPORT( SIMODE1, ENSELTIMO );

          /* Enable SELECTION OUT sequence */
          SETBITS(SCSISEQ, ENSELO | ENAUTOATNO );
        
  #if defined(DEBUG_RACE)
          leave_driver("(selecting) intr");
  #endif
          SETBITS( DMACNTRL0, INTEN );
          return;
        }

      /* No command we are busy with and no new to issue */
      printk("aha152x: ignoring spurious interrupt, nothing to do\n");
      return;
    }

  /* the bus is busy with something */

#if defined(DEBUG_INTR)
  if(aha152x_debug & debug_intr)
    disp_ports();
#endif

  /* we are waiting for the result of a selection attempt */
  if(current_SC->SCp.phase & in_selection)
    {
      if( TESTLO( SSTAT1, SELTO ) )
        /* no timeout */
        if( TESTHI( SSTAT0, SELDO ) )
          {
            /* clear BUS FREE interrupt */
            SETPORT( SSTAT1, CLRBUSFREE);

            /* Disable SELECTION OUT sequence */
            CLRBITS(SCSISEQ, ENSELO|ENAUTOATNO );

            /* Disable SELECTION OUT DONE interrupt */
            CLRBITS(SIMODE0, ENSELDO);
            CLRBITS(SIMODE1, ENSELTIMO);

            if( TESTLO(SSTAT0, SELDO) )
              {
                printk("aha152x: passing bus free condition\n");

#if defined(DEBUG_RACE)
                leave_driver("(passing bus free) intr");
#endif
                SETBITS( DMACNTRL0, INTEN);

                if(current_SC->SCp.phase & aborted)
                  {
                    abort_result=SCSI_ABORT_ERROR;
		    abortion_complete++;
                  }

                aha152x_done( DID_NO_CONNECT << 16 );
                return;
              }
#if defined(DEBUG_SELECTION) || defined(DEBUG_PHASES)
	    if(aha152x_debug & (debug_selection|debug_phases))
              printk("SELDO (SELID=%x), ", GETPORT(SELID));
#endif

            /* selection was done */
            SETPORT( SSTAT0, CLRSELDO );

#if defined(DEBUG_ABORT)
            if((aha152x_debug & debug_abort) && (current_SC->SCp.phase & aborted))
              printk("(ABORT) target selected, ");
#endif

            current_SC->SCp.phase &= ~in_selection;
            current_SC->SCp.phase |= in_other;

#if defined(DEBUG_RACE)
            leave_driver("(SELDO) intr");
#endif

            SETPORT( SCSISIG, P_MSGO );

            SETPORT( SIMODE0, 0 );
            SETPORT( SIMODE1, ENREQINIT|ENBUSFREE );
            SETBITS( DMACNTRL0, INTEN);
            return;
          }
        else
          aha152x_panic("neither timeout nor selection\007");
      else
        {
#if defined(DEBUG_SELECTION) || defined(DEBUG_PHASES)
	  if(aha152x_debug & (debug_selection|debug_phases))
          printk("SELTO, ");
#endif
	  /* end selection attempt */
          CLRBITS(SCSISEQ, ENSELO|ENAUTOATNO );

          /* timeout */
          SETPORT( SSTAT1, CLRSELTIMO );

          SETPORT(SIMODE0, disconnected_SC ? ENSELDI : 0 );
          SETPORT(SIMODE1, issue_SC ? ENBUSFREE : 0);
          SETBITS( DMACNTRL0, INTEN );
#if defined(DEBUG_RACE)
          leave_driver("(SELTO) intr");
#endif

          if(current_SC->SCp.phase & aborted)
            {
#if defined(DEBUG_ABORT)
	      if(aha152x_debug & debug_abort)
                printk("(ABORT) selection timeout, ");
#endif
              abort_result=SCSI_ABORT_ERROR;
              abortion_complete++;
            }

          if( TESTLO( SSTAT0, SELINGO ) )
            /* ARBITRATION not won */
            aha152x_done( DID_BUS_BUSY << 16 );
          else
            /* ARBITRATION won, but SELECTION failed */
            aha152x_done( DID_NO_CONNECT << 16 );
          return;
        }
    }

  /* enable interrupt, when target leaves current phase */
  phase = getphase();
  if(!(phase & ~P_MASK))                                      /* "real" phase */
    SETPORT(SCSISIG, phase);
  SETPORT(SSTAT1, CLRPHASECHG);
  current_SC->SCp.phase =
    (current_SC->SCp.phase & ~((P_MASK|1)<<16)) | (phase << 16 );

  /* information transfer phase */
  switch( phase )
    {
    case P_MSGO:                                               /* MESSAGE OUT */
      {
        unsigned char message;

#if defined(DEBUG_INTR) || defined(DEBUG_MSGO) || defined(DEBUG_PHASES)
        if(aha152x_debug & (debug_intr|debug_msgo|debug_phases))
          printk("MESSAGE OUT, ");
#endif

        if( current_SC->SCp.phase & aborted )
          {
#if defined(DEBUG_MSGO) || defined(DEBUG_ABORT)
            if(aha152x_debug & (debug_msgo|debug_abort))
              printk("ABORT, ");
#endif
            message=ABORT;
          }
        else
          /* If we didn't identify yet, do it. Otherwise there's nothing to do,
             but reject (probably we got an message before, that we have to
             reject (SDTR, WDTR, etc.) */
          if( !(current_SC->SCp.phase & sent_ident))
            {
              message=IDENTIFY(can_disconnect,current_SC->lun);
#if defined(DEBUG_MSGO)
              if(aha152x_debug & debug_msgo)
                printk("IDENTIFY (reconnect=%s;lun=%d), ", 
                        can_disconnect ? "enabled" : "disabled", current_SC->lun);
#endif
            }
          else
            {
              message=MESSAGE_REJECT;
#if defined(DEBUG_MSGO)
              if(aha152x_debug & debug_msgo)
                printk("REJECT, ");
#endif
            }
          
        CLRBITS( SXFRCTL0, ENDMA);

        SETPORT( SIMODE0, 0 );
        SETPORT( SIMODE1, ENPHASEMIS|ENREQINIT|ENBUSFREE );

        /* wait for data latch to become ready or a phase change */
        while( TESTLO( DMASTAT, INTSTAT ) )
          ;

        if( TESTHI( SSTAT1, PHASEMIS ) )
          aha152x_panic("unable to send message");

        /* Leave MESSAGE OUT after transfer */
        SETPORT( SSTAT1, CLRATNO);

        SETPORT( SCSIDAT, message );

        make_acklow();
        getphase();

        if(message==IDENTIFY(can_disconnect,current_SC->lun))
          current_SC->SCp.phase |= sent_ident;

        if(message==ABORT)
          {
            /* revive abort(); abort() enables interrupts */
            abort_result=SCSI_ABORT_SUCCESS;
            abortion_complete++;

            current_SC->SCp.phase = (current_SC->SCp.phase & ~(P_MASK<<16));

            /* exit */
            SETBITS( DMACNTRL0, INTEN );
#if defined(DEBUG_RACE)
            leave_driver("(ABORT) intr");
#endif
            aha152x_done(DID_ABORT<<16);
            return;
          }
      }
      break;

    case P_CMD:                                          /* COMMAND phase */
#if defined(DEBUG_INTR) || defined(DEBUG_CMD) || defined(DEBUG_PHASES)
      if(aha152x_debug & (debug_intr|debug_cmd|debug_phases))
        printk("COMMAND, ");
#endif
      if( !(current_SC->SCp.sent_command) )
        {
          if(GETPORT(FIFOSTAT) || GETPORT(SSTAT2) & (SFULL|SFCNT))
            printk("aha152x: P_CMD: %d(%d) bytes left in FIFO, resetting\n",
                   GETPORT(FIFOSTAT), GETPORT(SSTAT2) & (SFULL|SFCNT));

          /* reset fifo and enable writes */
          SETPORT(DMACNTRL0, WRITE_READ|RSTFIFO);
          SETPORT(DMACNTRL0, ENDMA|WRITE_READ);

          /* clear transfer count and scsi fifo */
          SETPORT(SXFRCTL0, CH1|CLRSTCNT|CLRCH1 );
          SETPORT(SXFRCTL0, SCSIEN|DMAEN|CH1);
  
          /* missing phase raises INTSTAT */
          SETPORT( SIMODE0, 0 );
          SETPORT( SIMODE1, ENPHASEMIS|ENBUSFREE );
  
#if defined(DEBUG_CMD)
          if(aha152x_debug & debug_cmd)
            printk("waiting, ");
#endif
          /* wait for FIFO to get empty */
          while( TESTLO ( DMASTAT, DFIFOEMP|INTSTAT ) )
            ;
  
          if( TESTHI( SSTAT1, PHASEMIS ) )
            aha152x_panic("target left COMMAND phase");

#if defined(DEBUG_CMD)
          if(aha152x_debug & debug_cmd)
          {
            printk("DFIFOEMP, outsw (%d bytes, %d words), ",
		   current_SC->cmd_len, current_SC->cmd_len >> 1 );
            disp_ports();
          }
#endif
  
          outsw( DATAPORT, &current_SC->cmnd, current_SC->cmd_len >> 1 );

#if defined(DEBUG_CMD)
	  if(aha152x_debug & debug_cmd)
          {
            printk("FCNT=%d, STCNT=%d, ", GETPORT(FIFOSTAT), GETSTCNT() );
            disp_ports();
          }
#endif

#if defined(DEBUG_CMD)
	  if(aha152x_debug & debug_cmd)
            printk("waiting for SEMPTY, ");
#endif

          /* wait for SCSI FIFO to get empty.
             very important to send complete commands. */
          while( TESTLO ( SSTAT2, SEMPTY ) )
            ;

#if defined(DEBUG_CMD)
	  if(aha152x_debug & debug_cmd)
            printk("SEMPTY, ");
#endif

          CLRBITS(SXFRCTL0, SCSIEN|DMAEN);
          /* transfer can be considered ended, when SCSIEN reads back zero */
          while( TESTHI( SXFRCTL0, SCSIEN ) )
            ;

#if defined(DEBUG_CMD)
	  if(aha152x_debug & debug_cmd)
            printk("!SEMPTY, ");
#endif

          CLRBITS(DMACNTRL0, ENDMA);

#if defined(DEBUG_CMD) || defined(DEBUG_INTR)
          if(debug_cmd & debug_intr)
            printk("sent %d/%d command bytes, ", GETSTCNT(),
                   current_SC->cmd_len);
#endif

        }
      else
        aha152x_panic("Nothing to sent while in COMMAND OUT");
      break;

    case P_MSGI:                                          /* MESSAGE IN phase */
#if defined(DEBUG_INTR) || defined(DEBUG_MSGI) || defined(DEBUG_PHASES)
      if(aha152x_debug & (debug_intr|debug_msgi|debug_phases))
        printk("MESSAGE IN, ");
#endif
      SETPORT( SXFRCTL0, CH1);

      SETPORT( SIMODE0, 0);
      SETPORT( SIMODE1, ENBUSFREE);
  
      while( phase == P_MSGI ) 
        {
          current_SC->SCp.Message = GETPORT( SCSIBUS );
          switch(current_SC->SCp.Message)
            {
            case DISCONNECT:
#if defined(DEBUG_MSGI) || defined(DEBUG_PHASES)
	      if(aha152x_debug & (debug_msgi|debug_phases))
                printk("target disconnected, ");
#endif
              current_SC->SCp.Message = 0;
              current_SC->SCp.phase   |= disconnected;
              if(!can_disconnect)
                aha152x_panic("target was not allowed to disconnect");
              break;
        
            case COMMAND_COMPLETE:
#if defined(DEBUG_MSGI) || defined(DEBUG_PHASES)
	      if(aha152x_debug & (debug_msgi|debug_phases))
                printk("inbound message ( COMMAND COMPLETE ), ");
#endif
              done++;
              break;

            case MESSAGE_REJECT:
#if defined(DEBUG_MSGI)
	      if(aha152x_debug & debug_msgi)
                printk("inbound message ( MESSAGE REJECT ), ");
#endif
              break;

            case SAVE_POINTERS:
#if defined(DEBUG_MSGI)
	      if(aha152x_debug & debug_msgi)
                printk("inbound message ( SAVE DATA POINTERS ), ");
#endif
              break;

            case EXTENDED_MESSAGE:
              { 
                int           i, code;

#if defined(DEBUG_MSGI)
	        if(aha152x_debug & debug_msgi)
                  printk("inbound message ( EXTENDED MESSAGE ), ");
#endif
                make_acklow();
                if(getphase()!=P_MSGI)
                  break;
  
                i=GETPORT(SCSIBUS);

#if defined(DEBUG_MSGI)
	        if(aha152x_debug & debug_msgi)
                  printk("length (%d), code ( ", i);
#endif

                make_acklow();
                if(getphase()!=P_MSGI)
                  break;

                code = GETPORT(SCSIBUS);

                switch( code )
                  {
                  case 0x00:
#if defined(DEBUG_MSGI)
	            if(aha152x_debug & debug_msgi)
                      printk("MODIFY DATA POINTER ");
#endif
                    SETPORT(SCSISIG, P_MSGI|ATNO);
                    break;
                  case 0x01:
#if defined(DEBUG_MSGI)
	            if(aha152x_debug & debug_msgi)
                      printk("SYNCHRONOUS DATA TRANSFER REQUEST ");
#endif
                    SETPORT(SCSISIG, P_MSGI|ATNO);
                    break;
                  case 0x02:
#if defined(DEBUG_MSGI)
	            if(aha152x_debug & debug_msgi)
                      printk("EXTENDED IDENTIFY ");
#endif
                    break;
                  case 0x03:
#if defined(DEBUG_MSGI)
	            if(aha152x_debug & debug_msgi)
                      printk("WIDE DATA TRANSFER REQUEST ");
#endif
                    SETPORT(SCSISIG, P_MSGI|ATNO);
                    break;
                  default:
#if defined(DEBUG_MSGI)
	            if(aha152x_debug & debug_msgi)
                      if( code & 0x80 )
                        printk("reserved (%d) ", code );
                      else
                        printk("vendor specific (%d) ", code);
#endif
                    SETPORT(SCSISIG, P_MSGI|ATNO);
                    break;
                  }
#if defined(DEBUG_MSGI)
	        if(aha152x_debug & debug_msgi)
                  printk(" ), data ( ");
#endif
                while( --i && (make_acklow(), getphase()==P_MSGI))
                  {
#if defined(DEBUG_MSGI)
	            if(aha152x_debug & debug_msgi)
                      printk("%x ", GETPORT(SCSIBUS) );
#else
                    GETPORT(SCSIBUS);
#endif
                  }
#if defined(DEBUG_MSGI)
	        if(aha152x_debug & debug_msgi)
                  printk(" ), ");
#endif
                /* We reject all extended messages. To do this
                   we just enter MSGO by asserting ATN. Since
                   we have already identified a REJECT message
                   will be sent. */
                SETPORT(SCSISIG, P_MSGI|ATNO);
              }
              break;
       
            default:
              printk("unsupported inbound message %x, ", current_SC->SCp.Message);
              break;

            }

          make_acklow();
          phase=getphase();
        } 

      /* clear SCSI fifo on BUSFREE */
      if(phase==P_BUSFREE)
        SETPORT(SXFRCTL0, CH1|CLRCH1);

      if(current_SC->SCp.phase & disconnected)
        {
          save_flags(flags);
          cli();
#if defined(DEBUG_QUEUES)
	  if(aha152x_debug & debug_queues)
            printk("d+, ");
#endif
          append_SC( &disconnected_SC, current_SC);
          current_SC = NULL;
          restore_flags(flags);

          SETBITS( SCSISEQ, ENRESELI );

          SETPORT(SIMODE0, disconnected_SC ? ENSELDI : 0 );
          SETPORT(SIMODE1, issue_SC ? ENBUSFREE : 0);

          SETBITS( DMACNTRL0, INTEN );
          return;
        }
      break;

    case P_STATUS:                                         /* STATUS IN phase */
#if defined(DEBUG_STATUS) || defined(DEBUG_INTR) || defined(DEBUG_PHASES)
      if(aha152x_debug & (debug_status|debug_intr|debug_phases))
        printk("STATUS, ");
#endif
      SETPORT( SXFRCTL0, CH1);

      SETPORT( SIMODE0, 0 );
      SETPORT( SIMODE1, ENREQINIT|ENBUSFREE );

      if( TESTHI( SSTAT1, PHASEMIS ) )
	printk("aha152x: passing STATUS phase");
	
      current_SC->SCp.Status = GETPORT( SCSIBUS );
      make_acklow();
      getphase();

#if defined(DEBUG_STATUS)
      if(aha152x_debug & debug_status)
      {
        printk("inbound status ");
        print_status( current_SC->SCp.Status );
        printk(", ");
      }
#endif
      break;

    case P_DATAI:                                            /* DATA IN phase */
      {
        int fifodata, data_count, done;

#if defined(DEBUG_DATAI) || defined(DEBUG_INTR) || defined(DEBUG_PHASES)
        if(aha152x_debug & (debug_datai|debug_intr|debug_phases))
          printk("DATA IN, ");
#endif

        if(GETPORT(FIFOSTAT) || GETPORT(SSTAT2) & (SFULL|SFCNT))
          printk("aha152x: P_DATAI: %d(%d) bytes left in FIFO, resetting\n",
                 GETPORT(FIFOSTAT), GETPORT(SSTAT2) & (SFULL|SFCNT));

        /* reset host fifo */
        SETPORT(DMACNTRL0, RSTFIFO);
        SETPORT(DMACNTRL0, RSTFIFO|ENDMA);

        SETPORT(SXFRCTL0, CH1|SCSIEN|DMAEN );

        SETPORT( SIMODE0, 0 );
        SETPORT( SIMODE1, ENPHASEMIS|ENBUSFREE );

        /* done is set when the FIFO is empty after the target left DATA IN */
        done=0;
      
        /* while the target stays in DATA to transfer data */
        while ( !done ) 
          {
#if defined(DEBUG_DATAI)
            if(aha152x_debug & debug_datai)
              printk("expecting data, ");
#endif
            /* wait for PHASEMIS or full FIFO */
            while( TESTLO ( DMASTAT, DFIFOFULL|INTSTAT ) )
              ;

            if( TESTHI( DMASTAT, DFIFOFULL ) )
              fifodata=GETPORT(FIFOSTAT);
            else
              {
                /* wait for SCSI fifo to get empty */
                while( TESTLO( SSTAT2, SEMPTY ) )
                  ;

                /* rest of data in FIFO */
                fifodata=GETPORT(FIFOSTAT);
#if defined(DEBUG_DATAI)
                if(aha152x_debug & debug_datai)
                  printk("last transfer, ");
#endif
                done=1;
              }
  
#if defined(DEBUG_DATAI)
            if(aha152x_debug & debug_datai)
              printk("fifodata=%d, ", fifodata);
#endif

            while( fifodata && current_SC->SCp.this_residual )
              {
                data_count=fifodata;
  
                /* limit data transfer to size of first sg buffer */
                if (data_count > current_SC->SCp.this_residual)
                  data_count = current_SC->SCp.this_residual;
  
                fifodata -= data_count;

#if defined(DEBUG_DATAI)
                if(aha152x_debug & debug_datai)
                  printk("data_count=%d, ", data_count);
#endif
  
                if(data_count&1)
                  {
                    /* get a single byte in byte mode */
                    SETBITS(DMACNTRL0, _8BIT );
                    *current_SC->SCp.ptr++ = GETPORT( DATAPORT );
                    current_SC->SCp.this_residual--;
                  }
                if(data_count>1)
                  {
                    CLRBITS(DMACNTRL0, _8BIT );
                    data_count >>= 1; /* Number of words */
                    insw( DATAPORT, current_SC->SCp.ptr, data_count );
#if defined(DEBUG_DATAI)
                    if(aha152x_debug & debug_datai)
/* show what comes with the last transfer */
                      if(done)
                        {
                          int           i;
                          unsigned char *data;
  
                          printk("data on last transfer (%d bytes: ",
                                 2*data_count);
                          data = (unsigned char *) current_SC->SCp.ptr;
                          for( i=0; i<2*data_count; i++)
                            printk("%2x ", *data++);
                          printk("), ");
                        }
#endif
                    current_SC->SCp.ptr           += 2 * data_count;
                    current_SC->SCp.this_residual -= 2 * data_count;
                  }
              
                /* if this buffer is full and there are more buffers left */
                if (!current_SC->SCp.this_residual &&
                     current_SC->SCp.buffers_residual)
                  {
                    /* advance to next buffer */
                    current_SC->SCp.buffers_residual--;
                    current_SC->SCp.buffer++;
                    current_SC->SCp.ptr =
                      current_SC->SCp.buffer->address;
                    current_SC->SCp.this_residual =
                      current_SC->SCp.buffer->length;
                  } 
              }
 
            /*
             * Fifo should be empty
             */
            if(fifodata>0)
              {
                printk("aha152x: more data than expected (%d bytes)\n",
                       GETPORT(FIFOSTAT));
                SETBITS(DMACNTRL0, _8BIT );
                printk("aha152x: data ( ");
                while(fifodata--)
                  printk("%2x ", GETPORT( DATAPORT ));
                printk(")\n");
              }

#if defined(DEBUG_DATAI)
            if(aha152x_debug & debug_datai)
              if(!fifodata)
                printk("fifo empty, ");
              else
                printk("something left in fifo, ");
#endif
          }

#if defined(DEBUG_DATAI)
        if((aha152x_debug & debug_datai) && (current_SC->SCp.buffers_residual || current_SC->SCp.this_residual))
          printk("left buffers (buffers=%d, bytes=%d), ",
                 current_SC->SCp.buffers_residual, 
                 current_SC->SCp.this_residual);
#endif
        /* transfer can be considered ended, when SCSIEN reads back zero */
        CLRBITS(SXFRCTL0, SCSIEN|DMAEN);
        while( TESTHI( SXFRCTL0, SCSIEN ) )
          ;
        CLRBITS(DMACNTRL0, ENDMA );

#if defined(DEBUG_DATAI) || defined(DEBUG_INTR)
        if(aha152x_debug & (debug_datai|debug_intr))
          printk("got %d bytes, ", GETSTCNT());
#endif

        current_SC->SCp.have_data_in++;
      }
      break;

    case P_DATAO:                                           /* DATA OUT phase */
      {
        int data_count;

#if defined(DEBUG_DATAO) || defined(DEBUG_INTR) || defined(DEBUG_PHASES)
        if(aha152x_debug & (debug_datao|debug_intr|debug_phases))
          printk("DATA OUT, ");
#endif
#if defined(DEBUG_DATAO)
        if(aha152x_debug & debug_datao)
          printk("got data to send (bytes=%d, buffers=%d), ",
                 current_SC->SCp.this_residual,
                 current_SC->SCp.buffers_residual );
#endif

        if(GETPORT(FIFOSTAT) || GETPORT(SSTAT2) & (SFULL|SFCNT) )
          {
            printk("%d(%d) left in FIFO, ", GETPORT(FIFOSTAT), GETPORT(SSTAT2) & (SFULL|SFCNT) );
            aha152x_panic("FIFO should be empty");
          }

        SETPORT(DMACNTRL0, WRITE_READ|RSTFIFO);
        SETPORT(DMACNTRL0, ENDMA|WRITE_READ);

        SETPORT(SXFRCTL0, CH1|CLRSTCNT|CLRCH1 );
        SETPORT(SXFRCTL0, SCSIEN|DMAEN|CH1);
 
        SETPORT( SIMODE0, 0 );
        SETPORT( SIMODE1, ENPHASEMIS|ENBUSFREE );

        /* while current buffer is not empty or
           there are more buffers to transfer */
        while( TESTLO( SSTAT1, PHASEMIS ) &&
                 (current_SC->SCp.this_residual ||
                  current_SC->SCp.buffers_residual) )
          {
#if defined(DEBUG_DATAO)
            if(aha152x_debug & debug_datao)
              printk("sending data (left: bytes=%d, buffers=%d), waiting, ",
                     current_SC->SCp.this_residual,
                     current_SC->SCp.buffers_residual);
#endif
            /* transfer rest of buffer, but max. 128 byte */
            data_count = current_SC->SCp.this_residual > 128 ?
                         128 : current_SC->SCp.this_residual ;

#if defined(DEBUG_DATAO)
            if(aha152x_debug & debug_datao)
              printk("data_count=%d, ", data_count);
#endif
  
            if(data_count&1)
              {
                /* put a single byte in byte mode */
                SETBITS(DMACNTRL0, _8BIT );
                SETPORT(DATAPORT, *current_SC->SCp.ptr++);
                current_SC->SCp.this_residual--;
              }
            if(data_count>1)
              {
                CLRBITS(DMACNTRL0, _8BIT );
                data_count >>= 1; /* Number of words */
                outsw( DATAPORT, current_SC->SCp.ptr, data_count );
                current_SC->SCp.ptr           += 2 * data_count;
                current_SC->SCp.this_residual -= 2 * data_count;
              }

            /* wait for FIFO to get empty */
            while( TESTLO ( DMASTAT, DFIFOEMP|INTSTAT ) )
              ;

#if defined(DEBUG_DATAO)
            if(aha152x_debug & debug_datao)
              printk("fifo (%d bytes), transfered (%d bytes), ",
                     GETPORT(FIFOSTAT), GETSTCNT() );
#endif

            /* if this buffer is empty and there are more buffers left */
            if ( TESTLO( SSTAT1, PHASEMIS ) &&
                 !current_SC->SCp.this_residual &&
                  current_SC->SCp.buffers_residual)
              {
                 /* advance to next buffer */
                 current_SC->SCp.buffers_residual--;
                 current_SC->SCp.buffer++;
                 current_SC->SCp.ptr =
                   current_SC->SCp.buffer->address;
                 current_SC->SCp.this_residual =
                 current_SC->SCp.buffer->length;
              }
          }

        if ( current_SC->SCp.this_residual ||
             current_SC->SCp.buffers_residual )
          {
            /* target leaves DATA OUT for an other phase
               (perhaps disconnect) */

            /* data in fifos has to be resend */
            data_count = GETPORT(SSTAT2) & (SFULL|SFCNT);

            data_count += GETPORT(FIFOSTAT) ;
            current_SC->SCp.ptr           -= data_count;
            current_SC->SCp.this_residual += data_count;
#if defined(DEBUG_DATAO)
            if(aha152x_debug & debug_datao)
              printk("left data (bytes=%d, buffers=%d), fifos (bytes=%d), transfer incomplete, resetting fifo, ",
                     current_SC->SCp.this_residual,
                     current_SC->SCp.buffers_residual,
                     data_count );
#endif
            SETPORT(DMACNTRL0, WRITE_READ|RSTFIFO);
            CLRBITS(SXFRCTL0, SCSIEN|DMAEN );
            CLRBITS(DMACNTRL0, ENDMA);
          }
        else
          {
#if defined(DEBUG_DATAO)
            if(aha152x_debug & debug_datao)
              printk("waiting for SCSI fifo to get empty, ");
#endif
            /* wait for SCSI fifo to get empty */
            while( TESTLO( SSTAT2, SEMPTY ) )
              ;
#if defined(DEBUG_DATAO)
            if(aha152x_debug & debug_datao)
              printk("ok, left data (bytes=%d, buffers=%d) ",
                     current_SC->SCp.this_residual,
                     current_SC->SCp.buffers_residual);
#endif
            CLRBITS(SXFRCTL0, SCSIEN|DMAEN);

            /* transfer can be considered ended, when SCSIEN reads back zero */
            while( TESTHI( SXFRCTL0, SCSIEN ) )
              ;

            CLRBITS(DMACNTRL0, ENDMA);
          }

#if defined(DEBUG_DATAO) || defined(DEBUG_INTR)
        if(aha152x_debug & (debug_datao|debug_intr))
          printk("sent %d data bytes, ", GETSTCNT() );
#endif
      }
      break;

    case P_BUSFREE:                                                /* BUSFREE */
#if defined(DEBUG_RACE)
      leave_driver("(BUSFREE) intr");
#endif
#if defined(DEBUG_PHASES)
      if(aha152x_debug & debug_phases)
        printk("unexpected BUS FREE, ");
#endif
      current_SC->SCp.phase = (current_SC->SCp.phase & ~(P_MASK<<16));

      aha152x_done( DID_ERROR << 16 );               /* Don't know any better */
      return;
      break;

    case P_PARITY:                              /* parity error in DATA phase */
#if defined(DEBUG_RACE)
      leave_driver("(DID_PARITY) intr");
#endif
      printk("PARITY error in DATA phase, ");

      current_SC->SCp.phase = (current_SC->SCp.phase & ~(P_MASK<<16));

      SETBITS( DMACNTRL0, INTEN );
      aha152x_done( DID_PARITY << 16 );
      return;
      break;

    default:
      printk("aha152x: unexpected phase\n");
      break;
    }

  if(done)
    {
#if defined(DEBUG_INTR)
      if(aha152x_debug & debug_intr)
        printk("command done.\n");
#endif
#if defined(DEBUG_RACE)
      leave_driver("(done) intr");
#endif

      SETPORT(SIMODE0, disconnected_SC ? ENSELDI : 0 );
      SETPORT(SIMODE1, issue_SC ? ENBUSFREE : 0);
      SETPORT(SCSISEQ, disconnected_SC ? ENRESELI : 0 );

      SETBITS( DMACNTRL0, INTEN );

      aha152x_done(   (current_SC->SCp.Status  & 0xff)
                    | ( (current_SC->SCp.Message & 0xff) << 8)
                    | ( DID_OK << 16) );

#if defined(DEBUG_RACE)
      printk("done returned (DID_OK: Status=%x; Message=%x).\n",
             current_SC->SCp.Status, current_SC->SCp.Message);
#endif
      return;
    }

  if(current_SC)
    current_SC->SCp.phase |= 1<<16 ;

  SETPORT( SIMODE0, 0 );
  SETPORT( SIMODE1, ENPHASEMIS|ENBUSFREE );
#if defined(DEBUG_INTR)
  if(aha152x_debug & debug_intr)
    disp_enintr();
#endif
#if defined(DEBUG_RACE)
  leave_driver("(PHASEEND) intr");
#endif

  SETBITS( DMACNTRL0, INTEN);
  return;
}

/* 
 * Dump the current driver status and panic...
 */
static void aha152x_panic(char *msg)
{
  printk("\naha152x_panic: %s\n", msg);
  show_queues();
  panic("aha152x panic");
}

/*
 * Display registers of AIC-6260
 */
static void disp_ports(void)
{
#ifdef DEBUG_AHA152X
  int s;

#ifdef SKIP_PORTS
  if(aha152x_debug & debug_skipports)
	return;
#endif

  printk("\n%s: ", current_SC ? "on bus" : "waiting");

  s=GETPORT(SCSISEQ);
  printk("SCSISEQ ( ");
  if( s & TEMODEO )     printk("TARGET MODE ");
  if( s & ENSELO )      printk("SELO ");
  if( s & ENSELI )      printk("SELI ");
  if( s & ENRESELI )    printk("RESELI ");
  if( s & ENAUTOATNO )  printk("AUTOATNO ");
  if( s & ENAUTOATNI )  printk("AUTOATNI ");
  if( s & ENAUTOATNP )  printk("AUTOATNP ");
  if( s & SCSIRSTO )    printk("SCSIRSTO ");
  printk(");");

  printk(" SCSISIG ( ");
  s=GETPORT(SCSISIG);
  switch(s & P_MASK)
    {
    case P_DATAO:
      printk("DATA OUT");
      break;
    case P_DATAI:
      printk("DATA IN");
      break;
    case P_CMD:
      printk("COMMAND"); 
      break;
    case P_STATUS:
      printk("STATUS"); 
      break;
    case P_MSGO:
      printk("MESSAGE OUT");
      break;
    case P_MSGI:
      printk("MESSAGE IN");
      break;
    default:
      printk("*illegal*");
      break;
    }
  
  printk(" ); ");

  printk("INTSTAT ( %s ); ", TESTHI(DMASTAT, INTSTAT) ? "hi" : "lo");

  printk("SSTAT ( ");
  s=GETPORT(SSTAT0);
  if( s & TARGET )   printk("TARGET ");
  if( s & SELDO )    printk("SELDO ");
  if( s & SELDI )    printk("SELDI ");
  if( s & SELINGO )  printk("SELINGO ");
  if( s & SWRAP )    printk("SWRAP ");
  if( s & SDONE )    printk("SDONE ");
  if( s & SPIORDY )  printk("SPIORDY ");
  if( s & DMADONE )  printk("DMADONE ");

  s=GETPORT(SSTAT1);
  if( s & SELTO )     printk("SELTO ");
  if( s & ATNTARG )   printk("ATNTARG ");
  if( s & SCSIRSTI )  printk("SCSIRSTI ");
  if( s & PHASEMIS )  printk("PHASEMIS ");
  if( s & BUSFREE )   printk("BUSFREE ");
  if( s & SCSIPERR )  printk("SCSIPERR ");
  if( s & PHASECHG )  printk("PHASECHG ");
  if( s & REQINIT )   printk("REQINIT ");
  printk("); ");


  printk("SSTAT ( ");

  s=GETPORT(SSTAT0) & GETPORT(SIMODE0);

  if( s & TARGET )    printk("TARGET ");
  if( s & SELDO )     printk("SELDO ");
  if( s & SELDI )     printk("SELDI ");
  if( s & SELINGO )   printk("SELINGO ");
  if( s & SWRAP )     printk("SWRAP ");
  if( s & SDONE )     printk("SDONE ");
  if( s & SPIORDY )   printk("SPIORDY ");
  if( s & DMADONE )   printk("DMADONE ");

  s=GETPORT(SSTAT1) & GETPORT(SIMODE1);

  if( s & SELTO )     printk("SELTO ");
  if( s & ATNTARG )   printk("ATNTARG ");
  if( s & SCSIRSTI )  printk("SCSIRSTI ");
  if( s & PHASEMIS )  printk("PHASEMIS ");
  if( s & BUSFREE )   printk("BUSFREE ");
  if( s & SCSIPERR )  printk("SCSIPERR ");
  if( s & PHASECHG )  printk("PHASECHG ");
  if( s & REQINIT )   printk("REQINIT ");
  printk("); ");

  printk("SXFRCTL0 ( ");

  s=GETPORT(SXFRCTL0);
  if( s & SCSIEN )    printk("SCSIEN ");
  if( s & DMAEN )     printk("DMAEN ");
  if( s & CH1 )       printk("CH1 ");
  if( s & CLRSTCNT )  printk("CLRSTCNT ");
  if( s & SPIOEN )    printk("SPIOEN ");
  if( s & CLRCH1 )    printk("CLRCH1 ");
  printk("); ");

  printk("SIGNAL ( ");

  s=GETPORT(SCSISIG);
  if( s & ATNI )  printk("ATNI ");
  if( s & SELI )  printk("SELI ");
  if( s & BSYI )  printk("BSYI ");
  if( s & REQI )  printk("REQI ");
  if( s & ACKI )  printk("ACKI ");
  printk("); ");

  printk("SELID ( %02x ), ", GETPORT(SELID) );

  printk("SSTAT2 ( ");

  s=GETPORT(SSTAT2);
  if( s & SOFFSET)  printk("SOFFSET ");
  if( s & SEMPTY)   printk("SEMPTY ");
  if( s & SFULL)    printk("SFULL ");
  printk("); SFCNT ( %d ); ", s & (SFULL|SFCNT) );

#if 0
  printk("SSTAT4 ( ");
  s=GETPORT(SSTAT4);
  if( s & SYNCERR)   printk("SYNCERR ");
  if( s & FWERR)     printk("FWERR ");
  if( s & FRERR)     printk("FRERR ");
  printk("); ");
#endif

  printk("FCNT ( %d ); ", GETPORT(FIFOSTAT) );

  printk("DMACNTRL0 ( ");
  s=GETPORT(DMACNTRL0);
  printk( "%s ", s & _8BIT      ? "8BIT"  : "16BIT" );
  printk( "%s ", s & DMA        ? "DMA"   : "PIO"   );
  printk( "%s ", s & WRITE_READ ? "WRITE" : "READ"  );
  if( s & ENDMA )    printk("ENDMA ");
  if( s & INTEN )    printk("INTEN ");
  if( s & RSTFIFO )  printk("RSTFIFO ");
  if( s & SWINT )    printk("SWINT ");
  printk("); ");


#if 0
  printk("DMACNTRL1 ( ");

  s=GETPORT(DMACNTRL1);
  if( s & PWRDWN )    printk("PWRDN ");
  printk("); ");


  printk("STK ( %d ); ", s & 0xf);

  printk("DMASTAT (");
  s=GETPORT(DMASTAT);
  if( s & ATDONE )     printk("ATDONE ");
  if( s & WORDRDY )    printk("WORDRDY ");
  if( s & DFIFOFULL )  printk("DFIFOFULL ");
  if( s & DFIFOEMP )   printk("DFIFOEMP ");
  printk(")");

#endif

  printk("\n");
#endif
}

/*
 * display enabled interrupts
 */
static void disp_enintr(void)
{
  int s;

  printk("enabled interrupts ( ");
  
  s=GETPORT(SIMODE0);
  if( s & ENSELDO )    printk("ENSELDO ");
  if( s & ENSELDI )    printk("ENSELDI ");
  if( s & ENSELINGO )  printk("ENSELINGO ");
  if( s & ENSWRAP )    printk("ENSWRAP ");
  if( s & ENSDONE )    printk("ENSDONE ");
  if( s & ENSPIORDY )  printk("ENSPIORDY ");
  if( s & ENDMADONE )  printk("ENDMADONE ");

  s=GETPORT(SIMODE1);
  if( s & ENSELTIMO )    printk("ENSELTIMO ");
  if( s & ENATNTARG )    printk("ENATNTARG ");
  if( s & ENPHASEMIS )   printk("ENPHASEMIS ");
  if( s & ENBUSFREE )    printk("ENBUSFREE ");
  if( s & ENSCSIPERR )   printk("ENSCSIPERR ");
  if( s & ENPHASECHG )   printk("ENPHASECHG ");
  if( s & ENREQINIT )    printk("ENREQINIT ");
  printk(")\n");
}

#if defined(DEBUG_RACE)

static const char *should_leave;
static int in_driver=0;

/*
 * Only one routine can be in the driver at once.
 */
static void enter_driver(const char *func)
{
  unsigned long flags;

  save_flags(flags);
  cli();
  printk("aha152x: entering %s() (%x)\n", func, jiffies);
  if(in_driver)
    {
      printk("%s should leave first.\n", should_leave);
      panic("aha152x: already in driver\n");
    }

  in_driver++;
  should_leave=func;
  restore_flags(flags);
}

static void leave_driver(const char *func)
{
  unsigned long flags;

  save_flags(flags);
  cli();
  printk("\naha152x: leaving %s() (%x)\n", func, jiffies);
  if(!in_driver)
    {
      printk("aha152x: %s already left.\n", should_leave);
      panic("aha152x: %s already left driver.\n");
    }

  in_driver--;
  should_leave=func;
  restore_flags(flags);
}
#endif

/*
 * Show the command data of a command
 */
static void show_command(Scsi_Cmnd *ptr)
{
  printk("0x%08x: target=%d; lun=%d; cmnd=( ",
         (unsigned int) ptr, ptr->target, ptr->lun);
  
  print_command(ptr->cmnd);

  printk("); residual=%d; buffers=%d; phase |",
         ptr->SCp.this_residual, ptr->SCp.buffers_residual);

  if( ptr->SCp.phase & not_issued   )  printk("not issued|");
  if( ptr->SCp.phase & in_selection )  printk("in selection|");
  if( ptr->SCp.phase & disconnected )  printk("disconnected|");
  if( ptr->SCp.phase & aborted      )  printk("aborted|");
  if( ptr->SCp.phase & sent_ident   )  printk("send_ident|");
  if( ptr->SCp.phase & in_other )
    { 
      printk("; in other(");
      switch( (ptr->SCp.phase >> 16) & P_MASK )
        {
        case P_DATAO:
          printk("DATA OUT");
          break;
        case P_DATAI:
          printk("DATA IN");
          break;
        case P_CMD:
          printk("COMMAND");
          break;
        case P_STATUS:
          printk("STATUS");
          break;
        case P_MSGO:
          printk("MESSAGE OUT");
          break;
        case P_MSGI:
          printk("MESSAGE IN");
          break;
        default: 
          printk("*illegal*");
          break;
        }
      printk(")");
      if(ptr->SCp.phase & (1<<16))
        printk("; phaseend");
    }
  printk("; next=0x%08x\n", (unsigned int) ptr->host_scribble);
}
 
/*
 * Dump the queued data
 */
static void show_queues(void)
{
  unsigned long flags;
  Scsi_Cmnd *ptr;

  save_flags(flags);
  cli();
  printk("QUEUE STATUS:\nissue_SC:\n");
  for(ptr=issue_SC; ptr; ptr = (Scsi_Cmnd *) ptr->host_scribble )
    show_command(ptr);

  printk("current_SC:\n");
  if(current_SC)
    show_command(current_SC);
  else
    printk("none\n");

  printk("disconnected_SC:\n");
  for(ptr=disconnected_SC; ptr; ptr = (Scsi_Cmnd *) ptr->host_scribble )
    show_command(ptr);

  disp_ports();
  disp_enintr();
  restore_flags(flags);
}
