xref: /linux/drivers/net/arcnet/com20020_cs.c (revision f2ee442115c9b6219083c019939a9cc0c9abb2f8)
1 /*
2  * Linux ARCnet driver - COM20020 PCMCIA support
3  *
4  * Written 1994-1999 by Avery Pennarun,
5  *    based on an ISA version by David Woodhouse.
6  * Derived from ibmtr_cs.c by Steve Kipisz (pcmcia-cs 3.1.4)
7  *    which was derived from pcnet_cs.c by David Hinds.
8  * Some additional portions derived from skeleton.c by Donald Becker.
9  *
10  * Special thanks to Contemporary Controls, Inc. (www.ccontrols.com)
11  *  for sponsoring the further development of this driver.
12  *
13  * **********************
14  *
15  * The original copyright of skeleton.c was as follows:
16  *
17  * skeleton.c Written 1993 by Donald Becker.
18  * Copyright 1993 United States Government as represented by the
19  * Director, National Security Agency.  This software may only be used
20  * and distributed according to the terms of the GNU General Public License as
21  * modified by SRC, incorporated herein by reference.
22  *
23  * **********************
24  * Changes:
25  * Arnaldo Carvalho de Melo <acme@conectiva.com.br> - 08/08/2000
26  * - reorganize kmallocs in com20020_attach, checking all for failure
27  *   and releasing the previous allocations if one fails
28  * **********************
29  *
30  * For more details, see drivers/net/arcnet.c
31  *
32  * **********************
33  */
34 #include <linux/kernel.h>
35 #include <linux/init.h>
36 #include <linux/ptrace.h>
37 #include <linux/slab.h>
38 #include <linux/string.h>
39 #include <linux/timer.h>
40 #include <linux/delay.h>
41 #include <linux/module.h>
42 #include <linux/netdevice.h>
43 #include <linux/arcdevice.h>
44 #include <linux/com20020.h>
45 
46 #include <pcmcia/cistpl.h>
47 #include <pcmcia/ds.h>
48 
49 #include <asm/io.h>
50 #include <asm/system.h>
51 
52 #define VERSION "arcnet: COM20020 PCMCIA support loaded.\n"
53 
54 
55 static void regdump(struct net_device *dev)
56 {
57 #ifdef DEBUG
58     int ioaddr = dev->base_addr;
59     int count;
60 
61     netdev_dbg(dev, "register dump:\n");
62     for (count = ioaddr; count < ioaddr + 16; count++)
63     {
64 	if (!(count % 16))
65 	    pr_cont("%04X:", count);
66 	pr_cont(" %02X", inb(count));
67     }
68     pr_cont("\n");
69 
70     netdev_dbg(dev, "buffer0 dump:\n");
71 	/* set up the address register */
72         count = 0;
73 	outb((count >> 8) | RDDATAflag | AUTOINCflag, _ADDR_HI);
74 	outb(count & 0xff, _ADDR_LO);
75 
76     for (count = 0; count < 256+32; count++)
77     {
78 	if (!(count % 16))
79 	    pr_cont("%04X:", count);
80 
81 	/* copy the data */
82 	pr_cont(" %02X", inb(_MEMDATA));
83     }
84     pr_cont("\n");
85 #endif
86 }
87 
88 
89 
90 /*====================================================================*/
91 
92 /* Parameters that can be set with 'insmod' */
93 
94 static int node;
95 static int timeout = 3;
96 static int backplane;
97 static int clockp;
98 static int clockm;
99 
100 module_param(node, int, 0);
101 module_param(timeout, int, 0);
102 module_param(backplane, int, 0);
103 module_param(clockp, int, 0);
104 module_param(clockm, int, 0);
105 
106 MODULE_LICENSE("GPL");
107 
108 /*====================================================================*/
109 
110 static int com20020_config(struct pcmcia_device *link);
111 static void com20020_release(struct pcmcia_device *link);
112 
113 static void com20020_detach(struct pcmcia_device *p_dev);
114 
115 /*====================================================================*/
116 
117 typedef struct com20020_dev_t {
118     struct net_device       *dev;
119 } com20020_dev_t;
120 
121 static int com20020_probe(struct pcmcia_device *p_dev)
122 {
123     com20020_dev_t *info;
124     struct net_device *dev;
125     struct arcnet_local *lp;
126 
127     dev_dbg(&p_dev->dev, "com20020_attach()\n");
128 
129     /* Create new network device */
130     info = kzalloc(sizeof(struct com20020_dev_t), GFP_KERNEL);
131     if (!info)
132 	goto fail_alloc_info;
133 
134     dev = alloc_arcdev("");
135     if (!dev)
136 	goto fail_alloc_dev;
137 
138     lp = netdev_priv(dev);
139     lp->timeout = timeout;
140     lp->backplane = backplane;
141     lp->clockp = clockp;
142     lp->clockm = clockm & 3;
143     lp->hw.owner = THIS_MODULE;
144 
145     /* fill in our module parameters as defaults */
146     dev->dev_addr[0] = node;
147 
148     p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
149     p_dev->resource[0]->end = 16;
150     p_dev->config_flags |= CONF_ENABLE_IRQ;
151 
152     info->dev = dev;
153     p_dev->priv = info;
154 
155     return com20020_config(p_dev);
156 
157 fail_alloc_dev:
158     kfree(info);
159 fail_alloc_info:
160     return -ENOMEM;
161 } /* com20020_attach */
162 
163 static void com20020_detach(struct pcmcia_device *link)
164 {
165     struct com20020_dev_t *info = link->priv;
166     struct net_device *dev = info->dev;
167 
168     dev_dbg(&link->dev, "detach...\n");
169 
170     dev_dbg(&link->dev, "com20020_detach\n");
171 
172     dev_dbg(&link->dev, "unregister...\n");
173 
174     unregister_netdev(dev);
175 
176     /*
177      * this is necessary because we register our IRQ separately
178      * from card services.
179      */
180     if (dev->irq)
181 	    free_irq(dev->irq, dev);
182 
183     com20020_release(link);
184 
185     /* Unlink device structure, free bits */
186     dev_dbg(&link->dev, "unlinking...\n");
187     if (link->priv)
188     {
189 	dev = info->dev;
190 	if (dev)
191 	{
192 	    dev_dbg(&link->dev, "kfree...\n");
193 	    free_netdev(dev);
194 	}
195 	dev_dbg(&link->dev, "kfree2...\n");
196 	kfree(info);
197     }
198 
199 } /* com20020_detach */
200 
201 static int com20020_config(struct pcmcia_device *link)
202 {
203     struct arcnet_local *lp;
204     com20020_dev_t *info;
205     struct net_device *dev;
206     int i, ret;
207     int ioaddr;
208 
209     info = link->priv;
210     dev = info->dev;
211 
212     dev_dbg(&link->dev, "config...\n");
213 
214     dev_dbg(&link->dev, "com20020_config\n");
215 
216     dev_dbg(&link->dev, "baseport1 is %Xh\n",
217 	    (unsigned int) link->resource[0]->start);
218 
219     i = -ENODEV;
220     link->io_lines = 16;
221 
222     if (!link->resource[0]->start)
223     {
224 	for (ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x10)
225 	{
226 	    link->resource[0]->start = ioaddr;
227 	    i = pcmcia_request_io(link);
228 	    if (i == 0)
229 		break;
230 	}
231     }
232     else
233 	i = pcmcia_request_io(link);
234 
235     if (i != 0)
236     {
237 	dev_dbg(&link->dev, "requestIO failed totally!\n");
238 	goto failed;
239     }
240 
241     ioaddr = dev->base_addr = link->resource[0]->start;
242     dev_dbg(&link->dev, "got ioaddr %Xh\n", ioaddr);
243 
244     dev_dbg(&link->dev, "request IRQ %d\n",
245 	    link->irq);
246     if (!link->irq)
247     {
248 	dev_dbg(&link->dev, "requestIRQ failed totally!\n");
249 	goto failed;
250     }
251 
252     dev->irq = link->irq;
253 
254     ret = pcmcia_enable_device(link);
255     if (ret)
256 	    goto failed;
257 
258     if (com20020_check(dev))
259     {
260 	regdump(dev);
261 	goto failed;
262     }
263 
264     lp = netdev_priv(dev);
265     lp->card_name = "PCMCIA COM20020";
266     lp->card_flags = ARC_CAN_10MBIT; /* pretend all of them can 10Mbit */
267 
268     SET_NETDEV_DEV(dev, &link->dev);
269 
270     i = com20020_found(dev, 0);	/* calls register_netdev */
271 
272     if (i != 0) {
273 	dev_notice(&link->dev,
274 		   "com20020_found() failed\n");
275 	goto failed;
276     }
277 
278     netdev_dbg(dev, "port %#3lx, irq %d\n",
279 	       dev->base_addr, dev->irq);
280     return 0;
281 
282 failed:
283     dev_dbg(&link->dev, "com20020_config failed...\n");
284     com20020_release(link);
285     return -ENODEV;
286 } /* com20020_config */
287 
288 static void com20020_release(struct pcmcia_device *link)
289 {
290 	dev_dbg(&link->dev, "com20020_release\n");
291 	pcmcia_disable_device(link);
292 }
293 
294 static int com20020_suspend(struct pcmcia_device *link)
295 {
296 	com20020_dev_t *info = link->priv;
297 	struct net_device *dev = info->dev;
298 
299 	if (link->open)
300 		netif_device_detach(dev);
301 
302 	return 0;
303 }
304 
305 static int com20020_resume(struct pcmcia_device *link)
306 {
307 	com20020_dev_t *info = link->priv;
308 	struct net_device *dev = info->dev;
309 
310 	if (link->open) {
311 		int ioaddr = dev->base_addr;
312 		struct arcnet_local *lp = netdev_priv(dev);
313 		ARCRESET;
314 	}
315 
316 	return 0;
317 }
318 
319 static const struct pcmcia_device_id com20020_ids[] = {
320 	PCMCIA_DEVICE_PROD_ID12("Contemporary Control Systems, Inc.",
321 			"PCM20 Arcnet Adapter", 0x59991666, 0x95dfffaf),
322 	PCMCIA_DEVICE_PROD_ID12("SoHard AG",
323 			"SH ARC PCMCIA", 0xf8991729, 0x69dff0c7),
324 	PCMCIA_DEVICE_NULL
325 };
326 MODULE_DEVICE_TABLE(pcmcia, com20020_ids);
327 
328 static struct pcmcia_driver com20020_cs_driver = {
329 	.owner		= THIS_MODULE,
330 	.name		= "com20020_cs",
331 	.probe		= com20020_probe,
332 	.remove		= com20020_detach,
333 	.id_table	= com20020_ids,
334 	.suspend	= com20020_suspend,
335 	.resume		= com20020_resume,
336 };
337 
338 static int __init init_com20020_cs(void)
339 {
340 	return pcmcia_register_driver(&com20020_cs_driver);
341 }
342 
343 static void __exit exit_com20020_cs(void)
344 {
345 	pcmcia_unregister_driver(&com20020_cs_driver);
346 }
347 
348 module_init(init_com20020_cs);
349 module_exit(exit_com20020_cs);
350