qlogicfas.c (5791B)
1/* 2 * Qlogic FAS408 ISA card driver 3 * 4 * Copyright 1994, Tom Zerucha. 5 * tz@execpc.com 6 * 7 * Redistributable under terms of the GNU General Public License 8 * 9 * For the avoidance of doubt the "preferred form" of this code is one which 10 * is in an open non patent encumbered format. Where cryptographic key signing 11 * forms part of the process of creating an executable the information 12 * including keys needed to generate an equivalently functional executable 13 * are deemed to be part of the source code. 14 * 15 * Check qlogicfas408.c for more credits and info. 16 */ 17 18#include <linux/module.h> 19#include <linux/blkdev.h> /* to get disk capacity */ 20#include <linux/kernel.h> 21#include <linux/string.h> 22#include <linux/init.h> 23#include <linux/interrupt.h> 24#include <linux/ioport.h> 25#include <linux/proc_fs.h> 26#include <linux/unistd.h> 27#include <linux/spinlock.h> 28#include <linux/stat.h> 29 30#include <asm/io.h> 31#include <asm/irq.h> 32#include <asm/dma.h> 33 34#include <scsi/scsi.h> 35#include <scsi/scsi_cmnd.h> 36#include <scsi/scsi_device.h> 37#include <scsi/scsi_eh.h> 38#include <scsi/scsi_host.h> 39#include <scsi/scsi_tcq.h> 40#include "qlogicfas408.h" 41 42/* Set the following to 2 to use normal interrupt (active high/totempole- 43 * tristate), otherwise use 0 (REQUIRED FOR PCMCIA) for active low, open 44 * drain 45 */ 46#define INT_TYPE 2 47 48static char qlogicfas_name[] = "qlogicfas"; 49 50/* 51 * Look for qlogic card and init if found 52 */ 53 54static struct Scsi_Host *__qlogicfas_detect(struct scsi_host_template *host, 55 int qbase, 56 int qlirq) 57{ 58 int qltyp; /* type of chip */ 59 int qinitid; 60 struct Scsi_Host *hreg; /* registered host structure */ 61 struct qlogicfas408_priv *priv; 62 63 /* Qlogic Cards only exist at 0x230 or 0x330 (the chip itself 64 * decodes the address - I check 230 first since MIDI cards are 65 * typically at 0x330 66 * 67 * Theoretically, two Qlogic cards can coexist in the same system. 68 * This should work by simply using this as a loadable module for 69 * the second card, but I haven't tested this. 70 */ 71 72 if (!qbase || qlirq == -1) 73 goto err; 74 75 if (!request_region(qbase, 0x10, qlogicfas_name)) { 76 printk(KERN_INFO "%s: address %#x is busy\n", qlogicfas_name, 77 qbase); 78 goto err; 79 } 80 81 if (!qlogicfas408_detect(qbase, INT_TYPE)) { 82 printk(KERN_WARNING "%s: probe failed for %#x\n", 83 qlogicfas_name, 84 qbase); 85 goto err_release_mem; 86 } 87 88 printk(KERN_INFO "%s: Using preset base address of %03x," 89 " IRQ %d\n", qlogicfas_name, qbase, qlirq); 90 91 qltyp = qlogicfas408_get_chip_type(qbase, INT_TYPE); 92 qinitid = host->this_id; 93 if (qinitid < 0) 94 qinitid = 7; /* if no ID, use 7 */ 95 96 qlogicfas408_setup(qbase, qinitid, INT_TYPE); 97 98 hreg = scsi_host_alloc(host, sizeof(struct qlogicfas408_priv)); 99 if (!hreg) 100 goto err_release_mem; 101 priv = get_priv_by_host(hreg); 102 hreg->io_port = qbase; 103 hreg->n_io_port = 16; 104 hreg->dma_channel = -1; 105 if (qlirq != -1) 106 hreg->irq = qlirq; 107 priv->qbase = qbase; 108 priv->qlirq = qlirq; 109 priv->qinitid = qinitid; 110 priv->shost = hreg; 111 priv->int_type = INT_TYPE; 112 113 sprintf(priv->qinfo, 114 "Qlogicfas Driver version 0.46, chip %02X at %03X, IRQ %d, TPdma:%d", 115 qltyp, qbase, qlirq, QL_TURBO_PDMA); 116 host->name = qlogicfas_name; 117 118 if (request_irq(qlirq, qlogicfas408_ihandl, 0, qlogicfas_name, hreg)) 119 goto free_scsi_host; 120 121 if (scsi_add_host(hreg, NULL)) 122 goto free_interrupt; 123 124 scsi_scan_host(hreg); 125 126 return hreg; 127 128free_interrupt: 129 free_irq(qlirq, hreg); 130 131free_scsi_host: 132 scsi_host_put(hreg); 133 134err_release_mem: 135 release_region(qbase, 0x10); 136err: 137 return NULL; 138} 139 140#define MAX_QLOGICFAS 8 141static struct qlogicfas408_priv *cards; 142static int iobase[MAX_QLOGICFAS]; 143static int irq[MAX_QLOGICFAS] = { [0 ... MAX_QLOGICFAS-1] = -1 }; 144module_param_hw_array(iobase, int, ioport, NULL, 0); 145module_param_hw_array(irq, int, irq, NULL, 0); 146MODULE_PARM_DESC(iobase, "I/O address"); 147MODULE_PARM_DESC(irq, "IRQ"); 148 149static int qlogicfas_detect(struct scsi_host_template *sht) 150{ 151 struct Scsi_Host *shost; 152 struct qlogicfas408_priv *priv; 153 int num; 154 155 for (num = 0; num < MAX_QLOGICFAS; num++) { 156 shost = __qlogicfas_detect(sht, iobase[num], irq[num]); 157 if (shost == NULL) { 158 /* no more devices */ 159 break; 160 } 161 priv = get_priv_by_host(shost); 162 priv->next = cards; 163 cards = priv; 164 } 165 166 return num; 167} 168 169static int qlogicfas_release(struct Scsi_Host *shost) 170{ 171 struct qlogicfas408_priv *priv = get_priv_by_host(shost); 172 173 scsi_remove_host(shost); 174 if (shost->irq) { 175 qlogicfas408_disable_ints(priv); 176 free_irq(shost->irq, shost); 177 } 178 if (shost->io_port && shost->n_io_port) 179 release_region(shost->io_port, shost->n_io_port); 180 scsi_host_put(shost); 181 182 return 0; 183} 184 185/* 186 * The driver template is also needed for PCMCIA 187 */ 188static struct scsi_host_template qlogicfas_driver_template = { 189 .module = THIS_MODULE, 190 .name = qlogicfas_name, 191 .proc_name = qlogicfas_name, 192 .info = qlogicfas408_info, 193 .queuecommand = qlogicfas408_queuecommand, 194 .eh_abort_handler = qlogicfas408_abort, 195 .eh_host_reset_handler = qlogicfas408_host_reset, 196 .bios_param = qlogicfas408_biosparam, 197 .can_queue = 1, 198 .this_id = -1, 199 .sg_tablesize = SG_ALL, 200 .dma_boundary = PAGE_SIZE - 1, 201}; 202 203static __init int qlogicfas_init(void) 204{ 205 if (!qlogicfas_detect(&qlogicfas_driver_template)) { 206 /* no cards found */ 207 printk(KERN_INFO "%s: no cards were found, please specify " 208 "I/O address and IRQ using iobase= and irq= " 209 "options", qlogicfas_name); 210 return -ENODEV; 211 } 212 213 return 0; 214} 215 216static __exit void qlogicfas_exit(void) 217{ 218 struct qlogicfas408_priv *priv; 219 220 for (priv = cards; priv != NULL; priv = priv->next) 221 qlogicfas_release(priv->shost); 222} 223 224MODULE_AUTHOR("Tom Zerucha, Michael Griffith"); 225MODULE_DESCRIPTION("Driver for the Qlogic FAS408 based ISA card"); 226MODULE_LICENSE("GPL"); 227module_init(qlogicfas_init); 228module_exit(qlogicfas_exit); 229