blob: 538fedb9792485ce62249376c4c0abad3187f76e [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*======================================================================
2
3 A driver for Future Domain-compatible PCMCIA SCSI cards
4
5 fdomain_cs.c 1.47 2001/10/13 00:08:52
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
23 which 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/sched.h>
38#include <linux/slab.h>
39#include <linux/string.h>
40#include <linux/ioport.h>
41#include <scsi/scsi.h>
42#include <linux/major.h>
43#include <linux/blkdev.h>
44#include <scsi/scsi_ioctl.h>
45
46#include "scsi.h"
47#include <scsi/scsi_host.h>
48#include "fdomain.h"
49
Linus Torvalds1da177e2005-04-16 15:20:36 -070050#include <pcmcia/cs_types.h>
51#include <pcmcia/cs.h>
52#include <pcmcia/cistpl.h>
53#include <pcmcia/ds.h>
54
55/*====================================================================*/
56
57/* Module parameters */
58
59MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
60MODULE_DESCRIPTION("Future Domain PCMCIA SCSI driver");
61MODULE_LICENSE("Dual MPL/GPL");
62
63#ifdef PCMCIA_DEBUG
64static int pc_debug = PCMCIA_DEBUG;
65module_param(pc_debug, int, 0);
66#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
67static char *version =
68"fdomain_cs.c 1.47 2001/10/13 00:08:52 (David Hinds)";
69#else
70#define DEBUG(n, args...)
71#endif
72
73/*====================================================================*/
74
75typedef struct scsi_info_t {
76 dev_link_t link;
77 dev_node_t node;
78 struct Scsi_Host *host;
79} scsi_info_t;
80
81
82static void fdomain_release(dev_link_t *link);
83static int fdomain_event(event_t event, int priority,
84 event_callback_args_t *args);
85
86static dev_link_t *fdomain_attach(void);
Dominik Brodowskicc3b4862005-11-14 21:23:14 +010087static void fdomain_detach(struct pcmcia_device *p_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -070088
89
Linus Torvalds1da177e2005-04-16 15:20:36 -070090static dev_info_t dev_info = "fdomain_cs";
91
92static dev_link_t *fdomain_attach(void)
93{
94 scsi_info_t *info;
95 client_reg_t client_reg;
96 dev_link_t *link;
97 int ret;
98
99 DEBUG(0, "fdomain_attach()\n");
100
101 /* Create new SCSI device */
102 info = kmalloc(sizeof(*info), GFP_KERNEL);
103 if (!info) return NULL;
104 memset(info, 0, sizeof(*info));
105 link = &info->link; link->priv = info;
106 link->io.NumPorts1 = 0x10;
107 link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
108 link->io.IOAddrLines = 10;
109 link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
110 link->irq.IRQInfo1 = IRQ_LEVEL_ID;
111 link->conf.Attributes = CONF_ENABLE_IRQ;
112 link->conf.Vcc = 50;
113 link->conf.IntType = INT_MEMORY_AND_IO;
114 link->conf.Present = PRESENT_OPTION;
115
116 /* Register with Card Services */
Dominik Brodowskib4635812005-11-14 21:25:35 +0100117 link->next = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118 client_reg.dev_info = &dev_info;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119 client_reg.Version = 0x0210;
120 client_reg.event_callback_args.client_data = link;
121 ret = pcmcia_register_client(&link->handle, &client_reg);
122 if (ret != 0) {
123 cs_error(link->handle, RegisterClient, ret);
Dominik Brodowskicc3b4862005-11-14 21:23:14 +0100124 fdomain_detach(link->handle);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125 return NULL;
126 }
127
128 return link;
129} /* fdomain_attach */
130
131/*====================================================================*/
132
Dominik Brodowskicc3b4862005-11-14 21:23:14 +0100133static void fdomain_detach(struct pcmcia_device *p_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134{
Dominik Brodowskib4635812005-11-14 21:25:35 +0100135 dev_link_t *link = dev_to_instance(p_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136
Dominik Brodowskib4635812005-11-14 21:25:35 +0100137 DEBUG(0, "fdomain_detach(0x%p)\n", link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138
Dominik Brodowskib4635812005-11-14 21:25:35 +0100139 if (link->state & DEV_CONFIG)
140 fdomain_release(link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141
Dominik Brodowskib4635812005-11-14 21:25:35 +0100142 kfree(link->priv);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143} /* fdomain_detach */
144
145/*====================================================================*/
146
147#define CS_CHECK(fn, ret) \
148do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
149
150static void fdomain_config(dev_link_t *link)
151{
152 client_handle_t handle = link->handle;
153 scsi_info_t *info = link->priv;
154 tuple_t tuple;
155 cisparse_t parse;
156 int i, last_ret, last_fn;
157 u_char tuple_data[64];
158 char str[16];
159 struct Scsi_Host *host;
160
161 DEBUG(0, "fdomain_config(0x%p)\n", link);
162
163 tuple.DesiredTuple = CISTPL_CONFIG;
164 tuple.TupleData = tuple_data;
165 tuple.TupleDataMax = 64;
166 tuple.TupleOffset = 0;
167 CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
168 CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
169 CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
170 link->conf.ConfigBase = parse.config.base;
171
172 /* Configure card */
173 link->state |= DEV_CONFIG;
174
175 tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
176 CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
177 while (1) {
178 if (pcmcia_get_tuple_data(handle, &tuple) != 0 ||
179 pcmcia_parse_tuple(handle, &tuple, &parse) != 0)
180 goto next_entry;
181 link->conf.ConfigIndex = parse.cftable_entry.index;
182 link->io.BasePort1 = parse.cftable_entry.io.win[0].base;
183 i = pcmcia_request_io(handle, &link->io);
184 if (i == CS_SUCCESS) break;
185 next_entry:
186 CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple));
187 }
188
189 CS_CHECK(RequestIRQ, pcmcia_request_irq(handle, &link->irq));
190 CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf));
191
192 /* A bad hack... */
193 release_region(link->io.BasePort1, link->io.NumPorts1);
194
195 /* Set configuration options for the fdomain driver */
196 sprintf(str, "%d,%d", link->io.BasePort1, link->irq.AssignedIRQ);
197 fdomain_setup(str);
198
199 host = __fdomain_16x0_detect(&fdomain_driver_template);
200 if (!host) {
201 printk(KERN_INFO "fdomain_cs: no SCSI devices found\n");
202 goto cs_failed;
203 }
204
205 scsi_add_host(host, NULL); /* XXX handle failure */
206 scsi_scan_host(host);
207
208 sprintf(info->node.dev_name, "scsi%d", host->host_no);
209 link->dev = &info->node;
210 info->host = host;
211
212 link->state &= ~DEV_CONFIG_PENDING;
213 return;
214
215cs_failed:
216 cs_error(link->handle, last_fn, last_ret);
217 fdomain_release(link);
218 return;
219
220} /* fdomain_config */
221
222/*====================================================================*/
223
224static void fdomain_release(dev_link_t *link)
225{
226 scsi_info_t *info = link->priv;
227
228 DEBUG(0, "fdomain_release(0x%p)\n", link);
229
230 scsi_remove_host(info->host);
231 link->dev = NULL;
232
233 pcmcia_release_configuration(link->handle);
234 pcmcia_release_io(link->handle, &link->io);
235 pcmcia_release_irq(link->handle, &link->irq);
236
237 scsi_unregister(info->host);
238
239 link->state &= ~DEV_CONFIG;
240}
241
242/*====================================================================*/
243
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100244static int fdomain_suspend(struct pcmcia_device *dev)
245{
246 dev_link_t *link = dev_to_instance(dev);
247
248 link->state |= DEV_SUSPEND;
249 if (link->state & DEV_CONFIG)
250 pcmcia_release_configuration(link->handle);
251
252 return 0;
253}
254
255static int fdomain_resume(struct pcmcia_device *dev)
256{
257 dev_link_t *link = dev_to_instance(dev);
258
259 link->state &= ~DEV_SUSPEND;
260 if (link->state & DEV_CONFIG) {
261 pcmcia_request_configuration(link->handle, &link->conf);
262 fdomain_16x0_bus_reset(NULL);
263 }
264
265 return 0;
266}
267
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268static int fdomain_event(event_t event, int priority,
269 event_callback_args_t *args)
270{
271 dev_link_t *link = args->client_data;
272
273 DEBUG(1, "fdomain_event(0x%06x)\n", event);
274
275 switch (event) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276 case CS_EVENT_CARD_INSERTION:
277 link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
278 fdomain_config(link);
279 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280 }
281 return 0;
282} /* fdomain_event */
283
Dominik Brodowski2d1fb372005-06-27 16:28:21 -0700284
285static struct pcmcia_device_id fdomain_ids[] = {
286 PCMCIA_DEVICE_PROD_ID12("IBM Corp.", "SCSI PCMCIA Card", 0xe3736c88, 0x859cad20),
287 PCMCIA_DEVICE_PROD_ID1("SCSI PCMCIA Adapter Card", 0x8dacb57e),
288 PCMCIA_DEVICE_PROD_ID12(" SIMPLE TECHNOLOGY Corporation", "SCSI PCMCIA Credit Card Controller", 0x182bdafe, 0xc80d106f),
289 PCMCIA_DEVICE_NULL,
290};
291MODULE_DEVICE_TABLE(pcmcia, fdomain_ids);
292
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293static struct pcmcia_driver fdomain_cs_driver = {
294 .owner = THIS_MODULE,
295 .drv = {
296 .name = "fdomain_cs",
297 },
298 .attach = fdomain_attach,
Dominik Brodowski1e212f32005-07-07 17:59:00 -0700299 .event = fdomain_event,
Dominik Brodowskicc3b4862005-11-14 21:23:14 +0100300 .remove = fdomain_detach,
Dominik Brodowski2d1fb372005-06-27 16:28:21 -0700301 .id_table = fdomain_ids,
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100302 .suspend = fdomain_suspend,
303 .resume = fdomain_resume,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304};
305
306static int __init init_fdomain_cs(void)
307{
308 return pcmcia_register_driver(&fdomain_cs_driver);
309}
310
311static void __exit exit_fdomain_cs(void)
312{
313 pcmcia_unregister_driver(&fdomain_cs_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314}
315
316module_init(init_fdomain_cs);
317module_exit(exit_fdomain_cs);