blob: b172361bbef87a350b4324f3fe7bfe42cd2a9075 [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/cistpl.h>
24#include <pcmcia/ciscode.h>
25#include <pcmcia/ds.h>
26#include <pcmcia/cisreg.h>
27
28#include <linux/skbuff.h>
29#include <linux/capi.h>
30#include <linux/b1lli.h>
31#include <linux/b1pcmcia.h>
32
33/*====================================================================*/
34
35MODULE_DESCRIPTION("CAPI4Linux: PCMCIA client driver for AVM B1/M1/M2");
36MODULE_AUTHOR("Carsten Paeth");
37MODULE_LICENSE("GPL");
38
39/*====================================================================*/
40
41/*
42 The event() function is this driver's Card Services event handler.
43 It will be called by Card Services when an appropriate card status
44 event is received. The config() and release() entry points are
45 used to configure or release a socket, in response to card insertion
46 and ejection events. They are invoked from the skeleton event
47 handler.
48*/
49
Dominik Brodowski15b99ac2006-03-31 17:26:06 +020050static int avmcs_config(struct pcmcia_device *link);
Dominik Brodowskifba395e2006-03-31 17:21:06 +020051static void avmcs_release(struct pcmcia_device *link);
Linus Torvalds1da177e2005-04-16 15:20:36 -070052
53/*
54 The attach() and detach() entry points are used to create and destroy
55 "instances" of the driver, where each instance represents everything
56 needed to manage one actual PCMCIA card.
57*/
58
Dominik Brodowskicc3b4862005-11-14 21:23:14 +010059static void avmcs_detach(struct pcmcia_device *p_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -070060
Linus Torvalds1da177e2005-04-16 15:20:36 -070061/*======================================================================
62
63 avmcs_attach() creates an "instance" of the driver, allocating
64 local data structures for one device. The device is registered
65 with Card Services.
66
67 The dev_link structure is initialized, but we don't actually
68 configure the card at this point -- we wait until we receive a
69 card insertion event.
70
71======================================================================*/
72
Dominik Brodowski15b99ac2006-03-31 17:26:06 +020073static int avmcs_probe(struct pcmcia_device *p_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -070074{
Linus Torvalds1da177e2005-04-16 15:20:36 -070075 /* General socket configuration */
Dominik Brodowski00990e72010-07-30 13:13:46 +020076 p_dev->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
Dominik Brodowski7feabb62010-07-29 18:35:47 +020077 p_dev->config_index = 1;
78 p_dev->config_regs = PRESENT_OPTION;
Linus Torvalds1da177e2005-04-16 15:20:36 -070079
Dominik Brodowski15b99ac2006-03-31 17:26:06 +020080 return avmcs_config(p_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -070081} /* avmcs_attach */
82
83/*======================================================================
84
85 This deletes a driver "instance". The device is de-registered
86 with Card Services. If it has been released, all local data
87 structures are freed. Otherwise, the structures will be freed
88 when the device is released.
89
90======================================================================*/
91
Dominik Brodowskifba395e2006-03-31 17:21:06 +020092static void avmcs_detach(struct pcmcia_device *link)
Linus Torvalds1da177e2005-04-16 15:20:36 -070093{
Dominik Brodowskicc3b4862005-11-14 21:23:14 +010094 avmcs_release(link);
Linus Torvalds1da177e2005-04-16 15:20:36 -070095} /* avmcs_detach */
96
97/*======================================================================
98
99 avmcs_config() is scheduled to run after a CARD_INSERTION event
100 is received, to configure the PCMCIA socket, and to make the
101 ethernet device available to the system.
102
103======================================================================*/
104
Dominik Brodowski00990e72010-07-30 13:13:46 +0200105static int avmcs_configcheck(struct pcmcia_device *p_dev, void *priv_data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700106{
Dominik Brodowski00990e72010-07-30 13:13:46 +0200107 p_dev->resource[0]->end = 16;
108 p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
109 p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700110
Dominik Brodowski90abdc32010-07-24 17:23:51 +0200111 return pcmcia_request_io(p_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112}
113
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200114static int avmcs_config(struct pcmcia_device *link)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115{
Dominik Brodowskieb141202010-03-07 12:21:16 +0100116 int i = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117 char devname[128];
118 int cardtype;
119 int (*addcard)(unsigned int port, unsigned irq);
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200120
Dominik Brodowski5fcd4da2008-07-29 08:38:55 +0200121 devname[0] = 0;
122 if (link->prod_id[1])
123 strlcpy(devname, link->prod_id[1], sizeof(devname));
124
125 /*
126 * find IO port
127 */
128 if (pcmcia_loop_config(link, avmcs_configcheck, NULL))
129 return -ENODEV;
130
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131 do {
Dominik Brodowskieb141202010-03-07 12:21:16 +0100132 if (!link->irq) {
Dominik Brodowski50db3fd2006-01-15 10:05:19 +0100133 /* undo */
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200134 pcmcia_disable_device(link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135 break;
136 }
Dominik Brodowski50db3fd2006-01-15 10:05:19 +0100137
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138 /*
139 * configure the PCMCIA socket
140 */
Dominik Brodowski1ac71e52010-07-29 19:27:09 +0200141 i = pcmcia_enable_device(link);
Dominik Brodowski4c89e882008-08-03 10:07:45 +0200142 if (i != 0) {
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200143 pcmcia_disable_device(link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144 break;
145 }
146
147 } while (0);
148
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149 if (devname[0]) {
150 char *s = strrchr(devname, ' ');
151 if (!s)
152 s = devname;
153 else s++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154 if (strcmp("M1", s) == 0) {
155 cardtype = AVM_CARDTYPE_M1;
156 } else if (strcmp("M2", s) == 0) {
157 cardtype = AVM_CARDTYPE_M2;
158 } else {
159 cardtype = AVM_CARDTYPE_B1;
160 }
Dominik Brodowskided6a1a2010-03-20 19:35:12 +0100161 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700162 cardtype = AVM_CARDTYPE_B1;
Dominik Brodowskie2d40962006-03-02 00:09:29 +0100163
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164 /* If any step failed, release any partially configured state */
165 if (i != 0) {
166 avmcs_release(link);
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200167 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168 }
169
170
171 switch (cardtype) {
172 case AVM_CARDTYPE_M1: addcard = b1pcmcia_addcard_m1; break;
173 case AVM_CARDTYPE_M2: addcard = b1pcmcia_addcard_m2; break;
174 default:
175 case AVM_CARDTYPE_B1: addcard = b1pcmcia_addcard_b1; break;
176 }
Dominik Brodowski9a017a92010-07-24 15:58:54 +0200177 if ((i = (*addcard)(link->resource[0]->start, link->irq)) < 0) {
178 dev_err(&link->dev,
179 "avm_cs: failed to add AVM-Controller at i/o %#x, irq %d\n",
180 (unsigned int) link->resource[0]->start, link->irq);
Dominik Brodowskided6a1a2010-03-20 19:35:12 +0100181 avmcs_release(link);
182 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183 }
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200184 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185
186} /* avmcs_config */
187
188/*======================================================================
189
190 After a card is removed, avmcs_release() will unregister the net
191 device, and release the PCMCIA configuration. If the device is
192 still open, this will be postponed until it is closed.
193
194======================================================================*/
195
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200196static void avmcs_release(struct pcmcia_device *link)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197{
Dominik Brodowski9a017a92010-07-24 15:58:54 +0200198 b1pcmcia_delcard(link->resource[0]->start, link->irq);
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200199 pcmcia_disable_device(link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200} /* avmcs_release */
201
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202
Dominik Brodowskia13bcf02005-06-27 16:28:35 -0700203static struct pcmcia_device_id avmcs_ids[] = {
204 PCMCIA_DEVICE_PROD_ID12("AVM", "ISDN-Controller B1", 0x95d42008, 0x845dc335),
205 PCMCIA_DEVICE_PROD_ID12("AVM", "Mobile ISDN-Controller M1", 0x95d42008, 0x81e10430),
206 PCMCIA_DEVICE_PROD_ID12("AVM", "Mobile ISDN-Controller M2", 0x95d42008, 0x18e8558a),
207 PCMCIA_DEVICE_NULL
208};
209MODULE_DEVICE_TABLE(pcmcia, avmcs_ids);
210
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211static struct pcmcia_driver avmcs_driver = {
212 .owner = THIS_MODULE,
Dominik Brodowski2e9b9812010-08-08 11:36:26 +0200213 .name = "avm_cs",
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200214 .probe = avmcs_probe,
Dominik Brodowskicc3b4862005-11-14 21:23:14 +0100215 .remove = avmcs_detach,
Dominik Brodowskia13bcf02005-06-27 16:28:35 -0700216 .id_table = avmcs_ids,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217};
218
219static int __init avmcs_init(void)
220{
221 return pcmcia_register_driver(&avmcs_driver);
222}
223
224static void __exit avmcs_exit(void)
225{
226 pcmcia_unregister_driver(&avmcs_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227}
228
229module_init(avmcs_init);
230module_exit(avmcs_exit);