aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/pcmcia/com20020_cs.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/pcmcia/com20020_cs.c')
-rw-r--r--drivers/net/pcmcia/com20020_cs.c509
1 files changed, 509 insertions, 0 deletions
diff --git a/drivers/net/pcmcia/com20020_cs.c b/drivers/net/pcmcia/com20020_cs.c
new file mode 100644
index 000000000000..4294e1e3f156
--- /dev/null
+++ b/drivers/net/pcmcia/com20020_cs.c
@@ -0,0 +1,509 @@
1/*
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
46#include <pcmcia/version.h>
47#include <pcmcia/cs_types.h>
48#include <pcmcia/cs.h>
49#include <pcmcia/cistpl.h>
50#include <pcmcia/ds.h>
51
52#include <asm/io.h>
53#include <asm/system.h>
54
55#define VERSION "arcnet: COM20020 PCMCIA support loaded.\n"
56
57#ifdef PCMCIA_DEBUG
58
59static int pc_debug = PCMCIA_DEBUG;
60module_param(pc_debug, int, 0);
61#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
62
63static void regdump(struct net_device *dev)
64{
65 int ioaddr = dev->base_addr;
66 int count;
67
68 printk("com20020 register dump:\n");
69 for (count = ioaddr; count < ioaddr + 16; count++)
70 {
71 if (!(count % 16))
72 printk("\n%04X: ", count);
73 printk("%02X ", inb(count));
74 }
75 printk("\n");
76
77 printk("buffer0 dump:\n");
78 /* set up the address register */
79 count = 0;
80 outb((count >> 8) | RDDATAflag | AUTOINCflag, _ADDR_HI);
81 outb(count & 0xff, _ADDR_LO);
82
83 for (count = 0; count < 256+32; count++)
84 {
85 if (!(count % 16))
86 printk("\n%04X: ", count);
87
88 /* copy the data */
89 printk("%02X ", inb(_MEMDATA));
90 }
91 printk("\n");
92}
93
94#else
95
96#define DEBUG(n, args...) do { } while (0)
97static inline void regdump(struct net_device *dev) { }
98
99#endif
100
101
102/*====================================================================*/
103
104/* Parameters that can be set with 'insmod' */
105
106static int node;
107static int timeout = 3;
108static int backplane;
109static int clockp;
110static int clockm;
111
112module_param(node, int, 0);
113module_param(timeout, int, 0);
114module_param(backplane, int, 0);
115module_param(clockp, int, 0);
116module_param(clockm, int, 0);
117
118MODULE_LICENSE("GPL");
119
120/*====================================================================*/
121
122static void com20020_config(dev_link_t *link);
123static void com20020_release(dev_link_t *link);
124static int com20020_event(event_t event, int priority,
125 event_callback_args_t *args);
126
127static dev_info_t dev_info = "com20020_cs";
128
129static dev_link_t *com20020_attach(void);
130static void com20020_detach(dev_link_t *);
131
132static dev_link_t *dev_list;
133
134/*====================================================================*/
135
136typedef struct com20020_dev_t {
137 struct net_device *dev;
138 dev_node_t node;
139} com20020_dev_t;
140
141/*======================================================================
142
143 com20020_attach() creates an "instance" of the driver, allocating
144 local data structures for one device. The device is registered
145 with Card Services.
146
147======================================================================*/
148
149static dev_link_t *com20020_attach(void)
150{
151 client_reg_t client_reg;
152 dev_link_t *link;
153 com20020_dev_t *info;
154 struct net_device *dev;
155 int ret;
156 struct arcnet_local *lp;
157
158 DEBUG(0, "com20020_attach()\n");
159
160 /* Create new network device */
161 link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL);
162 if (!link)
163 return NULL;
164
165 info = kmalloc(sizeof(struct com20020_dev_t), GFP_KERNEL);
166 if (!info)
167 goto fail_alloc_info;
168
169 dev = alloc_arcdev("");
170 if (!dev)
171 goto fail_alloc_dev;
172
173 memset(info, 0, sizeof(struct com20020_dev_t));
174 memset(link, 0, sizeof(struct dev_link_t));
175 lp = dev->priv;
176 lp->timeout = timeout;
177 lp->backplane = backplane;
178 lp->clockp = clockp;
179 lp->clockm = clockm & 3;
180 lp->hw.owner = THIS_MODULE;
181
182 /* fill in our module parameters as defaults */
183 dev->dev_addr[0] = node;
184
185 link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
186 link->io.NumPorts1 = 16;
187 link->io.IOAddrLines = 16;
188 link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
189 link->irq.IRQInfo1 = IRQ_LEVEL_ID;
190 link->conf.Attributes = CONF_ENABLE_IRQ;
191 link->conf.Vcc = 50;
192 link->conf.IntType = INT_MEMORY_AND_IO;
193 link->conf.Present = PRESENT_OPTION;
194
195
196 link->irq.Instance = info->dev = dev;
197 link->priv = info;
198
199 /* Register with Card Services */
200 link->next = dev_list;
201 dev_list = link;
202 client_reg.dev_info = &dev_info;
203 client_reg.EventMask =
204 CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
205 CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
206 CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
207 client_reg.event_handler = &com20020_event;
208 client_reg.Version = 0x0210;
209 client_reg.event_callback_args.client_data = link;
210 ret = pcmcia_register_client(&link->handle, &client_reg);
211 if (ret != 0) {
212 cs_error(link->handle, RegisterClient, ret);
213 com20020_detach(link);
214 return NULL;
215 }
216
217 return link;
218
219fail_alloc_dev:
220 kfree(info);
221fail_alloc_info:
222 kfree(link);
223 return NULL;
224} /* com20020_attach */
225
226/*======================================================================
227
228 This deletes a driver "instance". The device is de-registered
229 with Card Services. If it has been released, all local data
230 structures are freed. Otherwise, the structures will be freed
231 when the device is released.
232
233======================================================================*/
234
235static void com20020_detach(dev_link_t *link)
236{
237 struct com20020_dev_t *info = link->priv;
238 dev_link_t **linkp;
239 struct net_device *dev;
240
241 DEBUG(1,"detach...\n");
242
243 DEBUG(0, "com20020_detach(0x%p)\n", link);
244
245 /* Locate device structure */
246 for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
247 if (*linkp == link) break;
248 if (*linkp == NULL)
249 return;
250
251 dev = info->dev;
252
253 if (link->dev) {
254 DEBUG(1,"unregister...\n");
255
256 unregister_netdev(dev);
257
258 /*
259 * this is necessary because we register our IRQ separately
260 * from card services.
261 */
262 if (dev->irq)
263 free_irq(dev->irq, dev);
264 }
265
266 if (link->state & DEV_CONFIG)
267 com20020_release(link);
268
269 if (link->handle)
270 pcmcia_deregister_client(link->handle);
271
272 /* Unlink device structure, free bits */
273 DEBUG(1,"unlinking...\n");
274 *linkp = link->next;
275 if (link->priv)
276 {
277 dev = info->dev;
278 if (dev)
279 {
280 DEBUG(1,"kfree...\n");
281 free_netdev(dev);
282 }
283 DEBUG(1,"kfree2...\n");
284 kfree(info);
285 }
286 DEBUG(1,"kfree3...\n");
287 kfree(link);
288
289} /* com20020_detach */
290
291/*======================================================================
292
293 com20020_config() is scheduled to run after a CARD_INSERTION event
294 is received, to configure the PCMCIA socket, and to make the
295 device available to the system.
296
297======================================================================*/
298
299#define CS_CHECK(fn, ret) \
300do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
301
302static void com20020_config(dev_link_t *link)
303{
304 struct arcnet_local *lp;
305 client_handle_t handle;
306 tuple_t tuple;
307 cisparse_t parse;
308 com20020_dev_t *info;
309 struct net_device *dev;
310 int i, last_ret, last_fn;
311 u_char buf[64];
312 int ioaddr;
313
314 handle = link->handle;
315 info = link->priv;
316 dev = info->dev;
317
318 DEBUG(1,"config...\n");
319
320 DEBUG(0, "com20020_config(0x%p)\n", link);
321
322 tuple.Attributes = 0;
323 tuple.TupleData = buf;
324 tuple.TupleDataMax = 64;
325 tuple.TupleOffset = 0;
326 tuple.DesiredTuple = CISTPL_CONFIG;
327 CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
328 CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
329 CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
330 link->conf.ConfigBase = parse.config.base;
331
332 /* Configure card */
333 link->state |= DEV_CONFIG;
334
335 DEBUG(1,"arcnet: baseport1 is %Xh\n", link->io.BasePort1);
336 i = !CS_SUCCESS;
337 if (!link->io.BasePort1)
338 {
339 for (ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x10)
340 {
341 link->io.BasePort1 = ioaddr;
342 i = pcmcia_request_io(link->handle, &link->io);
343 if (i == CS_SUCCESS)
344 break;
345 }
346 }
347 else
348 i = pcmcia_request_io(link->handle, &link->io);
349
350 if (i != CS_SUCCESS)
351 {
352 DEBUG(1,"arcnet: requestIO failed totally!\n");
353 goto failed;
354 }
355
356 ioaddr = dev->base_addr = link->io.BasePort1;
357 DEBUG(1,"arcnet: got ioaddr %Xh\n", ioaddr);
358
359 DEBUG(1,"arcnet: request IRQ %d (%Xh/%Xh)\n",
360 link->irq.AssignedIRQ,
361 link->irq.IRQInfo1, link->irq.IRQInfo2);
362 i = pcmcia_request_irq(link->handle, &link->irq);
363 if (i != CS_SUCCESS)
364 {
365 DEBUG(1,"arcnet: requestIRQ failed totally!\n");
366 goto failed;
367 }
368
369 dev->irq = link->irq.AssignedIRQ;
370
371 CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf));
372
373 if (com20020_check(dev))
374 {
375 regdump(dev);
376 goto failed;
377 }
378
379 lp = dev->priv;
380 lp->card_name = "PCMCIA COM20020";
381 lp->card_flags = ARC_CAN_10MBIT; /* pretend all of them can 10Mbit */
382
383 link->dev = &info->node;
384 link->state &= ~DEV_CONFIG_PENDING;
385 SET_NETDEV_DEV(dev, &handle_to_dev(handle));
386
387 i = com20020_found(dev, 0); /* calls register_netdev */
388
389 if (i != 0) {
390 DEBUG(1,KERN_NOTICE "com20020_cs: com20020_found() failed\n");
391 link->dev = NULL;
392 goto failed;
393 }
394
395 strcpy(info->node.dev_name, dev->name);
396
397 DEBUG(1,KERN_INFO "%s: port %#3lx, irq %d\n",
398 dev->name, dev->base_addr, dev->irq);
399 return;
400
401cs_failed:
402 cs_error(link->handle, last_fn, last_ret);
403failed:
404 DEBUG(1,"com20020_config failed...\n");
405 com20020_release(link);
406} /* com20020_config */
407
408/*======================================================================
409
410 After a card is removed, com20020_release() will unregister the net
411 device, and release the PCMCIA configuration. If the device is
412 still open, this will be postponed until it is closed.
413
414======================================================================*/
415
416static void com20020_release(dev_link_t *link)
417{
418
419 DEBUG(1,"release...\n");
420
421 DEBUG(0, "com20020_release(0x%p)\n", link);
422
423 pcmcia_release_configuration(link->handle);
424 pcmcia_release_io(link->handle, &link->io);
425 pcmcia_release_irq(link->handle, &link->irq);
426
427 link->state &= ~(DEV_CONFIG | DEV_RELEASE_PENDING);
428}
429
430/*======================================================================
431
432 The card status event handler. Mostly, this schedules other
433 stuff to run after an event is received. A CARD_REMOVAL event
434 also sets some flags to discourage the net drivers from trying
435 to talk to the card any more.
436
437======================================================================*/
438
439static int com20020_event(event_t event, int priority,
440 event_callback_args_t *args)
441{
442 dev_link_t *link = args->client_data;
443 com20020_dev_t *info = link->priv;
444 struct net_device *dev = info->dev;
445
446 DEBUG(1, "com20020_event(0x%06x)\n", event);
447
448 switch (event) {
449 case CS_EVENT_CARD_REMOVAL:
450 link->state &= ~DEV_PRESENT;
451 if (link->state & DEV_CONFIG)
452 netif_device_detach(dev);
453 break;
454 case CS_EVENT_CARD_INSERTION:
455 link->state |= DEV_PRESENT;
456 com20020_config(link);
457 break;
458 case CS_EVENT_PM_SUSPEND:
459 link->state |= DEV_SUSPEND;
460 /* Fall through... */
461 case CS_EVENT_RESET_PHYSICAL:
462 if (link->state & DEV_CONFIG) {
463 if (link->open) {
464 netif_device_detach(dev);
465 }
466 pcmcia_release_configuration(link->handle);
467 }
468 break;
469 case CS_EVENT_PM_RESUME:
470 link->state &= ~DEV_SUSPEND;
471 /* Fall through... */
472 case CS_EVENT_CARD_RESET:
473 if (link->state & DEV_CONFIG) {
474 pcmcia_request_configuration(link->handle, &link->conf);
475 if (link->open) {
476 int ioaddr = dev->base_addr;
477 struct arcnet_local *lp = dev->priv;
478 ARCRESET;
479 }
480 }
481 break;
482 }
483 return 0;
484} /* com20020_event */
485
486
487
488static struct pcmcia_driver com20020_cs_driver = {
489 .owner = THIS_MODULE,
490 .drv = {
491 .name = "com20020_cs",
492 },
493 .attach = com20020_attach,
494 .detach = com20020_detach,
495};
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);