patch-2_6_7-vs1_9_1_12
[linux-2.6.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 "hosts.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_PARM(pc_debug, "i");
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 /* Parameters that can be set with 'insmod' */
97
98 /* Bit map of interrupts to choose from */
99 static unsigned int irq_mask = 0xdeb8;
100 static int irq_list[4] = { -1 };
101
102 MODULE_PARM(irq_mask, "i");
103 MODULE_PARM(irq_list, "1-4i");
104
105 /*====================================================================*/
106
107 typedef struct scsi_info_t {
108         dev_link_t link;
109         dev_node_t node;
110         struct Scsi_Host *host;
111         unsigned short manf_id;
112 } scsi_info_t;
113
114 static void qlogic_release(dev_link_t *link);
115 static int qlogic_event(event_t event, int priority, event_callback_args_t * args);
116
117 static dev_link_t *qlogic_attach(void);
118 static void qlogic_detach(dev_link_t *);
119
120
121 static dev_link_t *dev_list = NULL;
122
123 static dev_info_t dev_info = "qlogic_cs";
124
125 static struct Scsi_Host *qlogic_detect(Scsi_Host_Template *host,
126                                 dev_link_t *link, int qbase, int qlirq)
127 {
128         int qltyp;              /* type of chip */
129         int qinitid;
130         struct Scsi_Host *shost;        /* registered host structure */
131         struct qlogicfas408_priv *priv;
132
133         qltyp = qlogicfas408_get_chip_type(qbase, INT_TYPE);
134         qinitid = host->this_id;
135         if (qinitid < 0)
136                 qinitid = 7;    /* if no ID, use 7 */
137
138         qlogicfas408_setup(qbase, qinitid, INT_TYPE);
139
140         host->name = qlogic_name;
141         shost = scsi_host_alloc(host, sizeof(struct qlogicfas408_priv));
142         if (!shost)
143                 goto err;
144         shost->io_port = qbase;
145         shost->n_io_port = 16;
146         shost->dma_channel = -1;
147         if (qlirq != -1)
148                 shost->irq = qlirq;
149
150         priv = get_priv_by_host(shost);
151         priv->qlirq = qlirq;
152         priv->qbase = qbase;
153         priv->qinitid = qinitid;
154         priv->shost = shost;
155         priv->int_type = INT_TYPE;                                      
156
157         if (request_irq(qlirq, qlogicfas408_ihandl, 0, qlogic_name, shost))
158                 goto free_scsi_host;
159
160         sprintf(priv->qinfo,
161                 "Qlogicfas Driver version 0.46, chip %02X at %03X, IRQ %d, TPdma:%d",
162                 qltyp, qbase, qlirq, QL_TURBO_PDMA);
163
164         if (scsi_add_host(shost, NULL))
165                 goto free_interrupt;
166
167         scsi_scan_host(shost);
168
169         return shost;
170
171 free_interrupt:
172         free_irq(qlirq, shost);
173
174 free_scsi_host:
175         scsi_host_put(shost);
176         
177 err:
178         return NULL;
179 }
180 static dev_link_t *qlogic_attach(void)
181 {
182         scsi_info_t *info;
183         client_reg_t client_reg;
184         dev_link_t *link;
185         int i, ret;
186
187         DEBUG(0, "qlogic_attach()\n");
188
189         /* Create new SCSI device */
190         info = kmalloc(sizeof(*info), GFP_KERNEL);
191         if (!info)
192                 return NULL;
193         memset(info, 0, sizeof(*info));
194         link = &info->link;
195         link->priv = info;
196         link->io.NumPorts1 = 16;
197         link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
198         link->io.IOAddrLines = 10;
199         link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
200         link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID;
201         if (irq_list[0] == -1)
202                 link->irq.IRQInfo2 = irq_mask;
203         else
204                 for (i = 0; i < 4; i++)
205                         link->irq.IRQInfo2 |= 1 << irq_list[i];
206         link->conf.Attributes = CONF_ENABLE_IRQ;
207         link->conf.Vcc = 50;
208         link->conf.IntType = INT_MEMORY_AND_IO;
209         link->conf.Present = PRESENT_OPTION;
210
211         /* Register with Card Services */
212         link->next = dev_list;
213         dev_list = link;
214         client_reg.dev_info = &dev_info;
215         client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
216         client_reg.event_handler = &qlogic_event;
217         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;
218         client_reg.Version = 0x0210;
219         client_reg.event_callback_args.client_data = link;
220         ret = pcmcia_register_client(&link->handle, &client_reg);
221         if (ret != 0) {
222                 cs_error(link->handle, RegisterClient, ret);
223                 qlogic_detach(link);
224                 return NULL;
225         }
226
227         return link;
228 }                               /* qlogic_attach */
229
230 /*====================================================================*/
231
232 static void qlogic_detach(dev_link_t * link)
233 {
234         dev_link_t **linkp;
235
236         DEBUG(0, "qlogic_detach(0x%p)\n", link);
237
238         /* Locate device structure */
239         for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
240                 if (*linkp == link)
241                         break;
242         if (*linkp == NULL)
243                 return;
244
245         if (link->state & DEV_CONFIG)
246                 qlogic_release(link);
247
248         if (link->handle)
249                 pcmcia_deregister_client(link->handle);
250
251         /* Unlink device structure, free bits */
252         *linkp = link->next;
253         kfree(link->priv);
254
255 }                               /* qlogic_detach */
256
257 /*====================================================================*/
258
259 #define CS_CHECK(fn, ret) \
260 do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
261
262 static void qlogic_config(dev_link_t * link)
263 {
264         client_handle_t handle = link->handle;
265         scsi_info_t *info = link->priv;
266         tuple_t tuple;
267         cisparse_t parse;
268         int i, last_ret, last_fn;
269         unsigned short tuple_data[32];
270         struct Scsi_Host *host;
271
272         DEBUG(0, "qlogic_config(0x%p)\n", link);
273
274         tuple.TupleData = (cisdata_t *) tuple_data;
275         tuple.TupleDataMax = 64;
276         tuple.TupleOffset = 0;
277         tuple.DesiredTuple = CISTPL_CONFIG;
278         CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
279         CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
280         CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
281         link->conf.ConfigBase = parse.config.base;
282
283         tuple.DesiredTuple = CISTPL_MANFID;
284         if ((pcmcia_get_first_tuple(handle, &tuple) == CS_SUCCESS) && (pcmcia_get_tuple_data(handle, &tuple) == CS_SUCCESS))
285                 info->manf_id = le16_to_cpu(tuple.TupleData[0]);
286
287         /* Configure card */
288         link->state |= DEV_CONFIG;
289
290         tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
291         CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
292         while (1) {
293                 if (pcmcia_get_tuple_data(handle, &tuple) != 0 ||
294                                 pcmcia_parse_tuple(handle, &tuple, &parse) != 0)
295                         goto next_entry;
296                 link->conf.ConfigIndex = parse.cftable_entry.index;
297                 link->io.BasePort1 = parse.cftable_entry.io.win[0].base;
298                 link->io.NumPorts1 = parse.cftable_entry.io.win[0].len;
299                 if (link->io.BasePort1 != 0) {
300                         i = pcmcia_request_io(handle, &link->io);
301                         if (i == CS_SUCCESS)
302                                 break;
303                 }
304               next_entry:
305                 CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple));
306         }
307
308         CS_CHECK(RequestIRQ, pcmcia_request_irq(handle, &link->irq));
309         CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf));
310
311         if ((info->manf_id == MANFID_MACNICA) || (info->manf_id == MANFID_PIONEER) || (info->manf_id == 0x0098)) {
312                 /* set ATAcmd */
313                 outb(0xb4, link->io.BasePort1 + 0xd);
314                 outb(0x24, link->io.BasePort1 + 0x9);
315                 outb(0x04, link->io.BasePort1 + 0xd);
316         }
317
318         /* The KXL-810AN has a bigger IO port window */
319         if (link->io.NumPorts1 == 32)
320                 host = qlogic_detect(&qlogicfas_driver_template, link,
321                         link->io.BasePort1 + 16, link->irq.AssignedIRQ);
322         else
323                 host = qlogic_detect(&qlogicfas_driver_template, link,
324                         link->io.BasePort1, link->irq.AssignedIRQ);
325         
326         if (!host) {
327                 printk(KERN_INFO "%s: no SCSI devices found\n", qlogic_name);
328                 goto out;
329         }
330
331         sprintf(info->node.dev_name, "scsi%d", host->host_no);
332         link->dev = &info->node;
333         info->host = host;
334
335 out:
336         link->state &= ~DEV_CONFIG_PENDING;
337         return;
338
339 cs_failed:
340         cs_error(link->handle, last_fn, last_ret);
341         link->dev = NULL;
342         pcmcia_release_configuration(link->handle);
343         pcmcia_release_io(link->handle, &link->io);
344         pcmcia_release_irq(link->handle, &link->irq);
345         link->state &= ~DEV_CONFIG;
346         return;
347
348 }                               /* qlogic_config */
349
350 /*====================================================================*/
351
352 static void qlogic_release(dev_link_t *link)
353 {
354         scsi_info_t *info = link->priv;
355
356         DEBUG(0, "qlogic_release(0x%p)\n", link);
357
358         scsi_remove_host(info->host);
359         link->dev = NULL;
360
361         free_irq(link->irq.AssignedIRQ, info->host);
362
363         pcmcia_release_configuration(link->handle);
364         pcmcia_release_io(link->handle, &link->io);
365         pcmcia_release_irq(link->handle, &link->irq);
366
367         scsi_host_put(info->host);
368
369         link->state &= ~DEV_CONFIG;
370 }
371
372 /*====================================================================*/
373
374 static int qlogic_event(event_t event, int priority, event_callback_args_t * args)
375 {
376         dev_link_t *link = args->client_data;
377
378         DEBUG(1, "qlogic_event(0x%06x)\n", event);
379
380         switch (event) {
381         case CS_EVENT_CARD_REMOVAL:
382                 link->state &= ~DEV_PRESENT;
383                 if (link->state & DEV_CONFIG)
384                         qlogic_release(link);
385                 break;
386         case CS_EVENT_CARD_INSERTION:
387                 link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
388                 qlogic_config(link);
389                 break;
390         case CS_EVENT_PM_SUSPEND:
391                 link->state |= DEV_SUSPEND;
392                 /* Fall through... */
393         case CS_EVENT_RESET_PHYSICAL:
394                 if (link->state & DEV_CONFIG)
395                         pcmcia_release_configuration(link->handle);
396                 break;
397         case CS_EVENT_PM_RESUME:
398                 link->state &= ~DEV_SUSPEND;
399                 /* Fall through... */
400         case CS_EVENT_CARD_RESET:
401                 if (link->state & DEV_CONFIG) {
402                         scsi_info_t *info = link->priv;
403                         pcmcia_request_configuration(link->handle, &link->conf);
404                         if ((info->manf_id == MANFID_MACNICA) || (info->manf_id == MANFID_PIONEER) || (info->manf_id == 0x0098)) {
405                                 outb(0x80, link->io.BasePort1 + 0xd);
406                                 outb(0x24, link->io.BasePort1 + 0x9);
407                                 outb(0x04, link->io.BasePort1 + 0xd);
408                         }
409                         /* Ugggglllyyyy!!! */
410                         qlogicfas408_bus_reset(NULL);
411                 }
412                 break;
413         }
414         return 0;
415 }                               /* qlogic_event */
416
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         .detach         = qlogic_detach,
425 };
426
427 static int __init init_qlogic_cs(void)
428 {
429         return pcmcia_register_driver(&qlogic_cs_driver);
430 }
431
432 static void __exit exit_qlogic_cs(void)
433 {
434         pcmcia_unregister_driver(&qlogic_cs_driver);
435
436         /* XXX: this really needs to move into generic code.. */
437         while (dev_list != NULL)
438                 qlogic_detach(dev_list);
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);