/* * adaptec/bustek 1542B scsi support */ #include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "../port/error.h" #include "devtab.h" #include "io.h" typedef struct Ctlr Ctlr; typedef struct Cmdblk Cmdblk; typedef struct Letter Letter; enum { Nctl = 1, /* only support one */ Nmbox = 4, /* number of Mailboxes; up to 16? */ Nscatter = 17, /* s/g list limit fixed by controller */ Scatterlen = 6, /* 3 byte length + 3 byte start */ }; struct Letter { uchar status; uchar address[3]; /* 24-bit address, msb first */ }; /* put and get 3-byte longs for adaptec interface */ #define PL3(p,l) (((p)[0]=(l)>>16),((p)[1]=(l)>>8),((p)[2]=(l))) #define GL3(p) (((p)[0]<<16)|((p)[1]<<8)|(p)[2]) /* * Adaptec mailbox pointers point to one of these structures */ struct Cmdblk { uchar op; /* Adapter operation */ uchar addr; /* scsi ID, length-check, LUN */ uchar cmdlen; /* length of CDB */ uchar senselen; /* length of sense data */ uchar datalen[3]; /* transfer data length */ uchar datap[3]; /* transfer data phys. pointer */ uchar cmdlink[3]; /* command link phys. pointer */ uchar linkid; /* SCSI command link ID */ uchar adapterstatus; uchar targetstatus; uchar reserved[2]; uchar cmd[12+64]; /* SCSI command and sense data */ /* the remainder is used by software, not the device */ uchar sglist[Nscatter*Scatterlen]; Rendez iodone; int busy; /* interrupt pending */ int p9status; /* p9 status value */ Cmdblk *next; /* next in free list */ }; enum { Ctrl = 0, /* control and status */ Data = 1, /* command and data port */ Intr = 2, /* interrupt status */ /* control and status bits (write) */ Hardreset = 0x80, Softreset = 0x40, Intreset = 0x20, /* interrupt reset */ Busreset = 0x10, /* scsi bus reset */ /* control and status bits (read) */ Selftest = 0x80, /* self test running */ Diagfail = 0x40, /* diagnostics failed */ Mbxinitreq = 0x20, /* mbx init required */ Idle = 0x10, /* host adapter idle */ OutputBusy = 0x08, /* data output port full */ InputReady = 0x04, /* data input port full */ Invalidcmd = 0x01, /* invalid command */ /* commands */ Cnop = 0, /* no operation */ MboxInit = 1, /* start mbx initialisation */ StartScsi = 2, /* start scsi command */ GetDevices = 0xA, /* get list of installed devices */ GetConfig = 0xB, /* get configuration data */ EnableTarget = 0xC, /* enable target mode */ GetSetup = 0xD, /* get setup data */ EchoCmd = 0x1E, /* echo command data */ /* interrupt status bits */ AnyIntr = 0x80, ScsiResetInt = 0x08, /* SCSI reset detected */ CmdComplete = 0x04, MboxEmptied = 0x02, /* outgoing mbox emptied */ MboxFilled = 0x01, /* incoming mbox filled */ /* outgoing mbox */ MboxFree = 0, MboxStart = 1, MboxAbort = 2, /* incoming mbox */ MboxDone = 1, /* completed without error */ MboxAborted = 2, /* aborted Cmdblk */ MboxUnknown = 3, /* cannot abort: unknown */ MboxError = 4, /* completed with error */ MboxRequired = 0x10, /* target command received when no Cmdblk posted */ /* Cmdblk opcodes */ Initiate = 0, Target = 1, InitiateScatter = 2, DeviceReset = 0x81, }; #define OUT(c,v) outb(ctlr->io+(c), (v)) #define IN(c) inb(ctlr->io+(c)) struct Ctlr { Lock; QLock; int io; /* io port */ int irq; /* interrupt vector */ int dma; /* DMA channel */ int ownid; /* adapter's SCSI ID */ Cmdblk cmds[Nmbox]; struct { /* read/written by adapter; keep these together */ Letter out[Nmbox]; Letter in[Nmbox]; }; Cmdblk *free; /* next free slot */ Rendez mboxes; /* wait for free mbox */ QLock mboxq; /* mutex for mboxes */ int kproc; int mb; /* next mailbox out */ int mi; /* next mailbox in */ }; int scsidebugs[8] = {0}; int scsiownid = 7; static Ctlr softctlr[Nctl]; static int scatter(uchar *, void *, ulong); static void prmbox(Cmdblk *); static int scsiports[] = {0x134, 0x130, 0x234, 0x230, 0x334, 0x330, 0}; static void interrupt(Ureg*); static int cmd(Ctlr*, uchar*, int, uchar*, int); static void scsibusreset(void) { } static int cmd(Ctlr *ctlr, uchar *out, int no, uchar *in, int ni) { int stat; while((IN(Ctrl) & Idle) == 0) ; if(ni) while(IN(Ctrl) & InputReady) IN(Data); while(--no >= 0) { while(IN(Ctrl) & OutputBusy) ; OUT(Data, *out++); } while(--ni >= 0) { while((IN(Ctrl) & InputReady) == 0) ; *in++ = IN(Data); } while((IN(Intr) & CmdComplete) == 0) ; stat = IN(Ctrl); OUT(Ctrl, Intreset); return stat&Invalidcmd? 1: 0; } void resetscsi(void) { Ctlr *ctlr = &softctlr[0]; int i; uchar buf[64], config[3]; memset(ctlr, 0, sizeof(*ctlr)); /* for(i=0; (ctlr->io = scsiports[i]) != 0; i++) if(IN(ProductID) == 0x56 && (IN(ProductID+1)&0xf0) == 0x40) break; if(ctlr->io == 0) return; */ ctlr->io = 0x330; if(IN(Ctrl) == 0xff) { print("1542B not found at %x\n", ctlr->io); ctlr->io = 0; return; /* not there or broken */ } /* OUT(Ctrl, Softreset); for(i=1000000; IN(Ctrl) != (Idle|Mbxinitreq);) if(--i < 0) { print("cannot init adaptec %x\n", IN(Ctrl)); return; } */ /* does something odd to 1542C */ /* * get configuration and prepare the dma controller */ buf[0] = GetConfig; cmd(ctlr, buf, 1, config, sizeof(config)); switch(config[0]){ default: ctlr->io = 0; return; case 1<<0: outb(0x0b, 0x0c); outb(0x0a, 0); ctlr->dma = 0; break; case 1<<5: outb(0xd6, 0xc1); outb(0xd4, 1); ctlr->dma = 5; break; case 1<<6: outb(0xd6, 0xc2); outb(0xd4, 2); ctlr->dma = 6; break; case 1<<7: outb(0xd6, 0xc3); outb(0xd4, 3); ctlr->dma = 7; break; } for(i=0; i<7; i++) if(config[1] & (1<= 7){ ctlr->io = 0; return; } ctlr->irq = 9+i; ctlr->ownid = config[2]&07; print("1542B id %d io 0x%x irq %d dma %d\n", ctlr->ownid, ctlr->io, ctlr->irq, ctlr->dma); ctlr->free = 0; for(i=0; icmds[i]; memset(m, 0, sizeof(*m)); m->senselen = 1; /* disable(!) automatic sense */ m->next = ctlr->free; ctlr->free = m; } buf[0] = MboxInit; buf[1] = Nmbox; PL3(buf+2, PADDR(ctlr->out)); if(cmd(ctlr, buf, 2+3, 0, 0)) print("MboxInit rejected %x %x %x\n", buf[2], buf[3], buf[4]); setvec(Int0vec+ctlr->irq, interrupt); scsiownid = ctlr->ownid; } void initscsi(void) { } static void startcmd(Ctlr *ctlr, Cmdblk *m) { Letter *l; lock(ctlr); m->busy = 1; l = &ctlr->out[ctlr->mb]; ctlr->mb = (ctlr->mb+1)%Nmbox; PL3(l->address, PADDR(m)); l->status = MboxStart; OUT(Data, StartScsi); unlock(ctlr); } static void freebox(Ctlr *ctlr, Cmdblk *m) { lock(ctlr); if((m->next = ctlr->free) == 0) wakeup(&ctlr->mboxes); ctlr->free = m; unlock(ctlr); } static int boxavail(void *a) { return ((Ctlr*)a)->free != 0; } static int isdone(void *a) { return ((Cmdblk*)a)->busy == 0; } int scsiexec(Scsi *p, int rflag) { Ctlr *ctlr = &softctlr[0]; Cmdblk *m; long n; uchar *cp; if (p == 0 || ctlr->io == 0) return 0x0200; /* device not available */ /* * get a free Mailbox and build an adapter command */ while(lock(ctlr), (m = ctlr->free) == 0){ unlock(ctlr); qlock(&ctlr->mboxq); if(waserror()){ qunlock(&ctlr->mboxq); nexterror(); } sleep(&ctlr->mboxes, boxavail, ctlr); poperror(); qunlock(&ctlr->mboxq); } ctlr->free = m->next; unlock(ctlr); p->rflag = rflag; m->p9status = 0; m->addr = p->lun | (p->target<<5); if(p->cmd.lim - p->cmd.ptr > sizeof(m->cmd)) panic("scsiexec"); m->cmdlen = p->cmd.lim - p->cmd.ptr; for(cp = m->cmd; p->cmd.ptr < p->cmd.lim;) *cp++ = *p->cmd.ptr++; m->senselen = sizeof(m->cmd) - m->cmdlen - 4; n = p->data.lim - p->data.ptr; if(n){ m->op = InitiateScatter; /* initiator with scatter/gather */ PL3(m->datap, PADDR(m->sglist)); n = scatter(m->sglist, p->data.ptr, n); PL3(m->datalen, n); } else { /* controller refuses scatter/gather when length = 0 */ m->op = Initiate; /* initiator */ PL3(m->datap, 0); PL3(m->datalen, 0); } m->adapterstatus = 0; m->targetstatus = 0; /* * send the command to the host adapter */ startcmd(ctlr, m); /* * wait for reply */ while(waserror()) ; /* could send abort request to adapter */ sleep(&m->iodone, isdone, m); poperror(); p->status = m->p9status; if(scsidebugs[2]) prmbox(m); freebox(ctlr, m); if (p->status == 0x6000) p->data.ptr = p->data.lim; /* can only assume no residue */ else if(rflag&0x40) { /* devscuz */ uchar *sense = m->cmd + m->cmdlen; if(p->status == 0x6002 && sense[0]==0xf0 && sense[2]&0x20) { /* illegal length indication: find residue */ long res; res = ((sense[3]<<24)| (sense[4]<<16)| (sense[5]<<8)| sense[6]) * 1; /* should be logical block size */ if(res > 0 && res <= n) { p->data.ptr = p->data.lim - res; p->status = 0x6000; } else if(res < 0 && -res < n) { /* adaptec seems to get this the wrong way round */ {static int fred; if(fred==0){fred=1;print("res: %ld\n", res);}} p->data.ptr = p->data.lim + res; p->status = 0x6000; } } else prmbox(m); } return p->status; } static void scsimoan(char *msg, Cmdblk *m) { int i; uchar *sense; print("SCSI error: %s:", msg); print("id=%d.%d cmd=%2.2x status=%2.2x adapter=%2.2x", m->addr>>5, m->addr&7, m->cmd[0], m->targetstatus, m->adapterstatus); sense = m->cmd + m->cmdlen; print(" sense:"); for(i=0; i<10; i++) print(" %2.2x", sense[i]); print("\n"); } static void interrupt(Ureg *ur) { Ctlr *ctlr = &softctlr[0]; Cmdblk *m; ulong pa; int intr; int i, status; USED(ur); intr = IN(Intr); if((intr&AnyIntr)==0){ print("intr: not valid %x\n", intr); return; /* not valid */ } OUT(Ctrl, Intreset); if((intr&MboxFilled)==0){ print("intr: %x, no mbox\n", intr); return; } for(; ctlr->in[i=ctlr->mi].status != MboxFree; ctlr->mi = (ctlr->mi+1)%Nmbox){ if((status = ctlr->in[i].status) == MboxFree) continue; pa = GL3(ctlr->in[i].address); ctlr->in[i].status = MboxFree; m = (Cmdblk*)KADDR(pa); if(status&0xF0 || !(m >= &ctlr->cmds[0] && m < &ctlr->cmds[Nmbox]) || !m->busy){ /* consistency check */ print("1542B: bad mbox pointer i%x s%x #%lux", intr, status, pa); continue; } if(scsidebugs[1]) prmbox(m); m->p9status = 0x1000 | m->adapterstatus; switch(m->adapterstatus){ default: scsimoan("adapter", m); /* might adapter reset be required in some cases? */ break; case 0x12: /* data overrun/underrun */ scsimoan("overrun/underrun", m); break; case 0x11: /* selection timed out */ m->p9status = 0x0200; if(scsidebugs[0]) scsimoan("timeout", m); break; case 0x13: /* bus dropped unexpectedly */ case 0x14: /* phase error */ scsimoan("SCSI bus error", m); scsibusreset(); break; case 0x00: case 0x0B: m->p9status = 0x6000 | m->targetstatus; if(m->targetstatus && scsidebugs[0]) scsimoan("device", m); break; } m->busy = 0; wakeup(&m->iodone); } } static int scatter(uchar *list, void *base, ulong nb) { char *p = (char*)base; uchar *sp = list; int limit = Nscatter; ulong len; for(; nb; nb -= len, p += len, sp += Scatterlen){ if(--limit < 0) panic("Adaptec I/O too scattered"); if(((ulong)p&0xF0000000) != KZERO) panic("adaptec: scatter non-kernel #%lux", p); len = BY2PG - (PADDR(p)&(BY2PG-1)); /* the rest of the page */ if(len > nb) len = nb; PL3(sp, len); PL3(sp+3, PADDR(p)); } return sp - list; } static void prmbox(Cmdblk *m) { int i; uchar *sense; ulong len; len = GL3(m->datalen); print("scsi #%lux: op=%2.2x cmd=%d [", m, m->op, m->cmdlen); for(i=0; icmdlen; i++) print(" %2.2x", m->cmd[i]); print("] id=%d lun=%d df=%d", (m->addr>>5)&7, m->addr&7, (m->addr>>3)&3); print(" dlen=%lx dptr=%lx\n", len, GL3(m->datap)); print(" astat=%2.2x dstat=%2.2x [", m->adapterstatus, m->targetstatus); if(m->op==InitiateScatter) { for(i=0; isglist[i+3]), GL3(&m->sglist[i])); } print("]\n"); sense = m->cmd + m->cmdlen; for(i=0; i<10; i++) print(" %2.2x", sense[i]); print("\n"); } Scsibuf * scsialloc(ulong n) { Scsibuf *b; b = xalloc(sizeof(Scsibuf)); b->virt = xalloc(n); b->phys = (void*)-1; /* redundant with scatter/gather */ return b; }