11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*----------------------------------------------------------------*/
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   Qlogic linux driver - work in progress. No Warranty express or implied.
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   Use at your own risk.  Support Tort Reform so you won't have to read all
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   these silly disclaimers.
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   Copyright 1994, Tom Zerucha.
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   tz@execpc.com
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   Additional Code, and much appreciated help by
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   Michael A. Griffith
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   grif@cs.ucr.edu
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   Thanks to Eric Youngdale and Dave Hinds for loadable module and PCMCIA
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   help respectively, and for suffering through my foolishness during the
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   debugging process.
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   Reference Qlogic FAS408 Technical Manual, 53408-510-00A, May 10, 1994
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   (you can reference it, but it is incomplete and inaccurate in places)
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   Version 0.46 1/30/97 - kernel 1.2.0+
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   Functions as standalone, loadable, and PCMCIA driver, the latter from
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   Dave Hinds' PCMCIA package.
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
26fa195afe4ad3f6d85a9b7cc236ae85c05ca8db03Alan Cox   Cleaned up 26/10/2002 by Alan Cox <alan@lxorguk.ukuu.org.uk> as part of the 2.5
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   SCSI driver cleanup and audit. This driver still needs work on the
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   following
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   	-	Non terminating hardware waits
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   	-	Some layering violations with its pcmcia stub
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   Redistributable under terms of the GNU General Public License
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   For the avoidance of doubt the "preferred form" of this code is one which
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   is in an open non patent encumbered format. Where cryptographic key signing
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   forms part of the process of creating an executable the information
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   including keys needed to generate an equivalently functional executable
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   are deemed to be part of the source code.
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*/
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/blkdev.h>		/* to get disk capacity */
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h>
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/string.h>
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/interrupt.h>
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/ioport.h>
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/proc_fs.h>
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/unistd.h>
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/spinlock.h>
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/stat.h>
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/io.h>
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/irq.h>
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/dma.h>
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "scsi.h"
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <scsi/scsi_host.h>
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "qlogicfas408.h"
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*----------------------------------------------------------------*/
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int qlcfg5 = (XTALFREQ << 5);	/* 15625/512 */
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int qlcfg6 = SYNCXFRPD;
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int qlcfg7 = SYNCOFFST;
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int qlcfg8 = (SLOWCABLE << 7) | (QL_ENABLE_PARITY << 4);
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int qlcfg9 = ((XTALFREQ + 4) / 5);
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int qlcfgc = (FASTCLK << 3) | (FASTSCSI << 4);
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*----------------------------------------------------------------*/
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*----------------------------------------------------------------*/
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* local functions */
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*----------------------------------------------------------------*/
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* error recovery - reset everything */
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void ql_zap(struct qlogicfas408_priv *priv)
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int x;
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int qbase = priv->qbase;
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int int_type = priv->int_type;
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	x = inb(qbase + 0xd);
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	REG0;
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(3, qbase + 3);	/* reset SCSI */
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(2, qbase + 3);	/* reset chip */
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (x & 0x80)
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		REG1;
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Do a pseudo-dma tranfer
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int ql_pdma(struct qlogicfas408_priv *priv, int phase, char *request, int reqlen)
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int j;
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int qbase = priv->qbase;
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	j = 0;
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (phase & 1) {	/* in */
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if QL_TURBO_PDMA
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rtrc(4)
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* empty fifo in large chunks */
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (reqlen >= 128 && (inb(qbase + 8) & 2)) {	/* full */
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			insl(qbase + 4, request, 32);
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			reqlen -= 128;
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			request += 128;
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		while (reqlen >= 84 && !(j & 0xc0))	/* 2/3 */
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if ((j = inb(qbase + 8)) & 4)
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			{
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				insl(qbase + 4, request, 21);
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				reqlen -= 84;
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				request += 84;
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (reqlen >= 44 && (inb(qbase + 8) & 8)) {	/* 1/3 */
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			insl(qbase + 4, request, 11);
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			reqlen -= 44;
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			request += 44;
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* until both empty and int (or until reclen is 0) */
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rtrc(7)
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		j = 0;
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		while (reqlen && !((j & 0x10) && (j & 0xc0)))
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		{
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* while bytes to receive and not empty */
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			j &= 0xc0;
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			while (reqlen && !((j = inb(qbase + 8)) & 0x10))
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			{
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				*request++ = inb(qbase + 4);
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				reqlen--;
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (j & 0x10)
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				j = inb(qbase + 8);
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {		/* out */
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if QL_TURBO_PDMA
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rtrc(4)
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    if (reqlen >= 128 && inb(qbase + 8) & 0x10) {	/* empty */
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			outsl(qbase + 4, request, 32);
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			reqlen -= 128;
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			request += 128;
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		while (reqlen >= 84 && !(j & 0xc0))	/* 1/3 */
1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (!((j = inb(qbase + 8)) & 8)) {
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				outsl(qbase + 4, request, 21);
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				reqlen -= 84;
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				request += 84;
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (reqlen >= 40 && !(inb(qbase + 8) & 4)) {	/* 2/3 */
1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			outsl(qbase + 4, request, 10);
1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			reqlen -= 40;
1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			request += 40;
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* until full and int (or until reclen is 0) */
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rtrc(7)
1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    j = 0;
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		while (reqlen && !((j & 2) && (j & 0xc0))) {
1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* while bytes to send and not full */
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			while (reqlen && !((j = inb(qbase + 8)) & 2))
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			{
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				outb(*request++, qbase + 4);
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				reqlen--;
1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (j & 2)
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				j = inb(qbase + 8);
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* maybe return reqlen */
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return inb(qbase + 8) & 0xc0;
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Wait for interrupt flag (polled - not real hardware interrupt)
1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int ql_wai(struct qlogicfas408_priv *priv)
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int k;
1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int qbase = priv->qbase;
1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long i;
1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	k = 0;
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	i = jiffies + WATCHDOG;
1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (time_before(jiffies, i) && !priv->qabort &&
1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					!((k = inb(qbase + 4)) & 0xe0)) {
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		barrier();
1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		cpu_relax();
1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (time_after_eq(jiffies, i))
1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return (DID_TIME_OUT);
1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (priv->qabort)
1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return (priv->qabort == 1 ? DID_ABORT : DID_RESET);
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (k & 0x60)
1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ql_zap(priv);
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (k & 0x20)
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return (DID_PARITY);
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (k & 0x40)
2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return (DID_ERROR);
2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Initiate scsi command - queueing handler
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	caller must hold host lock
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
212a24342b90c9c829fc5fea9ee01b127f81bca18efHenrik Kretzschmarstatic void ql_icmd(struct scsi_cmnd *cmd)
2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd);
2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int 	qbase = priv->qbase;
2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int	int_type = priv->int_type;
2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int i;
2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	priv->qabort = 0;
2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	REG0;
2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* clearing of interrupts and the fifo is needed */
2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	inb(qbase + 5);		/* clear interrupts */
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (inb(qbase + 5))	/* if still interrupting */
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		outb(2, qbase + 3);	/* reset chip */
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else if (inb(qbase + 7) & 0x1f)
2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		outb(1, qbase + 3);	/* clear fifo */
2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (inb(qbase + 5));	/* clear ints */
2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	REG1;
2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(1, qbase + 8);	/* set for PIO pseudo DMA */
2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(0, qbase + 0xb);	/* disable ints */
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	inb(qbase + 8);		/* clear int bits */
2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	REG0;
2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(0x40, qbase + 0xb);	/* enable features */
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* configurables */
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(qlcfgc, qbase + 0xc);
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* config: no reset interrupt, (initiator) bus id */
2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(0x40 | qlcfg8 | priv->qinitid, qbase + 8);
2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(qlcfg7, qbase + 7);
2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(qlcfg6, qbase + 6);
2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 /**/ outb(qlcfg5, qbase + 5);	/* select timer */
2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(qlcfg9 & 7, qbase + 9);	/* prescaler */
2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*	outb(0x99, qbase + 5);	*/
246422c0d61d591cbfb70f029e13505fb437e169d68Jeff Garzik	outb(scmd_id(cmd), qbase + 4);
2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < cmd->cmd_len; i++)
2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		outb(cmd->cmnd[i], qbase + 2);
2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	priv->qlcmd = cmd;
2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(0x41, qbase + 3);	/* select and send command */
2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Process scsi command - usually after interrupt
2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
259a24342b90c9c829fc5fea9ee01b127f81bca18efHenrik Kretzschmarstatic unsigned int ql_pcmd(struct scsi_cmnd *cmd)
2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int i, j;
2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long k;
2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int result;	/* ultimate return result */
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int status;	/* scsi returned status */
2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int message;	/* scsi returned message */
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int phase;	/* recorded scsi phase */
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int reqlen;	/* total length of transfer */
2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char *buf;
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd);
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int qbase = priv->qbase;
2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int int_type = priv->int_type;
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rtrc(1)
2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	j = inb(qbase + 6);
2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	i = inb(qbase + 5);
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (i == 0x20) {
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return (DID_NO_CONNECT << 16);
2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	i |= inb(qbase + 5);	/* the 0x10 bit can be set after the 0x08 */
2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (i != 0x18) {
2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_ERR "Ql:Bad Interrupt status:%02x\n", i);
2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ql_zap(priv);
2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return (DID_BAD_INTR << 16);
2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	j &= 7;			/* j = inb( qbase + 7 ) >> 5; */
2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* correct status is supposed to be step 4 */
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* it sometimes returns step 3 but with 0 bytes left to send */
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* We can try stuffing the FIFO with the max each time, but we will get a
2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   sequence of 3 if any bytes are left (but we do flush the FIFO anyway */
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (j != 3 && j != 4) {
2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_ERR "Ql:Bad sequence for command %d, int %02X, cmdleft = %d\n",
2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		     j, i, inb(qbase + 7) & 0x1f);
2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ql_zap(priv);
2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return (DID_ERROR << 16);
2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	result = DID_OK;
2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (inb(qbase + 7) & 0x1f)	/* if some bytes in fifo */
3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		outb(1, qbase + 3);	/* clear fifo */
3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* note that request_bufflen is the total xfer size when sg is used */
302bc1ebfba1a3a27122462fd342d11216e3faea53fFUJITA Tomonori	reqlen = scsi_bufflen(cmd);
3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* note that it won't work if transfers > 16M are requested */
3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (reqlen && !((phase = inb(qbase + 4)) & 6)) {	/* data phase */
305bc1ebfba1a3a27122462fd342d11216e3faea53fFUJITA Tomonori		struct scatterlist *sg;
3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rtrc(2)
3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		outb(reqlen, qbase);	/* low-mid xfer cnt */
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		outb(reqlen >> 8, qbase + 1);	/* low-mid xfer cnt */
3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		outb(reqlen >> 16, qbase + 0xe);	/* high xfer cnt */
3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		outb(0x90, qbase + 3);	/* command do xfer */
3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* PIO pseudo DMA to buffer or sglist */
3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		REG1;
313bc1ebfba1a3a27122462fd342d11216e3faea53fFUJITA Tomonori
314bc1ebfba1a3a27122462fd342d11216e3faea53fFUJITA Tomonori		scsi_for_each_sg(cmd, sg, scsi_sg_count(cmd), i) {
315bc1ebfba1a3a27122462fd342d11216e3faea53fFUJITA Tomonori			if (priv->qabort) {
316bc1ebfba1a3a27122462fd342d11216e3faea53fFUJITA Tomonori				REG0;
317bc1ebfba1a3a27122462fd342d11216e3faea53fFUJITA Tomonori				return ((priv->qabort == 1 ?
318bc1ebfba1a3a27122462fd342d11216e3faea53fFUJITA Tomonori					 DID_ABORT : DID_RESET) << 16);
3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
32045711f1af6eff1a6d010703b4862e0d2b9afd056Jens Axboe			buf = sg_virt(sg);
321bc1ebfba1a3a27122462fd342d11216e3faea53fFUJITA Tomonori			if (ql_pdma(priv, phase, buf, sg->length))
322bc1ebfba1a3a27122462fd342d11216e3faea53fFUJITA Tomonori				break;
3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		REG0;
3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rtrc(2)
3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 *	Wait for irq (split into second state of irq handler
3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 *	if this can take time)
3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if ((k = ql_wai(priv)))
3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return (k << 16);
3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		k = inb(qbase + 5);	/* should be 0x10, bus service */
3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 *	Enter Status (and Message In) Phase
3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	k = jiffies + WATCHDOG;
3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (time_before(jiffies, k) && !priv->qabort &&
3421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						!(inb(qbase + 4) & 6))
3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		cpu_relax();	/* wait for status phase */
3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (time_after_eq(jiffies, k)) {
3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ql_zap(priv);
3471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return (DID_TIME_OUT << 16);
3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* FIXME: timeout ?? */
3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (inb(qbase + 5))
3521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		cpu_relax();	/* clear pending ints */
3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (priv->qabort)
3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return ((priv->qabort == 1 ? DID_ABORT : DID_RESET) << 16);
3561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(0x11, qbase + 3);	/* get status and message */
3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((k = ql_wai(priv)))
3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return (k << 16);
3601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	i = inb(qbase + 5);	/* get chip irq stat */
3611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	j = inb(qbase + 7) & 0x1f;	/* and bytes rec'd */
3621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	status = inb(qbase + 2);
3631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	message = inb(qbase + 2);
3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 *	Should get function complete int if Status and message, else
3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 *	bus serv if only status
3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!((i == 8 && j == 2) || (i == 0x10 && j == 1))) {
3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_ERR "Ql:Error during status phase, int=%02X, %d bytes recd\n", i, j);
3711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		result = DID_ERROR;
3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(0x12, qbase + 3);	/* done, disconnect */
3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rtrc(1)
3751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((k = ql_wai(priv)))
3761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return (k << 16);
3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
3791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 *	Should get bus service interrupt and disconnect interrupt
3801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	i = inb(qbase + 5);	/* should be bus service */
3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (!priv->qabort && ((i & 0x20) != 0x20)) {
3841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		barrier();
3851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		cpu_relax();
3861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		i |= inb(qbase + 5);
3871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rtrc(0)
3891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (priv->qabort)
3911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return ((priv->qabort == 1 ? DID_ABORT : DID_RESET) << 16);
3921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return (result << 16) | (message << 8) | (status & STATUS_MASK);
3941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
3971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Interrupt handler
3981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
3991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
400c7bec5aba52392aa8d675b8722735caf4a8b7265Jeff Garzikstatic void ql_ihandl(void *dev_id)
4011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
402a24342b90c9c829fc5fea9ee01b127f81bca18efHenrik Kretzschmar	struct scsi_cmnd *icmd;
403c7bec5aba52392aa8d675b8722735caf4a8b7265Jeff Garzik	struct Scsi_Host *host = dev_id;
4041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct qlogicfas408_priv *priv = get_priv_by_host(host);
4051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int qbase = priv->qbase;
4061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	REG0;
4071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!(inb(qbase + 4) & 0x80))	/* false alarm? */
4091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
4101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (priv->qlcmd == NULL) {	/* no command to process? */
4121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		int i;
4131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		i = 16;
4141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		while (i-- && inb(qbase + 5));	/* maybe also ql_zap() */
4151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
4161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	icmd = priv->qlcmd;
4181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	icmd->result = ql_pcmd(icmd);
4191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	priv->qlcmd = NULL;
4201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
4211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 *	If result is CHECK CONDITION done calls qcommand to request
4221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 *	sense
4231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
4241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	(icmd->scsi_done) (icmd);
4251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4277d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsirqreturn_t qlogicfas408_ihandl(int irq, void *dev_id)
4281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long flags;
4301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct Scsi_Host *host = dev_id;
4311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_irqsave(host->host_lock, flags);
433c7bec5aba52392aa8d675b8722735caf4a8b7265Jeff Garzik	ql_ihandl(dev_id);
4341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_irqrestore(host->host_lock, flags);
4351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return IRQ_HANDLED;
4361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
4391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Queued command
4401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
4411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
442f281233d3eba15fb225d21ae2e228fd4553d824aJeff Garzikstatic int qlogicfas408_queuecommand_lck(struct scsi_cmnd *cmd,
443a24342b90c9c829fc5fea9ee01b127f81bca18efHenrik Kretzschmar			      void (*done) (struct scsi_cmnd *))
4441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd);
446422c0d61d591cbfb70f029e13505fb437e169d68Jeff Garzik	if (scmd_id(cmd) == priv->qinitid) {
4471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		cmd->result = DID_BAD_TARGET << 16;
4481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		done(cmd);
4491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
4501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cmd->scsi_done = done;
4531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* wait for the last command's interrupt to finish */
4541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (priv->qlcmd != NULL) {
4551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		barrier();
4561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		cpu_relax();
4571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ql_icmd(cmd);
4591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
4601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
462f281233d3eba15fb225d21ae2e228fd4553d824aJeff GarzikDEF_SCSI_QCMD(qlogicfas408_queuecommand)
463f281233d3eba15fb225d21ae2e228fd4553d824aJeff Garzik
4641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
4651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Return bios parameters
4661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
4671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
468a24342b90c9c829fc5fea9ee01b127f81bca18efHenrik Kretzschmarint qlogicfas408_biosparam(struct scsi_device *disk, struct block_device *dev,
469a24342b90c9c829fc5fea9ee01b127f81bca18efHenrik Kretzschmar			   sector_t capacity, int ip[])
4701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* This should mimic the DOS Qlogic driver's behavior exactly */
4721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ip[0] = 0x40;
4731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ip[1] = 0x20;
4741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ip[2] = (unsigned long) capacity / (ip[0] * ip[1]);
4751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ip[2] > 1024) {
4761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ip[0] = 0xff;
4771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ip[1] = 0x3f;
4781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ip[2] = (unsigned long) capacity / (ip[0] * ip[1]);
4791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if 0
4801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (ip[2] > 1023)
4811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ip[2] = 1023;
4821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
4831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
4851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
4881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Abort a command in progress
4891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
4901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
491a24342b90c9c829fc5fea9ee01b127f81bca18efHenrik Kretzschmarint qlogicfas408_abort(struct scsi_cmnd *cmd)
4921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd);
4941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	priv->qabort = 1;
4951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ql_zap(priv);
4961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return SUCCESS;
4971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
5001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Reset SCSI bus
5011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	FIXME: This function is invoked with cmd = NULL directly by
5021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	the PCMCIA qlogic_stub code. This wants fixing
5031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
5041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
505a24342b90c9c829fc5fea9ee01b127f81bca18efHenrik Kretzschmarint qlogicfas408_bus_reset(struct scsi_cmnd *cmd)
5061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd);
50868b3aa7c9805aee9005a8ca53c5e99177961fbb9Jeff Garzik	unsigned long flags;
50968b3aa7c9805aee9005a8ca53c5e99177961fbb9Jeff Garzik
5101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	priv->qabort = 2;
51168b3aa7c9805aee9005a8ca53c5e99177961fbb9Jeff Garzik
51268b3aa7c9805aee9005a8ca53c5e99177961fbb9Jeff Garzik	spin_lock_irqsave(cmd->device->host->host_lock, flags);
5131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ql_zap(priv);
51468b3aa7c9805aee9005a8ca53c5e99177961fbb9Jeff Garzik	spin_unlock_irqrestore(cmd->device->host->host_lock, flags);
51568b3aa7c9805aee9005a8ca53c5e99177961fbb9Jeff Garzik
5161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return SUCCESS;
5171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
5201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Return info string
5211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
5221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsconst char *qlogicfas408_info(struct Scsi_Host *host)
5241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct qlogicfas408_priv *priv = get_priv_by_host(host);
5261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return priv->qinfo;
5271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
5301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Get type of chip
5311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
5321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint qlogicfas408_get_chip_type(int qbase, int int_type)
5341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	REG1;
5361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return inb(qbase + 0xe) & 0xf8;
5371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
5401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Perform initialization tasks
5411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
5421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid qlogicfas408_setup(int qbase, int id, int int_type)
5441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(1, qbase + 8);	/* set for PIO pseudo DMA */
5461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	REG0;
5471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(0x40 | qlcfg8 | id, qbase + 8);	/* (ini) bus id, disable scsi rst */
5481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(qlcfg5, qbase + 5);	/* select timer */
5491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(qlcfg9, qbase + 9);	/* prescaler */
5501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if QL_RESET_AT_START
5521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(3, qbase + 3);
5531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	REG1;
5551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* FIXME: timeout */
5561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (inb(qbase + 0xf) & 4)
5571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		cpu_relax();
5581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	REG0;
5601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
5611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
5641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Checks if this is a QLogic FAS 408
5651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
5661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint qlogicfas408_detect(int qbase, int int_type)
5681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        REG1;
5701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return (((inb(qbase + 0xe) ^ inb(qbase + 0xe)) == 7) &&
5711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       ((inb(qbase + 0xe) ^ inb(qbase + 0xe)) == 7));
5721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
5751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Disable interrupts
5761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
5771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid qlogicfas408_disable_ints(struct qlogicfas408_priv *priv)
5791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int qbase = priv->qbase;
5811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int int_type = priv->int_type;
5821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	REG1;
5841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(0, qbase + 0xb);	/* disable ints */
5851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
5881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Init and exit functions
5891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
5901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init qlogicfas408_init(void)
5921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
5941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit qlogicfas408_exit(void)
5971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR("Tom Zerucha, Michael Griffith");
6021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION("Driver for the Qlogic FAS SCSI controllers");
6031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
6041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(qlogicfas408_init);
6051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(qlogicfas408_exit);
6061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(qlogicfas408_info);
6081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(qlogicfas408_queuecommand);
6091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(qlogicfas408_abort);
6101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(qlogicfas408_bus_reset);
6111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(qlogicfas408_biosparam);
6121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(qlogicfas408_ihandl);
6131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(qlogicfas408_get_chip_type);
6141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(qlogicfas408_setup);
6151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(qlogicfas408_detect);
6161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(qlogicfas408_disable_ints);
6171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
618