diff options
Diffstat (limited to 'sys/i386/isa/aha1542.c')
-rw-r--r-- | sys/i386/isa/aha1542.c | 1644 |
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); +} |