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