xref: /linux/drivers/scsi/pcmcia/qlogic_stub.c (revision d67b569f5f620c0fb95d5212642746b7ba9d29e4)
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 static struct pcmcia_device_id qlogic_ids[] = {
399 	PCMCIA_DEVICE_PROD_ID12("EIger Labs", "PCMCIA-to-SCSI Adapter", 0x88395fa7, 0x33b7a5e6),
400 	PCMCIA_DEVICE_PROD_ID12("EPSON", "SCSI-2 PC Card SC200", 0xd361772f, 0x299d1751),
401 	PCMCIA_DEVICE_PROD_ID12("MACNICA", "MIRACLE SCSI-II mPS110", 0x20841b68, 0xab3c3b6d),
402 	PCMCIA_DEVICE_PROD_ID12("MIDORI ELECTRONICS ", "CN-SC43", 0x6534382a, 0xd67eee79),
403 	PCMCIA_DEVICE_PROD_ID12("NEC", "PC-9801N-J03R", 0x18df0ba0, 0x24662e8a),
404 	PCMCIA_DEVICE_PROD_ID12("KME ", "KXLC003", 0x82375a27, 0xf68e5bf7),
405 	PCMCIA_DEVICE_PROD_ID12("KME ", "KXLC004", 0x82375a27, 0x68eace54),
406 	PCMCIA_DEVICE_PROD_ID12("KME", "KXLC101", 0x3faee676, 0x194250ec),
407 	PCMCIA_DEVICE_PROD_ID12("QLOGIC CORPORATION", "pc05", 0xd77b2930, 0xa85b2735),
408 	PCMCIA_DEVICE_PROD_ID12("QLOGIC CORPORATION", "pc05 rev 1.10", 0xd77b2930, 0x70f8b5f8),
409 	PCMCIA_DEVICE_PROD_ID123("KME", "KXLC002", "00", 0x3faee676, 0x81896b61, 0xf99f065f),
410 	PCMCIA_DEVICE_PROD_ID12("RATOC System Inc.", "SCSI2 CARD 37", 0x85c10e17, 0x1a2640c1),
411 	PCMCIA_DEVICE_PROD_ID12("TOSHIBA", "SCSC200A PC CARD SCSI", 0xb4585a1a, 0xa6f06ebe),
412 	PCMCIA_DEVICE_PROD_ID12("TOSHIBA", "SCSC200B PC CARD SCSI-10", 0xb4585a1a, 0x0a88dea0),
413 	/* these conflict with other cards! */
414 	/* PCMCIA_DEVICE_PROD_ID123("MACNICA", "MIRACLE SCSI", "mPS100", 0x20841b68, 0xf8dedaeb, 0x89f7fafb), */
415 	/* PCMCIA_DEVICE_PROD_ID123("MACNICA", "MIRACLE SCSI", "mPS100", 0x20841b68, 0xf8dedaeb, 0x89f7fafb), */
416 	PCMCIA_DEVICE_NULL,
417 };
418 MODULE_DEVICE_TABLE(pcmcia, qlogic_ids);
419 
420 static struct pcmcia_driver qlogic_cs_driver = {
421 	.owner		= THIS_MODULE,
422 	.drv		= {
423 	.name		= "qlogic_cs",
424 	},
425 	.attach		= qlogic_attach,
426 	.detach		= qlogic_detach,
427 	.id_table       = qlogic_ids,
428 };
429 
430 static int __init init_qlogic_cs(void)
431 {
432 	return pcmcia_register_driver(&qlogic_cs_driver);
433 }
434 
435 static void __exit exit_qlogic_cs(void)
436 {
437 	pcmcia_unregister_driver(&qlogic_cs_driver);
438 	BUG_ON(dev_list != NULL);
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);
446