Ok. I didn't make 2.4.0 in 2000. Tough. I tried, but we had some
[davej-history.git] / drivers / scsi / pcmcia / qlogic_stub.c
blob1cfee657fea437568f7267ec9a9c6dac0f3346c9
1 /*======================================================================
3 A driver for the Qlogic SCSI card
5 qlogic_cs.c 1.79 2000/06/12 21:27:26
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/
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.
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.
21 Alternatively, the contents of this file may be used under the
22 terms of the GNU 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.
32 ======================================================================*/
34 #include <linux/module.h>
35 #include <linux/init.h>
36 #include <linux/kernel.h>
37 #include <linux/sched.h>
38 #include <linux/malloc.h>
39 #include <linux/string.h>
40 #include <linux/timer.h>
41 #include <linux/ioport.h>
42 #include <asm/io.h>
43 #include <scsi/scsi.h>
44 #include <linux/major.h>
45 #include <linux/blk.h>
47 #include <../drivers/scsi/scsi.h>
48 #include <../drivers/scsi/hosts.h>
49 #include <scsi/scsi_ioctl.h>
51 #include <../drivers/scsi/qlogicfas.h>
53 #define qlogic_reset(h) qlogicfas_reset(h, 0)
55 #include <pcmcia/version.h>
56 #include <pcmcia/cs_types.h>
57 #include <pcmcia/cs.h>
58 #include <pcmcia/cistpl.h>
59 #include <pcmcia/ds.h>
60 #include <pcmcia/ciscode.h>
62 extern void qlogicfas_preset(int port, int irq);
64 #ifdef PCMCIA_DEBUG
65 static int pc_debug = PCMCIA_DEBUG;
66 MODULE_PARM(pc_debug, "i");
67 #define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
68 static char *version =
69 "qlogic_cs.c 1.79 2000/06/12 21:27:26 (David Hinds)";
70 #else
71 #define DEBUG(n, args...)
72 #endif
74 /*====================================================================*/
76 /* Parameters that can be set with 'insmod' */
78 /* Bit map of interrupts to choose from */
79 static u_int irq_mask = 0xdeb8;
80 static int irq_list[4] = { -1 };
82 MODULE_PARM(irq_mask, "i");
83 MODULE_PARM(irq_list, "1-4i");
85 /*====================================================================*/
87 typedef struct scsi_info_t {
88 dev_link_t link;
89 u_short manf_id;
90 int ndev;
91 dev_node_t node[8];
92 } scsi_info_t;
94 static void qlogic_release(u_long arg);
95 static int qlogic_event(event_t event, int priority,
96 event_callback_args_t *args);
98 static dev_link_t *qlogic_attach(void);
99 static void qlogic_detach(dev_link_t *);
101 static Scsi_Host_Template driver_template = QLOGICFAS;
103 static dev_link_t *dev_list = NULL;
105 static dev_info_t dev_info = "qlogic_cs";
107 /*====================================================================*/
109 static void cs_error(client_handle_t handle, int func, int ret)
111 error_info_t err = { func, ret };
112 CardServices(ReportError, handle, &err);
115 /*====================================================================*/
117 static dev_link_t *qlogic_attach(void)
119 scsi_info_t *info;
120 client_reg_t client_reg;
121 dev_link_t *link;
122 int i, ret;
124 DEBUG(0, "qlogic_attach()\n");
126 /* Create new SCSI device */
127 info = kmalloc(sizeof(*info), GFP_KERNEL);
128 if (!info) return NULL;
129 memset(info, 0, sizeof(*info));
130 link = &info->link; link->priv = info;
131 link->release.function = &qlogic_release;
132 link->release.data = (u_long)link;
134 link->io.NumPorts1 = 16;
135 link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
136 link->io.IOAddrLines = 10;
137 link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
138 link->irq.IRQInfo1 = IRQ_INFO2_VALID|IRQ_LEVEL_ID;
139 if (irq_list[0] == -1)
140 link->irq.IRQInfo2 = irq_mask;
141 else
142 for (i = 0; i < 4; i++)
143 link->irq.IRQInfo2 |= 1 << irq_list[i];
144 link->conf.Attributes = CONF_ENABLE_IRQ;
145 link->conf.Vcc = 50;
146 link->conf.IntType = INT_MEMORY_AND_IO;
147 link->conf.Present = PRESENT_OPTION;
149 /* Register with Card Services */
150 link->next = dev_list;
151 dev_list = link;
152 client_reg.dev_info = &dev_info;
153 client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
154 client_reg.event_handler = &qlogic_event;
155 client_reg.EventMask =
156 CS_EVENT_RESET_REQUEST | CS_EVENT_CARD_RESET |
157 CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
158 CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
159 client_reg.Version = 0x0210;
160 client_reg.event_callback_args.client_data = link;
161 ret = CardServices(RegisterClient, &link->handle, &client_reg);
162 if (ret != 0) {
163 cs_error(link->handle, RegisterClient, ret);
164 qlogic_detach(link);
165 return NULL;
168 return link;
169 } /* qlogic_attach */
171 /*====================================================================*/
173 static void qlogic_detach(dev_link_t *link)
175 dev_link_t **linkp;
177 DEBUG(0, "qlogic_detach(0x%p)\n", link);
179 /* Locate device structure */
180 for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
181 if (*linkp == link) break;
182 if (*linkp == NULL)
183 return;
185 del_timer(&link->release);
186 if (link->state & DEV_CONFIG) {
187 qlogic_release((u_long)link);
188 if (link->state & DEV_STALE_CONFIG) {
189 link->state |= DEV_STALE_LINK;
190 return;
194 if (link->handle)
195 CardServices(DeregisterClient, link->handle);
197 /* Unlink device structure, free bits */
198 *linkp = link->next;
199 kfree(link->priv);
201 } /* qlogic_detach */
203 /*====================================================================*/
205 #define CS_CHECK(fn, args...) \
206 while ((last_ret=CardServices(last_fn=(fn), args))!=0) goto cs_failed
208 #define CFG_CHECK(fn, args...) \
209 if (CardServices(fn, args) != 0) goto next_entry
211 static void qlogic_config(dev_link_t *link)
213 client_handle_t handle = link->handle;
214 scsi_info_t *info = link->priv;
215 tuple_t tuple;
216 cisparse_t parse;
217 int i, last_ret, last_fn;
218 u_short tuple_data[32];
219 Scsi_Device *dev;
220 dev_node_t **tail, *node;
221 struct Scsi_Host *host;
223 DEBUG(0, "qlogic_config(0x%p)\n", link);
225 tuple.TupleData = (cisdata_t *)tuple_data;
226 tuple.TupleDataMax = 64;
227 tuple.TupleOffset = 0;
228 tuple.DesiredTuple = CISTPL_CONFIG;
229 CS_CHECK(GetFirstTuple, handle, &tuple);
230 CS_CHECK(GetTupleData, handle, &tuple);
231 CS_CHECK(ParseTuple, handle, &tuple, &parse);
232 link->conf.ConfigBase = parse.config.base;
234 tuple.DesiredTuple = CISTPL_MANFID;
235 if ((CardServices(GetFirstTuple, handle, &tuple) == CS_SUCCESS) &&
236 (CardServices(GetTupleData, handle, &tuple) == CS_SUCCESS))
237 info->manf_id = le16_to_cpu(tuple.TupleData[0]);
239 /* Configure card */
240 driver_template.module = &__this_module;
241 link->state |= DEV_CONFIG;
243 tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
244 CS_CHECK(GetFirstTuple, handle, &tuple);
245 while (1) {
246 CFG_CHECK(GetTupleData, handle, &tuple);
247 CFG_CHECK(ParseTuple, handle, &tuple, &parse);
248 link->conf.ConfigIndex = parse.cftable_entry.index;
249 link->io.BasePort1 = parse.cftable_entry.io.win[0].base;
250 link->io.NumPorts1 = parse.cftable_entry.io.win[0].len;
251 if (link->io.BasePort1 != 0) {
252 i = CardServices(RequestIO, handle, &link->io);
253 if (i == CS_SUCCESS) break;
255 next_entry:
256 CS_CHECK(GetNextTuple, handle, &tuple);
259 CS_CHECK(RequestIRQ, handle, &link->irq);
260 CS_CHECK(RequestConfiguration, handle, &link->conf);
262 if ((info->manf_id == MANFID_MACNICA) ||
263 (info->manf_id == MANFID_PIONEER) ||
264 (info->manf_id == 0x0098)) {
265 /* set ATAcmd */
266 outb(0xb4, link->io.BasePort1+0xd);
267 outb(0x24, link->io.BasePort1+0x9);
268 outb(0x04, link->io.BasePort1+0xd);
271 /* A bad hack... */
272 release_region(link->io.BasePort1, link->io.NumPorts1);
274 /* The KXL-810AN has a bigger IO port window */
275 if (link->io.NumPorts1 == 32)
276 qlogicfas_preset(link->io.BasePort1+16, link->irq.AssignedIRQ);
277 else
278 qlogicfas_preset(link->io.BasePort1, link->irq.AssignedIRQ);
280 scsi_register_module(MODULE_SCSI_HA, &driver_template);
282 tail = &link->dev;
283 info->ndev = 0;
284 for (host = scsi_hostlist; host; host = host->next)
285 if (host->hostt == &driver_template)
286 for (dev = host->host_queue; dev; dev = dev->next) {
287 u_long arg[2], id;
288 kernel_scsi_ioctl(dev, SCSI_IOCTL_GET_IDLUN, arg);
289 id = (arg[0]&0x0f) + ((arg[0]>>4)&0xf0) +
290 ((arg[0]>>8)&0xf00) + ((arg[0]>>12)&0xf000);
291 node = &info->node[info->ndev];
292 node->minor = 0;
293 switch (dev->type) {
294 case TYPE_TAPE:
295 node->major = SCSI_TAPE_MAJOR;
296 sprintf(node->dev_name, "st#%04lx", id);
297 break;
298 case TYPE_DISK:
299 case TYPE_MOD:
300 node->major = SCSI_DISK0_MAJOR;
301 sprintf(node->dev_name, "sd#%04lx", id);
302 break;
303 case TYPE_ROM:
304 case TYPE_WORM:
305 node->major = SCSI_CDROM_MAJOR;
306 sprintf(node->dev_name, "sr#%04lx", id);
307 break;
308 default:
309 node->major = SCSI_GENERIC_MAJOR;
310 sprintf(node->dev_name, "sg#%04lx", id);
311 break;
313 *tail = node; tail = &node->next;
314 info->ndev++;
316 *tail = NULL;
317 if (info->ndev == 0)
318 printk(KERN_INFO "qlogic_cs: no SCSI devices found\n");
320 link->state &= ~DEV_CONFIG_PENDING;
321 return;
323 cs_failed:
324 cs_error(link->handle, last_fn, last_ret);
325 qlogic_release((u_long)link);
326 return;
328 } /* qlogic_config */
330 /*====================================================================*/
332 static void qlogic_release(u_long arg)
334 dev_link_t *link = (dev_link_t *)arg;
336 DEBUG(0, "qlogic_release(0x%p)\n", link);
338 if (GET_USE_COUNT(&__this_module) != 0) {
339 DEBUG(0, "qlogic_cs: release postponed, device still open\n");
340 link->state |= DEV_STALE_CONFIG;
341 return;
344 scsi_unregister_module(MODULE_SCSI_HA, &driver_template);
345 link->dev = NULL;
347 CardServices(ReleaseConfiguration, link->handle);
348 CardServices(ReleaseIO, link->handle, &link->io);
349 CardServices(ReleaseIRQ, link->handle, &link->irq);
351 link->state &= ~DEV_CONFIG;
352 if (link->state & DEV_STALE_LINK)
353 qlogic_detach(link);
355 } /* qlogic_release */
357 /*====================================================================*/
359 static int qlogic_event(event_t event, int priority,
360 event_callback_args_t *args)
362 dev_link_t *link = args->client_data;
364 DEBUG(1, "qlogic_event(0x%06x)\n", event);
366 switch (event) {
367 case CS_EVENT_CARD_REMOVAL:
368 link->state &= ~DEV_PRESENT;
369 if (link->state & DEV_CONFIG)
370 mod_timer(&link->release, jiffies + HZ/20);
371 break;
372 case CS_EVENT_CARD_INSERTION:
373 link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
374 qlogic_config(link);
375 break;
376 case CS_EVENT_PM_SUSPEND:
377 link->state |= DEV_SUSPEND;
378 /* Fall through... */
379 case CS_EVENT_RESET_PHYSICAL:
380 if (link->state & DEV_CONFIG)
381 CardServices(ReleaseConfiguration, link->handle);
382 break;
383 case CS_EVENT_PM_RESUME:
384 link->state &= ~DEV_SUSPEND;
385 /* Fall through... */
386 case CS_EVENT_CARD_RESET:
387 if (link->state & DEV_CONFIG) {
388 scsi_info_t *info = link->priv;
389 CardServices(RequestConfiguration, link->handle, &link->conf);
390 if ((info->manf_id == MANFID_MACNICA) ||
391 (info->manf_id == MANFID_PIONEER) ||
392 (info->manf_id == 0x0098)) {
393 outb( 0x80, link->io.BasePort1+0xd);
394 outb( 0x24, link->io.BasePort1+0x9);
395 outb( 0x04, link->io.BasePort1+0xd);
397 qlogic_reset(NULL);
399 break;
401 return 0;
402 } /* qlogic_event */
404 /*====================================================================*/
406 static int __init init_qlogic_cs(void) {
407 servinfo_t serv;
408 DEBUG(0, "%s\n", version);
409 CardServices(GetCardServicesInfo, &serv);
410 if (serv.Revision != CS_RELEASE_CODE) {
411 printk(KERN_NOTICE "qlogic_cs: Card Services release "
412 "does not match!\n");
413 return -1;
415 register_pccard_driver(&dev_info, &qlogic_attach, &qlogic_detach);
416 return 0;
419 static void __exit exit_qlogic_cs(void) {
420 DEBUG(0, "qlogic_cs: unloading\n");
421 unregister_pccard_driver(&dev_info);
422 while (dev_list != NULL)
423 qlogic_detach(dev_list);
426 module_init(init_qlogic_cs);
427 module_exit(exit_qlogic_cs);