11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Qlogic FAS408 ISA card driver
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright 1994, Tom Zerucha.
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * tz@execpc.com
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Redistributable under terms of the GNU General Public License
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * For the avoidance of doubt the "preferred form" of this code is one which
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * is in an open non patent encumbered format. Where cryptographic key signing
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * forms part of the process of creating an executable the information
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * including keys needed to generate an equivalently functional executable
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * are deemed to be part of the source code.
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Check qlogicfas408.c for more credits and info.
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/blkdev.h>		/* to get disk capacity */
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h>
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/string.h>
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/interrupt.h>
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/ioport.h>
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/proc_fs.h>
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/unistd.h>
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/spinlock.h>
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/stat.h>
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/io.h>
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/irq.h>
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/dma.h>
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "scsi.h"
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <scsi/scsi_host.h>
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "qlogicfas408.h"
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Set the following to 2 to use normal interrupt (active high/totempole-
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * tristate), otherwise use 0 (REQUIRED FOR PCMCIA) for active low, open
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * drain
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define INT_TYPE	2
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic char qlogicfas_name[] = "qlogicfas";
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Look for qlogic card and init if found
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
50d0be4a7d29ad0bd3ce2209dd9e46d410b632db59Christoph Hellwigstatic struct Scsi_Host *__qlogicfas_detect(struct scsi_host_template *host,
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds								int qbase,
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds								int qlirq)
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int qltyp;		/* type of chip */
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int qinitid;
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct Scsi_Host *hreg;	/* registered host structure */
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct qlogicfas408_priv *priv;
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*	Qlogic Cards only exist at 0x230 or 0x330 (the chip itself
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 *	decodes the address - I check 230 first since MIDI cards are
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 *	typically at 0x330
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 *
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 *	Theoretically, two Qlogic cards can coexist in the same system.
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 *	This should work by simply using this as a loadable module for
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 *	the second card, but I haven't tested this.
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!qbase || qlirq == -1)
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto err;
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!request_region(qbase, 0x10, qlogicfas_name)) {
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_INFO "%s: address %#x is busy\n", qlogicfas_name,
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds							      qbase);
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto err;
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!qlogicfas408_detect(qbase, INT_TYPE)) {
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_WARNING "%s: probe failed for %#x\n",
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds								qlogicfas_name,
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds								qbase);
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto err_release_mem;
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk(KERN_INFO "%s: Using preset base address of %03x,"
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 " IRQ %d\n", qlogicfas_name, qbase, qlirq);
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	qltyp = qlogicfas408_get_chip_type(qbase, INT_TYPE);
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	qinitid = host->this_id;
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (qinitid < 0)
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		qinitid = 7;	/* if no ID, use 7 */
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	qlogicfas408_setup(qbase, qinitid, INT_TYPE);
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hreg = scsi_host_alloc(host, sizeof(struct qlogicfas408_priv));
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!hreg)
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto err_release_mem;
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	priv = get_priv_by_host(hreg);
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hreg->io_port = qbase;
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hreg->n_io_port = 16;
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hreg->dma_channel = -1;
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (qlirq != -1)
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		hreg->irq = qlirq;
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	priv->qbase = qbase;
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	priv->qlirq = qlirq;
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	priv->qinitid = qinitid;
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	priv->shost = hreg;
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	priv->int_type = INT_TYPE;
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sprintf(priv->qinfo,
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		"Qlogicfas Driver version 0.46, chip %02X at %03X, IRQ %d, TPdma:%d",
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		qltyp, qbase, qlirq, QL_TURBO_PDMA);
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	host->name = qlogicfas_name;
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (request_irq(qlirq, qlogicfas408_ihandl, 0, qlogicfas_name, hreg))
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto free_scsi_host;
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (scsi_add_host(hreg, NULL))
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto free_interrupt;
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	scsi_scan_host(hreg);
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return hreg;
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsfree_interrupt:
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	free_irq(qlirq, hreg);
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsfree_scsi_host:
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	scsi_host_put(hreg);
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldserr_release_mem:
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	release_region(qbase, 0x10);
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldserr:
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return NULL;
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define MAX_QLOGICFAS	8
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct qlogicfas408_priv *cards;
1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int iobase[MAX_QLOGICFAS];
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int irq[MAX_QLOGICFAS] = { [0 ... MAX_QLOGICFAS-1] = -1 };
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param_array(iobase, int, NULL, 0);
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param_array(irq, int, NULL, 0);
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(iobase, "I/O address");
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(irq, "IRQ");
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
145d0be4a7d29ad0bd3ce2209dd9e46d410b632db59Christoph Hellwigstatic int __devinit qlogicfas_detect(struct scsi_host_template *sht)
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct Scsi_Host *shost;
1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct qlogicfas408_priv *priv;
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int num;
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (num = 0; num < MAX_QLOGICFAS; num++) {
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		shost = __qlogicfas_detect(sht, iobase[num], irq[num]);
1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (shost == NULL) {
1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* no more devices */
1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		priv = get_priv_by_host(shost);
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		priv->next = cards;
1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		cards = priv;
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return num;
1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int qlogicfas_release(struct Scsi_Host *shost)
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct qlogicfas408_priv *priv = get_priv_by_host(shost);
1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
169c131993b4cbfb6d564619e609b3d0b471ec52c5dMatthew Wilcox	scsi_remove_host(shost);
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (shost->irq) {
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		qlogicfas408_disable_ints(priv);
1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		free_irq(shost->irq, shost);
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (shost->dma_channel != 0xff)
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		free_dma(shost->dma_channel);
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (shost->io_port && shost->n_io_port)
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		release_region(shost->io_port, shost->n_io_port);
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	scsi_host_put(shost);
1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	The driver template is also needed for PCMCIA
1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
186d0be4a7d29ad0bd3ce2209dd9e46d410b632db59Christoph Hellwigstatic struct scsi_host_template qlogicfas_driver_template = {
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.module			= THIS_MODULE,
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.name			= qlogicfas_name,
1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.proc_name		= qlogicfas_name,
1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.info			= qlogicfas408_info,
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.queuecommand		= qlogicfas408_queuecommand,
1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.eh_abort_handler	= qlogicfas408_abort,
1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.eh_bus_reset_handler	= qlogicfas408_bus_reset,
1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.bios_param		= qlogicfas408_biosparam,
1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.can_queue		= 1,
1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.this_id		= -1,
1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.sg_tablesize		= SG_ALL,
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.cmd_per_lun		= 1,
1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.use_clustering		= DISABLE_CLUSTERING,
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic __init int qlogicfas_init(void)
2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!qlogicfas_detect(&qlogicfas_driver_template)) {
2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* no cards found */
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_INFO "%s: no cards were found, please specify "
2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				 "I/O address and IRQ using iobase= and irq= "
2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				 "options", qlogicfas_name);
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENODEV;
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic __exit void qlogicfas_exit(void)
2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct qlogicfas408_priv *priv;
2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (priv = cards; priv != NULL; priv = priv->next)
2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		qlogicfas_release(priv->shost);
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR("Tom Zerucha, Michael Griffith");
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION("Driver for the Qlogic FAS408 based ISA card");
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(qlogicfas_init);
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(qlogicfas_exit);
2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
229