blob: 30a88849a626bb531997d938a68401a576e418ce [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
Hannes Reinecke5751a9e2021-04-27 10:30:27 +02007 Copyright 1994, Tom Zerucha.
Linus Torvalds1da177e2005-04-16 15:20:36 -07008 tz@execpc.com
Hannes Reinecke5751a9e2021-04-27 10:30:27 +02009
Linus Torvalds1da177e2005-04-16 15:20:36 -070010 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.
Hannes Reinecke5751a9e2021-04-27 10:30:27 +020025
Alan Coxfa195af2008-10-27 15:16:36 +000026 Cleaned up 26/10/2002 by Alan Cox <alan@lxorguk.ukuu.org.uk> as part of the 2.5
Linus Torvalds1da177e2005-04-16 15:20:36 -070027 SCSI driver cleanup and audit. This driver still needs work on the
28 following
Hannes Reinecke5751a9e2021-04-27 10:30:27 +020029 - Non terminating hardware waits
30 - Some layering violations with its pcmcia stub
Linus Torvalds1da177e2005-04-16 15:20:36 -070031
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 */
Hannes Reinecke5751a9e2021-04-27 10:30:27 +020095
96static int ql_pdma(struct qlogicfas408_priv *priv, int phase, char *request,
97 int reqlen)
Linus Torvalds1da177e2005-04-16 15:20:36 -070098{
99 int j;
100 int qbase = priv->qbase;
101 j = 0;
102 if (phase & 1) { /* in */
103#if QL_TURBO_PDMA
104 rtrc(4)
105 /* empty fifo in large chunks */
106 if (reqlen >= 128 && (inb(qbase + 8) & 2)) { /* full */
107 insl(qbase + 4, request, 32);
108 reqlen -= 128;
109 request += 128;
110 }
111 while (reqlen >= 84 && !(j & 0xc0)) /* 2/3 */
Hannes Reinecke5751a9e2021-04-27 10:30:27 +0200112 if ((j = inb(qbase + 8)) & 4)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113 {
114 insl(qbase + 4, request, 21);
115 reqlen -= 84;
116 request += 84;
117 }
118 if (reqlen >= 44 && (inb(qbase + 8) & 8)) { /* 1/3 */
119 insl(qbase + 4, request, 11);
120 reqlen -= 44;
121 request += 44;
122 }
123#endif
124 /* until both empty and int (or until reclen is 0) */
125 rtrc(7)
126 j = 0;
Hannes Reinecke5751a9e2021-04-27 10:30:27 +0200127 while (reqlen && !((j & 0x10) && (j & 0xc0)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128 {
129 /* while bytes to receive and not empty */
130 j &= 0xc0;
Hannes Reinecke5751a9e2021-04-27 10:30:27 +0200131 while (reqlen && !((j = inb(qbase + 8)) & 0x10))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132 {
133 *request++ = inb(qbase + 4);
134 reqlen--;
135 }
136 if (j & 0x10)
137 j = inb(qbase + 8);
138
139 }
140 } else { /* out */
141#if QL_TURBO_PDMA
142 rtrc(4)
Colin Ian Kingd2e993e2019-01-23 13:10:04 +0000143 if (reqlen >= 128 && inb(qbase + 8) & 0x10) { /* empty */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144 outsl(qbase + 4, request, 32);
145 reqlen -= 128;
146 request += 128;
147 }
148 while (reqlen >= 84 && !(j & 0xc0)) /* 1/3 */
149 if (!((j = inb(qbase + 8)) & 8)) {
150 outsl(qbase + 4, request, 21);
151 reqlen -= 84;
152 request += 84;
153 }
154 if (reqlen >= 40 && !(inb(qbase + 8) & 4)) { /* 2/3 */
155 outsl(qbase + 4, request, 10);
156 reqlen -= 40;
157 request += 40;
158 }
159#endif
160 /* until full and int (or until reclen is 0) */
161 rtrc(7)
162 j = 0;
163 while (reqlen && !((j & 2) && (j & 0xc0))) {
164 /* while bytes to send and not full */
Hannes Reinecke5751a9e2021-04-27 10:30:27 +0200165 while (reqlen && !((j = inb(qbase + 8)) & 2))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700166 {
167 outb(*request++, qbase + 4);
168 reqlen--;
169 }
170 if (j & 2)
171 j = inb(qbase + 8);
172 }
173 }
174 /* maybe return reqlen */
175 return inb(qbase + 8) & 0xc0;
176}
177
178/*
Hannes Reinecke5751a9e2021-04-27 10:30:27 +0200179 * Wait for interrupt flag (polled - not real hardware interrupt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180 */
181
182static int ql_wai(struct qlogicfas408_priv *priv)
183{
184 int k;
185 int qbase = priv->qbase;
186 unsigned long i;
187
188 k = 0;
189 i = jiffies + WATCHDOG;
190 while (time_before(jiffies, i) && !priv->qabort &&
191 !((k = inb(qbase + 4)) & 0xe0)) {
192 barrier();
193 cpu_relax();
194 }
195 if (time_after_eq(jiffies, i))
196 return (DID_TIME_OUT);
197 if (priv->qabort)
198 return (priv->qabort == 1 ? DID_ABORT : DID_RESET);
199 if (k & 0x60)
200 ql_zap(priv);
201 if (k & 0x20)
202 return (DID_PARITY);
203 if (k & 0x40)
204 return (DID_ERROR);
205 return 0;
206}
207
208/*
Hannes Reinecke5751a9e2021-04-27 10:30:27 +0200209 * Initiate scsi command - queueing handler
Linus Torvalds1da177e2005-04-16 15:20:36 -0700210 * caller must hold host lock
211 */
212
Hennea24342b2006-10-03 21:31:14 +0200213static void ql_icmd(struct scsi_cmnd *cmd)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214{
215 struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd);
Hannes Reinecke5751a9e2021-04-27 10:30:27 +0200216 int qbase = priv->qbase;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217 int int_type = priv->int_type;
218 unsigned int i;
219
220 priv->qabort = 0;
221
222 REG0;
223 /* clearing of interrupts and the fifo is needed */
224
225 inb(qbase + 5); /* clear interrupts */
226 if (inb(qbase + 5)) /* if still interrupting */
227 outb(2, qbase + 3); /* reset chip */
228 else if (inb(qbase + 7) & 0x1f)
229 outb(1, qbase + 3); /* clear fifo */
230 while (inb(qbase + 5)); /* clear ints */
231 REG1;
232 outb(1, qbase + 8); /* set for PIO pseudo DMA */
233 outb(0, qbase + 0xb); /* disable ints */
234 inb(qbase + 8); /* clear int bits */
235 REG0;
236 outb(0x40, qbase + 0xb); /* enable features */
237
238 /* configurables */
239 outb(qlcfgc, qbase + 0xc);
240 /* config: no reset interrupt, (initiator) bus id */
241 outb(0x40 | qlcfg8 | priv->qinitid, qbase + 8);
242 outb(qlcfg7, qbase + 7);
243 outb(qlcfg6, qbase + 6);
Colin Ian Kingd2e993e2019-01-23 13:10:04 +0000244 outb(qlcfg5, qbase + 5); /* select timer */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245 outb(qlcfg9 & 7, qbase + 9); /* prescaler */
246/* outb(0x99, qbase + 5); */
Jeff Garzik422c0d62005-10-24 18:05:09 -0400247 outb(scmd_id(cmd), qbase + 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248
249 for (i = 0; i < cmd->cmd_len; i++)
250 outb(cmd->cmnd[i], qbase + 2);
251
252 priv->qlcmd = cmd;
253 outb(0x41, qbase + 3); /* select and send command */
254}
255
256/*
Hannes Reinecke5751a9e2021-04-27 10:30:27 +0200257 * Process scsi command - usually after interrupt
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258 */
259
Hannes Reinecke9a588d22021-04-27 10:30:26 +0200260static void ql_pcmd(struct scsi_cmnd *cmd)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261{
262 unsigned int i, j;
263 unsigned long k;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264 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) {
Hannes Reinecke9a588d22021-04-27 10:30:26 +0200277 set_host_byte(cmd, DID_NO_CONNECT);
278 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279 }
280 i |= inb(qbase + 5); /* the 0x10 bit can be set after the 0x08 */
281 if (i != 0x18) {
282 printk(KERN_ERR "Ql:Bad Interrupt status:%02x\n", i);
283 ql_zap(priv);
Hannes Reinecke9a588d22021-04-27 10:30:26 +0200284 set_host_byte(cmd, DID_BAD_INTR);
285 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286 }
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);
Hannes Reinecke9a588d22021-04-27 10:30:26 +0200298 set_host_byte(cmd, DID_ERROR);
299 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700300 }
Hannes Reinecke9a588d22021-04-27 10:30:26 +0200301
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302 if (inb(qbase + 7) & 0x1f) /* if some bytes in fifo */
303 outb(1, qbase + 3); /* clear fifo */
304 /* note that request_bufflen is the total xfer size when sg is used */
FUJITA Tomonoribc1ebfb2007-05-14 19:24:01 +0900305 reqlen = scsi_bufflen(cmd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306 /* note that it won't work if transfers > 16M are requested */
307 if (reqlen && !((phase = inb(qbase + 4)) & 6)) { /* data phase */
FUJITA Tomonoribc1ebfb2007-05-14 19:24:01 +0900308 struct scatterlist *sg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309 rtrc(2)
310 outb(reqlen, qbase); /* low-mid xfer cnt */
311 outb(reqlen >> 8, qbase + 1); /* low-mid xfer cnt */
312 outb(reqlen >> 16, qbase + 0xe); /* high xfer cnt */
313 outb(0x90, qbase + 3); /* command do xfer */
314 /* PIO pseudo DMA to buffer or sglist */
315 REG1;
FUJITA Tomonoribc1ebfb2007-05-14 19:24:01 +0900316
317 scsi_for_each_sg(cmd, sg, scsi_sg_count(cmd), i) {
318 if (priv->qabort) {
319 REG0;
Hannes Reinecke9a588d22021-04-27 10:30:26 +0200320 set_host_byte(cmd,
321 priv->qabort == 1 ?
322 DID_ABORT : DID_RESET);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323 }
Jens Axboe45711f12007-10-22 21:19:53 +0200324 buf = sg_virt(sg);
FUJITA Tomonoribc1ebfb2007-05-14 19:24:01 +0900325 if (ql_pdma(priv, phase, buf, sg->length))
326 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327 }
328 REG0;
Hannes Reinecke9a588d22021-04-27 10:30:26 +0200329 rtrc(2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330 /*
331 * Wait for irq (split into second state of irq handler
Hannes Reinecke5751a9e2021-04-27 10:30:27 +0200332 * if this can take time)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333 */
Hannes Reinecke9a588d22021-04-27 10:30:26 +0200334 if ((k = ql_wai(priv))) {
335 set_host_byte(cmd, k);
336 return;
337 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338 k = inb(qbase + 5); /* should be 0x10, bus service */
339 }
340
341 /*
Hannes Reinecke5751a9e2021-04-27 10:30:27 +0200342 * Enter Status (and Message In) Phase
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343 */
Hannes Reinecke5751a9e2021-04-27 10:30:27 +0200344
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345 k = jiffies + WATCHDOG;
346
347 while (time_before(jiffies, k) && !priv->qabort &&
348 !(inb(qbase + 4) & 6))
349 cpu_relax(); /* wait for status phase */
350
351 if (time_after_eq(jiffies, k)) {
352 ql_zap(priv);
Hannes Reinecke9a588d22021-04-27 10:30:26 +0200353 set_host_byte(cmd, DID_TIME_OUT);
354 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355 }
356
357 /* FIXME: timeout ?? */
358 while (inb(qbase + 5))
359 cpu_relax(); /* clear pending ints */
360
Hannes Reinecke9a588d22021-04-27 10:30:26 +0200361 if (priv->qabort) {
362 set_host_byte(cmd,
363 priv->qabort == 1 ? DID_ABORT : DID_RESET);
364 return;
365 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366
367 outb(0x11, qbase + 3); /* get status and message */
Hannes Reinecke9a588d22021-04-27 10:30:26 +0200368 if ((k = ql_wai(priv))) {
369 set_host_byte(cmd, k);
370 return;
371 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372 i = inb(qbase + 5); /* get chip irq stat */
373 j = inb(qbase + 7) & 0x1f; /* and bytes rec'd */
374 status = inb(qbase + 2);
375 message = inb(qbase + 2);
376
377 /*
Hannes Reinecke5751a9e2021-04-27 10:30:27 +0200378 * Should get function complete int if Status and message, else
379 * bus serv if only status
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380 */
381 if (!((i == 8 && j == 2) || (i == 0x10 && j == 1))) {
382 printk(KERN_ERR "Ql:Error during status phase, int=%02X, %d bytes recd\n", i, j);
Hannes Reinecke9a588d22021-04-27 10:30:26 +0200383 set_host_byte(cmd, DID_ERROR);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384 }
385 outb(0x12, qbase + 3); /* done, disconnect */
Hannes Reinecke9a588d22021-04-27 10:30:26 +0200386 rtrc(1);
387 if ((k = ql_wai(priv))) {
388 set_host_byte(cmd, k);
389 return;
390 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391
392 /*
Hannes Reinecke5751a9e2021-04-27 10:30:27 +0200393 * Should get bus service interrupt and disconnect interrupt
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394 */
Hannes Reinecke5751a9e2021-04-27 10:30:27 +0200395
Linus Torvalds1da177e2005-04-16 15:20:36 -0700396 i = inb(qbase + 5); /* should be bus service */
397 while (!priv->qabort && ((i & 0x20) != 0x20)) {
398 barrier();
399 cpu_relax();
400 i |= inb(qbase + 5);
401 }
Hannes Reinecke9a588d22021-04-27 10:30:26 +0200402 rtrc(0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700403
Hannes Reinecke9a588d22021-04-27 10:30:26 +0200404 if (priv->qabort) {
405 set_host_byte(cmd,
406 priv->qabort == 1 ? DID_ABORT : DID_RESET);
407 return;
408 }
409
410 set_host_byte(cmd, DID_OK);
411 if (message != COMMAND_COMPLETE)
412 scsi_msg_to_host_byte(cmd, message);
413 set_status_byte(cmd, status);
414 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415}
416
417/*
Hannes Reinecke5751a9e2021-04-27 10:30:27 +0200418 * Interrupt handler
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419 */
420
Jeff Garzikc7bec5a2006-10-06 15:00:58 -0400421static void ql_ihandl(void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422{
Hennea24342b2006-10-03 21:31:14 +0200423 struct scsi_cmnd *icmd;
Jeff Garzikc7bec5a2006-10-06 15:00:58 -0400424 struct Scsi_Host *host = dev_id;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425 struct qlogicfas408_priv *priv = get_priv_by_host(host);
426 int qbase = priv->qbase;
427 REG0;
428
429 if (!(inb(qbase + 4) & 0x80)) /* false alarm? */
430 return;
431
432 if (priv->qlcmd == NULL) { /* no command to process? */
433 int i;
434 i = 16;
435 while (i-- && inb(qbase + 5)); /* maybe also ql_zap() */
436 return;
437 }
438 icmd = priv->qlcmd;
Hannes Reinecke9a588d22021-04-27 10:30:26 +0200439 ql_pcmd(icmd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700440 priv->qlcmd = NULL;
441 /*
Hannes Reinecke5751a9e2021-04-27 10:30:27 +0200442 * If result is CHECK CONDITION done calls qcommand to request
443 * sense
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444 */
Bart Van Asscheda65bc02021-10-07 13:29:02 -0700445 scsi_done(icmd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446}
447
David Howells7d12e782006-10-05 14:55:46 +0100448irqreturn_t qlogicfas408_ihandl(int irq, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449{
450 unsigned long flags;
451 struct Scsi_Host *host = dev_id;
452
453 spin_lock_irqsave(host->host_lock, flags);
Jeff Garzikc7bec5a2006-10-06 15:00:58 -0400454 ql_ihandl(dev_id);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455 spin_unlock_irqrestore(host->host_lock, flags);
456 return IRQ_HANDLED;
457}
458
459/*
460 * Queued command
461 */
462
Bart Van Asscheaf049df2021-10-07 13:46:14 -0700463static int qlogicfas408_queuecommand_lck(struct scsi_cmnd *cmd)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700464{
Bart Van Asscheaf049df2021-10-07 13:46:14 -0700465 void (*done)(struct scsi_cmnd *) = scsi_done;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700466 struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd);
Hannes Reinecke9a588d22021-04-27 10:30:26 +0200467
468 set_host_byte(cmd, DID_OK);
469 set_status_byte(cmd, SAM_STAT_GOOD);
Jeff Garzik422c0d62005-10-24 18:05:09 -0400470 if (scmd_id(cmd) == priv->qinitid) {
Hannes Reinecke9a588d22021-04-27 10:30:26 +0200471 set_host_byte(cmd, DID_BAD_TARGET);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472 done(cmd);
473 return 0;
474 }
475
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476 /* wait for the last command's interrupt to finish */
477 while (priv->qlcmd != NULL) {
478 barrier();
479 cpu_relax();
480 }
481 ql_icmd(cmd);
482 return 0;
483}
484
Jeff Garzikf2812332010-11-16 02:10:29 -0500485DEF_SCSI_QCMD(qlogicfas408_queuecommand)
486
Hannes Reinecke5751a9e2021-04-27 10:30:27 +0200487/*
488 * Return bios parameters
Linus Torvalds1da177e2005-04-16 15:20:36 -0700489 */
490
Hennea24342b2006-10-03 21:31:14 +0200491int qlogicfas408_biosparam(struct scsi_device *disk, struct block_device *dev,
492 sector_t capacity, int ip[])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493{
494/* This should mimic the DOS Qlogic driver's behavior exactly */
495 ip[0] = 0x40;
496 ip[1] = 0x20;
497 ip[2] = (unsigned long) capacity / (ip[0] * ip[1]);
498 if (ip[2] > 1024) {
499 ip[0] = 0xff;
500 ip[1] = 0x3f;
501 ip[2] = (unsigned long) capacity / (ip[0] * ip[1]);
502#if 0
503 if (ip[2] > 1023)
504 ip[2] = 1023;
505#endif
506 }
507 return 0;
508}
509
510/*
511 * Abort a command in progress
512 */
Hannes Reinecke5751a9e2021-04-27 10:30:27 +0200513
Hennea24342b2006-10-03 21:31:14 +0200514int qlogicfas408_abort(struct scsi_cmnd *cmd)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515{
516 struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd);
517 priv->qabort = 1;
518 ql_zap(priv);
519 return SUCCESS;
520}
521
Hannes Reinecke4a56c1c2017-08-25 13:57:11 +0200522/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523 * Reset SCSI bus
524 * FIXME: This function is invoked with cmd = NULL directly by
525 * the PCMCIA qlogic_stub code. This wants fixing
526 */
527
Hannes Reinecke4a56c1c2017-08-25 13:57:11 +0200528int qlogicfas408_host_reset(struct scsi_cmnd *cmd)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700529{
530 struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd);
Jeff Garzik 68b3aa72005-05-28 07:56:31 -0400531 unsigned long flags;
532
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533 priv->qabort = 2;
Jeff Garzik 68b3aa72005-05-28 07:56:31 -0400534
535 spin_lock_irqsave(cmd->device->host->host_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536 ql_zap(priv);
Jeff Garzik 68b3aa72005-05-28 07:56:31 -0400537 spin_unlock_irqrestore(cmd->device->host->host_lock, flags);
538
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539 return SUCCESS;
540}
541
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542/*
543 * Return info string
544 */
545
546const char *qlogicfas408_info(struct Scsi_Host *host)
547{
548 struct qlogicfas408_priv *priv = get_priv_by_host(host);
549 return priv->qinfo;
550}
551
552/*
553 * Get type of chip
554 */
555
556int qlogicfas408_get_chip_type(int qbase, int int_type)
557{
558 REG1;
559 return inb(qbase + 0xe) & 0xf8;
560}
561
562/*
563 * Perform initialization tasks
564 */
565
566void qlogicfas408_setup(int qbase, int id, int int_type)
567{
568 outb(1, qbase + 8); /* set for PIO pseudo DMA */
569 REG0;
570 outb(0x40 | qlcfg8 | id, qbase + 8); /* (ini) bus id, disable scsi rst */
571 outb(qlcfg5, qbase + 5); /* select timer */
572 outb(qlcfg9, qbase + 9); /* prescaler */
573
574#if QL_RESET_AT_START
575 outb(3, qbase + 3);
576
577 REG1;
578 /* FIXME: timeout */
579 while (inb(qbase + 0xf) & 4)
580 cpu_relax();
581
582 REG0;
583#endif
584}
585
586/*
587 * Checks if this is a QLogic FAS 408
588 */
589
590int qlogicfas408_detect(int qbase, int int_type)
591{
Hannes Reinecke5751a9e2021-04-27 10:30:27 +0200592 REG1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593 return (((inb(qbase + 0xe) ^ inb(qbase + 0xe)) == 7) &&
Hannes Reinecke5751a9e2021-04-27 10:30:27 +0200594 ((inb(qbase + 0xe) ^ inb(qbase + 0xe)) == 7));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595}
596
597/*
598 * Disable interrupts
599 */
600
601void qlogicfas408_disable_ints(struct qlogicfas408_priv *priv)
602{
603 int qbase = priv->qbase;
604 int int_type = priv->int_type;
605
606 REG1;
607 outb(0, qbase + 0xb); /* disable ints */
608}
609
610/*
611 * Init and exit functions
612 */
613
614static int __init qlogicfas408_init(void)
615{
616 return 0;
617}
618
619static void __exit qlogicfas408_exit(void)
620{
621
622}
623
624MODULE_AUTHOR("Tom Zerucha, Michael Griffith");
625MODULE_DESCRIPTION("Driver for the Qlogic FAS SCSI controllers");
626MODULE_LICENSE("GPL");
627module_init(qlogicfas408_init);
628module_exit(qlogicfas408_exit);
629
630EXPORT_SYMBOL(qlogicfas408_info);
631EXPORT_SYMBOL(qlogicfas408_queuecommand);
632EXPORT_SYMBOL(qlogicfas408_abort);
Hannes Reinecke4a56c1c2017-08-25 13:57:11 +0200633EXPORT_SYMBOL(qlogicfas408_host_reset);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634EXPORT_SYMBOL(qlogicfas408_biosparam);
635EXPORT_SYMBOL(qlogicfas408_ihandl);
636EXPORT_SYMBOL(qlogicfas408_get_chip_type);
637EXPORT_SYMBOL(qlogicfas408_setup);
638EXPORT_SYMBOL(qlogicfas408_detect);
639EXPORT_SYMBOL(qlogicfas408_disable_ints);
640