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 48 static char qlogicfas_name[] = "qlogicfas"; 49 50 /* 51 * Look for qlogic card and init if found 52 */ 53 54 static 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 128 free_interrupt: 129 free_irq(qlirq, hreg); 130 131 free_scsi_host: 132 scsi_host_put(hreg); 133 134 err_release_mem: 135 release_region(qbase, 0x10); 136 err: 137 return NULL; 138 } 139 140 #define MAX_QLOGICFAS 8 141 static struct qlogicfas408_priv *cards; 142 static int iobase[MAX_QLOGICFAS]; 143 static int irq[MAX_QLOGICFAS] = { [0 ... MAX_QLOGICFAS-1] = -1 }; 144 module_param_hw_array(iobase, int, ioport, NULL, 0); 145 module_param_hw_array(irq, int, irq, NULL, 0); 146 MODULE_PARM_DESC(iobase, "I/O address"); 147 MODULE_PARM_DESC(irq, "IRQ"); 148 149 static 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 169 static 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 */ 188 static 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 203 static __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 216 static __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 224 MODULE_AUTHOR("Tom Zerucha, Michael Griffith"); 225 MODULE_DESCRIPTION("Driver for the Qlogic FAS408 based ISA card"); 226 MODULE_LICENSE("GPL"); 227 module_init(qlogicfas_init); 228 module_exit(qlogicfas_exit); 229 230