## diffname pc/adaptec.c 1993/0114 ## diff -e /dev/null /n/bootesdump/1993/0114/sys/src/9/pc/adaptec.c 0a /* * Adaptec AHA-154[02]B Intelligent Host Adapter. */ #include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "../port/error.h" #include "ureg.h" /* * Uniprocessors can't (don't need to) lock. */ #define lock(l) #define unlock(l) enum { NCtlr = 1, NTarget = 8, /* targets per controller */ Port = 0x330, /* factory defaults: I/O port */ CtlrID = 7, /* adapter SCSI id */ Irq = 11, /* interrupt request level */ }; enum { /* registers */ Rc = 0, /* WO: control */ Rs = 0, /* RO: status */ Rcdo = 1, /* WO: command/data out */ Rdi = 1, /* RO: data in */ Rif = 2, /* RO: interrupt flags */ }; enum { /* Rc */ Scrst = 0x10, /* SCSI bus reset */ Irst = 0x20, /* interrupt reset */ Srst = 0x40, /* soft reset */ Hrst = 0x80, /* hard reset */ }; enum { /* Rs */ Invdcmd = 0x01, /* invalid host adapter command */ Df = 0x04, /* data in port full */ Cdf = 0x08, /* command/data port full */ Idle = 0x10, /* SCSI host adapter idle */ Init = 0x20, /* mailbox initialisation required */ Diagf = 0x40, /* internal diagnostic failure */ Stst = 0x80, /* self testing in progress */ }; enum { /* Rcdo */ Cnop = 0x00, /* no operation */ Cmbinit = 0x01, /* mailbox initialisation */ Cstart = 0x02, /* start SCSI command */ Cbios = 0x03, /* start PC/AT BIOS command */ Cinquiry = 0x04, /* adapter inquiry */ Cmboie = 0x05, /* enable mailbox out available interrupt */ Cselection = 0x06, /* set selection timeout */ Cbuson = 0x07, /* set bus-on time */ Cbusoff = 0x08, /* set bus-off time */ Ctransfer = 0x09, /* set transfer speed */ Cdevices = 0x0A, /* return installed devices */ Cconfiguration = 0x0B, /* return configuration data */ Ctenable = 0x0C, /* enable target mode */ Csetup = 0x0D, /* return setup data */ Cwbuff = 0x1A, /* write adapter channel 2 buffer */ Crbuff = 0x1B, /* read adapter channel 2 buffer */ Cwfifo = 0x1C, /* write adapter FIFO buffer */ Crfifo = 0x1D, /* read adapter FIFO buffer */ Cecho = 0x1F, /* ECHO command data */ Cdiag = 0x20, /* adapter diagnostic */ Coptions = 0x21, /* set host adapter options */ }; enum { /* Rif */ Mbif = 0x01, /* mailbox in full */ Mboa = 0x02, /* mailbox out available */ Hacc = 0x04, /* host adapter command complete */ Scrd = 0x08, /* SCSI reset detected */ Ai = 0x80, /* any interrupt */ }; typedef struct { uchar cmd; /* command */ uchar msb; /* CCB pointer MSB */ uchar mid; /* CCB pointer MID */ uchar lsb; /* CCB pointer LSB */ } Mbox; enum { /* mailbox commands */ Mbfree = 0x00, /* mailbox is free */ Mbostart = 0x01, /* start SCSI or adapter command */ Mboabort = 0x02, /* abort SCSI or adapter command */ Mbiok = 0x01, /* CCB completed without error */ Mbiabort = 0x02, /* CCB aborted by host */ Mbinx = 0x03, /* aborted CCB not found */ Mbierror = 0x04, /* CCB completed with error */ }; typedef struct { uchar op; /* command control block operation code */ uchar ctl; /* address and direction control */ uchar cmdlen; /* SCSI command length */ uchar reqlen; /* request sense allocation length */ uchar datalen[3]; /* data length (MSB, MID, LSB) */ uchar dataptr[3]; /* data pointer (MSB, MID, LSB) */ uchar linkptr[3]; /* link pointer (MSB, MID, LSB) */ uchar linkid; /* command linking identifier */ uchar hastat; /* host adapter status */ uchar tarstat; /* target device status */ uchar reserved[2]; uchar cs[12+0xFF]; /* SCSI command and sense bytes */ } Ccb; enum { /* op */ OInitiator = 0x00, /* initiator CCB */ OTarget = 0x01, /* target CCB */ Osg = 0x02, /* initiator CCB with scatter/gather */ Ordl = 0x03, /* initiator CCB, residual data length returned */ Osgrdl = 0x04, /* initiator CCB, both of the above */ Obdr = 0x81, /* bus device reset */ }; enum { /* ctl */ CCBdatain = 0x08, /* inbound data transfer, length is checked */ CCBdataout = 0x10, /* outbound data transfer, length is checked */ }; enum { /* hastat */ Eok = 0x00, /* no host adapter detected error */ Etimeout = 0x11, /* selection timeout */ Elength = 0x12, /* data over/under run */ Ebusfree = 0x13, /* unexpected bus free */ Ephase = 0x14, /* target bus phase sequence error */ Eopcode = 0x16, /* invalid CCB opcode */ Elink = 0x17, /* linked CCB does not have same LUN */ Edirection = 0x18, /* invalid target direction received from host */ Eduplicate = 0x19, /* duplicate CCB received in target mode */ Esegment = 0x1A, /* invalid CCB or segment list parameter */ }; enum { /* tarstat */ Sgood = 0x00, /* good status */ Scheck = 0x02, /* check status */ Sbusy = 0x08, /* LUN busy */ }; typedef struct Target Target; typedef struct Ctlr Ctlr; struct Target { QLock; Rendez; Ccb ccb; ulong paddr; /* physical address of ccb */ uchar *sense; /* address of returned sense data */ int done; Target *active; /* link on active list */ }; struct Ctlr { Lock; ulong port; /* I/O port */ ulong id; /* adapter SCSI id */ uchar cmd[5]; /* adapter command out */ uchar cmdlen; /* adapter command out length */ uchar data[256]; /* adapter command data in */ uchar datalen; /* adapter command data in length */ Mbox mb[NTarget+NTarget]; /* mailbox out + mailbox in */ Mbox *mbox; /* current mailbox out index into mb */ Mbox *mbix; /* current mailbox in index into mb */ Target target[NTarget]; Target *active; /* link on active list */ }; static Ctlr softctlr[NCtlr]; /* * Issue a command to the controller. The command and its length is * contained in ctlr->cmd and ctlr->cmdlen. If any data is to be * returned, ctlr->datalen should be non-0, and the returned data will * be placed in ctlr->data. * If we see Hacc set, bail out, we'll process * the invalid command at interrupt time. */ static void issue(Ctlr *ctlr) { int len; len = 0; while(len < ctlr->cmdlen){ if((inb(ctlr->port+Rs) & Cdf) == 0){ outb(ctlr->port+Rcdo, ctlr->cmd[len]); len++; } if(inb(ctlr->port+Rif) & Hacc) return; } if(ctlr->datalen){ len = 0; while(len < ctlr->datalen){ if(inb(ctlr->port+Rs) & Df){ ctlr->data[len] = inb(ctlr->port+Rdi); len++; } if(inb(ctlr->port+Rif) & Hacc) return; } } } static int done(void *arg) { return ((Target*)arg)->done; } static int scsiio(int bus, Scsi *p, int rw) { Ctlr *ctlr; Target *tp; ushort status; ulong len, s; /* * Wait for the target to become free, * then set it up. The Adaptec will allow us to * queue multiple transactions per target, but * gives no guarantee about ordering, so we just * allow one per target. */ ctlr = &softctlr[bus]; tp = &ctlr->target[p->target]; qlock(tp); if(waserror()){ qunlock(tp); nexterror(); } /* * If this is a request-sense and we have valid sense data * from the last command, return it immediately. * A pox on these weird enum names and the WD33C93A status * codes. */ if(p->cmd.base[0] == ScsiExtsens && tp->sense){ len = 8+tp->sense[7]; memmove(p->data.ptr, tp->sense, len); p->data.ptr += len; tp->sense = 0; qunlock(tp); poperror(); return 0x6000; } tp->sense = 0; /* * Fill in the ccb. */ tp->ccb.op = Ordl; tp->ccb.ctl = (p->target<<5)|p->lun; len = p->cmd.lim - p->cmd.base; tp->ccb.cmdlen = len; memmove(tp->ccb.cs, p->cmd.base, len); tp->ccb.reqlen = 0xFF; len = p->data.lim - p->data.base; tp->ccb.datalen[0] = (len>>16) & 0xFF; tp->ccb.datalen[1] = (len>>8) & 0xFF; tp->ccb.datalen[2] = len; if(len == 0) tp->ccb.ctl |= CCBdataout|CCBdatain; else if(rw == ScsiIn) tp->ccb.ctl |= CCBdatain; else tp->ccb.ctl |= CCBdataout; len = PADDR(p->data.base); tp->ccb.dataptr[0] = (len>>16) & 0xFF; tp->ccb.dataptr[1] = (len>>8) & 0xFF; tp->ccb.dataptr[2] = len; tp->ccb.linkptr[0] = tp->ccb.linkptr[1] = tp->ccb.linkptr[2] = 0; tp->ccb.linkid = 0; tp->ccb.hastat = tp->ccb.tarstat = 0; tp->ccb.reserved[0] = tp->ccb.reserved[1] = 0; /* * Link the target onto the beginning of the * ctlr active list and start the request. * The interrupt routine has to be able to take * requests off the queue in any order. */ s = splhi(); lock(ctlr); tp->done = 0; tp->active = ctlr->active; ctlr->active = tp; ctlr->mbox->msb = (tp->paddr>>16) & 0xFF; ctlr->mbox->mid = (tp->paddr>>8) & 0xFF; ctlr->mbox->lsb = tp->paddr & 0xFF; ctlr->mbox->cmd = Mbostart; ctlr->cmd[0] = Cstart; ctlr->cmdlen = 1; ctlr->datalen = 0; issue(ctlr); ctlr->mbox++; if(ctlr->mbox >= &ctlr->mb[NTarget]) ctlr->mbox = ctlr->mb; unlock(ctlr); splx(s); /* * Wait for the request to complete * and return the status. */ tsleep(tp, done, tp, 60*5*1000); if(done(tp) == 0) print("adaptec%d: timeout cmd=#%2.2ux\n", p->target, tp->ccb.cs[0]); if((status = (tp->ccb.hastat<<8)) == 0) status = 0x6000; status |= tp->ccb.tarstat; len = (tp->ccb.datalen[0]<<16)|(tp->ccb.datalen[1]<<8)|tp->ccb.datalen[2]; p->data.ptr = p->data.lim - len; /* * If the command returned sense data, keep a note * of where it is for a subsequent request-sense command. */ if(tp->ccb.tarstat == Scheck && tp->ccb.hastat == Eok) tp->sense = &tp->ccb.cs[tp->ccb.cmdlen]; qunlock(tp); poperror(); return status; } int scsiexec(Scsi *p, int rw) { USED(rw); if(p->target == CtlrID) return 0x6002; return p->status = scsiio(0, p, rw); } static void interrupt(Ureg *ur) { Ctlr *ctlr = &softctlr[0]; uchar rif, rs; Target *tp, **l; ulong paddr; USED(ur); lock(ctlr); /* * Save and clear the interrupt(s). The only * interrupts expected are Hacc, which we ignore, * and Mbif which means something completed. */ rif = inb(ctlr->port+Rif); rs = inb(ctlr->port+Rs); outb(ctlr->port+Rc, Irst); if(rif & ~(Ai|Hacc|Mbif)) print("adaptec%d: interrupt #%2.2ux\n", 0, rif); if((rif & Hacc) && (rs & Invdcmd)) print("adaptec%d: invdcmd #%2.2ux, len %d\n", 0, ctlr->cmd[0], ctlr->cmdlen); /* * Look for something in the mail. * If there is, try to find the recipient from the * ccb address, take it off the active list and * wakeup whoever. */ while(ctlr->mbix->cmd){ paddr = (ctlr->mbix->msb<<16)|(ctlr->mbix->mid<<8)|ctlr->mbix->lsb; l = &ctlr->active; for(tp = *l; tp; tp = tp->active){ if(tp->paddr == paddr) break; l = &tp->active; } if(tp == 0) panic("adaptec%d: no target for ccb #%lux\n", 0, paddr); *l = tp->active; ctlr->mbix->cmd = 0; tp->done = 1; wakeup(tp); ctlr->mbix++; if(ctlr->mbix >= &ctlr->mb[NTarget+NTarget]) ctlr->mbix = &ctlr->mb[NTarget]; } unlock(ctlr); } static void reset(Ctlr *ctlr, ulong port, ulong id) { ulong paddr; int i; /* * Initialise the software controller and set the board * scanning the mailboxes. * Need code here to tidy things up if we're * resetting after being active. */ memset(ctlr, 0, sizeof(Ctlr)); ctlr->port = port; ctlr->id = id; for(i = 0; i < NTarget; i++){ ctlr->target[i].paddr = PADDR(&ctlr->target[i].ccb); ctlr->mbox = ctlr->mb; ctlr->mbix = &ctlr->mb[NTarget]; } ctlr->cmd[0] = Cmbinit; paddr = PADDR(ctlr->mb); ctlr->cmd[1] = NTarget; ctlr->cmd[2] = (paddr>>16) & 0xFF; ctlr->cmd[3] = (paddr>>8) & 0xFF; ctlr->cmd[4] = paddr & 0xFF; ctlr->cmdlen = 5; ctlr->datalen = 0; issue(ctlr); } void resetscsi(void) { uchar rs; ulong port; /* * For the moment assume the factory default settings. */ port = Port; /* * Attempt to soft-reset the board and reset * the SCSI bus. If the board state doesn't settle to * idle with mailbox initialisation required, either * it isn't an Adaptec or it's broken. */ outb(port+Rc, Srst|Scrst); delay(500); if((rs = inb(port+Rs)) != (Init|Idle)){ print("adaptec%d: reset status #%2.2ux\n", 0, rs); return; } setvec(Int0vec+Irq, interrupt); reset(&softctlr[0], port, CtlrID); } /* * Known to devscsi.c. */ int scsidebugs[8]; int scsiownid = CtlrID; void initscsi(void) { } /* * Quick hack. Need to do a better job of dynamic initialisation * for machines with peculiar memory/cache restictions. * Also, what about 16Mb address limit on the Adaptec? */ static ulong bufdatasize; void scsibufreset(ulong datasize) { bufdatasize = datasize; } Scsibuf * scsibuf(void) { Scsibuf *b; b = smalloc(sizeof(*b)); b->virt = smalloc(bufdatasize); b->phys = (void *)(PADDR(b->virt)); return b; } void scsifree(Scsibuf *b) { free(b->virt); free(b); } /* * Hack for devvid */ Scsibuf * scsialloc(ulong n) { Scsibuf *b; b = smalloc(sizeof(*b)); b->virt = smalloc(n); b->phys = (void *)(PADDR(b->virt)); return b; } . ## diffname pc/adaptec.c 1993/0119 ## diff -e /n/bootesdump/1993/0114/sys/src/9/pc/adaptec.c /n/bootesdump/1993/0119/sys/src/9/pc/adaptec.c 371a if(ctlr->port == 0) error(Enodev); . 370c Ctlr *ctlr = &softctlr[0]; . ## diffname pc/adaptec.c 1993/0220 ## diff -e /n/bootesdump/1993/0119/sys/src/9/pc/adaptec.c /n/bootesdump/1993/0220/sys/src/9/pc/adaptec.c 488c /*print("adaptec%d: reset status #%2.2ux\n", 0, rs);/**/ . ## diffname pc/adaptec.c 1993/0223 ## diff -e /n/bootesdump/1993/0220/sys/src/9/pc/adaptec.c /n/bootesdump/1993/0223/sys/src/9/pc/adaptec.c 490d 487,488c if(inb(port+Rs) != (Init|Idle)) . 471d ## diffname pc/adaptec.c 1993/0915 ## diff -e /n/bootesdump/1993/0223/sys/src/9/pc/adaptec.c /n/fornaxdump/1993/0915/sys/src/brazil/pc/adaptec.c 493,546c return aha1542exec; . 489,491c setvec(Int0vec+isa->irq, interrupt); reset(&softctlr[0], isa->port, CtlrID); . 486,487c if(inb(isa->port+Rs) != (Init|Idle)) return 0; . 484c outb(isa->port+Rc, Hrst|Scrst); . 479c * Attempt to hard-reset the board and reset . 476c if(isaconfig("scsi", 0, &aha1542) == 0) return 0; . 474c * See if we have any configuration info. * Only one controller for now. . 471c ISAConf *isa = &aha1542; . 468,469c static ISAConf aha1542 = { "aha1542", Port, Irq, 0, 0, }; int (* aha1542reset(void))(Scsi*, int) . 433c if(active.machs > 1) unlock(ctlr); . 390c if(active.machs > 1) lock(ctlr); . 367,368c static int aha1542exec(Scsi *p, int rw) . 337c if(active.machs > 1) unlock(ctlr); . 317c if(active.machs > 1) lock(ctlr); . 14,19d 2c * Adaptec AHA-154[02][BC] Intelligent Host Adapter. * Needs work: * handle multiple controllers; * set options from user-level; * split out to handle ultrastor too; . ## diffname pc/adaptec.c 1993/1113 # deleted ## diff -e /n/fornaxdump/1993/0915/sys/src/brazil/pc/adaptec.c /n/fornaxdump/1993/1113/sys/src/brazil/pc/adaptec.c 1,505d