[PATCH] pcmcia: unify detach, REMOVAL_EVENT handlers into one remove callback
[firefly-linux-kernel-4.4.55.git] / drivers / scsi / pcmcia / fdomain_stub.c
1 /*======================================================================
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
50 #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
59 MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
60 MODULE_DESCRIPTION("Future Domain PCMCIA SCSI driver");
61 MODULE_LICENSE("Dual MPL/GPL");
62
63 #ifdef PCMCIA_DEBUG
64 static int pc_debug = PCMCIA_DEBUG;
65 module_param(pc_debug, int, 0);
66 #define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
67 static 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
75 typedef 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
82 static void fdomain_release(dev_link_t *link);
83 static int fdomain_event(event_t event, int priority,
84                         event_callback_args_t *args);
85
86 static dev_link_t *fdomain_attach(void);
87 static void fdomain_detach(struct pcmcia_device *p_dev);
88
89
90 static dev_link_t *dev_list = NULL;
91
92 static dev_info_t dev_info = "fdomain_cs";
93
94 static dev_link_t *fdomain_attach(void)
95 {
96     scsi_info_t *info;
97     client_reg_t client_reg;
98     dev_link_t *link;
99     int ret;
100     
101     DEBUG(0, "fdomain_attach()\n");
102
103     /* Create new SCSI device */
104     info = kmalloc(sizeof(*info), GFP_KERNEL);
105     if (!info) return NULL;
106     memset(info, 0, sizeof(*info));
107     link = &info->link; link->priv = info;
108     link->io.NumPorts1 = 0x10;
109     link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
110     link->io.IOAddrLines = 10;
111     link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
112     link->irq.IRQInfo1 = IRQ_LEVEL_ID;
113     link->conf.Attributes = CONF_ENABLE_IRQ;
114     link->conf.Vcc = 50;
115     link->conf.IntType = INT_MEMORY_AND_IO;
116     link->conf.Present = PRESENT_OPTION;
117
118     /* Register with Card Services */
119     link->next = dev_list;
120     dev_list = link;
121     client_reg.dev_info = &dev_info;
122     client_reg.Version = 0x0210;
123     client_reg.event_callback_args.client_data = link;
124     ret = pcmcia_register_client(&link->handle, &client_reg);
125     if (ret != 0) {
126         cs_error(link->handle, RegisterClient, ret);
127         fdomain_detach(link->handle);
128         return NULL;
129     }
130     
131     return link;
132 } /* fdomain_attach */
133
134 /*====================================================================*/
135
136 static void fdomain_detach(struct pcmcia_device *p_dev)
137 {
138     dev_link_t *link = dev_to_instance(p_dev);
139     dev_link_t **linkp;
140
141     DEBUG(0, "fdomain_detach(0x%p)\n", link);
142     
143     /* Locate device structure */
144     for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
145         if (*linkp == link) break;
146     if (*linkp == NULL)
147         return;
148
149     if (link->state & DEV_CONFIG)
150         fdomain_release(link);
151
152     /* Unlink device structure, free bits */
153     *linkp = link->next;
154     kfree(link->priv);
155     
156 } /* fdomain_detach */
157
158 /*====================================================================*/
159
160 #define CS_CHECK(fn, ret) \
161 do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
162
163 static void fdomain_config(dev_link_t *link)
164 {
165     client_handle_t handle = link->handle;
166     scsi_info_t *info = link->priv;
167     tuple_t tuple;
168     cisparse_t parse;
169     int i, last_ret, last_fn;
170     u_char tuple_data[64];
171     char str[16];
172     struct Scsi_Host *host;
173
174     DEBUG(0, "fdomain_config(0x%p)\n", link);
175
176     tuple.DesiredTuple = CISTPL_CONFIG;
177     tuple.TupleData = tuple_data;
178     tuple.TupleDataMax = 64;
179     tuple.TupleOffset = 0;
180     CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
181     CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
182     CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
183     link->conf.ConfigBase = parse.config.base;
184
185     /* Configure card */
186     link->state |= DEV_CONFIG;
187     
188     tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
189     CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
190     while (1) {
191         if (pcmcia_get_tuple_data(handle, &tuple) != 0 ||
192                 pcmcia_parse_tuple(handle, &tuple, &parse) != 0)
193             goto next_entry;
194         link->conf.ConfigIndex = parse.cftable_entry.index;
195         link->io.BasePort1 = parse.cftable_entry.io.win[0].base;
196         i = pcmcia_request_io(handle, &link->io);
197         if (i == CS_SUCCESS) break;
198     next_entry:
199         CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple));
200     }
201
202     CS_CHECK(RequestIRQ, pcmcia_request_irq(handle, &link->irq));
203     CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf));
204     
205     /* A bad hack... */
206     release_region(link->io.BasePort1, link->io.NumPorts1);
207
208     /* Set configuration options for the fdomain driver */
209     sprintf(str, "%d,%d", link->io.BasePort1, link->irq.AssignedIRQ);
210     fdomain_setup(str);
211     
212     host = __fdomain_16x0_detect(&fdomain_driver_template);
213     if (!host) {
214         printk(KERN_INFO "fdomain_cs: no SCSI devices found\n");
215         goto cs_failed;
216     }
217  
218     scsi_add_host(host, NULL); /* XXX handle failure */
219     scsi_scan_host(host);
220
221     sprintf(info->node.dev_name, "scsi%d", host->host_no);
222     link->dev = &info->node;
223     info->host = host;
224     
225     link->state &= ~DEV_CONFIG_PENDING;
226     return;
227     
228 cs_failed:
229     cs_error(link->handle, last_fn, last_ret);
230     fdomain_release(link);
231     return;
232     
233 } /* fdomain_config */
234
235 /*====================================================================*/
236
237 static void fdomain_release(dev_link_t *link)
238 {
239     scsi_info_t *info = link->priv;
240
241     DEBUG(0, "fdomain_release(0x%p)\n", link);
242
243     scsi_remove_host(info->host);
244     link->dev = NULL;
245     
246     pcmcia_release_configuration(link->handle);
247     pcmcia_release_io(link->handle, &link->io);
248     pcmcia_release_irq(link->handle, &link->irq);
249
250     scsi_unregister(info->host);
251
252     link->state &= ~DEV_CONFIG;
253 }
254
255 /*====================================================================*/
256
257 static int fdomain_suspend(struct pcmcia_device *dev)
258 {
259         dev_link_t *link = dev_to_instance(dev);
260
261         link->state |= DEV_SUSPEND;
262         if (link->state & DEV_CONFIG)
263                 pcmcia_release_configuration(link->handle);
264
265         return 0;
266 }
267
268 static int fdomain_resume(struct pcmcia_device *dev)
269 {
270         dev_link_t *link = dev_to_instance(dev);
271
272         link->state &= ~DEV_SUSPEND;
273         if (link->state & DEV_CONFIG) {
274                 pcmcia_request_configuration(link->handle, &link->conf);
275                 fdomain_16x0_bus_reset(NULL);
276         }
277
278         return 0;
279 }
280
281 static int fdomain_event(event_t event, int priority,
282                         event_callback_args_t *args)
283 {
284     dev_link_t *link = args->client_data;
285
286     DEBUG(1, "fdomain_event(0x%06x)\n", event);
287     
288     switch (event) {
289     case CS_EVENT_CARD_INSERTION:
290         link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
291         fdomain_config(link);
292         break;
293     }
294     return 0;
295 } /* fdomain_event */
296
297
298 static struct pcmcia_device_id fdomain_ids[] = {
299         PCMCIA_DEVICE_PROD_ID12("IBM Corp.", "SCSI PCMCIA Card", 0xe3736c88, 0x859cad20),
300         PCMCIA_DEVICE_PROD_ID1("SCSI PCMCIA Adapter Card", 0x8dacb57e),
301         PCMCIA_DEVICE_PROD_ID12(" SIMPLE TECHNOLOGY Corporation", "SCSI PCMCIA Credit Card Controller", 0x182bdafe, 0xc80d106f),
302         PCMCIA_DEVICE_NULL,
303 };
304 MODULE_DEVICE_TABLE(pcmcia, fdomain_ids);
305
306 static struct pcmcia_driver fdomain_cs_driver = {
307         .owner          = THIS_MODULE,
308         .drv            = {
309                 .name   = "fdomain_cs",
310         },
311         .attach         = fdomain_attach,
312         .event          = fdomain_event,
313         .remove         = fdomain_detach,
314         .id_table       = fdomain_ids,
315         .suspend        = fdomain_suspend,
316         .resume         = fdomain_resume,
317 };
318
319 static int __init init_fdomain_cs(void)
320 {
321         return pcmcia_register_driver(&fdomain_cs_driver);
322 }
323
324 static void __exit exit_fdomain_cs(void)
325 {
326         pcmcia_unregister_driver(&fdomain_cs_driver);
327         BUG_ON(dev_list != NULL);
328 }
329
330 module_init(init_fdomain_cs);
331 module_exit(exit_fdomain_cs);