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