blob: 7b5c77b7bd27ebc9185a99da37ac7bc0871c96fb [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
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
Linus Torvalds1da177e2005-04-16 15:20:36 -070046#include <pcmcia/cs_types.h>
47#include <pcmcia/cs.h>
48#include <pcmcia/cistpl.h>
49#include <pcmcia/ds.h>
50
51#include <asm/io.h>
52#include <asm/system.h>
53
54#define VERSION "arcnet: COM20020 PCMCIA support loaded.\n"
55
56#ifdef PCMCIA_DEBUG
57
58static int pc_debug = PCMCIA_DEBUG;
59module_param(pc_debug, int, 0);
60#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
61
62static void regdump(struct net_device *dev)
63{
64 int ioaddr = dev->base_addr;
65 int count;
66
67 printk("com20020 register dump:\n");
68 for (count = ioaddr; count < ioaddr + 16; count++)
69 {
70 if (!(count % 16))
71 printk("\n%04X: ", count);
72 printk("%02X ", inb(count));
73 }
74 printk("\n");
75
76 printk("buffer0 dump:\n");
77 /* set up the address register */
78 count = 0;
79 outb((count >> 8) | RDDATAflag | AUTOINCflag, _ADDR_HI);
80 outb(count & 0xff, _ADDR_LO);
81
82 for (count = 0; count < 256+32; count++)
83 {
84 if (!(count % 16))
85 printk("\n%04X: ", count);
86
87 /* copy the data */
88 printk("%02X ", inb(_MEMDATA));
89 }
90 printk("\n");
91}
92
93#else
94
95#define DEBUG(n, args...) do { } while (0)
96static inline void regdump(struct net_device *dev) { }
97
98#endif
99
100
101/*====================================================================*/
102
103/* Parameters that can be set with 'insmod' */
104
105static int node;
106static int timeout = 3;
107static int backplane;
108static int clockp;
109static int clockm;
110
111module_param(node, int, 0);
112module_param(timeout, int, 0);
113module_param(backplane, int, 0);
114module_param(clockp, int, 0);
115module_param(clockm, int, 0);
116
117MODULE_LICENSE("GPL");
118
119/*====================================================================*/
120
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200121static int com20020_config(struct pcmcia_device *link);
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200122static void com20020_release(struct pcmcia_device *link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123
Dominik Brodowskicc3b4862005-11-14 21:23:14 +0100124static void com20020_detach(struct pcmcia_device *p_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126/*====================================================================*/
127
128typedef struct com20020_dev_t {
129 struct net_device *dev;
130 dev_node_t node;
131} com20020_dev_t;
132
133/*======================================================================
134
135 com20020_attach() creates an "instance" of the driver, allocating
136 local data structures for one device. The device is registered
137 with Card Services.
138
139======================================================================*/
140
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200141static int com20020_probe(struct pcmcia_device *p_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143 com20020_dev_t *info;
144 struct net_device *dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145 struct arcnet_local *lp;
Dominik Brodowskif8cfa612005-11-14 21:25:51 +0100146
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147 DEBUG(0, "com20020_attach()\n");
148
149 /* Create new network device */
Yoann Padioleaudd00cc42007-07-19 01:49:03 -0700150 info = kzalloc(sizeof(struct com20020_dev_t), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151 if (!info)
152 goto fail_alloc_info;
153
154 dev = alloc_arcdev("");
155 if (!dev)
156 goto fail_alloc_dev;
157
Wang Chen4cf16532008-11-12 23:38:14 -0800158 lp = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159 lp->timeout = timeout;
160 lp->backplane = backplane;
161 lp->clockp = clockp;
162 lp->clockm = clockm & 3;
163 lp->hw.owner = THIS_MODULE;
164
165 /* fill in our module parameters as defaults */
166 dev->dev_addr[0] = node;
167
Dominik Brodowskifd238232006-03-05 10:45:09 +0100168 p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
169 p_dev->io.NumPorts1 = 16;
170 p_dev->io.IOAddrLines = 16;
171 p_dev->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
172 p_dev->irq.IRQInfo1 = IRQ_LEVEL_ID;
173 p_dev->conf.Attributes = CONF_ENABLE_IRQ;
174 p_dev->conf.IntType = INT_MEMORY_AND_IO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175
Dominik Brodowskifd238232006-03-05 10:45:09 +0100176 p_dev->irq.Instance = info->dev = dev;
177 p_dev->priv = info;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200179 return com20020_config(p_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180
181fail_alloc_dev:
182 kfree(info);
183fail_alloc_info:
Dominik Brodowskif8cfa612005-11-14 21:25:51 +0100184 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185} /* com20020_attach */
186
187/*======================================================================
188
189 This deletes a driver "instance". The device is de-registered
190 with Card Services. If it has been released, all local data
191 structures are freed. Otherwise, the structures will be freed
192 when the device is released.
193
194======================================================================*/
195
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200196static void com20020_detach(struct pcmcia_device *link)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197{
198 struct com20020_dev_t *info = link->priv;
Dominik Brodowskib4635812005-11-14 21:25:35 +0100199 struct net_device *dev = info->dev;
200
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201 DEBUG(1,"detach...\n");
202
203 DEBUG(0, "com20020_detach(0x%p)\n", link);
204
Dominik Brodowskifd238232006-03-05 10:45:09 +0100205 if (link->dev_node) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700206 DEBUG(1,"unregister...\n");
207
208 unregister_netdev(dev);
Dominik Brodowskib4635812005-11-14 21:25:35 +0100209
Linus Torvalds1da177e2005-04-16 15:20:36 -0700210 /*
211 * this is necessary because we register our IRQ separately
212 * from card services.
213 */
214 if (dev->irq)
215 free_irq(dev->irq, dev);
216 }
217
Dominik Brodowskie2d40962006-03-02 00:09:29 +0100218 com20020_release(link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220 /* Unlink device structure, free bits */
221 DEBUG(1,"unlinking...\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222 if (link->priv)
223 {
224 dev = info->dev;
225 if (dev)
226 {
227 DEBUG(1,"kfree...\n");
228 free_netdev(dev);
229 }
230 DEBUG(1,"kfree2...\n");
231 kfree(info);
232 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233
234} /* com20020_detach */
235
236/*======================================================================
237
238 com20020_config() is scheduled to run after a CARD_INSERTION event
239 is received, to configure the PCMCIA socket, and to make the
240 device available to the system.
241
242======================================================================*/
243
244#define CS_CHECK(fn, ret) \
245do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
246
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200247static int com20020_config(struct pcmcia_device *link)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248{
249 struct arcnet_local *lp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700250 com20020_dev_t *info;
251 struct net_device *dev;
252 int i, last_ret, last_fn;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253 int ioaddr;
254
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255 info = link->priv;
256 dev = info->dev;
257
258 DEBUG(1,"config...\n");
259
260 DEBUG(0, "com20020_config(0x%p)\n", link);
261
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262 DEBUG(1,"arcnet: baseport1 is %Xh\n", link->io.BasePort1);
Dominik Brodowski4c89e882008-08-03 10:07:45 +0200263 i = -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264 if (!link->io.BasePort1)
265 {
266 for (ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x10)
267 {
268 link->io.BasePort1 = ioaddr;
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200269 i = pcmcia_request_io(link, &link->io);
Dominik Brodowski4c89e882008-08-03 10:07:45 +0200270 if (i == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700271 break;
272 }
273 }
274 else
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200275 i = pcmcia_request_io(link, &link->io);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276
Dominik Brodowski4c89e882008-08-03 10:07:45 +0200277 if (i != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278 {
279 DEBUG(1,"arcnet: requestIO failed totally!\n");
280 goto failed;
281 }
282
283 ioaddr = dev->base_addr = link->io.BasePort1;
284 DEBUG(1,"arcnet: got ioaddr %Xh\n", ioaddr);
285
286 DEBUG(1,"arcnet: request IRQ %d (%Xh/%Xh)\n",
287 link->irq.AssignedIRQ,
288 link->irq.IRQInfo1, link->irq.IRQInfo2);
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200289 i = pcmcia_request_irq(link, &link->irq);
Dominik Brodowski4c89e882008-08-03 10:07:45 +0200290 if (i != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 {
292 DEBUG(1,"arcnet: requestIRQ failed totally!\n");
293 goto failed;
294 }
295
296 dev->irq = link->irq.AssignedIRQ;
297
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200298 CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700299
300 if (com20020_check(dev))
301 {
302 regdump(dev);
303 goto failed;
304 }
305
Wang Chen4cf16532008-11-12 23:38:14 -0800306 lp = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700307 lp->card_name = "PCMCIA COM20020";
308 lp->card_flags = ARC_CAN_10MBIT; /* pretend all of them can 10Mbit */
309
Dominik Brodowskifd238232006-03-05 10:45:09 +0100310 link->dev_node = &info->node;
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200311 SET_NETDEV_DEV(dev, &handle_to_dev(link));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312
313 i = com20020_found(dev, 0); /* calls register_netdev */
314
315 if (i != 0) {
316 DEBUG(1,KERN_NOTICE "com20020_cs: com20020_found() failed\n");
Dominik Brodowskifd238232006-03-05 10:45:09 +0100317 link->dev_node = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318 goto failed;
319 }
320
321 strcpy(info->node.dev_name, dev->name);
322
323 DEBUG(1,KERN_INFO "%s: port %#3lx, irq %d\n",
324 dev->name, dev->base_addr, dev->irq);
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200325 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326
327cs_failed:
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200328 cs_error(link, last_fn, last_ret);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329failed:
330 DEBUG(1,"com20020_config failed...\n");
331 com20020_release(link);
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200332 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333} /* com20020_config */
334
335/*======================================================================
336
337 After a card is removed, com20020_release() will unregister the net
338 device, and release the PCMCIA configuration. If the device is
339 still open, this will be postponed until it is closed.
340
341======================================================================*/
342
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200343static void com20020_release(struct pcmcia_device *link)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344{
Dominik Brodowski5f2a71f2006-01-15 09:32:39 +0100345 DEBUG(0, "com20020_release(0x%p)\n", link);
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200346 pcmcia_disable_device(link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347}
348
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200349static int com20020_suspend(struct pcmcia_device *link)
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100350{
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100351 com20020_dev_t *info = link->priv;
352 struct net_device *dev = info->dev;
353
Dominik Brodowskie2d40962006-03-02 00:09:29 +0100354 if (link->open)
Dominik Brodowski8661bb52006-03-02 00:02:33 +0100355 netif_device_detach(dev);
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100356
357 return 0;
358}
359
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200360static int com20020_resume(struct pcmcia_device *link)
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100361{
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100362 com20020_dev_t *info = link->priv;
363 struct net_device *dev = info->dev;
364
Dominik Brodowskie2d40962006-03-02 00:09:29 +0100365 if (link->open) {
Dominik Brodowski8661bb52006-03-02 00:02:33 +0100366 int ioaddr = dev->base_addr;
Wang Chen4cf16532008-11-12 23:38:14 -0800367 struct arcnet_local *lp = netdev_priv(dev);
Dominik Brodowski8661bb52006-03-02 00:02:33 +0100368 ARCRESET;
369 }
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100370
371 return 0;
372}
373
Dominik Brodowski7fb22bb2005-06-27 16:28:38 -0700374static struct pcmcia_device_id com20020_ids[] = {
Marc Sowen6bb1c392006-06-25 01:56:24 -0700375 PCMCIA_DEVICE_PROD_ID12("Contemporary Control Systems, Inc.",
376 "PCM20 Arcnet Adapter", 0x59991666, 0x95dfffaf),
377 PCMCIA_DEVICE_PROD_ID12("SoHard AG",
378 "SH ARC PCMCIA", 0xf8991729, 0x69dff0c7),
Dominik Brodowski7fb22bb2005-06-27 16:28:38 -0700379 PCMCIA_DEVICE_NULL
380};
381MODULE_DEVICE_TABLE(pcmcia, com20020_ids);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382
383static struct pcmcia_driver com20020_cs_driver = {
384 .owner = THIS_MODULE,
385 .drv = {
386 .name = "com20020_cs",
387 },
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200388 .probe = com20020_probe,
Dominik Brodowskicc3b4862005-11-14 21:23:14 +0100389 .remove = com20020_detach,
Dominik Brodowski7fb22bb2005-06-27 16:28:38 -0700390 .id_table = com20020_ids,
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100391 .suspend = com20020_suspend,
392 .resume = com20020_resume,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700393};
394
395static int __init init_com20020_cs(void)
396{
397 return pcmcia_register_driver(&com20020_cs_driver);
398}
399
400static void __exit exit_com20020_cs(void)
401{
402 pcmcia_unregister_driver(&com20020_cs_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700403}
404
405module_init(init_com20020_cs);
406module_exit(exit_com20020_cs);