blob: de7b3bc2cbc975ecbbd12658ea95b8d9e00714f5 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*----------------------------------------------------------------*/
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/*----------------------------------------------------------------*/
63static int qlcfg5 = (XTALFREQ << 5); /* 15625/512 */
64static int qlcfg6 = SYNCXFRPD;
65static int qlcfg7 = SYNCOFFST;
66static int qlcfg8 = (SLOWCABLE << 7) | (QL_ENABLE_PARITY << 4);
67static int qlcfg9 = ((XTALFREQ + 4) / 5);
68static int qlcfgc = (FASTCLK << 3) | (FASTSCSI << 4);
69
70/*----------------------------------------------------------------*/
71
72/*----------------------------------------------------------------*/
73/* local functions */
74/*----------------------------------------------------------------*/
75
76/* error recovery - reset everything */
77
78static 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
96static 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
181static 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
Hennea24342b2006-10-03 21:31:14 +0200212static void ql_icmd(struct scsi_cmnd *cmd)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213{
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); */
Jeff Garzik422c0d62005-10-24 18:05:09 -0400246 outb(scmd_id(cmd), qbase + 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247
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
Hennea24342b2006-10-03 21:31:14 +0200259static unsigned int ql_pcmd(struct scsi_cmnd *cmd)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260{
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 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268 char *buf;
269 struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd);
270 int qbase = priv->qbase;
271 int int_type = priv->int_type;
272
273 rtrc(1)
274 j = inb(qbase + 6);
275 i = inb(qbase + 5);
276 if (i == 0x20) {
277 return (DID_NO_CONNECT << 16);
278 }
279 i |= inb(qbase + 5); /* the 0x10 bit can be set after the 0x08 */
280 if (i != 0x18) {
281 printk(KERN_ERR "Ql:Bad Interrupt status:%02x\n", i);
282 ql_zap(priv);
283 return (DID_BAD_INTR << 16);
284 }
285 j &= 7; /* j = inb( qbase + 7 ) >> 5; */
286
287 /* correct status is supposed to be step 4 */
288 /* it sometimes returns step 3 but with 0 bytes left to send */
289 /* We can try stuffing the FIFO with the max each time, but we will get a
290 sequence of 3 if any bytes are left (but we do flush the FIFO anyway */
291
292 if (j != 3 && j != 4) {
293 printk(KERN_ERR "Ql:Bad sequence for command %d, int %02X, cmdleft = %d\n",
294 j, i, inb(qbase + 7) & 0x1f);
295 ql_zap(priv);
296 return (DID_ERROR << 16);
297 }
298 result = DID_OK;
299 if (inb(qbase + 7) & 0x1f) /* if some bytes in fifo */
300 outb(1, qbase + 3); /* clear fifo */
301 /* note that request_bufflen is the total xfer size when sg is used */
FUJITA Tomonoribc1ebfb2007-05-14 19:24:01 +0900302 reqlen = scsi_bufflen(cmd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303 /* note that it won't work if transfers > 16M are requested */
304 if (reqlen && !((phase = inb(qbase + 4)) & 6)) { /* data phase */
FUJITA Tomonoribc1ebfb2007-05-14 19:24:01 +0900305 struct scatterlist *sg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306 rtrc(2)
307 outb(reqlen, qbase); /* low-mid xfer cnt */
308 outb(reqlen >> 8, qbase + 1); /* low-mid xfer cnt */
309 outb(reqlen >> 16, qbase + 0xe); /* high xfer cnt */
310 outb(0x90, qbase + 3); /* command do xfer */
311 /* PIO pseudo DMA to buffer or sglist */
312 REG1;
FUJITA Tomonoribc1ebfb2007-05-14 19:24:01 +0900313
314 scsi_for_each_sg(cmd, sg, scsi_sg_count(cmd), i) {
315 if (priv->qabort) {
316 REG0;
317 return ((priv->qabort == 1 ?
318 DID_ABORT : DID_RESET) << 16);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319 }
Jens Axboe45711f12007-10-22 21:19:53 +0200320 buf = sg_virt(sg);
FUJITA Tomonoribc1ebfb2007-05-14 19:24:01 +0900321 if (ql_pdma(priv, phase, buf, sg->length))
322 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323 }
324 REG0;
325 rtrc(2)
326 /*
327 * Wait for irq (split into second state of irq handler
328 * if this can take time)
329 */
330 if ((k = ql_wai(priv)))
331 return (k << 16);
332 k = inb(qbase + 5); /* should be 0x10, bus service */
333 }
334
335 /*
336 * Enter Status (and Message In) Phase
337 */
338
339 k = jiffies + WATCHDOG;
340
341 while (time_before(jiffies, k) && !priv->qabort &&
342 !(inb(qbase + 4) & 6))
343 cpu_relax(); /* wait for status phase */
344
345 if (time_after_eq(jiffies, k)) {
346 ql_zap(priv);
347 return (DID_TIME_OUT << 16);
348 }
349
350 /* FIXME: timeout ?? */
351 while (inb(qbase + 5))
352 cpu_relax(); /* clear pending ints */
353
354 if (priv->qabort)
355 return ((priv->qabort == 1 ? DID_ABORT : DID_RESET) << 16);
356
357 outb(0x11, qbase + 3); /* get status and message */
358 if ((k = ql_wai(priv)))
359 return (k << 16);
360 i = inb(qbase + 5); /* get chip irq stat */
361 j = inb(qbase + 7) & 0x1f; /* and bytes rec'd */
362 status = inb(qbase + 2);
363 message = inb(qbase + 2);
364
365 /*
366 * Should get function complete int if Status and message, else
367 * bus serv if only status
368 */
369 if (!((i == 8 && j == 2) || (i == 0x10 && j == 1))) {
370 printk(KERN_ERR "Ql:Error during status phase, int=%02X, %d bytes recd\n", i, j);
371 result = DID_ERROR;
372 }
373 outb(0x12, qbase + 3); /* done, disconnect */
374 rtrc(1)
375 if ((k = ql_wai(priv)))
376 return (k << 16);
377
378 /*
379 * Should get bus service interrupt and disconnect interrupt
380 */
381
382 i = inb(qbase + 5); /* should be bus service */
383 while (!priv->qabort && ((i & 0x20) != 0x20)) {
384 barrier();
385 cpu_relax();
386 i |= inb(qbase + 5);
387 }
388 rtrc(0)
389
390 if (priv->qabort)
391 return ((priv->qabort == 1 ? DID_ABORT : DID_RESET) << 16);
392
393 return (result << 16) | (message << 8) | (status & STATUS_MASK);
394}
395
396/*
397 * Interrupt handler
398 */
399
Jeff Garzikc7bec5a2006-10-06 15:00:58 -0400400static void ql_ihandl(void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401{
Hennea24342b2006-10-03 21:31:14 +0200402 struct scsi_cmnd *icmd;
Jeff Garzikc7bec5a2006-10-06 15:00:58 -0400403 struct Scsi_Host *host = dev_id;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404 struct qlogicfas408_priv *priv = get_priv_by_host(host);
405 int qbase = priv->qbase;
406 REG0;
407
408 if (!(inb(qbase + 4) & 0x80)) /* false alarm? */
409 return;
410
411 if (priv->qlcmd == NULL) { /* no command to process? */
412 int i;
413 i = 16;
414 while (i-- && inb(qbase + 5)); /* maybe also ql_zap() */
415 return;
416 }
417 icmd = priv->qlcmd;
418 icmd->result = ql_pcmd(icmd);
419 priv->qlcmd = NULL;
420 /*
421 * If result is CHECK CONDITION done calls qcommand to request
422 * sense
423 */
424 (icmd->scsi_done) (icmd);
425}
426
David Howells7d12e782006-10-05 14:55:46 +0100427irqreturn_t qlogicfas408_ihandl(int irq, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428{
429 unsigned long flags;
430 struct Scsi_Host *host = dev_id;
431
432 spin_lock_irqsave(host->host_lock, flags);
Jeff Garzikc7bec5a2006-10-06 15:00:58 -0400433 ql_ihandl(dev_id);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434 spin_unlock_irqrestore(host->host_lock, flags);
435 return IRQ_HANDLED;
436}
437
438/*
439 * Queued command
440 */
441
Hennea24342b2006-10-03 21:31:14 +0200442int qlogicfas408_queuecommand(struct scsi_cmnd *cmd,
443 void (*done) (struct scsi_cmnd *))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444{
445 struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd);
Jeff Garzik422c0d62005-10-24 18:05:09 -0400446 if (scmd_id(cmd) == priv->qinitid) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447 cmd->result = DID_BAD_TARGET << 16;
448 done(cmd);
449 return 0;
450 }
451
452 cmd->scsi_done = done;
453 /* wait for the last command's interrupt to finish */
454 while (priv->qlcmd != NULL) {
455 barrier();
456 cpu_relax();
457 }
458 ql_icmd(cmd);
459 return 0;
460}
461
462/*
463 * Return bios parameters
464 */
465
Hennea24342b2006-10-03 21:31:14 +0200466int qlogicfas408_biosparam(struct scsi_device *disk, struct block_device *dev,
467 sector_t capacity, int ip[])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468{
469/* This should mimic the DOS Qlogic driver's behavior exactly */
470 ip[0] = 0x40;
471 ip[1] = 0x20;
472 ip[2] = (unsigned long) capacity / (ip[0] * ip[1]);
473 if (ip[2] > 1024) {
474 ip[0] = 0xff;
475 ip[1] = 0x3f;
476 ip[2] = (unsigned long) capacity / (ip[0] * ip[1]);
477#if 0
478 if (ip[2] > 1023)
479 ip[2] = 1023;
480#endif
481 }
482 return 0;
483}
484
485/*
486 * Abort a command in progress
487 */
488
Hennea24342b2006-10-03 21:31:14 +0200489int qlogicfas408_abort(struct scsi_cmnd *cmd)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490{
491 struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd);
492 priv->qabort = 1;
493 ql_zap(priv);
494 return SUCCESS;
495}
496
497/*
498 * Reset SCSI bus
499 * FIXME: This function is invoked with cmd = NULL directly by
500 * the PCMCIA qlogic_stub code. This wants fixing
501 */
502
Hennea24342b2006-10-03 21:31:14 +0200503int qlogicfas408_bus_reset(struct scsi_cmnd *cmd)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504{
505 struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd);
Jeff Garzik 68b3aa72005-05-28 07:56:31 -0400506 unsigned long flags;
507
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508 priv->qabort = 2;
Jeff Garzik 68b3aa72005-05-28 07:56:31 -0400509
510 spin_lock_irqsave(cmd->device->host->host_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511 ql_zap(priv);
Jeff Garzik 68b3aa72005-05-28 07:56:31 -0400512 spin_unlock_irqrestore(cmd->device->host->host_lock, flags);
513
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514 return SUCCESS;
515}
516
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517/*
518 * Return info string
519 */
520
521const char *qlogicfas408_info(struct Scsi_Host *host)
522{
523 struct qlogicfas408_priv *priv = get_priv_by_host(host);
524 return priv->qinfo;
525}
526
527/*
528 * Get type of chip
529 */
530
531int qlogicfas408_get_chip_type(int qbase, int int_type)
532{
533 REG1;
534 return inb(qbase + 0xe) & 0xf8;
535}
536
537/*
538 * Perform initialization tasks
539 */
540
541void qlogicfas408_setup(int qbase, int id, int int_type)
542{
543 outb(1, qbase + 8); /* set for PIO pseudo DMA */
544 REG0;
545 outb(0x40 | qlcfg8 | id, qbase + 8); /* (ini) bus id, disable scsi rst */
546 outb(qlcfg5, qbase + 5); /* select timer */
547 outb(qlcfg9, qbase + 9); /* prescaler */
548
549#if QL_RESET_AT_START
550 outb(3, qbase + 3);
551
552 REG1;
553 /* FIXME: timeout */
554 while (inb(qbase + 0xf) & 4)
555 cpu_relax();
556
557 REG0;
558#endif
559}
560
561/*
562 * Checks if this is a QLogic FAS 408
563 */
564
565int qlogicfas408_detect(int qbase, int int_type)
566{
567 REG1;
568 return (((inb(qbase + 0xe) ^ inb(qbase + 0xe)) == 7) &&
569 ((inb(qbase + 0xe) ^ inb(qbase + 0xe)) == 7));
570}
571
572/*
573 * Disable interrupts
574 */
575
576void qlogicfas408_disable_ints(struct qlogicfas408_priv *priv)
577{
578 int qbase = priv->qbase;
579 int int_type = priv->int_type;
580
581 REG1;
582 outb(0, qbase + 0xb); /* disable ints */
583}
584
585/*
586 * Init and exit functions
587 */
588
589static int __init qlogicfas408_init(void)
590{
591 return 0;
592}
593
594static void __exit qlogicfas408_exit(void)
595{
596
597}
598
599MODULE_AUTHOR("Tom Zerucha, Michael Griffith");
600MODULE_DESCRIPTION("Driver for the Qlogic FAS SCSI controllers");
601MODULE_LICENSE("GPL");
602module_init(qlogicfas408_init);
603module_exit(qlogicfas408_exit);
604
605EXPORT_SYMBOL(qlogicfas408_info);
606EXPORT_SYMBOL(qlogicfas408_queuecommand);
607EXPORT_SYMBOL(qlogicfas408_abort);
608EXPORT_SYMBOL(qlogicfas408_bus_reset);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609EXPORT_SYMBOL(qlogicfas408_biosparam);
610EXPORT_SYMBOL(qlogicfas408_ihandl);
611EXPORT_SYMBOL(qlogicfas408_get_chip_type);
612EXPORT_SYMBOL(qlogicfas408_setup);
613EXPORT_SYMBOL(qlogicfas408_detect);
614EXPORT_SYMBOL(qlogicfas408_disable_ints);
615