aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/qlogicfas408.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/scsi/qlogicfas408.c
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'drivers/scsi/qlogicfas408.c')
-rw-r--r--drivers/scsi/qlogicfas408.c637
1 files changed, 637 insertions, 0 deletions
diff --git a/drivers/scsi/qlogicfas408.c b/drivers/scsi/qlogicfas408.c
new file mode 100644
index 000000000000..5b6ce0a88f08
--- /dev/null
+++ b/drivers/scsi/qlogicfas408.c
@@ -0,0 +1,637 @@
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/*----------------------------------------------------------------*/
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
212static 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(cmd->device->id, 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
259static 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
408static void ql_ihandl(int irq, void *dev_id, struct pt_regs *regs)
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
435irqreturn_t qlogicfas408_ihandl(int irq, void *dev_id, struct pt_regs *regs)
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, regs);
442 spin_unlock_irqrestore(host->host_lock, flags);
443 return IRQ_HANDLED;
444}
445
446/*
447 * Queued command
448 */
449
450int qlogicfas408_queuecommand(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *))
451{
452 struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd);
453 if (cmd->device->id == 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
473int 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
497int 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
511int qlogicfas408_bus_reset(Scsi_Cmnd * cmd)
512{
513 struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd);
514 priv->qabort = 2;
515 ql_zap(priv);
516 return SUCCESS;
517}
518
519/*
520 * Reset SCSI host controller
521 */
522
523int qlogicfas408_host_reset(Scsi_Cmnd * cmd)
524{
525 return FAILED;
526}
527
528/*
529 * Reset SCSI device
530 */
531
532int qlogicfas408_device_reset(Scsi_Cmnd * cmd)
533{
534 return FAILED;
535}
536
537/*
538 * Return info string
539 */
540
541const char *qlogicfas408_info(struct Scsi_Host *host)
542{
543 struct qlogicfas408_priv *priv = get_priv_by_host(host);
544 return priv->qinfo;
545}
546
547/*
548 * Get type of chip
549 */
550
551int qlogicfas408_get_chip_type(int qbase, int int_type)
552{
553 REG1;
554 return inb(qbase + 0xe) & 0xf8;
555}
556
557/*
558 * Perform initialization tasks
559 */
560
561void qlogicfas408_setup(int qbase, int id, int int_type)
562{
563 outb(1, qbase + 8); /* set for PIO pseudo DMA */
564 REG0;
565 outb(0x40 | qlcfg8 | id, qbase + 8); /* (ini) bus id, disable scsi rst */
566 outb(qlcfg5, qbase + 5); /* select timer */
567 outb(qlcfg9, qbase + 9); /* prescaler */
568
569#if QL_RESET_AT_START
570 outb(3, qbase + 3);
571
572 REG1;
573 /* FIXME: timeout */
574 while (inb(qbase + 0xf) & 4)
575 cpu_relax();
576
577 REG0;
578#endif
579}
580
581/*
582 * Checks if this is a QLogic FAS 408
583 */
584
585int qlogicfas408_detect(int qbase, int int_type)
586{
587 REG1;
588 return (((inb(qbase + 0xe) ^ inb(qbase + 0xe)) == 7) &&
589 ((inb(qbase + 0xe) ^ inb(qbase + 0xe)) == 7));
590}
591
592/*
593 * Disable interrupts
594 */
595
596void qlogicfas408_disable_ints(struct qlogicfas408_priv *priv)
597{
598 int qbase = priv->qbase;
599 int int_type = priv->int_type;
600
601 REG1;
602 outb(0, qbase + 0xb); /* disable ints */
603}
604
605/*
606 * Init and exit functions
607 */
608
609static int __init qlogicfas408_init(void)
610{
611 return 0;
612}
613
614static void __exit qlogicfas408_exit(void)
615{
616
617}
618
619MODULE_AUTHOR("Tom Zerucha, Michael Griffith");
620MODULE_DESCRIPTION("Driver for the Qlogic FAS SCSI controllers");
621MODULE_LICENSE("GPL");
622module_init(qlogicfas408_init);
623module_exit(qlogicfas408_exit);
624
625EXPORT_SYMBOL(qlogicfas408_info);
626EXPORT_SYMBOL(qlogicfas408_queuecommand);
627EXPORT_SYMBOL(qlogicfas408_abort);
628EXPORT_SYMBOL(qlogicfas408_bus_reset);
629EXPORT_SYMBOL(qlogicfas408_device_reset);
630EXPORT_SYMBOL(qlogicfas408_host_reset);
631EXPORT_SYMBOL(qlogicfas408_biosparam);
632EXPORT_SYMBOL(qlogicfas408_ihandl);
633EXPORT_SYMBOL(qlogicfas408_get_chip_type);
634EXPORT_SYMBOL(qlogicfas408_setup);
635EXPORT_SYMBOL(qlogicfas408_detect);
636EXPORT_SYMBOL(qlogicfas408_disable_ints);
637