]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blob - drivers/scsi/pcmcia/qlogic_stub.c
Auto merge with /home/aegl/GIT/ia64-test
[mirror_ubuntu-artful-kernel.git] / drivers / scsi / pcmcia / qlogic_stub.c
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 .bios_param = qlogicfas408_biosparam,
85 .can_queue = 1,
86 .this_id = -1,
87 .sg_tablesize = SG_ALL,
88 .cmd_per_lun = 1,
89 .use_clustering = DISABLE_CLUSTERING,
90 };
91
92 /*====================================================================*/
93
94 typedef struct scsi_info_t {
95 dev_link_t link;
96 dev_node_t node;
97 struct Scsi_Host *host;
98 unsigned short manf_id;
99 } scsi_info_t;
100
101 static void qlogic_release(dev_link_t *link);
102 static int qlogic_event(event_t event, int priority, event_callback_args_t * args);
103
104 static dev_link_t *qlogic_attach(void);
105 static void qlogic_detach(dev_link_t *);
106
107
108 static dev_link_t *dev_list = NULL;
109
110 static dev_info_t dev_info = "qlogic_cs";
111
112 static struct Scsi_Host *qlogic_detect(Scsi_Host_Template *host,
113 dev_link_t *link, int qbase, int qlirq)
114 {
115 int qltyp; /* type of chip */
116 int qinitid;
117 struct Scsi_Host *shost; /* registered host structure */
118 struct qlogicfas408_priv *priv;
119
120 qltyp = qlogicfas408_get_chip_type(qbase, INT_TYPE);
121 qinitid = host->this_id;
122 if (qinitid < 0)
123 qinitid = 7; /* if no ID, use 7 */
124
125 qlogicfas408_setup(qbase, qinitid, INT_TYPE);
126
127 host->name = qlogic_name;
128 shost = scsi_host_alloc(host, sizeof(struct qlogicfas408_priv));
129 if (!shost)
130 goto err;
131 shost->io_port = qbase;
132 shost->n_io_port = 16;
133 shost->dma_channel = -1;
134 if (qlirq != -1)
135 shost->irq = qlirq;
136
137 priv = get_priv_by_host(shost);
138 priv->qlirq = qlirq;
139 priv->qbase = qbase;
140 priv->qinitid = qinitid;
141 priv->shost = shost;
142 priv->int_type = INT_TYPE;
143
144 if (request_irq(qlirq, qlogicfas408_ihandl, 0, qlogic_name, shost))
145 goto free_scsi_host;
146
147 sprintf(priv->qinfo,
148 "Qlogicfas Driver version 0.46, chip %02X at %03X, IRQ %d, TPdma:%d",
149 qltyp, qbase, qlirq, QL_TURBO_PDMA);
150
151 if (scsi_add_host(shost, NULL))
152 goto free_interrupt;
153
154 scsi_scan_host(shost);
155
156 return shost;
157
158 free_interrupt:
159 free_irq(qlirq, shost);
160
161 free_scsi_host:
162 scsi_host_put(shost);
163
164 err:
165 return NULL;
166 }
167 static dev_link_t *qlogic_attach(void)
168 {
169 scsi_info_t *info;
170 client_reg_t client_reg;
171 dev_link_t *link;
172 int ret;
173
174 DEBUG(0, "qlogic_attach()\n");
175
176 /* Create new SCSI device */
177 info = kmalloc(sizeof(*info), GFP_KERNEL);
178 if (!info)
179 return NULL;
180 memset(info, 0, sizeof(*info));
181 link = &info->link;
182 link->priv = info;
183 link->io.NumPorts1 = 16;
184 link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
185 link->io.IOAddrLines = 10;
186 link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
187 link->irq.IRQInfo1 = IRQ_LEVEL_ID;
188 link->conf.Attributes = CONF_ENABLE_IRQ;
189 link->conf.Vcc = 50;
190 link->conf.IntType = INT_MEMORY_AND_IO;
191 link->conf.Present = PRESENT_OPTION;
192
193 /* Register with Card Services */
194 link->next = dev_list;
195 dev_list = link;
196 client_reg.dev_info = &dev_info;
197 client_reg.event_handler = &qlogic_event;
198 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;
199 client_reg.Version = 0x0210;
200 client_reg.event_callback_args.client_data = link;
201 ret = pcmcia_register_client(&link->handle, &client_reg);
202 if (ret != 0) {
203 cs_error(link->handle, RegisterClient, ret);
204 qlogic_detach(link);
205 return NULL;
206 }
207
208 return link;
209 } /* qlogic_attach */
210
211 /*====================================================================*/
212
213 static void qlogic_detach(dev_link_t * link)
214 {
215 dev_link_t **linkp;
216
217 DEBUG(0, "qlogic_detach(0x%p)\n", link);
218
219 /* Locate device structure */
220 for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
221 if (*linkp == link)
222 break;
223 if (*linkp == NULL)
224 return;
225
226 if (link->state & DEV_CONFIG)
227 qlogic_release(link);
228
229 if (link->handle)
230 pcmcia_deregister_client(link->handle);
231
232 /* Unlink device structure, free bits */
233 *linkp = link->next;
234 kfree(link->priv);
235
236 } /* qlogic_detach */
237
238 /*====================================================================*/
239
240 #define CS_CHECK(fn, ret) \
241 do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
242
243 static void qlogic_config(dev_link_t * link)
244 {
245 client_handle_t handle = link->handle;
246 scsi_info_t *info = link->priv;
247 tuple_t tuple;
248 cisparse_t parse;
249 int i, last_ret, last_fn;
250 unsigned short tuple_data[32];
251 struct Scsi_Host *host;
252
253 DEBUG(0, "qlogic_config(0x%p)\n", link);
254
255 tuple.TupleData = (cisdata_t *) tuple_data;
256 tuple.TupleDataMax = 64;
257 tuple.TupleOffset = 0;
258 tuple.DesiredTuple = CISTPL_CONFIG;
259 CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
260 CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
261 CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
262 link->conf.ConfigBase = parse.config.base;
263
264 tuple.DesiredTuple = CISTPL_MANFID;
265 if ((pcmcia_get_first_tuple(handle, &tuple) == CS_SUCCESS) && (pcmcia_get_tuple_data(handle, &tuple) == CS_SUCCESS))
266 info->manf_id = le16_to_cpu(tuple.TupleData[0]);
267
268 /* Configure card */
269 link->state |= DEV_CONFIG;
270
271 tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
272 CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
273 while (1) {
274 if (pcmcia_get_tuple_data(handle, &tuple) != 0 ||
275 pcmcia_parse_tuple(handle, &tuple, &parse) != 0)
276 goto next_entry;
277 link->conf.ConfigIndex = parse.cftable_entry.index;
278 link->io.BasePort1 = parse.cftable_entry.io.win[0].base;
279 link->io.NumPorts1 = parse.cftable_entry.io.win[0].len;
280 if (link->io.BasePort1 != 0) {
281 i = pcmcia_request_io(handle, &link->io);
282 if (i == CS_SUCCESS)
283 break;
284 }
285 next_entry:
286 CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple));
287 }
288
289 CS_CHECK(RequestIRQ, pcmcia_request_irq(handle, &link->irq));
290 CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf));
291
292 if ((info->manf_id == MANFID_MACNICA) || (info->manf_id == MANFID_PIONEER) || (info->manf_id == 0x0098)) {
293 /* set ATAcmd */
294 outb(0xb4, link->io.BasePort1 + 0xd);
295 outb(0x24, link->io.BasePort1 + 0x9);
296 outb(0x04, link->io.BasePort1 + 0xd);
297 }
298
299 /* The KXL-810AN has a bigger IO port window */
300 if (link->io.NumPorts1 == 32)
301 host = qlogic_detect(&qlogicfas_driver_template, link,
302 link->io.BasePort1 + 16, link->irq.AssignedIRQ);
303 else
304 host = qlogic_detect(&qlogicfas_driver_template, link,
305 link->io.BasePort1, link->irq.AssignedIRQ);
306
307 if (!host) {
308 printk(KERN_INFO "%s: no SCSI devices found\n", qlogic_name);
309 goto out;
310 }
311
312 sprintf(info->node.dev_name, "scsi%d", host->host_no);
313 link->dev = &info->node;
314 info->host = host;
315
316 out:
317 link->state &= ~DEV_CONFIG_PENDING;
318 return;
319
320 cs_failed:
321 cs_error(link->handle, last_fn, last_ret);
322 link->dev = NULL;
323 pcmcia_release_configuration(link->handle);
324 pcmcia_release_io(link->handle, &link->io);
325 pcmcia_release_irq(link->handle, &link->irq);
326 link->state &= ~DEV_CONFIG;
327 return;
328
329 } /* qlogic_config */
330
331 /*====================================================================*/
332
333 static void qlogic_release(dev_link_t *link)
334 {
335 scsi_info_t *info = link->priv;
336
337 DEBUG(0, "qlogic_release(0x%p)\n", link);
338
339 scsi_remove_host(info->host);
340 link->dev = NULL;
341
342 free_irq(link->irq.AssignedIRQ, info->host);
343
344 pcmcia_release_configuration(link->handle);
345 pcmcia_release_io(link->handle, &link->io);
346 pcmcia_release_irq(link->handle, &link->irq);
347
348 scsi_host_put(info->host);
349
350 link->state &= ~DEV_CONFIG;
351 }
352
353 /*====================================================================*/
354
355 static int qlogic_event(event_t event, int priority, event_callback_args_t * args)
356 {
357 dev_link_t *link = args->client_data;
358
359 DEBUG(1, "qlogic_event(0x%06x)\n", event);
360
361 switch (event) {
362 case CS_EVENT_CARD_REMOVAL:
363 link->state &= ~DEV_PRESENT;
364 if (link->state & DEV_CONFIG)
365 qlogic_release(link);
366 break;
367 case CS_EVENT_CARD_INSERTION:
368 link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
369 qlogic_config(link);
370 break;
371 case CS_EVENT_PM_SUSPEND:
372 link->state |= DEV_SUSPEND;
373 /* Fall through... */
374 case CS_EVENT_RESET_PHYSICAL:
375 if (link->state & DEV_CONFIG)
376 pcmcia_release_configuration(link->handle);
377 break;
378 case CS_EVENT_PM_RESUME:
379 link->state &= ~DEV_SUSPEND;
380 /* Fall through... */
381 case CS_EVENT_CARD_RESET:
382 if (link->state & DEV_CONFIG) {
383 scsi_info_t *info = link->priv;
384 pcmcia_request_configuration(link->handle, &link->conf);
385 if ((info->manf_id == MANFID_MACNICA) || (info->manf_id == MANFID_PIONEER) || (info->manf_id == 0x0098)) {
386 outb(0x80, link->io.BasePort1 + 0xd);
387 outb(0x24, link->io.BasePort1 + 0x9);
388 outb(0x04, link->io.BasePort1 + 0xd);
389 }
390 /* Ugggglllyyyy!!! */
391 qlogicfas408_bus_reset(NULL);
392 }
393 break;
394 }
395 return 0;
396 } /* qlogic_event */
397
398 static struct pcmcia_device_id qlogic_ids[] = {
399 PCMCIA_DEVICE_PROD_ID12("EIger Labs", "PCMCIA-to-SCSI Adapter", 0x88395fa7, 0x33b7a5e6),
400 PCMCIA_DEVICE_PROD_ID12("EPSON", "SCSI-2 PC Card SC200", 0xd361772f, 0x299d1751),
401 PCMCIA_DEVICE_PROD_ID12("MACNICA", "MIRACLE SCSI-II mPS110", 0x20841b68, 0xab3c3b6d),
402 PCMCIA_DEVICE_PROD_ID12("MIDORI ELECTRONICS ", "CN-SC43", 0x6534382a, 0xd67eee79),
403 PCMCIA_DEVICE_PROD_ID12("NEC", "PC-9801N-J03R", 0x18df0ba0, 0x24662e8a),
404 PCMCIA_DEVICE_PROD_ID12("KME ", "KXLC003", 0x82375a27, 0xf68e5bf7),
405 PCMCIA_DEVICE_PROD_ID12("KME ", "KXLC004", 0x82375a27, 0x68eace54),
406 PCMCIA_DEVICE_PROD_ID12("KME", "KXLC101", 0x3faee676, 0x194250ec),
407 PCMCIA_DEVICE_PROD_ID12("QLOGIC CORPORATION", "pc05", 0xd77b2930, 0xa85b2735),
408 PCMCIA_DEVICE_PROD_ID12("QLOGIC CORPORATION", "pc05 rev 1.10", 0xd77b2930, 0x70f8b5f8),
409 PCMCIA_DEVICE_PROD_ID123("KME", "KXLC002", "00", 0x3faee676, 0x81896b61, 0xf99f065f),
410 PCMCIA_DEVICE_PROD_ID12("RATOC System Inc.", "SCSI2 CARD 37", 0x85c10e17, 0x1a2640c1),
411 PCMCIA_DEVICE_PROD_ID12("TOSHIBA", "SCSC200A PC CARD SCSI", 0xb4585a1a, 0xa6f06ebe),
412 PCMCIA_DEVICE_PROD_ID12("TOSHIBA", "SCSC200B PC CARD SCSI-10", 0xb4585a1a, 0x0a88dea0),
413 /* these conflict with other cards! */
414 /* PCMCIA_DEVICE_PROD_ID123("MACNICA", "MIRACLE SCSI", "mPS100", 0x20841b68, 0xf8dedaeb, 0x89f7fafb), */
415 /* PCMCIA_DEVICE_PROD_ID123("MACNICA", "MIRACLE SCSI", "mPS100", 0x20841b68, 0xf8dedaeb, 0x89f7fafb), */
416 PCMCIA_DEVICE_NULL,
417 };
418 MODULE_DEVICE_TABLE(pcmcia, qlogic_ids);
419
420 static struct pcmcia_driver qlogic_cs_driver = {
421 .owner = THIS_MODULE,
422 .drv = {
423 .name = "qlogic_cs",
424 },
425 .attach = qlogic_attach,
426 .detach = qlogic_detach,
427 .id_table = qlogic_ids,
428 };
429
430 static int __init init_qlogic_cs(void)
431 {
432 return pcmcia_register_driver(&qlogic_cs_driver);
433 }
434
435 static void __exit exit_qlogic_cs(void)
436 {
437 pcmcia_unregister_driver(&qlogic_cs_driver);
438 BUG_ON(dev_list != NULL);
439 }
440
441 MODULE_AUTHOR("Tom Zerucha, Michael Griffith");
442 MODULE_DESCRIPTION("Driver for the PCMCIA Qlogic FAS SCSI controllers");
443 MODULE_LICENSE("GPL");
444 module_init(init_qlogic_cs);
445 module_exit(exit_qlogic_cs);