Commit | Line | Data |
---|---|---|
1da177e4 LT |
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 | ||
53555fb7 BVA |
34 | #include <scsi/scsi.h> |
35 | #include <scsi/scsi_cmnd.h> | |
36 | #include <scsi/scsi_device.h> | |
37 | #include <scsi/scsi_eh.h> | |
1da177e4 | 38 | #include <scsi/scsi_host.h> |
53555fb7 | 39 | #include <scsi/scsi_tcq.h> |
1da177e4 LT |
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 | ||
d0be4a7d | 54 | static struct Scsi_Host *__qlogicfas_detect(struct scsi_host_template *host, |
1da177e4 LT |
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 }; | |
88f06b76 DH |
144 | module_param_hw_array(iobase, int, ioport, NULL, 0); |
145 | module_param_hw_array(irq, int, irq, NULL, 0); | |
1da177e4 LT |
146 | MODULE_PARM_DESC(iobase, "I/O address"); |
147 | MODULE_PARM_DESC(irq, "IRQ"); | |
148 | ||
6f039790 | 149 | static int qlogicfas_detect(struct scsi_host_template *sht) |
1da177e4 LT |
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 | ||
c131993b | 173 | scsi_remove_host(shost); |
1da177e4 LT |
174 | if (shost->irq) { |
175 | qlogicfas408_disable_ints(priv); | |
176 | free_irq(shost->irq, shost); | |
177 | } | |
1da177e4 LT |
178 | if (shost->io_port && shost->n_io_port) |
179 | release_region(shost->io_port, shost->n_io_port); | |
1da177e4 LT |
180 | scsi_host_put(shost); |
181 | ||
182 | return 0; | |
183 | } | |
184 | ||
185 | /* | |
186 | * The driver template is also needed for PCMCIA | |
187 | */ | |
d0be4a7d | 188 | static struct scsi_host_template qlogicfas_driver_template = { |
1da177e4 LT |
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, | |
4a56c1c1 | 195 | .eh_host_reset_handler = qlogicfas408_host_reset, |
1da177e4 LT |
196 | .bios_param = qlogicfas408_biosparam, |
197 | .can_queue = 1, | |
198 | .this_id = -1, | |
199 | .sg_tablesize = SG_ALL, | |
4af14d11 | 200 | .dma_boundary = PAGE_SIZE - 1, |
1da177e4 LT |
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 |