/* * DPT 3334 SCSI Host Adapter. * Version 0.9 - Sat Apr 4, 1997 - John L. Chmielewski, jlc@att.com * Needs work: * handle multiple controllers; */ #include "u.h" #include "lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #define ETA_SIG (((long) 'E') | (((long) 'A') << 8) |\ (((long) 'T') << 16) | (((long) 'A') << 24)) enum { Nctl = 1, /* Max # of Controllers */ CtrlID = 7, /* Default Controller ID */ NTarget = 16, /* Max targets/controller-chan */ Timeout = 10, /* Timeout in seconds */ SenLen = 0xFF, /* Maximum Sense Data Length */ PCIvid = 0x1044, /* PCI Vendor ID: DPT */ PCIdid = 0xA400, /* PCI Device ID: DPT */ }; enum { /* Eata Register Commands */ ReadCfg = 0xFD, /* Read Configuration Data, DMA */ SendCP = 0xFF, /* Execute Command, DMA */ Reset = 0xF9, /* Reset Controller */ }; enum { /* Eata Register Sets - PCI */ Cmd = 0x17, /* Command Register (write) */ CP3 = 0x15, /* CP Address (MSB) (write) */ CP2 = 0x14, /* CP Address (MSB -1) (write) */ CP1 = 0x13, /* CP Address (MSB-2) (write) */ CP0 = 0x12, /* CP Address (LSB) (write) */ ASR = 0X18, /* Aux Status Register (read) */ SR = 0x17, /* Status Register (read) */ DR = 0x10, /* 16 bit Data Register (read) */ }; enum { /* Eata Register Sets Bits */ /* Status Register */ ERROR = 0x01, /* Command ended in error */ DRQ = 0x08, /* Drive is Ready */ SC = 0x10, /* Seek Complete */ RDY = 0x40, /* Active data request */ BSY = 0x80, /* Controller busy - clear IRQ */ /* Aux Status Register */ ABSY = 0x01, /* Controller busy */ IRQ = 0x02, /* Drive interrupt asserted */ }; /* * DPT DMA Status Packet * Big Endian Format */ typedef struct DPTstat DPTstat; struct DPTstat { uchar info; /* Bits defined */ uchar sbs; /* SCSI Bus Status */ uchar reserved0[2]; ulong irl; /* Bytes Not Transfered */ ulong va; /* Virtual Address of CP */ uchar idmsg; uchar quemsg; uchar tagmsg; uchar msg[9]; }; enum { /* Status Packet bits */ /* info byte bits */ CS = 0x7F, /* Controller Status */ EOC = 0x80, /* End of Command - not safe */ }; /* * EATA Command Packet * */ typedef struct EATAcp EATAcp; struct EATAcp { uchar control; /* Bits defined */ uchar ReqSenLen; /* AutoRequestSense Data length */ uchar reserved1[3]; uchar FWNest; /* Firmware Nested Byte for RAID */ uchar PhsUnit; /* Physical Byte for RAID */ uchar device; /* Bits defined */ uchar msg[4]; /* Identify and Disconnect Message */ uchar CDB[12]; /* Total SCSI CDB */ ulong DTL; /* Byte Swapped Data Length In Bytes */ ulong va; /* Command Packet Virtual Address */ ulong dataDMA; /* Byte Swapped Data Physical Address */ ulong statDMA; /* Byte Swapped Status Packet Physical */ ulong reqDMA; /* Byte Swapped AutoRequestSense Phys */ }; enum { /* Command Packet bits */ /* control byte bits */ Sreset = 0x01, /* Issue A SCSI Bus Reset */ Init = 0x02, /* ReInitialize The Controller */ ReqSen = 0x04, /* Do Auto Request Sense */ SG = 0x08, /* Data Pointer To S.G. List */ Quick = 0x10, /* Quick PIOin, DMA in/out */ Interpret = 0x20, /* CDB Interpreted By Controller */ DataOut = 0x40, /* Data Out Phase With Command */ DataIn = 0x80, /* Data In Phase With Command */ /* device byte bits */ id = 0x1F, /* SCSI Target ID */ Channel = 0xE0, /* HBA Channel Number */ /* Message byte 0 bits */ MsgLun = 0x07, /* Message Lun */ MsgLuntar = 0x20, /* Target Routine */ MsgDisPri = 0x40, /* Disconnect Reconnect */ MsgIdent = 0x80, /* Indentify */ }; /* * ReadConfig Data Structure - This Structure Contains The EATA Configuration * Big Endian Format */ typedef struct DPTcfg DPTcfg; struct DPTcfg { /* Extra bytes returned form a SCSI Inquiry Command */ uchar DevType; uchar PageCode; uchar reserved2; uchar ConfigLength; /* Length In Bytes After This Field */ ulong EATAsig; /* EATA Signature Bytes */ uchar EATAver; /* EATA Version Number */ uchar byte9; /* Bits defined */ ushort PadLen; /* Pad Bytes For PIO Commands */ uchar reserved3; uchar Chan2; /* Channel 2 SCSI ID */ uchar Chan1; /* Channel 1 SCSI ID */ uchar Chan0; /* Channel 0 SCSI ID */ ulong CPlength; /* Command Packet Length */ ulong SPlength; /* Status Packet Length */ ushort QueueSize; /* Controller Queue Depth */ uchar reserved4[2]; ushort SG_Size; /* Maximum Scatter Gather List Size */ uchar byte30; /* Bits defined */ uchar IRQ; /* IRQ address */ uchar byte32; /* Bits defined */ uchar byte33; /* Bits defined */ uchar MaxLUN; /* Maximum SCSI LUN Supported */ uchar byte35; /* Bits defined */ uchar RaidNum; /* Raid Host Adapter Number */ }; enum { /* Read Config Bits */ /* Byte 9 */ OverLapCmds = 0x01, /* Overlapped Cmds Supported */ TargetMode = 0x02, /* SCSI Target Mode Supported */ TrunNotNec = 0x04, /* Truncate Command Not Supported */ MoreSupported = 0x08, /* More Command Supported */ MAsupported = 0x10, /* DMA Mode Supported */ DMAvalid = 0x20, /* DMA Channel Field Is Valid. */ ATAdevice = 0x40, /* This Is An ATA Device */ HBAvalid = 0x80, /* HBA field Is Valid */ /* Byte 30 */ IRQn = 0x0F, /* IRQ Number */ IRQt = 0x10, /* IRQ Trigger: 0 = Edge, 1 = Level */ Secondary = 0x20, /* Controller Not At Address 0x170 */ DMAchan = 0xD0, /* DMA Channel Index For ISA */ /* Byte 32 */ Disable = 0x01, /* ISA I/O Address Disabled */ ForceAddr = 0x02, /* PCI Forced To An EISA/ISA Addr */ SG64K = 0x04, /* 64K of SG space */ SGUAE = 0x08, /* Supports unaligned SG Table */ /* Byte 33 */ MaxID = 0x1F, /* Maximun SCSI Target ID Supported */ MaxChan = 0xE0, /* Maximun Channel Number Supported */ /* Byte 35 */ AutoTerm = 0x08, /* Support auto term (low byte) */ PCIM1 = 0x10, /* PCI M1 chipset */ RIDQ = 0x20, /* Raid ID may be questionable */ PCIbus = 0x40, /* PCI Adapter Flag */ EISAbus = 0x80, /* EISA Adapter Flag */ }; typedef struct Target Target; struct Target { int done; int senval; /* Sense data valid */ uchar sense[SenLen]; /* Sense data */ EATAcp; /* Command Packet */ uchar ctlrStatus; uchar scsiStatus; }; typedef struct Ctlr Ctlr; struct Ctlr { int port; /* base port */ int channel; /* Controller Channel Number */ DPTcfg; /* Controller Configuration */ DPTstat; /* Status Packet */ PCIcfg; /* PCI info */ Target target[NTarget]; /* CCP's in use */ }; static Ctlr ctlr[Nctl]; static long LongEndCvt(long x) { long t; char *p = (char *) &t; *p++ = (x>>24) & 0xFF; *p++ = (x>>16) & 0xFF; *p++ = (x>>8) & 0xFF; *p = x & 0xFF; return t; } static short ShortEndCvt(short x) { short t; char *p = (char *) &t; *p++ = (x>>8) & 0xFF; *p = x & 0xFF; return t; } #ifdef DEBUG static void printinfo(Ctlr *cp) { print("\nVendor ID: 0x%x\tDevice ID: 0x%x\tBase Address: 0x%x\n", cp->vid, cp->did, cp->baseaddr[0] & ~0x01); print("Signiture: %x\tIRQ: %d\t\t\tDMA: %d\n", LongEndCvt(cp->EATAsig), cp->byte30 & IRQn, cp->byte30 & DMAchan); print("CtlrChan0ID: %d\t\tCtlrChan1ID: %d\t\tCtlrChan2ID: %d\n", cp->Chan0, cp->Chan1, cp->Chan2); print("MaxChanID: %d\t\tMaxChanNum: %d\t\tInterrupt Vector: %d\n\n", cp->byte33 & MaxID, cp->byte33 & MaxChan, Int0vec); } #endif /* * Wait until command completes for target */ static void dptwait(Ctlr *cp, Target *tp) { ulong start; int x; static void interrupt(Ureg*, void*); x = spllo(); start = m->ticks; while (TK2SEC(m->ticks - start) < Timeout && tp->done == 0) ; if (TK2SEC(m->ticks - start) >= Timeout) { print("dptwait timed out\n"); interrupt(0, cp); } splx(x); } static void dptcmd(int port, ulong ccpaddr) { ulong paddr; paddr = PADDR(ccpaddr); outb(port+CP0, paddr & 0xFF); outb(port+CP1, (paddr>>8) & 0xFF); outb(port+CP2, (paddr>>16) & 0xFF); outb(port+CP3, (paddr>>24) & 0xFF); outb(port+Cmd, SendCP); } /* * Send SCSI command, wait for interrupt and then clear it. */ static void dptpoll(Ctlr *cp, ulong ccpaddr) { ulong start, port = cp->port; int x; x = splhi(); dptcmd(port, ccpaddr); splx(x); x = spllo(); start = m->ticks; while (TK2SEC(m->ticks - start) < Timeout && (inb(port+ASR)&IRQ) == 0) ; inb(port+SR); /* clear interrupt */ cp->info &= ~EOC; /* status packet available */ if (TK2SEC(m->ticks - start) >= Timeout) { print("dptpoll timed out\n"); } splx(x); } static int dptio(Ctlr *cp, Scsi *p, int rw) { Target *tp = &cp->target[p->target]; EATAcp *ccp = tp; ulong len; ushort status; int x; /* * If this is a request-sense and there is valid sense data * from the loas command, return it immediately. */ if (p->cmd.base[0] == ScsiExtsens && tp->senval) { len = tp->ReqSenLen; memmove(p->data.ptr, tp->sense, len); p->data.ptr += len; tp->senval = 0; return 0x6000; } /* * Fill in the ccp */ memset(ccp, 0, sizeof(EATAcp)); ccp->control |= (rw == ScsiIn ? DataIn : DataOut) | ReqSen; ccp->device = (cp->channel<<5) | (p->target & id); ccp->ReqSenLen = SenLen; ccp->dataDMA = LongEndCvt(PADDR(p->data.base)); ccp->DTL = LongEndCvt(p->data.lim - p->data.base); ccp->statDMA = LongEndCvt(PADDR(&cp->DPTstat)); ccp->reqDMA = LongEndCvt(PADDR(tp->sense)); ccp->ReqSenLen = SenLen; ccp->va = (ulong) tp; ccp->msg[0] |= (p->lun & MsgLun) | MsgDisPri | MsgIdent; len = p->cmd.lim - p->cmd.base; memmove(ccp->CDB, p->cmd.base, len); /* * Send the request */ x = splhi(); tp->done = 0; dptcmd(cp->port, (ulong) ccp); splx(x); /* * Wait for the request to complete. */ dptwait(cp, tp); switch (tp->ctlrStatus) { case 0x00: /* no error */ status = 0x6000 | tp->scsiStatus; p->data.ptr = p->data.lim; /* assume no residue */ break; case 0x01: /* selection timed out */ status = 0x20; break; default: /* controller error */ status = 0x1000 | tp->ctlrStatus; break; } return status; } static int dptexec(Scsi *p, int rw) { Ctlr *cp = &ctlr[0]; if (p == 0 || cp->port == 0) return 0x6001; /* device not available */ if (p->target == cp->Chan0) return 0x6002; /* controller ID */ return p->status = dptio(cp, p, rw); } static void interrupt(Ureg*, void *arg) { Ctlr *cp; Target *tp; cp = arg; /* * Check the interrupt. */ if ((cp->info & EOC) == 0) return; /* * Get ccb address, status information, and set flags. */ tp = (Target *) cp->va; if (tp == 0) panic("DPT: no target for ccb #%lux\n", tp); tp->ctlrStatus = cp->info & CS; tp->scsiStatus = cp->sbs; cp->info &= ~EOC; tp->done = 1; if (tp->scsiStatus == 0x02) tp->senval = 1; /* indicate command returned sense data */ inb(cp->port+SR); /* clear the interrupt */ } /* * Locate the SCSI controller PCI cards. */ static Ctlr* locate(void) { Ctlr *cp = &ctlr[0]; static int devno; int i; for(i = 0; i < Nctl; ++i) { cp = &ctlr[i]; memset(cp, 0, sizeof(Ctlr)); cp->vid = PCIvid; cp->did = PCIdid; if((devno = pcimatch(0, devno, cp)) == -1) break; /* no boards found */ cp->port = cp->baseaddr[0] & ~0x01; } if (cp->port) return &ctlr[0]; /* always return the 1st board found */ return 0; } int (* dpt3334reset(void))(Scsi*, int) { Ctlr *cp; EATAcp *ccp; /* * Assume one controller. */ if ((cp = locate()) == 0) return 0; /* * Do a Read Vital Data Page SCSI command * to get the configuration information * instead of using a Read Config Command. * This will cause an interrupt, but which * one isn't known yet. */ ccp = &cp->target[CtrlID].EATAcp; ccp->control |= (Interpret | DataIn | ReqSen); ccp->dataDMA = LongEndCvt(PADDR(&cp->DPTcfg)); ccp->DTL = LongEndCvt(sizeof(DPTcfg)); ccp->statDMA = LongEndCvt(PADDR(&cp->DPTstat)); ccp->va = (ulong) &cp->target[CtrlID]; ccp->msg[0] |= MsgIdent; ccp->CDB[0] = ScsiInquiry; /* SCSI Command */ ccp->CDB[1] |= 0x01; /* SCSI Extent */ ccp->CDB[2] = 0xC1; /* SCSI Page */ ccp->CDB[7] = sizeof(DPTcfg); /* Data Length */ dptpoll(cp, (ulong) ccp); /* get controller configuration */ setvec(Int0vec+(cp->byte30 & IRQn), interrupt, cp); outb(cp->port+Cmd, Reset); /* Reset Controller and SCSI Bus */ delay(5000); #ifdef DEBUG printinfo(cp); #endif return dptexec; }