|  | /*----------------------------------------------------------------*/ | 
|  | /* | 
|  | Qlogic linux driver - work in progress. No Warranty express or implied. | 
|  | Use at your own risk.  Support Tort Reform so you won't have to read all | 
|  | these silly disclaimers. | 
|  |  | 
|  | Copyright 1994, Tom Zerucha. | 
|  | tz@execpc.com | 
|  |  | 
|  | Additional Code, and much appreciated help by | 
|  | Michael A. Griffith | 
|  | grif@cs.ucr.edu | 
|  |  | 
|  | Thanks to Eric Youngdale and Dave Hinds for loadable module and PCMCIA | 
|  | help respectively, and for suffering through my foolishness during the | 
|  | debugging process. | 
|  |  | 
|  | Reference Qlogic FAS408 Technical Manual, 53408-510-00A, May 10, 1994 | 
|  | (you can reference it, but it is incomplete and inaccurate in places) | 
|  |  | 
|  | Version 0.46 1/30/97 - kernel 1.2.0+ | 
|  |  | 
|  | Functions as standalone, loadable, and PCMCIA driver, the latter from | 
|  | Dave Hinds' PCMCIA package. | 
|  |  | 
|  | Cleaned up 26/10/2002 by Alan Cox <alan@lxorguk.ukuu.org.uk> as part of the 2.5 | 
|  | SCSI driver cleanup and audit. This driver still needs work on the | 
|  | following | 
|  | -	Non terminating hardware waits | 
|  | -	Some layering violations with its pcmcia stub | 
|  |  | 
|  | Redistributable under terms of the GNU General Public License | 
|  |  | 
|  | For the avoidance of doubt the "preferred form" of this code is one which | 
|  | is in an open non patent encumbered format. Where cryptographic key signing | 
|  | forms part of the process of creating an executable the information | 
|  | including keys needed to generate an equivalently functional executable | 
|  | are deemed to be part of the source code. | 
|  |  | 
|  | */ | 
|  |  | 
|  | #include <linux/module.h> | 
|  | #include <linux/blkdev.h>		/* to get disk capacity */ | 
|  | #include <linux/kernel.h> | 
|  | #include <linux/string.h> | 
|  | #include <linux/init.h> | 
|  | #include <linux/interrupt.h> | 
|  | #include <linux/ioport.h> | 
|  | #include <linux/proc_fs.h> | 
|  | #include <linux/unistd.h> | 
|  | #include <linux/spinlock.h> | 
|  | #include <linux/stat.h> | 
|  |  | 
|  | #include <asm/io.h> | 
|  | #include <asm/irq.h> | 
|  | #include <asm/dma.h> | 
|  |  | 
|  | #include "scsi.h" | 
|  | #include <scsi/scsi_host.h> | 
|  | #include "qlogicfas408.h" | 
|  |  | 
|  | /*----------------------------------------------------------------*/ | 
|  | static int qlcfg5 = (XTALFREQ << 5);	/* 15625/512 */ | 
|  | static int qlcfg6 = SYNCXFRPD; | 
|  | static int qlcfg7 = SYNCOFFST; | 
|  | static int qlcfg8 = (SLOWCABLE << 7) | (QL_ENABLE_PARITY << 4); | 
|  | static int qlcfg9 = ((XTALFREQ + 4) / 5); | 
|  | static int qlcfgc = (FASTCLK << 3) | (FASTSCSI << 4); | 
|  |  | 
|  | /*----------------------------------------------------------------*/ | 
|  |  | 
|  | /*----------------------------------------------------------------*/ | 
|  | /* local functions */ | 
|  | /*----------------------------------------------------------------*/ | 
|  |  | 
|  | /* error recovery - reset everything */ | 
|  |  | 
|  | static void ql_zap(struct qlogicfas408_priv *priv) | 
|  | { | 
|  | int x; | 
|  | int qbase = priv->qbase; | 
|  | int int_type = priv->int_type; | 
|  |  | 
|  | x = inb(qbase + 0xd); | 
|  | REG0; | 
|  | outb(3, qbase + 3);	/* reset SCSI */ | 
|  | outb(2, qbase + 3);	/* reset chip */ | 
|  | if (x & 0x80) | 
|  | REG1; | 
|  | } | 
|  |  | 
|  | /* | 
|  | *	Do a pseudo-dma tranfer | 
|  | */ | 
|  |  | 
|  | static int ql_pdma(struct qlogicfas408_priv *priv, int phase, char *request, int reqlen) | 
|  | { | 
|  | int j; | 
|  | int qbase = priv->qbase; | 
|  | j = 0; | 
|  | if (phase & 1) {	/* in */ | 
|  | #if QL_TURBO_PDMA | 
|  | rtrc(4) | 
|  | /* empty fifo in large chunks */ | 
|  | if (reqlen >= 128 && (inb(qbase + 8) & 2)) {	/* full */ | 
|  | insl(qbase + 4, request, 32); | 
|  | reqlen -= 128; | 
|  | request += 128; | 
|  | } | 
|  | while (reqlen >= 84 && !(j & 0xc0))	/* 2/3 */ | 
|  | if ((j = inb(qbase + 8)) & 4) | 
|  | { | 
|  | insl(qbase + 4, request, 21); | 
|  | reqlen -= 84; | 
|  | request += 84; | 
|  | } | 
|  | if (reqlen >= 44 && (inb(qbase + 8) & 8)) {	/* 1/3 */ | 
|  | insl(qbase + 4, request, 11); | 
|  | reqlen -= 44; | 
|  | request += 44; | 
|  | } | 
|  | #endif | 
|  | /* until both empty and int (or until reclen is 0) */ | 
|  | rtrc(7) | 
|  | j = 0; | 
|  | while (reqlen && !((j & 0x10) && (j & 0xc0))) | 
|  | { | 
|  | /* while bytes to receive and not empty */ | 
|  | j &= 0xc0; | 
|  | while (reqlen && !((j = inb(qbase + 8)) & 0x10)) | 
|  | { | 
|  | *request++ = inb(qbase + 4); | 
|  | reqlen--; | 
|  | } | 
|  | if (j & 0x10) | 
|  | j = inb(qbase + 8); | 
|  |  | 
|  | } | 
|  | } else {		/* out */ | 
|  | #if QL_TURBO_PDMA | 
|  | rtrc(4) | 
|  | if (reqlen >= 128 && inb(qbase + 8) & 0x10) {	/* empty */ | 
|  | outsl(qbase + 4, request, 32); | 
|  | reqlen -= 128; | 
|  | request += 128; | 
|  | } | 
|  | while (reqlen >= 84 && !(j & 0xc0))	/* 1/3 */ | 
|  | if (!((j = inb(qbase + 8)) & 8)) { | 
|  | outsl(qbase + 4, request, 21); | 
|  | reqlen -= 84; | 
|  | request += 84; | 
|  | } | 
|  | if (reqlen >= 40 && !(inb(qbase + 8) & 4)) {	/* 2/3 */ | 
|  | outsl(qbase + 4, request, 10); | 
|  | reqlen -= 40; | 
|  | request += 40; | 
|  | } | 
|  | #endif | 
|  | /* until full and int (or until reclen is 0) */ | 
|  | rtrc(7) | 
|  | j = 0; | 
|  | while (reqlen && !((j & 2) && (j & 0xc0))) { | 
|  | /* while bytes to send and not full */ | 
|  | while (reqlen && !((j = inb(qbase + 8)) & 2)) | 
|  | { | 
|  | outb(*request++, qbase + 4); | 
|  | reqlen--; | 
|  | } | 
|  | if (j & 2) | 
|  | j = inb(qbase + 8); | 
|  | } | 
|  | } | 
|  | /* maybe return reqlen */ | 
|  | return inb(qbase + 8) & 0xc0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | *	Wait for interrupt flag (polled - not real hardware interrupt) | 
|  | */ | 
|  |  | 
|  | static int ql_wai(struct qlogicfas408_priv *priv) | 
|  | { | 
|  | int k; | 
|  | int qbase = priv->qbase; | 
|  | unsigned long i; | 
|  |  | 
|  | k = 0; | 
|  | i = jiffies + WATCHDOG; | 
|  | while (time_before(jiffies, i) && !priv->qabort && | 
|  | !((k = inb(qbase + 4)) & 0xe0)) { | 
|  | barrier(); | 
|  | cpu_relax(); | 
|  | } | 
|  | if (time_after_eq(jiffies, i)) | 
|  | return (DID_TIME_OUT); | 
|  | if (priv->qabort) | 
|  | return (priv->qabort == 1 ? DID_ABORT : DID_RESET); | 
|  | if (k & 0x60) | 
|  | ql_zap(priv); | 
|  | if (k & 0x20) | 
|  | return (DID_PARITY); | 
|  | if (k & 0x40) | 
|  | return (DID_ERROR); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | *	Initiate scsi command - queueing handler | 
|  | *	caller must hold host lock | 
|  | */ | 
|  |  | 
|  | static void ql_icmd(struct scsi_cmnd *cmd) | 
|  | { | 
|  | struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd); | 
|  | int 	qbase = priv->qbase; | 
|  | int	int_type = priv->int_type; | 
|  | unsigned int i; | 
|  |  | 
|  | priv->qabort = 0; | 
|  |  | 
|  | REG0; | 
|  | /* clearing of interrupts and the fifo is needed */ | 
|  |  | 
|  | inb(qbase + 5);		/* clear interrupts */ | 
|  | if (inb(qbase + 5))	/* if still interrupting */ | 
|  | outb(2, qbase + 3);	/* reset chip */ | 
|  | else if (inb(qbase + 7) & 0x1f) | 
|  | outb(1, qbase + 3);	/* clear fifo */ | 
|  | while (inb(qbase + 5));	/* clear ints */ | 
|  | REG1; | 
|  | outb(1, qbase + 8);	/* set for PIO pseudo DMA */ | 
|  | outb(0, qbase + 0xb);	/* disable ints */ | 
|  | inb(qbase + 8);		/* clear int bits */ | 
|  | REG0; | 
|  | outb(0x40, qbase + 0xb);	/* enable features */ | 
|  |  | 
|  | /* configurables */ | 
|  | outb(qlcfgc, qbase + 0xc); | 
|  | /* config: no reset interrupt, (initiator) bus id */ | 
|  | outb(0x40 | qlcfg8 | priv->qinitid, qbase + 8); | 
|  | outb(qlcfg7, qbase + 7); | 
|  | outb(qlcfg6, qbase + 6); | 
|  | /**/ outb(qlcfg5, qbase + 5);	/* select timer */ | 
|  | outb(qlcfg9 & 7, qbase + 9);	/* prescaler */ | 
|  | /*	outb(0x99, qbase + 5);	*/ | 
|  | outb(scmd_id(cmd), qbase + 4); | 
|  |  | 
|  | for (i = 0; i < cmd->cmd_len; i++) | 
|  | outb(cmd->cmnd[i], qbase + 2); | 
|  |  | 
|  | priv->qlcmd = cmd; | 
|  | outb(0x41, qbase + 3);	/* select and send command */ | 
|  | } | 
|  |  | 
|  | /* | 
|  | *	Process scsi command - usually after interrupt | 
|  | */ | 
|  |  | 
|  | static unsigned int ql_pcmd(struct scsi_cmnd *cmd) | 
|  | { | 
|  | unsigned int i, j; | 
|  | unsigned long k; | 
|  | unsigned int result;	/* ultimate return result */ | 
|  | unsigned int status;	/* scsi returned status */ | 
|  | unsigned int message;	/* scsi returned message */ | 
|  | unsigned int phase;	/* recorded scsi phase */ | 
|  | unsigned int reqlen;	/* total length of transfer */ | 
|  | char *buf; | 
|  | struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd); | 
|  | int qbase = priv->qbase; | 
|  | int int_type = priv->int_type; | 
|  |  | 
|  | rtrc(1) | 
|  | j = inb(qbase + 6); | 
|  | i = inb(qbase + 5); | 
|  | if (i == 0x20) { | 
|  | return (DID_NO_CONNECT << 16); | 
|  | } | 
|  | i |= inb(qbase + 5);	/* the 0x10 bit can be set after the 0x08 */ | 
|  | if (i != 0x18) { | 
|  | printk(KERN_ERR "Ql:Bad Interrupt status:%02x\n", i); | 
|  | ql_zap(priv); | 
|  | return (DID_BAD_INTR << 16); | 
|  | } | 
|  | j &= 7;			/* j = inb( qbase + 7 ) >> 5; */ | 
|  |  | 
|  | /* correct status is supposed to be step 4 */ | 
|  | /* it sometimes returns step 3 but with 0 bytes left to send */ | 
|  | /* We can try stuffing the FIFO with the max each time, but we will get a | 
|  | sequence of 3 if any bytes are left (but we do flush the FIFO anyway */ | 
|  |  | 
|  | if (j != 3 && j != 4) { | 
|  | printk(KERN_ERR "Ql:Bad sequence for command %d, int %02X, cmdleft = %d\n", | 
|  | j, i, inb(qbase + 7) & 0x1f); | 
|  | ql_zap(priv); | 
|  | return (DID_ERROR << 16); | 
|  | } | 
|  | result = DID_OK; | 
|  | if (inb(qbase + 7) & 0x1f)	/* if some bytes in fifo */ | 
|  | outb(1, qbase + 3);	/* clear fifo */ | 
|  | /* note that request_bufflen is the total xfer size when sg is used */ | 
|  | reqlen = scsi_bufflen(cmd); | 
|  | /* note that it won't work if transfers > 16M are requested */ | 
|  | if (reqlen && !((phase = inb(qbase + 4)) & 6)) {	/* data phase */ | 
|  | struct scatterlist *sg; | 
|  | rtrc(2) | 
|  | outb(reqlen, qbase);	/* low-mid xfer cnt */ | 
|  | outb(reqlen >> 8, qbase + 1);	/* low-mid xfer cnt */ | 
|  | outb(reqlen >> 16, qbase + 0xe);	/* high xfer cnt */ | 
|  | outb(0x90, qbase + 3);	/* command do xfer */ | 
|  | /* PIO pseudo DMA to buffer or sglist */ | 
|  | REG1; | 
|  |  | 
|  | scsi_for_each_sg(cmd, sg, scsi_sg_count(cmd), i) { | 
|  | if (priv->qabort) { | 
|  | REG0; | 
|  | return ((priv->qabort == 1 ? | 
|  | DID_ABORT : DID_RESET) << 16); | 
|  | } | 
|  | buf = sg_virt(sg); | 
|  | if (ql_pdma(priv, phase, buf, sg->length)) | 
|  | break; | 
|  | } | 
|  | REG0; | 
|  | rtrc(2) | 
|  | /* | 
|  | *	Wait for irq (split into second state of irq handler | 
|  | *	if this can take time) | 
|  | */ | 
|  | if ((k = ql_wai(priv))) | 
|  | return (k << 16); | 
|  | k = inb(qbase + 5);	/* should be 0x10, bus service */ | 
|  | } | 
|  |  | 
|  | /* | 
|  | *	Enter Status (and Message In) Phase | 
|  | */ | 
|  |  | 
|  | k = jiffies + WATCHDOG; | 
|  |  | 
|  | while (time_before(jiffies, k) && !priv->qabort && | 
|  | !(inb(qbase + 4) & 6)) | 
|  | cpu_relax();	/* wait for status phase */ | 
|  |  | 
|  | if (time_after_eq(jiffies, k)) { | 
|  | ql_zap(priv); | 
|  | return (DID_TIME_OUT << 16); | 
|  | } | 
|  |  | 
|  | /* FIXME: timeout ?? */ | 
|  | while (inb(qbase + 5)) | 
|  | cpu_relax();	/* clear pending ints */ | 
|  |  | 
|  | if (priv->qabort) | 
|  | return ((priv->qabort == 1 ? DID_ABORT : DID_RESET) << 16); | 
|  |  | 
|  | outb(0x11, qbase + 3);	/* get status and message */ | 
|  | if ((k = ql_wai(priv))) | 
|  | return (k << 16); | 
|  | i = inb(qbase + 5);	/* get chip irq stat */ | 
|  | j = inb(qbase + 7) & 0x1f;	/* and bytes rec'd */ | 
|  | status = inb(qbase + 2); | 
|  | message = inb(qbase + 2); | 
|  |  | 
|  | /* | 
|  | *	Should get function complete int if Status and message, else | 
|  | *	bus serv if only status | 
|  | */ | 
|  | if (!((i == 8 && j == 2) || (i == 0x10 && j == 1))) { | 
|  | printk(KERN_ERR "Ql:Error during status phase, int=%02X, %d bytes recd\n", i, j); | 
|  | result = DID_ERROR; | 
|  | } | 
|  | outb(0x12, qbase + 3);	/* done, disconnect */ | 
|  | rtrc(1) | 
|  | if ((k = ql_wai(priv))) | 
|  | return (k << 16); | 
|  |  | 
|  | /* | 
|  | *	Should get bus service interrupt and disconnect interrupt | 
|  | */ | 
|  |  | 
|  | i = inb(qbase + 5);	/* should be bus service */ | 
|  | while (!priv->qabort && ((i & 0x20) != 0x20)) { | 
|  | barrier(); | 
|  | cpu_relax(); | 
|  | i |= inb(qbase + 5); | 
|  | } | 
|  | rtrc(0) | 
|  |  | 
|  | if (priv->qabort) | 
|  | return ((priv->qabort == 1 ? DID_ABORT : DID_RESET) << 16); | 
|  |  | 
|  | return (result << 16) | (message << 8) | (status & STATUS_MASK); | 
|  | } | 
|  |  | 
|  | /* | 
|  | *	Interrupt handler | 
|  | */ | 
|  |  | 
|  | static void ql_ihandl(void *dev_id) | 
|  | { | 
|  | struct scsi_cmnd *icmd; | 
|  | struct Scsi_Host *host = dev_id; | 
|  | struct qlogicfas408_priv *priv = get_priv_by_host(host); | 
|  | int qbase = priv->qbase; | 
|  | REG0; | 
|  |  | 
|  | if (!(inb(qbase + 4) & 0x80))	/* false alarm? */ | 
|  | return; | 
|  |  | 
|  | if (priv->qlcmd == NULL) {	/* no command to process? */ | 
|  | int i; | 
|  | i = 16; | 
|  | while (i-- && inb(qbase + 5));	/* maybe also ql_zap() */ | 
|  | return; | 
|  | } | 
|  | icmd = priv->qlcmd; | 
|  | icmd->result = ql_pcmd(icmd); | 
|  | priv->qlcmd = NULL; | 
|  | /* | 
|  | *	If result is CHECK CONDITION done calls qcommand to request | 
|  | *	sense | 
|  | */ | 
|  | (icmd->scsi_done) (icmd); | 
|  | } | 
|  |  | 
|  | irqreturn_t qlogicfas408_ihandl(int irq, void *dev_id) | 
|  | { | 
|  | unsigned long flags; | 
|  | struct Scsi_Host *host = dev_id; | 
|  |  | 
|  | spin_lock_irqsave(host->host_lock, flags); | 
|  | ql_ihandl(dev_id); | 
|  | spin_unlock_irqrestore(host->host_lock, flags); | 
|  | return IRQ_HANDLED; | 
|  | } | 
|  |  | 
|  | /* | 
|  | *	Queued command | 
|  | */ | 
|  |  | 
|  | static int qlogicfas408_queuecommand_lck(struct scsi_cmnd *cmd, | 
|  | void (*done) (struct scsi_cmnd *)) | 
|  | { | 
|  | struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd); | 
|  | if (scmd_id(cmd) == priv->qinitid) { | 
|  | cmd->result = DID_BAD_TARGET << 16; | 
|  | done(cmd); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | cmd->scsi_done = done; | 
|  | /* wait for the last command's interrupt to finish */ | 
|  | while (priv->qlcmd != NULL) { | 
|  | barrier(); | 
|  | cpu_relax(); | 
|  | } | 
|  | ql_icmd(cmd); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | DEF_SCSI_QCMD(qlogicfas408_queuecommand) | 
|  |  | 
|  | /* | 
|  | *	Return bios parameters | 
|  | */ | 
|  |  | 
|  | int qlogicfas408_biosparam(struct scsi_device *disk, struct block_device *dev, | 
|  | sector_t capacity, int ip[]) | 
|  | { | 
|  | /* This should mimic the DOS Qlogic driver's behavior exactly */ | 
|  | ip[0] = 0x40; | 
|  | ip[1] = 0x20; | 
|  | ip[2] = (unsigned long) capacity / (ip[0] * ip[1]); | 
|  | if (ip[2] > 1024) { | 
|  | ip[0] = 0xff; | 
|  | ip[1] = 0x3f; | 
|  | ip[2] = (unsigned long) capacity / (ip[0] * ip[1]); | 
|  | #if 0 | 
|  | if (ip[2] > 1023) | 
|  | ip[2] = 1023; | 
|  | #endif | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | *	Abort a command in progress | 
|  | */ | 
|  |  | 
|  | int qlogicfas408_abort(struct scsi_cmnd *cmd) | 
|  | { | 
|  | struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd); | 
|  | priv->qabort = 1; | 
|  | ql_zap(priv); | 
|  | return SUCCESS; | 
|  | } | 
|  |  | 
|  | /* | 
|  | *	Reset SCSI bus | 
|  | *	FIXME: This function is invoked with cmd = NULL directly by | 
|  | *	the PCMCIA qlogic_stub code. This wants fixing | 
|  | */ | 
|  |  | 
|  | int qlogicfas408_bus_reset(struct scsi_cmnd *cmd) | 
|  | { | 
|  | struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd); | 
|  | unsigned long flags; | 
|  |  | 
|  | priv->qabort = 2; | 
|  |  | 
|  | spin_lock_irqsave(cmd->device->host->host_lock, flags); | 
|  | ql_zap(priv); | 
|  | spin_unlock_irqrestore(cmd->device->host->host_lock, flags); | 
|  |  | 
|  | return SUCCESS; | 
|  | } | 
|  |  | 
|  | /* | 
|  | *	Return info string | 
|  | */ | 
|  |  | 
|  | const char *qlogicfas408_info(struct Scsi_Host *host) | 
|  | { | 
|  | struct qlogicfas408_priv *priv = get_priv_by_host(host); | 
|  | return priv->qinfo; | 
|  | } | 
|  |  | 
|  | /* | 
|  | *	Get type of chip | 
|  | */ | 
|  |  | 
|  | int qlogicfas408_get_chip_type(int qbase, int int_type) | 
|  | { | 
|  | REG1; | 
|  | return inb(qbase + 0xe) & 0xf8; | 
|  | } | 
|  |  | 
|  | /* | 
|  | *	Perform initialization tasks | 
|  | */ | 
|  |  | 
|  | void qlogicfas408_setup(int qbase, int id, int int_type) | 
|  | { | 
|  | outb(1, qbase + 8);	/* set for PIO pseudo DMA */ | 
|  | REG0; | 
|  | outb(0x40 | qlcfg8 | id, qbase + 8);	/* (ini) bus id, disable scsi rst */ | 
|  | outb(qlcfg5, qbase + 5);	/* select timer */ | 
|  | outb(qlcfg9, qbase + 9);	/* prescaler */ | 
|  |  | 
|  | #if QL_RESET_AT_START | 
|  | outb(3, qbase + 3); | 
|  |  | 
|  | REG1; | 
|  | /* FIXME: timeout */ | 
|  | while (inb(qbase + 0xf) & 4) | 
|  | cpu_relax(); | 
|  |  | 
|  | REG0; | 
|  | #endif | 
|  | } | 
|  |  | 
|  | /* | 
|  | *	Checks if this is a QLogic FAS 408 | 
|  | */ | 
|  |  | 
|  | int qlogicfas408_detect(int qbase, int int_type) | 
|  | { | 
|  | REG1; | 
|  | return (((inb(qbase + 0xe) ^ inb(qbase + 0xe)) == 7) && | 
|  | ((inb(qbase + 0xe) ^ inb(qbase + 0xe)) == 7)); | 
|  | } | 
|  |  | 
|  | /* | 
|  | *	Disable interrupts | 
|  | */ | 
|  |  | 
|  | void qlogicfas408_disable_ints(struct qlogicfas408_priv *priv) | 
|  | { | 
|  | int qbase = priv->qbase; | 
|  | int int_type = priv->int_type; | 
|  |  | 
|  | REG1; | 
|  | outb(0, qbase + 0xb);	/* disable ints */ | 
|  | } | 
|  |  | 
|  | /* | 
|  | *	Init and exit functions | 
|  | */ | 
|  |  | 
|  | static int __init qlogicfas408_init(void) | 
|  | { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void __exit qlogicfas408_exit(void) | 
|  | { | 
|  |  | 
|  | } | 
|  |  | 
|  | MODULE_AUTHOR("Tom Zerucha, Michael Griffith"); | 
|  | MODULE_DESCRIPTION("Driver for the Qlogic FAS SCSI controllers"); | 
|  | MODULE_LICENSE("GPL"); | 
|  | module_init(qlogicfas408_init); | 
|  | module_exit(qlogicfas408_exit); | 
|  |  | 
|  | EXPORT_SYMBOL(qlogicfas408_info); | 
|  | EXPORT_SYMBOL(qlogicfas408_queuecommand); | 
|  | EXPORT_SYMBOL(qlogicfas408_abort); | 
|  | EXPORT_SYMBOL(qlogicfas408_bus_reset); | 
|  | EXPORT_SYMBOL(qlogicfas408_biosparam); | 
|  | EXPORT_SYMBOL(qlogicfas408_ihandl); | 
|  | EXPORT_SYMBOL(qlogicfas408_get_chip_type); | 
|  | EXPORT_SYMBOL(qlogicfas408_setup); | 
|  | EXPORT_SYMBOL(qlogicfas408_detect); | 
|  | EXPORT_SYMBOL(qlogicfas408_disable_ints); | 
|  |  |