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