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