blob: 21d9c9d815d12c9eb2e4d2302b0e99ea514aaf58 [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
Dominik Brodowskidd0fab52009-10-24 15:51:05 +020056#ifdef DEBUG
Linus Torvalds1da177e2005-04-16 15:20:36 -070057
58static void regdump(struct net_device *dev)
59{
60 int ioaddr = dev->base_addr;
61 int count;
62
63 printk("com20020 register dump:\n");
64 for (count = ioaddr; count < ioaddr + 16; count++)
65 {
66 if (!(count % 16))
67 printk("\n%04X: ", count);
68 printk("%02X ", inb(count));
69 }
70 printk("\n");
71
72 printk("buffer0 dump:\n");
73 /* set up the address register */
74 count = 0;
75 outb((count >> 8) | RDDATAflag | AUTOINCflag, _ADDR_HI);
76 outb(count & 0xff, _ADDR_LO);
77
78 for (count = 0; count < 256+32; count++)
79 {
80 if (!(count % 16))
81 printk("\n%04X: ", count);
82
83 /* copy the data */
84 printk("%02X ", inb(_MEMDATA));
85 }
86 printk("\n");
87}
88
89#else
90
Linus Torvalds1da177e2005-04-16 15:20:36 -070091static inline void regdump(struct net_device *dev) { }
92
93#endif
94
95
96/*====================================================================*/
97
98/* Parameters that can be set with 'insmod' */
99
100static int node;
101static int timeout = 3;
102static int backplane;
103static int clockp;
104static int clockm;
105
106module_param(node, int, 0);
107module_param(timeout, int, 0);
108module_param(backplane, int, 0);
109module_param(clockp, int, 0);
110module_param(clockm, int, 0);
111
112MODULE_LICENSE("GPL");
113
114/*====================================================================*/
115
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200116static int com20020_config(struct pcmcia_device *link);
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200117static void com20020_release(struct pcmcia_device *link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118
Dominik Brodowskicc3b4862005-11-14 21:23:14 +0100119static void com20020_detach(struct pcmcia_device *p_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121/*====================================================================*/
122
123typedef struct com20020_dev_t {
124 struct net_device *dev;
125 dev_node_t node;
126} com20020_dev_t;
127
128/*======================================================================
129
130 com20020_attach() creates an "instance" of the driver, allocating
131 local data structures for one device. The device is registered
132 with Card Services.
133
134======================================================================*/
135
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200136static int com20020_probe(struct pcmcia_device *p_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138 com20020_dev_t *info;
139 struct net_device *dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140 struct arcnet_local *lp;
Dominik Brodowskif8cfa612005-11-14 21:25:51 +0100141
Dominik Brodowskidd0fab52009-10-24 15:51:05 +0200142 dev_dbg(&p_dev->dev, "com20020_attach()\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143
144 /* Create new network device */
Yoann Padioleaudd00cc42007-07-19 01:49:03 -0700145 info = kzalloc(sizeof(struct com20020_dev_t), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146 if (!info)
147 goto fail_alloc_info;
148
149 dev = alloc_arcdev("");
150 if (!dev)
151 goto fail_alloc_dev;
152
Wang Chen4cf16532008-11-12 23:38:14 -0800153 lp = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154 lp->timeout = timeout;
155 lp->backplane = backplane;
156 lp->clockp = clockp;
157 lp->clockm = clockm & 3;
158 lp->hw.owner = THIS_MODULE;
159
160 /* fill in our module parameters as defaults */
161 dev->dev_addr[0] = node;
162
Dominik Brodowskifd238232006-03-05 10:45:09 +0100163 p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
164 p_dev->io.NumPorts1 = 16;
165 p_dev->io.IOAddrLines = 16;
166 p_dev->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
Dominik Brodowskifd238232006-03-05 10:45:09 +0100167 p_dev->conf.Attributes = CONF_ENABLE_IRQ;
168 p_dev->conf.IntType = INT_MEMORY_AND_IO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700169
Dominik Brodowski5fa91672009-11-08 17:24:46 +0100170 info->dev = dev;
Dominik Brodowskifd238232006-03-05 10:45:09 +0100171 p_dev->priv = info;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200173 return com20020_config(p_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174
175fail_alloc_dev:
176 kfree(info);
177fail_alloc_info:
Dominik Brodowskif8cfa612005-11-14 21:25:51 +0100178 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179} /* com20020_attach */
180
181/*======================================================================
182
183 This deletes a driver "instance". The device is de-registered
184 with Card Services. If it has been released, all local data
185 structures are freed. Otherwise, the structures will be freed
186 when the device is released.
187
188======================================================================*/
189
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200190static void com20020_detach(struct pcmcia_device *link)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191{
192 struct com20020_dev_t *info = link->priv;
Dominik Brodowskib4635812005-11-14 21:25:35 +0100193 struct net_device *dev = info->dev;
194
Dominik Brodowskidd0fab52009-10-24 15:51:05 +0200195 dev_dbg(&link->dev, "detach...\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196
Dominik Brodowskidd0fab52009-10-24 15:51:05 +0200197 dev_dbg(&link->dev, "com20020_detach\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198
Dominik Brodowskifd238232006-03-05 10:45:09 +0100199 if (link->dev_node) {
Dominik Brodowskidd0fab52009-10-24 15:51:05 +0200200 dev_dbg(&link->dev, "unregister...\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201
202 unregister_netdev(dev);
Dominik Brodowskib4635812005-11-14 21:25:35 +0100203
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204 /*
205 * this is necessary because we register our IRQ separately
206 * from card services.
207 */
208 if (dev->irq)
209 free_irq(dev->irq, dev);
210 }
211
Dominik Brodowskie2d40962006-03-02 00:09:29 +0100212 com20020_release(link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214 /* Unlink device structure, free bits */
Dominik Brodowskidd0fab52009-10-24 15:51:05 +0200215 dev_dbg(&link->dev, "unlinking...\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216 if (link->priv)
217 {
218 dev = info->dev;
219 if (dev)
220 {
Dominik Brodowskidd0fab52009-10-24 15:51:05 +0200221 dev_dbg(&link->dev, "kfree...\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222 free_netdev(dev);
223 }
Dominik Brodowskidd0fab52009-10-24 15:51:05 +0200224 dev_dbg(&link->dev, "kfree2...\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225 kfree(info);
226 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227
228} /* com20020_detach */
229
230/*======================================================================
231
232 com20020_config() is scheduled to run after a CARD_INSERTION event
233 is received, to configure the PCMCIA socket, and to make the
234 device available to the system.
235
236======================================================================*/
237
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200238static int com20020_config(struct pcmcia_device *link)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239{
240 struct arcnet_local *lp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700241 com20020_dev_t *info;
242 struct net_device *dev;
Dominik Brodowskidd0fab52009-10-24 15:51:05 +0200243 int i, ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244 int ioaddr;
245
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246 info = link->priv;
247 dev = info->dev;
248
Dominik Brodowskidd0fab52009-10-24 15:51:05 +0200249 dev_dbg(&link->dev, "config...\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700250
Dominik Brodowskidd0fab52009-10-24 15:51:05 +0200251 dev_dbg(&link->dev, "com20020_config\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252
Dominik Brodowskidd0fab52009-10-24 15:51:05 +0200253 dev_dbg(&link->dev, "baseport1 is %Xh\n", link->io.BasePort1);
Dominik Brodowski4c89e882008-08-03 10:07:45 +0200254 i = -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255 if (!link->io.BasePort1)
256 {
257 for (ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x10)
258 {
259 link->io.BasePort1 = ioaddr;
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200260 i = pcmcia_request_io(link, &link->io);
Dominik Brodowski4c89e882008-08-03 10:07:45 +0200261 if (i == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262 break;
263 }
264 }
265 else
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200266 i = pcmcia_request_io(link, &link->io);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267
Dominik Brodowski4c89e882008-08-03 10:07:45 +0200268 if (i != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269 {
Dominik Brodowskidd0fab52009-10-24 15:51:05 +0200270 dev_dbg(&link->dev, "requestIO failed totally!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700271 goto failed;
272 }
273
274 ioaddr = dev->base_addr = link->io.BasePort1;
Dominik Brodowskidd0fab52009-10-24 15:51:05 +0200275 dev_dbg(&link->dev, "got ioaddr %Xh\n", ioaddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276
Dominik Brodowski5fa91672009-11-08 17:24:46 +0100277 dev_dbg(&link->dev, "request IRQ %d\n",
278 link->irq.AssignedIRQ);
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200279 i = pcmcia_request_irq(link, &link->irq);
Dominik Brodowski4c89e882008-08-03 10:07:45 +0200280 if (i != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281 {
Dominik Brodowskidd0fab52009-10-24 15:51:05 +0200282 dev_dbg(&link->dev, "requestIRQ failed totally!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283 goto failed;
284 }
285
286 dev->irq = link->irq.AssignedIRQ;
287
Dominik Brodowskidd0fab52009-10-24 15:51:05 +0200288 ret = pcmcia_request_configuration(link, &link->conf);
289 if (ret)
290 goto failed;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291
292 if (com20020_check(dev))
293 {
294 regdump(dev);
295 goto failed;
296 }
297
Wang Chen4cf16532008-11-12 23:38:14 -0800298 lp = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700299 lp->card_name = "PCMCIA COM20020";
300 lp->card_flags = ARC_CAN_10MBIT; /* pretend all of them can 10Mbit */
301
Dominik Brodowskifd238232006-03-05 10:45:09 +0100302 link->dev_node = &info->node;
Dominik Brodowskidd2e5a12009-11-03 10:27:34 +0100303 SET_NETDEV_DEV(dev, &link->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304
305 i = com20020_found(dev, 0); /* calls register_netdev */
306
307 if (i != 0) {
Dominik Brodowskidd0fab52009-10-24 15:51:05 +0200308 dev_printk(KERN_NOTICE, &link->dev,
309 "com20020_cs: com20020_found() failed\n");
Dominik Brodowskifd238232006-03-05 10:45:09 +0100310 link->dev_node = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311 goto failed;
312 }
313
314 strcpy(info->node.dev_name, dev->name);
315
Dominik Brodowskidd0fab52009-10-24 15:51:05 +0200316 dev_dbg(&link->dev,KERN_INFO "%s: port %#3lx, irq %d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317 dev->name, dev->base_addr, dev->irq);
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200318 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319
Linus Torvalds1da177e2005-04-16 15:20:36 -0700320failed:
Dominik Brodowskidd0fab52009-10-24 15:51:05 +0200321 dev_dbg(&link->dev, "com20020_config failed...\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322 com20020_release(link);
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200323 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700324} /* com20020_config */
325
326/*======================================================================
327
328 After a card is removed, com20020_release() will unregister the net
329 device, and release the PCMCIA configuration. If the device is
330 still open, this will be postponed until it is closed.
331
332======================================================================*/
333
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200334static void com20020_release(struct pcmcia_device *link)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335{
Dominik Brodowskidd0fab52009-10-24 15:51:05 +0200336 dev_dbg(&link->dev, "com20020_release\n");
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200337 pcmcia_disable_device(link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338}
339
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200340static int com20020_suspend(struct pcmcia_device *link)
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100341{
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100342 com20020_dev_t *info = link->priv;
343 struct net_device *dev = info->dev;
344
Dominik Brodowskie2d40962006-03-02 00:09:29 +0100345 if (link->open)
Dominik Brodowski8661bb52006-03-02 00:02:33 +0100346 netif_device_detach(dev);
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100347
348 return 0;
349}
350
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200351static int com20020_resume(struct pcmcia_device *link)
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100352{
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100353 com20020_dev_t *info = link->priv;
354 struct net_device *dev = info->dev;
355
Dominik Brodowskie2d40962006-03-02 00:09:29 +0100356 if (link->open) {
Dominik Brodowski8661bb52006-03-02 00:02:33 +0100357 int ioaddr = dev->base_addr;
Wang Chen4cf16532008-11-12 23:38:14 -0800358 struct arcnet_local *lp = netdev_priv(dev);
Dominik Brodowski8661bb52006-03-02 00:02:33 +0100359 ARCRESET;
360 }
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100361
362 return 0;
363}
364
Dominik Brodowski7fb22bb2005-06-27 16:28:38 -0700365static struct pcmcia_device_id com20020_ids[] = {
Marc Sowen6bb1c392006-06-25 01:56:24 -0700366 PCMCIA_DEVICE_PROD_ID12("Contemporary Control Systems, Inc.",
367 "PCM20 Arcnet Adapter", 0x59991666, 0x95dfffaf),
368 PCMCIA_DEVICE_PROD_ID12("SoHard AG",
369 "SH ARC PCMCIA", 0xf8991729, 0x69dff0c7),
Dominik Brodowski7fb22bb2005-06-27 16:28:38 -0700370 PCMCIA_DEVICE_NULL
371};
372MODULE_DEVICE_TABLE(pcmcia, com20020_ids);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373
374static struct pcmcia_driver com20020_cs_driver = {
375 .owner = THIS_MODULE,
376 .drv = {
377 .name = "com20020_cs",
378 },
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200379 .probe = com20020_probe,
Dominik Brodowskicc3b4862005-11-14 21:23:14 +0100380 .remove = com20020_detach,
Dominik Brodowski7fb22bb2005-06-27 16:28:38 -0700381 .id_table = com20020_ids,
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100382 .suspend = com20020_suspend,
383 .resume = com20020_resume,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384};
385
386static int __init init_com20020_cs(void)
387{
388 return pcmcia_register_driver(&com20020_cs_driver);
389}
390
391static void __exit exit_com20020_cs(void)
392{
393 pcmcia_unregister_driver(&com20020_cs_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394}
395
396module_init(init_com20020_cs);
397module_exit(exit_com20020_cs);