blob: 6605480aa37fbe44d1b3977d55ca9890b478fc38 [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 Brodowski5fcd4da2008-07-29 08:38:55 +0200148 void *priv_data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149{
Dominik Brodowski5fcd4da2008-07-29 08:38:55 +0200150 int j;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151
Dominik Brodowski90abdc32010-07-24 17:23:51 +0200152 p_dev->io_lines = 5;
153
Dominik Brodowski5fcd4da2008-07-29 08:38:55 +0200154 if ((cf->io.nwin > 0) && cf->io.win[0].base) {
155 printk(KERN_INFO "(teles_cs: looks like the 96 model)\n");
Dominik Brodowski90abdc32010-07-24 17:23:51 +0200156 p_dev->resource[0]->start = cf->io.win[0].base;
157 if (!pcmcia_request_io(p_dev))
Dominik Brodowski5fcd4da2008-07-29 08:38:55 +0200158 return 0;
159 } else {
160 printk(KERN_INFO "(teles_cs: looks like the 97 model)\n");
Dominik Brodowski5fcd4da2008-07-29 08:38:55 +0200161 for (j = 0x2f0; j > 0x100; j -= 0x10) {
Dominik Brodowski90abdc32010-07-24 17:23:51 +0200162 p_dev->resource[0]->start = j;
163 if (!pcmcia_request_io(p_dev))
Dominik Brodowski5fcd4da2008-07-29 08:38:55 +0200164 return 0;
165 }
166 }
167 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168}
169
Henne158e33d2010-03-25 12:05:30 +0000170static int __devinit teles_cs_config(struct pcmcia_device *link)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172 local_info_t *dev;
Dominik Brodowskie773cfe2009-10-24 15:50:13 +0200173 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174 IsdnCard_t icard;
175
Dominik Brodowskie773cfe2009-10-24 15:50:13 +0200176 dev_dbg(&link->dev, "teles_config(0x%p)\n", link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177 dev = link->priv;
178
Dominik Brodowski5fcd4da2008-07-29 08:38:55 +0200179 i = pcmcia_loop_config(link, teles_cs_configcheck, NULL);
Dominik Brodowskie773cfe2009-10-24 15:50:13 +0200180 if (i != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181 goto cs_failed;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182
Dominik Brodowskieb141202010-03-07 12:21:16 +0100183 if (!link->irq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184 goto cs_failed;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185
Dominik Brodowski1ac71e52010-07-29 19:27:09 +0200186 i = pcmcia_enable_device(link);
Dominik Brodowskie773cfe2009-10-24 15:50:13 +0200187 if (i != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188 goto cs_failed;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190 /* Finally, report what we've done */
Dominik Brodowskided6a1a2010-03-20 19:35:12 +0100191 dev_info(&link->dev, "index 0x%02x:",
Dominik Brodowski7feabb62010-07-29 18:35:47 +0200192 link->config_index);
Dominik Brodowski1ac71e52010-07-29 19:27:09 +0200193 printk(", irq %d", link->irq);
Dominik Brodowski9a017a92010-07-24 15:58:54 +0200194 if (link->resource[0])
195 printk(" & %pR", link->resource[0]);
196 if (link->resource[1])
197 printk(" & %pR", link->resource[1]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198 printk("\n");
199
Dominik Brodowskieb141202010-03-07 12:21:16 +0100200 icard.para[0] = link->irq;
Dominik Brodowski9a017a92010-07-24 15:58:54 +0200201 icard.para[1] = link->resource[0]->start;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202 icard.protocol = protocol;
203 icard.typ = ISDN_CTYPE_TELESPCMCIA;
204
205 i = hisax_init_pcmcia(link, &(((local_info_t*)link->priv)->busy), &icard);
206 if (i < 0) {
207 printk(KERN_ERR "teles_cs: failed to initialize Teles PCMCIA %d at i/o %#x\n",
Dominik Brodowski9a017a92010-07-24 15:58:54 +0200208 i, (unsigned int) link->resource[0]->start);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209 teles_cs_release(link);
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200210 return -ENODEV;
211 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700212
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200213 ((local_info_t*)link->priv)->cardnr = i;
214 return 0;
215
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216cs_failed:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217 teles_cs_release(link);
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200218 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219} /* teles_cs_config */
220
221/*======================================================================
222
223 After a card is removed, teles_cs_release() will unregister the net
224 device, and release the PCMCIA configuration. If the device is
225 still open, this will be postponed until it is closed.
226
227======================================================================*/
228
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200229static void teles_cs_release(struct pcmcia_device *link)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230{
231 local_info_t *local = link->priv;
232
Dominik Brodowskie773cfe2009-10-24 15:50:13 +0200233 dev_dbg(&link->dev, "teles_cs_release(0x%p)\n", link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234
235 if (local) {
236 if (local->cardnr >= 0) {
237 /* no unregister function with hisax */
238 HiSax_closecard(local->cardnr);
239 }
240 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700241
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200242 pcmcia_disable_device(link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243} /* teles_cs_release */
244
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200245static int teles_suspend(struct pcmcia_device *link)
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100246{
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100247 local_info_t *dev = link->priv;
248
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100249 dev->busy = 1;
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100250
251 return 0;
252}
253
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200254static int teles_resume(struct pcmcia_device *link)
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100255{
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100256 local_info_t *dev = link->priv;
257
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100258 dev->busy = 0;
259
260 return 0;
261}
262
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263
Dominik Brodowski0a10d732005-06-27 16:28:24 -0700264static struct pcmcia_device_id teles_ids[] = {
265 PCMCIA_DEVICE_PROD_ID12("TELES", "S0/PC", 0x67b50eae, 0xe9e70119),
266 PCMCIA_DEVICE_NULL,
267};
268MODULE_DEVICE_TABLE(pcmcia, teles_ids);
269
Linus Torvalds1da177e2005-04-16 15:20:36 -0700270static struct pcmcia_driver teles_cs_driver = {
271 .owner = THIS_MODULE,
272 .drv = {
273 .name = "teles_cs",
274 },
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200275 .probe = teles_probe,
Henne158e33d2010-03-25 12:05:30 +0000276 .remove = __devexit_p(teles_detach),
Dominik Brodowski0a10d732005-06-27 16:28:24 -0700277 .id_table = teles_ids,
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100278 .suspend = teles_suspend,
279 .resume = teles_resume,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280};
281
282static int __init init_teles_cs(void)
283{
284 return pcmcia_register_driver(&teles_cs_driver);
285}
286
287static void __exit exit_teles_cs(void)
288{
289 pcmcia_unregister_driver(&teles_cs_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290}
291
292module_init(init_teles_cs);
293module_exit(exit_teles_cs);