1 /*======================================================================
3 A driver for the Qlogic SCSI card
5 qlogic_cs.c 1.79 2000/06/12 21:27:26
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/
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.
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.
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.
32 ======================================================================*/
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>
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>
50 #include "../qlogicfas.h"
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>
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);
65 static char *qlogic_name = "qlogic_cs";
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)";
73 #define DEBUG(n, args...)
76 /*====================================================================*/
78 /* Parameters that can be set with 'insmod' */
80 /* Bit map of interrupts to choose from */
81 static unsigned int irq_mask = 0xdeb8;
82 static int irq_list[4] = { -1 };
84 MODULE_PARM(irq_mask, "i");
85 MODULE_PARM(irq_list, "1-4i");
87 /*====================================================================*/
89 typedef struct scsi_info_t {
92 struct Scsi_Host *host;
93 unsigned short manf_id;
96 static void qlogic_release(dev_link_t *link);
97 static int qlogic_event(event_t event, int priority, event_callback_args_t * args);
99 static dev_link_t *qlogic_attach(void);
100 static void qlogic_detach(dev_link_t *);
103 static dev_link_t *dev_list = NULL;
105 static dev_info_t dev_info = "qlogic_cs";
107 static struct Scsi_Host *qlogic_detect(Scsi_Host_Template *host,
108 dev_link_t *link, int qbase, int qlirq)
110 int qltyp; /* type of chip */
112 struct Scsi_Host *shost; /* registered host structure */
113 qlogicfas_priv_t priv;
115 qltyp = inb(qbase + 0xe) & 0xf8;
116 qinitid = host->this_id;
118 qinitid = 7; /* if no ID, use 7 */
119 outb(1, qbase + 8); /* set for PIO pseudo DMA */
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 */
125 #if QL_RESET_AT_START
129 while (inb(qbase + 0xf) & 4)
134 host->name = qlogic_name;
135 shost = scsi_host_alloc(host, sizeof(struct qlogicfas_priv));
138 shost->io_port = qbase;
139 shost->n_io_port = 16;
140 shost->dma_channel = -1;
144 priv = (qlogicfas_priv_t)&(shost->hostdata[0]);
147 priv->qinitid = qinitid;
149 if (request_irq(qlirq, do_ql_ihandl, 0, qlogic_name, shost))
153 "Qlogicfas Driver version 0.46, chip %02X at %03X, IRQ %d, TPdma:%d",
154 qltyp, qbase, qlirq, QL_TURBO_PDMA);
156 if (scsi_add_host(shost, NULL))
159 scsi_scan_host(shost);
164 free_irq(qlirq, shost);
167 scsi_host_put(shost);
172 static dev_link_t *qlogic_attach(void)
175 client_reg_t client_reg;
179 DEBUG(0, "qlogic_attach()\n");
181 /* Create new SCSI device */
182 info = kmalloc(sizeof(*info), GFP_KERNEL);
185 memset(info, 0, sizeof(*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;
196 for (i = 0; i < 4; i++)
197 link->irq.IRQInfo2 |= 1 << irq_list[i];
198 link->conf.Attributes = CONF_ENABLE_IRQ;
200 link->conf.IntType = INT_MEMORY_AND_IO;
201 link->conf.Present = PRESENT_OPTION;
203 /* Register with Card Services */
204 link->next = dev_list;
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);
214 cs_error(link->handle, RegisterClient, ret);
220 } /* qlogic_attach */
222 /*====================================================================*/
224 static void qlogic_detach(dev_link_t * link)
228 DEBUG(0, "qlogic_detach(0x%p)\n", link);
230 /* Locate device structure */
231 for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
237 if (link->state & DEV_CONFIG)
238 qlogic_release(link);
241 pcmcia_deregister_client(link->handle);
243 /* Unlink device structure, free bits */
247 } /* qlogic_detach */
249 /*====================================================================*/
251 #define CS_CHECK(fn, ret) \
252 do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
254 static void qlogic_config(dev_link_t * link)
256 client_handle_t handle = link->handle;
257 scsi_info_t *info = link->priv;
260 int i, last_ret, last_fn;
261 unsigned short tuple_data[32];
262 struct Scsi_Host *host;
264 DEBUG(0, "qlogic_config(0x%p)\n", link);
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;
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]);
280 link->state |= DEV_CONFIG;
282 tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
283 CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
285 if (pcmcia_get_tuple_data(handle, &tuple) != 0 ||
286 pcmcia_parse_tuple(handle, &tuple, &parse) != 0)
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);
297 CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple));
300 CS_CHECK(RequestIRQ, pcmcia_request_irq(handle, &link->irq));
301 CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf));
303 if ((info->manf_id == MANFID_MACNICA) || (info->manf_id == MANFID_PIONEER) || (info->manf_id == 0x0098)) {
305 outb(0xb4, link->io.BasePort1 + 0xd);
306 outb(0x24, link->io.BasePort1 + 0x9);
307 outb(0x04, link->io.BasePort1 + 0xd);
310 qlogicfas_driver_template.name = qlogic_name;
311 qlogicfas_driver_template.proc_name = qlogic_name;
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);
318 host = qlogic_detect(&qlogicfas_driver_template, link,
319 link->io.BasePort1, link->irq.AssignedIRQ);
322 printk(KERN_INFO "%s: no SCSI devices found\n", qlogic_name);
326 sprintf(info->node.dev_name, "scsi%d", host->host_no);
327 link->dev = &info->node;
331 link->state &= ~DEV_CONFIG_PENDING;
335 cs_error(link->handle, last_fn, last_ret);
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;
343 } /* qlogic_config */
345 /*====================================================================*/
347 static void qlogic_release(dev_link_t *link)
349 scsi_info_t *info = link->priv;
351 DEBUG(0, "qlogic_release(0x%p)\n", link);
353 scsi_remove_host(info->host);
356 free_irq(link->irq.AssignedIRQ, info->host);
358 pcmcia_release_configuration(link->handle);
359 pcmcia_release_io(link->handle, &link->io);
360 pcmcia_release_irq(link->handle, &link->irq);
362 scsi_host_put(info->host);
364 link->state &= ~DEV_CONFIG;
367 /*====================================================================*/
369 static int qlogic_event(event_t event, int priority, event_callback_args_t * args)
371 dev_link_t *link = args->client_data;
373 DEBUG(1, "qlogic_event(0x%06x)\n", event);
376 case CS_EVENT_CARD_REMOVAL:
377 link->state &= ~DEV_PRESENT;
378 if (link->state & DEV_CONFIG)
379 qlogic_release(link);
381 case CS_EVENT_CARD_INSERTION:
382 link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
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);
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);
404 /* Ugggglllyyyy!!! */
405 qlogicfas_bus_reset(NULL);
413 static struct pcmcia_driver qlogic_cs_driver = {
414 .owner = THIS_MODULE,
418 .attach = qlogic_attach,
419 .detach = qlogic_detach,
422 static int __init init_qlogic_cs(void)
424 return pcmcia_register_driver(&qlogic_cs_driver);
427 static void __exit exit_qlogic_cs(void)
429 pcmcia_unregister_driver(&qlogic_cs_driver);
431 /* XXX: this really needs to move into generic code.. */
432 while (dev_list != NULL)
433 qlogic_detach(dev_list);
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);