IRQ: Maintain regs pointer globally rather than passing to IRQ handlers
[linux-2.6.git] / drivers / scsi / qlogicfas408.c
1 /*----------------------------------------------------------------*/
2 /*
3    Qlogic linux driver - work in progress. No Warranty express or implied.
4    Use at your own risk.  Support Tort Reform so you won't have to read all
5    these silly disclaimers.
6
7    Copyright 1994, Tom Zerucha.   
8    tz@execpc.com
9    
10    Additional Code, and much appreciated help by
11    Michael A. Griffith
12    grif@cs.ucr.edu
13
14    Thanks to Eric Youngdale and Dave Hinds for loadable module and PCMCIA
15    help respectively, and for suffering through my foolishness during the
16    debugging process.
17
18    Reference Qlogic FAS408 Technical Manual, 53408-510-00A, May 10, 1994
19    (you can reference it, but it is incomplete and inaccurate in places)
20
21    Version 0.46 1/30/97 - kernel 1.2.0+
22
23    Functions as standalone, loadable, and PCMCIA driver, the latter from
24    Dave Hinds' PCMCIA package.
25    
26    Cleaned up 26/10/2002 by Alan Cox <alan@redhat.com> as part of the 2.5
27    SCSI driver cleanup and audit. This driver still needs work on the
28    following
29         -       Non terminating hardware waits
30         -       Some layering violations with its pcmcia stub
31
32    Redistributable under terms of the GNU General Public License
33
34    For the avoidance of doubt the "preferred form" of this code is one which
35    is in an open non patent encumbered format. Where cryptographic key signing
36    forms part of the process of creating an executable the information
37    including keys needed to generate an equivalently functional executable
38    are deemed to be part of the source code.
39
40 */
41
42 #include <linux/module.h>
43 #include <linux/blkdev.h>               /* to get disk capacity */
44 #include <linux/kernel.h>
45 #include <linux/string.h>
46 #include <linux/init.h>
47 #include <linux/interrupt.h>
48 #include <linux/ioport.h>
49 #include <linux/proc_fs.h>
50 #include <linux/unistd.h>
51 #include <linux/spinlock.h>
52 #include <linux/stat.h>
53
54 #include <asm/io.h>
55 #include <asm/irq.h>
56 #include <asm/dma.h>
57
58 #include "scsi.h"
59 #include <scsi/scsi_host.h>
60 #include "qlogicfas408.h"
61
62 /*----------------------------------------------------------------*/
63 static int qlcfg5 = (XTALFREQ << 5);    /* 15625/512 */
64 static int qlcfg6 = SYNCXFRPD;
65 static int qlcfg7 = SYNCOFFST;
66 static int qlcfg8 = (SLOWCABLE << 7) | (QL_ENABLE_PARITY << 4);
67 static int qlcfg9 = ((XTALFREQ + 4) / 5);
68 static int qlcfgc = (FASTCLK << 3) | (FASTSCSI << 4);
69
70 /*----------------------------------------------------------------*/
71
72 /*----------------------------------------------------------------*/
73 /* local functions */
74 /*----------------------------------------------------------------*/
75
76 /* error recovery - reset everything */
77
78 static void ql_zap(struct qlogicfas408_priv *priv)
79 {
80         int x;
81         int qbase = priv->qbase;
82         int int_type = priv->int_type;
83
84         x = inb(qbase + 0xd);
85         REG0;
86         outb(3, qbase + 3);     /* reset SCSI */
87         outb(2, qbase + 3);     /* reset chip */
88         if (x & 0x80)
89                 REG1;
90 }
91
92 /*
93  *      Do a pseudo-dma tranfer
94  */
95  
96 static int ql_pdma(struct qlogicfas408_priv *priv, int phase, char *request, int reqlen)
97 {
98         int j;
99         int qbase = priv->qbase;
100         j = 0;
101         if (phase & 1) {        /* in */
102 #if QL_TURBO_PDMA
103                 rtrc(4)
104                 /* empty fifo in large chunks */
105                 if (reqlen >= 128 && (inb(qbase + 8) & 2)) {    /* full */
106                         insl(qbase + 4, request, 32);
107                         reqlen -= 128;
108                         request += 128;
109                 }
110                 while (reqlen >= 84 && !(j & 0xc0))     /* 2/3 */
111                         if ((j = inb(qbase + 8)) & 4) 
112                         {
113                                 insl(qbase + 4, request, 21);
114                                 reqlen -= 84;
115                                 request += 84;
116                         }
117                 if (reqlen >= 44 && (inb(qbase + 8) & 8)) {     /* 1/3 */
118                         insl(qbase + 4, request, 11);
119                         reqlen -= 44;
120                         request += 44;
121                 }
122 #endif
123                 /* until both empty and int (or until reclen is 0) */
124                 rtrc(7)
125                 j = 0;
126                 while (reqlen && !((j & 0x10) && (j & 0xc0))) 
127                 {
128                         /* while bytes to receive and not empty */
129                         j &= 0xc0;
130                         while (reqlen && !((j = inb(qbase + 8)) & 0x10)) 
131                         {
132                                 *request++ = inb(qbase + 4);
133                                 reqlen--;
134                         }
135                         if (j & 0x10)
136                                 j = inb(qbase + 8);
137
138                 }
139         } else {                /* out */
140 #if QL_TURBO_PDMA
141                 rtrc(4)
142                     if (reqlen >= 128 && inb(qbase + 8) & 0x10) {       /* empty */
143                         outsl(qbase + 4, request, 32);
144                         reqlen -= 128;
145                         request += 128;
146                 }
147                 while (reqlen >= 84 && !(j & 0xc0))     /* 1/3 */
148                         if (!((j = inb(qbase + 8)) & 8)) {
149                                 outsl(qbase + 4, request, 21);
150                                 reqlen -= 84;
151                                 request += 84;
152                         }
153                 if (reqlen >= 40 && !(inb(qbase + 8) & 4)) {    /* 2/3 */
154                         outsl(qbase + 4, request, 10);
155                         reqlen -= 40;
156                         request += 40;
157                 }
158 #endif
159                 /* until full and int (or until reclen is 0) */
160                 rtrc(7)
161                     j = 0;
162                 while (reqlen && !((j & 2) && (j & 0xc0))) {
163                         /* while bytes to send and not full */
164                         while (reqlen && !((j = inb(qbase + 8)) & 2)) 
165                         {
166                                 outb(*request++, qbase + 4);
167                                 reqlen--;
168                         }
169                         if (j & 2)
170                                 j = inb(qbase + 8);
171                 }
172         }
173         /* maybe return reqlen */
174         return inb(qbase + 8) & 0xc0;
175 }
176
177 /*
178  *      Wait for interrupt flag (polled - not real hardware interrupt) 
179  */
180
181 static int ql_wai(struct qlogicfas408_priv *priv)
182 {
183         int k;
184         int qbase = priv->qbase;
185         unsigned long i;
186
187         k = 0;
188         i = jiffies + WATCHDOG;
189         while (time_before(jiffies, i) && !priv->qabort &&
190                                         !((k = inb(qbase + 4)) & 0xe0)) {
191                 barrier();
192                 cpu_relax();
193         }
194         if (time_after_eq(jiffies, i))
195                 return (DID_TIME_OUT);
196         if (priv->qabort)
197                 return (priv->qabort == 1 ? DID_ABORT : DID_RESET);
198         if (k & 0x60)
199                 ql_zap(priv);
200         if (k & 0x20)
201                 return (DID_PARITY);
202         if (k & 0x40)
203                 return (DID_ERROR);
204         return 0;
205 }
206
207 /*
208  *      Initiate scsi command - queueing handler 
209  *      caller must hold host lock
210  */
211
212 static void ql_icmd(Scsi_Cmnd * cmd)
213 {
214         struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd);
215         int     qbase = priv->qbase;
216         int     int_type = priv->int_type;
217         unsigned int i;
218
219         priv->qabort = 0;
220
221         REG0;
222         /* clearing of interrupts and the fifo is needed */
223
224         inb(qbase + 5);         /* clear interrupts */
225         if (inb(qbase + 5))     /* if still interrupting */
226                 outb(2, qbase + 3);     /* reset chip */
227         else if (inb(qbase + 7) & 0x1f)
228                 outb(1, qbase + 3);     /* clear fifo */
229         while (inb(qbase + 5)); /* clear ints */
230         REG1;
231         outb(1, qbase + 8);     /* set for PIO pseudo DMA */
232         outb(0, qbase + 0xb);   /* disable ints */
233         inb(qbase + 8);         /* clear int bits */
234         REG0;
235         outb(0x40, qbase + 0xb);        /* enable features */
236
237         /* configurables */
238         outb(qlcfgc, qbase + 0xc);
239         /* config: no reset interrupt, (initiator) bus id */
240         outb(0x40 | qlcfg8 | priv->qinitid, qbase + 8);
241         outb(qlcfg7, qbase + 7);
242         outb(qlcfg6, qbase + 6);
243          /**/ outb(qlcfg5, qbase + 5);  /* select timer */
244         outb(qlcfg9 & 7, qbase + 9);    /* prescaler */
245 /*      outb(0x99, qbase + 5);  */
246         outb(scmd_id(cmd), qbase + 4);
247
248         for (i = 0; i < cmd->cmd_len; i++)
249                 outb(cmd->cmnd[i], qbase + 2);
250
251         priv->qlcmd = cmd;
252         outb(0x41, qbase + 3);  /* select and send command */
253 }
254
255 /*
256  *      Process scsi command - usually after interrupt 
257  */
258
259 static unsigned int ql_pcmd(Scsi_Cmnd * cmd)
260 {
261         unsigned int i, j;
262         unsigned long k;
263         unsigned int result;    /* ultimate return result */
264         unsigned int status;    /* scsi returned status */
265         unsigned int message;   /* scsi returned message */
266         unsigned int phase;     /* recorded scsi phase */
267         unsigned int reqlen;    /* total length of transfer */
268         struct scatterlist *sglist;     /* scatter-gather list pointer */
269         unsigned int sgcount;   /* sg counter */
270         char *buf;
271         struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd);
272         int qbase = priv->qbase;
273         int int_type = priv->int_type;
274
275         rtrc(1)
276         j = inb(qbase + 6);
277         i = inb(qbase + 5);
278         if (i == 0x20) {
279                 return (DID_NO_CONNECT << 16);
280         }
281         i |= inb(qbase + 5);    /* the 0x10 bit can be set after the 0x08 */
282         if (i != 0x18) {
283                 printk(KERN_ERR "Ql:Bad Interrupt status:%02x\n", i);
284                 ql_zap(priv);
285                 return (DID_BAD_INTR << 16);
286         }
287         j &= 7;                 /* j = inb( qbase + 7 ) >> 5; */
288
289         /* correct status is supposed to be step 4 */
290         /* it sometimes returns step 3 but with 0 bytes left to send */
291         /* We can try stuffing the FIFO with the max each time, but we will get a
292            sequence of 3 if any bytes are left (but we do flush the FIFO anyway */
293
294         if (j != 3 && j != 4) {
295                 printk(KERN_ERR "Ql:Bad sequence for command %d, int %02X, cmdleft = %d\n",
296                      j, i, inb(qbase + 7) & 0x1f);
297                 ql_zap(priv);
298                 return (DID_ERROR << 16);
299         }
300         result = DID_OK;
301         if (inb(qbase + 7) & 0x1f)      /* if some bytes in fifo */
302                 outb(1, qbase + 3);     /* clear fifo */
303         /* note that request_bufflen is the total xfer size when sg is used */
304         reqlen = cmd->request_bufflen;
305         /* note that it won't work if transfers > 16M are requested */
306         if (reqlen && !((phase = inb(qbase + 4)) & 6)) {        /* data phase */
307                 rtrc(2)
308                 outb(reqlen, qbase);    /* low-mid xfer cnt */
309                 outb(reqlen >> 8, qbase + 1);   /* low-mid xfer cnt */
310                 outb(reqlen >> 16, qbase + 0xe);        /* high xfer cnt */
311                 outb(0x90, qbase + 3);  /* command do xfer */
312                 /* PIO pseudo DMA to buffer or sglist */
313                 REG1;
314                 if (!cmd->use_sg)
315                         ql_pdma(priv, phase, cmd->request_buffer,
316                                 cmd->request_bufflen);
317                 else {
318                         sgcount = cmd->use_sg;
319                         sglist = cmd->request_buffer;
320                         while (sgcount--) {
321                                 if (priv->qabort) {
322                                         REG0;
323                                         return ((priv->qabort == 1 ?
324                                                 DID_ABORT : DID_RESET) << 16);
325                                 }
326                                 buf = page_address(sglist->page) + sglist->offset;
327                                 if (ql_pdma(priv, phase, buf, sglist->length))
328                                         break;
329                                 sglist++;
330                         }
331                 }
332                 REG0;
333                 rtrc(2)
334                 /*
335                  *      Wait for irq (split into second state of irq handler
336                  *      if this can take time) 
337                  */
338                 if ((k = ql_wai(priv)))
339                         return (k << 16);
340                 k = inb(qbase + 5);     /* should be 0x10, bus service */
341         }
342
343         /*
344          *      Enter Status (and Message In) Phase 
345          */
346          
347         k = jiffies + WATCHDOG;
348
349         while (time_before(jiffies, k) && !priv->qabort &&
350                                                 !(inb(qbase + 4) & 6))
351                 cpu_relax();    /* wait for status phase */
352
353         if (time_after_eq(jiffies, k)) {
354                 ql_zap(priv);
355                 return (DID_TIME_OUT << 16);
356         }
357
358         /* FIXME: timeout ?? */
359         while (inb(qbase + 5))
360                 cpu_relax();    /* clear pending ints */
361
362         if (priv->qabort)
363                 return ((priv->qabort == 1 ? DID_ABORT : DID_RESET) << 16);
364
365         outb(0x11, qbase + 3);  /* get status and message */
366         if ((k = ql_wai(priv)))
367                 return (k << 16);
368         i = inb(qbase + 5);     /* get chip irq stat */
369         j = inb(qbase + 7) & 0x1f;      /* and bytes rec'd */
370         status = inb(qbase + 2);
371         message = inb(qbase + 2);
372
373         /*
374          *      Should get function complete int if Status and message, else 
375          *      bus serv if only status 
376          */
377         if (!((i == 8 && j == 2) || (i == 0x10 && j == 1))) {
378                 printk(KERN_ERR "Ql:Error during status phase, int=%02X, %d bytes recd\n", i, j);
379                 result = DID_ERROR;
380         }
381         outb(0x12, qbase + 3);  /* done, disconnect */
382         rtrc(1)
383         if ((k = ql_wai(priv)))
384                 return (k << 16);
385
386         /*
387          *      Should get bus service interrupt and disconnect interrupt 
388          */
389          
390         i = inb(qbase + 5);     /* should be bus service */
391         while (!priv->qabort && ((i & 0x20) != 0x20)) {
392                 barrier();
393                 cpu_relax();
394                 i |= inb(qbase + 5);
395         }
396         rtrc(0)
397
398         if (priv->qabort)
399                 return ((priv->qabort == 1 ? DID_ABORT : DID_RESET) << 16);
400                 
401         return (result << 16) | (message << 8) | (status & STATUS_MASK);
402 }
403
404 /*
405  *      Interrupt handler 
406  */
407
408 static void ql_ihandl(int irq, void *dev_id)
409 {
410         Scsi_Cmnd *icmd;
411         struct Scsi_Host *host = (struct Scsi_Host *)dev_id;
412         struct qlogicfas408_priv *priv = get_priv_by_host(host);
413         int qbase = priv->qbase;
414         REG0;
415
416         if (!(inb(qbase + 4) & 0x80))   /* false alarm? */
417                 return;
418
419         if (priv->qlcmd == NULL) {      /* no command to process? */
420                 int i;
421                 i = 16;
422                 while (i-- && inb(qbase + 5));  /* maybe also ql_zap() */
423                 return;
424         }
425         icmd = priv->qlcmd;
426         icmd->result = ql_pcmd(icmd);
427         priv->qlcmd = NULL;
428         /*
429          *      If result is CHECK CONDITION done calls qcommand to request 
430          *      sense 
431          */
432         (icmd->scsi_done) (icmd);
433 }
434
435 irqreturn_t qlogicfas408_ihandl(int irq, void *dev_id)
436 {
437         unsigned long flags;
438         struct Scsi_Host *host = dev_id;
439
440         spin_lock_irqsave(host->host_lock, flags);
441         ql_ihandl(irq, dev_id);
442         spin_unlock_irqrestore(host->host_lock, flags);
443         return IRQ_HANDLED;
444 }
445
446 /*
447  *      Queued command
448  */
449
450 int qlogicfas408_queuecommand(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *))
451 {
452         struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd);
453         if (scmd_id(cmd) == priv->qinitid) {
454                 cmd->result = DID_BAD_TARGET << 16;
455                 done(cmd);
456                 return 0;
457         }
458
459         cmd->scsi_done = done;
460         /* wait for the last command's interrupt to finish */
461         while (priv->qlcmd != NULL) {
462                 barrier();
463                 cpu_relax();
464         }
465         ql_icmd(cmd);
466         return 0;
467 }
468
469 /* 
470  *      Return bios parameters 
471  */
472
473 int qlogicfas408_biosparam(struct scsi_device * disk,
474                         struct block_device *dev,
475                         sector_t capacity, int ip[])
476 {
477 /* This should mimic the DOS Qlogic driver's behavior exactly */
478         ip[0] = 0x40;
479         ip[1] = 0x20;
480         ip[2] = (unsigned long) capacity / (ip[0] * ip[1]);
481         if (ip[2] > 1024) {
482                 ip[0] = 0xff;
483                 ip[1] = 0x3f;
484                 ip[2] = (unsigned long) capacity / (ip[0] * ip[1]);
485 #if 0
486                 if (ip[2] > 1023)
487                         ip[2] = 1023;
488 #endif
489         }
490         return 0;
491 }
492
493 /*
494  *      Abort a command in progress
495  */
496  
497 int qlogicfas408_abort(Scsi_Cmnd * cmd)
498 {
499         struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd);
500         priv->qabort = 1;
501         ql_zap(priv);
502         return SUCCESS;
503 }
504
505 /* 
506  *      Reset SCSI bus
507  *      FIXME: This function is invoked with cmd = NULL directly by
508  *      the PCMCIA qlogic_stub code. This wants fixing
509  */
510
511 int qlogicfas408_bus_reset(Scsi_Cmnd * cmd)
512 {
513         struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd);
514         unsigned long flags;
515
516         priv->qabort = 2;
517
518         spin_lock_irqsave(cmd->device->host->host_lock, flags);
519         ql_zap(priv);
520         spin_unlock_irqrestore(cmd->device->host->host_lock, flags);
521
522         return SUCCESS;
523 }
524
525 /*
526  *      Return info string
527  */
528
529 const char *qlogicfas408_info(struct Scsi_Host *host)
530 {
531         struct qlogicfas408_priv *priv = get_priv_by_host(host);
532         return priv->qinfo;
533 }
534
535 /*
536  *      Get type of chip
537  */
538
539 int qlogicfas408_get_chip_type(int qbase, int int_type)
540 {
541         REG1;
542         return inb(qbase + 0xe) & 0xf8;
543 }
544
545 /*
546  *      Perform initialization tasks
547  */
548
549 void qlogicfas408_setup(int qbase, int id, int int_type)
550 {
551         outb(1, qbase + 8);     /* set for PIO pseudo DMA */
552         REG0;
553         outb(0x40 | qlcfg8 | id, qbase + 8);    /* (ini) bus id, disable scsi rst */
554         outb(qlcfg5, qbase + 5);        /* select timer */
555         outb(qlcfg9, qbase + 9);        /* prescaler */
556
557 #if QL_RESET_AT_START
558         outb(3, qbase + 3);
559
560         REG1;
561         /* FIXME: timeout */
562         while (inb(qbase + 0xf) & 4)
563                 cpu_relax();
564
565         REG0;
566 #endif
567 }
568
569 /*
570  *      Checks if this is a QLogic FAS 408
571  */
572
573 int qlogicfas408_detect(int qbase, int int_type)
574 {
575         REG1;
576         return (((inb(qbase + 0xe) ^ inb(qbase + 0xe)) == 7) &&
577                ((inb(qbase + 0xe) ^ inb(qbase + 0xe)) == 7));           
578 }
579
580 /*
581  *      Disable interrupts
582  */
583
584 void qlogicfas408_disable_ints(struct qlogicfas408_priv *priv)
585 {
586         int qbase = priv->qbase;
587         int int_type = priv->int_type;
588
589         REG1;
590         outb(0, qbase + 0xb);   /* disable ints */
591 }
592
593 /*
594  *      Init and exit functions
595  */
596
597 static int __init qlogicfas408_init(void)
598 {
599         return 0;
600 }
601
602 static void __exit qlogicfas408_exit(void)
603 {
604
605 }
606
607 MODULE_AUTHOR("Tom Zerucha, Michael Griffith");
608 MODULE_DESCRIPTION("Driver for the Qlogic FAS SCSI controllers");
609 MODULE_LICENSE("GPL");
610 module_init(qlogicfas408_init);
611 module_exit(qlogicfas408_exit);
612
613 EXPORT_SYMBOL(qlogicfas408_info);
614 EXPORT_SYMBOL(qlogicfas408_queuecommand);
615 EXPORT_SYMBOL(qlogicfas408_abort);
616 EXPORT_SYMBOL(qlogicfas408_bus_reset);
617 EXPORT_SYMBOL(qlogicfas408_biosparam);
618 EXPORT_SYMBOL(qlogicfas408_ihandl);
619 EXPORT_SYMBOL(qlogicfas408_get_chip_type);
620 EXPORT_SYMBOL(qlogicfas408_setup);
621 EXPORT_SYMBOL(qlogicfas408_detect);
622 EXPORT_SYMBOL(qlogicfas408_disable_ints);
623