Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * Qlogic FAS408 ISA card driver
0003  *
0004  * Copyright 1994, Tom Zerucha.   
0005  * tz@execpc.com
0006  * 
0007  * Redistributable under terms of the GNU General Public License
0008  *
0009  * For the avoidance of doubt the "preferred form" of this code is one which
0010  * is in an open non patent encumbered format. Where cryptographic key signing
0011  * forms part of the process of creating an executable the information
0012  * including keys needed to generate an equivalently functional executable
0013  * are deemed to be part of the source code.
0014  *
0015  * Check qlogicfas408.c for more credits and info.
0016  */
0017 
0018 #include <linux/module.h>
0019 #include <linux/blkdev.h>       /* to get disk capacity */
0020 #include <linux/kernel.h>
0021 #include <linux/string.h>
0022 #include <linux/init.h>
0023 #include <linux/interrupt.h>
0024 #include <linux/ioport.h>
0025 #include <linux/proc_fs.h>
0026 #include <linux/unistd.h>
0027 #include <linux/spinlock.h>
0028 #include <linux/stat.h>
0029 
0030 #include <asm/io.h>
0031 #include <asm/irq.h>
0032 #include <asm/dma.h>
0033 
0034 #include <scsi/scsi.h>
0035 #include <scsi/scsi_cmnd.h>
0036 #include <scsi/scsi_device.h>
0037 #include <scsi/scsi_eh.h>
0038 #include <scsi/scsi_host.h>
0039 #include <scsi/scsi_tcq.h>
0040 #include "qlogicfas408.h"
0041 
0042 /* Set the following to 2 to use normal interrupt (active high/totempole-
0043  * tristate), otherwise use 0 (REQUIRED FOR PCMCIA) for active low, open
0044  * drain
0045  */
0046 #define INT_TYPE    2
0047 
0048 static char qlogicfas_name[] = "qlogicfas";
0049 
0050 /*
0051  *  Look for qlogic card and init if found 
0052  */
0053  
0054 static struct Scsi_Host *__qlogicfas_detect(struct scsi_host_template *host,
0055                                 int qbase,
0056                                 int qlirq)
0057 {
0058     int qltyp;      /* type of chip */
0059     int qinitid;
0060     struct Scsi_Host *hreg; /* registered host structure */
0061     struct qlogicfas408_priv *priv;
0062 
0063     /*  Qlogic Cards only exist at 0x230 or 0x330 (the chip itself
0064      *  decodes the address - I check 230 first since MIDI cards are
0065      *  typically at 0x330
0066      *
0067      *  Theoretically, two Qlogic cards can coexist in the same system.
0068      *  This should work by simply using this as a loadable module for
0069      *  the second card, but I haven't tested this.
0070      */
0071 
0072     if (!qbase || qlirq == -1)
0073         goto err;
0074 
0075     if (!request_region(qbase, 0x10, qlogicfas_name)) {
0076         printk(KERN_INFO "%s: address %#x is busy\n", qlogicfas_name,
0077                                   qbase);
0078         goto err;
0079     }
0080 
0081     if (!qlogicfas408_detect(qbase, INT_TYPE)) {
0082         printk(KERN_WARNING "%s: probe failed for %#x\n",
0083                                 qlogicfas_name,
0084                                 qbase);
0085         goto err_release_mem;
0086     }
0087 
0088     printk(KERN_INFO "%s: Using preset base address of %03x,"
0089              " IRQ %d\n", qlogicfas_name, qbase, qlirq);
0090 
0091     qltyp = qlogicfas408_get_chip_type(qbase, INT_TYPE);
0092     qinitid = host->this_id;
0093     if (qinitid < 0)
0094         qinitid = 7;    /* if no ID, use 7 */
0095 
0096     qlogicfas408_setup(qbase, qinitid, INT_TYPE);
0097 
0098     hreg = scsi_host_alloc(host, sizeof(struct qlogicfas408_priv));
0099     if (!hreg)
0100         goto err_release_mem;
0101     priv = get_priv_by_host(hreg);
0102     hreg->io_port = qbase;
0103     hreg->n_io_port = 16;
0104     hreg->dma_channel = -1;
0105     if (qlirq != -1)
0106         hreg->irq = qlirq;
0107     priv->qbase = qbase;
0108     priv->qlirq = qlirq;
0109     priv->qinitid = qinitid;
0110     priv->shost = hreg;
0111     priv->int_type = INT_TYPE;
0112 
0113     sprintf(priv->qinfo,
0114         "Qlogicfas Driver version 0.46, chip %02X at %03X, IRQ %d, TPdma:%d",
0115         qltyp, qbase, qlirq, QL_TURBO_PDMA);
0116     host->name = qlogicfas_name;
0117 
0118     if (request_irq(qlirq, qlogicfas408_ihandl, 0, qlogicfas_name, hreg))
0119         goto free_scsi_host;
0120 
0121     if (scsi_add_host(hreg, NULL))
0122         goto free_interrupt;
0123 
0124     scsi_scan_host(hreg);
0125 
0126     return hreg;
0127 
0128 free_interrupt:
0129     free_irq(qlirq, hreg);
0130 
0131 free_scsi_host:
0132     scsi_host_put(hreg);
0133 
0134 err_release_mem:
0135     release_region(qbase, 0x10);
0136 err:
0137     return NULL;
0138 }
0139 
0140 #define MAX_QLOGICFAS   8
0141 static struct qlogicfas408_priv *cards;
0142 static int iobase[MAX_QLOGICFAS];
0143 static int irq[MAX_QLOGICFAS] = { [0 ... MAX_QLOGICFAS-1] = -1 };
0144 module_param_hw_array(iobase, int, ioport, NULL, 0);
0145 module_param_hw_array(irq, int, irq, NULL, 0);
0146 MODULE_PARM_DESC(iobase, "I/O address");
0147 MODULE_PARM_DESC(irq, "IRQ");
0148 
0149 static int qlogicfas_detect(struct scsi_host_template *sht)
0150 {
0151     struct Scsi_Host *shost;
0152     struct qlogicfas408_priv *priv;
0153     int num;
0154 
0155     for (num = 0; num < MAX_QLOGICFAS; num++) {
0156         shost = __qlogicfas_detect(sht, iobase[num], irq[num]);
0157         if (shost == NULL) {
0158             /* no more devices */
0159             break;
0160         }
0161         priv = get_priv_by_host(shost);
0162         priv->next = cards;
0163         cards = priv;
0164     }
0165 
0166     return num;
0167 }
0168 
0169 static int qlogicfas_release(struct Scsi_Host *shost)
0170 {
0171     struct qlogicfas408_priv *priv = get_priv_by_host(shost);
0172 
0173     scsi_remove_host(shost);
0174     if (shost->irq) {
0175         qlogicfas408_disable_ints(priv);    
0176         free_irq(shost->irq, shost);
0177     }
0178     if (shost->io_port && shost->n_io_port)
0179         release_region(shost->io_port, shost->n_io_port);
0180     scsi_host_put(shost);
0181 
0182     return 0;
0183 }
0184 
0185 /*
0186  *  The driver template is also needed for PCMCIA
0187  */
0188 static struct scsi_host_template qlogicfas_driver_template = {
0189     .module         = THIS_MODULE,
0190     .name           = qlogicfas_name,
0191     .proc_name      = qlogicfas_name,
0192     .info           = qlogicfas408_info,
0193     .queuecommand       = qlogicfas408_queuecommand,
0194     .eh_abort_handler   = qlogicfas408_abort,
0195     .eh_host_reset_handler  = qlogicfas408_host_reset,
0196     .bios_param     = qlogicfas408_biosparam,
0197     .can_queue      = 1,
0198     .this_id        = -1,
0199     .sg_tablesize       = SG_ALL,
0200     .dma_boundary       = PAGE_SIZE - 1,
0201 };
0202 
0203 static __init int qlogicfas_init(void)
0204 {
0205     if (!qlogicfas_detect(&qlogicfas_driver_template)) {
0206         /* no cards found */
0207         printk(KERN_INFO "%s: no cards were found, please specify "
0208                  "I/O address and IRQ using iobase= and irq= "
0209                  "options", qlogicfas_name);
0210         return -ENODEV;
0211     }
0212 
0213     return 0;
0214 }
0215 
0216 static __exit void qlogicfas_exit(void)
0217 {
0218     struct qlogicfas408_priv *priv;
0219 
0220     for (priv = cards; priv != NULL; priv = priv->next)
0221         qlogicfas_release(priv->shost);
0222 }
0223 
0224 MODULE_AUTHOR("Tom Zerucha, Michael Griffith");
0225 MODULE_DESCRIPTION("Driver for the Qlogic FAS408 based ISA card");
0226 MODULE_LICENSE("GPL");
0227 module_init(qlogicfas_init);
0228 module_exit(qlogicfas_exit);
0229