|
|
1.1 root 1: /* $Id: 7000fasst.c,v 1.1 1992/07/24 06:27:38 root Exp root $
2: * linux/kernel/7000fasst.c
3: *
4: * Copyright (C) 1992 Thomas Wuensche
5: * closely related to the aha1542 driver from Tommy Thorn
6: * ( as close as different hardware allows on a lowlevel-driver :-) )
7: */
8:
9: #include <linux/config.h>
10: #include <linux/kernel.h>
11: #include <linux/head.h>
12: #include <linux/types.h>
13: #include <linux/string.h>
14: #include <asm/system.h>
15: #include <asm/io.h>
16: #include "scsi.h"
17: #include "hosts.h"
18:
19: struct mailbox{
20: unchar status;
21: unchar scbptr[3];
22: };
23:
24: /* #define DEBUG */
25:
26: #include "7000fasst.h"
27: #ifdef DEBUG
28: #define DEB(x) x
29: #else
30: #define DEB(x)
31: #endif
32:
33: /*static const char RCSid[] = "$Header: /usr/src/linux/kernel/blk_drv/scsi/RCS/7000fasst.c,v 1.1 1992/07/24 06:27:38 root Exp root $";*/
34:
35: static struct scb scbs[OGMB_CNT];
36:
37: long wd7000fasst_WAITnexttimeout = 3000000;
38:
39: void (*wd7000fasst_do_done)() = NULL;
40: extern void wd7000fasst_interrupt();
41: void wd7000fasst_call_buh();
42:
43: static unchar controlstat = 0;
44: static unchar wd7000fasst_hostno;
45:
46: #define wd7000fasst_intr_reset() outb(0,INTR_ACK)
47: #define PC_IMR 0x21
48: #define AT_IMR 0xa1
49:
50: #define wd7000fasst_enable_intr(){\
51: controlstat |= INT_EN;\
52: outb(controlstat,CONTROL);\
53: outb((inb((intr_chan<=7)?PC_IMR:AT_IMR))& ~0xff,(intr_chan<=7)?PC_IMR:AT_IMR);}
54:
55: #define wd7000fasst_disable_intr() outb(controlstat |= INT_EN, CONTROL)
56: #define wd7000fasst_enable_dma() {\
57: controlstat |= DMA_EN;\
58: outb(controlstat,CONTROL);\
59: outb((DMA_CH|CASCADE),DMA_MODE_REG);\
60: outb(DMA_CH,DMA_MASK_REG);}
61:
62: #define wd7000fasst_disable_dma() {\
63: outb(DMA_CH|S_DMA_MASK,DMA_MASK_REG);\
64: controlstat &= ~DMA_EN;\
65: outb(controlstat,CONTROL);}
66:
67: #define WAIT(port, mask, allof, noneof) \
68: { register WAITbits; \
69: register WAITtimeout = wd7000fasst_WAITnexttimeout; \
70: while (1) { \
71: WAITbits = inb(port) & (mask); \
72: if ((WAITbits & (allof)) == (allof) && ((WAITbits & (noneof)) == 0)) \
73: break; \
74: if (--WAITtimeout == 0) goto fail; \
75: } \
76: }
77:
78: static void wd7000fasst_stat(void)
79: {
80: /* int s = inb(ASC_STAT), i = inb(INTR_STAT);*/
81: /* printk("status = %x, intrflags = %x served %d last %x\n", s, i, intr_flag, intr_last);
82: printk("status=%x intrflags=%x\n", s, i);
83: */}
84:
85: static int wd7000fasst_out(unchar *cmdp, int len)
86: {
87: while (len--)
88: {
89: WAIT(ASC_STAT, STATMASK, CMD_RDY, 0);
90: outb(*cmdp++, COMMAND);
91: }
92: return 0;
93: fail:
94: printk("wd7000fasst_out failed(%d): ", len+1); wd7000fasst_stat();
95: return 1;
96: }
97:
98: int wd7000fasst_make_error_code(unsigned hosterr, unsigned scsierr)
99: {
100: #ifdef DEBUG
101: int in_error=hosterr;
102: #endif
103: switch ((hosterr&0xff00)>>8){
104: case 0: /* It is reserved, should never happen */
105: hosterr=DID_ERROR;
106: break;
107: case 1: hosterr=DID_OK;
108: break;
109: case 2: /* Command complete with logged error */
110: /* My actual copies of the manual pages are unreadable
111: * For now we simply tell there is an error */
112: DEB(printk("Hosterror: VUE = %x\n",hosterr&0xff);)
113: switch (hosterr&0xff) {
114: default: DEB(printk("wd7000fasst_make_error_code: unknown hoststatus %x\n", hosterr);)
115: hosterr=DID_ERROR;
116: break;
117: }
118: break;
119: case 4: hosterr=DID_BAD_TARGET; /* Command failed to complete without SCSI status */
120: break;
121: case 5: hosterr=DID_RESET; /* Cmd terminated; Bus reset by external device */
122: break;
123: case 6: hosterr=DID_ERROR;/* Hardware Failure, requires host reset */
124: break;
125: case 7: hosterr=DID_RESET;
126: break;
127: case 8: hosterr=DID_OK;
128: printk("wd7000fasst: Linked command not implemented\n");
129: break;
130: }
131: #ifdef DEBUG
132: if (scsierr||hosterr) printk("SCSI-Command error: SCSI %x HOST %x RETURN %x\n",scsierr,in_error,hosterr);
133: #endif
134: return scsierr|(hosterr << 16);
135: }
136:
137: /* The following is space for the Mailboxes */
138: struct{ struct mailbox ombox[OGMB_CNT];
139: struct mailbox imbox[ICMB_CNT]; } mbstruct;
140:
141: int wd7000fasst_init(void)
142: { int i;
143: volatile int debug = 0;
144: /* Page 47 */
145: unchar init_block[]={ 1, 7, 0x18, 0x18, 0, 0, 0, 0, OGMB_CNT, ICMB_CNT };
146: /* Reset the adapter. I ought to make a hard reset, but it's not really nessesary */
147:
148: DEB(printk("wd7000fasst_init called \n"));
149:
150: outb(SCSI_RES|ASC_RES, CONTROL);
151: /* Wait at least 25 us */
152: for (i=0; i< 1000; i++) inb(ASC_STAT);
153: /* Now reset the reset */
154: outb(0,CONTROL);
155: debug = 1;
156: /* Expect Command Port Ready */
157: WAIT(ASC_STAT, STATMASK, CMD_RDY, 0);
158: DEB(printk("wd7000fasst_init: Power on Diagnostics finished\n"));
159: if ((i=inb(INTR_STAT))!=1)
160: printk("Power on Diagnostics error %x\n",i);
161:
162: debug = 2;
163: /* Clear mbstruct */
164: memset(&mbstruct,0,sizeof (mbstruct));
165: /* Set up init block */
166: any2scsi(init_block+5,&mbstruct);
167: /* Execute init command */
168: wd7000fasst_out(init_block,sizeof(init_block));
169: DEB(printk("Init-Block :");
170: for (i=0;i<sizeof(init_block);i++) printk(" %x",init_block[i]);
171: printk("\n");)
172: /* Wait until init finished */
173: WAIT(ASC_STAT, STATMASK, CMD_RDY | ASC_INI, 0);
174: outb(2,COMMAND);
175: WAIT(ASC_STAT, STATMASK, CMD_RDY | ASC_INI, 0);
176: /* Enable Interrupt and DMA */
177: wd7000fasst_enable_dma();
178: wd7000fasst_call_buh();
179: DEB(printk("wd7000fasst_detect: enable interrupt channel %d\n", intr_chan));
180: wd7000fasst_enable_intr();
181: printk("wd7000fasst_init: Controller initialized\n");
182: return 1;
183: fail:
184: return 0; /* 0 = not ok */
185: }
186:
187: /* What's this little function for? */
188: char *wd7000fasst_info(void)
189: {
190: static char buffer[] = "Western Digital 7000-FASST";
191: return buffer;
192: }
193:
194: /* A "high" level interrupt handler */
195: void wd7000fasst_intr_handle(void)
196: { struct scb * scbptr;
197: DEB(int len=sizeof (struct scb);)
198: DEB(int k;)
199: unsigned host_error,scsi_error;
200: int flag = inb(INTR_STAT);
201: void (*my_done)() = wd7000fasst_do_done;
202: int errstatus;
203: DEB(printk("WD Interrupt aufgetreten\n"));
204: if (!(inb(ASC_STAT)&0x80)){
205: printk("Interrupt without Interrupt\n");
206: wd7000fasst_intr_reset();
207: return;
208: }
209: wd7000fasst_do_done = NULL;
210: if (!my_done) {
211: printk("wd7000fasst_intr_handle: Unexpected interrupt\n");
212: wd7000fasst_intr_reset();
213: return;
214: }
215:
216: /* is there mail for me :-) */
217: if ((flag&0xc0)==0xc0){
218: /* Ok, the interrupt is for an incoming mailbox */
219: /* We make the content available for the starter of the command */
220: DEB(if ((flag&0xc0)==0xc0) printk("INTR_STAT: %x mbstat: %x\n",flag,mbstruct.imbox[flag&0x3f].status));
221: if (mbstruct.imbox[flag&0x3f].status==0){
222: /* Something strange happened */
223: wd7000fasst_intr_reset();
224: return;
225: ;
226: }
227: scbptr=(struct scb *)scsi2int(mbstruct.imbox[flag&0x3f].scbptr);
228: DEB(printk("Datenbereiche aus %x ein %x \n",scbptr,&(scbs[flag&0x3f]));
229: printk("SCB after return:\n");
230: k=0;
231: while (len-- >0){
232: printk("%x ",*((unchar *)scbptr));
233: ((unchar *)scbptr)++;
234: if (++k==16){ printk("\n"); k=0; }
235: });
236: }
237: else { printk("Error in interrupt\n"); return; }
238: /* more error checking left out here */
239:
240: scbptr=(struct scb *)scsi2int(mbstruct.imbox[flag&0x3f].scbptr);
241: host_error=scbptr->vue | mbstruct.imbox[flag&0x3f].status<<8;
242: scsi_error=scbptr->sretstat;
243: errstatus=wd7000fasst_make_error_code(host_error,scsi_error);
244: DEB(if (errstatus) printk("Target was %x\n",scbptr->idlun>>5);)
245: DEB(if (errstatus) printk("wd7000fasst_intr_handle: returning %6x\n", errstatus));
246: DEB(printk("wd7000fasst_intr_handle: Status of the finished command: %x\n",mbstruct.imbox[flag&0x3f].status));
247: /* I make a SCSI reset */
248: /* Left out */
249: my_done(wd7000fasst_hostno,errstatus);
250: wd7000fasst_intr_reset();
251: return;
252: }
253:
254: volatile static int internal_done_flag = 0;
255: volatile static int internal_done_errcode = 0;
256:
257: /* The following code queues a SCSI command */
258: int wd7000fasst_queuecommand(unchar target, const void *cmnd, void *buff, int bufflen,
259: void (*done)(int, int))
260: {
261: int i;
262: #ifdef DEBUG
263: int j;
264: #endif
265: unchar *cmd = (unchar *) cmnd;
266: /* We first look for a free outgoing mailbox */
267: for (i=0;i<OGMB_CNT;i++){
268: if (mbstruct.ombox[i].status==0){
269: /* We found one, now set up the scb */
270: DEB(printk("Found outgoing mbox %x\n",i));
271: memset(&scbs[i], 0, sizeof(struct scb));
272: /* scbs[i].cdblen = (*cmd<=0x1f)?6:10; */ /* SCSI Command Descriptor Block Length */
273: memcpy(scbs[i].scbdata, cmd, (*cmd<=0x1f)?6:10);
274: scbs[i].op = 0; /* SCSI Initiator Command */
275: scbs[i].idlun = (target<<5)&0xe0; /* SCSI Target Id Bit 7-5 Target Id*/
276: any2scsi(scbs[i].dataptr,buff);
277: any2scsi(scbs[i].maxdata,bufflen);
278: scbs[i].direc=0x40; /* Disable direction check */
279: DEB(printk("Kommando fuer target %x ist: ",target);
280: for (j=0;j<12;j++) printk(" %x",scbs[i].scbdata[j]);
281: printk("\n"));
282: /* Now we set up the pointer to scb, then the status of the mbox */
283: any2scsi((mbstruct.ombox[i].scbptr),&(scbs[i]));
284: mbstruct.ombox[i].status=1;
285: /* Everything set up, start the command */
286: break;
287: }
288: }
289: if (i==OGMB_CNT){
290: /* No free mbox, send command "Interrupt on free OGMB" */
291: DEB(printk("No free Mailbox\n"));
292: return 0;
293: }
294: { int len,k;
295: struct scb * scbptr;
296: DEB(printk("Found outgoing mbox %x\n",i));
297: scbptr=&(scbs[i]);
298: len=sizeof(struct scb);
299: k=0;
300: DEB(printk("SCB before execute:\n");
301: while (len-- >0){
302: printk("%x ",*((unchar *)scbptr));
303: ((unchar *)scbptr)++;
304: if (++k==16){ printk("\n"); k=0; }
305: };)
306: }
307: /* Set up the "done" response function */
308: if (done) {
309: DEB(printk("wd7000fasst_queuecommand: now waiting for interrupt ");
310: wd7000fasst_stat());
311: if (wd7000fasst_do_done)
312: printk("wd7000fasst_queuecommand: Two concurrent queuecommand?\n");
313: else
314: wd7000fasst_do_done = done;
315: DEB(wd7000fasst_stat());
316: wd7000fasst_enable_intr();
317: }
318: else{
319: printk("wd7000fasst_queuecommand: done can't be NULL\n");
320: return 0;
321: }
322: /* Now we initialize execution */
323: retry: WAIT(ASC_STAT,STATMASK,CMD_RDY,0);
324: outb(0x80+i,COMMAND);
325: WAIT(ASC_STAT,STATMASK,CMD_RDY,0);
326: if (inb(ASC_STAT)&CMD_REJ) goto retry;
327: return 1;
328: /* Wait until done */
329:
330: fail:
331: return 0;
332: }
333:
334: /* We use this function for queueing a command from wd7000fasst_command */
335: static void internal_done(int host, int errcode)
336: {
337: internal_done_errcode = errcode;
338: ++internal_done_flag;
339: }
340:
341: int wd7000fasst_command(unchar target, const void *cmnd, void *buff, int bufflen)
342: {
343: #ifdef DEBUG
344: int k;
345: #endif
346: wd7000fasst_queuecommand(target, cmnd, buff, bufflen, internal_done);
347:
348: while (!internal_done_flag);
349: internal_done_flag = 0;
350: DEB(printk("wd7000fasst_command finished: ..leaving with errcode %x\n",
351: internal_done_errcode));
352: DEB(for (k=0;k<5000000;k++) inb(INTR_STAT));
353: return internal_done_errcode;
354: }
355:
356: /* a hack to avoid a strange compilation error */
357:
358: void wd7000fasst_call_buh()
359: {
360: set_intr_gate((intr_chan<=7)?intr_chan+8:intr_chan+0x20,&wd7000fasst_interrupt);
361: }
362:
363: /* return non-zero on detection */
364: static const char *wd_bases[] = {(char *)0xce000};
365: typedef struct {char * signature;
366: unsigned offset;
367: unsigned length;
368: }Signature;
369:
370: static const Signature signatures[] =
371: {{"SSTBIOS",0xd,0x7}};
372:
373: #define NUM_SIGNATURES (sizeof(signatures)/sizeof(Signature))
374:
375: int wd7000fasst_detect(int hostnum) /* hostnum ignored for now */
376: {
377: int i,j;
378: char const * base_address = 0;
379: /* Store our host number */
380: wd7000fasst_hostno=hostnum;
381: DEB(printk("wd7000fasst_detect: \n"));
382:
383: for(i=0;i<(sizeof(wd_bases)/sizeof(char *));i++){
384: for(j=0;j<NUM_SIGNATURES;j++){
385: if(!memcmp((void *)(wd_bases[i] + signatures[j].offset),
386: (void *) signatures[j].signature,signatures[j].length)){
387: base_address=wd_bases[i];
388: printk("WD 7000-FASST detected\n");
389: }
390: }
391: }
392: if (!base_address) return 0;
393: wd7000fasst_init();
394:
395: /* Set the Bus on/off-times as not to ruin floppy performens */
396:
397: wd7000fasst_stat();
398:
399: printk(" *** READ CAPACITY ***\n");
400:
401: { unchar rstat;
402: unchar buf[8];
403: static unchar cmd[] = { READ_CAPACITY, 0, 0, 0, 0, 0, 0, 0, 0, 0};
404: int i;
405:
406: for (i = 0; i < sizeof(buf); ++i) buf[i] = 0x87;
407: for (i = 0; i < 3; ++i){
408: rstat=0;
409: while (rstat<2){
410: if (wd7000fasst_command(i, cmd, buf, sizeof(buf))) rstat++;
411: else break;
412: }
413: if (rstat<2)
414: printk("wd7000fasst_detect: LU %d sector_size 0x%x device_size 0x%x capacity %d\n",
415: i, xscsi2int(buf+4), xscsi2int(buf), xscsi2int(buf+4)*xscsi2int(buf));
416: }
417: }
418:
419: return 1;
420: }
421:
422: int wd7000fasst_abort(int i)
423: {
424: printk("wd7000fasst_abort\n");
425: return 0;
426: }
427:
428: int wd7000fasst_reset(void)
429: {
430: printk("wd7000fasst_reset called\n");
431: return 0;
432: }
433:
434: __asm__("
435: _wd7000fasst_interrupt:
436: cld
437: pushl %eax
438: pushl %ecx
439: pushl %edx
440: push %ds
441: push %es
442: push %fs
443: movl $0x10,%eax
444: mov %ax,%ds
445: mov %ax,%es
446: movl $0x17,%eax
447: mov %ax,%fs
448: # Please, someone, change this to use the timer
449: # andl $0xfffeffff,_timer_active
450: movl $_wd7000fasst_intr_handle,%edx
451: call *%edx # ``interesting'' way of handling intr.
452: # Free the interrupt only after resetting the host interrupt
453: movb $0x20,%al
454: outb %al,$0xA0 # EOI to interrupt controller #1
455: jmp 1f # give port chance to breathe
456: 1: jmp 1f
457: 1: outb %al,$0x20
458: pop %fs
459: pop %es
460: pop %ds
461: popl %edx
462: popl %ecx
463: popl %eax
464: iret
465: ");
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.