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>
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
);
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)";
71 #define DEBUG(n, args...)
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
{
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)
120 client_reg_t client_reg
;
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
;
142 for (i
= 0; i
< 4; i
++)
143 link
->irq
.IRQInfo2
|= 1 << irq_list
[i
];
144 link
->conf
.Attributes
= CONF_ENABLE_IRQ
;
146 link
->conf
.IntType
= INT_MEMORY_AND_IO
;
147 link
->conf
.Present
= PRESENT_OPTION
;
149 /* Register with Card Services */
150 link
->next
= dev_list
;
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
);
163 cs_error(link
->handle
, RegisterClient
, ret
);
169 } /* qlogic_attach */
171 /*====================================================================*/
173 static void qlogic_detach(dev_link_t
*link
)
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;
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
;
195 CardServices(DeregisterClient
, link
->handle
);
197 /* Unlink device structure, free bits */
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
;
217 int i
, last_ret
, last_fn
;
218 u_short tuple_data
[32];
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]);
240 driver_template
.module
= &__this_module
;
241 link
->state
|= DEV_CONFIG
;
243 tuple
.DesiredTuple
= CISTPL_CFTABLE_ENTRY
;
244 CS_CHECK(GetFirstTuple
, handle
, &tuple
);
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;
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)) {
266 outb(0xb4, link
->io
.BasePort1
+0xd);
267 outb(0x24, link
->io
.BasePort1
+0x9);
268 outb(0x04, link
->io
.BasePort1
+0xd);
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
);
278 qlogicfas_preset(link
->io
.BasePort1
, link
->irq
.AssignedIRQ
);
280 scsi_register_module(MODULE_SCSI_HA
, &driver_template
);
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
) {
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
];
295 node
->major
= SCSI_TAPE_MAJOR
;
296 sprintf(node
->dev_name
, "st#%04lx", id
);
300 node
->major
= SCSI_DISK0_MAJOR
;
301 sprintf(node
->dev_name
, "sd#%04lx", id
);
305 node
->major
= SCSI_CDROM_MAJOR
;
306 sprintf(node
->dev_name
, "sr#%04lx", id
);
309 node
->major
= SCSI_GENERIC_MAJOR
;
310 sprintf(node
->dev_name
, "sg#%04lx", id
);
313 *tail
= node
; tail
= &node
->next
;
318 printk(KERN_INFO
"qlogic_cs: no SCSI devices found\n");
320 link
->state
&= ~DEV_CONFIG_PENDING
;
324 cs_error(link
->handle
, last_fn
, last_ret
);
325 qlogic_release((u_long
)link
);
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
;
344 scsi_unregister_module(MODULE_SCSI_HA
, &driver_template
);
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
)
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
);
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);
372 case CS_EVENT_CARD_INSERTION
:
373 link
->state
|= DEV_PRESENT
| DEV_CONFIG_PENDING
;
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
);
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);
404 /*====================================================================*/
406 static int __init
init_qlogic_cs(void) {
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");
415 register_pccard_driver(&dev_info
, &qlogic_attach
, &qlogic_detach
);
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
);