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