Merge branch 'omap-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tmlind...
[pandora-kernel.git] / drivers / isdn / hardware / avm / avm_cs.c
1 /* $Id: avm_cs.c,v 1.4.6.3 2001/09/23 22:24:33 kai Exp $
2  *
3  * A PCMCIA client driver for AVM B1/M1/M2
4  *
5  * Copyright 1999 by Carsten Paeth <calle@calle.de>
6  *
7  * This software may be used and distributed according to the terms
8  * of the GNU General Public License, incorporated herein by reference.
9  *
10  */
11
12 #include <linux/module.h>
13 #include <linux/kernel.h>
14 #include <linux/init.h>
15 #include <linux/ptrace.h>
16 #include <linux/string.h>
17 #include <linux/tty.h>
18 #include <linux/serial.h>
19 #include <linux/major.h>
20 #include <asm/io.h>
21 #include <asm/system.h>
22
23 #include <pcmcia/cs_types.h>
24 #include <pcmcia/cs.h>
25 #include <pcmcia/cistpl.h>
26 #include <pcmcia/ciscode.h>
27 #include <pcmcia/ds.h>
28 #include <pcmcia/cisreg.h>
29
30 #include <linux/skbuff.h>
31 #include <linux/capi.h>
32 #include <linux/b1lli.h>
33 #include <linux/b1pcmcia.h>
34
35 /*====================================================================*/
36
37 MODULE_DESCRIPTION("CAPI4Linux: PCMCIA client driver for AVM B1/M1/M2");
38 MODULE_AUTHOR("Carsten Paeth");
39 MODULE_LICENSE("GPL");
40
41 /*====================================================================*/
42
43 /*
44    The event() function is this driver's Card Services event handler.
45    It will be called by Card Services when an appropriate card status
46    event is received.  The config() and release() entry points are
47    used to configure or release a socket, in response to card insertion
48    and ejection events.  They are invoked from the skeleton event
49    handler.
50 */
51
52 static int avmcs_config(struct pcmcia_device *link);
53 static void avmcs_release(struct pcmcia_device *link);
54
55 /*
56    The attach() and detach() entry points are used to create and destroy
57    "instances" of the driver, where each instance represents everything
58    needed to manage one actual PCMCIA card.
59 */
60
61 static void avmcs_detach(struct pcmcia_device *p_dev);
62
63 /*======================================================================
64
65     avmcs_attach() creates an "instance" of the driver, allocating
66     local data structures for one device.  The device is registered
67     with Card Services.
68
69     The dev_link structure is initialized, but we don't actually
70     configure the card at this point -- we wait until we receive a
71     card insertion event.
72     
73 ======================================================================*/
74
75 static int avmcs_probe(struct pcmcia_device *p_dev)
76 {
77
78     /* The io structure describes IO port mapping */
79     p_dev->io.NumPorts1 = 16;
80     p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
81     p_dev->io.NumPorts2 = 0;
82
83     /* General socket configuration */
84     p_dev->conf.Attributes = CONF_ENABLE_IRQ;
85     p_dev->conf.IntType = INT_MEMORY_AND_IO;
86     p_dev->conf.ConfigIndex = 1;
87     p_dev->conf.Present = PRESENT_OPTION;
88
89     return avmcs_config(p_dev);
90 } /* avmcs_attach */
91
92 /*======================================================================
93
94     This deletes a driver "instance".  The device is de-registered
95     with Card Services.  If it has been released, all local data
96     structures are freed.  Otherwise, the structures will be freed
97     when the device is released.
98
99 ======================================================================*/
100
101 static void avmcs_detach(struct pcmcia_device *link)
102 {
103         avmcs_release(link);
104 } /* avmcs_detach */
105
106 /*======================================================================
107
108     avmcs_config() is scheduled to run after a CARD_INSERTION event
109     is received, to configure the PCMCIA socket, and to make the
110     ethernet device available to the system.
111     
112 ======================================================================*/
113
114 static int avmcs_configcheck(struct pcmcia_device *p_dev,
115                              cistpl_cftable_entry_t *cf,
116                              cistpl_cftable_entry_t *dflt,
117                              unsigned int vcc,
118                              void *priv_data)
119 {
120         if (cf->io.nwin <= 0)
121                 return -ENODEV;
122
123         p_dev->io.BasePort1 = cf->io.win[0].base;
124         p_dev->io.NumPorts1 = cf->io.win[0].len;
125         p_dev->io.NumPorts2 = 0;
126         printk(KERN_INFO "avm_cs: testing i/o %#x-%#x\n",
127                p_dev->io.BasePort1,
128                p_dev->io.BasePort1+p_dev->io.NumPorts1-1);
129         return pcmcia_request_io(p_dev, &p_dev->io);
130 }
131
132 static int avmcs_config(struct pcmcia_device *link)
133 {
134     int i = -1;
135     char devname[128];
136     int cardtype;
137     int (*addcard)(unsigned int port, unsigned irq);
138
139     devname[0] = 0;
140     if (link->prod_id[1])
141             strlcpy(devname, link->prod_id[1], sizeof(devname));
142
143     /*
144      * find IO port
145      */
146     if (pcmcia_loop_config(link, avmcs_configcheck, NULL))
147             return -ENODEV;
148
149     do {
150         if (!link->irq) {
151             /* undo */
152             pcmcia_disable_device(link);
153             break;
154         }
155
156         /*
157          * configure the PCMCIA socket
158           */
159         i = pcmcia_request_configuration(link, &link->conf);
160         if (i != 0) {
161             pcmcia_disable_device(link);
162             break;
163         }
164
165     } while (0);
166
167     if (devname[0]) {
168         char *s = strrchr(devname, ' ');
169         if (!s)
170            s = devname;
171         else s++;
172         if (strcmp("M1", s) == 0) {
173            cardtype = AVM_CARDTYPE_M1;
174         } else if (strcmp("M2", s) == 0) {
175            cardtype = AVM_CARDTYPE_M2;
176         } else {
177            cardtype = AVM_CARDTYPE_B1;
178         }
179     } else
180         cardtype = AVM_CARDTYPE_B1;
181
182     /* If any step failed, release any partially configured state */
183     if (i != 0) {
184         avmcs_release(link);
185         return -ENODEV;
186     }
187
188
189     switch (cardtype) {
190         case AVM_CARDTYPE_M1: addcard = b1pcmcia_addcard_m1; break;
191         case AVM_CARDTYPE_M2: addcard = b1pcmcia_addcard_m2; break;
192         default:
193         case AVM_CARDTYPE_B1: addcard = b1pcmcia_addcard_b1; break;
194     }
195     if ((i = (*addcard)(link->io.BasePort1, link->irq)) < 0) {
196             dev_err(&link->dev, "avm_cs: failed to add AVM-Controller at i/o %#x, irq %d\n",
197                     link->io.BasePort1, link->irq);
198             avmcs_release(link);
199             return -ENODEV;
200     }
201     return 0;
202
203 } /* avmcs_config */
204
205 /*======================================================================
206
207     After a card is removed, avmcs_release() will unregister the net
208     device, and release the PCMCIA configuration.  If the device is
209     still open, this will be postponed until it is closed.
210     
211 ======================================================================*/
212
213 static void avmcs_release(struct pcmcia_device *link)
214 {
215         b1pcmcia_delcard(link->io.BasePort1, link->irq);
216         pcmcia_disable_device(link);
217 } /* avmcs_release */
218
219
220 static struct pcmcia_device_id avmcs_ids[] = {
221         PCMCIA_DEVICE_PROD_ID12("AVM", "ISDN-Controller B1", 0x95d42008, 0x845dc335),
222         PCMCIA_DEVICE_PROD_ID12("AVM", "Mobile ISDN-Controller M1", 0x95d42008, 0x81e10430),
223         PCMCIA_DEVICE_PROD_ID12("AVM", "Mobile ISDN-Controller M2", 0x95d42008, 0x18e8558a),
224         PCMCIA_DEVICE_NULL
225 };
226 MODULE_DEVICE_TABLE(pcmcia, avmcs_ids);
227
228 static struct pcmcia_driver avmcs_driver = {
229         .owner  = THIS_MODULE,
230         .drv    = {
231                 .name   = "avm_cs",
232         },
233         .probe = avmcs_probe,
234         .remove = avmcs_detach,
235         .id_table = avmcs_ids,
236 };
237
238 static int __init avmcs_init(void)
239 {
240         return pcmcia_register_driver(&avmcs_driver);
241 }
242
243 static void __exit avmcs_exit(void)
244 {
245         pcmcia_unregister_driver(&avmcs_driver);
246 }
247
248 module_init(avmcs_init);
249 module_exit(avmcs_exit);