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