This source file includes following definitions.
- wd7000fasst_stat
- wd7000fasst_out
- wd7000fasst_make_error_code
- wd7000fasst_init
- wd7000fasst_info
- wd7000fasst_intr_handle
- wd7000fasst_queuecommand
- internal_done
- wd7000fasst_command
- wd7000fasst_call_buh
- wd7000fasst_detect
- wd7000fasst_abort
- wd7000fasst_reset
1
2
3
4
5
6
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
25
26 #include "7000fasst.h"
27 #ifdef DEBUG
28 #define DEB(x) x
29 #else
30 #define DEB(x)
31 #endif
32
33
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
81
82
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:
105 hosterr=DID_ERROR;
106 break;
107 case 1: hosterr=DID_OK;
108 break;
109 case 2:
110
111
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;
120 break;
121 case 5: hosterr=DID_RESET;
122 break;
123 case 6: hosterr=DID_ERROR;
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
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
145 unchar init_block[]={ 1, 7, 0x18, 0x18, 0, 0, 0, 0, OGMB_CNT, ICMB_CNT };
146
147
148 DEB(printk("wd7000fasst_init called \n"));
149
150 outb(SCSI_RES|ASC_RES, CONTROL);
151
152 for (i=0; i< 1000; i++) inb(ASC_STAT);
153
154 outb(0,CONTROL);
155 debug = 1;
156
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
164 memset(&mbstruct,0,sizeof (mbstruct));
165
166 any2scsi(init_block+5,&mbstruct);
167
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
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
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;
185 }
186
187
188 char *wd7000fasst_info(void)
189 {
190 static char buffer[] = "Western Digital 7000-FASST";
191 return buffer;
192 }
193
194
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
217 if ((flag&0xc0)==0xc0){
218
219
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
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
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
248
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
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
267 for (i=0;i<OGMB_CNT;i++){
268 if (mbstruct.ombox[i].status==0){
269
270 DEB(printk("Found outgoing mbox %x\n",i));
271 memset(&scbs[i], 0, sizeof(struct scb));
272
273 memcpy(scbs[i].scbdata, cmd, (*cmd<=0x1f)?6:10);
274 scbs[i].op = 0;
275 scbs[i].idlun = (target<<5)&0xe0;
276 any2scsi(scbs[i].dataptr,buff);
277 any2scsi(scbs[i].maxdata,bufflen);
278 scbs[i].direc=0x40;
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
283 any2scsi((mbstruct.ombox[i].scbptr),&(scbs[i]));
284 mbstruct.ombox[i].status=1;
285
286 break;
287 }
288 }
289 if (i==OGMB_CNT){
290
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
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
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
329
330 fail:
331 return 0;
332 }
333
334
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
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
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)
376 {
377 int i,j;
378 char const * base_address = 0;
379
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
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 ");