blob: e14d3d18b97dddb2b85788933680f34ded8bfb27 [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;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181 link->conf.IntType = INT_MEMORY_AND_IO;
182 link->conf.Present = PRESENT_OPTION;
183
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184 link->irq.Instance = info->dev = dev;
185 link->priv = info;
186
Dominik Brodowskif8cfa612005-11-14 21:25:51 +0100187 link->state |= DEV_PRESENT;
188 com20020_config(link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189
Dominik Brodowskif8cfa612005-11-14 21:25:51 +0100190 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191
192fail_alloc_dev:
193 kfree(info);
194fail_alloc_info:
195 kfree(link);
Dominik Brodowskif8cfa612005-11-14 21:25:51 +0100196 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197} /* com20020_attach */
198
199/*======================================================================
200
201 This deletes a driver "instance". The device is de-registered
202 with Card Services. If it has been released, all local data
203 structures are freed. Otherwise, the structures will be freed
204 when the device is released.
205
206======================================================================*/
207
Dominik Brodowskicc3b4862005-11-14 21:23:14 +0100208static void com20020_detach(struct pcmcia_device *p_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209{
Dominik Brodowskicc3b4862005-11-14 21:23:14 +0100210 dev_link_t *link = dev_to_instance(p_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211 struct com20020_dev_t *info = link->priv;
Dominik Brodowskib4635812005-11-14 21:25:35 +0100212 struct net_device *dev = info->dev;
213
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214 DEBUG(1,"detach...\n");
215
216 DEBUG(0, "com20020_detach(0x%p)\n", link);
217
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218 if (link->dev) {
219 DEBUG(1,"unregister...\n");
220
221 unregister_netdev(dev);
Dominik Brodowskib4635812005-11-14 21:25:35 +0100222
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223 /*
224 * this is necessary because we register our IRQ separately
225 * from card services.
226 */
227 if (dev->irq)
228 free_irq(dev->irq, dev);
229 }
230
231 if (link->state & DEV_CONFIG)
232 com20020_release(link);
233
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234 /* Unlink device structure, free bits */
235 DEBUG(1,"unlinking...\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700236 if (link->priv)
237 {
238 dev = info->dev;
239 if (dev)
240 {
241 DEBUG(1,"kfree...\n");
242 free_netdev(dev);
243 }
244 DEBUG(1,"kfree2...\n");
245 kfree(info);
246 }
247 DEBUG(1,"kfree3...\n");
248 kfree(link);
249
250} /* com20020_detach */
251
252/*======================================================================
253
254 com20020_config() is scheduled to run after a CARD_INSERTION event
255 is received, to configure the PCMCIA socket, and to make the
256 device available to the system.
257
258======================================================================*/
259
260#define CS_CHECK(fn, ret) \
261do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
262
263static void com20020_config(dev_link_t *link)
264{
265 struct arcnet_local *lp;
266 client_handle_t handle;
267 tuple_t tuple;
268 cisparse_t parse;
269 com20020_dev_t *info;
270 struct net_device *dev;
271 int i, last_ret, last_fn;
272 u_char buf[64];
273 int ioaddr;
274
275 handle = link->handle;
276 info = link->priv;
277 dev = info->dev;
278
279 DEBUG(1,"config...\n");
280
281 DEBUG(0, "com20020_config(0x%p)\n", link);
282
283 tuple.Attributes = 0;
284 tuple.TupleData = buf;
285 tuple.TupleDataMax = 64;
286 tuple.TupleOffset = 0;
287 tuple.DesiredTuple = CISTPL_CONFIG;
288 CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
289 CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
290 CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
291 link->conf.ConfigBase = parse.config.base;
292
293 /* Configure card */
294 link->state |= DEV_CONFIG;
295
296 DEBUG(1,"arcnet: baseport1 is %Xh\n", link->io.BasePort1);
297 i = !CS_SUCCESS;
298 if (!link->io.BasePort1)
299 {
300 for (ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x10)
301 {
302 link->io.BasePort1 = ioaddr;
303 i = pcmcia_request_io(link->handle, &link->io);
304 if (i == CS_SUCCESS)
305 break;
306 }
307 }
308 else
309 i = pcmcia_request_io(link->handle, &link->io);
310
311 if (i != CS_SUCCESS)
312 {
313 DEBUG(1,"arcnet: requestIO failed totally!\n");
314 goto failed;
315 }
316
317 ioaddr = dev->base_addr = link->io.BasePort1;
318 DEBUG(1,"arcnet: got ioaddr %Xh\n", ioaddr);
319
320 DEBUG(1,"arcnet: request IRQ %d (%Xh/%Xh)\n",
321 link->irq.AssignedIRQ,
322 link->irq.IRQInfo1, link->irq.IRQInfo2);
323 i = pcmcia_request_irq(link->handle, &link->irq);
324 if (i != CS_SUCCESS)
325 {
326 DEBUG(1,"arcnet: requestIRQ failed totally!\n");
327 goto failed;
328 }
329
330 dev->irq = link->irq.AssignedIRQ;
331
332 CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf));
333
334 if (com20020_check(dev))
335 {
336 regdump(dev);
337 goto failed;
338 }
339
340 lp = dev->priv;
341 lp->card_name = "PCMCIA COM20020";
342 lp->card_flags = ARC_CAN_10MBIT; /* pretend all of them can 10Mbit */
343
344 link->dev = &info->node;
345 link->state &= ~DEV_CONFIG_PENDING;
346 SET_NETDEV_DEV(dev, &handle_to_dev(handle));
347
348 i = com20020_found(dev, 0); /* calls register_netdev */
349
350 if (i != 0) {
351 DEBUG(1,KERN_NOTICE "com20020_cs: com20020_found() failed\n");
352 link->dev = NULL;
353 goto failed;
354 }
355
356 strcpy(info->node.dev_name, dev->name);
357
358 DEBUG(1,KERN_INFO "%s: port %#3lx, irq %d\n",
359 dev->name, dev->base_addr, dev->irq);
360 return;
361
362cs_failed:
363 cs_error(link->handle, last_fn, last_ret);
364failed:
365 DEBUG(1,"com20020_config failed...\n");
366 com20020_release(link);
367} /* com20020_config */
368
369/*======================================================================
370
371 After a card is removed, com20020_release() will unregister the net
372 device, and release the PCMCIA configuration. If the device is
373 still open, this will be postponed until it is closed.
374
375======================================================================*/
376
377static void com20020_release(dev_link_t *link)
378{
Dominik Brodowski5f2a71f2006-01-15 09:32:39 +0100379 DEBUG(0, "com20020_release(0x%p)\n", link);
380 pcmcia_disable_device(link->handle);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381}
382
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100383static int com20020_suspend(struct pcmcia_device *p_dev)
384{
385 dev_link_t *link = dev_to_instance(p_dev);
386 com20020_dev_t *info = link->priv;
387 struct net_device *dev = info->dev;
388
Dominik Brodowski8661bb52006-03-02 00:02:33 +0100389 if ((link->state & DEV_CONFIG) && (link->open))
390 netif_device_detach(dev);
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100391
392 return 0;
393}
394
395static int com20020_resume(struct pcmcia_device *p_dev)
396{
397 dev_link_t *link = dev_to_instance(p_dev);
398 com20020_dev_t *info = link->priv;
399 struct net_device *dev = info->dev;
400
Dominik Brodowski8661bb52006-03-02 00:02:33 +0100401 if ((link->state & DEV_CONFIG) && (link->open)) {
402 int ioaddr = dev->base_addr;
403 struct arcnet_local *lp = dev->priv;
404 ARCRESET;
405 }
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100406
407 return 0;
408}
409
Dominik Brodowski7fb22bb2005-06-27 16:28:38 -0700410static struct pcmcia_device_id com20020_ids[] = {
411 PCMCIA_DEVICE_PROD_ID12("Contemporary Control Systems, Inc.", "PCM20 Arcnet Adapter", 0x59991666, 0x95dfffaf),
412 PCMCIA_DEVICE_NULL
413};
414MODULE_DEVICE_TABLE(pcmcia, com20020_ids);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415
416static struct pcmcia_driver com20020_cs_driver = {
417 .owner = THIS_MODULE,
418 .drv = {
419 .name = "com20020_cs",
420 },
Dominik Brodowskif8cfa612005-11-14 21:25:51 +0100421 .probe = com20020_attach,
Dominik Brodowskicc3b4862005-11-14 21:23:14 +0100422 .remove = com20020_detach,
Dominik Brodowski7fb22bb2005-06-27 16:28:38 -0700423 .id_table = com20020_ids,
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100424 .suspend = com20020_suspend,
425 .resume = com20020_resume,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426};
427
428static int __init init_com20020_cs(void)
429{
430 return pcmcia_register_driver(&com20020_cs_driver);
431}
432
433static void __exit exit_com20020_cs(void)
434{
435 pcmcia_unregister_driver(&com20020_cs_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436}
437
438module_init(init_com20020_cs);
439module_exit(exit_com20020_cs);