diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/scsi/qlogicfas.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/scsi/qlogicfas.c')
-rw-r--r-- | drivers/scsi/qlogicfas.c | 230 |
1 files changed, 230 insertions, 0 deletions
diff --git a/drivers/scsi/qlogicfas.c b/drivers/scsi/qlogicfas.c new file mode 100644 index 000000000000..a1adb38f69bb --- /dev/null +++ b/drivers/scsi/qlogicfas.c | |||
@@ -0,0 +1,230 @@ | |||
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.h" | ||
35 | #include <scsi/scsi_host.h> | ||
36 | #include "qlogicfas408.h" | ||
37 | |||
38 | /* Set the following to 2 to use normal interrupt (active high/totempole- | ||
39 | * tristate), otherwise use 0 (REQUIRED FOR PCMCIA) for active low, open | ||
40 | * drain | ||
41 | */ | ||
42 | #define INT_TYPE 2 | ||
43 | |||
44 | static char qlogicfas_name[] = "qlogicfas"; | ||
45 | |||
46 | /* | ||
47 | * Look for qlogic card and init if found | ||
48 | */ | ||
49 | |||
50 | static struct Scsi_Host *__qlogicfas_detect(Scsi_Host_Template *host, | ||
51 | int qbase, | ||
52 | int qlirq) | ||
53 | { | ||
54 | int qltyp; /* type of chip */ | ||
55 | int qinitid; | ||
56 | struct Scsi_Host *hreg; /* registered host structure */ | ||
57 | struct qlogicfas408_priv *priv; | ||
58 | |||
59 | /* Qlogic Cards only exist at 0x230 or 0x330 (the chip itself | ||
60 | * decodes the address - I check 230 first since MIDI cards are | ||
61 | * typically at 0x330 | ||
62 | * | ||
63 | * Theoretically, two Qlogic cards can coexist in the same system. | ||
64 | * This should work by simply using this as a loadable module for | ||
65 | * the second card, but I haven't tested this. | ||
66 | */ | ||
67 | |||
68 | if (!qbase || qlirq == -1) | ||
69 | goto err; | ||
70 | |||
71 | if (!request_region(qbase, 0x10, qlogicfas_name)) { | ||
72 | printk(KERN_INFO "%s: address %#x is busy\n", qlogicfas_name, | ||
73 | qbase); | ||
74 | goto err; | ||
75 | } | ||
76 | |||
77 | if (!qlogicfas408_detect(qbase, INT_TYPE)) { | ||
78 | printk(KERN_WARNING "%s: probe failed for %#x\n", | ||
79 | qlogicfas_name, | ||
80 | qbase); | ||
81 | goto err_release_mem; | ||
82 | } | ||
83 | |||
84 | printk(KERN_INFO "%s: Using preset base address of %03x," | ||
85 | " IRQ %d\n", qlogicfas_name, qbase, qlirq); | ||
86 | |||
87 | qltyp = qlogicfas408_get_chip_type(qbase, INT_TYPE); | ||
88 | qinitid = host->this_id; | ||
89 | if (qinitid < 0) | ||
90 | qinitid = 7; /* if no ID, use 7 */ | ||
91 | |||
92 | qlogicfas408_setup(qbase, qinitid, INT_TYPE); | ||
93 | |||
94 | hreg = scsi_host_alloc(host, sizeof(struct qlogicfas408_priv)); | ||
95 | if (!hreg) | ||
96 | goto err_release_mem; | ||
97 | priv = get_priv_by_host(hreg); | ||
98 | hreg->io_port = qbase; | ||
99 | hreg->n_io_port = 16; | ||
100 | hreg->dma_channel = -1; | ||
101 | if (qlirq != -1) | ||
102 | hreg->irq = qlirq; | ||
103 | priv->qbase = qbase; | ||
104 | priv->qlirq = qlirq; | ||
105 | priv->qinitid = qinitid; | ||
106 | priv->shost = hreg; | ||
107 | priv->int_type = INT_TYPE; | ||
108 | |||
109 | sprintf(priv->qinfo, | ||
110 | "Qlogicfas Driver version 0.46, chip %02X at %03X, IRQ %d, TPdma:%d", | ||
111 | qltyp, qbase, qlirq, QL_TURBO_PDMA); | ||
112 | host->name = qlogicfas_name; | ||
113 | |||
114 | if (request_irq(qlirq, qlogicfas408_ihandl, 0, qlogicfas_name, hreg)) | ||
115 | goto free_scsi_host; | ||
116 | |||
117 | if (scsi_add_host(hreg, NULL)) | ||
118 | goto free_interrupt; | ||
119 | |||
120 | scsi_scan_host(hreg); | ||
121 | |||
122 | return hreg; | ||
123 | |||
124 | free_interrupt: | ||
125 | free_irq(qlirq, hreg); | ||
126 | |||
127 | free_scsi_host: | ||
128 | scsi_host_put(hreg); | ||
129 | |||
130 | err_release_mem: | ||
131 | release_region(qbase, 0x10); | ||
132 | err: | ||
133 | return NULL; | ||
134 | } | ||
135 | |||
136 | #define MAX_QLOGICFAS 8 | ||
137 | static struct qlogicfas408_priv *cards; | ||
138 | static int iobase[MAX_QLOGICFAS]; | ||
139 | static int irq[MAX_QLOGICFAS] = { [0 ... MAX_QLOGICFAS-1] = -1 }; | ||
140 | module_param_array(iobase, int, NULL, 0); | ||
141 | module_param_array(irq, int, NULL, 0); | ||
142 | MODULE_PARM_DESC(iobase, "I/O address"); | ||
143 | MODULE_PARM_DESC(irq, "IRQ"); | ||
144 | |||
145 | static int __devinit qlogicfas_detect(Scsi_Host_Template *sht) | ||
146 | { | ||
147 | struct Scsi_Host *shost; | ||
148 | struct qlogicfas408_priv *priv; | ||
149 | int num; | ||
150 | |||
151 | for (num = 0; num < MAX_QLOGICFAS; num++) { | ||
152 | shost = __qlogicfas_detect(sht, iobase[num], irq[num]); | ||
153 | if (shost == NULL) { | ||
154 | /* no more devices */ | ||
155 | break; | ||
156 | } | ||
157 | priv = get_priv_by_host(shost); | ||
158 | priv->next = cards; | ||
159 | cards = priv; | ||
160 | } | ||
161 | |||
162 | return num; | ||
163 | } | ||
164 | |||
165 | static int qlogicfas_release(struct Scsi_Host *shost) | ||
166 | { | ||
167 | struct qlogicfas408_priv *priv = get_priv_by_host(shost); | ||
168 | |||
169 | if (shost->irq) { | ||
170 | qlogicfas408_disable_ints(priv); | ||
171 | free_irq(shost->irq, shost); | ||
172 | } | ||
173 | if (shost->dma_channel != 0xff) | ||
174 | free_dma(shost->dma_channel); | ||
175 | if (shost->io_port && shost->n_io_port) | ||
176 | release_region(shost->io_port, shost->n_io_port); | ||
177 | scsi_remove_host(shost); | ||
178 | scsi_host_put(shost); | ||
179 | |||
180 | return 0; | ||
181 | } | ||
182 | |||
183 | /* | ||
184 | * The driver template is also needed for PCMCIA | ||
185 | */ | ||
186 | static Scsi_Host_Template qlogicfas_driver_template = { | ||
187 | .module = THIS_MODULE, | ||
188 | .name = qlogicfas_name, | ||
189 | .proc_name = qlogicfas_name, | ||
190 | .info = qlogicfas408_info, | ||
191 | .queuecommand = qlogicfas408_queuecommand, | ||
192 | .eh_abort_handler = qlogicfas408_abort, | ||
193 | .eh_bus_reset_handler = qlogicfas408_bus_reset, | ||
194 | .eh_device_reset_handler= qlogicfas408_device_reset, | ||
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 | .cmd_per_lun = 1, | ||
201 | .use_clustering = DISABLE_CLUSTERING, | ||
202 | }; | ||
203 | |||
204 | static __init int qlogicfas_init(void) | ||
205 | { | ||
206 | if (!qlogicfas_detect(&qlogicfas_driver_template)) { | ||
207 | /* no cards found */ | ||
208 | printk(KERN_INFO "%s: no cards were found, please specify " | ||
209 | "I/O address and IRQ using iobase= and irq= " | ||
210 | "options", qlogicfas_name); | ||
211 | return -ENODEV; | ||
212 | } | ||
213 | |||
214 | return 0; | ||
215 | } | ||
216 | |||
217 | static __exit void qlogicfas_exit(void) | ||
218 | { | ||
219 | struct qlogicfas408_priv *priv; | ||
220 | |||
221 | for (priv = cards; priv != NULL; priv = priv->next) | ||
222 | qlogicfas_release(priv->shost); | ||
223 | } | ||
224 | |||
225 | MODULE_AUTHOR("Tom Zerucha, Michael Griffith"); | ||
226 | MODULE_DESCRIPTION("Driver for the Qlogic FAS408 based ISA card"); | ||
227 | MODULE_LICENSE("GPL"); | ||
228 | module_init(qlogicfas_init); | ||
229 | module_exit(qlogicfas_exit); | ||
230 | |||