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