230 lines · c
1/*2 * Qlogic FAS408 ISA card driver3 *4 * Copyright 1994, Tom Zerucha. 5 * tz@execpc.com6 * 7 * Redistributable under terms of the GNU General Public License8 *9 * For the avoidance of doubt the "preferred form" of this code is one which10 * is in an open non patent encumbered format. Where cryptographic key signing11 * forms part of the process of creating an executable the information12 * including keys needed to generate an equivalently functional executable13 * 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, open44 * drain45 */46#define INT_TYPE 247 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 itself64 * decodes the address - I check 230 first since MIDI cards are65 * typically at 0x33066 *67 * Theoretically, two Qlogic cards can coexist in the same system.68 * This should work by simply using this as a loadable module for69 * 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 8141static 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 PCMCIA187 */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 230