blob: 698e9ec95f0be07dbef1cb07b53b7ddc0f3d3aec [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/* $Id: teles_cs.c,v 1.1.2.2 2004/01/25 15:07:06 keil Exp $ */
2/*======================================================================
3
4 A teles S0 PCMCIA client driver
5
6 Based on skeleton by David Hinds, dhinds@allegro.stanford.edu
7 Written by Christof Petig, christof.petig@wtal.de
8
9 Also inspired by ELSA PCMCIA driver
10 by Klaus Lichtenwalder <Lichtenwalder@ACM.org>
11
12 Extentions to new hisax_pcmcia by Karsten Keil
13
14 minor changes to be compatible with kernel 2.4.x
15 by Jan.Schubert@GMX.li
16
17======================================================================*/
18
19#include <linux/kernel.h>
20#include <linux/module.h>
21#include <linux/init.h>
22#include <linux/sched.h>
23#include <linux/ptrace.h>
24#include <linux/slab.h>
25#include <linux/string.h>
26#include <linux/timer.h>
27#include <linux/ioport.h>
28#include <asm/io.h>
29#include <asm/system.h>
30
Linus Torvalds1da177e2005-04-16 15:20:36 -070031#include <pcmcia/cs_types.h>
32#include <pcmcia/cs.h>
33#include <pcmcia/cistpl.h>
34#include <pcmcia/cisreg.h>
35#include <pcmcia/ds.h>
36#include "hisax_cfg.h"
37
38MODULE_DESCRIPTION("ISDN4Linux: PCMCIA client driver for Teles PCMCIA cards");
39MODULE_AUTHOR("Christof Petig, christof.petig@wtal.de, Karsten Keil, kkeil@suse.de");
40MODULE_LICENSE("GPL");
41
42/*
43 All the PCMCIA modules use PCMCIA_DEBUG to control debugging. If
44 you do not define PCMCIA_DEBUG at all, all the debug code will be
45 left out. If you compile with PCMCIA_DEBUG=0, the debug code will
46 be present but disabled -- but it can then be enabled for specific
47 modules at load time with a 'pc_debug=#' option to insmod.
48*/
49
50#ifdef PCMCIA_DEBUG
51static int pc_debug = PCMCIA_DEBUG;
52module_param(pc_debug, int, 0);
53#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args);
54static char *version =
55"teles_cs.c 2.10 2002/07/30 22:23:34 kkeil";
56#else
57#define DEBUG(n, args...)
58#endif
59
60/*====================================================================*/
61
62/* Parameters that can be set with 'insmod' */
63
64static int protocol = 2; /* EURO-ISDN Default */
65module_param(protocol, int, 0);
66
67/*====================================================================*/
68
69/*
70 The event() function is this driver's Card Services event handler.
71 It will be called by Card Services when an appropriate card status
72 event is received. The config() and release() entry points are
73 used to configure or release a socket, in response to card insertion
74 and ejection events. They are invoked from the teles_cs event
75 handler.
76*/
77
Dominik Brodowskifba395e2006-03-31 17:21:06 +020078static void teles_cs_config(struct pcmcia_device *link);
79static void teles_cs_release(struct pcmcia_device *link);
Linus Torvalds1da177e2005-04-16 15:20:36 -070080
81/*
82 The attach() and detach() entry points are used to create and destroy
83 "instances" of the driver, where each instance represents everything
84 needed to manage one actual PCMCIA card.
85*/
86
Dominik Brodowskicc3b4862005-11-14 21:23:14 +010087static void teles_detach(struct pcmcia_device *p_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -070088
89/*
Linus Torvalds1da177e2005-04-16 15:20:36 -070090 A linked list of "instances" of the teles_cs device. Each actual
91 PCMCIA card corresponds to one device instance, and is described
Dominik Brodowskifba395e2006-03-31 17:21:06 +020092 by one struct pcmcia_device structure (defined in ds.h).
Linus Torvalds1da177e2005-04-16 15:20:36 -070093
94 You may not want to use a linked list for this -- for example, the
Dominik Brodowskifba395e2006-03-31 17:21:06 +020095 memory card driver uses an array of struct pcmcia_device pointers, where minor
Linus Torvalds1da177e2005-04-16 15:20:36 -070096 device numbers are used to derive the corresponding array index.
97*/
98
Linus Torvalds1da177e2005-04-16 15:20:36 -070099/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100 A driver needs to provide a dev_node_t structure for each device
101 on a card. In some cases, there is only one device per card (for
102 example, ethernet cards, modems). In other cases, there may be
103 many actual or logical devices (SCSI adapters, memory cards with
104 multiple partitions). The dev_node_t structures need to be kept
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200105 in a linked list starting at the 'dev' field of a struct pcmcia_device
Linus Torvalds1da177e2005-04-16 15:20:36 -0700106 structure. We allocate them in the card's private data structure,
107 because they generally shouldn't be allocated dynamically.
108 In this case, we also provide a flag to indicate if a device is
109 "stopped" due to a power management event, or card ejection. The
110 device IO routines can use a flag like this to throttle IO to a
111 card that is not ready to accept it.
112*/
113
114typedef struct local_info_t {
Dominik Brodowskifd238232006-03-05 10:45:09 +0100115 struct pcmcia_device *p_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116 dev_node_t node;
117 int busy;
118 int cardnr;
119} local_info_t;
120
121/*======================================================================
122
123 teles_attach() creates an "instance" of the driver, allocatingx
124 local data structures for one device. The device is registered
125 with Card Services.
126
127 The dev_link structure is initialized, but we don't actually
128 configure the card at this point -- we wait until we receive a
129 card insertion event.
130
131======================================================================*/
132
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200133static int teles_attach(struct pcmcia_device *link)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135 local_info_t *local;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136
137 DEBUG(0, "teles_attach()\n");
138
139 /* Allocate space for private device-specific data */
140 local = kmalloc(sizeof(local_info_t), GFP_KERNEL);
Dominik Brodowskif8cfa612005-11-14 21:25:51 +0100141 if (!local) return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142 memset(local, 0, sizeof(local_info_t));
143 local->cardnr = -1;
Dominik Brodowskifd238232006-03-05 10:45:09 +0100144
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200145 local->p_dev = link;
Dominik Brodowskifd238232006-03-05 10:45:09 +0100146 link->priv = local;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147
148 /* Interrupt setup */
149 link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED;
150 link->irq.IRQInfo1 = IRQ_LEVEL_ID|IRQ_SHARE_ID;
151 link->irq.Handler = NULL;
152
153 /*
154 General socket configuration defaults can go here. In this
155 client, we assume very little, and rely on the CIS for almost
156 everything. In most clients, many details (i.e., number, sizes,
157 and attributes of IO windows) are fixed by the nature of the
158 device, and can be hard-wired here.
159 */
160 link->io.NumPorts1 = 96;
161 link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
162 link->io.IOAddrLines = 5;
163
164 link->conf.Attributes = CONF_ENABLE_IRQ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165 link->conf.IntType = INT_MEMORY_AND_IO;
166
Dominik Brodowskif8cfa612005-11-14 21:25:51 +0100167 link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
168 teles_cs_config(link);
169
170 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171} /* teles_attach */
172
173/*======================================================================
174
175 This deletes a driver "instance". The device is de-registered
176 with Card Services. If it has been released, all local data
177 structures are freed. Otherwise, the structures will be freed
178 when the device is released.
179
180======================================================================*/
181
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200182static void teles_detach(struct pcmcia_device *link)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184 local_info_t *info = link->priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185
186 DEBUG(0, "teles_detach(0x%p)\n", link);
187
Dominik Brodowskicc3b4862005-11-14 21:23:14 +0100188 if (link->state & DEV_CONFIG) {
Dominik Brodowskib4635812005-11-14 21:25:35 +0100189 info->busy = 1;
190 teles_cs_release(link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191 }
192
Linus Torvalds1da177e2005-04-16 15:20:36 -0700193 kfree(info);
194
195} /* teles_detach */
196
197/*======================================================================
198
199 teles_cs_config() is scheduled to run after a CARD_INSERTION event
200 is received, to configure the PCMCIA socket, and to make the
201 device available to the system.
202
203======================================================================*/
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200204static int get_tuple(struct pcmcia_device *handle, tuple_t *tuple,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205 cisparse_t *parse)
206{
207 int i = pcmcia_get_tuple_data(handle, tuple);
208 if (i != CS_SUCCESS) return i;
209 return pcmcia_parse_tuple(handle, tuple, parse);
210}
211
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200212static int first_tuple(struct pcmcia_device *handle, tuple_t *tuple,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213 cisparse_t *parse)
214{
215 int i = pcmcia_get_first_tuple(handle, tuple);
216 if (i != CS_SUCCESS) return i;
217 return get_tuple(handle, tuple, parse);
218}
219
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200220static int next_tuple(struct pcmcia_device *handle, tuple_t *tuple,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700221 cisparse_t *parse)
222{
223 int i = pcmcia_get_next_tuple(handle, tuple);
224 if (i != CS_SUCCESS) return i;
225 return get_tuple(handle, tuple, parse);
226}
227
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200228static void teles_cs_config(struct pcmcia_device *link)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700229{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230 tuple_t tuple;
231 cisparse_t parse;
232 local_info_t *dev;
233 int i, j, last_fn;
234 u_short buf[128];
235 cistpl_cftable_entry_t *cf = &parse.cftable_entry;
236 IsdnCard_t icard;
237
238 DEBUG(0, "teles_config(0x%p)\n", link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239 dev = link->priv;
240
241 /*
242 This reads the card's CONFIG tuple to find its configuration
243 registers.
244 */
245 tuple.DesiredTuple = CISTPL_CONFIG;
246 tuple.TupleData = (cisdata_t *)buf;
247 tuple.TupleDataMax = 255;
248 tuple.TupleOffset = 0;
249 tuple.Attributes = 0;
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200250 i = first_tuple(link, &tuple, &parse);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251 if (i != CS_SUCCESS) {
252 last_fn = ParseTuple;
253 goto cs_failed;
254 }
255 link->conf.ConfigBase = parse.config.base;
256 link->conf.Present = parse.config.rmask[0];
257
258 /* Configure card */
259 link->state |= DEV_CONFIG;
260
261 tuple.TupleData = (cisdata_t *)buf;
262 tuple.TupleOffset = 0; tuple.TupleDataMax = 255;
263 tuple.Attributes = 0;
264 tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200265 i = first_tuple(link, &tuple, &parse);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700266 while (i == CS_SUCCESS) {
267 if ( (cf->io.nwin > 0) && cf->io.win[0].base) {
268 printk(KERN_INFO "(teles_cs: looks like the 96 model)\n");
269 link->conf.ConfigIndex = cf->index;
270 link->io.BasePort1 = cf->io.win[0].base;
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200271 i = pcmcia_request_io(link, &link->io);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272 if (i == CS_SUCCESS) break;
273 } else {
274 printk(KERN_INFO "(teles_cs: looks like the 97 model)\n");
275 link->conf.ConfigIndex = cf->index;
276 for (i = 0, j = 0x2f0; j > 0x100; j -= 0x10) {
277 link->io.BasePort1 = j;
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200278 i = pcmcia_request_io(link, &link->io);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279 if (i == CS_SUCCESS) break;
280 }
281 break;
282 }
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200283 i = next_tuple(link, &tuple, &parse);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284 }
285
286 if (i != CS_SUCCESS) {
287 last_fn = RequestIO;
288 goto cs_failed;
289 }
290
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200291 i = pcmcia_request_irq(link, &link->irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292 if (i != CS_SUCCESS) {
293 link->irq.AssignedIRQ = 0;
294 last_fn = RequestIRQ;
295 goto cs_failed;
296 }
297
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200298 i = pcmcia_request_configuration(link, &link->conf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700299 if (i != CS_SUCCESS) {
300 last_fn = RequestConfiguration;
301 goto cs_failed;
302 }
303
304 /* At this point, the dev_node_t structure(s) should be
305 initialized and arranged in a linked list at link->dev. *//* */
306 sprintf(dev->node.dev_name, "teles");
307 dev->node.major = dev->node.minor = 0x0;
308
Dominik Brodowskifd238232006-03-05 10:45:09 +0100309 link->dev_node = &dev->node;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310
311 /* Finally, report what we've done */
Dominik Brodowski70294b42006-01-15 12:43:16 +0100312 printk(KERN_INFO "%s: index 0x%02x:",
313 dev->node.dev_name, link->conf.ConfigIndex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314 if (link->conf.Attributes & CONF_ENABLE_IRQ)
315 printk(", irq %d", link->irq.AssignedIRQ);
316 if (link->io.NumPorts1)
317 printk(", io 0x%04x-0x%04x", link->io.BasePort1,
318 link->io.BasePort1+link->io.NumPorts1-1);
319 if (link->io.NumPorts2)
320 printk(" & 0x%04x-0x%04x", link->io.BasePort2,
321 link->io.BasePort2+link->io.NumPorts2-1);
322 printk("\n");
323
324 link->state &= ~DEV_CONFIG_PENDING;
325
326 icard.para[0] = link->irq.AssignedIRQ;
327 icard.para[1] = link->io.BasePort1;
328 icard.protocol = protocol;
329 icard.typ = ISDN_CTYPE_TELESPCMCIA;
330
331 i = hisax_init_pcmcia(link, &(((local_info_t*)link->priv)->busy), &icard);
332 if (i < 0) {
333 printk(KERN_ERR "teles_cs: failed to initialize Teles PCMCIA %d at i/o %#x\n",
334 i, link->io.BasePort1);
335 teles_cs_release(link);
336 } else
337 ((local_info_t*)link->priv)->cardnr = i;
338
339 return;
340cs_failed:
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200341 cs_error(link, last_fn, i);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342 teles_cs_release(link);
343} /* teles_cs_config */
344
345/*======================================================================
346
347 After a card is removed, teles_cs_release() will unregister the net
348 device, and release the PCMCIA configuration. If the device is
349 still open, this will be postponed until it is closed.
350
351======================================================================*/
352
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200353static void teles_cs_release(struct pcmcia_device *link)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354{
355 local_info_t *local = link->priv;
356
357 DEBUG(0, "teles_cs_release(0x%p)\n", link);
358
359 if (local) {
360 if (local->cardnr >= 0) {
361 /* no unregister function with hisax */
362 HiSax_closecard(local->cardnr);
363 }
364 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200366 pcmcia_disable_device(link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700367} /* teles_cs_release */
368
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200369static int teles_suspend(struct pcmcia_device *link)
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100370{
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100371 local_info_t *dev = link->priv;
372
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100373 dev->busy = 1;
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100374
375 return 0;
376}
377
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200378static int teles_resume(struct pcmcia_device *link)
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100379{
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100380 local_info_t *dev = link->priv;
381
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100382 dev->busy = 0;
383
384 return 0;
385}
386
Linus Torvalds1da177e2005-04-16 15:20:36 -0700387
Dominik Brodowski0a10d732005-06-27 16:28:24 -0700388static struct pcmcia_device_id teles_ids[] = {
389 PCMCIA_DEVICE_PROD_ID12("TELES", "S0/PC", 0x67b50eae, 0xe9e70119),
390 PCMCIA_DEVICE_NULL,
391};
392MODULE_DEVICE_TABLE(pcmcia, teles_ids);
393
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394static struct pcmcia_driver teles_cs_driver = {
395 .owner = THIS_MODULE,
396 .drv = {
397 .name = "teles_cs",
398 },
Dominik Brodowskif8cfa612005-11-14 21:25:51 +0100399 .probe = teles_attach,
Dominik Brodowskicc3b4862005-11-14 21:23:14 +0100400 .remove = teles_detach,
Dominik Brodowski0a10d732005-06-27 16:28:24 -0700401 .id_table = teles_ids,
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100402 .suspend = teles_suspend,
403 .resume = teles_resume,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404};
405
406static int __init init_teles_cs(void)
407{
408 return pcmcia_register_driver(&teles_cs_driver);
409}
410
411static void __exit exit_teles_cs(void)
412{
413 pcmcia_unregister_driver(&teles_cs_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414}
415
416module_init(init_teles_cs);
417module_exit(exit_teles_cs);