Merge master.kernel.org:/pub/scm/linux/kernel/git/dwmw2/audit-2.6
[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.event_handler = &qlogic_event;
198         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;
199         client_reg.Version = 0x0210;
200         client_reg.event_callback_args.client_data = link;
201         ret = pcmcia_register_client(&link->handle, &client_reg);
202         if (ret != 0) {
203                 cs_error(link->handle, RegisterClient, ret);
204                 qlogic_detach(link);
205                 return NULL;
206         }
207
208         return link;
209 }                               /* qlogic_attach */
210
211 /*====================================================================*/
212
213 static void qlogic_detach(dev_link_t * link)
214 {
215         dev_link_t **linkp;
216
217         DEBUG(0, "qlogic_detach(0x%p)\n", link);
218
219         /* Locate device structure */
220         for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
221                 if (*linkp == link)
222                         break;
223         if (*linkp == NULL)
224                 return;
225
226         if (link->state & DEV_CONFIG)
227                 qlogic_release(link);
228
229         if (link->handle)
230                 pcmcia_deregister_client(link->handle);
231
232         /* Unlink device structure, free bits */
233         *linkp = link->next;
234         kfree(link->priv);
235
236 }                               /* qlogic_detach */
237
238 /*====================================================================*/
239
240 #define CS_CHECK(fn, ret) \
241 do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
242
243 static void qlogic_config(dev_link_t * link)
244 {
245         client_handle_t handle = link->handle;
246         scsi_info_t *info = link->priv;
247         tuple_t tuple;
248         cisparse_t parse;
249         int i, last_ret, last_fn;
250         unsigned short tuple_data[32];
251         struct Scsi_Host *host;
252
253         DEBUG(0, "qlogic_config(0x%p)\n", link);
254
255         tuple.TupleData = (cisdata_t *) tuple_data;
256         tuple.TupleDataMax = 64;
257         tuple.TupleOffset = 0;
258         tuple.DesiredTuple = CISTPL_CONFIG;
259         CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
260         CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
261         CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
262         link->conf.ConfigBase = parse.config.base;
263
264         tuple.DesiredTuple = CISTPL_MANFID;
265         if ((pcmcia_get_first_tuple(handle, &tuple) == CS_SUCCESS) && (pcmcia_get_tuple_data(handle, &tuple) == CS_SUCCESS))
266                 info->manf_id = le16_to_cpu(tuple.TupleData[0]);
267
268         /* Configure card */
269         link->state |= DEV_CONFIG;
270
271         tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
272         CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
273         while (1) {
274                 if (pcmcia_get_tuple_data(handle, &tuple) != 0 ||
275                                 pcmcia_parse_tuple(handle, &tuple, &parse) != 0)
276                         goto next_entry;
277                 link->conf.ConfigIndex = parse.cftable_entry.index;
278                 link->io.BasePort1 = parse.cftable_entry.io.win[0].base;
279                 link->io.NumPorts1 = parse.cftable_entry.io.win[0].len;
280                 if (link->io.BasePort1 != 0) {
281                         i = pcmcia_request_io(handle, &link->io);
282                         if (i == CS_SUCCESS)
283                                 break;
284                 }
285               next_entry:
286                 CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple));
287         }
288
289         CS_CHECK(RequestIRQ, pcmcia_request_irq(handle, &link->irq));
290         CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf));
291
292         if ((info->manf_id == MANFID_MACNICA) || (info->manf_id == MANFID_PIONEER) || (info->manf_id == 0x0098)) {
293                 /* set ATAcmd */
294                 outb(0xb4, link->io.BasePort1 + 0xd);
295                 outb(0x24, link->io.BasePort1 + 0x9);
296                 outb(0x04, link->io.BasePort1 + 0xd);
297         }
298
299         /* The KXL-810AN has a bigger IO port window */
300         if (link->io.NumPorts1 == 32)
301                 host = qlogic_detect(&qlogicfas_driver_template, link,
302                         link->io.BasePort1 + 16, link->irq.AssignedIRQ);
303         else
304                 host = qlogic_detect(&qlogicfas_driver_template, link,
305                         link->io.BasePort1, link->irq.AssignedIRQ);
306         
307         if (!host) {
308                 printk(KERN_INFO "%s: no SCSI devices found\n", qlogic_name);
309                 goto out;
310         }
311
312         sprintf(info->node.dev_name, "scsi%d", host->host_no);
313         link->dev = &info->node;
314         info->host = host;
315
316 out:
317         link->state &= ~DEV_CONFIG_PENDING;
318         return;
319
320 cs_failed:
321         cs_error(link->handle, last_fn, last_ret);
322         link->dev = NULL;
323         pcmcia_release_configuration(link->handle);
324         pcmcia_release_io(link->handle, &link->io);
325         pcmcia_release_irq(link->handle, &link->irq);
326         link->state &= ~DEV_CONFIG;
327         return;
328
329 }                               /* qlogic_config */
330
331 /*====================================================================*/
332
333 static void qlogic_release(dev_link_t *link)
334 {
335         scsi_info_t *info = link->priv;
336
337         DEBUG(0, "qlogic_release(0x%p)\n", link);
338
339         scsi_remove_host(info->host);
340         link->dev = NULL;
341
342         free_irq(link->irq.AssignedIRQ, info->host);
343
344         pcmcia_release_configuration(link->handle);
345         pcmcia_release_io(link->handle, &link->io);
346         pcmcia_release_irq(link->handle, &link->irq);
347
348         scsi_host_put(info->host);
349
350         link->state &= ~DEV_CONFIG;
351 }
352
353 /*====================================================================*/
354
355 static int qlogic_event(event_t event, int priority, event_callback_args_t * args)
356 {
357         dev_link_t *link = args->client_data;
358
359         DEBUG(1, "qlogic_event(0x%06x)\n", event);
360
361         switch (event) {
362         case CS_EVENT_CARD_REMOVAL:
363                 link->state &= ~DEV_PRESENT;
364                 if (link->state & DEV_CONFIG)
365                         qlogic_release(link);
366                 break;
367         case CS_EVENT_CARD_INSERTION:
368                 link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
369                 qlogic_config(link);
370                 break;
371         case CS_EVENT_PM_SUSPEND:
372                 link->state |= DEV_SUSPEND;
373                 /* Fall through... */
374         case CS_EVENT_RESET_PHYSICAL:
375                 if (link->state & DEV_CONFIG)
376                         pcmcia_release_configuration(link->handle);
377                 break;
378         case CS_EVENT_PM_RESUME:
379                 link->state &= ~DEV_SUSPEND;
380                 /* Fall through... */
381         case CS_EVENT_CARD_RESET:
382                 if (link->state & DEV_CONFIG) {
383                         scsi_info_t *info = link->priv;
384                         pcmcia_request_configuration(link->handle, &link->conf);
385                         if ((info->manf_id == MANFID_MACNICA) || (info->manf_id == MANFID_PIONEER) || (info->manf_id == 0x0098)) {
386                                 outb(0x80, link->io.BasePort1 + 0xd);
387                                 outb(0x24, link->io.BasePort1 + 0x9);
388                                 outb(0x04, link->io.BasePort1 + 0xd);
389                         }
390                         /* Ugggglllyyyy!!! */
391                         qlogicfas408_bus_reset(NULL);
392                 }
393                 break;
394         }
395         return 0;
396 }                               /* qlogic_event */
397
398
399 static struct pcmcia_driver qlogic_cs_driver = {
400         .owner          = THIS_MODULE,
401         .drv            = {
402         .name           = "qlogic_cs",
403         },
404         .attach         = qlogic_attach,
405         .detach         = qlogic_detach,
406 };
407
408 static int __init init_qlogic_cs(void)
409 {
410         return pcmcia_register_driver(&qlogic_cs_driver);
411 }
412
413 static void __exit exit_qlogic_cs(void)
414 {
415         pcmcia_unregister_driver(&qlogic_cs_driver);
416         BUG_ON(dev_list != NULL);
417 }
418
419 MODULE_AUTHOR("Tom Zerucha, Michael Griffith");
420 MODULE_DESCRIPTION("Driver for the PCMCIA Qlogic FAS SCSI controllers");
421 MODULE_LICENSE("GPL");
422 module_init(init_qlogic_cs);
423 module_exit(exit_qlogic_cs);