|
|
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.