diff options
Diffstat (limited to 'drivers/net/pcmcia/com20020_cs.c')
-rw-r--r-- | drivers/net/pcmcia/com20020_cs.c | 509 |
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 | |||
59 | static int pc_debug = PCMCIA_DEBUG; | ||
60 | module_param(pc_debug, int, 0); | ||
61 | #define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args) | ||
62 | |||
63 | static 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) | ||
97 | static inline void regdump(struct net_device *dev) { } | ||
98 | |||
99 | #endif | ||
100 | |||
101 | |||
102 | /*====================================================================*/ | ||
103 | |||
104 | /* Parameters that can be set with 'insmod' */ | ||
105 | |||
106 | static int node; | ||
107 | static int timeout = 3; | ||
108 | static int backplane; | ||
109 | static int clockp; | ||
110 | static int clockm; | ||
111 | |||
112 | module_param(node, int, 0); | ||
113 | module_param(timeout, int, 0); | ||
114 | module_param(backplane, int, 0); | ||
115 | module_param(clockp, int, 0); | ||
116 | module_param(clockm, int, 0); | ||
117 | |||
118 | MODULE_LICENSE("GPL"); | ||
119 | |||
120 | /*====================================================================*/ | ||
121 | |||
122 | static void com20020_config(dev_link_t *link); | ||
123 | static void com20020_release(dev_link_t *link); | ||
124 | static int com20020_event(event_t event, int priority, | ||
125 | event_callback_args_t *args); | ||
126 | |||
127 | static dev_info_t dev_info = "com20020_cs"; | ||
128 | |||
129 | static dev_link_t *com20020_attach(void); | ||
130 | static void com20020_detach(dev_link_t *); | ||
131 | |||
132 | static dev_link_t *dev_list; | ||
133 | |||
134 | /*====================================================================*/ | ||
135 | |||
136 | typedef 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 | |||
149 | static 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 | |||
219 | fail_alloc_dev: | ||
220 | kfree(info); | ||
221 | fail_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 | |||
235 | static 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) \ | ||
300 | do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) | ||
301 | |||
302 | static 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 | |||
401 | cs_failed: | ||
402 | cs_error(link->handle, last_fn, last_ret); | ||
403 | failed: | ||
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 | |||
416 | static 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 | |||
439 | static 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 | |||
488 | static 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 | |||
497 | static int __init init_com20020_cs(void) | ||
498 | { | ||
499 | return pcmcia_register_driver(&com20020_cs_driver); | ||
500 | } | ||
501 | |||
502 | static void __exit exit_com20020_cs(void) | ||
503 | { | ||
504 | pcmcia_unregister_driver(&com20020_cs_driver); | ||
505 | BUG_ON(dev_list != NULL); | ||
506 | } | ||
507 | |||
508 | module_init(init_com20020_cs); | ||
509 | module_exit(exit_com20020_cs); | ||