blob: 3787fc70cf8fd7f551b1c9f8328d82d2d48c6730 [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/cs.h>
31#include <pcmcia/cistpl.h>
32#include <pcmcia/cisreg.h>
33#include <pcmcia/ds.h>
34#include "hisax_cfg.h"
35
36MODULE_DESCRIPTION("ISDN4Linux: PCMCIA client driver for Teles PCMCIA cards");
37MODULE_AUTHOR("Christof Petig, christof.petig@wtal.de, Karsten Keil, kkeil@suse.de");
38MODULE_LICENSE("GPL");
39
Linus Torvalds1da177e2005-04-16 15:20:36 -070040
41/*====================================================================*/
42
43/* Parameters that can be set with 'insmod' */
44
45static int protocol = 2; /* EURO-ISDN Default */
46module_param(protocol, int, 0);
47
48/*====================================================================*/
49
50/*
51 The event() function is this driver's Card Services event handler.
52 It will be called by Card Services when an appropriate card status
53 event is received. The config() and release() entry points are
54 used to configure or release a socket, in response to card insertion
55 and ejection events. They are invoked from the teles_cs event
56 handler.
57*/
58
Henne158e33d2010-03-25 12:05:30 +000059static int teles_cs_config(struct pcmcia_device *link) __devinit ;
Dominik Brodowskifba395e2006-03-31 17:21:06 +020060static void teles_cs_release(struct pcmcia_device *link);
Linus Torvalds1da177e2005-04-16 15:20:36 -070061
62/*
63 The attach() and detach() entry points are used to create and destroy
64 "instances" of the driver, where each instance represents everything
65 needed to manage one actual PCMCIA card.
66*/
67
Henne158e33d2010-03-25 12:05:30 +000068static void teles_detach(struct pcmcia_device *p_dev) __devexit ;
Linus Torvalds1da177e2005-04-16 15:20:36 -070069
Linus Torvalds1da177e2005-04-16 15:20:36 -070070typedef struct local_info_t {
Dominik Brodowskifd238232006-03-05 10:45:09 +010071 struct pcmcia_device *p_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -070072 int busy;
73 int cardnr;
74} local_info_t;
75
76/*======================================================================
77
78 teles_attach() creates an "instance" of the driver, allocatingx
79 local data structures for one device. The device is registered
80 with Card Services.
81
82 The dev_link structure is initialized, but we don't actually
83 configure the card at this point -- we wait until we receive a
84 card insertion event.
85
86======================================================================*/
87
Henne158e33d2010-03-25 12:05:30 +000088static int __devinit teles_probe(struct pcmcia_device *link)
Linus Torvalds1da177e2005-04-16 15:20:36 -070089{
Linus Torvalds1da177e2005-04-16 15:20:36 -070090 local_info_t *local;
Linus Torvalds1da177e2005-04-16 15:20:36 -070091
Dominik Brodowskie773cfe2009-10-24 15:50:13 +020092 dev_dbg(&link->dev, "teles_attach()\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -070093
94 /* Allocate space for private device-specific data */
Burman Yan41f96932006-12-08 02:39:35 -080095 local = kzalloc(sizeof(local_info_t), GFP_KERNEL);
Dominik Brodowskif8cfa612005-11-14 21:25:51 +010096 if (!local) return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -070097 local->cardnr = -1;
Dominik Brodowskifd238232006-03-05 10:45:09 +010098
Dominik Brodowskifba395e2006-03-31 17:21:06 +020099 local->p_dev = link;
Dominik Brodowskifd238232006-03-05 10:45:09 +0100100 link->priv = local;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102 /*
103 General socket configuration defaults can go here. In this
104 client, we assume very little, and rely on the CIS for almost
105 everything. In most clients, many details (i.e., number, sizes,
106 and attributes of IO windows) are fixed by the nature of the
107 device, and can be hard-wired here.
108 */
109 link->io.NumPorts1 = 96;
110 link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
111 link->io.IOAddrLines = 5;
112
113 link->conf.Attributes = CONF_ENABLE_IRQ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700114 link->conf.IntType = INT_MEMORY_AND_IO;
115
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200116 return teles_cs_config(link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117} /* teles_attach */
118
119/*======================================================================
120
121 This deletes a driver "instance". The device is de-registered
122 with Card Services. If it has been released, all local data
123 structures are freed. Otherwise, the structures will be freed
124 when the device is released.
125
126======================================================================*/
127
Henne158e33d2010-03-25 12:05:30 +0000128static void __devexit teles_detach(struct pcmcia_device *link)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129{
Dominik Brodowskie2d40962006-03-02 00:09:29 +0100130 local_info_t *info = link->priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131
Dominik Brodowskie773cfe2009-10-24 15:50:13 +0200132 dev_dbg(&link->dev, "teles_detach(0x%p)\n", link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133
Dominik Brodowskie2d40962006-03-02 00:09:29 +0100134 info->busy = 1;
135 teles_cs_release(link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136
Dominik Brodowskie2d40962006-03-02 00:09:29 +0100137 kfree(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138} /* teles_detach */
139
140/*======================================================================
141
142 teles_cs_config() is scheduled to run after a CARD_INSERTION event
143 is received, to configure the PCMCIA socket, and to make the
144 device available to the system.
145
146======================================================================*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147
Dominik Brodowski5fcd4da2008-07-29 08:38:55 +0200148static int teles_cs_configcheck(struct pcmcia_device *p_dev,
149 cistpl_cftable_entry_t *cf,
Dominik Brodowski8e2fc392008-08-02 15:30:31 +0200150 cistpl_cftable_entry_t *dflt,
Dominik Brodowskiad913c12008-08-02 16:12:00 +0200151 unsigned int vcc,
Dominik Brodowski5fcd4da2008-07-29 08:38:55 +0200152 void *priv_data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153{
Dominik Brodowski5fcd4da2008-07-29 08:38:55 +0200154 int j;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700155
Dominik Brodowski5fcd4da2008-07-29 08:38:55 +0200156 if ((cf->io.nwin > 0) && cf->io.win[0].base) {
157 printk(KERN_INFO "(teles_cs: looks like the 96 model)\n");
Dominik Brodowski5fcd4da2008-07-29 08:38:55 +0200158 p_dev->io.BasePort1 = cf->io.win[0].base;
159 if (!pcmcia_request_io(p_dev, &p_dev->io))
160 return 0;
161 } else {
162 printk(KERN_INFO "(teles_cs: looks like the 97 model)\n");
Dominik Brodowski5fcd4da2008-07-29 08:38:55 +0200163 for (j = 0x2f0; j > 0x100; j -= 0x10) {
164 p_dev->io.BasePort1 = j;
165 if (!pcmcia_request_io(p_dev, &p_dev->io))
166 return 0;
167 }
168 }
169 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700170}
171
Henne158e33d2010-03-25 12:05:30 +0000172static int __devinit teles_cs_config(struct pcmcia_device *link)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174 local_info_t *dev;
Dominik Brodowskie773cfe2009-10-24 15:50:13 +0200175 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176 IsdnCard_t icard;
177
Dominik Brodowskie773cfe2009-10-24 15:50:13 +0200178 dev_dbg(&link->dev, "teles_config(0x%p)\n", link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179 dev = link->priv;
180
Dominik Brodowski5fcd4da2008-07-29 08:38:55 +0200181 i = pcmcia_loop_config(link, teles_cs_configcheck, NULL);
Dominik Brodowskie773cfe2009-10-24 15:50:13 +0200182 if (i != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183 goto cs_failed;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184
Dominik Brodowskieb141202010-03-07 12:21:16 +0100185 if (!link->irq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186 goto cs_failed;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200188 i = pcmcia_request_configuration(link, &link->conf);
Dominik Brodowskie773cfe2009-10-24 15:50:13 +0200189 if (i != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190 goto cs_failed;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192 /* Finally, report what we've done */
Dominik Brodowskided6a1a2010-03-20 19:35:12 +0100193 dev_info(&link->dev, "index 0x%02x:",
194 link->conf.ConfigIndex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195 if (link->conf.Attributes & CONF_ENABLE_IRQ)
Dominik Brodowskieb141202010-03-07 12:21:16 +0100196 printk(", irq %d", link->irq);
Dominik Brodowski9a017a92010-07-24 15:58:54 +0200197 if (link->resource[0])
198 printk(" & %pR", link->resource[0]);
199 if (link->resource[1])
200 printk(" & %pR", link->resource[1]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201 printk("\n");
202
Dominik Brodowskieb141202010-03-07 12:21:16 +0100203 icard.para[0] = link->irq;
Dominik Brodowski9a017a92010-07-24 15:58:54 +0200204 icard.para[1] = link->resource[0]->start;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205 icard.protocol = protocol;
206 icard.typ = ISDN_CTYPE_TELESPCMCIA;
207
208 i = hisax_init_pcmcia(link, &(((local_info_t*)link->priv)->busy), &icard);
209 if (i < 0) {
210 printk(KERN_ERR "teles_cs: failed to initialize Teles PCMCIA %d at i/o %#x\n",
Dominik Brodowski9a017a92010-07-24 15:58:54 +0200211 i, (unsigned int) link->resource[0]->start);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700212 teles_cs_release(link);
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200213 return -ENODEV;
214 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200216 ((local_info_t*)link->priv)->cardnr = i;
217 return 0;
218
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219cs_failed:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220 teles_cs_release(link);
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200221 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222} /* teles_cs_config */
223
224/*======================================================================
225
226 After a card is removed, teles_cs_release() will unregister the net
227 device, and release the PCMCIA configuration. If the device is
228 still open, this will be postponed until it is closed.
229
230======================================================================*/
231
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200232static void teles_cs_release(struct pcmcia_device *link)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233{
234 local_info_t *local = link->priv;
235
Dominik Brodowskie773cfe2009-10-24 15:50:13 +0200236 dev_dbg(&link->dev, "teles_cs_release(0x%p)\n", link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237
238 if (local) {
239 if (local->cardnr >= 0) {
240 /* no unregister function with hisax */
241 HiSax_closecard(local->cardnr);
242 }
243 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200245 pcmcia_disable_device(link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246} /* teles_cs_release */
247
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200248static int teles_suspend(struct pcmcia_device *link)
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100249{
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100250 local_info_t *dev = link->priv;
251
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100252 dev->busy = 1;
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100253
254 return 0;
255}
256
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200257static int teles_resume(struct pcmcia_device *link)
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100258{
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100259 local_info_t *dev = link->priv;
260
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100261 dev->busy = 0;
262
263 return 0;
264}
265
Linus Torvalds1da177e2005-04-16 15:20:36 -0700266
Dominik Brodowski0a10d732005-06-27 16:28:24 -0700267static struct pcmcia_device_id teles_ids[] = {
268 PCMCIA_DEVICE_PROD_ID12("TELES", "S0/PC", 0x67b50eae, 0xe9e70119),
269 PCMCIA_DEVICE_NULL,
270};
271MODULE_DEVICE_TABLE(pcmcia, teles_ids);
272
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273static struct pcmcia_driver teles_cs_driver = {
274 .owner = THIS_MODULE,
275 .drv = {
276 .name = "teles_cs",
277 },
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200278 .probe = teles_probe,
Henne158e33d2010-03-25 12:05:30 +0000279 .remove = __devexit_p(teles_detach),
Dominik Brodowski0a10d732005-06-27 16:28:24 -0700280 .id_table = teles_ids,
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100281 .suspend = teles_suspend,
282 .resume = teles_resume,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283};
284
285static int __init init_teles_cs(void)
286{
287 return pcmcia_register_driver(&teles_cs_driver);
288}
289
290static void __exit exit_teles_cs(void)
291{
292 pcmcia_unregister_driver(&teles_cs_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293}
294
295module_init(init_teles_cs);
296module_exit(exit_teles_cs);