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