blob: 7c8c51f2200385cc2592fbc8eb63092ea0fb6088 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/* $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>
Linus Torvalds1da177e2005-04-16 15:20:36 -070015#include <linux/ptrace.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070016#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
Linus Torvalds1da177e2005-04-16 15:20:36 -070023#include <pcmcia/cs.h>
24#include <pcmcia/cistpl.h>
25#include <pcmcia/ciscode.h>
26#include <pcmcia/ds.h>
27#include <pcmcia/cisreg.h>
28
29#include <linux/skbuff.h>
30#include <linux/capi.h>
31#include <linux/b1lli.h>
32#include <linux/b1pcmcia.h>
33
34/*====================================================================*/
35
36MODULE_DESCRIPTION("CAPI4Linux: PCMCIA client driver for AVM B1/M1/M2");
37MODULE_AUTHOR("Carsten Paeth");
38MODULE_LICENSE("GPL");
39
40/*====================================================================*/
41
42/*
43 The event() function is this driver's Card Services event handler.
44 It will be called by Card Services when an appropriate card status
45 event is received. The config() and release() entry points are
46 used to configure or release a socket, in response to card insertion
47 and ejection events. They are invoked from the skeleton event
48 handler.
49*/
50
Dominik Brodowski15b99ac2006-03-31 17:26:06 +020051static int avmcs_config(struct pcmcia_device *link);
Dominik Brodowskifba395e2006-03-31 17:21:06 +020052static void avmcs_release(struct pcmcia_device *link);
Linus Torvalds1da177e2005-04-16 15:20:36 -070053
54/*
55 The attach() and detach() entry points are used to create and destroy
56 "instances" of the driver, where each instance represents everything
57 needed to manage one actual PCMCIA card.
58*/
59
Dominik Brodowskicc3b4862005-11-14 21:23:14 +010060static void avmcs_detach(struct pcmcia_device *p_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -070061
Linus Torvalds1da177e2005-04-16 15:20:36 -070062/*======================================================================
63
64 avmcs_attach() creates an "instance" of the driver, allocating
65 local data structures for one device. The device is registered
66 with Card Services.
67
68 The dev_link structure is initialized, but we don't actually
69 configure the card at this point -- we wait until we receive a
70 card insertion event.
71
72======================================================================*/
73
Dominik Brodowski15b99ac2006-03-31 17:26:06 +020074static int avmcs_probe(struct pcmcia_device *p_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -070075{
Dominik Brodowskif8cfa612005-11-14 21:25:51 +010076
Linus Torvalds1da177e2005-04-16 15:20:36 -070077 /* The io structure describes IO port mapping */
Dominik Brodowskifd238232006-03-05 10:45:09 +010078 p_dev->io.NumPorts1 = 16;
79 p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
80 p_dev->io.NumPorts2 = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070081
Linus Torvalds1da177e2005-04-16 15:20:36 -070082 /* General socket configuration */
Dominik Brodowskifd238232006-03-05 10:45:09 +010083 p_dev->conf.Attributes = CONF_ENABLE_IRQ;
84 p_dev->conf.IntType = INT_MEMORY_AND_IO;
85 p_dev->conf.ConfigIndex = 1;
86 p_dev->conf.Present = PRESENT_OPTION;
Linus Torvalds1da177e2005-04-16 15:20:36 -070087
Dominik Brodowski15b99ac2006-03-31 17:26:06 +020088 return avmcs_config(p_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -070089} /* avmcs_attach */
90
91/*======================================================================
92
93 This deletes a driver "instance". The device is de-registered
94 with Card Services. If it has been released, all local data
95 structures are freed. Otherwise, the structures will be freed
96 when the device is released.
97
98======================================================================*/
99
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200100static void avmcs_detach(struct pcmcia_device *link)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101{
Dominik Brodowskicc3b4862005-11-14 21:23:14 +0100102 avmcs_release(link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700103} /* avmcs_detach */
104
105/*======================================================================
106
107 avmcs_config() is scheduled to run after a CARD_INSERTION event
108 is received, to configure the PCMCIA socket, and to make the
109 ethernet device available to the system.
110
111======================================================================*/
112
Dominik Brodowski5fcd4da2008-07-29 08:38:55 +0200113static int avmcs_configcheck(struct pcmcia_device *p_dev,
114 cistpl_cftable_entry_t *cf,
Dominik Brodowski8e2fc392008-08-02 15:30:31 +0200115 cistpl_cftable_entry_t *dflt,
Dominik Brodowskiad913c12008-08-02 16:12:00 +0200116 unsigned int vcc,
Dominik Brodowski5fcd4da2008-07-29 08:38:55 +0200117 void *priv_data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118{
Dominik Brodowski5fcd4da2008-07-29 08:38:55 +0200119 if (cf->io.nwin <= 0)
120 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121
Dominik Brodowski5fcd4da2008-07-29 08:38:55 +0200122 p_dev->io.BasePort1 = cf->io.win[0].base;
123 p_dev->io.NumPorts1 = cf->io.win[0].len;
124 p_dev->io.NumPorts2 = 0;
125 printk(KERN_INFO "avm_cs: testing i/o %#x-%#x\n",
126 p_dev->io.BasePort1,
127 p_dev->io.BasePort1+p_dev->io.NumPorts1-1);
128 return pcmcia_request_io(p_dev, &p_dev->io);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129}
130
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200131static int avmcs_config(struct pcmcia_device *link)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132{
Dominik Brodowskieb141202010-03-07 12:21:16 +0100133 int i = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134 char devname[128];
135 int cardtype;
136 int (*addcard)(unsigned int port, unsigned irq);
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200137
Dominik Brodowski5fcd4da2008-07-29 08:38:55 +0200138 devname[0] = 0;
139 if (link->prod_id[1])
140 strlcpy(devname, link->prod_id[1], sizeof(devname));
141
142 /*
143 * find IO port
144 */
145 if (pcmcia_loop_config(link, avmcs_configcheck, NULL))
146 return -ENODEV;
147
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148 do {
Dominik Brodowskieb141202010-03-07 12:21:16 +0100149 if (!link->irq) {
Dominik Brodowski50db3fd2006-01-15 10:05:19 +0100150 /* undo */
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200151 pcmcia_disable_device(link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152 break;
153 }
Dominik Brodowski50db3fd2006-01-15 10:05:19 +0100154
Linus Torvalds1da177e2005-04-16 15:20:36 -0700155 /*
156 * configure the PCMCIA socket
157 */
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200158 i = pcmcia_request_configuration(link, &link->conf);
Dominik Brodowski4c89e882008-08-03 10:07:45 +0200159 if (i != 0) {
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200160 pcmcia_disable_device(link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161 break;
162 }
163
164 } while (0);
165
Linus Torvalds1da177e2005-04-16 15:20:36 -0700166 if (devname[0]) {
167 char *s = strrchr(devname, ' ');
168 if (!s)
169 s = devname;
170 else s++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171 if (strcmp("M1", s) == 0) {
172 cardtype = AVM_CARDTYPE_M1;
173 } else if (strcmp("M2", s) == 0) {
174 cardtype = AVM_CARDTYPE_M2;
175 } else {
176 cardtype = AVM_CARDTYPE_B1;
177 }
Dominik Brodowskided6a1a2010-03-20 19:35:12 +0100178 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179 cardtype = AVM_CARDTYPE_B1;
Dominik Brodowskie2d40962006-03-02 00:09:29 +0100180
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181 /* If any step failed, release any partially configured state */
182 if (i != 0) {
183 avmcs_release(link);
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200184 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185 }
186
187
188 switch (cardtype) {
189 case AVM_CARDTYPE_M1: addcard = b1pcmcia_addcard_m1; break;
190 case AVM_CARDTYPE_M2: addcard = b1pcmcia_addcard_m2; break;
191 default:
192 case AVM_CARDTYPE_B1: addcard = b1pcmcia_addcard_b1; break;
193 }
Dominik Brodowski9a017a92010-07-24 15:58:54 +0200194 if ((i = (*addcard)(link->resource[0]->start, link->irq)) < 0) {
195 dev_err(&link->dev,
196 "avm_cs: failed to add AVM-Controller at i/o %#x, irq %d\n",
197 (unsigned int) link->resource[0]->start, link->irq);
Dominik Brodowskided6a1a2010-03-20 19:35:12 +0100198 avmcs_release(link);
199 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200 }
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200201 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202
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
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200213static void avmcs_release(struct pcmcia_device *link)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214{
Dominik Brodowski9a017a92010-07-24 15:58:54 +0200215 b1pcmcia_delcard(link->resource[0]->start, link->irq);
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200216 pcmcia_disable_device(link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217} /* avmcs_release */
218
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219
Dominik Brodowskia13bcf02005-06-27 16:28:35 -0700220static 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};
226MODULE_DEVICE_TABLE(pcmcia, avmcs_ids);
227
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228static struct pcmcia_driver avmcs_driver = {
229 .owner = THIS_MODULE,
230 .drv = {
231 .name = "avm_cs",
232 },
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200233 .probe = avmcs_probe,
Dominik Brodowskicc3b4862005-11-14 21:23:14 +0100234 .remove = avmcs_detach,
Dominik Brodowskia13bcf02005-06-27 16:28:35 -0700235 .id_table = avmcs_ids,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700236};
237
238static int __init avmcs_init(void)
239{
240 return pcmcia_register_driver(&avmcs_driver);
241}
242
243static void __exit avmcs_exit(void)
244{
245 pcmcia_unregister_driver(&avmcs_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246}
247
248module_init(avmcs_init);
249module_exit(avmcs_exit);