This source file includes following definitions.
- wd7000_enable_intr
- wd7000_enable_dma
- delay
- command_out
- alloc_scb
- free_scb
- init_scbs
- mail_out
- make_code
- wd7000_scsi_done
- wd7000_intr_handle
- wd7000_queuecommand
- wd7000_command
- wd7000_init
- wd7000_detect
- wd7000_append_info
- wd7000_info
- wd7000_abort
- wd7000_reset
- wd7000_biosparam
1
2
3
4
5
6
7
8
9
10
11
12 #include <stdarg.h>
13 #include <linux/kernel.h>
14 #include <linux/head.h>
15 #include <linux/types.h>
16 #include <linux/string.h>
17 #include <linux/sched.h>
18 #include <asm/system.h>
19 #include <asm/dma.h>
20 #include <asm/io.h>
21 #include "../blk.h"
22 #include "scsi.h"
23 #include "hosts.h"
24
25
26
27 #include "wd7000.h"
28
29
30 #ifdef DEBUG
31 #define DEB(x) x
32 #else
33 #define DEB(x)
34 #endif
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69 static struct {
70 struct wd_mailbox ogmb[OGMB_CNT];
71 struct wd_mailbox icmb[ICMB_CNT];
72 } mb;
73 static int next_ogmb = 0;
74
75 static Scb scbs[MAX_SCBS];
76 static Scb *scbfree = NULL;
77
78 static int wd7000_host = 0;
79 static unchar controlstat = 0;
80
81 #define wd7000_intr_ack() outb(0,INTR_ACK)
82
83 static long WAITnexttimeout = 3000000;
84
85
86 static inline void wd7000_enable_intr()
87 {
88 controlstat |= INT_EN;
89 outb(controlstat,CONTROL);
90 }
91
92
93 static inline void wd7000_enable_dma()
94 {
95 controlstat |= DMA_EN;
96 outb(controlstat,CONTROL);
97 set_dma_mode(DMA_CH, DMA_MODE_CASCADE);
98 enable_dma(DMA_CH);
99 }
100
101
102 #define WAIT(port, mask, allof, noneof) \
103 { register WAITbits; \
104 register WAITtimeout = WAITnexttimeout; \
105 while (1) { \
106 WAITbits = inb(port) & (mask); \
107 if ((WAITbits & (allof)) == (allof) && ((WAITbits & (noneof)) == 0)) \
108 break; \
109 if (--WAITtimeout == 0) goto fail; \
110 } \
111 }
112
113
114 static inline void delay( unsigned how_long )
115 {
116 unsigned long time = jiffies + how_long;
117
118 while (jiffies < time);
119 }
120
121
122 static inline int command_out(unchar *cmdp, int len)
123 {
124 while (len--) {
125 WAIT(ASC_STAT, STATMASK, CMD_RDY, 0);
126 outb(*cmdp++, COMMAND);
127 }
128 return 1;
129
130 fail:
131 printk("wd7000_out WAIT failed(%d): ", len+1);
132 return 0;
133 }
134
135 static inline Scb *alloc_scb(void)
136 {
137 Scb *scb;
138 unsigned long flags;
139
140 save_flags(flags);
141 cli();
142
143 if (scbfree == NULL) {
144 panic("wd7000: can't allocate free SCB.\n");
145 restore_flags(flags);
146 return NULL;
147 }
148 scb = scbfree; scbfree = scb->next;
149 memset(scb, 0, sizeof(Scb)); scb->next = NULL;
150
151 restore_flags(flags);
152
153 return scb;
154 }
155
156
157 static inline void free_scb( Scb *scb )
158 {
159 unsigned long flags;
160
161 save_flags(flags);
162 cli();
163
164 memset(scb, 0, sizeof(Scb));
165 scb->next = scbfree; scbfree = scb;
166
167 restore_flags(flags);
168 }
169
170
171 static inline void init_scbs(void)
172 {
173 int i;
174 unsigned long flags;
175
176 save_flags(flags);
177 cli();
178
179 scbfree = &(scbs[0]);
180 for (i = 0; i < MAX_SCBS-1; i++) scbs[i].next = &(scbs[i+1]);
181 scbs[MAX_SCBS-1].next = NULL;
182
183 restore_flags(flags);
184 }
185
186
187 static int mail_out( Scb *scbptr )
188
189
190
191 {
192 int i, ogmb;
193 unsigned long flags;
194
195 DEB(printk("wd7000_scb_out: %06x");)
196
197
198 save_flags(flags);
199 cli();
200 ogmb = next_ogmb;
201 for (i = 0; i < OGMB_CNT; i++) {
202 if (mb.ogmb[ogmb].status == 0) {
203 DEB(printk(" using OGMB %x",ogmb));
204 mb.ogmb[ogmb].status = 1;
205 any2scsi(mb.ogmb[ogmb].scbptr, scbptr);
206
207 next_ogmb = (ogmb+1) % OGMB_CNT;
208 break;
209 } else
210 ogmb = (++ogmb) % OGMB_CNT;
211 }
212 restore_flags(flags);
213
214 if (i >= OGMB_CNT) {
215 DEB(printk(", no free OGMBs.\n");)
216
217 return 0;
218 }
219
220 wd7000_enable_intr();
221 do {
222 WAIT(ASC_STAT,STATMASK,CMD_RDY,0);
223 outb(START_OGMB|ogmb, COMMAND);
224 WAIT(ASC_STAT,STATMASK,CMD_RDY,0);
225 } while (inb(ASC_STAT) & CMD_REJ);
226
227 DEB(printk(", awaiting interrupt.\n");)
228 return 1;
229
230 fail:
231 DEB(printk(", WAIT timed out.\n");)
232 return 0;
233 }
234
235
236 int make_code(unsigned hosterr, unsigned scsierr)
237 {
238 #ifdef DEBUG
239 int in_error = hosterr;
240 #endif
241
242 switch ((hosterr>>8)&0xff){
243 case 0:
244 hosterr = DID_ERROR;
245 break;
246 case 1:
247 hosterr = DID_OK;
248 break;
249 case 2:
250 hosterr = DID_OK;
251 break;
252 case 4:
253 hosterr = DID_TIME_OUT;
254 break;
255 case 5:
256 hosterr = DID_RESET;
257 break;
258 case 6:
259 hosterr = DID_BAD_TARGET;
260 break;
261 case 80:
262 case 81:
263 hosterr = DID_BAD_INTR;
264 break;
265 case 82:
266 hosterr = DID_ABORT;
267 break;
268 case 83:
269 case 84:
270 hosterr = DID_RESET;
271 break;
272 default:
273 hosterr = DID_ERROR;
274 break;
275 }
276 #ifdef DEBUG
277 if (scsierr||hosterr)
278 printk("\nSCSI command error: SCSI %02x host %04x return %d",
279 scsierr,in_error,hosterr);
280 #endif
281 return scsierr | (hosterr << 16);
282 }
283
284
285 static void wd7000_scsi_done(Scsi_Cmnd * SCpnt)
286 {
287 DEB(printk("wd7000_scsi_done: %06x\n",SCpnt);)
288 SCpnt->SCp.phase = 0;
289 }
290
291
292 void wd7000_intr_handle(int irq)
293 {
294 int flag, icmb, errstatus, icmb_status;
295 int host_error, scsi_error;
296 Scb *scb, *scbn;
297 unchar *icb;
298 Scsi_Cmnd *SCpnt;
299
300 flag = inb(INTR_STAT);
301 DEB(printk("wd7000_intr_handle: intr stat = %02x",flag);)
302
303 if (!(inb(ASC_STAT)&0x80)){
304 DEB(printk("\nwd7000_intr_handle: phantom interrupt...\n");)
305 wd7000_intr_ack();
306 return;
307 }
308
309
310 if ((flag & 0x40) == 0) {
311
312 DEB(printk("wd7000_intr_handle: free outgoing mailbox\n");)
313 wd7000_intr_ack();
314 return;
315 }
316
317 icmb = flag & 0x3f;
318 scb = (struct scb *) scsi2int(mb.icmb[icmb].scbptr);
319 icmb_status = mb.icmb[icmb].status;
320 mb.icmb[icmb].status = 0;
321
322 #ifdef DEBUG
323 printk(" ICMB %d posted for SCB/ICB %06x, status %02x",
324 icmb, scb, icmb_status );
325 #endif
326
327 if (scb->op == 0) {
328 SCpnt = scb->SCpnt;
329 if (--(SCpnt->SCp.phase) <= 0) {
330 host_error = scb->vue | (icmb_status << 8);
331 scsi_error = scb->status;
332 errstatus = make_code(host_error,scsi_error);
333 SCpnt->result = errstatus;
334
335 scb = (Scb *) SCpnt->host_scribble; scbn = scb;
336 while (scb != NULL) {
337 scbn = scb->next;
338 free_scb(scb);
339 scb = scbn;
340 }
341
342 SCpnt->scsi_done(SCpnt);
343 }
344 } else {
345 icb = (unchar *) scb;
346 icb[ICB_STATUS] = icmb_status;
347 icb[ICB_PHASE] = 0;
348 }
349
350 wd7000_intr_ack();
351 DEB(printk(".\n");)
352 return;
353 }
354
355
356 int wd7000_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
357 {
358 Scb *scb;
359 unchar *cdb;
360 unchar idlun;
361 short cdblen;
362
363 cdb = (unchar *) SCpnt->cmnd;
364 cdblen = (*cdb <= 0x1f ? 6 : 10);
365 idlun = ((SCpnt->target << 5) & 0xe0) | (SCpnt->lun & 7);
366 SCpnt->scsi_done = done;
367
368 if (SCpnt->use_sg) {
369 #ifdef 0
370 struct scatterlist *sg = (struct scatterlist *) SCpnt->request_buffer;
371 short i;
372 Scb *scbn;
373
374
375
376
377
378
379 scbn = NULL;
380 for (i = 0; i < SCpnt->use_sg; i++) {
381 scb = alloc_scb(); scb->next = scbn; scbn = scb;
382 }
383 SCpnt->host_scribble = (unchar *) scb;
384
385
386
387
388
389
390
391
392
393 SCpnt->SCp.phase = 1;
394
395 for (i = 0; i < SCpnt->use_sg; i++) {
396 scb->op = 0;
397 scb->idlun = idlun;
398 memcpy(scb->cdb, cdb, cdblen);
399
400
401
402
403
404
405
406
407
408 any2scsi(scb->dataptr, sg[i].address);
409 any2scsi(scb->maxlen, sg[i].length);
410 if (i < SCpnt->use_sg-1) {
411 any2scsi(scb->linkptr, scb->next);
412 scb->cdb[cdblen-1] |= 0x01;
413 }
414 scb->direc = 0x40;
415 scb->SCpnt = SCpnt;
416 }
417 #else
418 panic("wd7000_queuecommand: scatter/gather not implemented.\n");
419 #endif
420 } else {
421 SCpnt->SCp.phase = 1;
422 scb = alloc_scb();
423 SCpnt->host_scribble = (unchar *) scb;
424 scb->op = 0;
425 scb->idlun = idlun;
426 memcpy(scb->cdb, cdb, cdblen);
427 any2scsi(scb->dataptr, SCpnt->request_buffer);
428 any2scsi(scb->maxlen, SCpnt->request_bufflen);
429 scb->direc = 0x40;
430 scb->SCpnt = SCpnt;
431 }
432
433 return mail_out(scb);
434 }
435
436
437 int wd7000_command(Scsi_Cmnd *SCpnt)
438 {
439 wd7000_queuecommand(SCpnt, wd7000_scsi_done);
440
441 while (SCpnt->SCp.phase > 0);
442
443 return SCpnt->result;
444 }
445
446
447 int wd7000_init(void)
448 { int i;
449 unchar init_block[] = {
450 INITIALIZATION, 7, BUS_ON, BUS_OFF, 0, 0, 0, 0, OGMB_CNT, ICMB_CNT
451 };
452
453
454 outb(SCSI_RES|ASC_RES, CONTROL);
455 delay(1);
456 outb(0,CONTROL); controlstat = 0;
457
458
459
460
461
462
463
464
465 delay(200);
466
467 WAIT(ASC_STAT, STATMASK, CMD_RDY, 0);
468 DEB(printk("wd7000_init: Power-on Diagnostics finished\n");)
469 if ((i=inb(INTR_STAT)) != 1) {
470 panic("wd7000_init: Power-on Diagnostics error\n");
471 return 0;
472 }
473
474
475 memset(&mb,0,sizeof (mb));
476
477 init_scbs();
478
479
480 any2scsi(init_block+5,&mb);
481
482 if (!command_out(init_block,sizeof(init_block))) {
483 panic("WD-7000 Initialization failed.\n");
484 return 0;
485 }
486
487
488 WAIT(ASC_STAT, STATMASK, CMD_RDY | ASC_INI, 0);
489 outb(DISABLE_UNS_INTR, COMMAND);
490 WAIT(ASC_STAT, STATMASK, CMD_RDY | ASC_INI, 0);
491
492
493 if (request_irq(IRQ_LVL, wd7000_intr_handle)) {
494 panic("Unable to allocate IRQ for WD-7000.\n");
495 return 0;
496 };
497 if(request_dma(DMA_CH)) {
498 panic("Unable to allocate DMA channel for WD-7000.\n");
499 free_irq(IRQ_LVL);
500 return 0;
501 };
502 wd7000_enable_dma();
503 wd7000_enable_intr();
504
505 printk("WD-7000 initialized.\n");
506 return 1;
507 fail:
508 return 0;
509 }
510
511
512 static const char *wd_bases[] = {(char *)0xce000};
513 typedef struct {
514 char * signature;
515 unsigned offset;
516 unsigned length;
517 } Signature;
518
519 static const Signature signatures[] = {{"SSTBIOS",0xd,0x7}};
520
521 #define NUM_SIGNATURES (sizeof(signatures)/sizeof(Signature))
522
523
524 int wd7000_detect(int hostnum)
525
526
527
528 {
529 int i,j;
530 char const * base_address = 0;
531
532 wd7000_host = hostnum;
533
534 for(i=0;i<(sizeof(wd_bases)/sizeof(char *));i++){
535 for(j=0;j<NUM_SIGNATURES;j++){
536 if(!memcmp((void *)(wd_bases[i] + signatures[j].offset),
537 (void *) signatures[j].signature,signatures[j].length)){
538 base_address=wd_bases[i];
539 printk("WD-7000 detected.\n");
540 }
541 }
542 }
543 if (!base_address) return 0;
544 wd7000_init();
545
546 return 1;
547 }
548
549
550
551 static void wd7000_append_info( char *info, const char *fmt, ... )
552
553
554
555 {
556 va_list args;
557 extern int vsprintf(char *buf, const char *fmt, va_list args);
558
559 va_start(args, fmt);
560 vsprintf(info, fmt, args);
561 va_end(args);
562
563 return;
564 }
565
566
567 const char *wd7000_info(void)
568 {
569 static char info[80] = "Western Digital WD-7000, Firmware Revision ";
570 volatile unchar icb[ICB_LEN] = {0x8c};
571 unchar rl1, rl2;
572
573 icb[ICB_PHASE] = 1;
574 mail_out( (struct scb *) icb );
575 while (icb[ICB_PHASE]) ;
576 rl1 = icb[1];
577 rl2 = icb[2];
578
579
580 wd7000_append_info( info+strlen(info), "%d.%d.\n", rl1, rl2 );
581
582 return info;
583 }
584
585
586 int wd7000_abort(Scsi_Cmnd * SCpnt, int i)
587 {
588 #ifdef DEBUG
589 printk("wd7000_abort: Scsi_Cmnd = 0x%08x, code = %d ", SCpnt, i);
590 printk("id %d lun %d cdb", SCpnt->target, SCpnt->lun);
591 { int j; unchar *cdbj = (unchar *) SCpnt->cmnd;
592 for (j=0; j < (*cdbj <= 0x1f?6:10); j++) printk(" %02x", *(cdbj++));
593 printk(" result %08x\n", SCpnt->result);
594 }
595 #endif
596 return 0;
597 }
598
599
600 int wd7000_reset(void)
601 {
602 printk("wd7000_reset\n");
603 return 0;
604 }
605
606
607 int wd7000_biosparam(int size, int dev, int* info)
608
609
610
611
612 {
613 info[0] = 32;
614 info[1] = 64;
615 info[2] = (size + 2047) >> 11;
616 if (info[2] >= 1024) info[2] = 1024;
617 return 0;
618 }
619