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