xref: /OK3568_Linux_fs/kernel/drivers/scsi/qlogicfas408.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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