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>
49 #include <scsi/scsi_host.h>
50 #include "../qlogicfas408.h"
52 #include <pcmcia/cs_types.h>
53 #include <pcmcia/cs.h>
54 #include <pcmcia/cistpl.h>
55 #include <pcmcia/ds.h>
56 #include <pcmcia/ciscode.h>
58 /* Set the following to 2 to use normal interrupt (active high/totempole-
59 * tristate), otherwise use 0 (REQUIRED FOR PCMCIA) for active low, open
64 static char qlogic_name[] = "qlogic_cs";
67 static int pc_debug = PCMCIA_DEBUG;
68 module_param(pc_debug, int, 0644);
69 #define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
70 static char *version = "qlogic_cs.c 1.79-ac 2002/10/26 (David Hinds)";
72 #define DEBUG(n, args...)
75 static struct scsi_host_template qlogicfas_driver_template = {
76 .module = THIS_MODULE,
78 .proc_name = qlogic_name,
79 .info = qlogicfas408_info,
80 .queuecommand = qlogicfas408_queuecommand,
81 .eh_abort_handler = qlogicfas408_abort,
82 .eh_bus_reset_handler = qlogicfas408_bus_reset,
83 .bios_param = qlogicfas408_biosparam,
86 .sg_tablesize = SG_ALL,
88 .use_clustering = DISABLE_CLUSTERING,
91 /*====================================================================*/
93 typedef struct scsi_info_t {
96 struct Scsi_Host *host;
97 unsigned short manf_id;
100 static void qlogic_release(dev_link_t *link);
101 static int qlogic_event(event_t event, int priority, event_callback_args_t * args);
103 static dev_link_t *qlogic_attach(void);
104 static void qlogic_detach(struct pcmcia_device *p_dev);
107 static dev_link_t *dev_list = NULL;
109 static dev_info_t dev_info = "qlogic_cs";
111 static struct Scsi_Host *qlogic_detect(struct scsi_host_template *host,
112 dev_link_t *link, int qbase, int qlirq)
114 int qltyp; /* type of chip */
116 struct Scsi_Host *shost; /* registered host structure */
117 struct qlogicfas408_priv *priv;
119 qltyp = qlogicfas408_get_chip_type(qbase, INT_TYPE);
120 qinitid = host->this_id;
122 qinitid = 7; /* if no ID, use 7 */
124 qlogicfas408_setup(qbase, qinitid, INT_TYPE);
126 host->name = qlogic_name;
127 shost = scsi_host_alloc(host, sizeof(struct qlogicfas408_priv));
130 shost->io_port = qbase;
131 shost->n_io_port = 16;
132 shost->dma_channel = -1;
136 priv = get_priv_by_host(shost);
139 priv->qinitid = qinitid;
141 priv->int_type = INT_TYPE;
143 if (request_irq(qlirq, qlogicfas408_ihandl, 0, qlogic_name, shost))
147 "Qlogicfas Driver version 0.46, chip %02X at %03X, IRQ %d, TPdma:%d",
148 qltyp, qbase, qlirq, QL_TURBO_PDMA);
150 if (scsi_add_host(shost, NULL))
153 scsi_scan_host(shost);
158 free_irq(qlirq, shost);
161 scsi_host_put(shost);
166 static dev_link_t *qlogic_attach(void)
169 client_reg_t client_reg;
173 DEBUG(0, "qlogic_attach()\n");
175 /* Create new SCSI device */
176 info = kmalloc(sizeof(*info), GFP_KERNEL);
179 memset(info, 0, sizeof(*info));
182 link->io.NumPorts1 = 16;
183 link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
184 link->io.IOAddrLines = 10;
185 link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
186 link->irq.IRQInfo1 = IRQ_LEVEL_ID;
187 link->conf.Attributes = CONF_ENABLE_IRQ;
189 link->conf.IntType = INT_MEMORY_AND_IO;
190 link->conf.Present = PRESENT_OPTION;
192 /* Register with Card Services */
193 link->next = dev_list;
195 client_reg.dev_info = &dev_info;
196 client_reg.Version = 0x0210;
197 client_reg.event_callback_args.client_data = link;
198 ret = pcmcia_register_client(&link->handle, &client_reg);
200 cs_error(link->handle, RegisterClient, ret);
201 qlogic_detach(link->handle);
206 } /* qlogic_attach */
208 /*====================================================================*/
210 static void qlogic_detach(struct pcmcia_device *p_dev)
212 dev_link_t *link = dev_to_instance(p_dev);
215 DEBUG(0, "qlogic_detach(0x%p)\n", link);
217 /* Locate device structure */
218 for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
224 if (link->state & DEV_CONFIG)
225 qlogic_release(link);
227 /* Unlink device structure, free bits */
231 } /* qlogic_detach */
233 /*====================================================================*/
235 #define CS_CHECK(fn, ret) \
236 do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
238 static void qlogic_config(dev_link_t * link)
240 client_handle_t handle = link->handle;
241 scsi_info_t *info = link->priv;
244 int i, last_ret, last_fn;
245 unsigned short tuple_data[32];
246 struct Scsi_Host *host;
248 DEBUG(0, "qlogic_config(0x%p)\n", link);
250 tuple.TupleData = (cisdata_t *) tuple_data;
251 tuple.TupleDataMax = 64;
252 tuple.TupleOffset = 0;
253 tuple.DesiredTuple = CISTPL_CONFIG;
254 CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
255 CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
256 CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
257 link->conf.ConfigBase = parse.config.base;
259 tuple.DesiredTuple = CISTPL_MANFID;
260 if ((pcmcia_get_first_tuple(handle, &tuple) == CS_SUCCESS) && (pcmcia_get_tuple_data(handle, &tuple) == CS_SUCCESS))
261 info->manf_id = le16_to_cpu(tuple.TupleData[0]);
264 link->state |= DEV_CONFIG;
266 tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
267 CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
269 if (pcmcia_get_tuple_data(handle, &tuple) != 0 ||
270 pcmcia_parse_tuple(handle, &tuple, &parse) != 0)
272 link->conf.ConfigIndex = parse.cftable_entry.index;
273 link->io.BasePort1 = parse.cftable_entry.io.win[0].base;
274 link->io.NumPorts1 = parse.cftable_entry.io.win[0].len;
275 if (link->io.BasePort1 != 0) {
276 i = pcmcia_request_io(handle, &link->io);
281 CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple));
284 CS_CHECK(RequestIRQ, pcmcia_request_irq(handle, &link->irq));
285 CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf));
287 if ((info->manf_id == MANFID_MACNICA) || (info->manf_id == MANFID_PIONEER) || (info->manf_id == 0x0098)) {
289 outb(0xb4, link->io.BasePort1 + 0xd);
290 outb(0x24, link->io.BasePort1 + 0x9);
291 outb(0x04, link->io.BasePort1 + 0xd);
294 /* The KXL-810AN has a bigger IO port window */
295 if (link->io.NumPorts1 == 32)
296 host = qlogic_detect(&qlogicfas_driver_template, link,
297 link->io.BasePort1 + 16, link->irq.AssignedIRQ);
299 host = qlogic_detect(&qlogicfas_driver_template, link,
300 link->io.BasePort1, link->irq.AssignedIRQ);
303 printk(KERN_INFO "%s: no SCSI devices found\n", qlogic_name);
307 sprintf(info->node.dev_name, "scsi%d", host->host_no);
308 link->dev = &info->node;
312 link->state &= ~DEV_CONFIG_PENDING;
316 cs_error(link->handle, last_fn, last_ret);
318 pcmcia_release_configuration(link->handle);
319 pcmcia_release_io(link->handle, &link->io);
320 pcmcia_release_irq(link->handle, &link->irq);
321 link->state &= ~DEV_CONFIG;
324 } /* qlogic_config */
326 /*====================================================================*/
328 static void qlogic_release(dev_link_t *link)
330 scsi_info_t *info = link->priv;
332 DEBUG(0, "qlogic_release(0x%p)\n", link);
334 scsi_remove_host(info->host);
337 free_irq(link->irq.AssignedIRQ, info->host);
339 pcmcia_release_configuration(link->handle);
340 pcmcia_release_io(link->handle, &link->io);
341 pcmcia_release_irq(link->handle, &link->irq);
343 scsi_host_put(info->host);
345 link->state &= ~DEV_CONFIG;
348 /*====================================================================*/
350 static int qlogic_suspend(struct pcmcia_device *dev)
352 dev_link_t *link = dev_to_instance(dev);
354 link->state |= DEV_SUSPEND;
355 if (link->state & DEV_CONFIG)
356 pcmcia_release_configuration(link->handle);
361 static int qlogic_resume(struct pcmcia_device *dev)
363 dev_link_t *link = dev_to_instance(dev);
365 link->state &= ~DEV_SUSPEND;
366 if (link->state & DEV_CONFIG) {
367 scsi_info_t *info = link->priv;
369 pcmcia_request_configuration(link->handle, &link->conf);
370 if ((info->manf_id == MANFID_MACNICA) ||
371 (info->manf_id == MANFID_PIONEER) ||
372 (info->manf_id == 0x0098)) {
373 outb(0x80, link->io.BasePort1 + 0xd);
374 outb(0x24, link->io.BasePort1 + 0x9);
375 outb(0x04, link->io.BasePort1 + 0xd);
377 /* Ugggglllyyyy!!! */
378 qlogicfas408_bus_reset(NULL);
384 static int qlogic_event(event_t event, int priority, event_callback_args_t * args)
386 dev_link_t *link = args->client_data;
388 DEBUG(1, "qlogic_event(0x%06x)\n", event);
391 case CS_EVENT_CARD_INSERTION:
392 link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
399 static struct pcmcia_device_id qlogic_ids[] = {
400 PCMCIA_DEVICE_PROD_ID12("EIger Labs", "PCMCIA-to-SCSI Adapter", 0x88395fa7, 0x33b7a5e6),
401 PCMCIA_DEVICE_PROD_ID12("EPSON", "SCSI-2 PC Card SC200", 0xd361772f, 0x299d1751),
402 PCMCIA_DEVICE_PROD_ID12("MACNICA", "MIRACLE SCSI-II mPS110", 0x20841b68, 0xab3c3b6d),
403 PCMCIA_DEVICE_PROD_ID12("MIDORI ELECTRONICS ", "CN-SC43", 0x6534382a, 0xd67eee79),
404 PCMCIA_DEVICE_PROD_ID12("NEC", "PC-9801N-J03R", 0x18df0ba0, 0x24662e8a),
405 PCMCIA_DEVICE_PROD_ID12("KME ", "KXLC003", 0x82375a27, 0xf68e5bf7),
406 PCMCIA_DEVICE_PROD_ID12("KME ", "KXLC004", 0x82375a27, 0x68eace54),
407 PCMCIA_DEVICE_PROD_ID12("KME", "KXLC101", 0x3faee676, 0x194250ec),
408 PCMCIA_DEVICE_PROD_ID12("QLOGIC CORPORATION", "pc05", 0xd77b2930, 0xa85b2735),
409 PCMCIA_DEVICE_PROD_ID12("QLOGIC CORPORATION", "pc05 rev 1.10", 0xd77b2930, 0x70f8b5f8),
410 PCMCIA_DEVICE_PROD_ID123("KME", "KXLC002", "00", 0x3faee676, 0x81896b61, 0xf99f065f),
411 PCMCIA_DEVICE_PROD_ID12("RATOC System Inc.", "SCSI2 CARD 37", 0x85c10e17, 0x1a2640c1),
412 PCMCIA_DEVICE_PROD_ID12("TOSHIBA", "SCSC200A PC CARD SCSI", 0xb4585a1a, 0xa6f06ebe),
413 PCMCIA_DEVICE_PROD_ID12("TOSHIBA", "SCSC200B PC CARD SCSI-10", 0xb4585a1a, 0x0a88dea0),
414 /* these conflict with other cards! */
415 /* PCMCIA_DEVICE_PROD_ID123("MACNICA", "MIRACLE SCSI", "mPS100", 0x20841b68, 0xf8dedaeb, 0x89f7fafb), */
416 /* PCMCIA_DEVICE_PROD_ID123("MACNICA", "MIRACLE SCSI", "mPS100", 0x20841b68, 0xf8dedaeb, 0x89f7fafb), */
419 MODULE_DEVICE_TABLE(pcmcia, qlogic_ids);
421 static struct pcmcia_driver qlogic_cs_driver = {
422 .owner = THIS_MODULE,
426 .attach = qlogic_attach,
427 .event = qlogic_event,
428 .remove = qlogic_detach,
429 .id_table = qlogic_ids,
430 .suspend = qlogic_suspend,
431 .resume = qlogic_resume,
434 static int __init init_qlogic_cs(void)
436 return pcmcia_register_driver(&qlogic_cs_driver);
439 static void __exit exit_qlogic_cs(void)
441 pcmcia_unregister_driver(&qlogic_cs_driver);
442 BUG_ON(dev_list != NULL);
445 MODULE_AUTHOR("Tom Zerucha, Michael Griffith");
446 MODULE_DESCRIPTION("Driver for the PCMCIA Qlogic FAS SCSI controllers");
447 MODULE_LICENSE("GPL");
448 module_init(init_qlogic_cs);
449 module_exit(exit_qlogic_cs);