blob: 2ae71e3297ba70c78188ed64a59122532a527b2b [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>
Linus Torvalds1da177e2005-04-16 15:20:36 -070022#include <linux/ptrace.h>
23#include <linux/slab.h>
24#include <linux/string.h>
25#include <linux/timer.h>
26#include <linux/ioport.h>
27#include <asm/io.h>
28#include <asm/system.h>
29
Linus Torvalds1da177e2005-04-16 15:20:36 -070030#include <pcmcia/cistpl.h>
31#include <pcmcia/cisreg.h>
32#include <pcmcia/ds.h>
33#include "hisax_cfg.h"
34
35MODULE_DESCRIPTION("ISDN4Linux: PCMCIA client driver for Teles PCMCIA cards");
36MODULE_AUTHOR("Christof Petig, christof.petig@wtal.de, Karsten Keil, kkeil@suse.de");
37MODULE_LICENSE("GPL");
38
Linus Torvalds1da177e2005-04-16 15:20:36 -070039
40/*====================================================================*/
41
42/* Parameters that can be set with 'insmod' */
43
44static int protocol = 2; /* EURO-ISDN Default */
45module_param(protocol, int, 0);
46
47/*====================================================================*/
48
49/*
50 The event() function is this driver's Card Services event handler.
51 It will be called by Card Services when an appropriate card status
52 event is received. The config() and release() entry points are
53 used to configure or release a socket, in response to card insertion
54 and ejection events. They are invoked from the teles_cs event
55 handler.
56*/
57
Henne158e33d2010-03-25 12:05:30 +000058static int teles_cs_config(struct pcmcia_device *link) __devinit ;
Dominik Brodowskifba395e2006-03-31 17:21:06 +020059static void teles_cs_release(struct pcmcia_device *link);
Linus Torvalds1da177e2005-04-16 15:20:36 -070060
61/*
62 The attach() and detach() entry points are used to create and destroy
63 "instances" of the driver, where each instance represents everything
64 needed to manage one actual PCMCIA card.
65*/
66
Henne158e33d2010-03-25 12:05:30 +000067static void teles_detach(struct pcmcia_device *p_dev) __devexit ;
Linus Torvalds1da177e2005-04-16 15:20:36 -070068
Linus Torvalds1da177e2005-04-16 15:20:36 -070069typedef struct local_info_t {
Dominik Brodowskifd238232006-03-05 10:45:09 +010070 struct pcmcia_device *p_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -070071 int busy;
72 int cardnr;
73} local_info_t;
74
75/*======================================================================
76
77 teles_attach() creates an "instance" of the driver, allocatingx
78 local data structures for one device. The device is registered
79 with Card Services.
80
81 The dev_link structure is initialized, but we don't actually
82 configure the card at this point -- we wait until we receive a
83 card insertion event.
84
85======================================================================*/
86
Henne158e33d2010-03-25 12:05:30 +000087static int __devinit teles_probe(struct pcmcia_device *link)
Linus Torvalds1da177e2005-04-16 15:20:36 -070088{
Linus Torvalds1da177e2005-04-16 15:20:36 -070089 local_info_t *local;
Linus Torvalds1da177e2005-04-16 15:20:36 -070090
Dominik Brodowskie773cfe2009-10-24 15:50:13 +020091 dev_dbg(&link->dev, "teles_attach()\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -070092
93 /* Allocate space for private device-specific data */
Burman Yan41f96932006-12-08 02:39:35 -080094 local = kzalloc(sizeof(local_info_t), GFP_KERNEL);
Dominik Brodowskif8cfa612005-11-14 21:25:51 +010095 if (!local) return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -070096 local->cardnr = -1;
Dominik Brodowskifd238232006-03-05 10:45:09 +010097
Dominik Brodowskifba395e2006-03-31 17:21:06 +020098 local->p_dev = link;
Dominik Brodowskifd238232006-03-05 10:45:09 +010099 link->priv = local;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101 /*
102 General socket configuration defaults can go here. In this
103 client, we assume very little, and rely on the CIS for almost
104 everything. In most clients, many details (i.e., number, sizes,
105 and attributes of IO windows) are fixed by the nature of the
106 device, and can be hard-wired here.
107 */
Dominik Brodowski90abdc32010-07-24 17:23:51 +0200108 link->resource[0]->end = 96;
109 link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700110
Dominik Brodowski1ac71e52010-07-29 19:27:09 +0200111 link->config_flags |= CONF_ENABLE_IRQ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200113 return teles_cs_config(link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700114} /* teles_attach */
115
116/*======================================================================
117
118 This deletes a driver "instance". The device is de-registered
119 with Card Services. If it has been released, all local data
120 structures are freed. Otherwise, the structures will be freed
121 when the device is released.
122
123======================================================================*/
124
Henne158e33d2010-03-25 12:05:30 +0000125static void __devexit teles_detach(struct pcmcia_device *link)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126{
Dominik Brodowskie2d40962006-03-02 00:09:29 +0100127 local_info_t *info = link->priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128
Dominik Brodowskie773cfe2009-10-24 15:50:13 +0200129 dev_dbg(&link->dev, "teles_detach(0x%p)\n", link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130
Dominik Brodowskie2d40962006-03-02 00:09:29 +0100131 info->busy = 1;
132 teles_cs_release(link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133
Dominik Brodowskie2d40962006-03-02 00:09:29 +0100134 kfree(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135} /* teles_detach */
136
137/*======================================================================
138
139 teles_cs_config() is scheduled to run after a CARD_INSERTION event
140 is received, to configure the PCMCIA socket, and to make the
141 device available to the system.
142
143======================================================================*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144
Dominik Brodowski5fcd4da2008-07-29 08:38:55 +0200145static int teles_cs_configcheck(struct pcmcia_device *p_dev,
146 cistpl_cftable_entry_t *cf,
Dominik Brodowski8e2fc392008-08-02 15:30:31 +0200147 cistpl_cftable_entry_t *dflt,
Dominik Brodowskiad913c12008-08-02 16:12:00 +0200148 unsigned int vcc,
Dominik Brodowski5fcd4da2008-07-29 08:38:55 +0200149 void *priv_data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150{
Dominik Brodowski5fcd4da2008-07-29 08:38:55 +0200151 int j;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152
Dominik Brodowski90abdc32010-07-24 17:23:51 +0200153 p_dev->io_lines = 5;
154
Dominik Brodowski5fcd4da2008-07-29 08:38:55 +0200155 if ((cf->io.nwin > 0) && cf->io.win[0].base) {
156 printk(KERN_INFO "(teles_cs: looks like the 96 model)\n");
Dominik Brodowski90abdc32010-07-24 17:23:51 +0200157 p_dev->resource[0]->start = cf->io.win[0].base;
158 if (!pcmcia_request_io(p_dev))
Dominik Brodowski5fcd4da2008-07-29 08:38:55 +0200159 return 0;
160 } else {
161 printk(KERN_INFO "(teles_cs: looks like the 97 model)\n");
Dominik Brodowski5fcd4da2008-07-29 08:38:55 +0200162 for (j = 0x2f0; j > 0x100; j -= 0x10) {
Dominik Brodowski90abdc32010-07-24 17:23:51 +0200163 p_dev->resource[0]->start = j;
164 if (!pcmcia_request_io(p_dev))
Dominik Brodowski5fcd4da2008-07-29 08:38:55 +0200165 return 0;
166 }
167 }
168 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700169}
170
Henne158e33d2010-03-25 12:05:30 +0000171static int __devinit teles_cs_config(struct pcmcia_device *link)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173 local_info_t *dev;
Dominik Brodowskie773cfe2009-10-24 15:50:13 +0200174 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175 IsdnCard_t icard;
176
Dominik Brodowskie773cfe2009-10-24 15:50:13 +0200177 dev_dbg(&link->dev, "teles_config(0x%p)\n", link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178 dev = link->priv;
179
Dominik Brodowski5fcd4da2008-07-29 08:38:55 +0200180 i = pcmcia_loop_config(link, teles_cs_configcheck, NULL);
Dominik Brodowskie773cfe2009-10-24 15:50:13 +0200181 if (i != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182 goto cs_failed;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183
Dominik Brodowskieb141202010-03-07 12:21:16 +0100184 if (!link->irq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185 goto cs_failed;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186
Dominik Brodowski1ac71e52010-07-29 19:27:09 +0200187 i = pcmcia_enable_device(link);
Dominik Brodowskie773cfe2009-10-24 15:50:13 +0200188 if (i != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189 goto cs_failed;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191 /* Finally, report what we've done */
Dominik Brodowskided6a1a2010-03-20 19:35:12 +0100192 dev_info(&link->dev, "index 0x%02x:",
Dominik Brodowski7feabb62010-07-29 18:35:47 +0200193 link->config_index);
Dominik Brodowski1ac71e52010-07-29 19:27:09 +0200194 printk(", irq %d", link->irq);
Dominik Brodowski9a017a92010-07-24 15:58:54 +0200195 if (link->resource[0])
196 printk(" & %pR", link->resource[0]);
197 if (link->resource[1])
198 printk(" & %pR", link->resource[1]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199 printk("\n");
200
Dominik Brodowskieb141202010-03-07 12:21:16 +0100201 icard.para[0] = link->irq;
Dominik Brodowski9a017a92010-07-24 15:58:54 +0200202 icard.para[1] = link->resource[0]->start;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203 icard.protocol = protocol;
204 icard.typ = ISDN_CTYPE_TELESPCMCIA;
205
206 i = hisax_init_pcmcia(link, &(((local_info_t*)link->priv)->busy), &icard);
207 if (i < 0) {
208 printk(KERN_ERR "teles_cs: failed to initialize Teles PCMCIA %d at i/o %#x\n",
Dominik Brodowski9a017a92010-07-24 15:58:54 +0200209 i, (unsigned int) link->resource[0]->start);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700210 teles_cs_release(link);
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200211 return -ENODEV;
212 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200214 ((local_info_t*)link->priv)->cardnr = i;
215 return 0;
216
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217cs_failed:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218 teles_cs_release(link);
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200219 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220} /* teles_cs_config */
221
222/*======================================================================
223
224 After a card is removed, teles_cs_release() will unregister the net
225 device, and release the PCMCIA configuration. If the device is
226 still open, this will be postponed until it is closed.
227
228======================================================================*/
229
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200230static void teles_cs_release(struct pcmcia_device *link)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231{
232 local_info_t *local = link->priv;
233
Dominik Brodowskie773cfe2009-10-24 15:50:13 +0200234 dev_dbg(&link->dev, "teles_cs_release(0x%p)\n", link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235
236 if (local) {
237 if (local->cardnr >= 0) {
238 /* no unregister function with hisax */
239 HiSax_closecard(local->cardnr);
240 }
241 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200243 pcmcia_disable_device(link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244} /* teles_cs_release */
245
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200246static int teles_suspend(struct pcmcia_device *link)
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100247{
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100248 local_info_t *dev = link->priv;
249
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100250 dev->busy = 1;
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100251
252 return 0;
253}
254
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200255static int teles_resume(struct pcmcia_device *link)
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100256{
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100257 local_info_t *dev = link->priv;
258
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100259 dev->busy = 0;
260
261 return 0;
262}
263
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264
Dominik Brodowski0a10d732005-06-27 16:28:24 -0700265static struct pcmcia_device_id teles_ids[] = {
266 PCMCIA_DEVICE_PROD_ID12("TELES", "S0/PC", 0x67b50eae, 0xe9e70119),
267 PCMCIA_DEVICE_NULL,
268};
269MODULE_DEVICE_TABLE(pcmcia, teles_ids);
270
Linus Torvalds1da177e2005-04-16 15:20:36 -0700271static struct pcmcia_driver teles_cs_driver = {
272 .owner = THIS_MODULE,
273 .drv = {
274 .name = "teles_cs",
275 },
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200276 .probe = teles_probe,
Henne158e33d2010-03-25 12:05:30 +0000277 .remove = __devexit_p(teles_detach),
Dominik Brodowski0a10d732005-06-27 16:28:24 -0700278 .id_table = teles_ids,
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100279 .suspend = teles_suspend,
280 .resume = teles_resume,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281};
282
283static int __init init_teles_cs(void)
284{
285 return pcmcia_register_driver(&teles_cs_driver);
286}
287
288static void __exit exit_teles_cs(void)
289{
290 pcmcia_unregister_driver(&teles_cs_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291}
292
293module_init(init_teles_cs);
294module_exit(exit_teles_cs);