blob: 2d8bbbf286aa7c5c652254143d24220c0c0a3c3e [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;
Dominik Brodowski7feabb62010-07-29 18:35:47 +020083 p_dev->config_index = 1;
84 p_dev->config_regs = PRESENT_OPTION;
Linus Torvalds1da177e2005-04-16 15:20:36 -070085
Dominik Brodowski15b99ac2006-03-31 17:26:06 +020086 return avmcs_config(p_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -070087} /* avmcs_attach */
88
89/*======================================================================
90
91 This deletes a driver "instance". The device is de-registered
92 with Card Services. If it has been released, all local data
93 structures are freed. Otherwise, the structures will be freed
94 when the device is released.
95
96======================================================================*/
97
Dominik Brodowskifba395e2006-03-31 17:21:06 +020098static void avmcs_detach(struct pcmcia_device *link)
Linus Torvalds1da177e2005-04-16 15:20:36 -070099{
Dominik Brodowskicc3b4862005-11-14 21:23:14 +0100100 avmcs_release(link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101} /* avmcs_detach */
102
103/*======================================================================
104
105 avmcs_config() is scheduled to run after a CARD_INSERTION event
106 is received, to configure the PCMCIA socket, and to make the
107 ethernet device available to the system.
108
109======================================================================*/
110
Dominik Brodowski5fcd4da2008-07-29 08:38:55 +0200111static int avmcs_configcheck(struct pcmcia_device *p_dev,
112 cistpl_cftable_entry_t *cf,
Dominik Brodowski8e2fc392008-08-02 15:30:31 +0200113 cistpl_cftable_entry_t *dflt,
Dominik Brodowskiad913c12008-08-02 16:12:00 +0200114 unsigned int vcc,
Dominik Brodowski5fcd4da2008-07-29 08:38:55 +0200115 void *priv_data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116{
Dominik Brodowski5fcd4da2008-07-29 08:38:55 +0200117 if (cf->io.nwin <= 0)
118 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119
Dominik Brodowski90abdc32010-07-24 17:23:51 +0200120 p_dev->resource[0]->start = cf->io.win[0].base;
121 p_dev->resource[0]->end = cf->io.win[0].len;
122 return pcmcia_request_io(p_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123}
124
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200125static int avmcs_config(struct pcmcia_device *link)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126{
Dominik Brodowskieb141202010-03-07 12:21:16 +0100127 int i = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128 char devname[128];
129 int cardtype;
130 int (*addcard)(unsigned int port, unsigned irq);
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200131
Dominik Brodowski5fcd4da2008-07-29 08:38:55 +0200132 devname[0] = 0;
133 if (link->prod_id[1])
134 strlcpy(devname, link->prod_id[1], sizeof(devname));
135
136 /*
137 * find IO port
138 */
139 if (pcmcia_loop_config(link, avmcs_configcheck, NULL))
140 return -ENODEV;
141
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142 do {
Dominik Brodowskieb141202010-03-07 12:21:16 +0100143 if (!link->irq) {
Dominik Brodowski50db3fd2006-01-15 10:05:19 +0100144 /* undo */
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200145 pcmcia_disable_device(link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146 break;
147 }
Dominik Brodowski50db3fd2006-01-15 10:05:19 +0100148
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149 /*
150 * configure the PCMCIA socket
151 */
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200152 i = pcmcia_request_configuration(link, &link->conf);
Dominik Brodowski4c89e882008-08-03 10:07:45 +0200153 if (i != 0) {
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200154 pcmcia_disable_device(link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700155 break;
156 }
157
158 } while (0);
159
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160 if (devname[0]) {
161 char *s = strrchr(devname, ' ');
162 if (!s)
163 s = devname;
164 else s++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165 if (strcmp("M1", s) == 0) {
166 cardtype = AVM_CARDTYPE_M1;
167 } else if (strcmp("M2", s) == 0) {
168 cardtype = AVM_CARDTYPE_M2;
169 } else {
170 cardtype = AVM_CARDTYPE_B1;
171 }
Dominik Brodowskided6a1a2010-03-20 19:35:12 +0100172 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173 cardtype = AVM_CARDTYPE_B1;
Dominik Brodowskie2d40962006-03-02 00:09:29 +0100174
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175 /* If any step failed, release any partially configured state */
176 if (i != 0) {
177 avmcs_release(link);
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200178 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179 }
180
181
182 switch (cardtype) {
183 case AVM_CARDTYPE_M1: addcard = b1pcmcia_addcard_m1; break;
184 case AVM_CARDTYPE_M2: addcard = b1pcmcia_addcard_m2; break;
185 default:
186 case AVM_CARDTYPE_B1: addcard = b1pcmcia_addcard_b1; break;
187 }
Dominik Brodowski9a017a92010-07-24 15:58:54 +0200188 if ((i = (*addcard)(link->resource[0]->start, link->irq)) < 0) {
189 dev_err(&link->dev,
190 "avm_cs: failed to add AVM-Controller at i/o %#x, irq %d\n",
191 (unsigned int) link->resource[0]->start, link->irq);
Dominik Brodowskided6a1a2010-03-20 19:35:12 +0100192 avmcs_release(link);
193 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194 }
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200195 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196
197} /* avmcs_config */
198
199/*======================================================================
200
201 After a card is removed, avmcs_release() will unregister the net
202 device, and release the PCMCIA configuration. If the device is
203 still open, this will be postponed until it is closed.
204
205======================================================================*/
206
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200207static void avmcs_release(struct pcmcia_device *link)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700208{
Dominik Brodowski9a017a92010-07-24 15:58:54 +0200209 b1pcmcia_delcard(link->resource[0]->start, link->irq);
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200210 pcmcia_disable_device(link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211} /* avmcs_release */
212
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213
Dominik Brodowskia13bcf02005-06-27 16:28:35 -0700214static struct pcmcia_device_id avmcs_ids[] = {
215 PCMCIA_DEVICE_PROD_ID12("AVM", "ISDN-Controller B1", 0x95d42008, 0x845dc335),
216 PCMCIA_DEVICE_PROD_ID12("AVM", "Mobile ISDN-Controller M1", 0x95d42008, 0x81e10430),
217 PCMCIA_DEVICE_PROD_ID12("AVM", "Mobile ISDN-Controller M2", 0x95d42008, 0x18e8558a),
218 PCMCIA_DEVICE_NULL
219};
220MODULE_DEVICE_TABLE(pcmcia, avmcs_ids);
221
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222static struct pcmcia_driver avmcs_driver = {
223 .owner = THIS_MODULE,
224 .drv = {
225 .name = "avm_cs",
226 },
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200227 .probe = avmcs_probe,
Dominik Brodowskicc3b4862005-11-14 21:23:14 +0100228 .remove = avmcs_detach,
Dominik Brodowskia13bcf02005-06-27 16:28:35 -0700229 .id_table = avmcs_ids,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230};
231
232static int __init avmcs_init(void)
233{
234 return pcmcia_register_driver(&avmcs_driver);
235}
236
237static void __exit avmcs_exit(void)
238{
239 pcmcia_unregister_driver(&avmcs_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240}
241
242module_init(avmcs_init);
243module_exit(avmcs_exit);