summaryrefslogtreecommitdiffstats
path: root/sys/i386/isa/aha1542.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/i386/isa/aha1542.c')
-rw-r--r--sys/i386/isa/aha1542.c1644
1 files changed, 1644 insertions, 0 deletions
diff --git a/sys/i386/isa/aha1542.c b/sys/i386/isa/aha1542.c
new file mode 100644
index 0000000..85271fc
--- /dev/null
+++ b/sys/i386/isa/aha1542.c
@@ -0,0 +1,1644 @@
+/*
+ * (Mostly) Written by Julian Elischer (julian@tfs.com)
+ * for TRW Financial Systems for use under the MACH(2.5) operating system.
+ *
+ * TRW Financial Systems, in accordance with their agreement with Carnegie
+ * Mellon University, makes this software available to CMU to distribute
+ * or use in any manner that they see fit as long as this message is kept with
+ * the software. For this reason TFS also grants any other persons or
+ * organisations permission to use or modify this software.
+ *
+ * TFS supplies this software to be publicly redistributed
+ * on the understanding that TFS is not responsible for the correct
+ * functioning of this software in any circumstances.
+ *
+ *
+ * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE
+ * -------------------- ----- ----------------------
+ * CURRENT PATCH LEVEL: 1 00098
+ * -------------------- ----- ----------------------
+ *
+ * 16 Feb 93 Julian Elischer ADDED for SCSI system
+ */
+
+/*
+ * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
+ */
+
+/*
+ * HISTORY
+ * $Log: aha1542.c,v $
+ * Revision 1.6 1992/08/24 21:01:58 jason
+ * many changes and bugfixes for osf1
+ *
+ * Revision 1.5 1992/07/31 01:22:03 julian
+ * support improved scsi.h layout
+ *
+ * Revision 1.4 1992/07/25 03:11:26 julian
+ * check each request fro sane flags.
+ *
+ * Revision 1.3 1992/07/24 00:52:45 julian
+ * improved timeout handling.
+ * added support for two arguments to the sd_done (or equiv) call so that
+ * they can pre-queue several arguments.
+ * slightly clean up error handling
+ *
+ * Revision 1.2 1992/07/17 22:03:54 julian
+ * upgraded the timeout code.
+ * added support for UIO-based i/o (as used for pmem operations)
+ *
+ * Revision 1.1 1992/05/27 00:51:12 balsup
+ * machkern/cor merge
+ */
+
+/*
+ * a FEW lines in this driver come from a MACH adaptec-disk driver
+ * so the copyright below is included:
+ *
+ * Copyright 1990 by Open Software Foundation,
+ * Grenoble, FRANCE
+ *
+ * All Rights Reserved
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both the copyright notice and this permission notice appear in
+ * supporting documentation, and that the name of OSF or Open Software
+ * Foundation not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission.
+ *
+ * OSF DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+ * IN NO EVENT SHALL OSF BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+ * NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+
+#include <sys/types.h>
+#include <aha.h>
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+#include <sys/user.h>
+
+#ifdef MACH /* EITHER CMU OR OSF */
+#include <i386/ipl.h>
+#include <i386at/scsi.h>
+#include <i386at/scsiconf.h>
+
+#ifdef OSF /* OSF ONLY */
+#include <sys/table.h>
+#include <i386/handler.h>
+#include <i386/dispatcher.h>
+#include <i386/AT386/atbus.h>
+
+#else OSF /* CMU ONLY */
+#include <i386at/atbus.h>
+#include <i386/pio.h>
+#endif OSF
+#endif MACH /* end of MACH specific */
+
+#ifdef __386BSD__ /* 386BSD specific */
+#define isa_dev isa_device
+#define dev_unit id_unit
+#define dev_addr id_iobase
+
+#include <i386/isa/isa_device.h>
+#include <scsi/scsi_all.h>
+#include <scsi/scsiconf.h>
+#endif __386BSD__
+
+
+#ifdef __386BSD__
+#include "ddb.h"
+#if NDDB > 0
+int Debugger();
+#else NDDB
+#define Debugger() panic("should call debugger here (adaptec.c)")
+#endif NDDB
+#endif __386BSD__
+extern int delaycount; /* from clock setup code */
+
+/************************** board definitions *******************************/
+/*
+ * I/O Port Interface
+ */
+
+#define AHA_BASE aha_base[unit]
+#define AHA_CTRL_STAT_PORT (AHA_BASE + 0x0) /* control & status */
+#define AHA_CMD_DATA_PORT (AHA_BASE + 0x1) /* cmds and datas */
+#define AHA_INTR_PORT (AHA_BASE + 0x2) /* Intr. stat */
+
+/*
+ * AHA_CTRL_STAT bits (write)
+ */
+
+#define AHA_HRST 0x80 /* Hardware reset */
+#define AHA_SRST 0x40 /* Software reset */
+#define AHA_IRST 0x20 /* Interrupt reset */
+#define AHA_SCRST 0x10 /* SCSI bus reset */
+
+/*
+ * AHA_CTRL_STAT bits (read)
+ */
+
+#define AHA_STST 0x80 /* Self test in Progress */
+#define AHA_DIAGF 0x40 /* Diagnostic Failure */
+#define AHA_INIT 0x20 /* Mbx Init required */
+#define AHA_IDLE 0x10 /* Host Adapter Idle */
+#define AHA_CDF 0x08 /* cmd/data out port full */
+#define AHA_DF 0x04 /* Data in port full */
+#define AHA_INVDCMD 0x01 /* Invalid command */
+
+/*
+ * AHA_CMD_DATA bits (write)
+ */
+
+#define AHA_NOP 0x00 /* No operation */
+#define AHA_MBX_INIT 0x01 /* Mbx initialization */
+#define AHA_START_SCSI 0x02 /* start scsi command */
+#define AHA_START_BIOS 0x03 /* start bios command */
+#define AHA_INQUIRE 0x04 /* Adapter Inquiry */
+#define AHA_MBO_INTR_EN 0x05 /* Enable MBO available interrupt */
+#define AHA_SEL_TIMEOUT_SET 0x06 /* set selection time-out */
+#define AHA_BUS_ON_TIME_SET 0x07 /* set bus-on time */
+#define AHA_BUS_OFF_TIME_SET 0x08 /* set bus-off time */
+#define AHA_SPEED_SET 0x09 /* set transfer speed */
+#define AHA_DEV_GET 0x0a /* return installed devices */
+#define AHA_CONF_GET 0x0b /* return configuration data */
+#define AHA_TARGET_EN 0x0c /* enable target mode */
+#define AHA_SETUP_GET 0x0d /* return setup data */
+#define AHA_WRITE_CH2 0x1a /* write channel 2 buffer */
+#define AHA_READ_CH2 0x1b /* read channel 2 buffer */
+#define AHA_WRITE_FIFO 0x1c /* write fifo buffer */
+#define AHA_READ_FIFO 0x1d /* read fifo buffer */
+#define AHA_ECHO 0x1e /* Echo command data */
+
+struct aha_cmd_buf {
+ u_char byte[16];
+};
+
+/*
+ * AHA_INTR_PORT bits (read)
+ */
+
+#define AHA_ANY_INTR 0x80 /* Any interrupt */
+#define AHA_SCRD 0x08 /* SCSI reset detected */
+#define AHA_HACC 0x04 /* Command complete */
+#define AHA_MBOA 0x02 /* MBX out empty */
+#define AHA_MBIF 0x01 /* MBX in full */
+
+/*
+ * Mail box defs
+ */
+
+#define AHA_MBX_SIZE 16 /* mail box size */
+
+struct aha_mbx {
+ struct aha_mbx_out {
+ unsigned char cmd;
+ unsigned char ccb_addr[3];
+ } mbo [AHA_MBX_SIZE];
+ struct aha_mbx_in{
+ unsigned char stat;
+ unsigned char ccb_addr[3];
+ } mbi[AHA_MBX_SIZE];
+};
+
+/*
+ * mbo.cmd values
+ */
+
+#define AHA_MBO_FREE 0x0 /* MBO entry is free */
+#define AHA_MBO_START 0x1 /* MBO activate entry */
+#define AHA_MBO_ABORT 0x2 /* MBO abort entry */
+
+#define AHA_MBI_FREE 0x0 /* MBI entry is free */
+#define AHA_MBI_OK 0x1 /* completed without error */
+#define AHA_MBI_ABORT 0x2 /* aborted ccb */
+#define AHA_MBI_UNKNOWN 0x3 /* Tried to abort invalid CCB */
+#define AHA_MBI_ERROR 0x4 /* Completed with error */
+
+extern struct aha_mbx aha_mbx[];
+
+/* FOR OLD VERSIONS OF THE !%$@ this may have to be 16 (yuk) */
+#define AHA_NSEG 17 /* Number of scatter gather segments <= 16 */
+ /* allow 64 K i/o (min) */
+
+struct aha_ccb {
+ unsigned char opcode;
+ unsigned char lun:3;
+ unsigned char data_in:1; /* must be 0 */
+ unsigned char data_out:1; /* must be 0 */
+ unsigned char target:3;
+ unsigned char scsi_cmd_length;
+ unsigned char req_sense_length;
+ unsigned char data_length[3];
+ unsigned char data_addr[3];
+ unsigned char link_addr[3];
+ unsigned char link_id;
+ unsigned char host_stat;
+ unsigned char target_stat;
+ unsigned char reserved[2];
+ struct scsi_generic scsi_cmd;
+ struct scsi_sense_data scsi_sense;
+ struct aha_scat_gath {
+ unsigned char seg_len[3];
+ unsigned char seg_addr[3];
+ } scat_gath[AHA_NSEG];
+ struct aha_ccb *next;
+ struct scsi_xfer *xfer; /* the scsi_xfer for this cmd */
+ struct aha_mbx_out *mbx; /* pointer to mail box */
+ long int delta; /* difference from previous*/
+ struct aha_ccb *later,*sooner;
+ int flags;
+#define CCB_FREE 0
+#define CCB_ACTIVE 1
+#define CCB_ABORTED 2
+
+};
+
+struct aha_ccb *aha_soonest = (struct aha_ccb *)0;
+struct aha_ccb *aha_latest = (struct aha_ccb *)0;
+long int aha_furtherest = 0; /* longest time in the timeout queue */
+
+/*
+ * opcode fields
+ */
+
+#define AHA_INITIATOR_CCB 0x00 /* SCSI Initiator CCB */
+#define AHA_TARGET_CCB 0x01 /* SCSI Target CCB */
+#define AHA_INIT_SCAT_GATH_CCB 0x02 /* SCSI Initiator with scattter gather*/
+#define AHA_RESET_CCB 0x81 /* SCSI Bus reset */
+
+
+/*
+ * aha_ccb.host_stat values
+ */
+
+#define AHA_OK 0x00 /* cmd ok */
+#define AHA_LINK_OK 0x0a /* Link cmd ok */
+#define AHA_LINK_IT 0x0b /* Link cmd ok + int */
+#define AHA_SEL_TIMEOUT 0x11 /* Selection time out */
+#define AHA_OVER_UNDER 0x12 /* Data over/under run */
+#define AHA_BUS_FREE 0x13 /* Bus dropped at unexpected time */
+#define AHA_INV_BUS 0x14 /* Invalid bus phase/sequence */
+#define AHA_BAD_MBO 0x15 /* Incorrect MBO cmd */
+#define AHA_BAD_CCB 0x16 /* Incorrect ccb opcode */
+#define AHA_BAD_LINK 0x17 /* Not same values of LUN for links */
+#define AHA_INV_TARGET 0x18 /* Invalid target direction */
+#define AHA_CCB_DUP 0x19 /* Duplicate CCB received */
+#define AHA_INV_CCB 0x1a /* Invalid CCB or segment list */
+#define AHA_ABORTED 42
+
+
+
+
+struct aha_setup
+{
+ u_char sync_neg:1;
+ u_char parity:1;
+ u_char :6;
+ u_char speed;
+ u_char bus_on;
+ u_char bus_off;
+ u_char num_mbx;
+ u_char mbx[3];
+ struct
+ {
+ u_char offset:4;
+ u_char period:3;
+ u_char valid:1;
+ }sync[8];
+ u_char disc_sts;
+};
+
+struct aha_config
+{
+ u_char chan;
+ u_char intr;
+ u_char scsi_dev:3;
+ u_char :5;
+};
+
+#define INT9 0x01
+#define INT10 0x02
+#define INT11 0x04
+#define INT12 0x08
+#define INT14 0x20
+#define INT15 0x40
+
+#define CHAN0 0x01
+#define CHAN5 0x20
+#define CHAN6 0x40
+#define CHAN7 0x80
+
+
+/*********************************** end of board definitions***************/
+
+
+#ifdef MACH
+#define PHYSTOKV(x) phystokv(x)
+#define KVTOPHYS(x) kvtophys(x)
+#else MACH
+#ifdef __386BSD__
+#define PHYSTOKV(x) (x | 0xFE000000)
+#define KVTOPHYS(x) vtophys(x)
+#else __386BSD__
+#endif __386BSD__
+#endif MACH
+#define AHA_DMA_PAGES AHA_NSEG
+
+#define PAGESIZ 4096
+#define INVALIDATE_CACHE {asm volatile( ".byte 0x0F ;.byte 0x08" ); }
+
+
+u_char aha_scratch_buf[256];
+#ifdef MACH
+caddr_t aha_base[NAHA]; /* base port for each board */
+#else
+short aha_base[NAHA]; /* base port for each board */
+#endif
+struct aha_mbx aha_mbx[NAHA];
+struct aha_ccb *aha_ccb_free[NAHA];
+struct aha_ccb aha_ccb[NAHA][AHA_MBX_SIZE];
+struct scsi_xfer aha_scsi_xfer[NAHA];
+struct isa_dev *ahainfo[NAHA];
+struct aha_ccb *aha_get_ccb();
+int aha_int[NAHA];
+int aha_dma[NAHA];
+int aha_scsi_dev[NAHA];
+int aha_initialized[NAHA];
+#ifdef OSF
+int aha_attached[NAHA];
+#endif OSF
+int aha_debug = 0;
+
+int ahaprobe(), ahaattach(), ahaintr();
+#ifdef MACH
+struct isa_driver ahadriver = { ahaprobe, 0, ahaattach, "aha", 0, 0, 0};
+int (*ahaintrs[])() = {ahaintr, 0};
+#endif
+#ifdef __386BSD__
+struct isa_driver ahadriver = { ahaprobe, ahaattach, "aha",};
+#endif __386BSD__
+static int ahaunit = 0;
+
+
+#define aha_abortmbx(mbx) \
+ (mbx)->cmd = AHA_MBO_ABORT; \
+ outb(AHA_CMD_DATA_PORT, AHA_START_SCSI);
+#define aha_startmbx(mbx) \
+ (mbx)->cmd = AHA_MBO_START; \
+ outb(AHA_CMD_DATA_PORT, AHA_START_SCSI);
+
+
+
+int aha_scsi_cmd();
+int aha_timeout();
+void ahaminphys();
+long int aha_adapter_info();
+
+struct scsi_switch aha_switch =
+{
+ aha_scsi_cmd,
+ ahaminphys,
+ 0,
+ 0,
+ aha_adapter_info,
+ 0,0,0
+};
+#define AHA_CMD_TIMEOUT_FUDGE 200 /* multiplied to get Secs */
+#define AHA_RESET_TIMEOUT 1000000 /* time to wait for reset */
+#define AHA_SCSI_TIMEOUT_FUDGE 20 /* divided by for mSecs */
+
+
+/***********************************************************************\
+* aha_cmd(unit,icnt, ocnt,wait, retval, opcode, args) *
+* Activate Adapter command *
+* icnt: number of args (outbound bytes written after opcode) *
+* ocnt: number of expected returned bytes *
+* wait: number of seconds to wait for response *
+* retval: buffer where to place returned bytes *
+* opcode: opcode AHA_NOP, AHA_MBX_INIT, AHA_START_SCSI ... *
+* args: parameters *
+* *
+* Performs an adapter command through the ports. Not to be confused *
+* with a scsi command, which is read in via the dma *
+* One of the adapter commands tells it to read in a scsi command *
+\***********************************************************************/
+
+
+aha_cmd(unit,icnt, ocnt, wait,retval, opcode, args)
+
+u_char *retval;
+unsigned opcode;
+u_char args;
+{
+ unsigned *ic = &opcode;
+ u_char oc;
+ register i;
+ int sts;
+
+ /*******************************************************\
+ * multiply the wait argument by a big constant *
+ * zero defaults to 1 *
+ \*******************************************************/
+ if(!wait)
+ wait = AHA_CMD_TIMEOUT_FUDGE * delaycount;
+ else
+ wait *= AHA_CMD_TIMEOUT_FUDGE * delaycount;
+ /*******************************************************\
+ * Wait for the adapter to go idle, unless it's one of *
+ * the commands which don't need this *
+ \*******************************************************/
+ if (opcode != AHA_MBX_INIT && opcode != AHA_START_SCSI)
+ {
+ i = AHA_CMD_TIMEOUT_FUDGE * delaycount; /* 1 sec?*/
+ while (--i)
+ {
+ sts = inb(AHA_CTRL_STAT_PORT);
+ if (sts & AHA_IDLE)
+ {
+ break;
+ }
+ }
+ if (!i)
+ {
+ printf("aha_cmd: aha1542 host not idle(0x%x)\n",sts);
+ return(ENXIO);
+ }
+ }
+ /*******************************************************\
+ * Now that it is idle, if we expect output, preflush the*
+ * queue feeding to us. *
+ \*******************************************************/
+ if (ocnt)
+ {
+ while((inb(AHA_CTRL_STAT_PORT)) & AHA_DF)
+ inb(AHA_CMD_DATA_PORT);
+ }
+
+ /*******************************************************\
+ * Output the command and the number of arguments given *
+ * for each byte, first check the port is empty. *
+ \*******************************************************/
+ icnt++; /* include the command */
+ while (icnt--)
+ {
+ sts = inb(AHA_CTRL_STAT_PORT);
+ for (i=0; i< wait; i++)
+ {
+ sts = inb(AHA_CTRL_STAT_PORT);
+ if (!(sts & AHA_CDF))
+ break;
+ }
+ if (i >= wait)
+ {
+ printf("aha_cmd: aha1542 cmd/data port full\n");
+ outb(AHA_CTRL_STAT_PORT, AHA_SRST);
+ return(ENXIO);
+ }
+ outb(AHA_CMD_DATA_PORT, (u_char)(*ic++));
+ }
+ /*******************************************************\
+ * If we expect input, loop that many times, each time, *
+ * looking for the data register to have valid data *
+ \*******************************************************/
+ while (ocnt--)
+ {
+ sts = inb(AHA_CTRL_STAT_PORT);
+ for (i=0; i< wait; i++)
+ {
+ sts = inb(AHA_CTRL_STAT_PORT);
+ if (sts & AHA_DF)
+ break;
+ }
+ if (i >= wait)
+ {
+ printf("aha_cmd: aha1542 cmd/data port empty %d\n",ocnt);
+ return(ENXIO);
+ }
+ oc = inb(AHA_CMD_DATA_PORT);
+ if (retval)
+ *retval++ = oc;
+ }
+ /*******************************************************\
+ * Wait for the board to report a finised instruction *
+ \*******************************************************/
+ i=AHA_CMD_TIMEOUT_FUDGE * delaycount; /* 1 sec? */
+ while (--i)
+ {
+ sts = inb(AHA_INTR_PORT);
+ if (sts & AHA_HACC)
+ {
+ break;
+ }
+ }
+ if (!i)
+ {
+ printf("aha_cmd: aha1542 host not finished(0x%x)\n",sts);
+ return(ENXIO);
+ }
+ outb(AHA_CTRL_STAT_PORT, AHA_IRST);
+ return(0);
+}
+
+/*******************************************************\
+* Check if the device can be found at the port given *
+* and if so, set it up ready for further work *
+* as an argument, takes the isa_dev structure from *
+* autoconf.c *
+\*******************************************************/
+ahaprobe(dev)
+struct isa_dev *dev;
+{
+ int unit = ahaunit;
+#if defined(OSF)
+ static ihandler_t aha_handler[NAHA];
+ static ihandler_id_t *aha_handler_id[NAHA];
+ register ihandler_t *chp = &aha_handler[unit];;
+#endif /* defined(OSF) */
+
+ /***********************************************\
+ /***********************************************\
+ * find unit and check we have that many defined *
+ \***********************************************/
+ dev->dev_unit = unit;
+ aha_base[unit] = dev->dev_addr;
+ if(unit >= NAHA)
+ {
+ printf("aha: unit number (%d) too high\n",unit);
+ return(0);
+ }
+ /***********************************************\
+ * Try initialise a unit at this location *
+ * sets up dma and bus speed, loads aha_int[unit]*
+ \***********************************************/
+ if (aha_init(unit) != 0)
+ {
+ return(0);
+ }
+
+ /***********************************************\
+ * If it's there, put in it's interrupt vectors *
+ \***********************************************/
+#if !defined(OSF)
+#if defined MACH
+ iunit[aha_int[unit]] =unit;
+ ivect[aha_int[unit]] = ahaintr;
+ intpri[aha_int[unit]] = dev->dev_spl;
+ form_pic_mask();
+ /*take_dev_irq(dev);*/
+#else
+#ifdef __386BSD__
+ dev->id_irq = (1 << aha_int[unit]);
+ dev->id_drq = aha_dma[unit];
+#endif __386BSD__
+#endif
+#else /* !defined(OSF) */
+
+ chp->ih_level = dev->dev_pic;
+ chp->ih_handler = dev->dev_intr[0];
+ chp->ih_resolver = i386_resolver;
+ chp->ih_rdev = dev;
+ chp->ih_stats.intr_type = INTR_DEVICE;
+ chp->ih_stats.intr_cnt = 0;
+ chp->ih_hparam[0].intparam = unit;
+ if ((aha_handler_id[unit] = handler_add(chp)) != NULL)
+ handler_enable(aha_handler_id[unit]);
+ else
+ panic("Unable to add aha interrupt handler");
+#endif /* !defined(OSF) */
+#ifdef __386BSD__
+ printf("\n **");
+#else __386BSD__
+ printf("port=%x spl=%d\n",
+ dev->dev_addr, dev->dev_spl);
+#endif __386BSD__
+ ahaunit ++;
+ return(1);
+}
+
+/***********************************************\
+* Attach all the sub-devices we can find *
+\***********************************************/
+ahaattach(dev)
+struct isa_dev *dev;
+{
+ int unit = dev->dev_unit;
+
+#ifdef __386BSD__
+ printf(" probing for scsi devices**\n");
+#endif __386BSD__
+ /***********************************************\
+ * ask the adapter what subunits are present *
+ \***********************************************/
+ scsi_attachdevs( unit, aha_scsi_dev[unit], &aha_switch);
+#if defined(OSF)
+ aha_attached[unit]=1;
+#endif /* defined(OSF) */
+ if(!unit) /* only one for all boards */
+ {
+ aha_timeout(0);
+ }
+#ifdef __386BSD__
+ printf("aha%d",unit);
+#endif __386BSD__
+ return;
+
+}
+
+
+/***********************************************\
+* Return some information to the caller about *
+* the adapter and it's capabilities *
+\***********************************************/
+long int aha_adapter_info(unit)
+int unit;
+{
+ return(2); /* 2 outstanding requests at a time per device */
+}
+
+/***********************************************\
+* Catch an interrupt from the adaptor *
+\***********************************************/
+ahaintr(unit)
+{
+ struct aha_ccb *ccb;
+ unsigned char stat;
+ register i;
+
+ if(scsi_debug & PRINTROUTINES)
+ printf("ahaintr ");
+ /***********************************************\
+ * First acknowlege the interrupt, Then if it's *
+ * not telling about a completed operation *
+ * just return. *
+ \***********************************************/
+ stat = inb(AHA_INTR_PORT);
+ outb(AHA_CTRL_STAT_PORT, AHA_IRST);
+ if(scsi_debug & TRACEINTERRUPTS)
+ printf("int ");
+ if (! (stat & AHA_MBIF))
+ return(1);
+ if(scsi_debug & TRACEINTERRUPTS)
+ printf("b ");
+#if defined(OSF)
+ if (!aha_attached[unit])
+ {
+ return(1);
+ }
+#endif /* defined(OSF) */
+ /***********************************************\
+ * If it IS then process the competed operation *
+ \***********************************************/
+ for (i = 0; i < AHA_MBX_SIZE; i++)
+ {
+ if (aha_mbx[unit].mbi[i].stat != AHA_MBI_FREE)
+ {
+ ccb = (struct aha_ccb *)PHYSTOKV(
+ (_3btol(aha_mbx[unit].mbi[i].ccb_addr)));
+
+ if((stat = aha_mbx[unit].mbi[i].stat) != AHA_MBI_OK)
+ {
+ switch(stat)
+ {
+ case AHA_MBI_ABORT:
+ if(aha_debug)
+ printf("abort");
+ ccb->host_stat = AHA_ABORTED;
+ break;
+
+ case AHA_MBI_UNKNOWN:
+ ccb = (struct aha_ccb *)0;
+ if(aha_debug)
+ printf("unknown ccb for abort ");
+ /* may have missed it */
+ /* no such ccb known for abort */
+
+ case AHA_MBI_ERROR:
+ break;
+
+ default:
+ panic("Impossible mbxi status");
+
+ }
+ if( aha_debug && ccb )
+ {
+ u_char *cp;
+ cp = (u_char *)(&(ccb->scsi_cmd));
+ printf("op=%x %x %x %x %x %x\n",
+ cp[0], cp[1], cp[2],
+ cp[3], cp[4], cp[5]);
+ printf("stat %x for mbi[%d]\n"
+ , aha_mbx[unit].mbi[i].stat, i);
+ printf("addr = 0x%x\n", ccb);
+ }
+ }
+ if(ccb)
+ {
+ aha_remove_timeout(ccb);
+ aha_done(unit,ccb);
+ }
+ aha_mbx[unit].mbi[i].stat = AHA_MBI_FREE;
+ }
+ }
+ return(1);
+}
+
+/***********************************************\
+* A ccb (and hence a mbx-out is put onto the *
+* free list. *
+\***********************************************/
+aha_free_ccb(unit,ccb, flags)
+struct aha_ccb *ccb;
+{
+ unsigned int opri;
+
+ if(scsi_debug & PRINTROUTINES)
+ printf("ccb%d(0x%x)> ",unit,flags);
+ if (!(flags & SCSI_NOMASK))
+ opri = splbio();
+
+ ccb->next = aha_ccb_free[unit];
+ aha_ccb_free[unit] = ccb;
+ ccb->flags = CCB_FREE;
+ if(ccb->sooner || ccb->later)
+ {
+ printf("yikes, still in timeout queue\n");
+ aha_remove_timeout(ccb);
+ }
+ /***********************************************\
+ * If there were none, wake abybody waiting for *
+ * one to come free, starting with queued entries*
+ \***********************************************/
+ if (!ccb->next) {
+ wakeup(&aha_ccb_free[unit]);
+ }
+ if (!(flags & SCSI_NOMASK))
+ splx(opri);
+}
+
+/***********************************************\
+* Get a free ccb (and hence mbox-out entry) *
+\***********************************************/
+struct aha_ccb *
+aha_get_ccb(unit,flags)
+{
+ unsigned opri;
+ struct aha_ccb *rc;
+
+ if(scsi_debug & PRINTROUTINES)
+ printf("<ccb%d(0x%x) ",unit,flags);
+ if (!(flags & SCSI_NOMASK))
+ opri = splbio();
+ /***********************************************\
+ * If we can and have to, sleep waiting for one *
+ * to come free *
+ \***********************************************/
+ while ((!(rc = aha_ccb_free[unit])) && (!(flags & SCSI_NOSLEEP)))
+ {
+ sleep(&aha_ccb_free[unit], PRIBIO);
+ }
+ if (rc)
+ {
+ aha_ccb_free[unit] = aha_ccb_free[unit]->next;
+ rc->flags = CCB_ACTIVE;
+ }
+ if (!(flags & SCSI_NOMASK))
+ splx(opri);
+ return(rc);
+}
+
+
+/***********************************************\
+* We have a ccb which has been processed by the *
+* adaptor, now we look to see how the operation *
+* went. Wake up the owner if waiting *
+\***********************************************/
+aha_done(unit,ccb)
+struct aha_ccb *ccb;
+{
+ struct scsi_sense_data *s1,*s2;
+ struct scsi_xfer *xs = ccb->xfer;
+
+ if(scsi_debug & PRINTROUTINES )
+ printf("aha_done ");
+ /***********************************************\
+ * Otherwise, put the results of the operation *
+ * into the xfer and call whoever started it *
+ \***********************************************/
+ if(!(xs->flags & INUSE))
+ {
+ printf("exiting but not in use! ");
+ Debugger();
+ }
+ if ( ( ccb->host_stat != AHA_OK
+ || ccb->target_stat != SCSI_OK)
+ && (!(xs->flags & SCSI_ERR_OK)))
+ {
+ s1 = (struct scsi_sense_data *)(((char *)(&ccb->scsi_cmd))
+ + ccb->scsi_cmd_length);
+ s2 = &(xs->sense);
+
+ if(ccb->host_stat)
+ {
+ switch(ccb->host_stat)
+ {
+ case AHA_ABORTED:
+ case AHA_SEL_TIMEOUT: /* No response */
+ xs->error = XS_TIMEOUT;
+ break;
+ default: /* Other scsi protocol messes */
+ xs->error = XS_DRIVER_STUFFUP;
+ if (aha_debug > 1)
+ {
+ printf("host_stat%x\n",
+ ccb->host_stat);
+ }
+ }
+
+ }
+ else
+ {
+ switch(ccb->target_stat)
+ {
+ case 0x02:
+ /* structure copy!!!!!*/
+ *s2=*s1;
+ xs->error = XS_SENSE;
+ break;
+ case 0x08:
+ xs->error = XS_BUSY;
+ break;
+ default:
+ if (aha_debug > 1)
+ {
+ printf("target_stat%x\n",
+ ccb->target_stat);
+ }
+ xs->error = XS_DRIVER_STUFFUP;
+ }
+ }
+ }
+ else /* All went correctly OR errors expected */
+ {
+ xs->resid = 0;
+ }
+ xs->flags |= ITSDONE;
+ aha_free_ccb(unit,ccb, xs->flags);
+ if(xs->when_done)
+ (*(xs->when_done))(xs->done_arg,xs->done_arg2);
+}
+
+
+/***********************************************\
+* Start the board, ready for normal operation *
+\***********************************************/
+aha_init(unit)
+int unit;
+{
+ unsigned char ad[3];
+ volatile int i,sts;
+ struct aha_config conf;
+
+ /***********************************************\
+ * reset board, If it doesn't respond, assume *
+ * that it's not there.. good for the probe *
+ \***********************************************/
+
+ outb(AHA_CTRL_STAT_PORT, AHA_HRST|AHA_SRST);
+
+ for (i=0; i < AHA_RESET_TIMEOUT; i++)
+ {
+ sts = inb(AHA_CTRL_STAT_PORT) ;
+ if ( sts == (AHA_IDLE | AHA_INIT))
+ break;
+ }
+ if (i >= AHA_RESET_TIMEOUT)
+ {
+ if (aha_debug)
+ printf("aha_init: No answer from adaptec board\n");
+ return(ENXIO);
+ }
+
+ /***********************************************\
+ * Assume we have a board at this stage *
+ * setup dma channel from jumpers and save int *
+ * level *
+ \***********************************************/
+#ifdef __386BSD__
+ printf("aha%d reading board settings, ",unit);
+#define PRNT(x)
+#else __386BSD__
+ printf("aha%d:",unit);
+#define PRNT(x) printf(x)
+#endif __386BSD__
+ aha_cmd(unit,0, sizeof(conf), 0 ,&conf, AHA_CONF_GET);
+ switch(conf.chan)
+ {
+ case CHAN0:
+ outb(0x0b, 0x0c);
+ outb(0x0a, 0x00);
+ aha_dma[unit] = 0;
+ PRNT("dma=0 ");
+ break;
+ case CHAN5:
+ outb(0xd6, 0xc1);
+ outb(0xd4, 0x01);
+ aha_dma[unit] = 5;
+ PRNT("dma=5 ");
+ break;
+ case CHAN6:
+ outb(0xd6, 0xc2);
+ outb(0xd4, 0x02);
+ aha_dma[unit] = 6;
+ PRNT("dma=6 ");
+ break;
+ case CHAN7:
+ outb(0xd6, 0xc3);
+ outb(0xd4, 0x03);
+ aha_dma[unit] = 7;
+ PRNT("dma=7 ");
+ break;
+ default:
+ printf("illegal dma jumper setting\n");
+ return(EIO);
+ }
+ switch(conf.intr)
+ {
+ case INT9:
+ aha_int[unit] = 9;
+ PRNT("int=9 ");
+ break;
+ case INT10:
+ aha_int[unit] = 10;
+ PRNT("int=10 ");
+ break;
+ case INT11:
+ aha_int[unit] = 11;
+ PRNT("int=11 ");
+ break;
+ case INT12:
+ aha_int[unit] = 12;
+ PRNT("int=12 ");
+ break;
+ case INT14:
+ aha_int[unit] = 14;
+ PRNT("int=14 ");
+ break;
+ case INT15:
+ aha_int[unit] = 15;
+ PRNT("int=15 ");
+ break;
+ default:
+ printf("illegal int jumper setting\n");
+ return(EIO);
+ }
+ /* who are we on the scsi bus */
+ aha_scsi_dev[unit] = conf.scsi_dev;
+
+
+ /***********************************************\
+ * Initialize memory transfer speed *
+ \***********************************************/
+ if(!(aha_set_bus_speed(unit)))
+ {
+ return(EIO);
+ }
+
+
+ /***********************************************\
+ * Initialize mail box *
+ \***********************************************/
+
+ lto3b(KVTOPHYS(&aha_mbx[unit]), ad);
+
+ aha_cmd(unit,4, 0, 0, 0, AHA_MBX_INIT,
+ AHA_MBX_SIZE,
+ ad[0],
+ ad[1],
+ ad[2]);
+
+
+ /***********************************************\
+ * link the ccb's with the mbox-out entries and *
+ * into a free-list *
+ \***********************************************/
+ for (i=0; i < AHA_MBX_SIZE; i++) {
+ aha_ccb[unit][i].next = aha_ccb_free[unit];
+ aha_ccb_free[unit] = &aha_ccb[unit][i];
+ aha_ccb_free[unit]->flags = CCB_FREE;
+ aha_ccb_free[unit]->mbx = &aha_mbx[unit].mbo[i];
+ lto3b(KVTOPHYS(aha_ccb_free[unit]), aha_mbx[unit].mbo[i].ccb_addr);
+ }
+
+ /***********************************************\
+ * Note that we are going and return (to probe) *
+ \***********************************************/
+ aha_initialized[unit]++;
+ return(0);
+}
+
+
+
+
+
+void ahaminphys(bp)
+struct buf *bp;
+{
+#ifdef MACH
+#if !defined(OSF)
+ bp->b_flags |= B_NPAGES; /* can support scat/gather */
+#endif /* !defined(OSF) */
+#endif MACH
+/* aha seems to explode with 17 segs (64k may require 17 segs) */
+/* on old boards so use a max of 16 segs if you have problems here*/
+ if(bp->b_bcount > ((AHA_NSEG - 1) * PAGESIZ))
+ {
+ bp->b_bcount = ((AHA_NSEG - 1) * PAGESIZ);
+ }
+}
+
+/***********************************************\
+* start a scsi operation given the command and *
+* the data address. Also needs the unit, target *
+* and lu *
+\***********************************************/
+int aha_scsi_cmd(xs)
+struct scsi_xfer *xs;
+{
+ struct scsi_sense_data *s1,*s2;
+ struct aha_ccb *ccb;
+ struct aha_scat_gath *sg;
+ int seg; /* scatter gather seg being worked on */
+ int i = 0;
+ int rc = 0;
+ int thiskv;
+ int thisphys,nextphys;
+ int unit =xs->adapter;
+ int bytes_this_seg,bytes_this_page,datalen,flags;
+ struct iovec *iovp;
+ int s;
+
+ if(scsi_debug & PRINTROUTINES)
+ printf("aha_scsi_cmd ");
+ /***********************************************\
+ * get a ccb (mbox-out) to use. If the transfer *
+ * is from a buf (possibly from interrupt time) *
+ * then we can't allow it to sleep *
+ \***********************************************/
+ flags = xs->flags;
+ if(!(flags & INUSE))
+ {
+ printf("not in use!");
+ Debugger();
+ xs->flags |= INUSE;
+ }
+ if(flags & ITSDONE)
+ {
+ printf("Already done! check device retry code ");
+ Debugger();
+ xs->flags &= ~ITSDONE;
+ }
+ if(xs->bp) flags |= (SCSI_NOSLEEP); /* just to be sure */
+ if (!(ccb = aha_get_ccb(unit,flags)))
+ {
+ xs->error = XS_DRIVER_STUFFUP;
+ return(TRY_AGAIN_LATER);
+ }
+
+ if (ccb->mbx->cmd != AHA_MBO_FREE)
+ printf("MBO not free\n");
+
+ /***********************************************\
+ * Put all the arguments for the xfer in the ccb *
+ \***********************************************/
+ ccb->xfer = xs;
+ if(flags & SCSI_RESET)
+ {
+ ccb->opcode = AHA_RESET_CCB;
+ }
+ else
+ {
+ /* can't use S/G if zero length */
+ ccb->opcode = (xs->datalen?
+ AHA_INIT_SCAT_GATH_CCB
+ :AHA_INITIATOR_CCB);
+ }
+ ccb->target = xs->targ;;
+ ccb->data_out = 0;
+ ccb->data_in = 0;
+ ccb->lun = xs->lu;
+ ccb->scsi_cmd_length = xs->cmdlen;
+ ccb->req_sense_length = sizeof(ccb->scsi_sense);
+
+ if((xs->datalen) && (!(flags & SCSI_RESET)))
+ { /* can use S/G only if not zero length */
+ lto3b(KVTOPHYS(ccb->scat_gath), ccb->data_addr );
+ sg = ccb->scat_gath ;
+ seg = 0;
+ if(flags & SCSI_DATA_UIO)
+ {
+ iovp = ((struct uio *)xs->data)->uio_iov;
+ datalen = ((struct uio *)xs->data)->uio_iovcnt;
+ while ((datalen) && (seg < AHA_NSEG))
+ {
+ lto3b(iovp->iov_base,&(sg->seg_addr));
+ lto3b(iovp->iov_len,&(sg->seg_len));
+ if(scsi_debug & SHOWSCATGATH)
+ printf("(0x%x@0x%x)"
+ ,iovp->iov_len
+ ,iovp->iov_base);
+ sg++;
+ iovp++;
+ seg++;
+ datalen--;
+ }
+ }
+ else
+ {
+ /***********************************************\
+ * Set up the scatter gather block *
+ \***********************************************/
+
+ if(scsi_debug & SHOWSCATGATH)
+ printf("%d @0x%x:- ",xs->datalen,xs->data);
+ datalen = xs->datalen;
+ thiskv = (int)xs->data;
+ thisphys = KVTOPHYS(thiskv);
+
+ while ((datalen) && (seg < AHA_NSEG))
+ {
+ bytes_this_seg = 0;
+
+ /* put in the base address */
+ lto3b(thisphys,&(sg->seg_addr));
+
+ if(scsi_debug & SHOWSCATGATH)
+ printf("0x%x",thisphys);
+
+ /* do it at least once */
+ nextphys = thisphys;
+ while ((datalen) && (thisphys == nextphys))
+ /***************************************\
+ * This page is contiguous (physically) *
+ * with the the last, just extend the *
+ * length *
+ \***************************************/
+ {
+ /** how far to the end of the page ***/
+ nextphys = (thisphys & (~(PAGESIZ - 1)))
+ + PAGESIZ;
+ bytes_this_page = nextphys - thisphys;
+ /**** or the data ****/
+ bytes_this_page = min(bytes_this_page
+ ,datalen);
+ bytes_this_seg += bytes_this_page;
+ datalen -= bytes_this_page;
+
+ /**** get more ready for the next page ****/
+ thiskv = (thiskv & (~(PAGESIZ - 1)))
+ + PAGESIZ;
+ if(datalen)
+ thisphys = KVTOPHYS(thiskv);
+ }
+ /***************************************\
+ * next page isn't contiguous, finish the seg*
+ \***************************************/
+ if(scsi_debug & SHOWSCATGATH)
+ printf("(0x%x)",bytes_this_seg);
+ lto3b(bytes_this_seg,&(sg->seg_len));
+ sg++;
+ seg++;
+ }
+ }
+ lto3b(seg * sizeof(struct aha_scat_gath),ccb->data_length);
+ if(scsi_debug & SHOWSCATGATH)
+ printf("\n");
+ if (datalen)
+ { /* there's still data, must have run out of segs! */
+ printf("aha_scsi_cmd%d: more than %d DMA segs\n",
+ unit,AHA_NSEG);
+ xs->error = XS_DRIVER_STUFFUP;
+ aha_free_ccb(unit,ccb,flags);
+ return(HAD_ERROR);
+ }
+
+ }
+ else
+ { /* No data xfer, use non S/G values */
+ lto3b(0, ccb->data_addr );
+ lto3b(0,ccb->data_length);
+ }
+ lto3b(0, ccb->link_addr );
+ /***********************************************\
+ * Put the scsi command in the ccb and start it *
+ \***********************************************/
+ if(!(flags & SCSI_RESET))
+ bcopy(xs->cmd, &ccb->scsi_cmd, ccb->scsi_cmd_length);
+ if(scsi_debug & SHOWCOMMANDS)
+ {
+ u_char *b = (u_char *)&ccb->scsi_cmd;
+ if(!(flags & SCSI_RESET))
+ {
+ int i = 0;
+ printf("aha%d:%d:%d-"
+ ,unit
+ ,ccb->target
+ ,ccb->lun );
+ while(i < ccb->scsi_cmd_length )
+ {
+ if(i) printf(",");
+ printf("%x",b[i++]);
+ }
+ }
+ else
+ {
+ printf("aha%d:%d:%d-RESET- "
+ ,unit
+ ,ccb->target
+ ,ccb->lun
+ );
+ }
+ }
+ if (!(flags & SCSI_NOMASK))
+ {
+ s= splbio(); /* stop instant timeouts */
+ aha_add_timeout(ccb,xs->timeout);
+ aha_startmbx(ccb->mbx);
+ /***********************************************\
+ * Usually return SUCCESSFULLY QUEUED *
+ \***********************************************/
+ splx(s);
+ if(scsi_debug & TRACEINTERRUPTS)
+ printf("sent ");
+ return(SUCCESSFULLY_QUEUED);
+ }
+ aha_startmbx(ccb->mbx);
+ if(scsi_debug & TRACEINTERRUPTS)
+ printf("cmd_sent, waiting ");
+ /***********************************************\
+ * If we can't use interrupts, poll on completion*
+ \***********************************************/
+ {
+ int done = 0;
+ int count = delaycount * xs->timeout / AHA_SCSI_TIMEOUT_FUDGE;
+ while((!done) && count)
+ {
+ i=0;
+ while ( (!done) && i<AHA_MBX_SIZE)
+ {
+ if ((aha_mbx[unit].mbi[i].stat != AHA_MBI_FREE )
+ && (PHYSTOKV(_3btol(aha_mbx[unit].mbi[i].ccb_addr)
+ == (int)ccb)))
+ {
+ aha_mbx[unit].mbi[i].stat = AHA_MBI_FREE;
+ aha_done(unit,ccb);
+ done++;
+ }
+ i++;
+ }
+ count--;
+ }
+ if (!count)
+ {
+ if (!(xs->flags & SCSI_SILENT))
+ printf("cmd fail\n");
+ aha_abortmbx(ccb->mbx);
+ count = delaycount * 2000 / AHA_SCSI_TIMEOUT_FUDGE;
+ while((!done) && count)
+ {
+ i=0;
+ while ( (!done) && i<AHA_MBX_SIZE)
+ {
+ if ((aha_mbx[unit].mbi[i].stat != AHA_MBI_FREE )
+ && (PHYSTOKV(_3btol(aha_mbx[unit].mbi[i].ccb_addr)
+ == (int)ccb)))
+ {
+ aha_mbx[unit].mbi[i].stat = AHA_MBI_FREE;
+ aha_done(unit,ccb);
+ done++;
+ }
+ i++;
+ }
+ count--;
+ }
+ if(!count)
+ {
+ printf("abort failed in wait\n");
+ ccb->mbx->cmd = AHA_MBO_FREE;
+ }
+ aha_free_ccb(unit,ccb,flags);
+ ahaintr(unit);
+ xs->error = XS_DRIVER_STUFFUP;
+ return(HAD_ERROR);
+ }
+ ahaintr(unit);
+ if(xs->error) return(HAD_ERROR);
+ return(COMPLETE);
+
+ }
+}
+/***************************************************************\
+* try each speed in turn, when we find one that works, use *
+* the NEXT one for a safety margin, unless that doesn't exist *
+* or doesn't work. returns the nSEC value of the time used *
+* or 0 if it could get a working speed ( or the NEXT speed *
+* failed) *
+\***************************************************************/
+
+int aha_set_bus_speed(unit)
+int unit;
+{
+ int speed;
+ int retval,retval2;
+
+#ifdef EISA
+ speed = 0; /* start at the fastest */
+#else EISA
+ speed = 1; /* 100 ns can crash some ISA busses (!?!) */
+#endif EISA
+ while (1)
+ {
+ retval = aha_bus_speed_check(unit,speed);
+ if(retval == HAD_ERROR)
+ {
+ printf("no working bus speed!!!\n");
+ return(0);
+ }
+ if(retval == 0)
+ {
+ speed++;
+ }
+ else /* Go one slower to be safe */
+ { /* unless eisa at 100 ns.. trust it */
+ if(speed != 0)
+ {
+ speed++;
+ }
+ printf("%d nSEC ok, use ",retval);
+ retval2 = aha_bus_speed_check(unit,speed);
+ if(retval2 == HAD_ERROR) /* retval is slowest already */
+ {
+ printf("marginal ");
+ retval2 = retval;
+ }
+ if(retval2)
+ {
+ printf("%d nSEC ",retval2);
+ return(retval2);
+ }
+ else
+ {
+ printf(".. slower failed, abort.\n",retval);
+ return(0);
+ }
+
+ }
+ }
+}
+
+/***************************************************************\
+* Set the DMA speed to the Nth speed and try an xfer. If it *
+* fails return 0, if it succeeds return the nSec value selected *
+* If there is no such speed return HAD_ERROR. *
+\***************************************************************/
+static struct bus_speed
+{
+ char arg;
+ int nsecs;
+}aha_bus_speeds[] =
+{
+ {0x88,100},
+ {0x99,150},
+ {0xaa,200},
+ {0xbb,250},
+ {0xcc,300},
+ {0xdd,350},
+ {0xee,400},
+ {0xff,450}
+};
+static char aha_test_string[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890abcdefghijklmnopqrstuvwxyz!@";
+
+int aha_bus_speed_check(unit,speed)
+int unit,speed;
+{
+ int numspeeds = sizeof(aha_bus_speeds)/sizeof(struct bus_speed);
+ u_char ad[3];
+
+ /*******************************************************\
+ * Check we have such an entry *
+ \*******************************************************/
+ if(speed >= numspeeds) return(HAD_ERROR); /* illegal speed */
+
+ /*******************************************************\
+ * Set the dma-speed *
+ \*******************************************************/
+ aha_cmd(unit,1, 0, 0, 0, AHA_SPEED_SET,aha_bus_speeds[speed].arg);
+
+ /*******************************************************\
+ * put the test data into the buffer and calculate *
+ * it's address. Read it onto the board *
+ \*******************************************************/
+ strcpy(aha_scratch_buf,aha_test_string);
+ lto3b(KVTOPHYS(aha_scratch_buf),ad);
+
+ aha_cmd(unit,3, 0, 0, 0, AHA_WRITE_FIFO, ad[0], ad[1], ad[2]);
+
+ /*******************************************************\
+ * clear the buffer then copy the contents back from the *
+ * board. *
+ \*******************************************************/
+ bzero(aha_scratch_buf,54); /* 54 bytes transfered by test */
+
+ aha_cmd(unit,3, 0, 0, 0, AHA_READ_FIFO, ad[0], ad[1], ad[2]);
+
+ /*******************************************************\
+ * Compare the original data and the final data and *
+ * return the correct value depending upon the result *
+ \*******************************************************/
+ if(strcmp(aha_test_string,aha_scratch_buf))
+ { /* copy failed.. assume too fast */
+ return(0);
+ }
+ else
+ { /* copy succeded assume speed ok */
+ return(aha_bus_speeds[speed].nsecs);
+ }
+}
+
+
+/*
+ * +----------+ +----------+ +----------+
+ * aha_soonest--->| later |--->| later|--->| later|-->0
+ * | [Delta] | | [Delta] | | [Delta] |
+ * 0<---|sooner |<---|sooner |<---|sooner |<---aha_latest
+ * +----------+ +----------+ +----------+
+ *
+ * aha_furtherest = sum(Delta[1..n])
+ */
+aha_add_timeout(ccb,time)
+struct aha_ccb *ccb;
+int time;
+{
+ int timeprev;
+ struct aha_ccb *prev;
+ int s = splbio();
+
+ if(prev = aha_latest) /* yes, an assign */
+ {
+ timeprev = aha_furtherest;
+ }
+ else
+ {
+ timeprev = 0;
+ }
+ while(prev && (timeprev > time))
+ {
+ timeprev -= prev->delta;
+ prev = prev->sooner;
+ }
+ if(prev)
+ {
+ ccb->delta = time - timeprev;
+ if( ccb->later = prev->later) /* yes an assign */
+ {
+ ccb->later->sooner = ccb;
+ ccb->later->delta -= ccb->delta;
+ }
+ else
+ {
+ aha_furtherest = time;
+ aha_latest = ccb;
+ }
+ ccb->sooner = prev;
+ prev->later = ccb;
+ }
+ else
+ {
+ if( ccb->later = aha_soonest) /* yes, an assign*/
+ {
+ ccb->later->sooner = ccb;
+ ccb->later->delta -= time;
+ }
+ else
+ {
+ aha_furtherest = time;
+ aha_latest = ccb;
+ }
+ ccb->delta = time;
+ ccb->sooner = (struct aha_ccb *)0;
+ aha_soonest = ccb;
+ }
+ splx(s);
+}
+
+aha_remove_timeout(ccb)
+struct aha_ccb *ccb;
+{
+ int s = splbio();
+
+ if(ccb->sooner)
+ {
+ ccb->sooner->later = ccb->later;
+ }
+ else
+ {
+ aha_soonest = ccb->later;
+ }
+ if(ccb->later)
+ {
+ ccb->later->sooner = ccb->sooner;
+ ccb->later->delta += ccb->delta;
+ }
+ else
+ {
+ aha_latest = ccb->sooner;
+ aha_furtherest -= ccb->delta;
+ }
+ ccb->sooner = ccb->later = (struct aha_ccb *)0;
+ splx(s);
+}
+
+
+extern int hz;
+#define ONETICK 500 /* milliseconds */
+#define SLEEPTIME ((hz * 1000) / ONETICK)
+aha_timeout(arg)
+int arg;
+{
+ struct aha_ccb *ccb;
+ int unit;
+ int s = splbio();
+
+ while( ccb = aha_soonest )
+ {
+ if(ccb->delta <= ONETICK)
+ /***********************************************\
+ * It has timed out, we need to do some work *
+ \***********************************************/
+ {
+ unit = ccb->xfer->adapter;
+ printf("aha%d: device %d timed out ",unit
+ ,ccb->xfer->targ);
+
+ /***************************************\
+ * Unlink it from the queue *
+ \***************************************/
+ aha_remove_timeout(ccb);
+
+ /***************************************\
+ * If The ccb's mbx is not free, then *
+ * the board has gone south *
+ \***************************************/
+ if(ccb->mbx->cmd != AHA_MBO_FREE)
+ {
+ printf("aha%d not taking commands!\n"
+ ,unit);
+ Debugger();
+ }
+ /***************************************\
+ * If it has been through before, then *
+ * a previous abort has failed, don't *
+ * try abort again *
+ \***************************************/
+ if(ccb->flags == CCB_ABORTED) /* abort timed out */
+ {
+ printf(" AGAIN\n");
+ ccb->xfer->retries = 0; /* I MEAN IT ! */
+ ccb->host_stat = AHA_ABORTED;
+ aha_done(unit,ccb);
+ }
+ else /* abort the operation that has timed out */
+ {
+ printf("\n");
+ aha_abortmbx(ccb->mbx);
+ /* 2 secs for the abort */
+ aha_add_timeout(ccb,2000 + ONETICK);
+ ccb->flags = CCB_ABORTED;
+ }
+ }
+ else
+ /***********************************************\
+ * It has not timed out, adjust and leave *
+ \***********************************************/
+ {
+ ccb->delta -= ONETICK;
+ aha_furtherest -= ONETICK;
+ break;
+ }
+ }
+ splx(s);
+ timeout(aha_timeout,arg,SLEEPTIME);
+}
OpenPOWER on IntegriCloud