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/pcmcia/qlogic_stub.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/pcmcia/qlogic_stub.c')
-rw-r--r-- | drivers/scsi/pcmcia/qlogic_stub.c | 425 |
1 files changed, 425 insertions, 0 deletions
diff --git a/drivers/scsi/pcmcia/qlogic_stub.c b/drivers/scsi/pcmcia/qlogic_stub.c new file mode 100644 index 000000000000..4766bcd63692 --- /dev/null +++ b/drivers/scsi/pcmcia/qlogic_stub.c | |||
@@ -0,0 +1,425 @@ | |||
1 | /*====================================================================== | ||
2 | |||
3 | A driver for the Qlogic SCSI card | ||
4 | |||
5 | qlogic_cs.c 1.79 2000/06/12 21:27:26 | ||
6 | |||
7 | The contents of this file are subject to the Mozilla Public | ||
8 | License Version 1.1 (the "License"); you may not use this file | ||
9 | except in compliance with the License. You may obtain a copy of | ||
10 | the License at http://www.mozilla.org/MPL/ | ||
11 | |||
12 | Software distributed under the License is distributed on an "AS | ||
13 | IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or | ||
14 | implied. See the License for the specific language governing | ||
15 | rights and limitations under the License. | ||
16 | |||
17 | The initial developer of the original code is David A. Hinds | ||
18 | <dahinds@users.sourceforge.net>. Portions created by David A. Hinds | ||
19 | are Copyright (C) 1999 David A. Hinds. All Rights Reserved. | ||
20 | |||
21 | Alternatively, the contents of this file may be used under the | ||
22 | terms of the GNU General Public License version 2 (the "GPL"), in which | ||
23 | case the provisions of the GPL are applicable instead of the | ||
24 | above. If you wish to allow the use of your version of this file | ||
25 | only under the terms of the GPL and not to allow others to use | ||
26 | your version of this file under the MPL, indicate your decision | ||
27 | by deleting the provisions above and replace them with the notice | ||
28 | and other provisions required by the GPL. If you do not delete | ||
29 | the provisions above, a recipient may use your version of this | ||
30 | file under either the MPL or the GPL. | ||
31 | |||
32 | ======================================================================*/ | ||
33 | |||
34 | #include <linux/module.h> | ||
35 | #include <linux/init.h> | ||
36 | #include <linux/kernel.h> | ||
37 | #include <linux/sched.h> | ||
38 | #include <linux/slab.h> | ||
39 | #include <linux/string.h> | ||
40 | #include <linux/ioport.h> | ||
41 | #include <asm/io.h> | ||
42 | #include <scsi/scsi.h> | ||
43 | #include <linux/major.h> | ||
44 | #include <linux/blkdev.h> | ||
45 | #include <scsi/scsi_ioctl.h> | ||
46 | #include <linux/interrupt.h> | ||
47 | |||
48 | #include "scsi.h" | ||
49 | #include <scsi/scsi_host.h> | ||
50 | #include "../qlogicfas408.h" | ||
51 | |||
52 | #include <pcmcia/version.h> | ||
53 | #include <pcmcia/cs_types.h> | ||
54 | #include <pcmcia/cs.h> | ||
55 | #include <pcmcia/cistpl.h> | ||
56 | #include <pcmcia/ds.h> | ||
57 | #include <pcmcia/ciscode.h> | ||
58 | |||
59 | /* Set the following to 2 to use normal interrupt (active high/totempole- | ||
60 | * tristate), otherwise use 0 (REQUIRED FOR PCMCIA) for active low, open | ||
61 | * drain | ||
62 | */ | ||
63 | #define INT_TYPE 0 | ||
64 | |||
65 | static char qlogic_name[] = "qlogic_cs"; | ||
66 | |||
67 | #ifdef PCMCIA_DEBUG | ||
68 | static int pc_debug = PCMCIA_DEBUG; | ||
69 | module_param(pc_debug, int, 0644); | ||
70 | #define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args) | ||
71 | static char *version = "qlogic_cs.c 1.79-ac 2002/10/26 (David Hinds)"; | ||
72 | #else | ||
73 | #define DEBUG(n, args...) | ||
74 | #endif | ||
75 | |||
76 | static Scsi_Host_Template qlogicfas_driver_template = { | ||
77 | .module = THIS_MODULE, | ||
78 | .name = qlogic_name, | ||
79 | .proc_name = qlogic_name, | ||
80 | .info = qlogicfas408_info, | ||
81 | .queuecommand = qlogicfas408_queuecommand, | ||
82 | .eh_abort_handler = qlogicfas408_abort, | ||
83 | .eh_bus_reset_handler = qlogicfas408_bus_reset, | ||
84 | .eh_device_reset_handler= qlogicfas408_device_reset, | ||
85 | .eh_host_reset_handler = qlogicfas408_host_reset, | ||
86 | .bios_param = qlogicfas408_biosparam, | ||
87 | .can_queue = 1, | ||
88 | .this_id = -1, | ||
89 | .sg_tablesize = SG_ALL, | ||
90 | .cmd_per_lun = 1, | ||
91 | .use_clustering = DISABLE_CLUSTERING, | ||
92 | }; | ||
93 | |||
94 | /*====================================================================*/ | ||
95 | |||
96 | typedef struct scsi_info_t { | ||
97 | dev_link_t link; | ||
98 | dev_node_t node; | ||
99 | struct Scsi_Host *host; | ||
100 | unsigned short manf_id; | ||
101 | } scsi_info_t; | ||
102 | |||
103 | static void qlogic_release(dev_link_t *link); | ||
104 | static int qlogic_event(event_t event, int priority, event_callback_args_t * args); | ||
105 | |||
106 | static dev_link_t *qlogic_attach(void); | ||
107 | static void qlogic_detach(dev_link_t *); | ||
108 | |||
109 | |||
110 | static dev_link_t *dev_list = NULL; | ||
111 | |||
112 | static dev_info_t dev_info = "qlogic_cs"; | ||
113 | |||
114 | static struct Scsi_Host *qlogic_detect(Scsi_Host_Template *host, | ||
115 | dev_link_t *link, int qbase, int qlirq) | ||
116 | { | ||
117 | int qltyp; /* type of chip */ | ||
118 | int qinitid; | ||
119 | struct Scsi_Host *shost; /* registered host structure */ | ||
120 | struct qlogicfas408_priv *priv; | ||
121 | |||
122 | qltyp = qlogicfas408_get_chip_type(qbase, INT_TYPE); | ||
123 | qinitid = host->this_id; | ||
124 | if (qinitid < 0) | ||
125 | qinitid = 7; /* if no ID, use 7 */ | ||
126 | |||
127 | qlogicfas408_setup(qbase, qinitid, INT_TYPE); | ||
128 | |||
129 | host->name = qlogic_name; | ||
130 | shost = scsi_host_alloc(host, sizeof(struct qlogicfas408_priv)); | ||
131 | if (!shost) | ||
132 | goto err; | ||
133 | shost->io_port = qbase; | ||
134 | shost->n_io_port = 16; | ||
135 | shost->dma_channel = -1; | ||
136 | if (qlirq != -1) | ||
137 | shost->irq = qlirq; | ||
138 | |||
139 | priv = get_priv_by_host(shost); | ||
140 | priv->qlirq = qlirq; | ||
141 | priv->qbase = qbase; | ||
142 | priv->qinitid = qinitid; | ||
143 | priv->shost = shost; | ||
144 | priv->int_type = INT_TYPE; | ||
145 | |||
146 | if (request_irq(qlirq, qlogicfas408_ihandl, 0, qlogic_name, shost)) | ||
147 | goto free_scsi_host; | ||
148 | |||
149 | sprintf(priv->qinfo, | ||
150 | "Qlogicfas Driver version 0.46, chip %02X at %03X, IRQ %d, TPdma:%d", | ||
151 | qltyp, qbase, qlirq, QL_TURBO_PDMA); | ||
152 | |||
153 | if (scsi_add_host(shost, NULL)) | ||
154 | goto free_interrupt; | ||
155 | |||
156 | scsi_scan_host(shost); | ||
157 | |||
158 | return shost; | ||
159 | |||
160 | free_interrupt: | ||
161 | free_irq(qlirq, shost); | ||
162 | |||
163 | free_scsi_host: | ||
164 | scsi_host_put(shost); | ||
165 | |||
166 | err: | ||
167 | return NULL; | ||
168 | } | ||
169 | static dev_link_t *qlogic_attach(void) | ||
170 | { | ||
171 | scsi_info_t *info; | ||
172 | client_reg_t client_reg; | ||
173 | dev_link_t *link; | ||
174 | int ret; | ||
175 | |||
176 | DEBUG(0, "qlogic_attach()\n"); | ||
177 | |||
178 | /* Create new SCSI device */ | ||
179 | info = kmalloc(sizeof(*info), GFP_KERNEL); | ||
180 | if (!info) | ||
181 | return NULL; | ||
182 | memset(info, 0, sizeof(*info)); | ||
183 | link = &info->link; | ||
184 | link->priv = info; | ||
185 | link->io.NumPorts1 = 16; | ||
186 | link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; | ||
187 | link->io.IOAddrLines = 10; | ||
188 | link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; | ||
189 | link->irq.IRQInfo1 = IRQ_LEVEL_ID; | ||
190 | link->conf.Attributes = CONF_ENABLE_IRQ; | ||
191 | link->conf.Vcc = 50; | ||
192 | link->conf.IntType = INT_MEMORY_AND_IO; | ||
193 | link->conf.Present = PRESENT_OPTION; | ||
194 | |||
195 | /* Register with Card Services */ | ||
196 | link->next = dev_list; | ||
197 | dev_list = link; | ||
198 | client_reg.dev_info = &dev_info; | ||
199 | client_reg.event_handler = &qlogic_event; | ||
200 | client_reg.EventMask = CS_EVENT_RESET_REQUEST | CS_EVENT_CARD_RESET | CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; | ||
201 | client_reg.Version = 0x0210; | ||
202 | client_reg.event_callback_args.client_data = link; | ||
203 | ret = pcmcia_register_client(&link->handle, &client_reg); | ||
204 | if (ret != 0) { | ||
205 | cs_error(link->handle, RegisterClient, ret); | ||
206 | qlogic_detach(link); | ||
207 | return NULL; | ||
208 | } | ||
209 | |||
210 | return link; | ||
211 | } /* qlogic_attach */ | ||
212 | |||
213 | /*====================================================================*/ | ||
214 | |||
215 | static void qlogic_detach(dev_link_t * link) | ||
216 | { | ||
217 | dev_link_t **linkp; | ||
218 | |||
219 | DEBUG(0, "qlogic_detach(0x%p)\n", link); | ||
220 | |||
221 | /* Locate device structure */ | ||
222 | for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) | ||
223 | if (*linkp == link) | ||
224 | break; | ||
225 | if (*linkp == NULL) | ||
226 | return; | ||
227 | |||
228 | if (link->state & DEV_CONFIG) | ||
229 | qlogic_release(link); | ||
230 | |||
231 | if (link->handle) | ||
232 | pcmcia_deregister_client(link->handle); | ||
233 | |||
234 | /* Unlink device structure, free bits */ | ||
235 | *linkp = link->next; | ||
236 | kfree(link->priv); | ||
237 | |||
238 | } /* qlogic_detach */ | ||
239 | |||
240 | /*====================================================================*/ | ||
241 | |||
242 | #define CS_CHECK(fn, ret) \ | ||
243 | do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) | ||
244 | |||
245 | static void qlogic_config(dev_link_t * link) | ||
246 | { | ||
247 | client_handle_t handle = link->handle; | ||
248 | scsi_info_t *info = link->priv; | ||
249 | tuple_t tuple; | ||
250 | cisparse_t parse; | ||
251 | int i, last_ret, last_fn; | ||
252 | unsigned short tuple_data[32]; | ||
253 | struct Scsi_Host *host; | ||
254 | |||
255 | DEBUG(0, "qlogic_config(0x%p)\n", link); | ||
256 | |||
257 | tuple.TupleData = (cisdata_t *) tuple_data; | ||
258 | tuple.TupleDataMax = 64; | ||
259 | tuple.TupleOffset = 0; | ||
260 | tuple.DesiredTuple = CISTPL_CONFIG; | ||
261 | CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); | ||
262 | CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple)); | ||
263 | CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse)); | ||
264 | link->conf.ConfigBase = parse.config.base; | ||
265 | |||
266 | tuple.DesiredTuple = CISTPL_MANFID; | ||
267 | if ((pcmcia_get_first_tuple(handle, &tuple) == CS_SUCCESS) && (pcmcia_get_tuple_data(handle, &tuple) == CS_SUCCESS)) | ||
268 | info->manf_id = le16_to_cpu(tuple.TupleData[0]); | ||
269 | |||
270 | /* Configure card */ | ||
271 | link->state |= DEV_CONFIG; | ||
272 | |||
273 | tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; | ||
274 | CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); | ||
275 | while (1) { | ||
276 | if (pcmcia_get_tuple_data(handle, &tuple) != 0 || | ||
277 | pcmcia_parse_tuple(handle, &tuple, &parse) != 0) | ||
278 | goto next_entry; | ||
279 | link->conf.ConfigIndex = parse.cftable_entry.index; | ||
280 | link->io.BasePort1 = parse.cftable_entry.io.win[0].base; | ||
281 | link->io.NumPorts1 = parse.cftable_entry.io.win[0].len; | ||
282 | if (link->io.BasePort1 != 0) { | ||
283 | i = pcmcia_request_io(handle, &link->io); | ||
284 | if (i == CS_SUCCESS) | ||
285 | break; | ||
286 | } | ||
287 | next_entry: | ||
288 | CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple)); | ||
289 | } | ||
290 | |||
291 | CS_CHECK(RequestIRQ, pcmcia_request_irq(handle, &link->irq)); | ||
292 | CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf)); | ||
293 | |||
294 | if ((info->manf_id == MANFID_MACNICA) || (info->manf_id == MANFID_PIONEER) || (info->manf_id == 0x0098)) { | ||
295 | /* set ATAcmd */ | ||
296 | outb(0xb4, link->io.BasePort1 + 0xd); | ||
297 | outb(0x24, link->io.BasePort1 + 0x9); | ||
298 | outb(0x04, link->io.BasePort1 + 0xd); | ||
299 | } | ||
300 | |||
301 | /* The KXL-810AN has a bigger IO port window */ | ||
302 | if (link->io.NumPorts1 == 32) | ||
303 | host = qlogic_detect(&qlogicfas_driver_template, link, | ||
304 | link->io.BasePort1 + 16, link->irq.AssignedIRQ); | ||
305 | else | ||
306 | host = qlogic_detect(&qlogicfas_driver_template, link, | ||
307 | link->io.BasePort1, link->irq.AssignedIRQ); | ||
308 | |||
309 | if (!host) { | ||
310 | printk(KERN_INFO "%s: no SCSI devices found\n", qlogic_name); | ||
311 | goto out; | ||
312 | } | ||
313 | |||
314 | sprintf(info->node.dev_name, "scsi%d", host->host_no); | ||
315 | link->dev = &info->node; | ||
316 | info->host = host; | ||
317 | |||
318 | out: | ||
319 | link->state &= ~DEV_CONFIG_PENDING; | ||
320 | return; | ||
321 | |||
322 | cs_failed: | ||
323 | cs_error(link->handle, last_fn, last_ret); | ||
324 | link->dev = NULL; | ||
325 | pcmcia_release_configuration(link->handle); | ||
326 | pcmcia_release_io(link->handle, &link->io); | ||
327 | pcmcia_release_irq(link->handle, &link->irq); | ||
328 | link->state &= ~DEV_CONFIG; | ||
329 | return; | ||
330 | |||
331 | } /* qlogic_config */ | ||
332 | |||
333 | /*====================================================================*/ | ||
334 | |||
335 | static void qlogic_release(dev_link_t *link) | ||
336 | { | ||
337 | scsi_info_t *info = link->priv; | ||
338 | |||
339 | DEBUG(0, "qlogic_release(0x%p)\n", link); | ||
340 | |||
341 | scsi_remove_host(info->host); | ||
342 | link->dev = NULL; | ||
343 | |||
344 | free_irq(link->irq.AssignedIRQ, info->host); | ||
345 | |||
346 | pcmcia_release_configuration(link->handle); | ||
347 | pcmcia_release_io(link->handle, &link->io); | ||
348 | pcmcia_release_irq(link->handle, &link->irq); | ||
349 | |||
350 | scsi_host_put(info->host); | ||
351 | |||
352 | link->state &= ~DEV_CONFIG; | ||
353 | } | ||
354 | |||
355 | /*====================================================================*/ | ||
356 | |||
357 | static int qlogic_event(event_t event, int priority, event_callback_args_t * args) | ||
358 | { | ||
359 | dev_link_t *link = args->client_data; | ||
360 | |||
361 | DEBUG(1, "qlogic_event(0x%06x)\n", event); | ||
362 | |||
363 | switch (event) { | ||
364 | case CS_EVENT_CARD_REMOVAL: | ||
365 | link->state &= ~DEV_PRESENT; | ||
366 | if (link->state & DEV_CONFIG) | ||
367 | qlogic_release(link); | ||
368 | break; | ||
369 | case CS_EVENT_CARD_INSERTION: | ||
370 | link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; | ||
371 | qlogic_config(link); | ||
372 | break; | ||
373 | case CS_EVENT_PM_SUSPEND: | ||
374 | link->state |= DEV_SUSPEND; | ||
375 | /* Fall through... */ | ||
376 | case CS_EVENT_RESET_PHYSICAL: | ||
377 | if (link->state & DEV_CONFIG) | ||
378 | pcmcia_release_configuration(link->handle); | ||
379 | break; | ||
380 | case CS_EVENT_PM_RESUME: | ||
381 | link->state &= ~DEV_SUSPEND; | ||
382 | /* Fall through... */ | ||
383 | case CS_EVENT_CARD_RESET: | ||
384 | if (link->state & DEV_CONFIG) { | ||
385 | scsi_info_t *info = link->priv; | ||
386 | pcmcia_request_configuration(link->handle, &link->conf); | ||
387 | if ((info->manf_id == MANFID_MACNICA) || (info->manf_id == MANFID_PIONEER) || (info->manf_id == 0x0098)) { | ||
388 | outb(0x80, link->io.BasePort1 + 0xd); | ||
389 | outb(0x24, link->io.BasePort1 + 0x9); | ||
390 | outb(0x04, link->io.BasePort1 + 0xd); | ||
391 | } | ||
392 | /* Ugggglllyyyy!!! */ | ||
393 | qlogicfas408_bus_reset(NULL); | ||
394 | } | ||
395 | break; | ||
396 | } | ||
397 | return 0; | ||
398 | } /* qlogic_event */ | ||
399 | |||
400 | |||
401 | static struct pcmcia_driver qlogic_cs_driver = { | ||
402 | .owner = THIS_MODULE, | ||
403 | .drv = { | ||
404 | .name = "qlogic_cs", | ||
405 | }, | ||
406 | .attach = qlogic_attach, | ||
407 | .detach = qlogic_detach, | ||
408 | }; | ||
409 | |||
410 | static int __init init_qlogic_cs(void) | ||
411 | { | ||
412 | return pcmcia_register_driver(&qlogic_cs_driver); | ||
413 | } | ||
414 | |||
415 | static void __exit exit_qlogic_cs(void) | ||
416 | { | ||
417 | pcmcia_unregister_driver(&qlogic_cs_driver); | ||
418 | BUG_ON(dev_list != NULL); | ||
419 | } | ||
420 | |||
421 | MODULE_AUTHOR("Tom Zerucha, Michael Griffith"); | ||
422 | MODULE_DESCRIPTION("Driver for the PCMCIA Qlogic FAS SCSI controllers"); | ||
423 | MODULE_LICENSE("GPL"); | ||
424 | module_init(init_qlogic_cs); | ||
425 | module_exit(exit_qlogic_cs); | ||