blob: 68612222de6e1ddd7f3ab7b3927ad3675a981167 [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
121static void com20020_config(dev_link_t *link);
122static void com20020_release(dev_link_t *link);
123static int com20020_event(event_t event, int priority,
124 event_callback_args_t *args);
125
126static dev_info_t dev_info = "com20020_cs";
127
128static dev_link_t *com20020_attach(void);
129static void com20020_detach(dev_link_t *);
130
131static dev_link_t *dev_list;
132
133/*====================================================================*/
134
135typedef struct com20020_dev_t {
136 struct net_device *dev;
137 dev_node_t node;
138} com20020_dev_t;
139
140/*======================================================================
141
142 com20020_attach() creates an "instance" of the driver, allocating
143 local data structures for one device. The device is registered
144 with Card Services.
145
146======================================================================*/
147
148static dev_link_t *com20020_attach(void)
149{
150 client_reg_t client_reg;
151 dev_link_t *link;
152 com20020_dev_t *info;
153 struct net_device *dev;
154 int ret;
155 struct arcnet_local *lp;
156
157 DEBUG(0, "com20020_attach()\n");
158
159 /* Create new network device */
160 link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL);
161 if (!link)
162 return NULL;
163
164 info = kmalloc(sizeof(struct com20020_dev_t), GFP_KERNEL);
165 if (!info)
166 goto fail_alloc_info;
167
168 dev = alloc_arcdev("");
169 if (!dev)
170 goto fail_alloc_dev;
171
172 memset(info, 0, sizeof(struct com20020_dev_t));
173 memset(link, 0, sizeof(struct dev_link_t));
174 lp = dev->priv;
175 lp->timeout = timeout;
176 lp->backplane = backplane;
177 lp->clockp = clockp;
178 lp->clockm = clockm & 3;
179 lp->hw.owner = THIS_MODULE;
180
181 /* fill in our module parameters as defaults */
182 dev->dev_addr[0] = node;
183
184 link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
185 link->io.NumPorts1 = 16;
186 link->io.IOAddrLines = 16;
187 link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
188 link->irq.IRQInfo1 = IRQ_LEVEL_ID;
189 link->conf.Attributes = CONF_ENABLE_IRQ;
190 link->conf.Vcc = 50;
191 link->conf.IntType = INT_MEMORY_AND_IO;
192 link->conf.Present = PRESENT_OPTION;
193
194
195 link->irq.Instance = info->dev = dev;
196 link->priv = info;
197
198 /* Register with Card Services */
199 link->next = dev_list;
200 dev_list = link;
201 client_reg.dev_info = &dev_info;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202 client_reg.Version = 0x0210;
203 client_reg.event_callback_args.client_data = link;
204 ret = pcmcia_register_client(&link->handle, &client_reg);
205 if (ret != 0) {
206 cs_error(link->handle, RegisterClient, ret);
207 com20020_detach(link);
208 return NULL;
209 }
210
211 return link;
212
213fail_alloc_dev:
214 kfree(info);
215fail_alloc_info:
216 kfree(link);
217 return NULL;
218} /* com20020_attach */
219
220/*======================================================================
221
222 This deletes a driver "instance". The device is de-registered
223 with Card Services. If it has been released, all local data
224 structures are freed. Otherwise, the structures will be freed
225 when the device is released.
226
227======================================================================*/
228
229static void com20020_detach(dev_link_t *link)
230{
231 struct com20020_dev_t *info = link->priv;
232 dev_link_t **linkp;
233 struct net_device *dev;
234
235 DEBUG(1,"detach...\n");
236
237 DEBUG(0, "com20020_detach(0x%p)\n", link);
238
239 /* Locate device structure */
240 for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
241 if (*linkp == link) break;
242 if (*linkp == NULL)
243 return;
244
245 dev = info->dev;
246
247 if (link->dev) {
248 DEBUG(1,"unregister...\n");
249
250 unregister_netdev(dev);
251
252 /*
253 * this is necessary because we register our IRQ separately
254 * from card services.
255 */
256 if (dev->irq)
257 free_irq(dev->irq, dev);
258 }
259
260 if (link->state & DEV_CONFIG)
261 com20020_release(link);
262
263 if (link->handle)
264 pcmcia_deregister_client(link->handle);
265
266 /* Unlink device structure, free bits */
267 DEBUG(1,"unlinking...\n");
268 *linkp = link->next;
269 if (link->priv)
270 {
271 dev = info->dev;
272 if (dev)
273 {
274 DEBUG(1,"kfree...\n");
275 free_netdev(dev);
276 }
277 DEBUG(1,"kfree2...\n");
278 kfree(info);
279 }
280 DEBUG(1,"kfree3...\n");
281 kfree(link);
282
283} /* com20020_detach */
284
285/*======================================================================
286
287 com20020_config() is scheduled to run after a CARD_INSERTION event
288 is received, to configure the PCMCIA socket, and to make the
289 device available to the system.
290
291======================================================================*/
292
293#define CS_CHECK(fn, ret) \
294do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
295
296static void com20020_config(dev_link_t *link)
297{
298 struct arcnet_local *lp;
299 client_handle_t handle;
300 tuple_t tuple;
301 cisparse_t parse;
302 com20020_dev_t *info;
303 struct net_device *dev;
304 int i, last_ret, last_fn;
305 u_char buf[64];
306 int ioaddr;
307
308 handle = link->handle;
309 info = link->priv;
310 dev = info->dev;
311
312 DEBUG(1,"config...\n");
313
314 DEBUG(0, "com20020_config(0x%p)\n", link);
315
316 tuple.Attributes = 0;
317 tuple.TupleData = buf;
318 tuple.TupleDataMax = 64;
319 tuple.TupleOffset = 0;
320 tuple.DesiredTuple = CISTPL_CONFIG;
321 CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
322 CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
323 CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
324 link->conf.ConfigBase = parse.config.base;
325
326 /* Configure card */
327 link->state |= DEV_CONFIG;
328
329 DEBUG(1,"arcnet: baseport1 is %Xh\n", link->io.BasePort1);
330 i = !CS_SUCCESS;
331 if (!link->io.BasePort1)
332 {
333 for (ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x10)
334 {
335 link->io.BasePort1 = ioaddr;
336 i = pcmcia_request_io(link->handle, &link->io);
337 if (i == CS_SUCCESS)
338 break;
339 }
340 }
341 else
342 i = pcmcia_request_io(link->handle, &link->io);
343
344 if (i != CS_SUCCESS)
345 {
346 DEBUG(1,"arcnet: requestIO failed totally!\n");
347 goto failed;
348 }
349
350 ioaddr = dev->base_addr = link->io.BasePort1;
351 DEBUG(1,"arcnet: got ioaddr %Xh\n", ioaddr);
352
353 DEBUG(1,"arcnet: request IRQ %d (%Xh/%Xh)\n",
354 link->irq.AssignedIRQ,
355 link->irq.IRQInfo1, link->irq.IRQInfo2);
356 i = pcmcia_request_irq(link->handle, &link->irq);
357 if (i != CS_SUCCESS)
358 {
359 DEBUG(1,"arcnet: requestIRQ failed totally!\n");
360 goto failed;
361 }
362
363 dev->irq = link->irq.AssignedIRQ;
364
365 CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf));
366
367 if (com20020_check(dev))
368 {
369 regdump(dev);
370 goto failed;
371 }
372
373 lp = dev->priv;
374 lp->card_name = "PCMCIA COM20020";
375 lp->card_flags = ARC_CAN_10MBIT; /* pretend all of them can 10Mbit */
376
377 link->dev = &info->node;
378 link->state &= ~DEV_CONFIG_PENDING;
379 SET_NETDEV_DEV(dev, &handle_to_dev(handle));
380
381 i = com20020_found(dev, 0); /* calls register_netdev */
382
383 if (i != 0) {
384 DEBUG(1,KERN_NOTICE "com20020_cs: com20020_found() failed\n");
385 link->dev = NULL;
386 goto failed;
387 }
388
389 strcpy(info->node.dev_name, dev->name);
390
391 DEBUG(1,KERN_INFO "%s: port %#3lx, irq %d\n",
392 dev->name, dev->base_addr, dev->irq);
393 return;
394
395cs_failed:
396 cs_error(link->handle, last_fn, last_ret);
397failed:
398 DEBUG(1,"com20020_config failed...\n");
399 com20020_release(link);
400} /* com20020_config */
401
402/*======================================================================
403
404 After a card is removed, com20020_release() will unregister the net
405 device, and release the PCMCIA configuration. If the device is
406 still open, this will be postponed until it is closed.
407
408======================================================================*/
409
410static void com20020_release(dev_link_t *link)
411{
412
413 DEBUG(1,"release...\n");
414
415 DEBUG(0, "com20020_release(0x%p)\n", link);
416
417 pcmcia_release_configuration(link->handle);
418 pcmcia_release_io(link->handle, &link->io);
419 pcmcia_release_irq(link->handle, &link->irq);
420
421 link->state &= ~(DEV_CONFIG | DEV_RELEASE_PENDING);
422}
423
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100424static int com20020_suspend(struct pcmcia_device *p_dev)
425{
426 dev_link_t *link = dev_to_instance(p_dev);
427 com20020_dev_t *info = link->priv;
428 struct net_device *dev = info->dev;
429
430 link->state |= DEV_SUSPEND;
431 if (link->state & DEV_CONFIG) {
432 if (link->open) {
433 netif_device_detach(dev);
434 }
435 pcmcia_release_configuration(link->handle);
436 }
437
438 return 0;
439}
440
441static int com20020_resume(struct pcmcia_device *p_dev)
442{
443 dev_link_t *link = dev_to_instance(p_dev);
444 com20020_dev_t *info = link->priv;
445 struct net_device *dev = info->dev;
446
447 link->state &= ~DEV_SUSPEND;
448 if (link->state & DEV_CONFIG) {
449 pcmcia_request_configuration(link->handle, &link->conf);
450 if (link->open) {
451 int ioaddr = dev->base_addr;
452 struct arcnet_local *lp = dev->priv;
453 ARCRESET;
454 }
455 }
456
457 return 0;
458}
459
Linus Torvalds1da177e2005-04-16 15:20:36 -0700460/*======================================================================
461
462 The card status event handler. Mostly, this schedules other
463 stuff to run after an event is received. A CARD_REMOVAL event
464 also sets some flags to discourage the net drivers from trying
465 to talk to the card any more.
466
467======================================================================*/
468
469static int com20020_event(event_t event, int priority,
470 event_callback_args_t *args)
471{
472 dev_link_t *link = args->client_data;
473 com20020_dev_t *info = link->priv;
474 struct net_device *dev = info->dev;
475
476 DEBUG(1, "com20020_event(0x%06x)\n", event);
477
478 switch (event) {
479 case CS_EVENT_CARD_REMOVAL:
480 link->state &= ~DEV_PRESENT;
481 if (link->state & DEV_CONFIG)
482 netif_device_detach(dev);
483 break;
484 case CS_EVENT_CARD_INSERTION:
485 link->state |= DEV_PRESENT;
486 com20020_config(link);
487 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488 }
489 return 0;
490} /* com20020_event */
491
Dominik Brodowski7fb22bb2005-06-27 16:28:38 -0700492static struct pcmcia_device_id com20020_ids[] = {
493 PCMCIA_DEVICE_PROD_ID12("Contemporary Control Systems, Inc.", "PCM20 Arcnet Adapter", 0x59991666, 0x95dfffaf),
494 PCMCIA_DEVICE_NULL
495};
496MODULE_DEVICE_TABLE(pcmcia, com20020_ids);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497
498static struct pcmcia_driver com20020_cs_driver = {
499 .owner = THIS_MODULE,
500 .drv = {
501 .name = "com20020_cs",
502 },
503 .attach = com20020_attach,
Dominik Brodowski1e212f32005-07-07 17:59:00 -0700504 .event = com20020_event,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505 .detach = com20020_detach,
Dominik Brodowski7fb22bb2005-06-27 16:28:38 -0700506 .id_table = com20020_ids,
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100507 .suspend = com20020_suspend,
508 .resume = com20020_resume,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509};
510
511static int __init init_com20020_cs(void)
512{
513 return pcmcia_register_driver(&com20020_cs_driver);
514}
515
516static void __exit exit_com20020_cs(void)
517{
518 pcmcia_unregister_driver(&com20020_cs_driver);
519 BUG_ON(dev_list != NULL);
520}
521
522module_init(init_com20020_cs);
523module_exit(exit_com20020_cs);