Index: head/sys/i386/eisa/aha1742.c =================================================================== --- head/sys/i386/eisa/aha1742.c (revision 584) +++ head/sys/i386/eisa/aha1742.c (revision 585) @@ -1,1323 +1,1322 @@ /* * 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. * * commenced: Sun Sep 27 18:14:01 PDT 1992 * - * $Id: aha1742.c,v 1.7 93/08/26 21:12:21 julian Exp Locker: julian $ + * $Id: aha1742.c,v 1.9 1993/08/28 03:07:40 rgrimes Exp $ */ #include #include #include #include #include #include #include #include #include #ifdef MACH /* EITHER CMU OR OSF */ #include #include #include #ifdef OSF /* OSF ONLY */ #include #include #include #include #else OSF /* CMU ONLY */ #include #include #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 #include #include #include #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__ #ifdef MACH int Debugger(); #endif MACH typedef unsigned long int physaddr; extern int hz; #ifdef MACH extern physaddr kvtophys(); #define PHYSTOKV(x) phystokv(x) #define KVTOPHYS(x) kvtophys(x) #endif MACH #ifdef __386BSD__ -#define PHYSTOKV(x) (x | 0xFE000000) #define KVTOPHYS(x) vtophys(x) #endif __386BSD__ extern int delaycount; /* from clock setup code */ #define NUM_CONCURRENT 16 /* number of concurrent ops per board */ #define AHB_NSEG 33 /* number of dma segments supported */ #define FUDGE(X) (X>>1) /* our loops are slower than spinwait() */ /* */ /***********************************************************************\ * AHA1740 standard EISA Host ID regs (Offset from slot base) * \***********************************************************************/ #define HID0 0xC80 /* 0,1: msb of ID2, 3-7: ID1 */ #define HID1 0xC81 /* 0-4: ID3, 4-7: LSB ID2 */ #define HID2 0xC82 /* product, 0=174[20] 1 = 1744 */ #define HID3 0xC83 /* firmware revision */ #define CHAR1(B1,B2) (((B1>>2) & 0x1F) | '@') #define CHAR2(B1,B2) (((B1<<3) & 0x18) | ((B2>>5) & 0x7)|'@') #define CHAR3(B1,B2) ((B2 & 0x1F) | '@') /* AHA1740 EISA board control registers (Offset from slot base) */ #define EBCTRL 0xC84 #define CDEN 0x01 /***********************************************************************\ * AHA1740 EISA board mode registers (Offset from slot base) * \***********************************************************************/ #define PORTADDR 0xCC0 #define PORTADDR_ENHANCED 0x80 #define BIOSADDR 0xCC1 #define INTDEF 0xCC2 #define SCSIDEF 0xCC3 #define BUSDEF 0xCC4 #define RESV0 0xCC5 #define RESV1 0xCC6 #define RESV2 0xCC7 /**** bit definitions for INTDEF ****/ #define INT9 0x00 #define INT10 0x01 #define INT11 0x02 #define INT12 0x03 #define INT14 0x05 #define INT15 0x06 #define INTHIGH 0x08 /* int high=ACTIVE (else edge) */ #define INTEN 0x10 /**** bit definitions for SCSIDEF ****/ #define HSCSIID 0x0F /* our SCSI ID */ #define RSTPWR 0x10 /* reset scsi bus on power up or reset */ /**** bit definitions for BUSDEF ****/ #define B0uS 0x00 /* give up bus immediatly */ #define B4uS 0x01 /* delay 4uSec. */ #define B8uS 0x02 /***********************************************************************\ * AHA1740 ENHANCED mode mailbox control regs (Offset from slot base) * \***********************************************************************/ #define MBOXOUT0 0xCD0 #define MBOXOUT1 0xCD1 #define MBOXOUT2 0xCD2 #define MBOXOUT3 0xCD3 #define ATTN 0xCD4 #define G2CNTRL 0xCD5 #define G2INTST 0xCD6 #define G2STAT 0xCD7 #define MBOXIN0 0xCD8 #define MBOXIN1 0xCD9 #define MBOXIN2 0xCDA #define MBOXIN3 0xCDB #define G2STAT2 0xCDC /*******************************************************\ * Bit definitions for the 5 control/status registers * \*******************************************************/ #define ATTN_TARGET 0x0F #define ATTN_OPCODE 0xF0 #define OP_IMMED 0x10 #define AHB_TARG_RESET 0x80 #define OP_START_ECB 0x40 #define OP_ABORT_ECB 0x50 #define G2CNTRL_SET_HOST_READY 0x20 #define G2CNTRL_CLEAR_EISA_INT 0x40 #define G2CNTRL_HARD_RESET 0x80 #define G2INTST_TARGET 0x0F #define G2INTST_INT_STAT 0xF0 #define AHB_ECB_OK 0x10 #define AHB_ECB_RECOVERED 0x50 #define AHB_HW_ERR 0x70 #define AHB_IMMED_OK 0xA0 #define AHB_ECB_ERR 0xC0 #define AHB_ASN 0xD0 /* for target mode */ #define AHB_IMMED_ERR 0xE0 #define G2STAT_BUSY 0x01 #define G2STAT_INT_PEND 0x02 #define G2STAT_MBOX_EMPTY 0x04 #define G2STAT2_HOST_READY 0x01 /* */ struct ahb_dma_seg { physaddr addr; long len; }; struct ahb_ecb_status { u_short status; # define ST_DON 0x0001 # define ST_DU 0x0002 # define ST_QF 0x0008 # define ST_SC 0x0010 # define ST_DO 0x0020 # define ST_CH 0x0040 # define ST_INT 0x0080 # define ST_ASA 0x0100 # define ST_SNS 0x0200 # define ST_INI 0x0800 # define ST_ME 0x1000 # define ST_ECA 0x4000 u_char ha_status; # define HS_OK 0x00 # define HS_CMD_ABORTED_HOST 0x04 # define HS_CMD_ABORTED_ADAPTER 0x05 # define HS_TIMED_OUT 0x11 # define HS_HARDWARE_ERR 0x20 # define HS_SCSI_RESET_ADAPTER 0x22 # define HS_SCSI_RESET_INCOMING 0x23 u_char targ_status; # define TS_OK 0x00 # define TS_CHECK_CONDITION 0x02 # define TS_BUSY 0x08 u_long resid_count; u_long resid_addr; u_short addit_status; u_char sense_len; u_char unused[9]; u_char cdb[6]; }; /* */ struct ecb { u_char opcode; # define ECB_SCSI_OP 0x01 u_char :4; u_char options:3; u_char :1; short opt1; # define ECB_CNE 0x0001 # define ECB_DI 0x0080 # define ECB_SES 0x0400 # define ECB_S_G 0x1000 # define ECB_DSB 0x4000 # define ECB_ARS 0x8000 short opt2; # define ECB_LUN 0x0007 # define ECB_TAG 0x0008 # define ECB_TT 0x0030 # define ECB_ND 0x0040 # define ECB_DAT 0x0100 # define ECB_DIR 0x0200 # define ECB_ST 0x0400 # define ECB_CHK 0x0800 # define ECB_REC 0x4000 # define ECB_NRB 0x8000 u_short unused1; physaddr data; u_long datalen; physaddr status; physaddr chain; short unused2; short unused3; physaddr sense; u_char senselen; u_char cdblen; short cksum; u_char cdb[12]; /*-----------------end of hardware supported fields----------------*/ struct ecb *next; /* in free list */ struct scsi_xfer *xs; /* the scsi_xfer for this cmd */ int flags; #define ECB_FREE 0 #define ECB_ACTIVE 1 #define ECB_ABORTED 2 #define ECB_IMMED 4 #define ECB_IMMED_FAIL 8 struct ahb_dma_seg ahb_dma[AHB_NSEG]; struct ahb_ecb_status ecb_status; struct scsi_sense_data ecb_sense; }; /* */ struct ahb_data { int flags; #define AHB_INIT 0x01; int baseport; struct ecb ecbs[NUM_CONCURRENT]; struct ecb *free_ecb; int our_id; /* our scsi id */ int vect; struct ecb *immed_ecb; /* an outstanding immediete command */ } ahb_data[NAHB]; int ahbprobe(); int ahb_attach(); int ahbintr(); int ahb_scsi_cmd(); int ahb_timeout(); struct ecb *cheat; void ahbminphys(); long int ahb_adapter_info(); #ifdef MACH struct isa_driver ahbdriver = { ahbprobe, 0, ahb_attach, "ahb", 0, 0, 0}; int (*ahbintrs[])() = {ahbintr, 0}; #endif MACH #ifdef __386BSD__ struct isa_driver ahbdriver = { ahbprobe, ahb_attach, "ahb"}; #endif __386BSD__ #define MAX_SLOTS 8 static ahb_slot = 0; /* slot last board was found in */ static ahb_unit = 0; int ahb_debug = 0; #define AHB_SHOWECBS 0x01 #define AHB_SHOWINTS 0x02 #define AHB_SHOWCMDS 0x04 #define AHB_SHOWMISC 0x08 #define FAIL 1 #define SUCCESS 0 #define PAGESIZ 4096 struct scsi_switch ahb_switch = { ahb_scsi_cmd, ahbminphys, 0, 0, ahb_adapter_info, "ahb", 0,0 }; /* */ /***********************************************************************\ * Function to send a command out through a mailbox * \***********************************************************************/ ahb_send_mbox( int unit ,int opcode ,int target ,struct ecb *ecb) { int port = ahb_data[unit].baseport; int spincount = FUDGE(delaycount) * 1; /* 1ms should be enough */ int s = splbio(); int stport = port + G2STAT; while( ((inb(stport) & (G2STAT_BUSY | G2STAT_MBOX_EMPTY)) != (G2STAT_MBOX_EMPTY)) && (spincount--)); if(spincount == -1) { printf("ahb%d: board not responding\n",unit); Debugger(); } outl(port + MBOXOUT0,KVTOPHYS(ecb)); /* don't know this will work */ outb(port + ATTN, opcode|target); splx(s); } /***********************************************************************\ * Function to poll for command completion when in poll mode * \***********************************************************************/ ahb_poll(int unit ,int wait) /* in msec */ { int port = ahb_data[unit].baseport; int spincount = FUDGE(delaycount) * wait; /* in msec */ int stport = port + G2STAT; int start = spincount; retry: while( (spincount--) && (!(inb(stport) & G2STAT_INT_PEND))); if(spincount == -1) { printf("ahb%d: board not responding\n",unit); return(EIO); } if ((int)cheat != PHYSTOKV(inl(port + MBOXIN0))) { printf("discarding %x ",inl(port + MBOXIN0)); outb(port + G2CNTRL, G2CNTRL_CLEAR_EISA_INT); spinwait(50); goto retry; }/* don't know this will work */ ahbintr(unit); return(0); } /***********************************************************************\ * Function to send an immediate type command to the adapter * \***********************************************************************/ ahb_send_immed( int unit ,int target ,u_long cmd) { int port = ahb_data[unit].baseport; int spincount = FUDGE(delaycount) * 1; /* 1ms should be enough */ int s = splbio(); int stport = port + G2STAT; while( ((inb(stport) & (G2STAT_BUSY | G2STAT_MBOX_EMPTY)) != (G2STAT_MBOX_EMPTY)) && (spincount--)); if(spincount == -1) { printf("ahb%d: board not responding\n",unit); Debugger(); } outl(port + MBOXOUT0,cmd); /* don't know this will work */ outb(port + G2CNTRL, G2CNTRL_SET_HOST_READY); outb(port + ATTN, OP_IMMED | target); splx(s); } /* */ /*******************************************************\ * Check the slots looking for a board we recognise * * If we find one, note it's address (slot) and call * * the actual probe routine to check it out. * \*******************************************************/ ahbprobe(dev) struct isa_dev *dev; { int port; u_char byte1,byte2,byte3; ahb_slot++; while (ahb_slot<8) { port = 0x1000 * ahb_slot; byte1 = inb(port + HID0); byte2 = inb(port + HID1); byte3 = inb(port + HID2); if(byte1 == 0xff) { ahb_slot++; continue; } if ((CHAR1(byte1,byte2) == 'A') && (CHAR2(byte1,byte2) == 'D') && (CHAR3(byte1,byte2) == 'P') && ((byte3 == 0 ) || (byte3 == 1))) { dev->dev_addr = port; return(ahbprobe1(dev)); } ahb_slot++; } 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 * \*******************************************************/ ahbprobe1(dev) struct isa_dev *dev; { /***********************************************\ * find unit and check we have that many defined * \***********************************************/ int unit = ahb_unit; #if defined(OSF) static ihandler_t ahb_handler[NAHB]; static ihandler_id_t *ahb_handler_id[NAHB]; register ihandler_t *chp = &ahb_handler[unit];; #endif /* defined(OSF) */ dev->dev_unit = unit; ahb_data[unit].baseport = dev->dev_addr; if(unit >= NAHB) { printf("ahb: unit number (%d) too high\n",unit); return(0); } /***********************************************\ * Try initialise a unit at this location * * sets up dma and bus speed, loads ahb_data[unit].vect* \***********************************************/ if (ahb_init(unit) != 0) { return(0); } /***********************************************\ * If it's there, put in it's interrupt vectors * \***********************************************/ #ifdef MACH dev->dev_pic = ahb_data[unit].vect; #if defined(OSF) /* 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 ((ahb_handler_id[unit] = handler_add(chp)) != NULL) handler_enable(ahb_handler_id[unit]); else panic("Unable to add ahb interrupt handler"); #else /* CMU */ take_dev_irq(dev); #endif /* !defined(OSF) */ printf("port=%x spl=%d\n", dev->dev_addr, dev->dev_spl); #endif MACH #ifdef __386BSD__ /* 386BSD */ dev->id_irq = (1 << ahb_data[unit].vect); dev->id_drq = -1; /* use EISA dma */ #endif __386BSD__ ahb_unit++; return(1); } /***********************************************\ * Attach all the sub-devices we can find * \***********************************************/ ahb_attach(dev) struct isa_dev *dev; { int unit = dev->dev_unit; /***********************************************\ * ask the adapter what subunits are present * \***********************************************/ scsi_attachdevs( unit, ahb_data[unit].our_id, &ahb_switch); #if defined(OSF) ahb_attached[unit]=1; #endif /* defined(OSF) */ return; } /***********************************************\ * Return some information to the caller about * * the adapter and it's capabilities * \***********************************************/ long int ahb_adapter_info(unit) int unit; { return(2); /* 2 outstanding requests at a time per device */ } /***********************************************\ * Catch an interrupt from the adaptor * \***********************************************/ ahbintr(unit) { struct ecb *ecb; unsigned char stat; register i; u_char ahbstat; int target; long int mboxval; int port = ahb_data[unit].baseport; #ifdef AHBDEBUG if(scsi_debug & PRINTROUTINES) printf("ahbintr "); #endif /*AHBDEBUG*/ #if defined(OSF) if (!ahb_attached[unit]) { return(1); } #endif /* defined(OSF) */ while(inb(port + G2STAT) & G2STAT_INT_PEND) { /***********************************************\ * First get all the information and then * * acknowlege the interrupt * \***********************************************/ ahbstat = inb(port + G2INTST); target = ahbstat & G2INTST_TARGET; stat = ahbstat & G2INTST_INT_STAT; mboxval = inl(port + MBOXIN0);/* don't know this will work */ outb(port + G2CNTRL, G2CNTRL_CLEAR_EISA_INT); #ifdef AHBDEBUG if(scsi_debug & TRACEINTERRUPTS) printf("status = 0x%x ",stat); #endif /*AHBDEBUG*/ /***********************************************\ * Process the completed operation * \***********************************************/ if(stat == AHB_ECB_OK) /* common case is fast */ { ecb = (struct ecb *)PHYSTOKV(mboxval); } else { switch(stat) { case AHB_IMMED_OK: ecb = ahb_data[unit].immed_ecb; ahb_data[unit].immed_ecb = 0; break; case AHB_IMMED_ERR: ecb = ahb_data[unit].immed_ecb; ecb->flags |= ECB_IMMED_FAIL; ahb_data[unit].immed_ecb = 0; break; case AHB_ASN: /* for target mode */ printf("ahb%d: Unexpected ASN interrupt(%x)\n", unit, mboxval); ecb = 0; break; case AHB_HW_ERR: printf("ahb%d: Hardware error interrupt(%x)\n", unit, mboxval); ecb = 0; break; case AHB_ECB_RECOVERED: ecb = (struct ecb *)PHYSTOKV(mboxval); break; case AHB_ECB_ERR: ecb = (struct ecb *)PHYSTOKV(mboxval); break; default: printf(" Unknown return from ahb%d(%x)\n",unit,ahbstat); ecb=0; } } if(ecb) { #ifdef AHBDEBUG if(ahb_debug & AHB_SHOWCMDS ) { ahb_show_scsi_cmd(ecb->xs); } if((ahb_debug & AHB_SHOWECBS) && ecb) printf("",ecb); #endif /*AHBDEBUG*/ untimeout(ahb_timeout,ecb); ahb_done(unit,ecb,((stat == AHB_ECB_OK)?SUCCESS:FAIL)); } } return(1); } /***********************************************\ * We have a ecb which has been processed by the * * adaptor, now we look to see how the operation * * went. * \***********************************************/ ahb_done(unit,ecb,state) int unit,state; struct ecb *ecb; { struct ahb_ecb_status *stat = &ecb->ecb_status; struct scsi_sense_data *s1,*s2; struct scsi_xfer *xs = ecb->xs; #ifdef AHBDEBUG if(scsi_debug & (PRINTROUTINES | TRACEINTERRUPTS)) printf("ahb_done "); #endif /*AHBDEBUG*/ /***********************************************\ * Otherwise, put the results of the operation * * into the xfer and call whoever started it * \***********************************************/ if(ecb->flags & ECB_IMMED) { if(ecb->flags & ECB_IMMED_FAIL) { xs->error = XS_DRIVER_STUFFUP; } goto done; } if ( (state == SUCCESS) || (xs->flags & SCSI_ERR_OK)) { /* All went correctly OR errors expected */ xs->resid = 0; xs->error = 0; } else { s1 = &(ecb->ecb_sense); s2 = &(xs->sense); if(stat->ha_status) { switch(stat->ha_status) { case HS_SCSI_RESET_ADAPTER: break; case HS_SCSI_RESET_INCOMING: break; case HS_CMD_ABORTED_HOST: /* No response */ case HS_CMD_ABORTED_ADAPTER: /* No response */ break; case HS_TIMED_OUT: /* No response */ #ifdef AHBDEBUG if (ahb_debug & AHB_SHOWMISC) { printf("timeout reported back\n"); } #endif /*AHBDEBUG*/ xs->error = XS_TIMEOUT; break; default: /* Other scsi protocol messes */ xs->error = XS_DRIVER_STUFFUP; #ifdef AHBDEBUG if (ahb_debug & AHB_SHOWMISC) { printf("unexpected ha_status: %x\n", stat->ha_status); } #endif /*AHBDEBUG*/ } } else { switch(stat->targ_status) { case TS_CHECK_CONDITION: /* structure copy!!!!!*/ *s2=*s1; xs->error = XS_SENSE; break; case TS_BUSY: xs->error = XS_BUSY; break; default: #ifdef AHBDEBUG if (ahb_debug & AHB_SHOWMISC) { printf("unexpected targ_status: %x\n", stat->targ_status); } #endif /*AHBDEBUG*/ xs->error = XS_DRIVER_STUFFUP; } } } done: xs->flags |= ITSDONE; ahb_free_ecb(unit,ecb, xs->flags); if(xs->when_done) (*(xs->when_done))(xs->done_arg,xs->done_arg2); } /***********************************************\ * A ecb (and hence a mbx-out is put onto the * * free list. * \***********************************************/ ahb_free_ecb(unit,ecb, flags) struct ecb *ecb; { unsigned int opri; #ifdef AHBDEBUG if(scsi_debug & PRINTROUTINES) printf("ecb%d(0x%x)> ",unit,flags); #endif /*AHBDEBUG*/ if (!(flags & SCSI_NOMASK)) opri = splbio(); ecb->next = ahb_data[unit].free_ecb; ahb_data[unit].free_ecb = ecb; ecb->flags = ECB_FREE; /***********************************************\ * If there were none, wake abybody waiting for * * one to come free, starting with queued entries* \***********************************************/ if (!ecb->next) { wakeup(&ahb_data[unit].free_ecb); } if (!(flags & SCSI_NOMASK)) splx(opri); } /***********************************************\ * Get a free ecb (and hence mbox-out entry) * \***********************************************/ struct ecb * ahb_get_ecb(unit,flags) { unsigned opri; struct ecb *rc; #ifdef AHBDEBUG if(scsi_debug & PRINTROUTINES) printf("next; rc->flags = ECB_ACTIVE; } if (!(flags & SCSI_NOMASK)) splx(opri); return(rc); } /***********************************************\ * Start the board, ready for normal operation * \***********************************************/ ahb_init(unit) int unit; { int port = ahb_data[unit].baseport; int intdef; int spincount = FUDGE(delaycount) * 1000; /* 1 sec enough? */ int i; int stport = port + G2STAT; #define NO_NO 1 #ifdef NO_NO /***********************************************\ * reset board, If it doesn't respond, assume * * that it's not there.. good for the probe * \***********************************************/ outb(port + EBCTRL,CDEN); /* enable full card */ outb(port + PORTADDR,PORTADDR_ENHANCED); outb(port + G2CNTRL,G2CNTRL_HARD_RESET); spinwait(1); outb(port + G2CNTRL,0); spinwait(10); while( ((inb(stport) & G2STAT_BUSY )) && (spincount--)); if(spincount == -1) { #ifdef AHBDEBUG if (ahb_debug & AHB_SHOWMISC) printf("ahb_init: No answer from bt742a board\n"); #endif /*AHBDEBUG*/ return(ENXIO); } i = inb(port + MBOXIN0) & 0xff; if(i) { printf("self test failed, val = 0x%x\n",i); return(EIO); } #endif while( inb(stport) & G2STAT_INT_PEND) { printf("."); outb(port + G2CNTRL, G2CNTRL_CLEAR_EISA_INT); spinwait(10); } outb(port + EBCTRL,CDEN); /* enable full card */ outb(port + PORTADDR,PORTADDR_ENHANCED); /***********************************************\ * Assume we have a board at this stage * * setup dma channel from jumpers and save int * * level * \***********************************************/ #ifdef __386BSD__ printf("ahb%d: reading board settings, ",unit); #else __386BSD__ printf("ahb%d:",unit); #endif __386BSD__ intdef = inb(port + INTDEF); switch(intdef & 0x07) { case INT9: ahb_data[unit].vect = 9; break; case INT10: ahb_data[unit].vect = 10; break; case INT11: ahb_data[unit].vect = 11; break; case INT12: ahb_data[unit].vect = 12; break; case INT14: ahb_data[unit].vect = 14; break; case INT15: ahb_data[unit].vect = 15; break; default: printf("illegal int setting\n"); return(EIO); } #ifdef __386BSD__ printf("int=%d\n",ahb_data[unit].vect); #else __386BSD__ printf("int=%d ",ahb_data[unit].vect); #endif __386BSD__ outb(port + INTDEF ,(intdef | INTEN)); /* make sure we can interrupt */ /* who are we on the scsi bus */ ahb_data[unit].our_id = (inb(port + SCSIDEF) & HSCSIID); /***********************************************\ * link up all our ECBs into a free list * \***********************************************/ for (i=0; i < NUM_CONCURRENT; i++) { ahb_data[unit].ecbs[i].next = ahb_data[unit].free_ecb; ahb_data[unit].free_ecb = &ahb_data[unit].ecbs[i]; ahb_data[unit].free_ecb->flags = ECB_FREE; } /***********************************************\ * Note that we are going and return (to probe) * \***********************************************/ ahb_data[unit].flags |= AHB_INIT; return( 0 ); } #ifndef min #define min(x,y) (x < y ? x : y) #endif min void ahbminphys(bp) struct buf *bp; { #ifdef MACH #if !defined(OSF) bp->b_flags |= B_NPAGES; /* can support scat/gather */ #endif /* defined(OSF) */ #endif MACH if(bp->b_bcount > ((AHB_NSEG-1) * PAGESIZ)) { bp->b_bcount = ((AHB_NSEG-1) * PAGESIZ); } } /***********************************************\ * start a scsi operation given the command and * * the data address. Also needs the unit, target * * and lu * \***********************************************/ int ahb_scsi_cmd(xs) struct scsi_xfer *xs; { struct scsi_sense_data *s1,*s2; struct ecb *ecb; struct ahb_dma_seg *sg; int seg; /* scatter gather seg being worked on */ int i = 0; int rc = 0; int thiskv; physaddr thisphys,nextphys; int unit =xs->adapter; int bytes_this_seg,bytes_this_page,datalen,flags; struct iovec *iovp; int s; #ifdef AHBDEBUG if(scsi_debug & PRINTROUTINES) printf("ahb_scsi_cmd "); #endif /*AHBDEBUG*/ /***********************************************\ * get a ecb (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(xs->bp) flags |= (SCSI_NOSLEEP); /* just to be sure */ if(flags & ITSDONE) { printf("ahb%d: Already done?",unit); xs->flags &= ~ITSDONE; } if(!(flags & INUSE)) { printf("ahb%d: Not in use?",unit); xs->flags |= INUSE; } if (!(ecb = ahb_get_ecb(unit,flags))) { xs->error = XS_DRIVER_STUFFUP; return(TRY_AGAIN_LATER); } cheat = ecb; #ifdef AHBDEBUG if(ahb_debug & AHB_SHOWECBS) printf("",ecb); if(scsi_debug & SHOWCOMMANDS) { ahb_show_scsi_cmd(xs); } #endif /*AHBDEBUG*/ ecb->xs = xs; /***********************************************\ * If it's a reset, we need to do an 'immediate' * * command, and store it's ccb for later * * if there is already an immediate waiting, * * then WE must wait * \***********************************************/ if(flags & SCSI_RESET) { ecb->flags |= ECB_IMMED; if(ahb_data[unit].immed_ecb) { return(TRY_AGAIN_LATER); } ahb_data[unit].immed_ecb = ecb; if (!(flags & SCSI_NOMASK)) { s = splbio(); ahb_send_immed(unit,xs->targ,AHB_TARG_RESET); timeout(ahb_timeout,ecb,(xs->timeout * hz)/1000); splx(s); return(SUCCESSFULLY_QUEUED); } else { ahb_send_immed(unit,xs->targ,AHB_TARG_RESET); /***********************************************\ * If we can't use interrupts, poll on completion* \***********************************************/ #ifdef AHBDEBUG if(scsi_debug & TRACEINTERRUPTS) printf("wait "); #endif /*AHBDEBUG*/ if( ahb_poll(unit,xs->timeout)) { ahb_free_ecb(unit,ecb,flags); xs->error = XS_TIMEOUT; return(HAD_ERROR); } return(COMPLETE); } } /***********************************************\ * Put all the arguments for the xfer in the ecb * \***********************************************/ ecb->opcode = ECB_SCSI_OP; ecb->opt1 = ECB_SES|ECB_DSB|ECB_ARS; if(xs->datalen) { ecb->opt1 |= ECB_S_G; } ecb->opt2 = xs->lu | ECB_NRB; ecb->cdblen = xs->cmdlen; ecb->sense = KVTOPHYS(&(ecb->ecb_sense)); ecb->senselen = sizeof(ecb->ecb_sense); ecb->status = KVTOPHYS(&(ecb->ecb_status)); if(xs->datalen) { /* should use S/G only if not zero length */ ecb->data = KVTOPHYS(ecb->ahb_dma); sg = ecb->ahb_dma ; seg = 0; if(flags & SCSI_DATA_UIO) { iovp = ((struct uio *)xs->data)->uio_iov; datalen = ((struct uio *)xs->data)->uio_iovcnt; xs->datalen = 0; while ((datalen) && (seg < AHB_NSEG)) { sg->addr = (physaddr)iovp->iov_base; xs->datalen += sg->len = iovp->iov_len; #ifdef AHBDEBUG if(scsi_debug & SHOWSCATGATH) printf("(0x%x@0x%x)" ,iovp->iov_len ,iovp->iov_base); #endif /*AHBDEBUG*/ sg++; iovp++; seg++; datalen--; } } else { /***********************************************\ * Set up the scatter gather block * \***********************************************/ #ifdef AHBDEBUG if(scsi_debug & SHOWSCATGATH) printf("%d @0x%x:- ",xs->datalen,xs->data); #endif /*AHBDEBUG*/ datalen = xs->datalen; thiskv = (int)xs->data; thisphys = KVTOPHYS(thiskv); while ((datalen) && (seg < AHB_NSEG)) { bytes_this_seg = 0; /* put in the base address */ sg->addr = thisphys; #ifdef AHBDEBUG if(scsi_debug & SHOWSCATGATH) printf("0x%x",thisphys); #endif /*AHBDEBUG*/ /* 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 * \********************************************/ #ifdef AHBDEBUG if(scsi_debug & SHOWSCATGATH) printf("(0x%x)",bytes_this_seg); #endif /*AHBDEBUG*/ sg->len = bytes_this_seg; sg++; seg++; } } /*end of iov/kv decision */ ecb->datalen = seg * sizeof(struct ahb_dma_seg); #ifdef AHBDEBUG if(scsi_debug & SHOWSCATGATH) printf("\n"); #endif /*AHBDEBUG*/ if (datalen) { /* there's still data, must have run out of segs! */ printf("ahb_scsi_cmd%d: more than %d DMA segs\n", unit,AHB_NSEG); xs->error = XS_DRIVER_STUFFUP; ahb_free_ecb(unit,ecb,flags); return(HAD_ERROR); } } else { /* No data xfer, use non S/G values */ ecb->data = (physaddr)0; ecb->datalen = 0; } ecb->chain = (physaddr)0; /***********************************************\ * Put the scsi command in the ecb and start it * \***********************************************/ bcopy(xs->cmd, ecb->cdb, xs->cmdlen); /***********************************************\ * Usually return SUCCESSFULLY QUEUED * \***********************************************/ if (!(flags & SCSI_NOMASK)) { s = splbio(); ahb_send_mbox(unit,OP_START_ECB,xs->targ,ecb); timeout(ahb_timeout,ecb,(xs->timeout * hz)/1000); splx(s); #ifdef AHBDEBUG if(scsi_debug & TRACEINTERRUPTS) printf("cmd_sent "); #endif /*AHBDEBUG*/ return(SUCCESSFULLY_QUEUED); } /***********************************************\ * If we can't use interrupts, poll on completion* \***********************************************/ ahb_send_mbox(unit,OP_START_ECB,xs->targ,ecb); #ifdef AHBDEBUG if(scsi_debug & TRACEINTERRUPTS) printf("cmd_wait "); #endif /*AHBDEBUG*/ do { if(ahb_poll(unit,xs->timeout)) { if (!(xs->flags & SCSI_SILENT)) printf("cmd fail\n"); ahb_send_mbox(unit,OP_ABORT_ECB,xs->targ,ecb); if(ahb_poll(unit,2000)) { printf("abort failed in wait\n"); ahb_free_ecb(unit,ecb,flags); } xs->error = XS_DRIVER_STUFFUP; return(HAD_ERROR); } } while (!(xs->flags & ITSDONE));/* something (?) else finished */ if(xs->error) { return(HAD_ERROR); } return(COMPLETE); } ahb_timeout(struct ecb *ecb) { int unit; int s = splbio(); unit = ecb->xs->adapter; printf("ahb%d:%d device timed out\n",unit ,ecb->xs->targ); #ifdef AHBDEBUG if(ahb_debug & AHB_SHOWECBS) ahb_print_active_ecb(unit); #endif /*AHBDEBUG*/ /***************************************\ * If it's immediate, don't try abort it * \***************************************/ if(ecb->flags & ECB_IMMED) { ecb->xs->retries = 0; /* I MEAN IT ! */ ecb->flags |= ECB_IMMED_FAIL; ahb_done(unit,ecb,FAIL); splx(s); return; } /***************************************\ * If it has been through before, then * * a previous abort has failed, don't * * try abort again * \***************************************/ if(ecb->flags == ECB_ABORTED) /* abort timed out */ { printf("AGAIN"); ecb->xs->retries = 0; /* I MEAN IT ! */ ecb->ecb_status.ha_status = HS_CMD_ABORTED_HOST; ahb_done(unit,ecb,FAIL); } else /* abort the operation that has timed out */ { printf("\n"); ahb_send_mbox(unit,OP_ABORT_ECB,ecb->xs->targ,ecb); /* 2 secs for the abort */ timeout(ahb_timeout,ecb,2 * hz); ecb->flags = ECB_ABORTED; } splx(s); } #ifdef AHBDEBUG ahb_show_scsi_cmd(struct scsi_xfer *xs) { u_char *b = (u_char *)xs->cmd; int i = 0; if(!(xs->flags & SCSI_RESET)) { printf("ahb%d:%d:%d-" ,xs->adapter ,xs->targ ,xs->lu); while(i < xs->cmdlen ) { if(i) printf(","); printf("%x",b[i++]); } printf("-\n"); } else { printf("ahb%d:%d:%d-RESET-\n" ,xs->adapter ,xs->targ ,xs->lu ); } } ahb_print_ecb(ecb) struct ecb *ecb; { printf("ecb:%x op:%x cmdlen:%d senlen:%d\n" ,ecb ,ecb->opcode ,ecb->cdblen ,ecb->senselen); printf(" datlen:%d hstat:%x tstat:%x flags:%x\n" ,ecb->datalen ,ecb->ecb_status.ha_status ,ecb->ecb_status.targ_status ,ecb->flags); ahb_show_scsi_cmd(ecb->xs); } ahb_print_active_ecb(int unit) { struct ecb *ecb = ahb_data[unit].ecbs; int i = NUM_CONCURRENT; while(i--) { if(ecb->flags != ECB_FREE) { ahb_print_ecb(ecb); } ecb++; } } #endif /*AHBDEBUG */ Index: head/sys/i386/isa/aha1542.c =================================================================== --- head/sys/i386/isa/aha1542.c (revision 584) +++ head/sys/i386/isa/aha1542.c (revision 585) @@ -1,1514 +1,1513 @@ /* * (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. * - * $Id: aha1542.c,v 1.5 93/08/26 21:12:17 julian Exp Locker: julian $ + * $Id: aha1542.c,v 1.8 1993/08/28 03:07:38 rgrimes Exp $ */ /* * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992 */ /* * 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 #include #include #include #include #include #include #include #include #ifdef MACH /* EITHER CMU OR OSF */ #include #include #include #ifdef OSF /* OSF ONLY */ #include #include #include #include #else OSF /* CMU ONLY */ #include #include #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 #include #include #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 hz; 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 */ int flags; #define CCB_FREE 0 #define CCB_ACTIVE 1 #define CCB_ABORTED 2 }; /* * 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 #ifdef AHADEBUG int aha_debug = 1; #endif /*AHADEBUG*/ 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, "aha", 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%d: aha_cmd, host not idle(0x%x)\n", unit,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%d: aha_cmd, cmd/data port full\n",unit); 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%d: aha_cmd, cmd/data port empty %d\n", unit,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%d: aha_cmd, host not finished(0x%x)\n",unit,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%d: unit number 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) */ dev->dev_pic = aha_dma[unit]; 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) */ #ifndef __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; /***********************************************\ * 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) */ 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; #ifdef AHADEBUG if(scsi_debug & PRINTROUTINES) printf("ahaintr "); #endif /*AHADEBUG*/ /***********************************************\ * 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); #ifdef AHADEBUG if(scsi_debug & TRACEINTERRUPTS) printf("int "); #endif /*AHADEBUG*/ if (! (stat & AHA_MBIF)) return(1); #ifdef AHADEBUG if(scsi_debug & TRACEINTERRUPTS) printf("b "); #endif /*AHADEBUG*/ #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: #ifdef AHADEBUG if(aha_debug) printf("abort"); #endif /*AHADEBUG*/ ccb->host_stat = AHA_ABORTED; break; case AHA_MBI_UNKNOWN: ccb = (struct aha_ccb *)0; #ifdef AHADEBUG if(aha_debug) printf("unknown ccb for abort "); #endif /*AHADEBUG*/ /* may have missed it */ /* no such ccb known for abort */ case AHA_MBI_ERROR: break; default: panic("Impossible mbxi status"); } #ifdef AHADEBUG 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); } #endif /*AHADEBUG*/ } if(ccb) { untimeout(aha_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; #ifdef AHADEBUG if(scsi_debug & PRINTROUTINES) printf("ccb%d(0x%x)> ",unit,flags); #endif /*AHADEBUG*/ if (!(flags & SCSI_NOMASK)) opri = splbio(); ccb->next = aha_ccb_free[unit]; aha_ccb_free[unit] = ccb; ccb->flags = CCB_FREE; /***********************************************\ * 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; #ifdef AHADEBUG if(scsi_debug & PRINTROUTINES) printf("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; #ifdef AHADEBUG if(scsi_debug & PRINTROUTINES ) printf("aha_done "); #endif /*AHADEBUG*/ /***********************************************\ * Otherwise, put the results of the operation * * into the xfer and call whoever started it * \***********************************************/ if(!(xs->flags & INUSE)) { printf("aha%d: exiting but not in use!\n",unit); 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; #ifdef AHADEBUG if (aha_debug > 1) { printf("host_stat%x\n", ccb->host_stat); } #endif /*AHADEBUG*/ } } else { switch(ccb->target_stat) { case 0x02: /* structure copy!!!!!*/ *s2=*s1; xs->error = XS_SENSE; break; case 0x08: xs->error = XS_BUSY; break; default: #ifdef AHADEBUG if (aha_debug > 1) { printf("target_stat%x\n", ccb->target_stat); } #endif /*AHADEBUG*/ 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) { #ifdef AHADEBUG if (aha_debug) printf("aha_init: No answer from adaptec board\n"); #endif /*AHADEBUG*/ 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) printf(x) #else __386BSD__ printf("aha%d:",unit); #define PRNT(x) printf(x) #endif __386BSD__ DELAY(1000); /* for Bustek 545 */ 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; #ifdef AHADEBUG if(scsi_debug & PRINTROUTINES) printf("aha_scsi_cmd "); #endif /*AHADEBUG*/ /***********************************************\ * 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("aha%d: not in use!\n",unit); Debugger(); xs->flags |= INUSE; } if(flags & ITSDONE) { printf("aha%d: Already done! check device retry code\n",unit); 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("aha%d: MBO not free\n",unit); /***********************************************\ * 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)); #ifdef AHADEBUG if(scsi_debug & SHOWSCATGATH) printf("(0x%x@0x%x)" ,iovp->iov_len ,iovp->iov_base); #endif /*AHADEBUG*/ sg++; iovp++; seg++; datalen--; } } else { /***********************************************\ * Set up the scatter gather block * \***********************************************/ #ifdef AHADEBUG if(scsi_debug & SHOWSCATGATH) printf("%d @0x%x:- ",xs->datalen,xs->data); #endif /*AHADEBUG*/ 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)); #ifdef AHADEBUG if(scsi_debug & SHOWSCATGATH) printf("0x%x",thisphys); #endif /*AHADEBUG*/ /* 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* \***************************************/ #ifdef AHADEBUG if(scsi_debug & SHOWSCATGATH) printf("(0x%x)",bytes_this_seg); #endif /*AHADEBUG*/ lto3b(bytes_this_seg,&(sg->seg_len)); sg++; seg++; } } lto3b(seg * sizeof(struct aha_scat_gath),ccb->data_length); #ifdef AHADEBUG if(scsi_debug & SHOWSCATGATH) printf("\n"); #endif /*AHADEBUG*/ if (datalen) { /* there's still data, must have run out of segs! */ printf("aha%d: aha_scsi_cmd, 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); #ifdef AHADEBUG 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 ); } } #endif /*AHADEBUG*/ if (!(flags & SCSI_NOMASK)) { s= splbio(); /* stop instant timeouts */ timeout(aha_timeout,ccb,(xs->timeout * hz) / 1000); aha_startmbx(ccb->mbx); /***********************************************\ * Usually return SUCCESSFULLY QUEUED * \***********************************************/ splx(s); #ifdef AHADEBUG if(scsi_debug & TRACEINTERRUPTS) printf("sent "); #endif /*AHADEBUG*/ return(SUCCESSFULLY_QUEUED); } aha_startmbx(ccb->mbx); #ifdef AHADEBUG if(scsi_debug & TRACEINTERRUPTS) printf("cmd_sent, waiting "); #endif /*AHADEBUG*/ /***********************************************\ * 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) && iflags & SCSI_SILENT)) printf("aha%d: cmd fail\n",unit); aha_abortmbx(ccb->mbx); count = delaycount * 2000 / AHA_SCSI_TIMEOUT_FUDGE; while((!done) && count) { i=0; while ( (!done) && imbx->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, using ",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\n",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_timeout(struct aha_ccb *ccb) { int unit; int s = splbio(); unit = ccb->xfer->adapter; printf("aha%d: device %d timed out ",unit ,ccb->xfer->targ); /***************************************\ * 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 */ timeout(aha_timeout,ccb,2 * hz); ccb->flags = CCB_ABORTED; } splx(s); } Index: head/sys/i386/isa/aha1742.c =================================================================== --- head/sys/i386/isa/aha1742.c (revision 584) +++ head/sys/i386/isa/aha1742.c (revision 585) @@ -1,1323 +1,1322 @@ /* * 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. * * commenced: Sun Sep 27 18:14:01 PDT 1992 * - * $Id: aha1742.c,v 1.7 93/08/26 21:12:21 julian Exp Locker: julian $ + * $Id: aha1742.c,v 1.9 1993/08/28 03:07:40 rgrimes Exp $ */ #include #include #include #include #include #include #include #include #include #ifdef MACH /* EITHER CMU OR OSF */ #include #include #include #ifdef OSF /* OSF ONLY */ #include #include #include #include #else OSF /* CMU ONLY */ #include #include #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 #include #include #include #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__ #ifdef MACH int Debugger(); #endif MACH typedef unsigned long int physaddr; extern int hz; #ifdef MACH extern physaddr kvtophys(); #define PHYSTOKV(x) phystokv(x) #define KVTOPHYS(x) kvtophys(x) #endif MACH #ifdef __386BSD__ -#define PHYSTOKV(x) (x | 0xFE000000) #define KVTOPHYS(x) vtophys(x) #endif __386BSD__ extern int delaycount; /* from clock setup code */ #define NUM_CONCURRENT 16 /* number of concurrent ops per board */ #define AHB_NSEG 33 /* number of dma segments supported */ #define FUDGE(X) (X>>1) /* our loops are slower than spinwait() */ /* */ /***********************************************************************\ * AHA1740 standard EISA Host ID regs (Offset from slot base) * \***********************************************************************/ #define HID0 0xC80 /* 0,1: msb of ID2, 3-7: ID1 */ #define HID1 0xC81 /* 0-4: ID3, 4-7: LSB ID2 */ #define HID2 0xC82 /* product, 0=174[20] 1 = 1744 */ #define HID3 0xC83 /* firmware revision */ #define CHAR1(B1,B2) (((B1>>2) & 0x1F) | '@') #define CHAR2(B1,B2) (((B1<<3) & 0x18) | ((B2>>5) & 0x7)|'@') #define CHAR3(B1,B2) ((B2 & 0x1F) | '@') /* AHA1740 EISA board control registers (Offset from slot base) */ #define EBCTRL 0xC84 #define CDEN 0x01 /***********************************************************************\ * AHA1740 EISA board mode registers (Offset from slot base) * \***********************************************************************/ #define PORTADDR 0xCC0 #define PORTADDR_ENHANCED 0x80 #define BIOSADDR 0xCC1 #define INTDEF 0xCC2 #define SCSIDEF 0xCC3 #define BUSDEF 0xCC4 #define RESV0 0xCC5 #define RESV1 0xCC6 #define RESV2 0xCC7 /**** bit definitions for INTDEF ****/ #define INT9 0x00 #define INT10 0x01 #define INT11 0x02 #define INT12 0x03 #define INT14 0x05 #define INT15 0x06 #define INTHIGH 0x08 /* int high=ACTIVE (else edge) */ #define INTEN 0x10 /**** bit definitions for SCSIDEF ****/ #define HSCSIID 0x0F /* our SCSI ID */ #define RSTPWR 0x10 /* reset scsi bus on power up or reset */ /**** bit definitions for BUSDEF ****/ #define B0uS 0x00 /* give up bus immediatly */ #define B4uS 0x01 /* delay 4uSec. */ #define B8uS 0x02 /***********************************************************************\ * AHA1740 ENHANCED mode mailbox control regs (Offset from slot base) * \***********************************************************************/ #define MBOXOUT0 0xCD0 #define MBOXOUT1 0xCD1 #define MBOXOUT2 0xCD2 #define MBOXOUT3 0xCD3 #define ATTN 0xCD4 #define G2CNTRL 0xCD5 #define G2INTST 0xCD6 #define G2STAT 0xCD7 #define MBOXIN0 0xCD8 #define MBOXIN1 0xCD9 #define MBOXIN2 0xCDA #define MBOXIN3 0xCDB #define G2STAT2 0xCDC /*******************************************************\ * Bit definitions for the 5 control/status registers * \*******************************************************/ #define ATTN_TARGET 0x0F #define ATTN_OPCODE 0xF0 #define OP_IMMED 0x10 #define AHB_TARG_RESET 0x80 #define OP_START_ECB 0x40 #define OP_ABORT_ECB 0x50 #define G2CNTRL_SET_HOST_READY 0x20 #define G2CNTRL_CLEAR_EISA_INT 0x40 #define G2CNTRL_HARD_RESET 0x80 #define G2INTST_TARGET 0x0F #define G2INTST_INT_STAT 0xF0 #define AHB_ECB_OK 0x10 #define AHB_ECB_RECOVERED 0x50 #define AHB_HW_ERR 0x70 #define AHB_IMMED_OK 0xA0 #define AHB_ECB_ERR 0xC0 #define AHB_ASN 0xD0 /* for target mode */ #define AHB_IMMED_ERR 0xE0 #define G2STAT_BUSY 0x01 #define G2STAT_INT_PEND 0x02 #define G2STAT_MBOX_EMPTY 0x04 #define G2STAT2_HOST_READY 0x01 /* */ struct ahb_dma_seg { physaddr addr; long len; }; struct ahb_ecb_status { u_short status; # define ST_DON 0x0001 # define ST_DU 0x0002 # define ST_QF 0x0008 # define ST_SC 0x0010 # define ST_DO 0x0020 # define ST_CH 0x0040 # define ST_INT 0x0080 # define ST_ASA 0x0100 # define ST_SNS 0x0200 # define ST_INI 0x0800 # define ST_ME 0x1000 # define ST_ECA 0x4000 u_char ha_status; # define HS_OK 0x00 # define HS_CMD_ABORTED_HOST 0x04 # define HS_CMD_ABORTED_ADAPTER 0x05 # define HS_TIMED_OUT 0x11 # define HS_HARDWARE_ERR 0x20 # define HS_SCSI_RESET_ADAPTER 0x22 # define HS_SCSI_RESET_INCOMING 0x23 u_char targ_status; # define TS_OK 0x00 # define TS_CHECK_CONDITION 0x02 # define TS_BUSY 0x08 u_long resid_count; u_long resid_addr; u_short addit_status; u_char sense_len; u_char unused[9]; u_char cdb[6]; }; /* */ struct ecb { u_char opcode; # define ECB_SCSI_OP 0x01 u_char :4; u_char options:3; u_char :1; short opt1; # define ECB_CNE 0x0001 # define ECB_DI 0x0080 # define ECB_SES 0x0400 # define ECB_S_G 0x1000 # define ECB_DSB 0x4000 # define ECB_ARS 0x8000 short opt2; # define ECB_LUN 0x0007 # define ECB_TAG 0x0008 # define ECB_TT 0x0030 # define ECB_ND 0x0040 # define ECB_DAT 0x0100 # define ECB_DIR 0x0200 # define ECB_ST 0x0400 # define ECB_CHK 0x0800 # define ECB_REC 0x4000 # define ECB_NRB 0x8000 u_short unused1; physaddr data; u_long datalen; physaddr status; physaddr chain; short unused2; short unused3; physaddr sense; u_char senselen; u_char cdblen; short cksum; u_char cdb[12]; /*-----------------end of hardware supported fields----------------*/ struct ecb *next; /* in free list */ struct scsi_xfer *xs; /* the scsi_xfer for this cmd */ int flags; #define ECB_FREE 0 #define ECB_ACTIVE 1 #define ECB_ABORTED 2 #define ECB_IMMED 4 #define ECB_IMMED_FAIL 8 struct ahb_dma_seg ahb_dma[AHB_NSEG]; struct ahb_ecb_status ecb_status; struct scsi_sense_data ecb_sense; }; /* */ struct ahb_data { int flags; #define AHB_INIT 0x01; int baseport; struct ecb ecbs[NUM_CONCURRENT]; struct ecb *free_ecb; int our_id; /* our scsi id */ int vect; struct ecb *immed_ecb; /* an outstanding immediete command */ } ahb_data[NAHB]; int ahbprobe(); int ahb_attach(); int ahbintr(); int ahb_scsi_cmd(); int ahb_timeout(); struct ecb *cheat; void ahbminphys(); long int ahb_adapter_info(); #ifdef MACH struct isa_driver ahbdriver = { ahbprobe, 0, ahb_attach, "ahb", 0, 0, 0}; int (*ahbintrs[])() = {ahbintr, 0}; #endif MACH #ifdef __386BSD__ struct isa_driver ahbdriver = { ahbprobe, ahb_attach, "ahb"}; #endif __386BSD__ #define MAX_SLOTS 8 static ahb_slot = 0; /* slot last board was found in */ static ahb_unit = 0; int ahb_debug = 0; #define AHB_SHOWECBS 0x01 #define AHB_SHOWINTS 0x02 #define AHB_SHOWCMDS 0x04 #define AHB_SHOWMISC 0x08 #define FAIL 1 #define SUCCESS 0 #define PAGESIZ 4096 struct scsi_switch ahb_switch = { ahb_scsi_cmd, ahbminphys, 0, 0, ahb_adapter_info, "ahb", 0,0 }; /* */ /***********************************************************************\ * Function to send a command out through a mailbox * \***********************************************************************/ ahb_send_mbox( int unit ,int opcode ,int target ,struct ecb *ecb) { int port = ahb_data[unit].baseport; int spincount = FUDGE(delaycount) * 1; /* 1ms should be enough */ int s = splbio(); int stport = port + G2STAT; while( ((inb(stport) & (G2STAT_BUSY | G2STAT_MBOX_EMPTY)) != (G2STAT_MBOX_EMPTY)) && (spincount--)); if(spincount == -1) { printf("ahb%d: board not responding\n",unit); Debugger(); } outl(port + MBOXOUT0,KVTOPHYS(ecb)); /* don't know this will work */ outb(port + ATTN, opcode|target); splx(s); } /***********************************************************************\ * Function to poll for command completion when in poll mode * \***********************************************************************/ ahb_poll(int unit ,int wait) /* in msec */ { int port = ahb_data[unit].baseport; int spincount = FUDGE(delaycount) * wait; /* in msec */ int stport = port + G2STAT; int start = spincount; retry: while( (spincount--) && (!(inb(stport) & G2STAT_INT_PEND))); if(spincount == -1) { printf("ahb%d: board not responding\n",unit); return(EIO); } if ((int)cheat != PHYSTOKV(inl(port + MBOXIN0))) { printf("discarding %x ",inl(port + MBOXIN0)); outb(port + G2CNTRL, G2CNTRL_CLEAR_EISA_INT); spinwait(50); goto retry; }/* don't know this will work */ ahbintr(unit); return(0); } /***********************************************************************\ * Function to send an immediate type command to the adapter * \***********************************************************************/ ahb_send_immed( int unit ,int target ,u_long cmd) { int port = ahb_data[unit].baseport; int spincount = FUDGE(delaycount) * 1; /* 1ms should be enough */ int s = splbio(); int stport = port + G2STAT; while( ((inb(stport) & (G2STAT_BUSY | G2STAT_MBOX_EMPTY)) != (G2STAT_MBOX_EMPTY)) && (spincount--)); if(spincount == -1) { printf("ahb%d: board not responding\n",unit); Debugger(); } outl(port + MBOXOUT0,cmd); /* don't know this will work */ outb(port + G2CNTRL, G2CNTRL_SET_HOST_READY); outb(port + ATTN, OP_IMMED | target); splx(s); } /* */ /*******************************************************\ * Check the slots looking for a board we recognise * * If we find one, note it's address (slot) and call * * the actual probe routine to check it out. * \*******************************************************/ ahbprobe(dev) struct isa_dev *dev; { int port; u_char byte1,byte2,byte3; ahb_slot++; while (ahb_slot<8) { port = 0x1000 * ahb_slot; byte1 = inb(port + HID0); byte2 = inb(port + HID1); byte3 = inb(port + HID2); if(byte1 == 0xff) { ahb_slot++; continue; } if ((CHAR1(byte1,byte2) == 'A') && (CHAR2(byte1,byte2) == 'D') && (CHAR3(byte1,byte2) == 'P') && ((byte3 == 0 ) || (byte3 == 1))) { dev->dev_addr = port; return(ahbprobe1(dev)); } ahb_slot++; } 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 * \*******************************************************/ ahbprobe1(dev) struct isa_dev *dev; { /***********************************************\ * find unit and check we have that many defined * \***********************************************/ int unit = ahb_unit; #if defined(OSF) static ihandler_t ahb_handler[NAHB]; static ihandler_id_t *ahb_handler_id[NAHB]; register ihandler_t *chp = &ahb_handler[unit];; #endif /* defined(OSF) */ dev->dev_unit = unit; ahb_data[unit].baseport = dev->dev_addr; if(unit >= NAHB) { printf("ahb: unit number (%d) too high\n",unit); return(0); } /***********************************************\ * Try initialise a unit at this location * * sets up dma and bus speed, loads ahb_data[unit].vect* \***********************************************/ if (ahb_init(unit) != 0) { return(0); } /***********************************************\ * If it's there, put in it's interrupt vectors * \***********************************************/ #ifdef MACH dev->dev_pic = ahb_data[unit].vect; #if defined(OSF) /* 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 ((ahb_handler_id[unit] = handler_add(chp)) != NULL) handler_enable(ahb_handler_id[unit]); else panic("Unable to add ahb interrupt handler"); #else /* CMU */ take_dev_irq(dev); #endif /* !defined(OSF) */ printf("port=%x spl=%d\n", dev->dev_addr, dev->dev_spl); #endif MACH #ifdef __386BSD__ /* 386BSD */ dev->id_irq = (1 << ahb_data[unit].vect); dev->id_drq = -1; /* use EISA dma */ #endif __386BSD__ ahb_unit++; return(1); } /***********************************************\ * Attach all the sub-devices we can find * \***********************************************/ ahb_attach(dev) struct isa_dev *dev; { int unit = dev->dev_unit; /***********************************************\ * ask the adapter what subunits are present * \***********************************************/ scsi_attachdevs( unit, ahb_data[unit].our_id, &ahb_switch); #if defined(OSF) ahb_attached[unit]=1; #endif /* defined(OSF) */ return; } /***********************************************\ * Return some information to the caller about * * the adapter and it's capabilities * \***********************************************/ long int ahb_adapter_info(unit) int unit; { return(2); /* 2 outstanding requests at a time per device */ } /***********************************************\ * Catch an interrupt from the adaptor * \***********************************************/ ahbintr(unit) { struct ecb *ecb; unsigned char stat; register i; u_char ahbstat; int target; long int mboxval; int port = ahb_data[unit].baseport; #ifdef AHBDEBUG if(scsi_debug & PRINTROUTINES) printf("ahbintr "); #endif /*AHBDEBUG*/ #if defined(OSF) if (!ahb_attached[unit]) { return(1); } #endif /* defined(OSF) */ while(inb(port + G2STAT) & G2STAT_INT_PEND) { /***********************************************\ * First get all the information and then * * acknowlege the interrupt * \***********************************************/ ahbstat = inb(port + G2INTST); target = ahbstat & G2INTST_TARGET; stat = ahbstat & G2INTST_INT_STAT; mboxval = inl(port + MBOXIN0);/* don't know this will work */ outb(port + G2CNTRL, G2CNTRL_CLEAR_EISA_INT); #ifdef AHBDEBUG if(scsi_debug & TRACEINTERRUPTS) printf("status = 0x%x ",stat); #endif /*AHBDEBUG*/ /***********************************************\ * Process the completed operation * \***********************************************/ if(stat == AHB_ECB_OK) /* common case is fast */ { ecb = (struct ecb *)PHYSTOKV(mboxval); } else { switch(stat) { case AHB_IMMED_OK: ecb = ahb_data[unit].immed_ecb; ahb_data[unit].immed_ecb = 0; break; case AHB_IMMED_ERR: ecb = ahb_data[unit].immed_ecb; ecb->flags |= ECB_IMMED_FAIL; ahb_data[unit].immed_ecb = 0; break; case AHB_ASN: /* for target mode */ printf("ahb%d: Unexpected ASN interrupt(%x)\n", unit, mboxval); ecb = 0; break; case AHB_HW_ERR: printf("ahb%d: Hardware error interrupt(%x)\n", unit, mboxval); ecb = 0; break; case AHB_ECB_RECOVERED: ecb = (struct ecb *)PHYSTOKV(mboxval); break; case AHB_ECB_ERR: ecb = (struct ecb *)PHYSTOKV(mboxval); break; default: printf(" Unknown return from ahb%d(%x)\n",unit,ahbstat); ecb=0; } } if(ecb) { #ifdef AHBDEBUG if(ahb_debug & AHB_SHOWCMDS ) { ahb_show_scsi_cmd(ecb->xs); } if((ahb_debug & AHB_SHOWECBS) && ecb) printf("",ecb); #endif /*AHBDEBUG*/ untimeout(ahb_timeout,ecb); ahb_done(unit,ecb,((stat == AHB_ECB_OK)?SUCCESS:FAIL)); } } return(1); } /***********************************************\ * We have a ecb which has been processed by the * * adaptor, now we look to see how the operation * * went. * \***********************************************/ ahb_done(unit,ecb,state) int unit,state; struct ecb *ecb; { struct ahb_ecb_status *stat = &ecb->ecb_status; struct scsi_sense_data *s1,*s2; struct scsi_xfer *xs = ecb->xs; #ifdef AHBDEBUG if(scsi_debug & (PRINTROUTINES | TRACEINTERRUPTS)) printf("ahb_done "); #endif /*AHBDEBUG*/ /***********************************************\ * Otherwise, put the results of the operation * * into the xfer and call whoever started it * \***********************************************/ if(ecb->flags & ECB_IMMED) { if(ecb->flags & ECB_IMMED_FAIL) { xs->error = XS_DRIVER_STUFFUP; } goto done; } if ( (state == SUCCESS) || (xs->flags & SCSI_ERR_OK)) { /* All went correctly OR errors expected */ xs->resid = 0; xs->error = 0; } else { s1 = &(ecb->ecb_sense); s2 = &(xs->sense); if(stat->ha_status) { switch(stat->ha_status) { case HS_SCSI_RESET_ADAPTER: break; case HS_SCSI_RESET_INCOMING: break; case HS_CMD_ABORTED_HOST: /* No response */ case HS_CMD_ABORTED_ADAPTER: /* No response */ break; case HS_TIMED_OUT: /* No response */ #ifdef AHBDEBUG if (ahb_debug & AHB_SHOWMISC) { printf("timeout reported back\n"); } #endif /*AHBDEBUG*/ xs->error = XS_TIMEOUT; break; default: /* Other scsi protocol messes */ xs->error = XS_DRIVER_STUFFUP; #ifdef AHBDEBUG if (ahb_debug & AHB_SHOWMISC) { printf("unexpected ha_status: %x\n", stat->ha_status); } #endif /*AHBDEBUG*/ } } else { switch(stat->targ_status) { case TS_CHECK_CONDITION: /* structure copy!!!!!*/ *s2=*s1; xs->error = XS_SENSE; break; case TS_BUSY: xs->error = XS_BUSY; break; default: #ifdef AHBDEBUG if (ahb_debug & AHB_SHOWMISC) { printf("unexpected targ_status: %x\n", stat->targ_status); } #endif /*AHBDEBUG*/ xs->error = XS_DRIVER_STUFFUP; } } } done: xs->flags |= ITSDONE; ahb_free_ecb(unit,ecb, xs->flags); if(xs->when_done) (*(xs->when_done))(xs->done_arg,xs->done_arg2); } /***********************************************\ * A ecb (and hence a mbx-out is put onto the * * free list. * \***********************************************/ ahb_free_ecb(unit,ecb, flags) struct ecb *ecb; { unsigned int opri; #ifdef AHBDEBUG if(scsi_debug & PRINTROUTINES) printf("ecb%d(0x%x)> ",unit,flags); #endif /*AHBDEBUG*/ if (!(flags & SCSI_NOMASK)) opri = splbio(); ecb->next = ahb_data[unit].free_ecb; ahb_data[unit].free_ecb = ecb; ecb->flags = ECB_FREE; /***********************************************\ * If there were none, wake abybody waiting for * * one to come free, starting with queued entries* \***********************************************/ if (!ecb->next) { wakeup(&ahb_data[unit].free_ecb); } if (!(flags & SCSI_NOMASK)) splx(opri); } /***********************************************\ * Get a free ecb (and hence mbox-out entry) * \***********************************************/ struct ecb * ahb_get_ecb(unit,flags) { unsigned opri; struct ecb *rc; #ifdef AHBDEBUG if(scsi_debug & PRINTROUTINES) printf("next; rc->flags = ECB_ACTIVE; } if (!(flags & SCSI_NOMASK)) splx(opri); return(rc); } /***********************************************\ * Start the board, ready for normal operation * \***********************************************/ ahb_init(unit) int unit; { int port = ahb_data[unit].baseport; int intdef; int spincount = FUDGE(delaycount) * 1000; /* 1 sec enough? */ int i; int stport = port + G2STAT; #define NO_NO 1 #ifdef NO_NO /***********************************************\ * reset board, If it doesn't respond, assume * * that it's not there.. good for the probe * \***********************************************/ outb(port + EBCTRL,CDEN); /* enable full card */ outb(port + PORTADDR,PORTADDR_ENHANCED); outb(port + G2CNTRL,G2CNTRL_HARD_RESET); spinwait(1); outb(port + G2CNTRL,0); spinwait(10); while( ((inb(stport) & G2STAT_BUSY )) && (spincount--)); if(spincount == -1) { #ifdef AHBDEBUG if (ahb_debug & AHB_SHOWMISC) printf("ahb_init: No answer from bt742a board\n"); #endif /*AHBDEBUG*/ return(ENXIO); } i = inb(port + MBOXIN0) & 0xff; if(i) { printf("self test failed, val = 0x%x\n",i); return(EIO); } #endif while( inb(stport) & G2STAT_INT_PEND) { printf("."); outb(port + G2CNTRL, G2CNTRL_CLEAR_EISA_INT); spinwait(10); } outb(port + EBCTRL,CDEN); /* enable full card */ outb(port + PORTADDR,PORTADDR_ENHANCED); /***********************************************\ * Assume we have a board at this stage * * setup dma channel from jumpers and save int * * level * \***********************************************/ #ifdef __386BSD__ printf("ahb%d: reading board settings, ",unit); #else __386BSD__ printf("ahb%d:",unit); #endif __386BSD__ intdef = inb(port + INTDEF); switch(intdef & 0x07) { case INT9: ahb_data[unit].vect = 9; break; case INT10: ahb_data[unit].vect = 10; break; case INT11: ahb_data[unit].vect = 11; break; case INT12: ahb_data[unit].vect = 12; break; case INT14: ahb_data[unit].vect = 14; break; case INT15: ahb_data[unit].vect = 15; break; default: printf("illegal int setting\n"); return(EIO); } #ifdef __386BSD__ printf("int=%d\n",ahb_data[unit].vect); #else __386BSD__ printf("int=%d ",ahb_data[unit].vect); #endif __386BSD__ outb(port + INTDEF ,(intdef | INTEN)); /* make sure we can interrupt */ /* who are we on the scsi bus */ ahb_data[unit].our_id = (inb(port + SCSIDEF) & HSCSIID); /***********************************************\ * link up all our ECBs into a free list * \***********************************************/ for (i=0; i < NUM_CONCURRENT; i++) { ahb_data[unit].ecbs[i].next = ahb_data[unit].free_ecb; ahb_data[unit].free_ecb = &ahb_data[unit].ecbs[i]; ahb_data[unit].free_ecb->flags = ECB_FREE; } /***********************************************\ * Note that we are going and return (to probe) * \***********************************************/ ahb_data[unit].flags |= AHB_INIT; return( 0 ); } #ifndef min #define min(x,y) (x < y ? x : y) #endif min void ahbminphys(bp) struct buf *bp; { #ifdef MACH #if !defined(OSF) bp->b_flags |= B_NPAGES; /* can support scat/gather */ #endif /* defined(OSF) */ #endif MACH if(bp->b_bcount > ((AHB_NSEG-1) * PAGESIZ)) { bp->b_bcount = ((AHB_NSEG-1) * PAGESIZ); } } /***********************************************\ * start a scsi operation given the command and * * the data address. Also needs the unit, target * * and lu * \***********************************************/ int ahb_scsi_cmd(xs) struct scsi_xfer *xs; { struct scsi_sense_data *s1,*s2; struct ecb *ecb; struct ahb_dma_seg *sg; int seg; /* scatter gather seg being worked on */ int i = 0; int rc = 0; int thiskv; physaddr thisphys,nextphys; int unit =xs->adapter; int bytes_this_seg,bytes_this_page,datalen,flags; struct iovec *iovp; int s; #ifdef AHBDEBUG if(scsi_debug & PRINTROUTINES) printf("ahb_scsi_cmd "); #endif /*AHBDEBUG*/ /***********************************************\ * get a ecb (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(xs->bp) flags |= (SCSI_NOSLEEP); /* just to be sure */ if(flags & ITSDONE) { printf("ahb%d: Already done?",unit); xs->flags &= ~ITSDONE; } if(!(flags & INUSE)) { printf("ahb%d: Not in use?",unit); xs->flags |= INUSE; } if (!(ecb = ahb_get_ecb(unit,flags))) { xs->error = XS_DRIVER_STUFFUP; return(TRY_AGAIN_LATER); } cheat = ecb; #ifdef AHBDEBUG if(ahb_debug & AHB_SHOWECBS) printf("",ecb); if(scsi_debug & SHOWCOMMANDS) { ahb_show_scsi_cmd(xs); } #endif /*AHBDEBUG*/ ecb->xs = xs; /***********************************************\ * If it's a reset, we need to do an 'immediate' * * command, and store it's ccb for later * * if there is already an immediate waiting, * * then WE must wait * \***********************************************/ if(flags & SCSI_RESET) { ecb->flags |= ECB_IMMED; if(ahb_data[unit].immed_ecb) { return(TRY_AGAIN_LATER); } ahb_data[unit].immed_ecb = ecb; if (!(flags & SCSI_NOMASK)) { s = splbio(); ahb_send_immed(unit,xs->targ,AHB_TARG_RESET); timeout(ahb_timeout,ecb,(xs->timeout * hz)/1000); splx(s); return(SUCCESSFULLY_QUEUED); } else { ahb_send_immed(unit,xs->targ,AHB_TARG_RESET); /***********************************************\ * If we can't use interrupts, poll on completion* \***********************************************/ #ifdef AHBDEBUG if(scsi_debug & TRACEINTERRUPTS) printf("wait "); #endif /*AHBDEBUG*/ if( ahb_poll(unit,xs->timeout)) { ahb_free_ecb(unit,ecb,flags); xs->error = XS_TIMEOUT; return(HAD_ERROR); } return(COMPLETE); } } /***********************************************\ * Put all the arguments for the xfer in the ecb * \***********************************************/ ecb->opcode = ECB_SCSI_OP; ecb->opt1 = ECB_SES|ECB_DSB|ECB_ARS; if(xs->datalen) { ecb->opt1 |= ECB_S_G; } ecb->opt2 = xs->lu | ECB_NRB; ecb->cdblen = xs->cmdlen; ecb->sense = KVTOPHYS(&(ecb->ecb_sense)); ecb->senselen = sizeof(ecb->ecb_sense); ecb->status = KVTOPHYS(&(ecb->ecb_status)); if(xs->datalen) { /* should use S/G only if not zero length */ ecb->data = KVTOPHYS(ecb->ahb_dma); sg = ecb->ahb_dma ; seg = 0; if(flags & SCSI_DATA_UIO) { iovp = ((struct uio *)xs->data)->uio_iov; datalen = ((struct uio *)xs->data)->uio_iovcnt; xs->datalen = 0; while ((datalen) && (seg < AHB_NSEG)) { sg->addr = (physaddr)iovp->iov_base; xs->datalen += sg->len = iovp->iov_len; #ifdef AHBDEBUG if(scsi_debug & SHOWSCATGATH) printf("(0x%x@0x%x)" ,iovp->iov_len ,iovp->iov_base); #endif /*AHBDEBUG*/ sg++; iovp++; seg++; datalen--; } } else { /***********************************************\ * Set up the scatter gather block * \***********************************************/ #ifdef AHBDEBUG if(scsi_debug & SHOWSCATGATH) printf("%d @0x%x:- ",xs->datalen,xs->data); #endif /*AHBDEBUG*/ datalen = xs->datalen; thiskv = (int)xs->data; thisphys = KVTOPHYS(thiskv); while ((datalen) && (seg < AHB_NSEG)) { bytes_this_seg = 0; /* put in the base address */ sg->addr = thisphys; #ifdef AHBDEBUG if(scsi_debug & SHOWSCATGATH) printf("0x%x",thisphys); #endif /*AHBDEBUG*/ /* 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 * \********************************************/ #ifdef AHBDEBUG if(scsi_debug & SHOWSCATGATH) printf("(0x%x)",bytes_this_seg); #endif /*AHBDEBUG*/ sg->len = bytes_this_seg; sg++; seg++; } } /*end of iov/kv decision */ ecb->datalen = seg * sizeof(struct ahb_dma_seg); #ifdef AHBDEBUG if(scsi_debug & SHOWSCATGATH) printf("\n"); #endif /*AHBDEBUG*/ if (datalen) { /* there's still data, must have run out of segs! */ printf("ahb_scsi_cmd%d: more than %d DMA segs\n", unit,AHB_NSEG); xs->error = XS_DRIVER_STUFFUP; ahb_free_ecb(unit,ecb,flags); return(HAD_ERROR); } } else { /* No data xfer, use non S/G values */ ecb->data = (physaddr)0; ecb->datalen = 0; } ecb->chain = (physaddr)0; /***********************************************\ * Put the scsi command in the ecb and start it * \***********************************************/ bcopy(xs->cmd, ecb->cdb, xs->cmdlen); /***********************************************\ * Usually return SUCCESSFULLY QUEUED * \***********************************************/ if (!(flags & SCSI_NOMASK)) { s = splbio(); ahb_send_mbox(unit,OP_START_ECB,xs->targ,ecb); timeout(ahb_timeout,ecb,(xs->timeout * hz)/1000); splx(s); #ifdef AHBDEBUG if(scsi_debug & TRACEINTERRUPTS) printf("cmd_sent "); #endif /*AHBDEBUG*/ return(SUCCESSFULLY_QUEUED); } /***********************************************\ * If we can't use interrupts, poll on completion* \***********************************************/ ahb_send_mbox(unit,OP_START_ECB,xs->targ,ecb); #ifdef AHBDEBUG if(scsi_debug & TRACEINTERRUPTS) printf("cmd_wait "); #endif /*AHBDEBUG*/ do { if(ahb_poll(unit,xs->timeout)) { if (!(xs->flags & SCSI_SILENT)) printf("cmd fail\n"); ahb_send_mbox(unit,OP_ABORT_ECB,xs->targ,ecb); if(ahb_poll(unit,2000)) { printf("abort failed in wait\n"); ahb_free_ecb(unit,ecb,flags); } xs->error = XS_DRIVER_STUFFUP; return(HAD_ERROR); } } while (!(xs->flags & ITSDONE));/* something (?) else finished */ if(xs->error) { return(HAD_ERROR); } return(COMPLETE); } ahb_timeout(struct ecb *ecb) { int unit; int s = splbio(); unit = ecb->xs->adapter; printf("ahb%d:%d device timed out\n",unit ,ecb->xs->targ); #ifdef AHBDEBUG if(ahb_debug & AHB_SHOWECBS) ahb_print_active_ecb(unit); #endif /*AHBDEBUG*/ /***************************************\ * If it's immediate, don't try abort it * \***************************************/ if(ecb->flags & ECB_IMMED) { ecb->xs->retries = 0; /* I MEAN IT ! */ ecb->flags |= ECB_IMMED_FAIL; ahb_done(unit,ecb,FAIL); splx(s); return; } /***************************************\ * If it has been through before, then * * a previous abort has failed, don't * * try abort again * \***************************************/ if(ecb->flags == ECB_ABORTED) /* abort timed out */ { printf("AGAIN"); ecb->xs->retries = 0; /* I MEAN IT ! */ ecb->ecb_status.ha_status = HS_CMD_ABORTED_HOST; ahb_done(unit,ecb,FAIL); } else /* abort the operation that has timed out */ { printf("\n"); ahb_send_mbox(unit,OP_ABORT_ECB,ecb->xs->targ,ecb); /* 2 secs for the abort */ timeout(ahb_timeout,ecb,2 * hz); ecb->flags = ECB_ABORTED; } splx(s); } #ifdef AHBDEBUG ahb_show_scsi_cmd(struct scsi_xfer *xs) { u_char *b = (u_char *)xs->cmd; int i = 0; if(!(xs->flags & SCSI_RESET)) { printf("ahb%d:%d:%d-" ,xs->adapter ,xs->targ ,xs->lu); while(i < xs->cmdlen ) { if(i) printf(","); printf("%x",b[i++]); } printf("-\n"); } else { printf("ahb%d:%d:%d-RESET-\n" ,xs->adapter ,xs->targ ,xs->lu ); } } ahb_print_ecb(ecb) struct ecb *ecb; { printf("ecb:%x op:%x cmdlen:%d senlen:%d\n" ,ecb ,ecb->opcode ,ecb->cdblen ,ecb->senselen); printf(" datlen:%d hstat:%x tstat:%x flags:%x\n" ,ecb->datalen ,ecb->ecb_status.ha_status ,ecb->ecb_status.targ_status ,ecb->flags); ahb_show_scsi_cmd(ecb->xs); } ahb_print_active_ecb(int unit) { struct ecb *ecb = ahb_data[unit].ecbs; int i = NUM_CONCURRENT; while(i--) { if(ecb->flags != ECB_FREE) { ahb_print_ecb(ecb); } ecb++; } } #endif /*AHBDEBUG */ Index: head/sys/i386/isa/bt742a.c =================================================================== --- head/sys/i386/isa/bt742a.c (revision 584) +++ head/sys/i386/isa/bt742a.c (revision 585) @@ -1,1618 +1,1617 @@ /* * 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. * - * $Id: bt742a.c,v 1.13 93/08/26 21:12:24 julian Exp Locker: julian $ + * $Id: bt742a.c,v 1.7 1993/08/28 03:07:42 rgrimes Exp $ */ /* * bt742a SCSI driver */ #include #include #include #include #include #include #include #include #include #ifdef MACH /* EITHER CMU OR OSF */ #include #include #include #ifdef OSF /* OSF ONLY */ #include #include #include #include #else OSF /* CMU ONLY */ #include #include #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 #include #include #endif __386BSD__ #ifdef __386BSD__ #include "ddb.h" #if NDDB > 0 int Debugger(); #else NDDB #define Debugger() panic("should call debugger here (bt742a.c)") #endif NDDB #endif __386BSD__ #ifdef MACH int Debugger(); #endif MACH extern int hz; extern int delaycount; /* from clock setup code */ typedef unsigned long int physaddr; /* * I/O Port Interface */ #define BT_BASE bt_base[unit] #define BT_CTRL_STAT_PORT (BT_BASE + 0x0) /* control & status */ #define BT_CMD_DATA_PORT (BT_BASE + 0x1) /* cmds and datas */ #define BT_INTR_PORT (BT_BASE + 0x2) /* Intr. stat */ /* * BT_CTRL_STAT bits (write) */ #define BT_HRST 0x80 /* Hardware reset */ #define BT_SRST 0x40 /* Software reset */ #define BT_IRST 0x20 /* Interrupt reset */ #define BT_SCRST 0x10 /* SCSI bus reset */ /* * BT_CTRL_STAT bits (read) */ #define BT_STST 0x80 /* Self test in Progress */ #define BT_DIAGF 0x40 /* Diagnostic Failure */ #define BT_INIT 0x20 /* Mbx Init required */ #define BT_IDLE 0x10 /* Host Adapter Idle */ #define BT_CDF 0x08 /* cmd/data out port full */ #define BT_DF 0x04 /* Data in port full */ #define BT_INVDCMD 0x01 /* Invalid command */ /* * BT_CMD_DATA bits (write) */ #define BT_NOP 0x00 /* No operation */ #define BT_MBX_INIT 0x01 /* Mbx initialization */ #define BT_START_SCSI 0x02 /* start scsi command */ #define BT_START_BIOS 0x03 /* start bios command */ #define BT_INQUIRE 0x04 /* Adapter Inquiry */ #define BT_MBO_INTR_EN 0x05 /* Enable MBO available interrupt */ #define BT_SEL_TIMEOUT_SET 0x06 /* set selection time-out */ #define BT_BUS_ON_TIME_SET 0x07 /* set bus-on time */ #define BT_BUS_OFF_TIME_SET 0x08 /* set bus-off time */ #define BT_SPEED_SET 0x09 /* set transfer speed */ #define BT_DEV_GET 0x0a /* return installed devices */ #define BT_CONF_GET 0x0b /* return configuration data */ #define BT_TARGET_EN 0x0c /* enable target mode */ #define BT_SETUP_GET 0x0d /* return setup data */ #define BT_WRITE_CH2 0x1a /* write channel 2 buffer */ #define BT_READ_CH2 0x1b /* read channel 2 buffer */ #define BT_WRITE_FIFO 0x1c /* write fifo buffer */ #define BT_READ_FIFO 0x1d /* read fifo buffer */ #define BT_ECHO 0x1e /* Echo command data */ #define BT_MBX_INIT_EXTENDED 0x81 /* Mbx initialization */ #define BT_INQUIRE_EXTENDED 0x8D /* Adapter Setup Inquiry */ /* Follows command appeared at FirmWare 3.31 */ #define BT_ROUND_ROBIN 0x8f /* Enable/Disable(default) round robin */ #define BT_DISABLE 0x00 /* Parameter value for Disable */ #define BT_ENABLE 0x01 /* Parameter value for Enable */ struct bt_cmd_buf { u_char byte[16]; }; /* * BT_INTR_PORT bits (read) */ #define BT_ANY_INTR 0x80 /* Any interrupt */ #define BT_SCRD 0x08 /* SCSI reset detected */ #define BT_HACC 0x04 /* Command complete */ #define BT_MBOA 0x02 /* MBX out empty */ #define BT_MBIF 0x01 /* MBX in full */ /* * Mail box defs */ #define BT_MBX_SIZE 255 /* mail box size (MAX 255 MBxs) */ #define BT_CCB_SIZE 32 /* store up to 32CCBs at any one time */ /* in bt742a H/W ( Not MAX ? ) */ #define bt_nextmbx( wmb, mbx, mbio ) \ if ( (wmb) == &((mbx)->mbio[BT_MBX_SIZE - 1 ]) ) { \ (wmb) = &((mbx)->mbio[0]); \ } else { \ (wmb)++; \ } typedef struct bt_mbx_out { physaddr ccb_addr; unsigned char dummy[3]; unsigned char cmd; } BT_MBO; typedef struct bt_mbx_in{ physaddr ccb_addr; unsigned char btstat; unsigned char sdstat; unsigned char dummy; unsigned char stat; } BT_MBI; struct bt_mbx { BT_MBO mbo[BT_MBX_SIZE]; BT_MBI mbi[BT_MBX_SIZE]; BT_MBO *tmbo; /* Target Mail Box out */ BT_MBI *tmbi; /* Target Mail Box in */ }; /* * mbo.cmd values */ #define BT_MBO_FREE 0x0 /* MBO entry is free */ #define BT_MBO_START 0x1 /* MBO activate entry */ #define BT_MBO_ABORT 0x2 /* MBO abort entry */ #define BT_MBI_FREE 0x0 /* MBI entry is free */ #define BT_MBI_OK 0x1 /* completed without error */ #define BT_MBI_ABORT 0x2 /* aborted ccb */ #define BT_MBI_UNKNOWN 0x3 /* Tried to abort invalid CCB */ #define BT_MBI_ERROR 0x4 /* Completed with error */ extern struct bt_mbx bt_mbx[]; #if defined(BIG_DMA) /* #define BT_NSEG 8192 /* Number of scatter gather segments - to much vm */ #define BT_NSEG 512 #else #define BT_NSEG 33 #endif /* BIG_DMA */ struct bt_scat_gath { unsigned long seg_len; physaddr seg_addr; }; struct bt_ccb { unsigned char opcode; unsigned char :3,data_in:1,data_out:1,:3; unsigned char scsi_cmd_length; unsigned char req_sense_length; /*------------------------------------longword boundary */ unsigned long data_length; /*------------------------------------longword boundary */ physaddr data_addr; /*------------------------------------longword boundary */ unsigned char dummy[2]; unsigned char host_stat; unsigned char target_stat; /*------------------------------------longword boundary */ unsigned char target; unsigned char lun; unsigned char scsi_cmd[12]; /* 12 bytes (bytes only)*/ unsigned char dummy2[1]; unsigned char link_id; /*------------------------------------4 longword boundary */ physaddr link_addr; /*------------------------------------longword boundary */ physaddr sense_ptr; /*------------------------------------longword boundary */ struct scsi_sense_data scsi_sense; /*------------------------------------longword boundary */ struct bt_scat_gath scat_gath[BT_NSEG]; /*------------------------------------longword boundary */ struct bt_ccb *next; /*------------------------------------longword boundary */ struct scsi_xfer *xfer; /* the scsi_xfer for this cmd */ /*------------------------------------longword boundary */ struct bt_mbx_out *mbx; /* pointer to mail box */ /*------------------------------------longword boundary */ int flags; #define CCB_FREE 0 #define CCB_ACTIVE 1 #define CCB_ABORTED 2 }; /* * opcode fields */ #define BT_INITIATOR_CCB 0x00 /* SCSI Initiator CCB */ #define BT_TARGET_CCB 0x01 /* SCSI Target CCB */ #define BT_INIT_SCAT_GATH_CCB 0x02 /* SCSI Initiator with scattter gather*/ #define BT_RESET_CCB 0x81 /* SCSI Bus reset */ /* * bt_ccb.host_stat values */ #define BT_OK 0x00 /* cmd ok */ #define BT_LINK_OK 0x0a /* Link cmd ok */ #define BT_LINK_IT 0x0b /* Link cmd ok + int */ #define BT_SEL_TIMEOUT 0x11 /* Selection time out */ #define BT_OVER_UNDER 0x12 /* Data over/under run */ #define BT_BUS_FREE 0x13 /* Bus dropped at unexpected time */ #define BT_INV_BUS 0x14 /* Invalid bus phase/sequence */ #define BT_BAD_MBO 0x15 /* Incorrect MBO cmd */ #define BT_BAD_CCB 0x16 /* Incorrect ccb opcode */ #define BT_BAD_LINK 0x17 /* Not same values of LUN for links */ #define BT_INV_TARGET 0x18 /* Invalid target direction */ #define BT_CCB_DUP 0x19 /* Duplicate CCB received */ #define BT_INV_CCB 0x1a /* Invalid CCB or segment list */ #define BT_ABORTED 42 /* pseudo value from driver */ struct bt_boardID { u_char board_type; u_char custom_feture; char firm_revision; u_char firm_version; }; struct bt_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 bt_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 EISADMA 0x00 #define CHAN0 0x01 #define CHAN5 0x20 #define CHAN6 0x40 #define CHAN7 0x80 #ifdef MACH extern physaddr kvtophys(); #define PHYSTOKV(x) phystokv(x) #define KVTOPHYS(x) kvtophys(x) #endif MACH #ifdef __386BSD__ -#define PHYSTOKV(x) (x | 0xFE000000) #define KVTOPHYS(x) vtophys(x) #endif __386BSD__ #define PAGESIZ 4096 #define INVALIDATE_CACHE {asm volatile( ".byte 0x0F ;.byte 0x08" ); } u_char bt_scratch_buf[256]; #ifdef MACH caddr_t bt_base[NBT]; /* base port for each board */ #else MACH short bt_base[NBT]; /* base port for each board */ #endif MACH struct bt_mbx bt_mbx[NBT]; struct bt_ccb *bt_ccb_free[NBT]; struct bt_ccb bt_ccb[NBT][BT_CCB_SIZE]; struct scsi_xfer bt_scsi_xfer[NBT]; struct isa_dev *btinfo[NBT]; struct bt_ccb *bt_get_ccb(); int bt_int[NBT]; int bt_dma[NBT]; int bt_scsi_dev[NBT]; int bt_initialized[NBT]; #if defined(OSF) int bt_attached[NBT]; #endif /* defined(OSF) */ /***********debug values *************/ #define BT_SHOWCCBS 0x01 #define BT_SHOWINTS 0x02 #define BT_SHOWCMDS 0x04 #define BT_SHOWMISC 0x08 int bt_debug = 0; int btprobe(), btattach(); int btintr(); #ifdef MACH struct isa_driver btdriver = { btprobe, 0, btattach, "bt", 0, 0, 0}; int (*btintrs[])() = {btintr, 0}; #endif MACH #ifdef __386BSD__ struct isa_driver btdriver = { btprobe, btattach, "bt"}; #endif __386BSD__ static int btunit = 0; int bt_scsi_cmd(); int bt_timeout(); void btminphys(); long int bt_adapter_info(); struct scsi_switch bt_switch = { bt_scsi_cmd, btminphys, 0, 0, bt_adapter_info, "bt", 0,0 }; #define BT_CMD_TIMEOUT_FUDGE 200 /* multiplied to get Secs */ #define BT_RESET_TIMEOUT 1000000 #define BT_SCSI_TIMEOUT_FUDGE 20 /* divided by for mSecs */ /***********************************************************************\ * bt_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 BT_NOP, BT_MBX_INIT, BT_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 * \***********************************************************************/ bt_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 = BT_CMD_TIMEOUT_FUDGE * delaycount; else wait *= BT_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 != BT_MBX_INIT && opcode != BT_START_SCSI) { i = BT_CMD_TIMEOUT_FUDGE * delaycount; /* 1 sec?*/ while (--i) { sts = inb(BT_CTRL_STAT_PORT); if (sts & BT_IDLE) { break; } } if (!i) { printf("bt%d: bt_cmd, host not idle(0x%x)\n",unit,sts); return(ENXIO); } } /*******************************************************\ * Now that it is idle, if we expect output, preflush the* * queue feeding to us. * \*******************************************************/ if (ocnt) { while((inb(BT_CTRL_STAT_PORT)) & BT_DF) inb(BT_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(BT_CTRL_STAT_PORT); for (i=0; i< wait; i++) { sts = inb(BT_CTRL_STAT_PORT); if (!(sts & BT_CDF)) break; } if (i >= wait) { printf("bt%d: bt_cmd, cmd/data port full\n",unit); outb(BT_CTRL_STAT_PORT, BT_SRST); return(ENXIO); } outb(BT_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(BT_CTRL_STAT_PORT); for (i=0; i< wait; i++) { sts = inb(BT_CTRL_STAT_PORT); if (sts & BT_DF) break; } if (i >= wait) { printf("bt%d: bt_cmd, cmd/data port empty %d\n", unit,ocnt); return(ENXIO); } oc = inb(BT_CMD_DATA_PORT); if (retval) *retval++ = oc; } /*******************************************************\ * Wait for the board to report a finised instruction * \*******************************************************/ i=BT_CMD_TIMEOUT_FUDGE * delaycount; /* 1 sec? */ while (--i) { sts = inb(BT_INTR_PORT); if (sts & BT_HACC) { break; } } if (!i) { printf("bt%d: bt_cmd, host not finished(0x%x)\n",unit,sts); return(ENXIO); } outb(BT_CTRL_STAT_PORT, BT_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 * \*******************************************************/ btprobe(dev) struct isa_dev *dev; { /***********************************************\ * find unit and check we have that many defined * \***********************************************/ int unit = btunit; #if defined(OSF) static ihandler_t bt_handler[NBT]; static ihandler_id_t *bt_handler_id[NBT]; register ihandler_t *chp = &bt_handler[unit];; #endif /* defined(OSF) */ dev->dev_unit = unit; bt_base[unit] = dev->dev_addr; if(unit >= NBT) { printf("bt%d: unit number too high\n",unit); return(0); } /***********************************************\ * Try initialise a unit at this location * * sets up dma and bus speed, loads bt_int[unit]* \***********************************************/ if (bt_init(unit) != 0) { return(0); } /***********************************************\ * If it's there, put in it's interrupt vectors * \***********************************************/ #ifdef MACH dev->dev_pic = bt_int[unit]; #if defined(OSF) /* 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 ((bt_handler_id[unit] = handler_add(chp)) != NULL) handler_enable(bt_handler_id[unit]); else panic("Unable to add bt interrupt handler"); #else /* CMU */ take_dev_irq(dev); #endif /* !defined(OSF) */ printf("port=%x spl=%d\n", dev->dev_addr, dev->dev_spl); #endif MACH #ifdef __386BSD__ /* 386BSD */ dev->id_irq = (1 << bt_int[unit]); dev->id_drq = bt_dma[unit]; #endif __386BSD__ btunit++; return(1); } /***********************************************\ * Attach all the sub-devices we can find * \***********************************************/ btattach(dev) struct isa_dev *dev; { int unit = dev->dev_unit; /***********************************************\ * ask the adapter what subunits are present * \***********************************************/ scsi_attachdevs( unit, bt_scsi_dev[unit], &bt_switch); #if defined(OSF) bt_attached[unit]=1; #endif /* defined(OSF) */ return; } /***********************************************\ * Return some information to the caller about * * the adapter and it's capabilities * \***********************************************/ long int bt_adapter_info(unit) int unit; { return(2); /* 2 outstanding requests at a time per device */ } /***********************************************\ * Catch an interrupt from the adaptor * \***********************************************/ btintr(unit) { BT_MBI *wmbi; struct bt_mbx *wmbx; struct bt_ccb *ccb; unsigned char stat; int i,wait; int found = 0; #ifdef UTEST if(scsi_debug & PRINTROUTINES) printf("btintr "); #endif /***********************************************\ * First acknowlege the interrupt, Then if it's * * not telling about a completed operation * * just return. * \***********************************************/ stat = inb(BT_INTR_PORT); /* Mail Box out empty ? */ if ( stat & BT_MBOA ) { printf("bt%d: Available Free mbo post\n",unit); /* Disable MBO available interrupt */ outb(BT_CMD_DATA_PORT,BT_MBO_INTR_EN); wait = BT_CMD_TIMEOUT_FUDGE * delaycount; for (i=0; i< wait; i++) { if (!(inb(BT_CTRL_STAT_PORT) & BT_CDF)) break; } if (i >= wait) { printf("bt%d: bt_intr, cmd/data port full\n",unit); outb(BT_CTRL_STAT_PORT, BT_SRST); return 1; } outb(BT_CMD_DATA_PORT, 0x00); /* Disable */ wakeup(&bt_mbx[unit]); outb(BT_CTRL_STAT_PORT, BT_IRST); return 1; } if (! (stat & BT_MBIF)) { outb(BT_CTRL_STAT_PORT, BT_IRST); return 1; } #if defined(OSF) if (!bt_attached[unit]) { return(1); } #endif /* defined(OSF) */ /***********************************************\ * If it IS then process the competed operation * \***********************************************/ wmbx = &bt_mbx[unit]; wmbi = wmbx->tmbi; AGAIN: while ( wmbi->stat != BT_MBI_FREE ) { found++; ccb = (struct bt_ccb *)PHYSTOKV((wmbi->ccb_addr)); if((stat = wmbi->stat) != BT_MBI_OK) { switch(stat) { case BT_MBI_ABORT: #ifdef UTEST if(bt_debug & BT_SHOWMISC) printf("abort "); #endif ccb->host_stat = BT_ABORTED; break; case BT_MBI_UNKNOWN: ccb = (struct bt_ccb *)0; #ifdef UTEST if(bt_debug & BT_SHOWMISC) printf("unknown ccb for abort"); #endif break; case BT_MBI_ERROR: break; default: panic("Impossible mbxi status"); } #ifdef UTEST if((bt_debug & BT_SHOWCMDS ) && ccb) { u_char *cp; cp = 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 addr = 0x%08x\n" , wmbi->stat, wmbi ); printf("addr = 0x%x\n", ccb); } #endif } wmbi->stat = BT_MBI_FREE; if(ccb) { untimeout(bt_timeout,ccb); bt_done(unit,ccb); } /* Set the IN mail Box pointer for next */ bt_nextmbx( wmbi, wmbx, mbi ); } if ( !found ) { for ( i = 0; i < BT_MBX_SIZE; i++) { if ( wmbi->stat != BT_MBI_FREE ) { found ++; break; } bt_nextmbx( wmbi, wmbx, mbi ); } if ( !found ) { printf("bt%d: mbi at 0x%08x should be found, stat=%02x..resync\n", unit, wmbi, stat ); } else { found = 0; goto AGAIN; } } wmbx->tmbi = wmbi; outb(BT_CTRL_STAT_PORT, BT_IRST); return(1); } /***********************************************\ * A ccb (and hence a mbx-out is put onto the * * free list. * \***********************************************/ bt_free_ccb(unit,ccb, flags) struct bt_ccb *ccb; { unsigned int opri; #ifdef UTEST if(scsi_debug & PRINTROUTINES) printf("ccb%d(0x%x)> ",unit,flags); #endif if (!(flags & SCSI_NOMASK)) opri = splbio(); ccb->next = bt_ccb_free[unit]; bt_ccb_free[unit] = ccb; ccb->flags = CCB_FREE; /***********************************************\ * If there were none, wake abybody waiting for * * one to come free, starting with queued entries* \***********************************************/ if (!ccb->next) { wakeup(&bt_ccb_free[unit]); } if (!(flags & SCSI_NOMASK)) splx(opri); } /***********************************************\ * Get a free ccb * \***********************************************/ struct bt_ccb * bt_get_ccb(unit,flags) { unsigned opri; struct bt_ccb *rc; struct bt_mbx *wmbx; /* Mail Box pointer specified unit */ BT_MBO *wmbo; /* Out Mail Box pointer */ #ifdef UTEST if(scsi_debug & PRINTROUTINES) printf("next; rc->flags = CCB_ACTIVE; #ifdef HE /* Get the Target OUT mail Box pointer */ wmbx = &bt_mbx[unit]; wmbo = wmbx->tmbo; while ( wmbo->cmd != BT_MBO_FREE ) { /* Enable MBO available interrupt */ outb(BT_CMD_DATA_PORT,BT_MBO_INTR_EN); printf("Wait free mbo.."); /* AMURAI */ sleep( wmbx, PRIBIO); printf("Got free mbo\n"); /* AMURAI */ } /* Link CCB to the Mail Box */ rc->mbx = wmbo; wmbo->ccb_addr = KVTOPHYS(rc); /* Set the OUT mail Box pointer for next */ bt_nextmbx( wmbo, wmbx, mbo ); wmbx->tmbo = wmbo; #endif } if (!(flags & SCSI_NOMASK)) splx(opri); return(rc); } /***********************************************\ * Get a MBO and then Send it * \***********************************************/ BT_MBO *bt_send_mbo( int unit, int flags, int cmd, struct bt_ccb *ccb ) { unsigned opri; BT_MBO *wmbo; /* Mail Box Out pointer */ struct bt_mbx *wmbx; /* Mail Box pointer specified unit */ int i, wait; wmbx = &bt_mbx[unit]; if (!(flags & SCSI_NOMASK)) opri = splbio(); /* Get the Target OUT mail Box pointer and move to Next */ wmbo = wmbx->tmbo; wmbx->tmbo = ( wmbo == &( wmbx->mbo[BT_MBX_SIZE - 1 ] ) ? &(wmbx->mbo[0]) : wmbo + 1 ); /* * Check the outmail box is free or not * Note: Under the normal operation, it shuld NOT happen to wait. */ while ( wmbo->cmd != BT_MBO_FREE ) { wait = BT_CMD_TIMEOUT_FUDGE * delaycount; /* Enable MBO available interrupt */ outb(BT_CMD_DATA_PORT,BT_MBO_INTR_EN); for (i=0; i< wait; i++) { if (!(inb(BT_CTRL_STAT_PORT) & BT_CDF)) break; } if (i >= wait) { printf("bt%d: bt_send_mbo, cmd/data port full\n",unit); outb(BT_CTRL_STAT_PORT, BT_SRST); return( (BT_MBO *)0 ); } outb(BT_CMD_DATA_PORT, 0x01); /* Enable */ sleep( wmbx, PRIBIO); } /* Link CCB to the Mail Box */ wmbo->ccb_addr = KVTOPHYS(ccb); ccb->mbx = wmbo; wmbo->cmd = cmd; /* Send it ! */ outb(BT_CMD_DATA_PORT, BT_START_SCSI); if (!(flags & SCSI_NOMASK)) splx(opri); return(wmbo); } /***********************************************\ * 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 * \***********************************************/ bt_done(unit,ccb) struct bt_ccb *ccb; { struct scsi_sense_data *s1,*s2; struct scsi_xfer *xs = ccb->xfer; #ifdef UTEST if(scsi_debug & (PRINTROUTINES | TRACEINTERRUPTS)) printf("bt_done "); #endif /***********************************************\ * Otherwise, put the results of the operation * * into the xfer and call whoever started it * \***********************************************/ if ( ( ccb->host_stat != BT_OK || ccb->target_stat != SCSI_OK) && (!(xs->flags & SCSI_ERR_OK))) { s1 = &(ccb->scsi_sense); s2 = &(xs->sense); if(ccb->host_stat) { switch(ccb->host_stat) { case BT_ABORTED: /* No response */ case BT_SEL_TIMEOUT: /* No response */ #ifdef UTEST if (bt_debug & BT_SHOWMISC) { printf("timeout reported back\n"); } #endif xs->error = XS_TIMEOUT; break; default: /* Other scsi protocol messes */ xs->error = XS_DRIVER_STUFFUP; #ifdef UTEST if (bt_debug & BT_SHOWMISC) { printf("unexpected host_stat: %x\n", ccb->host_stat); } #endif } } else { switch(ccb->target_stat) { case 0x02: /* structure copy!!!!!*/ *s2=*s1; xs->error = XS_SENSE; break; case 0x08: xs->error = XS_BUSY; break; default: #ifdef UTEST if (bt_debug & BT_SHOWMISC) { printf("unexpected target_stat: %x\n", ccb->target_stat); } #endif xs->error = XS_DRIVER_STUFFUP; } } } else /* All went correctly OR errors expected */ { xs->resid = 0; } xs->flags |= ITSDONE; bt_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 * \***********************************************/ bt_init(unit) int unit; { unsigned char ad[4]; volatile int i,sts; struct bt_config conf; /***********************************************\ * reset board, If it doesn't respond, assume * * that it's not there.. good for the probe * \***********************************************/ outb(BT_CTRL_STAT_PORT, BT_HRST|BT_SRST); for (i=0; i < BT_RESET_TIMEOUT; i++) { sts = inb(BT_CTRL_STAT_PORT) ; if ( sts == (BT_IDLE | BT_INIT)) break; } if (i >= BT_RESET_TIMEOUT) { #ifdef UTEST if (bt_debug & BT_SHOWMISC) printf("bt_init: No answer from bt742a board\n"); #endif return(ENXIO); } /***********************************************\ * Assume we have a board at this stage * * setup dma channel from jumpers and save int * * level * \***********************************************/ #ifdef __386BSD__ printf("bt%d: reading board settings, ",unit); #else __386BSD__ printf("bt%d:",unit); #endif __386BSD__ bt_cmd(unit,0, sizeof(conf), 0 ,&conf, BT_CONF_GET); switch(conf.chan) { case EISADMA: bt_dma[unit] = -1; break; case CHAN0: outb(0x0b, 0x0c); outb(0x0a, 0x00); bt_dma[unit] = 0; break; case CHAN5: outb(0xd6, 0xc1); outb(0xd4, 0x01); bt_dma[unit] = 5; break; case CHAN6: outb(0xd6, 0xc2); outb(0xd4, 0x02); bt_dma[unit] = 6; break; case CHAN7: outb(0xd6, 0xc3); outb(0xd4, 0x03); bt_dma[unit] = 7; break; default: printf("illegal dma setting %x\n",conf.chan); return(EIO); } if (bt_dma[unit] == -1) printf("eisa dma, "); else printf("dma=%d, ",bt_dma[unit]); switch(conf.intr) { case INT9: bt_int[unit] = 9; break; case INT10: bt_int[unit] = 10; break; case INT11: bt_int[unit] = 11; break; case INT12: bt_int[unit] = 12; break; case INT14: bt_int[unit] = 14; break; case INT15: bt_int[unit] = 15; break; default: printf("illegal int setting\n"); return(EIO); } #ifdef __386BSD__ printf("int=%d\n",bt_int[unit]); #else printf("int=%d ",bt_int[unit]); #endif __386BSD__ /* who are we on the scsi bus */ bt_scsi_dev[unit] = conf.scsi_dev; /***********************************************\ * Initialize mail box * \***********************************************/ *((physaddr *)ad) = KVTOPHYS(&bt_mbx[unit]); bt_cmd(unit,5, 0, 0, 0, BT_MBX_INIT_EXTENDED , BT_MBX_SIZE , ad[0] , ad[1] , ad[2] , ad[3]); /***********************************************\ * Set Pointer chain null for just in case * * Link the ccb's into a free-list W/O mbox * * Initilize Mail Box stat to Free * \***********************************************/ if ( bt_ccb_free[unit] != (struct bt_ccb *)0 ) { printf("bt%d: bt_ccb_free is NOT initialized but init here\n", unit); bt_ccb_free[unit] = (struct bt_ccb *)0; } for (i=0; i < BT_CCB_SIZE; i++) { bt_ccb[unit][i].next = bt_ccb_free[unit]; bt_ccb_free[unit] = &bt_ccb[unit][i]; bt_ccb_free[unit]->flags = CCB_FREE; } for (i=0; i < BT_MBX_SIZE; i++) { bt_mbx[unit].mbo[i].cmd = BT_MBO_FREE; bt_mbx[unit].mbi[i].stat = BT_MBI_FREE; } /***********************************************\ * Set up Initial mail box for round-robin * \***********************************************/ bt_mbx[unit].tmbo = &bt_mbx[unit].mbo[0]; bt_mbx[unit].tmbi = &bt_mbx[unit].mbi[0]; bt_inquire_setup_information( unit ); /* Enable round-robin scheme - appeared at FirmWare 3.31 */ bt_cmd(unit, 1, 0, 0, 0, BT_ROUND_ROBIN, BT_ENABLE ); /***********************************************\ * Note that we are going and return (to probe) * \***********************************************/ bt_initialized[unit]++; return( 0 ); } bt_inquire_setup_information( unit ) int unit; { struct bt_setup setup; struct bt_boardID bID; int i; /* Inquire Board ID to Bt742 for FirmWare Version */ bt_cmd(unit, 0, sizeof(bID), 0, &bID, BT_INQUIRE ); printf("bt%d: version %c.%c, ", unit, bID.firm_revision, bID.firm_version ); /* Ask setup information to Bt742 */ bt_cmd(unit, 1, sizeof(setup), 0, &setup, BT_SETUP_GET, sizeof(setup) ); if ( setup.sync_neg ) { printf("sync, "); } else { printf("async, "); } if ( setup.parity ) { printf("parity, "); } else { printf("no parity, "); } printf("%d mbxs, %d ccbs\n", setup.num_mbx, sizeof(bt_ccb)/(sizeof(struct bt_ccb) * NBT) ); for ( i = 0; i < 8; i++ ) { if( !setup.sync[i].offset && !setup.sync[i].period && !setup.sync[i].valid ) continue; printf("bt%d: dev%02d Offset=%d,Transfer period=%d, Synchronous? %s", unit, i, setup.sync[i].offset, setup.sync[i].period, setup.sync[i].valid ? "Yes" : "No" ); } } #ifndef min #define min(x,y) (x < y ? x : y) #endif min void btminphys(bp) struct buf *bp; { #ifdef MACH #if !defined(OSF) bp->b_flags |= B_NPAGES; /* can support scat/gather */ #endif /* defined(OSF) */ #endif MACH if(bp->b_bcount > ((BT_NSEG-1) * PAGESIZ)) { bp->b_bcount = ((BT_NSEG-1) * PAGESIZ); } } /***********************************************\ * start a scsi operation given the command and * * the data address. Also needs the unit, target * * and lu * \***********************************************/ int bt_scsi_cmd(xs) struct scsi_xfer *xs; { struct scsi_sense_data *s1,*s2; struct bt_ccb *ccb; struct bt_scat_gath *sg; int seg; /* scatter gather seg being worked on */ int i = 0; int rc = 0; int thiskv; physaddr thisphys,nextphys; int unit =xs->adapter; int bytes_this_seg,bytes_this_page,datalen,flags; struct iovec *iovp; BT_MBO *mbo; #ifdef UTEST if(scsi_debug & PRINTROUTINES) printf("bt_scsi_cmd "); #endif /***********************************************\ * 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(xs->bp) flags |= (SCSI_NOSLEEP); /* just to be sure */ if(flags & ITSDONE) { printf("bt%d: Already done?\n",unit); xs->flags &= ~ITSDONE; } if(!(flags & INUSE)) { printf("bt%d: Not in use?\n",unit); xs->flags |= INUSE; } if (!(ccb = bt_get_ccb(unit,flags))) { xs->error = XS_DRIVER_STUFFUP; return(TRY_AGAIN_LATER); } #ifdef UTEST if(bt_debug & BT_SHOWCCBS) printf("",ccb); #endif /***********************************************\ * Put all the arguments for the xfer in the ccb * \***********************************************/ ccb->xfer = xs; if(flags & SCSI_RESET) { ccb->opcode = BT_RESET_CCB; } else { /* can't use S/G if zero length */ ccb->opcode = (xs->datalen? BT_INIT_SCAT_GATH_CCB :BT_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->sense_ptr = KVTOPHYS(&(ccb->scsi_sense)); ccb->req_sense_length = sizeof(ccb->scsi_sense); if((xs->datalen) && (!(flags & SCSI_RESET))) { /* can use S/G only if not zero length */ ccb->data_addr = KVTOPHYS(ccb->scat_gath); 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; xs->datalen = 0; while ((datalen) && (seg < BT_NSEG)) { sg->seg_addr = (physaddr)iovp->iov_base; xs->datalen += sg->seg_len = iovp->iov_len; #ifdef UTEST if(scsi_debug & SHOWSCATGATH) printf("(0x%x@0x%x)" ,iovp->iov_len ,iovp->iov_base); #endif sg++; iovp++; seg++; datalen--; } } else { /***********************************************\ * Set up the scatter gather block * \***********************************************/ #ifdef UTEST if(scsi_debug & SHOWSCATGATH) printf("%d @0x%x:- ",xs->datalen,xs->data); #endif datalen = xs->datalen; thiskv = (int)xs->data; thisphys = KVTOPHYS(thiskv); while ((datalen) && (seg < BT_NSEG)) { bytes_this_seg = 0; /* put in the base address */ sg->seg_addr = thisphys; #ifdef UTEST if(scsi_debug & SHOWSCATGATH) printf("0x%x",thisphys); #endif /* 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 * \********************************************/ #ifdef UTEST if(scsi_debug & SHOWSCATGATH) printf("(0x%x)",bytes_this_seg); #endif sg->seg_len = bytes_this_seg; sg++; seg++; } } /*end of iov/kv decision */ ccb->data_length = seg * sizeof(struct bt_scat_gath); #ifdef UTEST if(scsi_debug & SHOWSCATGATH) printf("\n"); #endif if (datalen) { /* there's still data, must have run out of segs! */ printf("bt%d: bt_scsi_cmd, more than %d DMA segs\n", unit,BT_NSEG); xs->error = XS_DRIVER_STUFFUP; bt_free_ccb(unit,ccb,flags); return(HAD_ERROR); } } else { /* No data xfer, use non S/G values */ ccb->data_addr = (physaddr)0; ccb->data_length = 0; } ccb->link_id = 0; ccb->link_addr = (physaddr)0; /***********************************************\ * Put the scsi command in the ccb and start it * \***********************************************/ if(!(flags & SCSI_RESET)) { bcopy(xs->cmd, ccb->scsi_cmd, ccb->scsi_cmd_length); } #ifdef UTEST if(scsi_debug & SHOWCOMMANDS) { u_char *b = ccb->scsi_cmd; if(!(flags & SCSI_RESET)) { int i = 0; printf("bt%d:%d:%d-" ,unit ,ccb->target ,ccb->lun); while(i < ccb->scsi_cmd_length ) { if(i) printf(","); printf("%x",b[i++]); } printf("-\n"); } else { printf("bt%d:%d:%d-RESET- " ,unit ,ccb->target ,ccb->lun ); } } #endif if ( bt_send_mbo( unit, flags, BT_MBO_START, ccb ) == (BT_MBO *)0 ) { xs->error = XS_DRIVER_STUFFUP; bt_free_ccb(unit,ccb,flags); return(TRY_AGAIN_LATER); } /***********************************************\ * Usually return SUCCESSFULLY QUEUED * \***********************************************/ #ifdef UTEST if(scsi_debug & TRACEINTERRUPTS) printf("cmd_sent "); #endif if (!(flags & SCSI_NOMASK)) { timeout(bt_timeout,ccb,(xs->timeout * hz) / 1000); return(SUCCESSFULLY_QUEUED); } else /***********************************************\ * If we can't use interrupts, poll on completion* \***********************************************/ { int done = 0; int count = delaycount * xs->timeout / BT_SCSI_TIMEOUT_FUDGE; struct bt_mbx *wmbx = &bt_mbx[unit]; BT_MBI *wmbi = wmbx->tmbi; unsigned char stat; #ifdef UTEST if(scsi_debug & TRACEINTERRUPTS) printf("wait "); #endif while((!done) && count) { stat = inb(BT_INTR_PORT) & (BT_ANY_INTR | BT_MBIF ); if ( !( stat & BT_ANY_INTR ) || ( wmbi->stat == BT_MBI_FREE )|| (PHYSTOKV(wmbi->ccb_addr) != (int)ccb ) ) { count--; continue; } wmbi->stat = BT_MBI_FREE; bt_done(unit,ccb); done ++; outb(BT_CTRL_STAT_PORT, BT_IRST); /* Set the IN mail Box pointer for next */ bt_nextmbx( wmbi, wmbx, mbi ); wmbx->tmbi = wmbi; } if (!count && !done) { #ifdef UTEST if (!(xs->flags & SCSI_SILENT)) printf("cmd fail\n"); #endif bt_send_mbo( unit, flags, BT_MBO_ABORT, ccb ); count = delaycount * 2000 / BT_SCSI_TIMEOUT_FUDGE; while((!done) && count) { if ( !( stat & BT_ANY_INTR ) || ( wmbi->stat == BT_MBI_FREE )|| ( PHYSTOKV(wmbi->ccb_addr ) != (int)ccb ) ) { count--; continue; } wmbi->stat = BT_MBI_FREE; bt_done(unit,ccb); done ++; outb(BT_CTRL_STAT_PORT, BT_IRST); /* Set the IN mail Box pointer for next */ bt_nextmbx( wmbi, wmbx, mbi ); wmbx->tmbi = wmbi; } if(!count && !done) { printf("bt%d: abort failed in wait\n", unit); ccb->mbx->cmd = BT_MBO_FREE; } bt_free_ccb(unit,ccb,flags); xs->error = XS_DRIVER_STUFFUP; return(HAD_ERROR); } if(xs->error) return(HAD_ERROR); return(COMPLETE); } } bt_timeout(struct bt_ccb *ccb) { int unit; int s = splbio(); unit = ccb->xfer->adapter; printf("bt%d: %d device timed out\n",unit ,ccb->xfer->targ); #ifdef UTEST if(bt_debug & BT_SHOWCCBS) bt_print_active_ccbs(unit); #endif /***************************************\ * If The ccb's mbx is not free, then * * the board has gone Far East ? * \***************************************/ if((struct bt_ccb *)PHYSTOKV(ccb->mbx->ccb_addr)==ccb && ccb->mbx->cmd != BT_MBO_FREE ) { printf("bt%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("bt%d: Abort Operation has timed out\n",unit); ccb->xfer->retries = 0; /* I MEAN IT ! */ ccb->host_stat = BT_ABORTED; bt_done(unit,ccb); } else /* abort the operation that has timed out */ { printf("bt%d: Try to abort\n",unit); bt_send_mbo( unit, ~SCSI_NOMASK, BT_MBO_ABORT, ccb ); /* 2 secs for the abort */ timeout(bt_timeout,ccb,2 * hz); ccb->flags = CCB_ABORTED; } splx(s); } #ifdef UTEST bt_print_ccb(ccb) struct bt_ccb *ccb; { printf("ccb:%x op:%x cmdlen:%d senlen:%d\n" ,ccb ,ccb->opcode ,ccb->scsi_cmd_length ,ccb->req_sense_length); printf(" datlen:%d hstat:%x tstat:%x flags:%x\n" ,ccb->data_length ,ccb->host_stat ,ccb->target_stat ,ccb->flags); } bt_print_active_ccbs(int unit) { struct bt_ccb *ccb; ccb = &(bt_ccb[unit][0]); int i = BT_CCB_SIZE; while(i--) { if(ccb->flags != CCB_FREE) bt_print_ccb(ccb); ccb++; } } #endif /*UTEST*/ Index: head/sys/i386/isa/ultra14f.c =================================================================== --- head/sys/i386/isa/ultra14f.c (revision 584) +++ head/sys/i386/isa/ultra14f.c (revision 585) @@ -1,1207 +1,1206 @@ /* * Ported for use with the UltraStor 14f by Gary Close (gclose@wvnvms.wvnet.edu) * Thanks to Julian Elischer for advice and help with this port. * * 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. * * commenced: Sun Sep 27 18:14:01 PDT 1992 * slight mod to make work with 34F as well: Wed Jun 2 18:05:48 WST 1993 * - * $Id: ultra14f.c,v 1.5 93/08/26 21:12:28 julian Exp Locker: julian $ + * $Id: ultra14f.c,v 1.8 1993/08/28 03:07:44 rgrimes Exp $ */ #include #include #include #include #include #include #include #include #include #ifdef MACH /* EITHER CMU OR OSF */ #include #include #include #ifdef OSF /* OSF ONLY */ #include #include #include #include #else OSF /* CMU ONLY */ #include #include #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 #include #include #include #endif __386BSD__ /* */ #ifdef __386BSD__ #include "ddb.h" #if NDDB > 0 int Debugger(); #else NDDB #define Debugger() panic("should call debugger here") #endif NDDB #endif __386BSD__ #ifdef MACH int Debugger(); #endif MACH typedef struct {unsigned char addr[4]; } physaddr; typedef struct {unsigned char len[4]; } physlen; #ifdef MACH extern physaddr kvtophys(); #define PHYSTOKV(x) phystokv(x) #define KVTOPHYS(x) kvtophys(x) #endif MACH #ifdef __386BSD__ -#define PHYSTOKV(x) (x | 0xFE000000) #define KVTOPHYS(x) vtophys(x) #endif __386BSD__ extern int hz; extern int delaycount; /* from clock setup code */ #define NUM_CONCURRENT 16 /* number of concurrent ops per board */ #define UHA_NSEG 33 /* number of dma segments supported */ #define FUDGE(X) (X>>1) /* our loops are slower than spinwait() */ /* */ /************************** board definitions *******************************/ /* * I/O Port Interface */ #define UHA_LMASK (0x000) /* local doorbell mask reg */ #define UHA_LINT (0x001) /* local doorbell int/stat reg */ #define UHA_SMASK (0x002) /* system doorbell mask reg */ #define UHA_SINT (0x003) /* system doorbell int/stat reg */ #define UHA_ID0 (0x004) /* product id reg 0 */ #define UHA_ID1 (0x005) /* product id reg 1 */ #define UHA_CONF1 (0x006) /* config reg 1 */ #define UHA_CONF2 (0x007) /* config reg 2 */ #define UHA_OGM0 (0x008) /* outgoing mail ptr 0 least sig */ #define UHA_OGM1 (0x009) /* outgoing mail ptr 1 least mid */ #define UHA_OGM2 (0x00a) /* outgoing mail ptr 2 most mid */ #define UHA_OGM3 (0x00b) /* outgoing mail ptr 3 most sig */ #define UHA_ICM0 (0x00c) /* incoming mail ptr 0 */ #define UHA_ICM1 (0x00d) /* incoming mail ptr 1 */ #define UHA_ICM2 (0x00e) /* incoming mail ptr 2 */ #define UHA_ICM3 (0x00f) /* incoming mail ptr 3 */ /* * UHA_LMASK bits (read only) */ #define UHA_LDIE 0x80 /* local doorbell int enabled */ #define UHA_SRSTE 0x40 /* soft reset enabled */ #define UHA_ABORTEN 0x10 /* abort MSCP enabled */ #define UHA_OGMINTEN 0x01 /* outgoing mail interrupt enabled */ /* * UHA_LINT bits (read) */ #define UHA_LDIP 0x80 /* local doorbell int pending */ /* * UHA_LINT bits (write) */ #define UHA_ADRST 0x40 /* adapter soft reset */ #define UHA_SBRST 0x20 /* scsi bus reset */ #define UHA_ASRST 0x60 /* adapter and scsi reset */ #define UHA_ABORT 0x10 /* abort MSCP */ #define UHA_OGMINT 0x01 /* tell adapter to get mail */ /* * UHA_SMASK bits (read) */ #define UHA_SINTEN 0x80 /* system doorbell interupt Enabled */ #define UHA_ABORT_COMPLETE_EN 0x10 /* abort MSCP command complete int Enabled */ #define UHA_ICM_ENABLED 0x01 /* ICM interrupt enabled /* * UHA_SMASK bits (write) */ #define UHA_ENSINT 0x80 /* enable system doorbell interrupt */ #define UHA_EN_ABORT_COMPLETE 0x10 /* enable abort MSCP complete int */ #define UHA_ENICM 0x01 /* enable ICM interrupt */ /* * UHA_SINT bits (read) */ #define UHA_SINTP 0x80 /* system doorbell int pending */ #define UHA_ABORT_SUCC 0x10 /* abort MSCP successful */ #define UHA_ABORT_FAIL 0x18 /* abort MSCP failed */ /* * UHA_SINT bits (write) */ #define UHA_ABORT_ACK 0x18 /* acknowledge status and clear */ #define UHA_ICM_ACK 0x01 /* acknowledge ICM and clear */ /* * UHA_CONF1 bits (read only) */ #define UHA_DMA_CH5 0x00 /* DMA channel 5 */ #define UHA_DMA_CH6 0x40 /* 6 */ #define UHA_DMA_CH7 0x80 /* 7 */ #define UHA_IRQ15 0x00 /* IRQ 15 */ #define UHA_IRQ14 0x10 /* 14 */ #define UHA_IRQ11 0x20 /* 11 */ #define UHA_IRQ10 0x30 /* 10 */ /*********************************** * ha_status error codes \***********************************/ #define UHA_NO_ERR 0x00 /* No error supposedly */ #define UHA_SBUS_ABORT_ERR 0x84 /* scsi bus abort error */ #define UHA_SBUS_TIMEOUT 0x91 /* scsi bus selection timeout */ #define UHA_SBUS_OVER_UNDER 0x92 /* scsi bus over/underrun */ #define UHA_BAD_SCSI_CMD 0x96 /* illegal scsi command */ #define UHA_AUTO_SENSE_ERR 0x9b /* auto request sense err */ #define UHA_SBUS_RES_ERR 0xa3 /* scsi bus reset error */ #define UHA_BAD_SG_LIST 0xff /* invalid scatter gath list */ /* */ struct uha_dma_seg { physaddr addr; physlen len; }; /* */ struct mscp { unsigned char opcode:3; #define U14_HAC 0x01 /*host adapter command*/ #define U14_TSP 0x02 /*target scsi pass through command*/ #define U14_SDR 0x04 /*scsi device reset*/ unsigned char xdir:2; /*xfer direction*/ #define U14_SDET 0x00 /*determined by scsi command*/ #define U14_SDIN 0x01 /*scsi data in*/ #define U14_SDOUT 0x02 /*scsi data out*/ #define U14_NODATA 0x03 /*no data xfer*/ unsigned char dcn:1; /*disable disconnect for this command*/ unsigned char ca:1; /*Cache control*/ unsigned char sgth:1; /*scatter gather flag*/ unsigned char target:3; unsigned char chan:2; /*scsi channel (always 0 for 14f)*/ unsigned char lun:3; physaddr data; physlen datalen; physaddr link; unsigned char link_id; unsigned char sg_num; /*number of scat gath segs */ /*in s-g list if sg flag is*/ /*set. starts at 1, 8bytes per*/ unsigned char senselen; unsigned char cdblen; unsigned char cdb[12]; unsigned char ha_status; unsigned char targ_status; physaddr sense; /* if 0 no auto sense */ /*-----------------end of hardware supported fields----------------*/ struct mscp *next; /* in free list */ struct scsi_xfer *xs; /* the scsi_xfer for this cmd */ int flags; #define MSCP_FREE 0 #define MSCP_ACTIVE 1 #define MSCP_ABORTED 2 struct uha_dma_seg uha_dma[UHA_NSEG]; struct scsi_sense_data mscp_sense; }; /* */ struct uha_data { int flags; #define UHA_INIT 0x01; int baseport; struct mscp mscps[NUM_CONCURRENT]; struct mscp *free_mscp; int our_id; /* our scsi id */ int vect; int dma; } uha_data[NUHA]; int uhaprobe(); int uha_attach(); int uhaintr(); int uha_scsi_cmd(); int uha_timeout(); int uha_abort(); struct mscp *cheat; void uhaminphys(); long int uha_adapter_info(); unsigned long int scratch; #ifdef MACH struct isa_driver uhadriver = { uhaprobe, 0, uha_attach, "uha", 0, 0, 0}; int (*uhaintrs[])() = {uhaintr, 0}; #endif MACH #ifdef __386BSD__ struct isa_driver uhadriver = { uhaprobe, uha_attach, "uha"}; #endif __386BSD__ static uha_unit = 0; #ifdef UHADEBUG int uha_debug = 0; #endif /*UHADEBUG*/ #define UHA_SHOWMSCPS 0x01 #define UHA_SHOWINTS 0x02 #define UHA_SHOWCMDS 0x04 #define UHA_SHOWMISC 0x08 #define FAIL 1 #define SUCCESS 0 #define PAGESIZ 4096 struct scsi_switch uha_switch = { uha_scsi_cmd, uhaminphys, 0, 0, uha_adapter_info, "uha", 0,0 }; /* */ /***********************************************************************\ * Function to send a command out through a mailbox * \***********************************************************************/ uha_send_mbox( int unit ,struct mscp *mscp) { int port = uha_data[unit].baseport; int spincount = FUDGE(delaycount) * 1000; /* 1s should be enough */ int s = splbio(); while( ((inb(port + UHA_LINT) & (UHA_LDIP)) != (0)) && (spincount--)); if(spincount == -1) { printf("uha%d: uha_send_mbox, board not responding\n",unit); Debugger(); } outl(port + UHA_OGM0,KVTOPHYS(mscp)); outb(port + UHA_LINT, (UHA_OGMINT)); splx(s); } /***********************************************************************\ * Function to send abort to 14f * \***********************************************************************/ uha_abort( int unit ,struct mscp *mscp) { int port = uha_data[unit].baseport; int spincount = FUDGE(delaycount) * 1; int abortcount = FUDGE(delaycount) * 2000; int s = splbio(); while(((inb(port + UHA_LINT) & (UHA_LDIP)) != (0)) && (spincount--)); if(spincount == -1); { printf("uha%d: uha_abort, board not responding\n",unit); Debugger(); } outl(port + UHA_OGM0,KVTOPHYS(mscp)); outb(port + UHA_LINT,UHA_ABORT); while((abortcount--) && (!(inb(port + UHA_SINT) & UHA_ABORT_FAIL))); if(abortcount == -1) { printf("uha%d: uha_abort, board not responding\n",unit); Debugger(); } if((inb(port + UHA_SINT) & 0x10) != 0) { outb(port + UHA_SINT,UHA_ABORT_ACK); return(1); } else { outb(port + UHA_SINT,UHA_ABORT_ACK); return(0); } } /***********************************************************************\ * Function to poll for command completion when in poll mode * \***********************************************************************/ uha_poll(int unit ,int wait) /* in msec */ { int port = uha_data[unit].baseport; int spincount = FUDGE(delaycount) * wait; /* in msec */ int stport = port + UHA_SINT; int start = spincount; retry: while( (spincount--) && (!(inb(stport) & UHA_SINTP))); if(spincount == -1) { printf("uha%d: uha_poll, board not responding\n",unit); return(EIO); } if ((int)cheat != PHYSTOKV(inl(port + UHA_ICM0))) { printf("uha%d: discarding %x\n",unit,inl(port + UHA_ICM0)); outb(port + UHA_SINT, UHA_ICM_ACK); spinwait(50); goto retry; }/* don't know this will work */ uhaintr(unit); 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 * \*******************************************************/ uhaprobe(dev) struct isa_dev *dev; { int unit = uha_unit; dev->dev_unit = unit; uha_data[unit].baseport = dev->dev_addr; if(unit >= NUHA) { printf("uha%d: unit number too high\n",unit); return(0); } /*try and initialize unit at this location*/ if (uha_init(unit) != 0) { return(0); } /* if its there put in it's interrupt and DRQ vectors */ dev->id_irq = (1 << uha_data[unit].vect); dev->id_drq = uha_data[unit].dma; uha_unit ++; return(1); } /***********************************************\ * Attach all the sub-devices we can find * \***********************************************/ uha_attach(dev) struct isa_dev *dev; { int unit = dev->dev_unit; /***********************************************\ * ask the adapter what subunits are present * \***********************************************/ scsi_attachdevs( unit, uha_data[unit].our_id, &uha_switch); #if defined(OSF) uha_attached[unit]=1; #endif /* defined(OSF) */ return; } /***********************************************\ * Return some information to the caller about * * the adapter and it's capabilities * \***********************************************/ long int uha_adapter_info(unit) int unit; { return(2); /* 2 outstanding requests at a time per device */ } /***********************************************\ * Catch an interrupt from the adaptor * \***********************************************/ uhaintr(unit) { struct mscp *mscp; u_char uhastat; unsigned long int mboxval; int port = uha_data[unit].baseport; #ifdef UHADEBUG if(scsi_debug & PRINTROUTINES) printf("uhaintr "); #endif /*UHADEBUG*/ #if defined(OSF) if (!uha_attached[unit]) { return(1); } #endif /* defined(OSF) */ while(inb(port + UHA_SINT) & UHA_SINTP) { /***********************************************\ * First get all the information and then * * acknowlege the interrupt * \***********************************************/ uhastat = inb(port + UHA_SINT); mboxval = inl(port + UHA_ICM0); outb(port + UHA_SINT,UHA_ICM_ACK); #ifdef UHADEBUG if(scsi_debug & TRACEINTERRUPTS) printf("status = 0x%x ",uhastat); #endif /*UHADEBUG*/ /***********************************************\ * Process the completed operation * \***********************************************/ mscp = (struct mscp *)(PHYSTOKV(mboxval)); #ifdef UHADEBUG if(uha_debug & UHA_SHOWCMDS ) { uha_show_scsi_cmd(mscp->xs); } if((uha_debug & UHA_SHOWMSCPS) && mscp) printf("",mscp); #endif /*UHADEBUG*/ untimeout(uha_timeout,mscp); uha_done(unit,mscp); } return(1); } /***********************************************\ * We have a mscp which has been processed by the * * adaptor, now we look to see how the operation * * went. * \***********************************************/ uha_done(unit,mscp) int unit; struct mscp *mscp; { struct scsi_sense_data *s1,*s2; struct scsi_xfer *xs = mscp->xs; #ifdef UHADEBUG if(scsi_debug & (PRINTROUTINES | TRACEINTERRUPTS)) printf("uha_done "); #endif /*UHADEBUG*/ /***********************************************\ * Otherwise, put the results of the operation * * into the xfer and call whoever started it * \***********************************************/ if ( (mscp->ha_status == UHA_NO_ERR) || (xs->flags & SCSI_ERR_OK)) { /* All went correctly OR errors expected */ xs->resid = 0; xs->error = 0; } else { s1 = &(mscp->mscp_sense); s2 = &(xs->sense); if(mscp->ha_status != UHA_NO_ERR) { switch(mscp->ha_status) { case UHA_SBUS_TIMEOUT: /* No response */ #ifdef UHADEBUG if (uha_debug & UHA_SHOWMISC) { printf("timeout reported back\n"); } #endif /*UHADEBUG*/ xs->error = XS_TIMEOUT; break; case UHA_SBUS_OVER_UNDER: #ifdef UHADEBUG if (uha_debug & UHA_SHOWMISC) { printf("scsi bus xfer over/underrun\n"); } #endif /*UHADEBUG*/ xs->error = XS_DRIVER_STUFFUP; break; case UHA_BAD_SG_LIST: #ifdef UHADEBUG if (uha_debug & UHA_SHOWMISC) { printf("bad sg list reported back\n"); } #endif /*UHADEBUG*/ xs->error = XS_DRIVER_STUFFUP; break; default: /* Other scsi protocol messes */ xs->error = XS_DRIVER_STUFFUP; #ifdef UHADEBUG if (uha_debug & UHA_SHOWMISC) { printf("unexpected ha_status: %x\n", mscp->ha_status); } #endif /*UHADEBUG*/ } } else { if (mscp->targ_status != 0) /**************************************************************************\ * I have no information for any possible value of target status field * * other than 0 means no error!! So I guess any error is unexpected in that * * event!! * \**************************************************************************/ { #ifdef UHADEBUG if (uha_debug & UHA_SHOWMISC) { printf("unexpected targ_status: %x\n", mscp->targ_status); } #endif /*UHADEBUG*/ xs->error = XS_DRIVER_STUFFUP; } } } done: xs->flags |= ITSDONE; uha_free_mscp(unit,mscp, xs->flags); if(xs->when_done) (*(xs->when_done))(xs->done_arg,xs->done_arg2); } /***********************************************\ * A mscp (and hence a mbx-out is put onto the * * free list. * \***********************************************/ uha_free_mscp(unit,mscp, flags) struct mscp *mscp; { unsigned int opri; #ifdef UHADEBUG if(scsi_debug & PRINTROUTINES) printf("mscp%d(0x%x)> ",unit,flags); #endif /*UHADEBUG*/ if (!(flags & SCSI_NOMASK)) opri = splbio(); mscp->next = uha_data[unit].free_mscp; uha_data[unit].free_mscp = mscp; mscp->flags = MSCP_FREE; /***********************************************\ * If there were none, wake abybody waiting for * * one to come free, starting with queued entries* \***********************************************/ if (!mscp->next) { wakeup(&uha_data[unit].free_mscp); } if (!(flags & SCSI_NOMASK)) splx(opri); } /***********************************************\ * Get a free mscp (and hence mbox-out entry) * \***********************************************/ struct mscp * uha_get_mscp(unit,flags) { unsigned opri; struct mscp *rc; #ifdef UHADEBUG if(scsi_debug & PRINTROUTINES) printf("next; rc->flags = MSCP_ACTIVE; } if (!(flags & SCSI_NOMASK)) splx(opri); return(rc); } /***********************************************\ * Start the board, ready for normal operation * \***********************************************/ uha_init(unit) int unit; { unsigned char ad[4]; volatile unsigned char model; volatile unsigned char submodel; unsigned char config_reg1; unsigned char config_reg2; unsigned char dma_ch; unsigned char irq_ch; unsigned char uha_id; int port = uha_data[unit].baseport; int i; int resetcount = FUDGE(delaycount) * 4000; model = inb(port + UHA_ID0); submodel = inb(port + UHA_ID1); if ((model != 0x56) & (submodel != 0x40)) { printf("uha%d: uha_init, board not responding\n",unit); return(ENXIO); } printf("uha%d: reading board settings, ",unit); config_reg1 = inb(port + UHA_CONF1); config_reg2 = inb(port + UHA_CONF2); dma_ch = (config_reg1 & 0xc0); irq_ch = (config_reg1 & 0x30); uha_id = (config_reg2 & 0x07); switch(dma_ch) { case UHA_DMA_CH5: uha_data[unit].dma = 5; printf("dma=5 "); break; case UHA_DMA_CH6: uha_data[unit].dma = 6; printf("dma=6 "); break; case UHA_DMA_CH7: uha_data[unit].dma = 7; printf("dma=7 "); break; default: printf("illegal dma jumper setting\n"); return(EIO); } switch(irq_ch) { case UHA_IRQ10: uha_data[unit].vect = 10; printf("int=10 "); break; case UHA_IRQ11: uha_data[unit].vect = 11; printf("int=11 "); break; case UHA_IRQ14: uha_data[unit].vect = 14; printf("int=14 "); break; case UHA_IRQ15: uha_data[unit].vect = 15; printf("int=15 "); break; default: printf("illegal int jumper setting\n"); return(EIO); } /* who are we on the scsi bus */ printf("id=%x\n",uha_id); uha_data[unit].our_id = uha_id; /***********************************************\ * link up all our MSCPs into a free list * \***********************************************/ for (i=0; i < NUM_CONCURRENT; i++) { uha_data[unit].mscps[i].next = uha_data[unit].free_mscp; uha_data[unit].free_mscp = &uha_data[unit].mscps[i]; uha_data[unit].free_mscp->flags = MSCP_FREE; } /***********************************************\ * Note that we are going and return (to probe) * \***********************************************/ outb(port + UHA_LINT, UHA_ASRST); while( (resetcount--) && (!(inb(port + UHA_LINT)))); if(resetcount == -1) { printf("uha%d: board timed out during reset\n",unit); return(ENXIO); } outb(port + UHA_SMASK, 0x81); /* make sure interrupts are enabled */ uha_data[unit].flags |= UHA_INIT; return(0); } #ifndef min #define min(x,y) (x < y ? x : y) #endif min void uhaminphys(bp) struct buf *bp; { #ifdef MACH #if !defined(OSF) bp->b_flags |= B_NPAGES; /* can support scat/gather */ #endif /* defined(OSF) */ #endif MACH if(bp->b_bcount > ((UHA_NSEG-1) * PAGESIZ)) { bp->b_bcount = ((UHA_NSEG-1) * PAGESIZ); } } /***********************************************\ * start a scsi operation given the command and * * the data address. Also needs the unit, target * * and lu * \***********************************************/ int uha_scsi_cmd(xs) struct scsi_xfer *xs; { struct scsi_sense_data *s1,*s2; struct mscp *mscp; struct uha_dma_seg *sg; int seg; /* scatter gather seg being worked on */ int i = 0; int rc = 0; int thiskv; unsigned long int thisphys,nextphys; int unit =xs->adapter; int bytes_this_seg,bytes_this_page,datalen,flags; struct iovec *iovp; int s; unsigned int stat; int port = uha_data[unit].baseport; unsigned long int templen; #ifdef UHADEBUG if(scsi_debug & PRINTROUTINES) printf("uha_scsi_cmd "); #endif /*UHADEBUG*/ /***********************************************\ * get a mscp (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(xs->bp) flags |= (SCSI_NOSLEEP); /* just to be sure */ if(flags & ITSDONE) { printf("uha%d: Already done?",unit); xs->flags &= ~ITSDONE; } if(!(flags & INUSE)) { printf("uha%d: Not in use?",unit); xs->flags |= INUSE; } if (!(mscp = uha_get_mscp(unit,flags))) { xs->error = XS_DRIVER_STUFFUP; return(TRY_AGAIN_LATER); } cheat = mscp; #ifdef UHADEBUG if(uha_debug & UHA_SHOWMSCPS) printf("",mscp); if(scsi_debug & SHOWCOMMANDS) { uha_show_scsi_cmd(xs); } #endif /*UHADEBUG*/ mscp->xs = xs; /***********************************************\ * Put all the arguments for the xfer in the mscp * \***********************************************/ if (flags & SCSI_RESET) { mscp->opcode = 0x04; mscp->ca = 0x01; } else { mscp->opcode = 0x02; mscp->ca = 0x01; } if (flags & SCSI_DATA_IN) { mscp->xdir = 0x01; } if (flags & SCSI_DATA_OUT) { mscp->xdir = 0x02; } if (xs->lu != 0) { xs->error = XS_DRIVER_STUFFUP; uha_free_mscp(unit,mscp,flags); return(HAD_ERROR); } mscp->dcn = 0x00; mscp->chan = 0x00; mscp->target = xs->targ; mscp->lun = xs->lu; mscp->link.addr[0] = 0x00; mscp->link.addr[1] = 0x00; mscp->link.addr[2] = 0x00; mscp->link.addr[3] = 0x00; mscp->link_id = 0x00; mscp->cdblen = xs->cmdlen; scratch = KVTOPHYS(&(mscp->mscp_sense)); mscp->sense.addr[0] = (scratch & 0xff); mscp->sense.addr[1] = ((scratch >> 8) & 0xff); mscp->sense.addr[2] = ((scratch >> 16) & 0xff); mscp->sense.addr[3] = ((scratch >> 24) & 0xff); mscp->senselen = sizeof(mscp->mscp_sense); mscp->ha_status = 0x00; mscp->targ_status = 0x00; if(xs->datalen) { /* should use S/G only if not zero length */ scratch = KVTOPHYS(mscp->uha_dma); mscp->data.addr[0] = (scratch & 0xff); mscp->data.addr[1] = ((scratch >> 8) & 0xff); mscp->data.addr[2] = ((scratch >> 16) & 0xff); mscp->data.addr[3] = ((scratch >> 24) & 0xff); sg = mscp->uha_dma ; seg = 0; mscp->sgth = 0x01; if(flags & SCSI_DATA_UIO) { iovp = ((struct uio *)xs->data)->uio_iov; datalen = ((struct uio *)xs->data)->uio_iovcnt; xs->datalen = 0; while ((datalen) && (seg < UHA_NSEG)) { scratch = (unsigned long)iovp->iov_base; sg->addr.addr[0] = (scratch & 0xff); sg->addr.addr[1] = ((scratch >> 8) & 0xff); sg->addr.addr[2] = ((scratch >> 16) & 0xff); sg->addr.addr[3] = ((scratch >> 24) & 0xff); xs->datalen += *(unsigned long *)sg->len.len = iovp->iov_len; #ifdef UHADEBUG if(scsi_debug & SHOWSCATGATH) printf("(0x%x@0x%x)" ,iovp->iov_len ,iovp->iov_base); #endif /*UHADEBUG*/ sg++; iovp++; seg++; datalen--; } } else { /***********************************************\ * Set up the scatter gather block * \***********************************************/ #ifdef UHADEBUG if(scsi_debug & SHOWSCATGATH) printf("%d @0x%x:- ",xs->datalen,xs->data); #endif /*UHADEBUG*/ datalen = xs->datalen; thiskv = (int)xs->data; thisphys = KVTOPHYS(thiskv); templen = 0; while ((datalen) && (seg < UHA_NSEG)) { bytes_this_seg = 0; /* put in the base address */ sg->addr.addr[0] = (thisphys & 0xff); sg->addr.addr[1] = ((thisphys >> 8) & 0xff); sg->addr.addr[2] = ((thisphys >> 16) & 0xff); sg->addr.addr[3] = ((thisphys >> 24) & 0xff); #ifdef UHADEBUG if(scsi_debug & SHOWSCATGATH) printf("0x%x",thisphys); #endif /*UHADEBUG*/ /* 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 * \********************************************/ #ifdef UHADEBUG if(scsi_debug & SHOWSCATGATH) printf("(0x%x)",bytes_this_seg); #endif /*UHADEBUG*/ sg->len.len[0] = (bytes_this_seg & 0xff); sg->len.len[1] = ((bytes_this_seg >> 8) & 0xff); sg->len.len[2] = ((bytes_this_seg >> 16) & 0xff); sg->len.len[3] = ((bytes_this_seg >> 24) & 0xff); templen += bytes_this_seg; sg++; seg++; } } /*end of iov/kv decision */ mscp->datalen.len[0] = (templen & 0xff); mscp->datalen.len[1] = ((templen >> 8) & 0xff); mscp->datalen.len[2] = ((templen >> 16) & 0xff); mscp->datalen.len[3] = ((templen >> 24) & 0xff); mscp->sg_num = seg; #ifdef UHADEBUG if(scsi_debug & SHOWSCATGATH) printf("\n"); #endif /*UHADEBUG*/ if (datalen) { /* there's still data, must have run out of segs! */ printf("uha%d: uha_scsi_cmd, more than %d DMA segs\n", unit,UHA_NSEG); xs->error = XS_DRIVER_STUFFUP; uha_free_mscp(unit,mscp,flags); return(HAD_ERROR); } } else { /* No data xfer, use non S/G values */ mscp->data.addr[0] = 0x00; mscp->data.addr[1] = 0x00; mscp->data.addr[2] = 0x00; mscp->data.addr[3] = 0x00; mscp->datalen.len[0] = 0x00; mscp->datalen.len[1] = 0x00; mscp->datalen.len[2] = 0x00; mscp->datalen.len[3] = 0x00; mscp->xdir = 0x03; mscp->sgth = 0x00; mscp->sg_num = 0x00; } /***********************************************\ * Put the scsi command in the mscp and start it * \***********************************************/ bcopy(xs->cmd, mscp->cdb, xs->cmdlen); /***********************************************\ * Usually return SUCCESSFULLY QUEUED * \***********************************************/ if (!(flags & SCSI_NOMASK)) { s = splbio(); uha_send_mbox(unit,mscp); timeout(uha_timeout,mscp,(xs->timeout * hz) / 1000); splx(s); #ifdef UHADEBUG if(scsi_debug & TRACEINTERRUPTS) printf("cmd_sent "); #endif /*UHADEBUG*/ return(SUCCESSFULLY_QUEUED); } /***********************************************\ * If we can't use interrupts, poll on completion* \***********************************************/ uha_send_mbox(unit,mscp); #ifdef UHADEBUG if(scsi_debug & TRACEINTERRUPTS) printf("cmd_wait "); #endif /*UHADEBUG*/ do { if(uha_poll(unit,xs->timeout)) { if (!(xs->flags & SCSI_SILENT)) printf("uha%d: cmd fail\n",unit); if(!(uha_abort(unit,mscp))) { printf("uha%d: abort failed in wait\n",unit); uha_free_mscp(unit,mscp,flags); } xs->error = XS_DRIVER_STUFFUP; return(HAD_ERROR); } } while (!(xs->flags & ITSDONE));/* something (?) else finished */ if(xs->error) { return(HAD_ERROR); } return(COMPLETE); } uha_timeout(struct mscp *mscp) { int unit; int s = splbio(); int port = uha_data[unit].baseport; unit = mscp->xs->adapter; printf("uha%d:%d device timed out\n",unit ,mscp->xs->targ); #ifdef UHADEBUG if(uha_debug & UHA_SHOWMSCPS) uha_print_active_mscp(unit); #endif /*UHADEBUG*/ if((uha_abort(unit,mscp) !=1) || (mscp->flags = MSCP_ABORTED)) { printf("AGAIN"); mscp->xs->retries = 0; /* I MEAN IT ! */ uha_done(unit,mscp,FAIL); } else /* abort the operation that has timed out */ { printf("\n"); timeout(uha_timeout,mscp,2 * hz); mscp->flags = MSCP_ABORTED; } splx(s); } uha_show_scsi_cmd(struct scsi_xfer *xs) { u_char *b = (u_char *)xs->cmd; int i = 0; if(!(xs->flags & SCSI_RESET)) { printf("uha%d:%d:%d-" ,xs->adapter ,xs->targ ,xs->lu); while(i < xs->cmdlen ) { if(i) printf(","); printf("%x",b[i++]); } printf("-\n"); } else { printf("uha%d:%d:%d-RESET-\n" ,xs->adapter ,xs->targ ,xs->lu ); } } uha_print_mscp(mscp) struct mscp *mscp; { printf("mscp:%x op:%x cmdlen:%d senlen:%d\n" ,mscp ,mscp->opcode ,mscp->cdblen ,mscp->senselen); printf(" sg:%d sgnum:%x datlen:%d hstat:%x tstat:%x flags:%x\n" ,mscp->sgth ,mscp->sg_num ,mscp->datalen ,mscp->ha_status ,mscp->targ_status ,mscp->flags); uha_show_scsi_cmd(mscp->xs); } uha_print_active_mscp(int unit) { struct mscp *mscp = uha_data[unit].mscps; int i = NUHA; while(i--) { if(mscp->flags != MSCP_FREE) uha_print_mscp(mscp); mscp++; } }