diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /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.c | 637 |
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 | /*----------------------------------------------------------------*/ | ||
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(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 | |||
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, 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 | |||
435 | irqreturn_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 | |||
450 | int 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 | |||
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 | priv->qabort = 2; | ||
515 | ql_zap(priv); | ||
516 | return SUCCESS; | ||
517 | } | ||
518 | |||
519 | /* | ||
520 | * Reset SCSI host controller | ||
521 | */ | ||
522 | |||
523 | int qlogicfas408_host_reset(Scsi_Cmnd * cmd) | ||
524 | { | ||
525 | return FAILED; | ||
526 | } | ||
527 | |||
528 | /* | ||
529 | * Reset SCSI device | ||
530 | */ | ||
531 | |||
532 | int qlogicfas408_device_reset(Scsi_Cmnd * cmd) | ||
533 | { | ||
534 | return FAILED; | ||
535 | } | ||
536 | |||
537 | /* | ||
538 | * Return info string | ||
539 | */ | ||
540 | |||
541 | const 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 | |||
551 | int 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 | |||
561 | void 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 | |||
585 | int 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 | |||
596 | void 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 | |||
609 | static int __init qlogicfas408_init(void) | ||
610 | { | ||
611 | return 0; | ||
612 | } | ||
613 | |||
614 | static void __exit qlogicfas408_exit(void) | ||
615 | { | ||
616 | |||
617 | } | ||
618 | |||
619 | MODULE_AUTHOR("Tom Zerucha, Michael Griffith"); | ||
620 | MODULE_DESCRIPTION("Driver for the Qlogic FAS SCSI controllers"); | ||
621 | MODULE_LICENSE("GPL"); | ||
622 | module_init(qlogicfas408_init); | ||
623 | module_exit(qlogicfas408_exit); | ||
624 | |||
625 | EXPORT_SYMBOL(qlogicfas408_info); | ||
626 | EXPORT_SYMBOL(qlogicfas408_queuecommand); | ||
627 | EXPORT_SYMBOL(qlogicfas408_abort); | ||
628 | EXPORT_SYMBOL(qlogicfas408_bus_reset); | ||
629 | EXPORT_SYMBOL(qlogicfas408_device_reset); | ||
630 | EXPORT_SYMBOL(qlogicfas408_host_reset); | ||
631 | EXPORT_SYMBOL(qlogicfas408_biosparam); | ||
632 | EXPORT_SYMBOL(qlogicfas408_ihandl); | ||
633 | EXPORT_SYMBOL(qlogicfas408_get_chip_type); | ||
634 | EXPORT_SYMBOL(qlogicfas408_setup); | ||
635 | EXPORT_SYMBOL(qlogicfas408_detect); | ||
636 | EXPORT_SYMBOL(qlogicfas408_disable_ints); | ||
637 | |||