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