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/slab.h>
38#include <linux/string.h>
39#include <linux/ioport.h>
40#include <asm/io.h>
41#include <scsi/scsi.h>
42#include <linux/major.h>
43#include <linux/blkdev.h>
44#include <scsi/scsi_ioctl.h>
45#include <linux/interrupt.h>
46
47#include "scsi.h"
48#include <scsi/scsi_host.h>
49#include "../qlogicfas408.h"
50
51#include <pcmcia/cs_types.h>
52#include <pcmcia/cs.h>
53#include <pcmcia/cistpl.h>
54#include <pcmcia/ds.h>
55#include <pcmcia/ciscode.h>
56
57/* Set the following to 2 to use normal interrupt (active high/totempole-
58 * tristate), otherwise use 0 (REQUIRED FOR PCMCIA) for active low, open
59 * drain
60 */
61#define INT_TYPE	0
62
63static char qlogic_name[] = "qlogic_cs";
64
65#ifdef PCMCIA_DEBUG
66static int pc_debug = PCMCIA_DEBUG;
67module_param(pc_debug, int, 0644);
68#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
69static char *version = "qlogic_cs.c 1.79-ac 2002/10/26 (David Hinds)";
70#else
71#define DEBUG(n, args...)
72#endif
73
74static struct scsi_host_template qlogicfas_driver_template = {
75	.module			= THIS_MODULE,
76	.name			= qlogic_name,
77	.proc_name		= qlogic_name,
78	.info			= qlogicfas408_info,
79	.queuecommand		= qlogicfas408_queuecommand,
80	.eh_abort_handler	= qlogicfas408_abort,
81	.eh_bus_reset_handler	= qlogicfas408_bus_reset,
82	.bios_param		= qlogicfas408_biosparam,
83	.can_queue		= 1,
84	.this_id		= -1,
85	.sg_tablesize		= SG_ALL,
86	.cmd_per_lun		= 1,
87	.use_clustering		= DISABLE_CLUSTERING,
88};
89
90/*====================================================================*/
91
92typedef struct scsi_info_t {
93	struct pcmcia_device	*p_dev;
94	dev_node_t node;
95	struct Scsi_Host *host;
96	unsigned short manf_id;
97} scsi_info_t;
98
99static void qlogic_release(struct pcmcia_device *link);
100static void qlogic_detach(struct pcmcia_device *p_dev);
101static int qlogic_config(struct pcmcia_device * link);
102
103static struct Scsi_Host *qlogic_detect(struct scsi_host_template *host,
104				struct pcmcia_device *link, int qbase, int qlirq)
105{
106	int qltyp;		/* type of chip */
107	int qinitid;
108	struct Scsi_Host *shost;	/* registered host structure */
109	struct qlogicfas408_priv *priv;
110
111	qltyp = qlogicfas408_get_chip_type(qbase, INT_TYPE);
112	qinitid = host->this_id;
113	if (qinitid < 0)
114		qinitid = 7;	/* if no ID, use 7 */
115
116	qlogicfas408_setup(qbase, qinitid, INT_TYPE);
117
118	host->name = qlogic_name;
119	shost = scsi_host_alloc(host, sizeof(struct qlogicfas408_priv));
120	if (!shost)
121		goto err;
122	shost->io_port = qbase;
123	shost->n_io_port = 16;
124	shost->dma_channel = -1;
125	if (qlirq != -1)
126		shost->irq = qlirq;
127
128	priv = get_priv_by_host(shost);
129	priv->qlirq = qlirq;
130	priv->qbase = qbase;
131	priv->qinitid = qinitid;
132	priv->shost = shost;
133	priv->int_type = INT_TYPE;
134
135	if (request_irq(qlirq, qlogicfas408_ihandl, 0, qlogic_name, shost))
136		goto free_scsi_host;
137
138	sprintf(priv->qinfo,
139		"Qlogicfas Driver version 0.46, chip %02X at %03X, IRQ %d, TPdma:%d",
140		qltyp, qbase, qlirq, QL_TURBO_PDMA);
141
142	if (scsi_add_host(shost, NULL))
143		goto free_interrupt;
144
145	scsi_scan_host(shost);
146
147	return shost;
148
149free_interrupt:
150	free_irq(qlirq, shost);
151
152free_scsi_host:
153	scsi_host_put(shost);
154
155err:
156	return NULL;
157}
158static int qlogic_probe(struct pcmcia_device *link)
159{
160	scsi_info_t *info;
161
162	DEBUG(0, "qlogic_attach()\n");
163
164	/* Create new SCSI device */
165	info = kmalloc(sizeof(*info), GFP_KERNEL);
166	if (!info)
167		return -ENOMEM;
168	memset(info, 0, sizeof(*info));
169	info->p_dev = link;
170	link->priv = info;
171	link->io.NumPorts1 = 16;
172	link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
173	link->io.IOAddrLines = 10;
174	link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
175	link->irq.IRQInfo1 = IRQ_LEVEL_ID;
176	link->conf.Attributes = CONF_ENABLE_IRQ;
177	link->conf.IntType = INT_MEMORY_AND_IO;
178	link->conf.Present = PRESENT_OPTION;
179
180	return qlogic_config(link);
181}				/* qlogic_attach */
182
183/*====================================================================*/
184
185static void qlogic_detach(struct pcmcia_device *link)
186{
187	DEBUG(0, "qlogic_detach(0x%p)\n", link);
188
189	qlogic_release(link);
190	kfree(link->priv);
191
192}				/* qlogic_detach */
193
194/*====================================================================*/
195
196#define CS_CHECK(fn, ret) \
197do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
198
199static int qlogic_config(struct pcmcia_device * link)
200{
201	scsi_info_t *info = link->priv;
202	tuple_t tuple;
203	cisparse_t parse;
204	int i, last_ret, last_fn;
205	unsigned short tuple_data[32];
206	struct Scsi_Host *host;
207
208	DEBUG(0, "qlogic_config(0x%p)\n", link);
209
210	info->manf_id = link->manf_id;
211
212	tuple.TupleData = (cisdata_t *) tuple_data;
213	tuple.TupleDataMax = 64;
214	tuple.TupleOffset = 0;
215
216	tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
217	CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
218	while (1) {
219		if (pcmcia_get_tuple_data(link, &tuple) != 0 ||
220				pcmcia_parse_tuple(link, &tuple, &parse) != 0)
221			goto next_entry;
222		link->conf.ConfigIndex = parse.cftable_entry.index;
223		link->io.BasePort1 = parse.cftable_entry.io.win[0].base;
224		link->io.NumPorts1 = parse.cftable_entry.io.win[0].len;
225		if (link->io.BasePort1 != 0) {
226			i = pcmcia_request_io(link, &link->io);
227			if (i == CS_SUCCESS)
228				break;
229		}
230	      next_entry:
231		CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(link, &tuple));
232	}
233
234	CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
235	CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
236
237	if ((info->manf_id == MANFID_MACNICA) || (info->manf_id == MANFID_PIONEER) || (info->manf_id == 0x0098)) {
238		/* set ATAcmd */
239		outb(0xb4, link->io.BasePort1 + 0xd);
240		outb(0x24, link->io.BasePort1 + 0x9);
241		outb(0x04, link->io.BasePort1 + 0xd);
242	}
243
244	/* The KXL-810AN has a bigger IO port window */
245	if (link->io.NumPorts1 == 32)
246		host = qlogic_detect(&qlogicfas_driver_template, link,
247			link->io.BasePort1 + 16, link->irq.AssignedIRQ);
248	else
249		host = qlogic_detect(&qlogicfas_driver_template, link,
250			link->io.BasePort1, link->irq.AssignedIRQ);
251
252	if (!host) {
253		printk(KERN_INFO "%s: no SCSI devices found\n", qlogic_name);
254		goto cs_failed;
255	}
256
257	sprintf(info->node.dev_name, "scsi%d", host->host_no);
258	link->dev_node = &info->node;
259	info->host = host;
260
261	return 0;
262
263cs_failed:
264	cs_error(link, last_fn, last_ret);
265	pcmcia_disable_device(link);
266	return -ENODEV;
267
268}				/* qlogic_config */
269
270/*====================================================================*/
271
272static void qlogic_release(struct pcmcia_device *link)
273{
274	scsi_info_t *info = link->priv;
275
276	DEBUG(0, "qlogic_release(0x%p)\n", link);
277
278	scsi_remove_host(info->host);
279
280	free_irq(link->irq.AssignedIRQ, info->host);
281	pcmcia_disable_device(link);
282
283	scsi_host_put(info->host);
284}
285
286/*====================================================================*/
287
288static int qlogic_resume(struct pcmcia_device *link)
289{
290	scsi_info_t *info = link->priv;
291
292	pcmcia_request_configuration(link, &link->conf);
293	if ((info->manf_id == MANFID_MACNICA) ||
294	    (info->manf_id == MANFID_PIONEER) ||
295	    (info->manf_id == 0x0098)) {
296		outb(0x80, link->io.BasePort1 + 0xd);
297		outb(0x24, link->io.BasePort1 + 0x9);
298		outb(0x04, link->io.BasePort1 + 0xd);
299	}
300	/* Ugggglllyyyy!!! */
301	qlogicfas408_bus_reset(NULL);
302
303	return 0;
304}
305
306static struct pcmcia_device_id qlogic_ids[] = {
307	PCMCIA_DEVICE_PROD_ID12("EIger Labs", "PCMCIA-to-SCSI Adapter", 0x88395fa7, 0x33b7a5e6),
308	PCMCIA_DEVICE_PROD_ID12("EPSON", "SCSI-2 PC Card SC200", 0xd361772f, 0x299d1751),
309	PCMCIA_DEVICE_PROD_ID12("MACNICA", "MIRACLE SCSI-II mPS110", 0x20841b68, 0xab3c3b6d),
310	PCMCIA_DEVICE_PROD_ID12("MIDORI ELECTRONICS ", "CN-SC43", 0x6534382a, 0xd67eee79),
311	PCMCIA_DEVICE_PROD_ID12("NEC", "PC-9801N-J03R", 0x18df0ba0, 0x24662e8a),
312	PCMCIA_DEVICE_PROD_ID12("KME ", "KXLC003", 0x82375a27, 0xf68e5bf7),
313	PCMCIA_DEVICE_PROD_ID12("KME ", "KXLC004", 0x82375a27, 0x68eace54),
314	PCMCIA_DEVICE_PROD_ID12("KME", "KXLC101", 0x3faee676, 0x194250ec),
315	PCMCIA_DEVICE_PROD_ID12("QLOGIC CORPORATION", "pc05", 0xd77b2930, 0xa85b2735),
316	PCMCIA_DEVICE_PROD_ID12("QLOGIC CORPORATION", "pc05 rev 1.10", 0xd77b2930, 0x70f8b5f8),
317	PCMCIA_DEVICE_PROD_ID123("KME", "KXLC002", "00", 0x3faee676, 0x81896b61, 0xf99f065f),
318	PCMCIA_DEVICE_PROD_ID12("RATOC System Inc.", "SCSI2 CARD 37", 0x85c10e17, 0x1a2640c1),
319	PCMCIA_DEVICE_PROD_ID12("TOSHIBA", "SCSC200A PC CARD SCSI", 0xb4585a1a, 0xa6f06ebe),
320	PCMCIA_DEVICE_PROD_ID12("TOSHIBA", "SCSC200B PC CARD SCSI-10", 0xb4585a1a, 0x0a88dea0),
321	/* these conflict with other cards! */
322	/* PCMCIA_DEVICE_PROD_ID123("MACNICA", "MIRACLE SCSI", "mPS100", 0x20841b68, 0xf8dedaeb, 0x89f7fafb), */
323	/* PCMCIA_DEVICE_PROD_ID123("MACNICA", "MIRACLE SCSI", "mPS100", 0x20841b68, 0xf8dedaeb, 0x89f7fafb), */
324	PCMCIA_DEVICE_NULL,
325};
326MODULE_DEVICE_TABLE(pcmcia, qlogic_ids);
327
328static struct pcmcia_driver qlogic_cs_driver = {
329	.owner		= THIS_MODULE,
330	.drv		= {
331	.name		= "qlogic_cs",
332	},
333	.probe		= qlogic_probe,
334	.remove		= qlogic_detach,
335	.id_table       = qlogic_ids,
336	.resume		= qlogic_resume,
337};
338
339static int __init init_qlogic_cs(void)
340{
341	return pcmcia_register_driver(&qlogic_cs_driver);
342}
343
344static void __exit exit_qlogic_cs(void)
345{
346	pcmcia_unregister_driver(&qlogic_cs_driver);
347}
348
349MODULE_AUTHOR("Tom Zerucha, Michael Griffith");
350MODULE_DESCRIPTION("Driver for the PCMCIA Qlogic FAS SCSI controllers");
351MODULE_LICENSE("GPL");
352module_init(init_qlogic_cs);
353module_exit(exit_qlogic_cs);
354