blob: 9a2e5006570b9472e1e727cc6476d1576e0c4e52 [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;
167 p_dev->irq.IRQInfo1 = IRQ_LEVEL_ID;
168 p_dev->conf.Attributes = CONF_ENABLE_IRQ;
169 p_dev->conf.IntType = INT_MEMORY_AND_IO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700170
Dominik Brodowskifd238232006-03-05 10:45:09 +0100171 p_dev->irq.Instance = info->dev = dev;
172 p_dev->priv = info;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200174 return com20020_config(p_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175
176fail_alloc_dev:
177 kfree(info);
178fail_alloc_info:
Dominik Brodowskif8cfa612005-11-14 21:25:51 +0100179 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180} /* com20020_attach */
181
182/*======================================================================
183
184 This deletes a driver "instance". The device is de-registered
185 with Card Services. If it has been released, all local data
186 structures are freed. Otherwise, the structures will be freed
187 when the device is released.
188
189======================================================================*/
190
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200191static void com20020_detach(struct pcmcia_device *link)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192{
193 struct com20020_dev_t *info = link->priv;
Dominik Brodowskib4635812005-11-14 21:25:35 +0100194 struct net_device *dev = info->dev;
195
Dominik Brodowskidd0fab52009-10-24 15:51:05 +0200196 dev_dbg(&link->dev, "detach...\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197
Dominik Brodowskidd0fab52009-10-24 15:51:05 +0200198 dev_dbg(&link->dev, "com20020_detach\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199
Dominik Brodowskifd238232006-03-05 10:45:09 +0100200 if (link->dev_node) {
Dominik Brodowskidd0fab52009-10-24 15:51:05 +0200201 dev_dbg(&link->dev, "unregister...\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202
203 unregister_netdev(dev);
Dominik Brodowskib4635812005-11-14 21:25:35 +0100204
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205 /*
206 * this is necessary because we register our IRQ separately
207 * from card services.
208 */
209 if (dev->irq)
210 free_irq(dev->irq, dev);
211 }
212
Dominik Brodowskie2d40962006-03-02 00:09:29 +0100213 com20020_release(link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215 /* Unlink device structure, free bits */
Dominik Brodowskidd0fab52009-10-24 15:51:05 +0200216 dev_dbg(&link->dev, "unlinking...\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217 if (link->priv)
218 {
219 dev = info->dev;
220 if (dev)
221 {
Dominik Brodowskidd0fab52009-10-24 15:51:05 +0200222 dev_dbg(&link->dev, "kfree...\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223 free_netdev(dev);
224 }
Dominik Brodowskidd0fab52009-10-24 15:51:05 +0200225 dev_dbg(&link->dev, "kfree2...\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226 kfree(info);
227 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228
229} /* com20020_detach */
230
231/*======================================================================
232
233 com20020_config() is scheduled to run after a CARD_INSERTION event
234 is received, to configure the PCMCIA socket, and to make the
235 device available to the system.
236
237======================================================================*/
238
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200239static int com20020_config(struct pcmcia_device *link)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240{
241 struct arcnet_local *lp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242 com20020_dev_t *info;
243 struct net_device *dev;
Dominik Brodowskidd0fab52009-10-24 15:51:05 +0200244 int i, ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245 int ioaddr;
246
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247 info = link->priv;
248 dev = info->dev;
249
Dominik Brodowskidd0fab52009-10-24 15:51:05 +0200250 dev_dbg(&link->dev, "config...\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251
Dominik Brodowskidd0fab52009-10-24 15:51:05 +0200252 dev_dbg(&link->dev, "com20020_config\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253
Dominik Brodowskidd0fab52009-10-24 15:51:05 +0200254 dev_dbg(&link->dev, "baseport1 is %Xh\n", link->io.BasePort1);
Dominik Brodowski4c89e882008-08-03 10:07:45 +0200255 i = -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256 if (!link->io.BasePort1)
257 {
258 for (ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x10)
259 {
260 link->io.BasePort1 = ioaddr;
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200261 i = pcmcia_request_io(link, &link->io);
Dominik Brodowski4c89e882008-08-03 10:07:45 +0200262 if (i == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263 break;
264 }
265 }
266 else
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200267 i = pcmcia_request_io(link, &link->io);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268
Dominik Brodowski4c89e882008-08-03 10:07:45 +0200269 if (i != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700270 {
Dominik Brodowskidd0fab52009-10-24 15:51:05 +0200271 dev_dbg(&link->dev, "requestIO failed totally!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272 goto failed;
273 }
274
275 ioaddr = dev->base_addr = link->io.BasePort1;
Dominik Brodowskidd0fab52009-10-24 15:51:05 +0200276 dev_dbg(&link->dev, "got ioaddr %Xh\n", ioaddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277
Dominik Brodowskidd0fab52009-10-24 15:51:05 +0200278 dev_dbg(&link->dev, "request IRQ %d (%Xh/%Xh)\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279 link->irq.AssignedIRQ,
280 link->irq.IRQInfo1, link->irq.IRQInfo2);
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200281 i = pcmcia_request_irq(link, &link->irq);
Dominik Brodowski4c89e882008-08-03 10:07:45 +0200282 if (i != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283 {
Dominik Brodowskidd0fab52009-10-24 15:51:05 +0200284 dev_dbg(&link->dev, "requestIRQ failed totally!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285 goto failed;
286 }
287
288 dev->irq = link->irq.AssignedIRQ;
289
Dominik Brodowskidd0fab52009-10-24 15:51:05 +0200290 ret = pcmcia_request_configuration(link, &link->conf);
291 if (ret)
292 goto failed;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293
294 if (com20020_check(dev))
295 {
296 regdump(dev);
297 goto failed;
298 }
299
Wang Chen4cf16532008-11-12 23:38:14 -0800300 lp = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301 lp->card_name = "PCMCIA COM20020";
302 lp->card_flags = ARC_CAN_10MBIT; /* pretend all of them can 10Mbit */
303
Dominik Brodowskifd238232006-03-05 10:45:09 +0100304 link->dev_node = &info->node;
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200305 SET_NETDEV_DEV(dev, &handle_to_dev(link));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306
307 i = com20020_found(dev, 0); /* calls register_netdev */
308
309 if (i != 0) {
Dominik Brodowskidd0fab52009-10-24 15:51:05 +0200310 dev_printk(KERN_NOTICE, &link->dev,
311 "com20020_cs: com20020_found() failed\n");
Dominik Brodowskifd238232006-03-05 10:45:09 +0100312 link->dev_node = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313 goto failed;
314 }
315
316 strcpy(info->node.dev_name, dev->name);
317
Dominik Brodowskidd0fab52009-10-24 15:51:05 +0200318 dev_dbg(&link->dev,KERN_INFO "%s: port %#3lx, irq %d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319 dev->name, dev->base_addr, dev->irq);
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200320 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700321
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322failed:
Dominik Brodowskidd0fab52009-10-24 15:51:05 +0200323 dev_dbg(&link->dev, "com20020_config failed...\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700324 com20020_release(link);
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200325 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326} /* com20020_config */
327
328/*======================================================================
329
330 After a card is removed, com20020_release() will unregister the net
331 device, and release the PCMCIA configuration. If the device is
332 still open, this will be postponed until it is closed.
333
334======================================================================*/
335
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200336static void com20020_release(struct pcmcia_device *link)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337{
Dominik Brodowskidd0fab52009-10-24 15:51:05 +0200338 dev_dbg(&link->dev, "com20020_release\n");
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200339 pcmcia_disable_device(link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340}
341
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200342static int com20020_suspend(struct pcmcia_device *link)
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100343{
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100344 com20020_dev_t *info = link->priv;
345 struct net_device *dev = info->dev;
346
Dominik Brodowskie2d40962006-03-02 00:09:29 +0100347 if (link->open)
Dominik Brodowski8661bb52006-03-02 00:02:33 +0100348 netif_device_detach(dev);
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100349
350 return 0;
351}
352
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200353static int com20020_resume(struct pcmcia_device *link)
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100354{
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100355 com20020_dev_t *info = link->priv;
356 struct net_device *dev = info->dev;
357
Dominik Brodowskie2d40962006-03-02 00:09:29 +0100358 if (link->open) {
Dominik Brodowski8661bb52006-03-02 00:02:33 +0100359 int ioaddr = dev->base_addr;
Wang Chen4cf16532008-11-12 23:38:14 -0800360 struct arcnet_local *lp = netdev_priv(dev);
Dominik Brodowski8661bb52006-03-02 00:02:33 +0100361 ARCRESET;
362 }
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100363
364 return 0;
365}
366
Dominik Brodowski7fb22bb2005-06-27 16:28:38 -0700367static struct pcmcia_device_id com20020_ids[] = {
Marc Sowen6bb1c392006-06-25 01:56:24 -0700368 PCMCIA_DEVICE_PROD_ID12("Contemporary Control Systems, Inc.",
369 "PCM20 Arcnet Adapter", 0x59991666, 0x95dfffaf),
370 PCMCIA_DEVICE_PROD_ID12("SoHard AG",
371 "SH ARC PCMCIA", 0xf8991729, 0x69dff0c7),
Dominik Brodowski7fb22bb2005-06-27 16:28:38 -0700372 PCMCIA_DEVICE_NULL
373};
374MODULE_DEVICE_TABLE(pcmcia, com20020_ids);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700375
376static struct pcmcia_driver com20020_cs_driver = {
377 .owner = THIS_MODULE,
378 .drv = {
379 .name = "com20020_cs",
380 },
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200381 .probe = com20020_probe,
Dominik Brodowskicc3b4862005-11-14 21:23:14 +0100382 .remove = com20020_detach,
Dominik Brodowski7fb22bb2005-06-27 16:28:38 -0700383 .id_table = com20020_ids,
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100384 .suspend = com20020_suspend,
385 .resume = com20020_resume,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386};
387
388static int __init init_com20020_cs(void)
389{
390 return pcmcia_register_driver(&com20020_cs_driver);
391}
392
393static void __exit exit_com20020_cs(void)
394{
395 pcmcia_unregister_driver(&com20020_cs_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700396}
397
398module_init(init_com20020_cs);
399module_exit(exit_com20020_cs);