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