blob: 0d1c7a41c9c63787ccdfecc9ee2e83abb3a1a39b [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 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150 info = kmalloc(sizeof(struct com20020_dev_t), GFP_KERNEL);
151 if (!info)
152 goto fail_alloc_info;
153
154 dev = alloc_arcdev("");
155 if (!dev)
156 goto fail_alloc_dev;
157
158 memset(info, 0, sizeof(struct com20020_dev_t));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159 lp = dev->priv;
160 lp->timeout = timeout;
161 lp->backplane = backplane;
162 lp->clockp = clockp;
163 lp->clockm = clockm & 3;
164 lp->hw.owner = THIS_MODULE;
165
166 /* fill in our module parameters as defaults */
167 dev->dev_addr[0] = node;
168
Dominik Brodowskifd238232006-03-05 10:45:09 +0100169 p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
170 p_dev->io.NumPorts1 = 16;
171 p_dev->io.IOAddrLines = 16;
172 p_dev->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
173 p_dev->irq.IRQInfo1 = IRQ_LEVEL_ID;
174 p_dev->conf.Attributes = CONF_ENABLE_IRQ;
175 p_dev->conf.IntType = INT_MEMORY_AND_IO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176
Dominik Brodowskifd238232006-03-05 10:45:09 +0100177 p_dev->irq.Instance = info->dev = dev;
178 p_dev->priv = info;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200180 return com20020_config(p_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181
182fail_alloc_dev:
183 kfree(info);
184fail_alloc_info:
Dominik Brodowskif8cfa612005-11-14 21:25:51 +0100185 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186} /* com20020_attach */
187
188/*======================================================================
189
190 This deletes a driver "instance". The device is de-registered
191 with Card Services. If it has been released, all local data
192 structures are freed. Otherwise, the structures will be freed
193 when the device is released.
194
195======================================================================*/
196
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200197static void com20020_detach(struct pcmcia_device *link)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198{
199 struct com20020_dev_t *info = link->priv;
Dominik Brodowskib4635812005-11-14 21:25:35 +0100200 struct net_device *dev = info->dev;
201
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202 DEBUG(1,"detach...\n");
203
204 DEBUG(0, "com20020_detach(0x%p)\n", link);
205
Dominik Brodowskifd238232006-03-05 10:45:09 +0100206 if (link->dev_node) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207 DEBUG(1,"unregister...\n");
208
209 unregister_netdev(dev);
Dominik Brodowskib4635812005-11-14 21:25:35 +0100210
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211 /*
212 * this is necessary because we register our IRQ separately
213 * from card services.
214 */
215 if (dev->irq)
216 free_irq(dev->irq, dev);
217 }
218
Dominik Brodowskie2d40962006-03-02 00:09:29 +0100219 com20020_release(link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220
Linus Torvalds1da177e2005-04-16 15:20:36 -0700221 /* Unlink device structure, free bits */
222 DEBUG(1,"unlinking...\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223 if (link->priv)
224 {
225 dev = info->dev;
226 if (dev)
227 {
228 DEBUG(1,"kfree...\n");
229 free_netdev(dev);
230 }
231 DEBUG(1,"kfree2...\n");
232 kfree(info);
233 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234
235} /* com20020_detach */
236
237/*======================================================================
238
239 com20020_config() is scheduled to run after a CARD_INSERTION event
240 is received, to configure the PCMCIA socket, and to make the
241 device available to the system.
242
243======================================================================*/
244
245#define CS_CHECK(fn, ret) \
246do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
247
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200248static int com20020_config(struct pcmcia_device *link)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249{
250 struct arcnet_local *lp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251 com20020_dev_t *info;
252 struct net_device *dev;
253 int i, last_ret, last_fn;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254 int ioaddr;
255
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256 info = link->priv;
257 dev = info->dev;
258
259 DEBUG(1,"config...\n");
260
261 DEBUG(0, "com20020_config(0x%p)\n", link);
262
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263 DEBUG(1,"arcnet: baseport1 is %Xh\n", link->io.BasePort1);
264 i = !CS_SUCCESS;
265 if (!link->io.BasePort1)
266 {
267 for (ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x10)
268 {
269 link->io.BasePort1 = ioaddr;
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200270 i = pcmcia_request_io(link, &link->io);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700271 if (i == CS_SUCCESS)
272 break;
273 }
274 }
275 else
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200276 i = pcmcia_request_io(link, &link->io);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277
278 if (i != CS_SUCCESS)
279 {
280 DEBUG(1,"arcnet: requestIO failed totally!\n");
281 goto failed;
282 }
283
284 ioaddr = dev->base_addr = link->io.BasePort1;
285 DEBUG(1,"arcnet: got ioaddr %Xh\n", ioaddr);
286
287 DEBUG(1,"arcnet: request IRQ %d (%Xh/%Xh)\n",
288 link->irq.AssignedIRQ,
289 link->irq.IRQInfo1, link->irq.IRQInfo2);
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200290 i = pcmcia_request_irq(link, &link->irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 if (i != CS_SUCCESS)
292 {
293 DEBUG(1,"arcnet: requestIRQ failed totally!\n");
294 goto failed;
295 }
296
297 dev->irq = link->irq.AssignedIRQ;
298
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200299 CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700300
301 if (com20020_check(dev))
302 {
303 regdump(dev);
304 goto failed;
305 }
306
307 lp = dev->priv;
308 lp->card_name = "PCMCIA COM20020";
309 lp->card_flags = ARC_CAN_10MBIT; /* pretend all of them can 10Mbit */
310
Dominik Brodowskifd238232006-03-05 10:45:09 +0100311 link->dev_node = &info->node;
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200312 SET_NETDEV_DEV(dev, &handle_to_dev(link));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313
314 i = com20020_found(dev, 0); /* calls register_netdev */
315
316 if (i != 0) {
317 DEBUG(1,KERN_NOTICE "com20020_cs: com20020_found() failed\n");
Dominik Brodowskifd238232006-03-05 10:45:09 +0100318 link->dev_node = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319 goto failed;
320 }
321
322 strcpy(info->node.dev_name, dev->name);
323
324 DEBUG(1,KERN_INFO "%s: port %#3lx, irq %d\n",
325 dev->name, dev->base_addr, dev->irq);
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200326 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327
328cs_failed:
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200329 cs_error(link, last_fn, last_ret);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330failed:
331 DEBUG(1,"com20020_config failed...\n");
332 com20020_release(link);
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200333 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700334} /* com20020_config */
335
336/*======================================================================
337
338 After a card is removed, com20020_release() will unregister the net
339 device, and release the PCMCIA configuration. If the device is
340 still open, this will be postponed until it is closed.
341
342======================================================================*/
343
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200344static void com20020_release(struct pcmcia_device *link)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345{
Dominik Brodowski5f2a71f2006-01-15 09:32:39 +0100346 DEBUG(0, "com20020_release(0x%p)\n", link);
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200347 pcmcia_disable_device(link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348}
349
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200350static int com20020_suspend(struct pcmcia_device *link)
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100351{
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100352 com20020_dev_t *info = link->priv;
353 struct net_device *dev = info->dev;
354
Dominik Brodowskie2d40962006-03-02 00:09:29 +0100355 if (link->open)
Dominik Brodowski8661bb52006-03-02 00:02:33 +0100356 netif_device_detach(dev);
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100357
358 return 0;
359}
360
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200361static int com20020_resume(struct pcmcia_device *link)
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100362{
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100363 com20020_dev_t *info = link->priv;
364 struct net_device *dev = info->dev;
365
Dominik Brodowskie2d40962006-03-02 00:09:29 +0100366 if (link->open) {
Dominik Brodowski8661bb52006-03-02 00:02:33 +0100367 int ioaddr = dev->base_addr;
368 struct arcnet_local *lp = dev->priv;
369 ARCRESET;
370 }
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100371
372 return 0;
373}
374
Dominik Brodowski7fb22bb2005-06-27 16:28:38 -0700375static struct pcmcia_device_id com20020_ids[] = {
Marc Sowen6bb1c392006-06-25 01:56:24 -0700376 PCMCIA_DEVICE_PROD_ID12("Contemporary Control Systems, Inc.",
377 "PCM20 Arcnet Adapter", 0x59991666, 0x95dfffaf),
378 PCMCIA_DEVICE_PROD_ID12("SoHard AG",
379 "SH ARC PCMCIA", 0xf8991729, 0x69dff0c7),
Dominik Brodowski7fb22bb2005-06-27 16:28:38 -0700380 PCMCIA_DEVICE_NULL
381};
382MODULE_DEVICE_TABLE(pcmcia, com20020_ids);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700383
384static struct pcmcia_driver com20020_cs_driver = {
385 .owner = THIS_MODULE,
386 .drv = {
387 .name = "com20020_cs",
388 },
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200389 .probe = com20020_probe,
Dominik Brodowskicc3b4862005-11-14 21:23:14 +0100390 .remove = com20020_detach,
Dominik Brodowski7fb22bb2005-06-27 16:28:38 -0700391 .id_table = com20020_ids,
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100392 .suspend = com20020_suspend,
393 .resume = com20020_resume,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394};
395
396static int __init init_com20020_cs(void)
397{
398 return pcmcia_register_driver(&com20020_cs_driver);
399}
400
401static void __exit exit_com20020_cs(void)
402{
403 pcmcia_unregister_driver(&com20020_cs_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404}
405
406module_init(init_com20020_cs);
407module_exit(exit_com20020_cs);