blob: 2827a48ea37c64c66c0f508ae220a189ab0bb5eb [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);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123
Dominik Brodowskicc3b4862005-11-14 21:23:14 +0100124static void com20020_detach(struct pcmcia_device *p_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126/*====================================================================*/
127
128typedef struct com20020_dev_t {
129 struct net_device *dev;
130 dev_node_t node;
131} com20020_dev_t;
132
133/*======================================================================
134
135 com20020_attach() creates an "instance" of the driver, allocating
136 local data structures for one device. The device is registered
137 with Card Services.
138
139======================================================================*/
140
Dominik Brodowskif8cfa612005-11-14 21:25:51 +0100141static int com20020_attach(struct pcmcia_device *p_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143 dev_link_t *link;
144 com20020_dev_t *info;
145 struct net_device *dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146 struct arcnet_local *lp;
Dominik Brodowskif8cfa612005-11-14 21:25:51 +0100147
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148 DEBUG(0, "com20020_attach()\n");
149
150 /* Create new network device */
151 link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL);
152 if (!link)
Dominik Brodowskif8cfa612005-11-14 21:25:51 +0100153 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154
155 info = kmalloc(sizeof(struct com20020_dev_t), GFP_KERNEL);
156 if (!info)
157 goto fail_alloc_info;
158
159 dev = alloc_arcdev("");
160 if (!dev)
161 goto fail_alloc_dev;
162
163 memset(info, 0, sizeof(struct com20020_dev_t));
164 memset(link, 0, sizeof(struct dev_link_t));
165 lp = dev->priv;
166 lp->timeout = timeout;
167 lp->backplane = backplane;
168 lp->clockp = clockp;
169 lp->clockm = clockm & 3;
170 lp->hw.owner = THIS_MODULE;
171
172 /* fill in our module parameters as defaults */
173 dev->dev_addr[0] = node;
174
175 link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
176 link->io.NumPorts1 = 16;
177 link->io.IOAddrLines = 16;
178 link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
179 link->irq.IRQInfo1 = IRQ_LEVEL_ID;
180 link->conf.Attributes = CONF_ENABLE_IRQ;
181 link->conf.Vcc = 50;
182 link->conf.IntType = INT_MEMORY_AND_IO;
183 link->conf.Present = PRESENT_OPTION;
184
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185 link->irq.Instance = info->dev = dev;
186 link->priv = info;
187
Dominik Brodowskif8cfa612005-11-14 21:25:51 +0100188 link->state |= DEV_PRESENT;
189 com20020_config(link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190
Dominik Brodowskif8cfa612005-11-14 21:25:51 +0100191 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192
193fail_alloc_dev:
194 kfree(info);
195fail_alloc_info:
196 kfree(link);
Dominik Brodowskif8cfa612005-11-14 21:25:51 +0100197 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198} /* com20020_attach */
199
200/*======================================================================
201
202 This deletes a driver "instance". The device is de-registered
203 with Card Services. If it has been released, all local data
204 structures are freed. Otherwise, the structures will be freed
205 when the device is released.
206
207======================================================================*/
208
Dominik Brodowskicc3b4862005-11-14 21:23:14 +0100209static void com20020_detach(struct pcmcia_device *p_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700210{
Dominik Brodowskicc3b4862005-11-14 21:23:14 +0100211 dev_link_t *link = dev_to_instance(p_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700212 struct com20020_dev_t *info = link->priv;
Dominik Brodowskib4635812005-11-14 21:25:35 +0100213 struct net_device *dev = info->dev;
214
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215 DEBUG(1,"detach...\n");
216
217 DEBUG(0, "com20020_detach(0x%p)\n", link);
218
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219 if (link->dev) {
220 DEBUG(1,"unregister...\n");
221
222 unregister_netdev(dev);
Dominik Brodowskib4635812005-11-14 21:25:35 +0100223
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224 /*
225 * this is necessary because we register our IRQ separately
226 * from card services.
227 */
228 if (dev->irq)
229 free_irq(dev->irq, dev);
230 }
231
232 if (link->state & DEV_CONFIG)
233 com20020_release(link);
234
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235 /* Unlink device structure, free bits */
236 DEBUG(1,"unlinking...\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237 if (link->priv)
238 {
239 dev = info->dev;
240 if (dev)
241 {
242 DEBUG(1,"kfree...\n");
243 free_netdev(dev);
244 }
245 DEBUG(1,"kfree2...\n");
246 kfree(info);
247 }
248 DEBUG(1,"kfree3...\n");
249 kfree(link);
250
251} /* com20020_detach */
252
253/*======================================================================
254
255 com20020_config() is scheduled to run after a CARD_INSERTION event
256 is received, to configure the PCMCIA socket, and to make the
257 device available to the system.
258
259======================================================================*/
260
261#define CS_CHECK(fn, ret) \
262do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
263
264static void com20020_config(dev_link_t *link)
265{
266 struct arcnet_local *lp;
267 client_handle_t handle;
268 tuple_t tuple;
269 cisparse_t parse;
270 com20020_dev_t *info;
271 struct net_device *dev;
272 int i, last_ret, last_fn;
273 u_char buf[64];
274 int ioaddr;
275
276 handle = link->handle;
277 info = link->priv;
278 dev = info->dev;
279
280 DEBUG(1,"config...\n");
281
282 DEBUG(0, "com20020_config(0x%p)\n", link);
283
284 tuple.Attributes = 0;
285 tuple.TupleData = buf;
286 tuple.TupleDataMax = 64;
287 tuple.TupleOffset = 0;
288 tuple.DesiredTuple = CISTPL_CONFIG;
289 CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
290 CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
291 CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
292 link->conf.ConfigBase = parse.config.base;
293
294 /* Configure card */
295 link->state |= DEV_CONFIG;
296
297 DEBUG(1,"arcnet: baseport1 is %Xh\n", link->io.BasePort1);
298 i = !CS_SUCCESS;
299 if (!link->io.BasePort1)
300 {
301 for (ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x10)
302 {
303 link->io.BasePort1 = ioaddr;
304 i = pcmcia_request_io(link->handle, &link->io);
305 if (i == CS_SUCCESS)
306 break;
307 }
308 }
309 else
310 i = pcmcia_request_io(link->handle, &link->io);
311
312 if (i != CS_SUCCESS)
313 {
314 DEBUG(1,"arcnet: requestIO failed totally!\n");
315 goto failed;
316 }
317
318 ioaddr = dev->base_addr = link->io.BasePort1;
319 DEBUG(1,"arcnet: got ioaddr %Xh\n", ioaddr);
320
321 DEBUG(1,"arcnet: request IRQ %d (%Xh/%Xh)\n",
322 link->irq.AssignedIRQ,
323 link->irq.IRQInfo1, link->irq.IRQInfo2);
324 i = pcmcia_request_irq(link->handle, &link->irq);
325 if (i != CS_SUCCESS)
326 {
327 DEBUG(1,"arcnet: requestIRQ failed totally!\n");
328 goto failed;
329 }
330
331 dev->irq = link->irq.AssignedIRQ;
332
333 CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf));
334
335 if (com20020_check(dev))
336 {
337 regdump(dev);
338 goto failed;
339 }
340
341 lp = dev->priv;
342 lp->card_name = "PCMCIA COM20020";
343 lp->card_flags = ARC_CAN_10MBIT; /* pretend all of them can 10Mbit */
344
345 link->dev = &info->node;
346 link->state &= ~DEV_CONFIG_PENDING;
347 SET_NETDEV_DEV(dev, &handle_to_dev(handle));
348
349 i = com20020_found(dev, 0); /* calls register_netdev */
350
351 if (i != 0) {
352 DEBUG(1,KERN_NOTICE "com20020_cs: com20020_found() failed\n");
353 link->dev = NULL;
354 goto failed;
355 }
356
357 strcpy(info->node.dev_name, dev->name);
358
359 DEBUG(1,KERN_INFO "%s: port %#3lx, irq %d\n",
360 dev->name, dev->base_addr, dev->irq);
361 return;
362
363cs_failed:
364 cs_error(link->handle, last_fn, last_ret);
365failed:
366 DEBUG(1,"com20020_config failed...\n");
367 com20020_release(link);
368} /* com20020_config */
369
370/*======================================================================
371
372 After a card is removed, com20020_release() will unregister the net
373 device, and release the PCMCIA configuration. If the device is
374 still open, this will be postponed until it is closed.
375
376======================================================================*/
377
378static void com20020_release(dev_link_t *link)
379{
380
381 DEBUG(1,"release...\n");
382
383 DEBUG(0, "com20020_release(0x%p)\n", link);
384
385 pcmcia_release_configuration(link->handle);
386 pcmcia_release_io(link->handle, &link->io);
387 pcmcia_release_irq(link->handle, &link->irq);
388
389 link->state &= ~(DEV_CONFIG | DEV_RELEASE_PENDING);
390}
391
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100392static int com20020_suspend(struct pcmcia_device *p_dev)
393{
394 dev_link_t *link = dev_to_instance(p_dev);
395 com20020_dev_t *info = link->priv;
396 struct net_device *dev = info->dev;
397
398 link->state |= DEV_SUSPEND;
399 if (link->state & DEV_CONFIG) {
400 if (link->open) {
401 netif_device_detach(dev);
402 }
403 pcmcia_release_configuration(link->handle);
404 }
405
406 return 0;
407}
408
409static int com20020_resume(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 pcmcia_request_configuration(link->handle, &link->conf);
418 if (link->open) {
419 int ioaddr = dev->base_addr;
420 struct arcnet_local *lp = dev->priv;
421 ARCRESET;
422 }
423 }
424
425 return 0;
426}
427
Dominik Brodowski7fb22bb2005-06-27 16:28:38 -0700428static struct pcmcia_device_id com20020_ids[] = {
429 PCMCIA_DEVICE_PROD_ID12("Contemporary Control Systems, Inc.", "PCM20 Arcnet Adapter", 0x59991666, 0x95dfffaf),
430 PCMCIA_DEVICE_NULL
431};
432MODULE_DEVICE_TABLE(pcmcia, com20020_ids);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433
434static struct pcmcia_driver com20020_cs_driver = {
435 .owner = THIS_MODULE,
436 .drv = {
437 .name = "com20020_cs",
438 },
Dominik Brodowskif8cfa612005-11-14 21:25:51 +0100439 .probe = com20020_attach,
Dominik Brodowskicc3b4862005-11-14 21:23:14 +0100440 .remove = com20020_detach,
Dominik Brodowski7fb22bb2005-06-27 16:28:38 -0700441 .id_table = com20020_ids,
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100442 .suspend = com20020_suspend,
443 .resume = com20020_resume,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444};
445
446static int __init init_com20020_cs(void)
447{
448 return pcmcia_register_driver(&com20020_cs_driver);
449}
450
451static void __exit exit_com20020_cs(void)
452{
453 pcmcia_unregister_driver(&com20020_cs_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454}
455
456module_init(init_com20020_cs);
457module_exit(exit_com20020_cs);