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