blob: 7296102ca255d89ef34bee0a1736d695629214d2 [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 */
Dominik Brodowski90abdc32010-07-24 17:23:51 +0200109 link->resource[0]->end = 96;
110 link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111
112 link->conf.Attributes = CONF_ENABLE_IRQ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113 link->conf.IntType = INT_MEMORY_AND_IO;
114
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200115 return teles_cs_config(link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116} /* teles_attach */
117
118/*======================================================================
119
120 This deletes a driver "instance". The device is de-registered
121 with Card Services. If it has been released, all local data
122 structures are freed. Otherwise, the structures will be freed
123 when the device is released.
124
125======================================================================*/
126
Henne158e33d2010-03-25 12:05:30 +0000127static void __devexit teles_detach(struct pcmcia_device *link)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128{
Dominik Brodowskie2d40962006-03-02 00:09:29 +0100129 local_info_t *info = link->priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130
Dominik Brodowskie773cfe2009-10-24 15:50:13 +0200131 dev_dbg(&link->dev, "teles_detach(0x%p)\n", link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132
Dominik Brodowskie2d40962006-03-02 00:09:29 +0100133 info->busy = 1;
134 teles_cs_release(link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135
Dominik Brodowskie2d40962006-03-02 00:09:29 +0100136 kfree(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137} /* teles_detach */
138
139/*======================================================================
140
141 teles_cs_config() is scheduled to run after a CARD_INSERTION event
142 is received, to configure the PCMCIA socket, and to make the
143 device available to the system.
144
145======================================================================*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146
Dominik Brodowski5fcd4da2008-07-29 08:38:55 +0200147static int teles_cs_configcheck(struct pcmcia_device *p_dev,
148 cistpl_cftable_entry_t *cf,
Dominik Brodowski8e2fc392008-08-02 15:30:31 +0200149 cistpl_cftable_entry_t *dflt,
Dominik Brodowskiad913c12008-08-02 16:12:00 +0200150 unsigned int vcc,
Dominik Brodowski5fcd4da2008-07-29 08:38:55 +0200151 void *priv_data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152{
Dominik Brodowski5fcd4da2008-07-29 08:38:55 +0200153 int j;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154
Dominik Brodowski90abdc32010-07-24 17:23:51 +0200155 p_dev->io_lines = 5;
156
Dominik Brodowski5fcd4da2008-07-29 08:38:55 +0200157 if ((cf->io.nwin > 0) && cf->io.win[0].base) {
158 printk(KERN_INFO "(teles_cs: looks like the 96 model)\n");
Dominik Brodowski90abdc32010-07-24 17:23:51 +0200159 p_dev->resource[0]->start = cf->io.win[0].base;
160 if (!pcmcia_request_io(p_dev))
Dominik Brodowski5fcd4da2008-07-29 08:38:55 +0200161 return 0;
162 } else {
163 printk(KERN_INFO "(teles_cs: looks like the 97 model)\n");
Dominik Brodowski5fcd4da2008-07-29 08:38:55 +0200164 for (j = 0x2f0; j > 0x100; j -= 0x10) {
Dominik Brodowski90abdc32010-07-24 17:23:51 +0200165 p_dev->resource[0]->start = j;
166 if (!pcmcia_request_io(p_dev))
Dominik Brodowski5fcd4da2008-07-29 08:38:55 +0200167 return 0;
168 }
169 }
170 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171}
172
Henne158e33d2010-03-25 12:05:30 +0000173static int __devinit teles_cs_config(struct pcmcia_device *link)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175 local_info_t *dev;
Dominik Brodowskie773cfe2009-10-24 15:50:13 +0200176 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177 IsdnCard_t icard;
178
Dominik Brodowskie773cfe2009-10-24 15:50:13 +0200179 dev_dbg(&link->dev, "teles_config(0x%p)\n", link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180 dev = link->priv;
181
Dominik Brodowski5fcd4da2008-07-29 08:38:55 +0200182 i = pcmcia_loop_config(link, teles_cs_configcheck, NULL);
Dominik Brodowskie773cfe2009-10-24 15:50:13 +0200183 if (i != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184 goto cs_failed;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185
Dominik Brodowskieb141202010-03-07 12:21:16 +0100186 if (!link->irq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187 goto cs_failed;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200189 i = pcmcia_request_configuration(link, &link->conf);
Dominik Brodowskie773cfe2009-10-24 15:50:13 +0200190 if (i != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191 goto cs_failed;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192
Linus Torvalds1da177e2005-04-16 15:20:36 -0700193 /* Finally, report what we've done */
Dominik Brodowskided6a1a2010-03-20 19:35:12 +0100194 dev_info(&link->dev, "index 0x%02x:",
195 link->conf.ConfigIndex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196 if (link->conf.Attributes & CONF_ENABLE_IRQ)
Dominik Brodowskieb141202010-03-07 12:21:16 +0100197 printk(", irq %d", link->irq);
Dominik Brodowski9a017a92010-07-24 15:58:54 +0200198 if (link->resource[0])
199 printk(" & %pR", link->resource[0]);
200 if (link->resource[1])
201 printk(" & %pR", link->resource[1]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202 printk("\n");
203
Dominik Brodowskieb141202010-03-07 12:21:16 +0100204 icard.para[0] = link->irq;
Dominik Brodowski9a017a92010-07-24 15:58:54 +0200205 icard.para[1] = link->resource[0]->start;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700206 icard.protocol = protocol;
207 icard.typ = ISDN_CTYPE_TELESPCMCIA;
208
209 i = hisax_init_pcmcia(link, &(((local_info_t*)link->priv)->busy), &icard);
210 if (i < 0) {
211 printk(KERN_ERR "teles_cs: failed to initialize Teles PCMCIA %d at i/o %#x\n",
Dominik Brodowski9a017a92010-07-24 15:58:54 +0200212 i, (unsigned int) link->resource[0]->start);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213 teles_cs_release(link);
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200214 return -ENODEV;
215 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200217 ((local_info_t*)link->priv)->cardnr = i;
218 return 0;
219
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220cs_failed:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700221 teles_cs_release(link);
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200222 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223} /* teles_cs_config */
224
225/*======================================================================
226
227 After a card is removed, teles_cs_release() will unregister the net
228 device, and release the PCMCIA configuration. If the device is
229 still open, this will be postponed until it is closed.
230
231======================================================================*/
232
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200233static void teles_cs_release(struct pcmcia_device *link)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234{
235 local_info_t *local = link->priv;
236
Dominik Brodowskie773cfe2009-10-24 15:50:13 +0200237 dev_dbg(&link->dev, "teles_cs_release(0x%p)\n", link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238
239 if (local) {
240 if (local->cardnr >= 0) {
241 /* no unregister function with hisax */
242 HiSax_closecard(local->cardnr);
243 }
244 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200246 pcmcia_disable_device(link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247} /* teles_cs_release */
248
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200249static int teles_suspend(struct pcmcia_device *link)
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100250{
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100251 local_info_t *dev = link->priv;
252
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100253 dev->busy = 1;
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100254
255 return 0;
256}
257
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200258static int teles_resume(struct pcmcia_device *link)
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100259{
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100260 local_info_t *dev = link->priv;
261
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100262 dev->busy = 0;
263
264 return 0;
265}
266
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267
Dominik Brodowski0a10d732005-06-27 16:28:24 -0700268static struct pcmcia_device_id teles_ids[] = {
269 PCMCIA_DEVICE_PROD_ID12("TELES", "S0/PC", 0x67b50eae, 0xe9e70119),
270 PCMCIA_DEVICE_NULL,
271};
272MODULE_DEVICE_TABLE(pcmcia, teles_ids);
273
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274static struct pcmcia_driver teles_cs_driver = {
275 .owner = THIS_MODULE,
276 .drv = {
277 .name = "teles_cs",
278 },
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200279 .probe = teles_probe,
Henne158e33d2010-03-25 12:05:30 +0000280 .remove = __devexit_p(teles_detach),
Dominik Brodowski0a10d732005-06-27 16:28:24 -0700281 .id_table = teles_ids,
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100282 .suspend = teles_suspend,
283 .resume = teles_resume,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284};
285
286static int __init init_teles_cs(void)
287{
288 return pcmcia_register_driver(&teles_cs_driver);
289}
290
291static void __exit exit_teles_cs(void)
292{
293 pcmcia_unregister_driver(&teles_cs_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700294}
295
296module_init(init_teles_cs);
297module_exit(exit_teles_cs);