blob: 09b1795516f4e95c4e49f0ce6e1e91c9eefa5bd1 [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 Brodowski90abdc32010-07-24 17:23:51 +020078 p_dev->resource[0]->end = 16;
79 p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
Linus Torvalds1da177e2005-04-16 15:20:36 -070080
Linus Torvalds1da177e2005-04-16 15:20:36 -070081 /* General socket configuration */
Dominik Brodowskifd238232006-03-05 10:45:09 +010082 p_dev->conf.Attributes = CONF_ENABLE_IRQ;
83 p_dev->conf.IntType = INT_MEMORY_AND_IO;
84 p_dev->conf.ConfigIndex = 1;
85 p_dev->conf.Present = PRESENT_OPTION;
Linus Torvalds1da177e2005-04-16 15:20:36 -070086
Dominik Brodowski15b99ac2006-03-31 17:26:06 +020087 return avmcs_config(p_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -070088} /* avmcs_attach */
89
90/*======================================================================
91
92 This deletes a driver "instance". The device is de-registered
93 with Card Services. If it has been released, all local data
94 structures are freed. Otherwise, the structures will be freed
95 when the device is released.
96
97======================================================================*/
98
Dominik Brodowskifba395e2006-03-31 17:21:06 +020099static void avmcs_detach(struct pcmcia_device *link)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100{
Dominik Brodowskicc3b4862005-11-14 21:23:14 +0100101 avmcs_release(link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102} /* avmcs_detach */
103
104/*======================================================================
105
106 avmcs_config() is scheduled to run after a CARD_INSERTION event
107 is received, to configure the PCMCIA socket, and to make the
108 ethernet device available to the system.
109
110======================================================================*/
111
Dominik Brodowski5fcd4da2008-07-29 08:38:55 +0200112static int avmcs_configcheck(struct pcmcia_device *p_dev,
113 cistpl_cftable_entry_t *cf,
Dominik Brodowski8e2fc392008-08-02 15:30:31 +0200114 cistpl_cftable_entry_t *dflt,
Dominik Brodowskiad913c12008-08-02 16:12:00 +0200115 unsigned int vcc,
Dominik Brodowski5fcd4da2008-07-29 08:38:55 +0200116 void *priv_data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117{
Dominik Brodowski5fcd4da2008-07-29 08:38:55 +0200118 if (cf->io.nwin <= 0)
119 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120
Dominik Brodowski90abdc32010-07-24 17:23:51 +0200121 p_dev->resource[0]->start = cf->io.win[0].base;
122 p_dev->resource[0]->end = cf->io.win[0].len;
123 return pcmcia_request_io(p_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700124}
125
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200126static int avmcs_config(struct pcmcia_device *link)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127{
Dominik Brodowskieb141202010-03-07 12:21:16 +0100128 int i = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129 char devname[128];
130 int cardtype;
131 int (*addcard)(unsigned int port, unsigned irq);
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200132
Dominik Brodowski5fcd4da2008-07-29 08:38:55 +0200133 devname[0] = 0;
134 if (link->prod_id[1])
135 strlcpy(devname, link->prod_id[1], sizeof(devname));
136
137 /*
138 * find IO port
139 */
140 if (pcmcia_loop_config(link, avmcs_configcheck, NULL))
141 return -ENODEV;
142
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143 do {
Dominik Brodowskieb141202010-03-07 12:21:16 +0100144 if (!link->irq) {
Dominik Brodowski50db3fd2006-01-15 10:05:19 +0100145 /* undo */
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200146 pcmcia_disable_device(link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147 break;
148 }
Dominik Brodowski50db3fd2006-01-15 10:05:19 +0100149
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150 /*
151 * configure the PCMCIA socket
152 */
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200153 i = pcmcia_request_configuration(link, &link->conf);
Dominik Brodowski4c89e882008-08-03 10:07:45 +0200154 if (i != 0) {
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200155 pcmcia_disable_device(link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156 break;
157 }
158
159 } while (0);
160
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161 if (devname[0]) {
162 char *s = strrchr(devname, ' ');
163 if (!s)
164 s = devname;
165 else s++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700166 if (strcmp("M1", s) == 0) {
167 cardtype = AVM_CARDTYPE_M1;
168 } else if (strcmp("M2", s) == 0) {
169 cardtype = AVM_CARDTYPE_M2;
170 } else {
171 cardtype = AVM_CARDTYPE_B1;
172 }
Dominik Brodowskided6a1a2010-03-20 19:35:12 +0100173 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174 cardtype = AVM_CARDTYPE_B1;
Dominik Brodowskie2d40962006-03-02 00:09:29 +0100175
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176 /* If any step failed, release any partially configured state */
177 if (i != 0) {
178 avmcs_release(link);
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200179 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180 }
181
182
183 switch (cardtype) {
184 case AVM_CARDTYPE_M1: addcard = b1pcmcia_addcard_m1; break;
185 case AVM_CARDTYPE_M2: addcard = b1pcmcia_addcard_m2; break;
186 default:
187 case AVM_CARDTYPE_B1: addcard = b1pcmcia_addcard_b1; break;
188 }
Dominik Brodowski9a017a92010-07-24 15:58:54 +0200189 if ((i = (*addcard)(link->resource[0]->start, link->irq)) < 0) {
190 dev_err(&link->dev,
191 "avm_cs: failed to add AVM-Controller at i/o %#x, irq %d\n",
192 (unsigned int) link->resource[0]->start, link->irq);
Dominik Brodowskided6a1a2010-03-20 19:35:12 +0100193 avmcs_release(link);
194 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195 }
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200196 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197
198} /* avmcs_config */
199
200/*======================================================================
201
202 After a card is removed, avmcs_release() will unregister the net
203 device, and release the PCMCIA configuration. If the device is
204 still open, this will be postponed until it is closed.
205
206======================================================================*/
207
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200208static void avmcs_release(struct pcmcia_device *link)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209{
Dominik Brodowski9a017a92010-07-24 15:58:54 +0200210 b1pcmcia_delcard(link->resource[0]->start, link->irq);
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200211 pcmcia_disable_device(link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700212} /* avmcs_release */
213
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214
Dominik Brodowskia13bcf02005-06-27 16:28:35 -0700215static struct pcmcia_device_id avmcs_ids[] = {
216 PCMCIA_DEVICE_PROD_ID12("AVM", "ISDN-Controller B1", 0x95d42008, 0x845dc335),
217 PCMCIA_DEVICE_PROD_ID12("AVM", "Mobile ISDN-Controller M1", 0x95d42008, 0x81e10430),
218 PCMCIA_DEVICE_PROD_ID12("AVM", "Mobile ISDN-Controller M2", 0x95d42008, 0x18e8558a),
219 PCMCIA_DEVICE_NULL
220};
221MODULE_DEVICE_TABLE(pcmcia, avmcs_ids);
222
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223static struct pcmcia_driver avmcs_driver = {
224 .owner = THIS_MODULE,
225 .drv = {
226 .name = "avm_cs",
227 },
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200228 .probe = avmcs_probe,
Dominik Brodowskicc3b4862005-11-14 21:23:14 +0100229 .remove = avmcs_detach,
Dominik Brodowskia13bcf02005-06-27 16:28:35 -0700230 .id_table = avmcs_ids,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231};
232
233static int __init avmcs_init(void)
234{
235 return pcmcia_register_driver(&avmcs_driver);
236}
237
238static void __exit avmcs_exit(void)
239{
240 pcmcia_unregister_driver(&avmcs_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700241}
242
243module_init(avmcs_init);
244module_exit(avmcs_exit);