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