blob: b9355d9498a31140f90820022ff0746e41f987b9 [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
424/*======================================================================
425
426 The card status event handler. Mostly, this schedules other
427 stuff to run after an event is received. A CARD_REMOVAL event
428 also sets some flags to discourage the net drivers from trying
429 to talk to the card any more.
430
431======================================================================*/
432
433static int com20020_event(event_t event, int priority,
434 event_callback_args_t *args)
435{
436 dev_link_t *link = args->client_data;
437 com20020_dev_t *info = link->priv;
438 struct net_device *dev = info->dev;
439
440 DEBUG(1, "com20020_event(0x%06x)\n", event);
441
442 switch (event) {
443 case CS_EVENT_CARD_REMOVAL:
444 link->state &= ~DEV_PRESENT;
445 if (link->state & DEV_CONFIG)
446 netif_device_detach(dev);
447 break;
448 case CS_EVENT_CARD_INSERTION:
449 link->state |= DEV_PRESENT;
450 com20020_config(link);
451 break;
452 case CS_EVENT_PM_SUSPEND:
453 link->state |= DEV_SUSPEND;
454 /* Fall through... */
455 case CS_EVENT_RESET_PHYSICAL:
456 if (link->state & DEV_CONFIG) {
457 if (link->open) {
458 netif_device_detach(dev);
459 }
460 pcmcia_release_configuration(link->handle);
461 }
462 break;
463 case CS_EVENT_PM_RESUME:
464 link->state &= ~DEV_SUSPEND;
465 /* Fall through... */
466 case CS_EVENT_CARD_RESET:
467 if (link->state & DEV_CONFIG) {
468 pcmcia_request_configuration(link->handle, &link->conf);
469 if (link->open) {
470 int ioaddr = dev->base_addr;
471 struct arcnet_local *lp = dev->priv;
472 ARCRESET;
473 }
474 }
475 break;
476 }
477 return 0;
478} /* com20020_event */
479
Dominik Brodowski7fb22bb2005-06-27 16:28:38 -0700480static struct pcmcia_device_id com20020_ids[] = {
481 PCMCIA_DEVICE_PROD_ID12("Contemporary Control Systems, Inc.", "PCM20 Arcnet Adapter", 0x59991666, 0x95dfffaf),
482 PCMCIA_DEVICE_NULL
483};
484MODULE_DEVICE_TABLE(pcmcia, com20020_ids);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485
486static struct pcmcia_driver com20020_cs_driver = {
487 .owner = THIS_MODULE,
488 .drv = {
489 .name = "com20020_cs",
490 },
491 .attach = com20020_attach,
Dominik Brodowski1e212f32005-07-07 17:59:00 -0700492 .event = com20020_event,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493 .detach = com20020_detach,
Dominik Brodowski7fb22bb2005-06-27 16:28:38 -0700494 .id_table = com20020_ids,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495};
496
497static int __init init_com20020_cs(void)
498{
499 return pcmcia_register_driver(&com20020_cs_driver);
500}
501
502static void __exit exit_com20020_cs(void)
503{
504 pcmcia_unregister_driver(&com20020_cs_driver);
505 BUG_ON(dev_list != NULL);
506}
507
508module_init(init_com20020_cs);
509module_exit(exit_com20020_cs);