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/cistpl.h>
52 #include <pcmcia/ds.h>
53 #include <pcmcia/ciscode.h>
54 
55 /* Set the following to 2 to use normal interrupt (active high/totempole-
56  * tristate), otherwise use 0 (REQUIRED FOR PCMCIA) for active low, open
57  * drain
58  */
59 #define INT_TYPE	0
60 
61 static char qlogic_name[] = "qlogic_cs";
62 
63 static struct scsi_host_template qlogicfas_driver_template = {
64 	.module			= THIS_MODULE,
65 	.name			= qlogic_name,
66 	.proc_name		= qlogic_name,
67 	.info			= qlogicfas408_info,
68 	.queuecommand		= qlogicfas408_queuecommand,
69 	.eh_abort_handler	= qlogicfas408_abort,
70 	.eh_bus_reset_handler	= qlogicfas408_bus_reset,
71 	.bios_param		= qlogicfas408_biosparam,
72 	.can_queue		= 1,
73 	.this_id		= -1,
74 	.sg_tablesize		= SG_ALL,
75 	.cmd_per_lun		= 1,
76 	.use_clustering		= DISABLE_CLUSTERING,
77 };
78 
79 /*====================================================================*/
80 
81 typedef struct scsi_info_t {
82 	struct pcmcia_device	*p_dev;
83 	struct Scsi_Host *host;
84 	unsigned short manf_id;
85 } scsi_info_t;
86 
87 static void qlogic_release(struct pcmcia_device *link);
88 static void qlogic_detach(struct pcmcia_device *p_dev);
89 static int qlogic_config(struct pcmcia_device * link);
90 
91 static struct Scsi_Host *qlogic_detect(struct scsi_host_template *host,
92 				struct pcmcia_device *link, int qbase, int qlirq)
93 {
94 	int qltyp;		/* type of chip */
95 	int qinitid;
96 	struct Scsi_Host *shost;	/* registered host structure */
97 	struct qlogicfas408_priv *priv;
98 
99 	qltyp = qlogicfas408_get_chip_type(qbase, INT_TYPE);
100 	qinitid = host->this_id;
101 	if (qinitid < 0)
102 		qinitid = 7;	/* if no ID, use 7 */
103 
104 	qlogicfas408_setup(qbase, qinitid, INT_TYPE);
105 
106 	host->name = qlogic_name;
107 	shost = scsi_host_alloc(host, sizeof(struct qlogicfas408_priv));
108 	if (!shost)
109 		goto err;
110 	shost->io_port = qbase;
111 	shost->n_io_port = 16;
112 	shost->dma_channel = -1;
113 	if (qlirq != -1)
114 		shost->irq = qlirq;
115 
116 	priv = get_priv_by_host(shost);
117 	priv->qlirq = qlirq;
118 	priv->qbase = qbase;
119 	priv->qinitid = qinitid;
120 	priv->shost = shost;
121 	priv->int_type = INT_TYPE;
122 
123 	if (request_irq(qlirq, qlogicfas408_ihandl, 0, qlogic_name, shost))
124 		goto free_scsi_host;
125 
126 	sprintf(priv->qinfo,
127 		"Qlogicfas Driver version 0.46, chip %02X at %03X, IRQ %d, TPdma:%d",
128 		qltyp, qbase, qlirq, QL_TURBO_PDMA);
129 
130 	if (scsi_add_host(shost, NULL))
131 		goto free_interrupt;
132 
133 	scsi_scan_host(shost);
134 
135 	return shost;
136 
137 free_interrupt:
138 	free_irq(qlirq, shost);
139 
140 free_scsi_host:
141 	scsi_host_put(shost);
142 
143 err:
144 	return NULL;
145 }
146 static int qlogic_probe(struct pcmcia_device *link)
147 {
148 	scsi_info_t *info;
149 
150 	dev_dbg(&link->dev, "qlogic_attach()\n");
151 
152 	/* Create new SCSI device */
153 	info = kzalloc(sizeof(*info), GFP_KERNEL);
154 	if (!info)
155 		return -ENOMEM;
156 	info->p_dev = link;
157 	link->priv = info;
158 	link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
159 	link->config_regs = PRESENT_OPTION;
160 
161 	return qlogic_config(link);
162 }				/* qlogic_attach */
163 
164 /*====================================================================*/
165 
166 static void qlogic_detach(struct pcmcia_device *link)
167 {
168 	dev_dbg(&link->dev, "qlogic_detach\n");
169 
170 	qlogic_release(link);
171 	kfree(link->priv);
172 
173 }				/* qlogic_detach */
174 
175 /*====================================================================*/
176 
177 static int qlogic_config_check(struct pcmcia_device *p_dev, void *priv_data)
178 {
179 	p_dev->io_lines = 10;
180 	p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
181 	p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO;
182 
183 	if (p_dev->resource[0]->start == 0)
184 		return -ENODEV;
185 
186 	return pcmcia_request_io(p_dev);
187 }
188 
189 static int qlogic_config(struct pcmcia_device * link)
190 {
191 	scsi_info_t *info = link->priv;
192 	int ret;
193 	struct Scsi_Host *host;
194 
195 	dev_dbg(&link->dev, "qlogic_config\n");
196 
197 	ret = pcmcia_loop_config(link, qlogic_config_check, NULL);
198 	if (ret)
199 		goto failed;
200 
201 	if (!link->irq)
202 		goto failed;
203 
204 	ret = pcmcia_enable_device(link);
205 	if (ret)
206 		goto failed;
207 
208 	if ((info->manf_id == MANFID_MACNICA) || (info->manf_id == MANFID_PIONEER) || (info->manf_id == 0x0098)) {
209 		/* set ATAcmd */
210 		outb(0xb4, link->resource[0]->start + 0xd);
211 		outb(0x24, link->resource[0]->start + 0x9);
212 		outb(0x04, link->resource[0]->start + 0xd);
213 	}
214 
215 	/* The KXL-810AN has a bigger IO port window */
216 	if (resource_size(link->resource[0]) == 32)
217 		host = qlogic_detect(&qlogicfas_driver_template, link,
218 			link->resource[0]->start + 16, link->irq);
219 	else
220 		host = qlogic_detect(&qlogicfas_driver_template, link,
221 			link->resource[0]->start, link->irq);
222 
223 	if (!host) {
224 		printk(KERN_INFO "%s: no SCSI devices found\n", qlogic_name);
225 		goto failed;
226 	}
227 
228 	info->host = host;
229 
230 	return 0;
231 
232 failed:
233 	pcmcia_disable_device(link);
234 	return -ENODEV;
235 }				/* qlogic_config */
236 
237 /*====================================================================*/
238 
239 static void qlogic_release(struct pcmcia_device *link)
240 {
241 	scsi_info_t *info = link->priv;
242 
243 	dev_dbg(&link->dev, "qlogic_release\n");
244 
245 	scsi_remove_host(info->host);
246 
247 	free_irq(link->irq, info->host);
248 	pcmcia_disable_device(link);
249 
250 	scsi_host_put(info->host);
251 }
252 
253 /*====================================================================*/
254 
255 static int qlogic_resume(struct pcmcia_device *link)
256 {
257 	scsi_info_t *info = link->priv;
258 
259 	pcmcia_enable_device(link);
260 	if ((info->manf_id == MANFID_MACNICA) ||
261 	    (info->manf_id == MANFID_PIONEER) ||
262 	    (info->manf_id == 0x0098)) {
263 		outb(0x80, link->resource[0]->start + 0xd);
264 		outb(0x24, link->resource[0]->start + 0x9);
265 		outb(0x04, link->resource[0]->start + 0xd);
266 	}
267 	/* Ugggglllyyyy!!! */
268 	qlogicfas408_bus_reset(NULL);
269 
270 	return 0;
271 }
272 
273 static const struct pcmcia_device_id qlogic_ids[] = {
274 	PCMCIA_DEVICE_PROD_ID12("EIger Labs", "PCMCIA-to-SCSI Adapter", 0x88395fa7, 0x33b7a5e6),
275 	PCMCIA_DEVICE_PROD_ID12("EPSON", "SCSI-2 PC Card SC200", 0xd361772f, 0x299d1751),
276 	PCMCIA_DEVICE_PROD_ID12("MACNICA", "MIRACLE SCSI-II mPS110", 0x20841b68, 0xab3c3b6d),
277 	PCMCIA_DEVICE_PROD_ID12("MIDORI ELECTRONICS ", "CN-SC43", 0x6534382a, 0xd67eee79),
278 	PCMCIA_DEVICE_PROD_ID12("NEC", "PC-9801N-J03R", 0x18df0ba0, 0x24662e8a),
279 	PCMCIA_DEVICE_PROD_ID12("KME ", "KXLC003", 0x82375a27, 0xf68e5bf7),
280 	PCMCIA_DEVICE_PROD_ID12("KME ", "KXLC004", 0x82375a27, 0x68eace54),
281 	PCMCIA_DEVICE_PROD_ID12("KME", "KXLC101", 0x3faee676, 0x194250ec),
282 	PCMCIA_DEVICE_PROD_ID12("QLOGIC CORPORATION", "pc05", 0xd77b2930, 0xa85b2735),
283 	PCMCIA_DEVICE_PROD_ID12("QLOGIC CORPORATION", "pc05 rev 1.10", 0xd77b2930, 0x70f8b5f8),
284 	PCMCIA_DEVICE_PROD_ID123("KME", "KXLC002", "00", 0x3faee676, 0x81896b61, 0xf99f065f),
285 	PCMCIA_DEVICE_PROD_ID12("RATOC System Inc.", "SCSI2 CARD 37", 0x85c10e17, 0x1a2640c1),
286 	PCMCIA_DEVICE_PROD_ID12("TOSHIBA", "SCSC200A PC CARD SCSI", 0xb4585a1a, 0xa6f06ebe),
287 	PCMCIA_DEVICE_PROD_ID12("TOSHIBA", "SCSC200B PC CARD SCSI-10", 0xb4585a1a, 0x0a88dea0),
288 	/* these conflict with other cards! */
289 	/* PCMCIA_DEVICE_PROD_ID123("MACNICA", "MIRACLE SCSI", "mPS100", 0x20841b68, 0xf8dedaeb, 0x89f7fafb), */
290 	/* PCMCIA_DEVICE_PROD_ID123("MACNICA", "MIRACLE SCSI", "mPS100", 0x20841b68, 0xf8dedaeb, 0x89f7fafb), */
291 	PCMCIA_DEVICE_NULL,
292 };
293 MODULE_DEVICE_TABLE(pcmcia, qlogic_ids);
294 
295 static struct pcmcia_driver qlogic_cs_driver = {
296 	.owner		= THIS_MODULE,
297 	.name		= "qlogic_cs",
298 	.probe		= qlogic_probe,
299 	.remove		= qlogic_detach,
300 	.id_table       = qlogic_ids,
301 	.resume		= qlogic_resume,
302 };
303 
304 static int __init init_qlogic_cs(void)
305 {
306 	return pcmcia_register_driver(&qlogic_cs_driver);
307 }
308 
309 static void __exit exit_qlogic_cs(void)
310 {
311 	pcmcia_unregister_driver(&qlogic_cs_driver);
312 }
313 
314 MODULE_AUTHOR("Tom Zerucha, Michael Griffith");
315 MODULE_DESCRIPTION("Driver for the PCMCIA Qlogic FAS SCSI controllers");
316 MODULE_LICENSE("GPL");
317 module_init(init_qlogic_cs);
318 module_exit(exit_qlogic_cs);
319