Linux-2.6.12-rc2
[linux-flexiantxendom0-natty.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         .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);