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