pcmcia: introduce autoconfiguration feature
[linux-2.6.git] / drivers / isdn / hisax / teles_cs.c
1 /* $Id: teles_cs.c,v 1.1.2.2 2004/01/25 15:07:06 keil Exp $ */
2 /*======================================================================
3
4     A teles S0 PCMCIA client driver
5
6     Based on skeleton by David Hinds, dhinds@allegro.stanford.edu
7     Written by Christof Petig, christof.petig@wtal.de
8     
9     Also inspired by ELSA PCMCIA driver 
10     by Klaus Lichtenwalder <Lichtenwalder@ACM.org>
11     
12     Extentions to new hisax_pcmcia by Karsten Keil
13
14     minor changes to be compatible with kernel 2.4.x
15     by Jan.Schubert@GMX.li
16
17 ======================================================================*/
18
19 #include <linux/kernel.h>
20 #include <linux/module.h>
21 #include <linux/init.h>
22 #include <linux/ptrace.h>
23 #include <linux/slab.h>
24 #include <linux/string.h>
25 #include <linux/timer.h>
26 #include <linux/ioport.h>
27 #include <asm/io.h>
28 #include <asm/system.h>
29
30 #include <pcmcia/cistpl.h>
31 #include <pcmcia/cisreg.h>
32 #include <pcmcia/ds.h>
33 #include "hisax_cfg.h"
34
35 MODULE_DESCRIPTION("ISDN4Linux: PCMCIA client driver for Teles PCMCIA cards");
36 MODULE_AUTHOR("Christof Petig, christof.petig@wtal.de, Karsten Keil, kkeil@suse.de");
37 MODULE_LICENSE("GPL");
38
39
40 /*====================================================================*/
41
42 /* Parameters that can be set with 'insmod' */
43
44 static int protocol = 2;        /* EURO-ISDN Default */
45 module_param(protocol, int, 0);
46
47 /*====================================================================*/
48
49 /*
50    The event() function is this driver's Card Services event handler.
51    It will be called by Card Services when an appropriate card status
52    event is received.  The config() and release() entry points are
53    used to configure or release a socket, in response to card insertion
54    and ejection events.  They are invoked from the teles_cs event
55    handler.
56 */
57
58 static int teles_cs_config(struct pcmcia_device *link) __devinit ;
59 static void teles_cs_release(struct pcmcia_device *link);
60
61 /*
62    The attach() and detach() entry points are used to create and destroy
63    "instances" of the driver, where each instance represents everything
64    needed to manage one actual PCMCIA card.
65 */
66
67 static void teles_detach(struct pcmcia_device *p_dev) __devexit ;
68
69 typedef struct local_info_t {
70         struct pcmcia_device    *p_dev;
71     int                 busy;
72     int                 cardnr;
73 } local_info_t;
74
75 /*======================================================================
76
77     teles_attach() creates an "instance" of the driver, allocatingx
78     local data structures for one device.  The device is registered
79     with Card Services.
80
81     The dev_link structure is initialized, but we don't actually
82     configure the card at this point -- we wait until we receive a
83     card insertion event.
84
85 ======================================================================*/
86
87 static int __devinit teles_probe(struct pcmcia_device *link)
88 {
89     local_info_t *local;
90
91     dev_dbg(&link->dev, "teles_attach()\n");
92
93     /* Allocate space for private device-specific data */
94     local = kzalloc(sizeof(local_info_t), GFP_KERNEL);
95     if (!local) return -ENOMEM;
96     local->cardnr = -1;
97
98     local->p_dev = link;
99     link->priv = local;
100
101     /*
102       General socket configuration defaults can go here.  In this
103       client, we assume very little, and rely on the CIS for almost
104       everything.  In most clients, many details (i.e., number, sizes,
105       and attributes of IO windows) are fixed by the nature of the
106       device, and can be hard-wired here.
107     */
108     link->resource[0]->end = 96;
109     link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO;
110
111     link->config_flags |= CONF_ENABLE_IRQ;
112
113     return teles_cs_config(link);
114 } /* teles_attach */
115
116 /*======================================================================
117
118     This deletes a driver "instance".  The device is de-registered
119     with Card Services.  If it has been released, all local data
120     structures are freed.  Otherwise, the structures will be freed
121     when the device is released.
122
123 ======================================================================*/
124
125 static void __devexit teles_detach(struct pcmcia_device *link)
126 {
127         local_info_t *info = link->priv;
128
129         dev_dbg(&link->dev, "teles_detach(0x%p)\n", link);
130
131         info->busy = 1;
132         teles_cs_release(link);
133
134         kfree(info);
135 } /* teles_detach */
136
137 /*======================================================================
138
139     teles_cs_config() is scheduled to run after a CARD_INSERTION event
140     is received, to configure the PCMCIA socket, and to make the
141     device available to the system.
142
143 ======================================================================*/
144
145 static int teles_cs_configcheck(struct pcmcia_device *p_dev,
146                                 cistpl_cftable_entry_t *cf,
147                                 cistpl_cftable_entry_t *dflt,
148                                 void *priv_data)
149 {
150         int j;
151
152         p_dev->io_lines = 5;
153
154         if ((cf->io.nwin > 0) && cf->io.win[0].base) {
155                 printk(KERN_INFO "(teles_cs: looks like the 96 model)\n");
156                 p_dev->resource[0]->start = cf->io.win[0].base;
157                 if (!pcmcia_request_io(p_dev))
158                         return 0;
159         } else {
160                 printk(KERN_INFO "(teles_cs: looks like the 97 model)\n");
161                 for (j = 0x2f0; j > 0x100; j -= 0x10) {
162                         p_dev->resource[0]->start = j;
163                         if (!pcmcia_request_io(p_dev))
164                                 return 0;
165                 }
166         }
167         return -ENODEV;
168 }
169
170 static int __devinit teles_cs_config(struct pcmcia_device *link)
171 {
172     local_info_t *dev;
173     int i;
174     IsdnCard_t icard;
175
176     dev_dbg(&link->dev, "teles_config(0x%p)\n", link);
177     dev = link->priv;
178
179     i = pcmcia_loop_config(link, teles_cs_configcheck, NULL);
180     if (i != 0)
181         goto cs_failed;
182
183     if (!link->irq)
184         goto cs_failed;
185
186     i = pcmcia_enable_device(link);
187     if (i != 0)
188       goto cs_failed;
189
190     /* Finally, report what we've done */
191     dev_info(&link->dev, "index 0x%02x:",
192             link->config_index);
193     printk(", irq %d", link->irq);
194     if (link->resource[0])
195         printk(" & %pR", link->resource[0]);
196     if (link->resource[1])
197         printk(" & %pR", link->resource[1]);
198     printk("\n");
199
200     icard.para[0] = link->irq;
201     icard.para[1] = link->resource[0]->start;
202     icard.protocol = protocol;
203     icard.typ = ISDN_CTYPE_TELESPCMCIA;
204     
205     i = hisax_init_pcmcia(link, &(((local_info_t*)link->priv)->busy), &icard);
206     if (i < 0) {
207         printk(KERN_ERR "teles_cs: failed to initialize Teles PCMCIA %d at i/o %#x\n",
208                         i, (unsigned int) link->resource[0]->start);
209         teles_cs_release(link);
210         return -ENODEV;
211     }
212
213     ((local_info_t*)link->priv)->cardnr = i;
214     return 0;
215
216 cs_failed:
217     teles_cs_release(link);
218     return -ENODEV;
219 } /* teles_cs_config */
220
221 /*======================================================================
222
223     After a card is removed, teles_cs_release() will unregister the net
224     device, and release the PCMCIA configuration.  If the device is
225     still open, this will be postponed until it is closed.
226
227 ======================================================================*/
228
229 static void teles_cs_release(struct pcmcia_device *link)
230 {
231     local_info_t *local = link->priv;
232
233     dev_dbg(&link->dev, "teles_cs_release(0x%p)\n", link);
234
235     if (local) {
236         if (local->cardnr >= 0) {
237             /* no unregister function with hisax */
238             HiSax_closecard(local->cardnr);
239         }
240     }
241
242     pcmcia_disable_device(link);
243 } /* teles_cs_release */
244
245 static int teles_suspend(struct pcmcia_device *link)
246 {
247         local_info_t *dev = link->priv;
248
249         dev->busy = 1;
250
251         return 0;
252 }
253
254 static int teles_resume(struct pcmcia_device *link)
255 {
256         local_info_t *dev = link->priv;
257
258         dev->busy = 0;
259
260         return 0;
261 }
262
263
264 static struct pcmcia_device_id teles_ids[] = {
265         PCMCIA_DEVICE_PROD_ID12("TELES", "S0/PC", 0x67b50eae, 0xe9e70119),
266         PCMCIA_DEVICE_NULL,
267 };
268 MODULE_DEVICE_TABLE(pcmcia, teles_ids);
269
270 static struct pcmcia_driver teles_cs_driver = {
271         .owner          = THIS_MODULE,
272         .drv            = {
273                 .name   = "teles_cs",
274         },
275         .probe          = teles_probe,
276         .remove         = __devexit_p(teles_detach),
277         .id_table       = teles_ids,
278         .suspend        = teles_suspend,
279         .resume         = teles_resume,
280 };
281
282 static int __init init_teles_cs(void)
283 {
284         return pcmcia_register_driver(&teles_cs_driver);
285 }
286
287 static void __exit exit_teles_cs(void)
288 {
289         pcmcia_unregister_driver(&teles_cs_driver);
290 }
291
292 module_init(init_teles_cs);
293 module_exit(exit_teles_cs);