blob: a9bcfb4ba15e6cb669502ea6ec367f60e6d88e75 [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 Brodowskifba395e2006-03-31 17:21:06 +0200121static void com20020_config(struct pcmcia_device *link);
122static 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 Brodowskif8cfa612005-11-14 21:25:51 +0100141static int com20020_attach(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;
176 p_dev->conf.Present = PRESENT_OPTION;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177
Dominik Brodowskifd238232006-03-05 10:45:09 +0100178 p_dev->irq.Instance = info->dev = dev;
179 p_dev->priv = info;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180
Dominik Brodowskifd238232006-03-05 10:45:09 +0100181 p_dev->state |= DEV_PRESENT;
182 com20020_config(p_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183
Dominik Brodowskif8cfa612005-11-14 21:25:51 +0100184 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185
186fail_alloc_dev:
187 kfree(info);
188fail_alloc_info:
Dominik Brodowskif8cfa612005-11-14 21:25:51 +0100189 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190} /* com20020_attach */
191
192/*======================================================================
193
194 This deletes a driver "instance". The device is de-registered
195 with Card Services. If it has been released, all local data
196 structures are freed. Otherwise, the structures will be freed
197 when the device is released.
198
199======================================================================*/
200
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200201static void com20020_detach(struct pcmcia_device *link)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202{
203 struct com20020_dev_t *info = link->priv;
Dominik Brodowskib4635812005-11-14 21:25:35 +0100204 struct net_device *dev = info->dev;
205
Linus Torvalds1da177e2005-04-16 15:20:36 -0700206 DEBUG(1,"detach...\n");
207
208 DEBUG(0, "com20020_detach(0x%p)\n", link);
209
Dominik Brodowskifd238232006-03-05 10:45:09 +0100210 if (link->dev_node) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211 DEBUG(1,"unregister...\n");
212
213 unregister_netdev(dev);
Dominik Brodowskib4635812005-11-14 21:25:35 +0100214
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215 /*
216 * this is necessary because we register our IRQ separately
217 * from card services.
218 */
219 if (dev->irq)
220 free_irq(dev->irq, dev);
221 }
222
223 if (link->state & DEV_CONFIG)
224 com20020_release(link);
225
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226 /* Unlink device structure, free bits */
227 DEBUG(1,"unlinking...\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228 if (link->priv)
229 {
230 dev = info->dev;
231 if (dev)
232 {
233 DEBUG(1,"kfree...\n");
234 free_netdev(dev);
235 }
236 DEBUG(1,"kfree2...\n");
237 kfree(info);
238 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239
240} /* com20020_detach */
241
242/*======================================================================
243
244 com20020_config() is scheduled to run after a CARD_INSERTION event
245 is received, to configure the PCMCIA socket, and to make the
246 device available to the system.
247
248======================================================================*/
249
250#define CS_CHECK(fn, ret) \
251do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
252
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200253static void com20020_config(struct pcmcia_device *link)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254{
255 struct arcnet_local *lp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256 tuple_t tuple;
257 cisparse_t parse;
258 com20020_dev_t *info;
259 struct net_device *dev;
260 int i, last_ret, last_fn;
261 u_char buf[64];
262 int ioaddr;
263
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264 info = link->priv;
265 dev = info->dev;
266
267 DEBUG(1,"config...\n");
268
269 DEBUG(0, "com20020_config(0x%p)\n", link);
270
271 tuple.Attributes = 0;
272 tuple.TupleData = buf;
273 tuple.TupleDataMax = 64;
274 tuple.TupleOffset = 0;
275 tuple.DesiredTuple = CISTPL_CONFIG;
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200276 CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
277 CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple));
278 CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279 link->conf.ConfigBase = parse.config.base;
280
281 /* Configure card */
282 link->state |= DEV_CONFIG;
283
284 DEBUG(1,"arcnet: baseport1 is %Xh\n", link->io.BasePort1);
285 i = !CS_SUCCESS;
286 if (!link->io.BasePort1)
287 {
288 for (ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x10)
289 {
290 link->io.BasePort1 = ioaddr;
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200291 i = pcmcia_request_io(link, &link->io);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292 if (i == CS_SUCCESS)
293 break;
294 }
295 }
296 else
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200297 i = pcmcia_request_io(link, &link->io);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298
299 if (i != CS_SUCCESS)
300 {
301 DEBUG(1,"arcnet: requestIO failed totally!\n");
302 goto failed;
303 }
304
305 ioaddr = dev->base_addr = link->io.BasePort1;
306 DEBUG(1,"arcnet: got ioaddr %Xh\n", ioaddr);
307
308 DEBUG(1,"arcnet: request IRQ %d (%Xh/%Xh)\n",
309 link->irq.AssignedIRQ,
310 link->irq.IRQInfo1, link->irq.IRQInfo2);
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200311 i = pcmcia_request_irq(link, &link->irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312 if (i != CS_SUCCESS)
313 {
314 DEBUG(1,"arcnet: requestIRQ failed totally!\n");
315 goto failed;
316 }
317
318 dev->irq = link->irq.AssignedIRQ;
319
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200320 CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700321
322 if (com20020_check(dev))
323 {
324 regdump(dev);
325 goto failed;
326 }
327
328 lp = dev->priv;
329 lp->card_name = "PCMCIA COM20020";
330 lp->card_flags = ARC_CAN_10MBIT; /* pretend all of them can 10Mbit */
331
Dominik Brodowskifd238232006-03-05 10:45:09 +0100332 link->dev_node = &info->node;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333 link->state &= ~DEV_CONFIG_PENDING;
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200334 SET_NETDEV_DEV(dev, &handle_to_dev(link));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335
336 i = com20020_found(dev, 0); /* calls register_netdev */
337
338 if (i != 0) {
339 DEBUG(1,KERN_NOTICE "com20020_cs: com20020_found() failed\n");
Dominik Brodowskifd238232006-03-05 10:45:09 +0100340 link->dev_node = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341 goto failed;
342 }
343
344 strcpy(info->node.dev_name, dev->name);
345
346 DEBUG(1,KERN_INFO "%s: port %#3lx, irq %d\n",
347 dev->name, dev->base_addr, dev->irq);
348 return;
349
350cs_failed:
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200351 cs_error(link, last_fn, last_ret);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352failed:
353 DEBUG(1,"com20020_config failed...\n");
354 com20020_release(link);
355} /* com20020_config */
356
357/*======================================================================
358
359 After a card is removed, com20020_release() will unregister the net
360 device, and release the PCMCIA configuration. If the device is
361 still open, this will be postponed until it is closed.
362
363======================================================================*/
364
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200365static void com20020_release(struct pcmcia_device *link)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366{
Dominik Brodowski5f2a71f2006-01-15 09:32:39 +0100367 DEBUG(0, "com20020_release(0x%p)\n", link);
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200368 pcmcia_disable_device(link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700369}
370
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200371static int com20020_suspend(struct pcmcia_device *link)
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100372{
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100373 com20020_dev_t *info = link->priv;
374 struct net_device *dev = info->dev;
375
Dominik Brodowski8661bb52006-03-02 00:02:33 +0100376 if ((link->state & DEV_CONFIG) && (link->open))
377 netif_device_detach(dev);
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100378
379 return 0;
380}
381
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200382static int com20020_resume(struct pcmcia_device *link)
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100383{
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100384 com20020_dev_t *info = link->priv;
385 struct net_device *dev = info->dev;
386
Dominik Brodowski8661bb52006-03-02 00:02:33 +0100387 if ((link->state & DEV_CONFIG) && (link->open)) {
388 int ioaddr = dev->base_addr;
389 struct arcnet_local *lp = dev->priv;
390 ARCRESET;
391 }
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100392
393 return 0;
394}
395
Dominik Brodowski7fb22bb2005-06-27 16:28:38 -0700396static struct pcmcia_device_id com20020_ids[] = {
397 PCMCIA_DEVICE_PROD_ID12("Contemporary Control Systems, Inc.", "PCM20 Arcnet Adapter", 0x59991666, 0x95dfffaf),
398 PCMCIA_DEVICE_NULL
399};
400MODULE_DEVICE_TABLE(pcmcia, com20020_ids);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401
402static struct pcmcia_driver com20020_cs_driver = {
403 .owner = THIS_MODULE,
404 .drv = {
405 .name = "com20020_cs",
406 },
Dominik Brodowskif8cfa612005-11-14 21:25:51 +0100407 .probe = com20020_attach,
Dominik Brodowskicc3b4862005-11-14 21:23:14 +0100408 .remove = com20020_detach,
Dominik Brodowski7fb22bb2005-06-27 16:28:38 -0700409 .id_table = com20020_ids,
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100410 .suspend = com20020_suspend,
411 .resume = com20020_resume,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412};
413
414static int __init init_com20020_cs(void)
415{
416 return pcmcia_register_driver(&com20020_cs_driver);
417}
418
419static void __exit exit_com20020_cs(void)
420{
421 pcmcia_unregister_driver(&com20020_cs_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422}
423
424module_init(init_com20020_cs);
425module_exit(exit_com20020_cs);