|
|
1.1 ! root 1: /* ! 2: * Adaptec AHA-154[02][BC] Intelligent Host Adapter. ! 3: * Also works for ! 4: * Buslogic BT-545C ISA adapter; ! 5: * Buslogic BT-747 EISA adapter. ! 6: * Needs work: ! 7: * handle multiple controllers; ! 8: * set options from user-level; ! 9: * split out to handle ultrastor too; ! 10: */ ! 11: #include "u.h" ! 12: #include "../port/lib.h" ! 13: #include "mem.h" ! 14: #include "dat.h" ! 15: #include "fns.h" ! 16: #include "io.h" ! 17: #include "../port/error.h" ! 18: ! 19: #include "ureg.h" ! 20: ! 21: enum { ! 22: NCtlr = 1, ! 23: NTarget = 8, /* targets per controller */ ! 24: ! 25: Port = 0x330, /* factory defaults: I/O port */ ! 26: CtlrID = 7, /* adapter SCSI id */ ! 27: Irq = 11, /* interrupt request level */ ! 28: }; ! 29: ! 30: enum { /* registers */ ! 31: Rc = 0, /* WO: control */ ! 32: Rs = 0, /* RO: status */ ! 33: Rcdo = 1, /* WO: command/data out */ ! 34: Rdi = 1, /* RO: data in */ ! 35: Rif = 2, /* RO: interrupt flags */ ! 36: }; ! 37: ! 38: enum { /* Rc */ ! 39: Scrst = 0x10, /* SCSI bus reset */ ! 40: Irst = 0x20, /* interrupt reset */ ! 41: Srst = 0x40, /* soft reset */ ! 42: Hrst = 0x80, /* hard reset */ ! 43: }; ! 44: ! 45: enum { /* Rs */ ! 46: Invdcmd = 0x01, /* invalid host adapter command */ ! 47: Df = 0x04, /* data in port full */ ! 48: Cdf = 0x08, /* command/data port full */ ! 49: Idle = 0x10, /* SCSI host adapter idle */ ! 50: Init = 0x20, /* mailbox initialisation required */ ! 51: Diagf = 0x40, /* internal diagnostic failure */ ! 52: Stst = 0x80, /* self testing in progress */ ! 53: }; ! 54: ! 55: enum { /* Rcdo */ ! 56: Cnop = 0x00, /* no operation */ ! 57: Cmbinit = 0x01, /* mailbox initialisation */ ! 58: Cstart = 0x02, /* start SCSI command */ ! 59: Cbios = 0x03, /* start PC/AT BIOS command */ ! 60: Cinquiry = 0x04, /* adapter inquiry */ ! 61: Cmboie = 0x05, /* enable mailbox out available interrupt */ ! 62: Cselection = 0x06, /* set selection timeout */ ! 63: Cbuson = 0x07, /* set bus-on time */ ! 64: Cbusoff = 0x08, /* set bus-off time */ ! 65: Ctransfer = 0x09, /* set transfer speed */ ! 66: Cdevices = 0x0A, /* return installed devices */ ! 67: Cconfiguration = 0x0B, /* return configuration data */ ! 68: Ctenable = 0x0C, /* enable target mode */ ! 69: Csetup = 0x0D, /* return setup data */ ! 70: Cwbuff = 0x1A, /* write adapter channel 2 buffer */ ! 71: Crbuff = 0x1B, /* read adapter channel 2 buffer */ ! 72: Cwfifo = 0x1C, /* write adapter FIFO buffer */ ! 73: Crfifo = 0x1D, /* read adapter FIFO buffer */ ! 74: Cecho = 0x1F, /* ECHO command data */ ! 75: Cdiag = 0x20, /* adapter diagnostic */ ! 76: Coptions = 0x21, /* set host adapter options */ ! 77: }; ! 78: ! 79: enum { /* Rif */ ! 80: Mbif = 0x01, /* mailbox in full */ ! 81: Mboa = 0x02, /* mailbox out available */ ! 82: Hacc = 0x04, /* host adapter command complete */ ! 83: Scrd = 0x08, /* SCSI reset detected */ ! 84: Ai = 0x80, /* any interrupt */ ! 85: }; ! 86: ! 87: typedef struct { ! 88: uchar cmd; /* command */ ! 89: uchar msb; /* CCB pointer MSB */ ! 90: uchar mid; /* CCB pointer MID */ ! 91: uchar lsb; /* CCB pointer LSB */ ! 92: } Mbox; ! 93: ! 94: enum { /* mailbox commands */ ! 95: Mbfree = 0x00, /* mailbox is free */ ! 96: ! 97: Mbostart = 0x01, /* start SCSI or adapter command */ ! 98: Mboabort = 0x02, /* abort SCSI or adapter command */ ! 99: ! 100: Mbiok = 0x01, /* CCB completed without error */ ! 101: Mbiabort = 0x02, /* CCB aborted by host */ ! 102: Mbinx = 0x03, /* aborted CCB not found */ ! 103: Mbierror = 0x04, /* CCB completed with error */ ! 104: }; ! 105: ! 106: typedef struct { ! 107: uchar op; /* command control block operation code */ ! 108: uchar ctl; /* address and direction control */ ! 109: uchar cmdlen; /* SCSI command length */ ! 110: uchar reqlen; /* request sense allocation length */ ! 111: uchar datalen[3]; /* data length (MSB, MID, LSB) */ ! 112: uchar dataptr[3]; /* data pointer (MSB, MID, LSB) */ ! 113: uchar linkptr[3]; /* link pointer (MSB, MID, LSB) */ ! 114: uchar linkid; /* command linking identifier */ ! 115: uchar hastat; /* host adapter status */ ! 116: uchar tarstat; /* target device status */ ! 117: uchar reserved[2]; ! 118: uchar cs[12+0xFF]; /* SCSI command and sense bytes */ ! 119: } Ccb; ! 120: ! 121: enum { /* op */ ! 122: OInitiator = 0x00, /* initiator CCB */ ! 123: OTarget = 0x01, /* target CCB */ ! 124: Osg = 0x02, /* initiator CCB with scatter/gather */ ! 125: Ordl = 0x03, /* initiator CCB, residual data length returned */ ! 126: Osgrdl = 0x04, /* initiator CCB, both of the above */ ! 127: Obdr = 0x81, /* bus device reset */ ! 128: }; ! 129: ! 130: enum { /* ctl */ ! 131: CCBdatain = 0x08, /* inbound data transfer, length is checked */ ! 132: CCBdataout = 0x10, /* outbound data transfer, length is checked */ ! 133: }; ! 134: ! 135: enum { /* hastat */ ! 136: Eok = 0x00, /* no host adapter detected error */ ! 137: Etimeout = 0x11, /* selection timeout */ ! 138: Elength = 0x12, /* data over/under run */ ! 139: Ebusfree = 0x13, /* unexpected bus free */ ! 140: Ephase = 0x14, /* target bus phase sequence error */ ! 141: Eopcode = 0x16, /* invalid CCB opcode */ ! 142: Elink = 0x17, /* linked CCB does not have same LUN */ ! 143: Edirection = 0x18, /* invalid target direction received from host */ ! 144: Eduplicate = 0x19, /* duplicate CCB received in target mode */ ! 145: Esegment = 0x1A, /* invalid CCB or segment list parameter */ ! 146: }; ! 147: ! 148: enum { /* tarstat */ ! 149: Sgood = 0x00, /* good status */ ! 150: Scheck = 0x02, /* check status */ ! 151: Sbusy = 0x08, /* LUN busy */ ! 152: }; ! 153: ! 154: typedef struct Target Target; ! 155: typedef struct Ctlr Ctlr; ! 156: ! 157: struct Target { ! 158: QLock; ! 159: Rendez; ! 160: ! 161: Ccb ccb; ! 162: ulong paddr; /* physical address of ccb */ ! 163: uchar *sense; /* address of returned sense data */ ! 164: ! 165: int done; ! 166: Target *active; /* link on active list */ ! 167: }; ! 168: ! 169: struct Ctlr { ! 170: Lock; ! 171: ! 172: ulong port; /* I/O port */ ! 173: ulong id; /* adapter SCSI id */ ! 174: ! 175: uchar cmd[5]; /* adapter command out */ ! 176: uchar cmdlen; /* adapter command out length */ ! 177: uchar data[256]; /* adapter command data in */ ! 178: uchar datalen; /* adapter command data in length */ ! 179: ! 180: Mbox mb[NTarget+NTarget]; /* mailbox out + mailbox in */ ! 181: ! 182: Mbox *mbox; /* current mailbox out index into mb */ ! 183: Mbox *mbix; /* current mailbox in index into mb */ ! 184: ! 185: Target target[NTarget]; ! 186: Target *active; /* link on active list */ ! 187: }; ! 188: ! 189: static Ctlr softctlr[NCtlr]; ! 190: ! 191: /* ! 192: * Issue a command to the controller. The command and its length is ! 193: * contained in ctlr->cmd and ctlr->cmdlen. If any data is to be ! 194: * returned, ctlr->datalen should be non-0, and the returned data will ! 195: * be placed in ctlr->data. ! 196: * If we see Hacc set, bail out, we'll process ! 197: * the invalid command at interrupt time. ! 198: */ ! 199: static void ! 200: issue(Ctlr *ctlr) ! 201: { ! 202: int len; ! 203: ! 204: len = 0; ! 205: while(len < ctlr->cmdlen){ ! 206: if((inb(ctlr->port+Rs) & Cdf) == 0){ ! 207: outb(ctlr->port+Rcdo, ctlr->cmd[len]); ! 208: len++; ! 209: } ! 210: ! 211: if(inb(ctlr->port+Rif) & Hacc) ! 212: return; ! 213: } ! 214: ! 215: if(ctlr->datalen){ ! 216: len = 0; ! 217: while(len < ctlr->datalen){ ! 218: if(inb(ctlr->port+Rs) & Df){ ! 219: ctlr->data[len] = inb(ctlr->port+Rdi); ! 220: len++; ! 221: } ! 222: ! 223: if(inb(ctlr->port+Rif) & Hacc) ! 224: return; ! 225: } ! 226: } ! 227: } ! 228: ! 229: static int ! 230: done(void *arg) ! 231: { ! 232: return ((Target*)arg)->done; ! 233: } ! 234: ! 235: static int ! 236: scsiio(int bus, Scsi *p, int rw) ! 237: { ! 238: Ctlr *ctlr; ! 239: Target *tp; ! 240: ushort status; ! 241: ulong len, s; ! 242: ! 243: /* ! 244: * Wait for the target to become free, ! 245: * then set it up. The Adaptec will allow us to ! 246: * queue multiple transactions per target, but ! 247: * gives no guarantee about ordering, so we just ! 248: * allow one per target. ! 249: */ ! 250: ctlr = &softctlr[bus]; ! 251: tp = &ctlr->target[p->target]; ! 252: ! 253: qlock(tp); ! 254: if(waserror()){ ! 255: qunlock(tp); ! 256: nexterror(); ! 257: } ! 258: ! 259: /* ! 260: * If this is a request-sense and we have valid sense data ! 261: * from the last command, return it immediately. ! 262: * A pox on these weird enum names and the WD33C93A status ! 263: * codes. ! 264: */ ! 265: if(p->cmd.base[0] == ScsiExtsens && tp->sense){ ! 266: len = 8+tp->sense[7]; ! 267: memmove(p->data.ptr, tp->sense, len); ! 268: p->data.ptr += len; ! 269: tp->sense = 0; ! 270: ! 271: qunlock(tp); ! 272: poperror(); ! 273: return 0x6000; ! 274: } ! 275: tp->sense = 0; ! 276: ! 277: /* ! 278: * Fill in the ccb. ! 279: */ ! 280: tp->ccb.op = Ordl; ! 281: tp->ccb.ctl = (p->target<<5)|p->lun; ! 282: ! 283: len = p->cmd.lim - p->cmd.base; ! 284: tp->ccb.cmdlen = len; ! 285: memmove(tp->ccb.cs, p->cmd.base, len); ! 286: ! 287: tp->ccb.reqlen = 0xFF; ! 288: ! 289: len = p->data.lim - p->data.base; ! 290: tp->ccb.datalen[0] = (len>>16) & 0xFF; ! 291: tp->ccb.datalen[1] = (len>>8) & 0xFF; ! 292: tp->ccb.datalen[2] = len; ! 293: if(len == 0) ! 294: tp->ccb.ctl |= CCBdataout|CCBdatain; ! 295: else if(rw == ScsiIn) ! 296: tp->ccb.ctl |= CCBdatain; ! 297: else ! 298: tp->ccb.ctl |= CCBdataout; ! 299: ! 300: len = PADDR(p->data.base); ! 301: tp->ccb.dataptr[0] = (len>>16) & 0xFF; ! 302: tp->ccb.dataptr[1] = (len>>8) & 0xFF; ! 303: tp->ccb.dataptr[2] = len; ! 304: ! 305: tp->ccb.linkptr[0] = tp->ccb.linkptr[1] = tp->ccb.linkptr[2] = 0; ! 306: tp->ccb.linkid = 0; ! 307: ! 308: tp->ccb.hastat = tp->ccb.tarstat = 0; ! 309: tp->ccb.reserved[0] = tp->ccb.reserved[1] = 0; ! 310: ! 311: /* ! 312: * Link the target onto the beginning of the ! 313: * ctlr active list and start the request. ! 314: * The interrupt routine has to be able to take ! 315: * requests off the queue in any order. ! 316: */ ! 317: s = splhi(); ! 318: if(active.machs > 1) ! 319: lock(ctlr); ! 320: ! 321: tp->done = 0; ! 322: tp->active = ctlr->active; ! 323: ctlr->active = tp; ! 324: ! 325: ctlr->mbox->msb = (tp->paddr>>16) & 0xFF; ! 326: ctlr->mbox->mid = (tp->paddr>>8) & 0xFF; ! 327: ctlr->mbox->lsb = tp->paddr & 0xFF; ! 328: ctlr->mbox->cmd = Mbostart; ! 329: ! 330: ctlr->cmd[0] = Cstart; ! 331: ctlr->cmdlen = 1; ! 332: ctlr->datalen = 0; ! 333: issue(ctlr); ! 334: ! 335: ctlr->mbox++; ! 336: if(ctlr->mbox >= &ctlr->mb[NTarget]) ! 337: ctlr->mbox = ctlr->mb; ! 338: ! 339: if(active.machs > 1) ! 340: unlock(ctlr); ! 341: splx(s); ! 342: ! 343: /* ! 344: * Wait for the request to complete ! 345: * and return the status. ! 346: */ ! 347: tsleep(tp, done, tp, 60*5*1000); ! 348: if(done(tp) == 0) ! 349: print("adaptec%d: timeout cmd=#%2.2ux\n", p->target, tp->ccb.cs[0]); ! 350: ! 351: if((status = (tp->ccb.hastat<<8)) == 0) ! 352: status = 0x6000; ! 353: status |= tp->ccb.tarstat; ! 354: len = (tp->ccb.datalen[0]<<16)|(tp->ccb.datalen[1]<<8)|tp->ccb.datalen[2]; ! 355: p->data.ptr = p->data.lim - len; ! 356: ! 357: /* ! 358: * If the command returned sense data, keep a note ! 359: * of where it is for a subsequent request-sense command. ! 360: */ ! 361: if(tp->ccb.tarstat == Scheck && tp->ccb.hastat == Eok) ! 362: tp->sense = &tp->ccb.cs[tp->ccb.cmdlen]; ! 363: ! 364: qunlock(tp); ! 365: poperror(); ! 366: ! 367: return status; ! 368: } ! 369: ! 370: static int ! 371: aha1542exec(Scsi *p, int rw) ! 372: { ! 373: Ctlr *ctlr = &softctlr[0]; ! 374: ! 375: if(ctlr->port == 0) ! 376: error(Enodev); ! 377: if(p->target == CtlrID) ! 378: return 0x6002; ! 379: ! 380: return p->status = scsiio(0, p, rw); ! 381: } ! 382: ! 383: static void ! 384: interrupt(Ureg *ur, void *a) ! 385: { ! 386: Ctlr *ctlr = &softctlr[0]; ! 387: uchar rif, rs; ! 388: Target *tp, **l; ! 389: ulong paddr; ! 390: ! 391: USED(ur, a); ! 392: ! 393: if(active.machs > 1) ! 394: lock(ctlr); ! 395: ! 396: /* ! 397: * Save and clear the interrupt(s). The only ! 398: * interrupts expected are Hacc, which we ignore, ! 399: * and Mbif which means something completed. ! 400: */ ! 401: rif = inb(ctlr->port+Rif); ! 402: rs = inb(ctlr->port+Rs); ! 403: outb(ctlr->port+Rc, Irst); ! 404: if(rif & ~(Ai|Hacc|Mbif)) ! 405: print("adaptec%d: interrupt #%2.2ux\n", 0, rif); ! 406: if((rif & Hacc) && (rs & Invdcmd)) ! 407: print("adaptec%d: invdcmd #%2.2ux, len %d\n", 0, ctlr->cmd[0], ctlr->cmdlen); ! 408: ! 409: /* ! 410: * Look for something in the mail. ! 411: * If there is, try to find the recipient from the ! 412: * ccb address, take it off the active list and ! 413: * wakeup whoever. ! 414: */ ! 415: while(ctlr->mbix->cmd){ ! 416: paddr = (ctlr->mbix->msb<<16)|(ctlr->mbix->mid<<8)|ctlr->mbix->lsb; ! 417: l = &ctlr->active; ! 418: for(tp = *l; tp; tp = tp->active){ ! 419: if(tp->paddr == paddr) ! 420: break; ! 421: l = &tp->active; ! 422: } ! 423: if(tp == 0) ! 424: panic("adaptec%d: no target for ccb #%lux\n", 0, paddr); ! 425: *l = tp->active; ! 426: ! 427: ctlr->mbix->cmd = 0; ! 428: ! 429: tp->done = 1; ! 430: wakeup(tp); ! 431: ! 432: ctlr->mbix++; ! 433: if(ctlr->mbix >= &ctlr->mb[NTarget+NTarget]) ! 434: ctlr->mbix = &ctlr->mb[NTarget]; ! 435: } ! 436: ! 437: if(active.machs > 1) ! 438: unlock(ctlr); ! 439: } ! 440: ! 441: static void ! 442: reset(Ctlr *ctlr) ! 443: { ! 444: ulong paddr; ! 445: int i; ! 446: ! 447: /* ! 448: * Initialise the software controller and set the board ! 449: * scanning the mailboxes. ! 450: * Need code here to tidy things up if we're ! 451: * resetting after being active. ! 452: */ ! 453: for(i = 0; i < NTarget; i++){ ! 454: ctlr->target[i].paddr = PADDR(&ctlr->target[i].ccb); ! 455: ctlr->mbox = ctlr->mb; ! 456: ctlr->mbix = &ctlr->mb[NTarget]; ! 457: } ! 458: ! 459: ctlr->cmd[0] = Cmbinit; ! 460: paddr = PADDR(ctlr->mb); ! 461: ctlr->cmd[1] = NTarget; ! 462: ctlr->cmd[2] = (paddr>>16) & 0xFF; ! 463: ctlr->cmd[3] = (paddr>>8) & 0xFF; ! 464: ctlr->cmd[4] = paddr & 0xFF; ! 465: ctlr->cmdlen = 5; ! 466: ctlr->datalen = 0; ! 467: issue(ctlr); ! 468: } ! 469: ! 470: static ISAConf aha1542 = { ! 471: "aha1542", ! 472: Port, ! 473: Irq, ! 474: 0, ! 475: 0, ! 476: }; ! 477: ! 478: int (* ! 479: aha1542reset(void))(Scsi*, int) ! 480: { ! 481: ISAConf *isa = &aha1542; ! 482: Ctlr *ctlr; ! 483: ulong s; ! 484: uchar rif, rs; ! 485: int dma, irq; ! 486: ! 487: /* ! 488: * See if we have any configuration info. ! 489: * Only one controller for now. ! 490: */ ! 491: if(isaconfig("scsi", 0, &aha1542) == 0) ! 492: return 0; ! 493: ctlr = &softctlr[0]; ! 494: memset(ctlr, 0, sizeof(Ctlr)); ! 495: ctlr->port = isa->port; ! 496: ! 497: /* ! 498: * Attempt to hard-reset the board and reset ! 499: * the SCSI bus. If the board state doesn't settle to ! 500: * idle with mailbox initialisation required, either ! 501: * it isn't an Adaptec or it's broken. ! 502: * The 154[02]C has an extra I/O port we could use for ! 503: * identification, but that wouldn't work on the B. ! 504: */ ! 505: outb(ctlr->port+Rc, Hrst|Scrst); ! 506: delay(500); ! 507: if(inb(ctlr->port+Rs) != (Init|Idle)) ! 508: return 0; ! 509: ! 510: /* ! 511: * Get the DMA and IRQ info from the board. This will ! 512: * cause an interrupt which we hope doesn't cause any ! 513: * trouble because we don't know which one it is yet. ! 514: * We have to do this to get the DMA info as it won't ! 515: * be set up if the board has the BIOS disabled. ! 516: */ ! 517: s = splhi(); ! 518: ctlr->cmd[0] = Cconfiguration; ! 519: ctlr->cmdlen = 1; ! 520: ctlr->datalen = 3; ! 521: issue(ctlr); ! 522: while(((rif = inb(ctlr->port+Rif)) & Hacc) == 0) ! 523: ; ! 524: ! 525: rs = inb(ctlr->port+Rs); ! 526: outb(ctlr->port+Rc, Irst); ! 527: if((rif & Hacc) && (rs & Invdcmd)){ ! 528: splx(s); ! 529: return 0; ! 530: } ! 531: splx(s); ! 532: ! 533: dma = -1; ! 534: switch(ctlr->data[0]){ /* DMA Arbitration Priority */ ! 535: ! 536: case 0x80: /* Channel 7 */ ! 537: outb(0xD6, 0xC3); ! 538: outb(0xD4, 0x03); ! 539: dma = 7; ! 540: break; ! 541: ! 542: case 0x40: /* Channel 6 */ ! 543: outb(0xD6, 0xC2); ! 544: outb(0xD4, 0x02); ! 545: dma = 6; ! 546: break; ! 547: ! 548: case 0x20: /* Channel 5 */ ! 549: outb(0xD6, 0xC1); ! 550: outb(0xD4, 0x01); ! 551: dma = 5; ! 552: break; ! 553: ! 554: case 0x01: /* Channel 0 */ ! 555: outb(0x0B, 0xC0); ! 556: outb(0x0A, 0x00); ! 557: dma = 0; ! 558: break; ! 559: ! 560: default: ! 561: /* ! 562: * This might be an EISA card (e.g. Buslogic 747) ! 563: * which doesn't have ISA DMA compatibility set ! 564: * so no DMA channel will show. ! 565: * Carry on regardless. ! 566: print("adaptec%d: invalid DMA priority #%2.2ux\n", 0, ctlr->data[0]); ! 567: return 0; ! 568: */ ! 569: break; ! 570: } ! 571: ! 572: switch(ctlr->data[1]){ /* Interrupt Channel */ ! 573: ! 574: case 0x40: ! 575: irq = 15; ! 576: break; ! 577: ! 578: case 0x20: ! 579: irq = 14; ! 580: break; ! 581: ! 582: case 0x08: ! 583: irq = 12; ! 584: break; ! 585: ! 586: case 0x04: ! 587: irq = 11; ! 588: break; ! 589: ! 590: case 0x02: ! 591: irq = 10; ! 592: break; ! 593: ! 594: case 0x01: ! 595: irq = 9; ! 596: break; ! 597: ! 598: default: ! 599: print("scsi%d: %s: invalid irq #%2.2ux\n", ! 600: 0, isa->type, ctlr->data[1]); ! 601: return 0; ! 602: } ! 603: ! 604: setvec(Int0vec+irq, interrupt, 0); ! 605: ctlr->id = ctlr->data[2] & 0x07; ! 606: reset(ctlr); ! 607: ! 608: print("scsi%d: %s: port %lux irq %d, dma %d\n", ! 609: 0, isa->type, ctlr->port, irq, dma); ! 610: ! 611: return aha1542exec; ! 612: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.