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