]> err.no Git - linux-2.6/blob - drivers/scsi/pcmcia/qlogic_stub.c
[PATCH] pcmcia: move event handler
[linux-2.6] / 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.Version = 0x0210;
198         client_reg.event_callback_args.client_data = link;
199         ret = pcmcia_register_client(&link->handle, &client_reg);
200         if (ret != 0) {
201                 cs_error(link->handle, RegisterClient, ret);
202                 qlogic_detach(link);
203                 return NULL;
204         }
205
206         return link;
207 }                               /* qlogic_attach */
208
209 /*====================================================================*/
210
211 static void qlogic_detach(dev_link_t * link)
212 {
213         dev_link_t **linkp;
214
215         DEBUG(0, "qlogic_detach(0x%p)\n", link);
216
217         /* Locate device structure */
218         for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
219                 if (*linkp == link)
220                         break;
221         if (*linkp == NULL)
222                 return;
223
224         if (link->state & DEV_CONFIG)
225                 qlogic_release(link);
226
227         if (link->handle)
228                 pcmcia_deregister_client(link->handle);
229
230         /* Unlink device structure, free bits */
231         *linkp = link->next;
232         kfree(link->priv);
233
234 }                               /* qlogic_detach */
235
236 /*====================================================================*/
237
238 #define CS_CHECK(fn, ret) \
239 do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
240
241 static void qlogic_config(dev_link_t * link)
242 {
243         client_handle_t handle = link->handle;
244         scsi_info_t *info = link->priv;
245         tuple_t tuple;
246         cisparse_t parse;
247         int i, last_ret, last_fn;
248         unsigned short tuple_data[32];
249         struct Scsi_Host *host;
250
251         DEBUG(0, "qlogic_config(0x%p)\n", link);
252
253         tuple.TupleData = (cisdata_t *) tuple_data;
254         tuple.TupleDataMax = 64;
255         tuple.TupleOffset = 0;
256         tuple.DesiredTuple = CISTPL_CONFIG;
257         CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
258         CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
259         CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
260         link->conf.ConfigBase = parse.config.base;
261
262         tuple.DesiredTuple = CISTPL_MANFID;
263         if ((pcmcia_get_first_tuple(handle, &tuple) == CS_SUCCESS) && (pcmcia_get_tuple_data(handle, &tuple) == CS_SUCCESS))
264                 info->manf_id = le16_to_cpu(tuple.TupleData[0]);
265
266         /* Configure card */
267         link->state |= DEV_CONFIG;
268
269         tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
270         CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
271         while (1) {
272                 if (pcmcia_get_tuple_data(handle, &tuple) != 0 ||
273                                 pcmcia_parse_tuple(handle, &tuple, &parse) != 0)
274                         goto next_entry;
275                 link->conf.ConfigIndex = parse.cftable_entry.index;
276                 link->io.BasePort1 = parse.cftable_entry.io.win[0].base;
277                 link->io.NumPorts1 = parse.cftable_entry.io.win[0].len;
278                 if (link->io.BasePort1 != 0) {
279                         i = pcmcia_request_io(handle, &link->io);
280                         if (i == CS_SUCCESS)
281                                 break;
282                 }
283               next_entry:
284                 CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple));
285         }
286
287         CS_CHECK(RequestIRQ, pcmcia_request_irq(handle, &link->irq));
288         CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf));
289
290         if ((info->manf_id == MANFID_MACNICA) || (info->manf_id == MANFID_PIONEER) || (info->manf_id == 0x0098)) {
291                 /* set ATAcmd */
292                 outb(0xb4, link->io.BasePort1 + 0xd);
293                 outb(0x24, link->io.BasePort1 + 0x9);
294                 outb(0x04, link->io.BasePort1 + 0xd);
295         }
296
297         /* The KXL-810AN has a bigger IO port window */
298         if (link->io.NumPorts1 == 32)
299                 host = qlogic_detect(&qlogicfas_driver_template, link,
300                         link->io.BasePort1 + 16, link->irq.AssignedIRQ);
301         else
302                 host = qlogic_detect(&qlogicfas_driver_template, link,
303                         link->io.BasePort1, link->irq.AssignedIRQ);
304         
305         if (!host) {
306                 printk(KERN_INFO "%s: no SCSI devices found\n", qlogic_name);
307                 goto out;
308         }
309
310         sprintf(info->node.dev_name, "scsi%d", host->host_no);
311         link->dev = &info->node;
312         info->host = host;
313
314 out:
315         link->state &= ~DEV_CONFIG_PENDING;
316         return;
317
318 cs_failed:
319         cs_error(link->handle, last_fn, last_ret);
320         link->dev = NULL;
321         pcmcia_release_configuration(link->handle);
322         pcmcia_release_io(link->handle, &link->io);
323         pcmcia_release_irq(link->handle, &link->irq);
324         link->state &= ~DEV_CONFIG;
325         return;
326
327 }                               /* qlogic_config */
328
329 /*====================================================================*/
330
331 static void qlogic_release(dev_link_t *link)
332 {
333         scsi_info_t *info = link->priv;
334
335         DEBUG(0, "qlogic_release(0x%p)\n", link);
336
337         scsi_remove_host(info->host);
338         link->dev = NULL;
339
340         free_irq(link->irq.AssignedIRQ, info->host);
341
342         pcmcia_release_configuration(link->handle);
343         pcmcia_release_io(link->handle, &link->io);
344         pcmcia_release_irq(link->handle, &link->irq);
345
346         scsi_host_put(info->host);
347
348         link->state &= ~DEV_CONFIG;
349 }
350
351 /*====================================================================*/
352
353 static int qlogic_event(event_t event, int priority, event_callback_args_t * args)
354 {
355         dev_link_t *link = args->client_data;
356
357         DEBUG(1, "qlogic_event(0x%06x)\n", event);
358
359         switch (event) {
360         case CS_EVENT_CARD_REMOVAL:
361                 link->state &= ~DEV_PRESENT;
362                 if (link->state & DEV_CONFIG)
363                         qlogic_release(link);
364                 break;
365         case CS_EVENT_CARD_INSERTION:
366                 link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
367                 qlogic_config(link);
368                 break;
369         case CS_EVENT_PM_SUSPEND:
370                 link->state |= DEV_SUSPEND;
371                 /* Fall through... */
372         case CS_EVENT_RESET_PHYSICAL:
373                 if (link->state & DEV_CONFIG)
374                         pcmcia_release_configuration(link->handle);
375                 break;
376         case CS_EVENT_PM_RESUME:
377                 link->state &= ~DEV_SUSPEND;
378                 /* Fall through... */
379         case CS_EVENT_CARD_RESET:
380                 if (link->state & DEV_CONFIG) {
381                         scsi_info_t *info = link->priv;
382                         pcmcia_request_configuration(link->handle, &link->conf);
383                         if ((info->manf_id == MANFID_MACNICA) || (info->manf_id == MANFID_PIONEER) || (info->manf_id == 0x0098)) {
384                                 outb(0x80, link->io.BasePort1 + 0xd);
385                                 outb(0x24, link->io.BasePort1 + 0x9);
386                                 outb(0x04, link->io.BasePort1 + 0xd);
387                         }
388                         /* Ugggglllyyyy!!! */
389                         qlogicfas408_bus_reset(NULL);
390                 }
391                 break;
392         }
393         return 0;
394 }                               /* qlogic_event */
395
396 static struct pcmcia_device_id qlogic_ids[] = {
397         PCMCIA_DEVICE_PROD_ID12("EIger Labs", "PCMCIA-to-SCSI Adapter", 0x88395fa7, 0x33b7a5e6),
398         PCMCIA_DEVICE_PROD_ID12("EPSON", "SCSI-2 PC Card SC200", 0xd361772f, 0x299d1751),
399         PCMCIA_DEVICE_PROD_ID12("MACNICA", "MIRACLE SCSI-II mPS110", 0x20841b68, 0xab3c3b6d),
400         PCMCIA_DEVICE_PROD_ID12("MIDORI ELECTRONICS ", "CN-SC43", 0x6534382a, 0xd67eee79),
401         PCMCIA_DEVICE_PROD_ID12("NEC", "PC-9801N-J03R", 0x18df0ba0, 0x24662e8a),
402         PCMCIA_DEVICE_PROD_ID12("KME ", "KXLC003", 0x82375a27, 0xf68e5bf7),
403         PCMCIA_DEVICE_PROD_ID12("KME ", "KXLC004", 0x82375a27, 0x68eace54),
404         PCMCIA_DEVICE_PROD_ID12("KME", "KXLC101", 0x3faee676, 0x194250ec),
405         PCMCIA_DEVICE_PROD_ID12("QLOGIC CORPORATION", "pc05", 0xd77b2930, 0xa85b2735),
406         PCMCIA_DEVICE_PROD_ID12("QLOGIC CORPORATION", "pc05 rev 1.10", 0xd77b2930, 0x70f8b5f8),
407         PCMCIA_DEVICE_PROD_ID123("KME", "KXLC002", "00", 0x3faee676, 0x81896b61, 0xf99f065f),
408         PCMCIA_DEVICE_PROD_ID12("RATOC System Inc.", "SCSI2 CARD 37", 0x85c10e17, 0x1a2640c1),
409         PCMCIA_DEVICE_PROD_ID12("TOSHIBA", "SCSC200A PC CARD SCSI", 0xb4585a1a, 0xa6f06ebe),
410         PCMCIA_DEVICE_PROD_ID12("TOSHIBA", "SCSC200B PC CARD SCSI-10", 0xb4585a1a, 0x0a88dea0),
411         /* these conflict with other cards! */
412         /* PCMCIA_DEVICE_PROD_ID123("MACNICA", "MIRACLE SCSI", "mPS100", 0x20841b68, 0xf8dedaeb, 0x89f7fafb), */
413         /* PCMCIA_DEVICE_PROD_ID123("MACNICA", "MIRACLE SCSI", "mPS100", 0x20841b68, 0xf8dedaeb, 0x89f7fafb), */
414         PCMCIA_DEVICE_NULL,
415 };
416 MODULE_DEVICE_TABLE(pcmcia, qlogic_ids);
417
418 static struct pcmcia_driver qlogic_cs_driver = {
419         .owner          = THIS_MODULE,
420         .drv            = {
421         .name           = "qlogic_cs",
422         },
423         .attach         = qlogic_attach,
424         .event          = qlogic_event,
425         .detach         = qlogic_detach,
426         .id_table       = qlogic_ids,
427 };
428
429 static int __init init_qlogic_cs(void)
430 {
431         return pcmcia_register_driver(&qlogic_cs_driver);
432 }
433
434 static void __exit exit_qlogic_cs(void)
435 {
436         pcmcia_unregister_driver(&qlogic_cs_driver);
437         BUG_ON(dev_list != NULL);
438 }
439
440 MODULE_AUTHOR("Tom Zerucha, Michael Griffith");
441 MODULE_DESCRIPTION("Driver for the PCMCIA Qlogic FAS SCSI controllers");
442 MODULE_LICENSE("GPL");
443 module_init(init_qlogic_cs);
444 module_exit(exit_qlogic_cs);