1 /*======================================================================
2 
3   An elsa_cs PCMCIA client driver
4 
5   This driver is for the Elsa PCM ISDN Cards, i.e. the MicroLink
6 
7 
8   The contents of this file are subject to the Mozilla Public
9   License Version 1.1 (the "License"); you may not use this file
10   except in compliance with the License. You may obtain a copy of
11   the License at http://www.mozilla.org/MPL/
12 
13   Software distributed under the License is distributed on an "AS
14   IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
15   implied. See the License for the specific language governing
16   rights and limitations under the License.
17 
18   The initial developer of the original code is David A. Hinds
19   <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
20   are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
21 
22   Modifications from dummy_cs.c are Copyright (C) 1999-2001 Klaus
23   Lichtenwalder <Lichtenwalder@ACM.org>. All Rights Reserved.
24 
25   Alternatively, the contents of this file may be used under the
26   terms of the GNU General Public License version 2 (the "GPL"), in
27   which case the provisions of the GPL are applicable instead of the
28   above.  If you wish to allow the use of your version of this file
29   only under the terms of the GPL and not to allow others to use
30   your version of this file under the MPL, indicate your decision
31   by deleting the provisions above and replace them with the notice
32   and other provisions required by the GPL.  If you do not delete
33   the provisions above, a recipient may use your version of this
34   file under either the MPL or the GPL.
35 
36   ======================================================================*/
37 
38 #include <linux/module.h>
39 #include <linux/kernel.h>
40 #include <linux/init.h>
41 #include <linux/ptrace.h>
42 #include <linux/slab.h>
43 #include <linux/string.h>
44 #include <linux/timer.h>
45 #include <linux/ioport.h>
46 #include <asm/io.h>
47 
48 #include <pcmcia/cistpl.h>
49 #include <pcmcia/cisreg.h>
50 #include <pcmcia/ds.h>
51 #include "hisax_cfg.h"
52 
53 MODULE_DESCRIPTION("ISDN4Linux: PCMCIA client driver for Elsa PCM cards");
54 MODULE_AUTHOR("Klaus Lichtenwalder");
55 MODULE_LICENSE("Dual MPL/GPL");
56 
57 
58 /*====================================================================*/
59 
60 /* Parameters that can be set with 'insmod' */
61 
62 static int protocol = 2;        /* EURO-ISDN Default */
63 module_param(protocol, int, 0);
64 
65 static int elsa_cs_config(struct pcmcia_device *link);
66 static void elsa_cs_release(struct pcmcia_device *link);
67 static void elsa_cs_detach(struct pcmcia_device *p_dev);
68 
69 typedef struct local_info_t {
70 	struct pcmcia_device	*p_dev;
71 	int                 busy;
72 	int			cardnr;
73 } local_info_t;
74 
elsa_cs_probe(struct pcmcia_device * link)75 static int elsa_cs_probe(struct pcmcia_device *link)
76 {
77 	local_info_t *local;
78 
79 	dev_dbg(&link->dev, "elsa_cs_attach()\n");
80 
81 	/* Allocate space for private device-specific data */
82 	local = kzalloc(sizeof(local_info_t), GFP_KERNEL);
83 	if (!local) return -ENOMEM;
84 
85 	local->p_dev = link;
86 	link->priv = local;
87 
88 	local->cardnr = -1;
89 
90 	return elsa_cs_config(link);
91 } /* elsa_cs_attach */
92 
elsa_cs_detach(struct pcmcia_device * link)93 static void elsa_cs_detach(struct pcmcia_device *link)
94 {
95 	local_info_t *info = link->priv;
96 
97 	dev_dbg(&link->dev, "elsa_cs_detach(0x%p)\n", link);
98 
99 	info->busy = 1;
100 	elsa_cs_release(link);
101 
102 	kfree(info);
103 } /* elsa_cs_detach */
104 
elsa_cs_configcheck(struct pcmcia_device * p_dev,void * priv_data)105 static int elsa_cs_configcheck(struct pcmcia_device *p_dev, void *priv_data)
106 {
107 	int j;
108 
109 	p_dev->io_lines = 3;
110 	p_dev->resource[0]->end = 8;
111 	p_dev->resource[0]->flags &= IO_DATA_PATH_WIDTH;
112 	p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO;
113 
114 	if ((p_dev->resource[0]->end) && p_dev->resource[0]->start) {
115 		printk(KERN_INFO "(elsa_cs: looks like the 96 model)\n");
116 		if (!pcmcia_request_io(p_dev))
117 			return 0;
118 	} else {
119 		printk(KERN_INFO "(elsa_cs: looks like the 97 model)\n");
120 		for (j = 0x2f0; j > 0x100; j -= 0x10) {
121 			p_dev->resource[0]->start = j;
122 			if (!pcmcia_request_io(p_dev))
123 				return 0;
124 		}
125 	}
126 	return -ENODEV;
127 }
128 
elsa_cs_config(struct pcmcia_device * link)129 static int elsa_cs_config(struct pcmcia_device *link)
130 {
131 	int i;
132 	IsdnCard_t icard;
133 
134 	dev_dbg(&link->dev, "elsa_config(0x%p)\n", link);
135 
136 	link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
137 
138 	i = pcmcia_loop_config(link, elsa_cs_configcheck, NULL);
139 	if (i != 0)
140 		goto failed;
141 
142 	if (!link->irq)
143 		goto failed;
144 
145 	i = pcmcia_enable_device(link);
146 	if (i != 0)
147 		goto failed;
148 
149 	icard.para[0] = link->irq;
150 	icard.para[1] = link->resource[0]->start;
151 	icard.protocol = protocol;
152 	icard.typ = ISDN_CTYPE_ELSA_PCMCIA;
153 
154 	i = hisax_init_pcmcia(link, &(((local_info_t *)link->priv)->busy), &icard);
155 	if (i < 0) {
156 		printk(KERN_ERR "elsa_cs: failed to initialize Elsa "
157 		       "PCMCIA %d with %pR\n", i, link->resource[0]);
158 		elsa_cs_release(link);
159 	} else
160 		((local_info_t *)link->priv)->cardnr = i;
161 
162 	return 0;
163 failed:
164 	elsa_cs_release(link);
165 	return -ENODEV;
166 } /* elsa_cs_config */
167 
elsa_cs_release(struct pcmcia_device * link)168 static void elsa_cs_release(struct pcmcia_device *link)
169 {
170 	local_info_t *local = link->priv;
171 
172 	dev_dbg(&link->dev, "elsa_cs_release(0x%p)\n", link);
173 
174 	if (local) {
175 		if (local->cardnr >= 0) {
176 			/* no unregister function with hisax */
177 			HiSax_closecard(local->cardnr);
178 		}
179 	}
180 
181 	pcmcia_disable_device(link);
182 } /* elsa_cs_release */
183 
elsa_suspend(struct pcmcia_device * link)184 static int elsa_suspend(struct pcmcia_device *link)
185 {
186 	local_info_t *dev = link->priv;
187 
188 	dev->busy = 1;
189 
190 	return 0;
191 }
192 
elsa_resume(struct pcmcia_device * link)193 static int elsa_resume(struct pcmcia_device *link)
194 {
195 	local_info_t *dev = link->priv;
196 
197 	dev->busy = 0;
198 
199 	return 0;
200 }
201 
202 static const struct pcmcia_device_id elsa_ids[] = {
203 	PCMCIA_DEVICE_PROD_ID12("ELSA AG (Aachen, Germany)", "MicroLink ISDN/MC ", 0x983de2c4, 0x333ba257),
204 	PCMCIA_DEVICE_PROD_ID12("ELSA GmbH, Aachen", "MicroLink ISDN/MC ", 0x639e5718, 0x333ba257),
205 	PCMCIA_DEVICE_NULL
206 };
207 MODULE_DEVICE_TABLE(pcmcia, elsa_ids);
208 
209 static struct pcmcia_driver elsa_cs_driver = {
210 	.owner		= THIS_MODULE,
211 	.name		= "elsa_cs",
212 	.probe		= elsa_cs_probe,
213 	.remove		= elsa_cs_detach,
214 	.id_table	= elsa_ids,
215 	.suspend	= elsa_suspend,
216 	.resume		= elsa_resume,
217 };
218 module_pcmcia_driver(elsa_cs_driver);
219