18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Qlogic FAS408 ISA card driver 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright 1994, Tom Zerucha. 58c2ecf20Sopenharmony_ci * tz@execpc.com 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Redistributable under terms of the GNU General Public License 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * For the avoidance of doubt the "preferred form" of this code is one which 108c2ecf20Sopenharmony_ci * is in an open non patent encumbered format. Where cryptographic key signing 118c2ecf20Sopenharmony_ci * forms part of the process of creating an executable the information 128c2ecf20Sopenharmony_ci * including keys needed to generate an equivalently functional executable 138c2ecf20Sopenharmony_ci * are deemed to be part of the source code. 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * Check qlogicfas408.c for more credits and info. 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/module.h> 198c2ecf20Sopenharmony_ci#include <linux/blkdev.h> /* to get disk capacity */ 208c2ecf20Sopenharmony_ci#include <linux/kernel.h> 218c2ecf20Sopenharmony_ci#include <linux/string.h> 228c2ecf20Sopenharmony_ci#include <linux/init.h> 238c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 248c2ecf20Sopenharmony_ci#include <linux/ioport.h> 258c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 268c2ecf20Sopenharmony_ci#include <linux/unistd.h> 278c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 288c2ecf20Sopenharmony_ci#include <linux/stat.h> 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#include <asm/io.h> 318c2ecf20Sopenharmony_ci#include <asm/irq.h> 328c2ecf20Sopenharmony_ci#include <asm/dma.h> 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#include "scsi.h" 358c2ecf20Sopenharmony_ci#include <scsi/scsi_host.h> 368c2ecf20Sopenharmony_ci#include "qlogicfas408.h" 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/* Set the following to 2 to use normal interrupt (active high/totempole- 398c2ecf20Sopenharmony_ci * tristate), otherwise use 0 (REQUIRED FOR PCMCIA) for active low, open 408c2ecf20Sopenharmony_ci * drain 418c2ecf20Sopenharmony_ci */ 428c2ecf20Sopenharmony_ci#define INT_TYPE 2 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic char qlogicfas_name[] = "qlogicfas"; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci/* 478c2ecf20Sopenharmony_ci * Look for qlogic card and init if found 488c2ecf20Sopenharmony_ci */ 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic struct Scsi_Host *__qlogicfas_detect(struct scsi_host_template *host, 518c2ecf20Sopenharmony_ci int qbase, 528c2ecf20Sopenharmony_ci int qlirq) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci int qltyp; /* type of chip */ 558c2ecf20Sopenharmony_ci int qinitid; 568c2ecf20Sopenharmony_ci struct Scsi_Host *hreg; /* registered host structure */ 578c2ecf20Sopenharmony_ci struct qlogicfas408_priv *priv; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci /* Qlogic Cards only exist at 0x230 or 0x330 (the chip itself 608c2ecf20Sopenharmony_ci * decodes the address - I check 230 first since MIDI cards are 618c2ecf20Sopenharmony_ci * typically at 0x330 628c2ecf20Sopenharmony_ci * 638c2ecf20Sopenharmony_ci * Theoretically, two Qlogic cards can coexist in the same system. 648c2ecf20Sopenharmony_ci * This should work by simply using this as a loadable module for 658c2ecf20Sopenharmony_ci * the second card, but I haven't tested this. 668c2ecf20Sopenharmony_ci */ 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci if (!qbase || qlirq == -1) 698c2ecf20Sopenharmony_ci goto err; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci if (!request_region(qbase, 0x10, qlogicfas_name)) { 728c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: address %#x is busy\n", qlogicfas_name, 738c2ecf20Sopenharmony_ci qbase); 748c2ecf20Sopenharmony_ci goto err; 758c2ecf20Sopenharmony_ci } 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci if (!qlogicfas408_detect(qbase, INT_TYPE)) { 788c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: probe failed for %#x\n", 798c2ecf20Sopenharmony_ci qlogicfas_name, 808c2ecf20Sopenharmony_ci qbase); 818c2ecf20Sopenharmony_ci goto err_release_mem; 828c2ecf20Sopenharmony_ci } 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: Using preset base address of %03x," 858c2ecf20Sopenharmony_ci " IRQ %d\n", qlogicfas_name, qbase, qlirq); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci qltyp = qlogicfas408_get_chip_type(qbase, INT_TYPE); 888c2ecf20Sopenharmony_ci qinitid = host->this_id; 898c2ecf20Sopenharmony_ci if (qinitid < 0) 908c2ecf20Sopenharmony_ci qinitid = 7; /* if no ID, use 7 */ 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci qlogicfas408_setup(qbase, qinitid, INT_TYPE); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci hreg = scsi_host_alloc(host, sizeof(struct qlogicfas408_priv)); 958c2ecf20Sopenharmony_ci if (!hreg) 968c2ecf20Sopenharmony_ci goto err_release_mem; 978c2ecf20Sopenharmony_ci priv = get_priv_by_host(hreg); 988c2ecf20Sopenharmony_ci hreg->io_port = qbase; 998c2ecf20Sopenharmony_ci hreg->n_io_port = 16; 1008c2ecf20Sopenharmony_ci hreg->dma_channel = -1; 1018c2ecf20Sopenharmony_ci if (qlirq != -1) 1028c2ecf20Sopenharmony_ci hreg->irq = qlirq; 1038c2ecf20Sopenharmony_ci priv->qbase = qbase; 1048c2ecf20Sopenharmony_ci priv->qlirq = qlirq; 1058c2ecf20Sopenharmony_ci priv->qinitid = qinitid; 1068c2ecf20Sopenharmony_ci priv->shost = hreg; 1078c2ecf20Sopenharmony_ci priv->int_type = INT_TYPE; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci sprintf(priv->qinfo, 1108c2ecf20Sopenharmony_ci "Qlogicfas Driver version 0.46, chip %02X at %03X, IRQ %d, TPdma:%d", 1118c2ecf20Sopenharmony_ci qltyp, qbase, qlirq, QL_TURBO_PDMA); 1128c2ecf20Sopenharmony_ci host->name = qlogicfas_name; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci if (request_irq(qlirq, qlogicfas408_ihandl, 0, qlogicfas_name, hreg)) 1158c2ecf20Sopenharmony_ci goto free_scsi_host; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci if (scsi_add_host(hreg, NULL)) 1188c2ecf20Sopenharmony_ci goto free_interrupt; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci scsi_scan_host(hreg); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci return hreg; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cifree_interrupt: 1258c2ecf20Sopenharmony_ci free_irq(qlirq, hreg); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cifree_scsi_host: 1288c2ecf20Sopenharmony_ci scsi_host_put(hreg); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cierr_release_mem: 1318c2ecf20Sopenharmony_ci release_region(qbase, 0x10); 1328c2ecf20Sopenharmony_cierr: 1338c2ecf20Sopenharmony_ci return NULL; 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci#define MAX_QLOGICFAS 8 1378c2ecf20Sopenharmony_cistatic struct qlogicfas408_priv *cards; 1388c2ecf20Sopenharmony_cistatic int iobase[MAX_QLOGICFAS]; 1398c2ecf20Sopenharmony_cistatic int irq[MAX_QLOGICFAS] = { [0 ... MAX_QLOGICFAS-1] = -1 }; 1408c2ecf20Sopenharmony_cimodule_param_hw_array(iobase, int, ioport, NULL, 0); 1418c2ecf20Sopenharmony_cimodule_param_hw_array(irq, int, irq, NULL, 0); 1428c2ecf20Sopenharmony_ciMODULE_PARM_DESC(iobase, "I/O address"); 1438c2ecf20Sopenharmony_ciMODULE_PARM_DESC(irq, "IRQ"); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic int qlogicfas_detect(struct scsi_host_template *sht) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci struct Scsi_Host *shost; 1488c2ecf20Sopenharmony_ci struct qlogicfas408_priv *priv; 1498c2ecf20Sopenharmony_ci int num; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci for (num = 0; num < MAX_QLOGICFAS; num++) { 1528c2ecf20Sopenharmony_ci shost = __qlogicfas_detect(sht, iobase[num], irq[num]); 1538c2ecf20Sopenharmony_ci if (shost == NULL) { 1548c2ecf20Sopenharmony_ci /* no more devices */ 1558c2ecf20Sopenharmony_ci break; 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci priv = get_priv_by_host(shost); 1588c2ecf20Sopenharmony_ci priv->next = cards; 1598c2ecf20Sopenharmony_ci cards = priv; 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci return num; 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic int qlogicfas_release(struct Scsi_Host *shost) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci struct qlogicfas408_priv *priv = get_priv_by_host(shost); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci scsi_remove_host(shost); 1708c2ecf20Sopenharmony_ci if (shost->irq) { 1718c2ecf20Sopenharmony_ci qlogicfas408_disable_ints(priv); 1728c2ecf20Sopenharmony_ci free_irq(shost->irq, shost); 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci if (shost->io_port && shost->n_io_port) 1758c2ecf20Sopenharmony_ci release_region(shost->io_port, shost->n_io_port); 1768c2ecf20Sopenharmony_ci scsi_host_put(shost); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci return 0; 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci/* 1828c2ecf20Sopenharmony_ci * The driver template is also needed for PCMCIA 1838c2ecf20Sopenharmony_ci */ 1848c2ecf20Sopenharmony_cistatic struct scsi_host_template qlogicfas_driver_template = { 1858c2ecf20Sopenharmony_ci .module = THIS_MODULE, 1868c2ecf20Sopenharmony_ci .name = qlogicfas_name, 1878c2ecf20Sopenharmony_ci .proc_name = qlogicfas_name, 1888c2ecf20Sopenharmony_ci .info = qlogicfas408_info, 1898c2ecf20Sopenharmony_ci .queuecommand = qlogicfas408_queuecommand, 1908c2ecf20Sopenharmony_ci .eh_abort_handler = qlogicfas408_abort, 1918c2ecf20Sopenharmony_ci .eh_host_reset_handler = qlogicfas408_host_reset, 1928c2ecf20Sopenharmony_ci .bios_param = qlogicfas408_biosparam, 1938c2ecf20Sopenharmony_ci .can_queue = 1, 1948c2ecf20Sopenharmony_ci .this_id = -1, 1958c2ecf20Sopenharmony_ci .sg_tablesize = SG_ALL, 1968c2ecf20Sopenharmony_ci .dma_boundary = PAGE_SIZE - 1, 1978c2ecf20Sopenharmony_ci}; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_cistatic __init int qlogicfas_init(void) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci if (!qlogicfas_detect(&qlogicfas_driver_template)) { 2028c2ecf20Sopenharmony_ci /* no cards found */ 2038c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: no cards were found, please specify " 2048c2ecf20Sopenharmony_ci "I/O address and IRQ using iobase= and irq= " 2058c2ecf20Sopenharmony_ci "options", qlogicfas_name); 2068c2ecf20Sopenharmony_ci return -ENODEV; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci return 0; 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic __exit void qlogicfas_exit(void) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci struct qlogicfas408_priv *priv; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci for (priv = cards; priv != NULL; priv = priv->next) 2178c2ecf20Sopenharmony_ci qlogicfas_release(priv->shost); 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ciMODULE_AUTHOR("Tom Zerucha, Michael Griffith"); 2218c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Driver for the Qlogic FAS408 based ISA card"); 2228c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 2238c2ecf20Sopenharmony_cimodule_init(qlogicfas_init); 2248c2ecf20Sopenharmony_cimodule_exit(qlogicfas_exit); 2258c2ecf20Sopenharmony_ci 226