blob: 278438beb915f0f87dc693068c4cb4024c3a4510 [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;
Dominik Brodowskifd238232006-03-05 10:45:09 +0100166 p_dev->conf.Attributes = CONF_ENABLE_IRQ;
167 p_dev->conf.IntType = INT_MEMORY_AND_IO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168
Dominik Brodowski5fa91672009-11-08 17:24:46 +0100169 info->dev = dev;
Dominik Brodowskifd238232006-03-05 10:45:09 +0100170 p_dev->priv = info;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200172 return com20020_config(p_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173
174fail_alloc_dev:
175 kfree(info);
176fail_alloc_info:
Dominik Brodowskif8cfa612005-11-14 21:25:51 +0100177 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178} /* com20020_attach */
179
180/*======================================================================
181
182 This deletes a driver "instance". The device is de-registered
183 with Card Services. If it has been released, all local data
184 structures are freed. Otherwise, the structures will be freed
185 when the device is released.
186
187======================================================================*/
188
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200189static void com20020_detach(struct pcmcia_device *link)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190{
191 struct com20020_dev_t *info = link->priv;
Dominik Brodowskib4635812005-11-14 21:25:35 +0100192 struct net_device *dev = info->dev;
193
Dominik Brodowskidd0fab52009-10-24 15:51:05 +0200194 dev_dbg(&link->dev, "detach...\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195
Dominik Brodowskidd0fab52009-10-24 15:51:05 +0200196 dev_dbg(&link->dev, "com20020_detach\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197
Dominik Brodowskifd238232006-03-05 10:45:09 +0100198 if (link->dev_node) {
Dominik Brodowskidd0fab52009-10-24 15:51:05 +0200199 dev_dbg(&link->dev, "unregister...\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200
201 unregister_netdev(dev);
Dominik Brodowskib4635812005-11-14 21:25:35 +0100202
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203 /*
204 * this is necessary because we register our IRQ separately
205 * from card services.
206 */
207 if (dev->irq)
208 free_irq(dev->irq, dev);
209 }
210
Dominik Brodowskie2d40962006-03-02 00:09:29 +0100211 com20020_release(link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700212
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213 /* Unlink device structure, free bits */
Dominik Brodowskidd0fab52009-10-24 15:51:05 +0200214 dev_dbg(&link->dev, "unlinking...\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215 if (link->priv)
216 {
217 dev = info->dev;
218 if (dev)
219 {
Dominik Brodowskidd0fab52009-10-24 15:51:05 +0200220 dev_dbg(&link->dev, "kfree...\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700221 free_netdev(dev);
222 }
Dominik Brodowskidd0fab52009-10-24 15:51:05 +0200223 dev_dbg(&link->dev, "kfree2...\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224 kfree(info);
225 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226
227} /* com20020_detach */
228
229/*======================================================================
230
231 com20020_config() is scheduled to run after a CARD_INSERTION event
232 is received, to configure the PCMCIA socket, and to make the
233 device available to the system.
234
235======================================================================*/
236
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200237static int com20020_config(struct pcmcia_device *link)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238{
239 struct arcnet_local *lp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240 com20020_dev_t *info;
241 struct net_device *dev;
Dominik Brodowskidd0fab52009-10-24 15:51:05 +0200242 int i, ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243 int ioaddr;
244
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245 info = link->priv;
246 dev = info->dev;
247
Dominik Brodowskidd0fab52009-10-24 15:51:05 +0200248 dev_dbg(&link->dev, "config...\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249
Dominik Brodowskidd0fab52009-10-24 15:51:05 +0200250 dev_dbg(&link->dev, "com20020_config\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251
Dominik Brodowskidd0fab52009-10-24 15:51:05 +0200252 dev_dbg(&link->dev, "baseport1 is %Xh\n", link->io.BasePort1);
Dominik Brodowski4c89e882008-08-03 10:07:45 +0200253 i = -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254 if (!link->io.BasePort1)
255 {
256 for (ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x10)
257 {
258 link->io.BasePort1 = ioaddr;
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200259 i = pcmcia_request_io(link, &link->io);
Dominik Brodowski4c89e882008-08-03 10:07:45 +0200260 if (i == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261 break;
262 }
263 }
264 else
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200265 i = pcmcia_request_io(link, &link->io);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700266
Dominik Brodowski4c89e882008-08-03 10:07:45 +0200267 if (i != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268 {
Dominik Brodowskidd0fab52009-10-24 15:51:05 +0200269 dev_dbg(&link->dev, "requestIO failed totally!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700270 goto failed;
271 }
272
273 ioaddr = dev->base_addr = link->io.BasePort1;
Dominik Brodowskidd0fab52009-10-24 15:51:05 +0200274 dev_dbg(&link->dev, "got ioaddr %Xh\n", ioaddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275
Dominik Brodowski5fa91672009-11-08 17:24:46 +0100276 dev_dbg(&link->dev, "request IRQ %d\n",
Dominik Brodowskieb141202010-03-07 12:21:16 +0100277 link->irq);
278 if (!link->irq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279 {
Dominik Brodowskidd0fab52009-10-24 15:51:05 +0200280 dev_dbg(&link->dev, "requestIRQ failed totally!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281 goto failed;
282 }
283
Dominik Brodowskieb141202010-03-07 12:21:16 +0100284 dev->irq = link->irq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285
Dominik Brodowskidd0fab52009-10-24 15:51:05 +0200286 ret = pcmcia_request_configuration(link, &link->conf);
287 if (ret)
288 goto failed;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289
290 if (com20020_check(dev))
291 {
292 regdump(dev);
293 goto failed;
294 }
295
Wang Chen4cf16532008-11-12 23:38:14 -0800296 lp = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700297 lp->card_name = "PCMCIA COM20020";
298 lp->card_flags = ARC_CAN_10MBIT; /* pretend all of them can 10Mbit */
299
Dominik Brodowskifd238232006-03-05 10:45:09 +0100300 link->dev_node = &info->node;
Dominik Brodowskidd2e5a12009-11-03 10:27:34 +0100301 SET_NETDEV_DEV(dev, &link->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302
303 i = com20020_found(dev, 0); /* calls register_netdev */
304
305 if (i != 0) {
Dominik Brodowskidd0fab52009-10-24 15:51:05 +0200306 dev_printk(KERN_NOTICE, &link->dev,
307 "com20020_cs: com20020_found() failed\n");
Dominik Brodowskifd238232006-03-05 10:45:09 +0100308 link->dev_node = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309 goto failed;
310 }
311
312 strcpy(info->node.dev_name, dev->name);
313
Dominik Brodowskidd0fab52009-10-24 15:51:05 +0200314 dev_dbg(&link->dev,KERN_INFO "%s: port %#3lx, irq %d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315 dev->name, dev->base_addr, dev->irq);
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200316 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318failed:
Dominik Brodowskidd0fab52009-10-24 15:51:05 +0200319 dev_dbg(&link->dev, "com20020_config failed...\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700320 com20020_release(link);
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200321 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322} /* com20020_config */
323
324/*======================================================================
325
326 After a card is removed, com20020_release() will unregister the net
327 device, and release the PCMCIA configuration. If the device is
328 still open, this will be postponed until it is closed.
329
330======================================================================*/
331
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200332static void com20020_release(struct pcmcia_device *link)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333{
Dominik Brodowskidd0fab52009-10-24 15:51:05 +0200334 dev_dbg(&link->dev, "com20020_release\n");
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200335 pcmcia_disable_device(link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336}
337
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200338static int com20020_suspend(struct pcmcia_device *link)
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100339{
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100340 com20020_dev_t *info = link->priv;
341 struct net_device *dev = info->dev;
342
Dominik Brodowskie2d40962006-03-02 00:09:29 +0100343 if (link->open)
Dominik Brodowski8661bb52006-03-02 00:02:33 +0100344 netif_device_detach(dev);
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100345
346 return 0;
347}
348
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200349static int com20020_resume(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 int ioaddr = dev->base_addr;
Wang Chen4cf16532008-11-12 23:38:14 -0800356 struct arcnet_local *lp = netdev_priv(dev);
Dominik Brodowski8661bb52006-03-02 00:02:33 +0100357 ARCRESET;
358 }
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100359
360 return 0;
361}
362
Dominik Brodowski7fb22bb2005-06-27 16:28:38 -0700363static struct pcmcia_device_id com20020_ids[] = {
Marc Sowen6bb1c392006-06-25 01:56:24 -0700364 PCMCIA_DEVICE_PROD_ID12("Contemporary Control Systems, Inc.",
365 "PCM20 Arcnet Adapter", 0x59991666, 0x95dfffaf),
366 PCMCIA_DEVICE_PROD_ID12("SoHard AG",
367 "SH ARC PCMCIA", 0xf8991729, 0x69dff0c7),
Dominik Brodowski7fb22bb2005-06-27 16:28:38 -0700368 PCMCIA_DEVICE_NULL
369};
370MODULE_DEVICE_TABLE(pcmcia, com20020_ids);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371
372static struct pcmcia_driver com20020_cs_driver = {
373 .owner = THIS_MODULE,
374 .drv = {
375 .name = "com20020_cs",
376 },
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200377 .probe = com20020_probe,
Dominik Brodowskicc3b4862005-11-14 21:23:14 +0100378 .remove = com20020_detach,
Dominik Brodowski7fb22bb2005-06-27 16:28:38 -0700379 .id_table = com20020_ids,
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100380 .suspend = com20020_suspend,
381 .resume = com20020_resume,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382};
383
384static int __init init_com20020_cs(void)
385{
386 return pcmcia_register_driver(&com20020_cs_driver);
387}
388
389static void __exit exit_com20020_cs(void)
390{
391 pcmcia_unregister_driver(&com20020_cs_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700392}
393
394module_init(init_com20020_cs);
395module_exit(exit_com20020_cs);