blob: a7d675bc6051a06782c3a0f58f4ecbaf77a77bd9 [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
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200121static int com20020_config(struct pcmcia_device *link);
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200122static void com20020_release(struct pcmcia_device *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 Brodowski15b99ac2006-03-31 17:26:06 +0200141static int com20020_probe(struct pcmcia_device *p_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143 com20020_dev_t *info;
144 struct net_device *dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145 struct arcnet_local *lp;
Dominik Brodowskif8cfa612005-11-14 21:25:51 +0100146
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147 DEBUG(0, "com20020_attach()\n");
148
149 /* Create new network device */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150 info = kmalloc(sizeof(struct com20020_dev_t), GFP_KERNEL);
151 if (!info)
152 goto fail_alloc_info;
153
154 dev = alloc_arcdev("");
155 if (!dev)
156 goto fail_alloc_dev;
157
158 memset(info, 0, sizeof(struct com20020_dev_t));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159 lp = dev->priv;
160 lp->timeout = timeout;
161 lp->backplane = backplane;
162 lp->clockp = clockp;
163 lp->clockm = clockm & 3;
164 lp->hw.owner = THIS_MODULE;
165
166 /* fill in our module parameters as defaults */
167 dev->dev_addr[0] = node;
168
Dominik Brodowskifd238232006-03-05 10:45:09 +0100169 p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
170 p_dev->io.NumPorts1 = 16;
171 p_dev->io.IOAddrLines = 16;
172 p_dev->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
173 p_dev->irq.IRQInfo1 = IRQ_LEVEL_ID;
174 p_dev->conf.Attributes = CONF_ENABLE_IRQ;
175 p_dev->conf.IntType = INT_MEMORY_AND_IO;
176 p_dev->conf.Present = PRESENT_OPTION;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177
Dominik Brodowskifd238232006-03-05 10:45:09 +0100178 p_dev->irq.Instance = info->dev = dev;
179 p_dev->priv = info;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180
Dominik Brodowskifd238232006-03-05 10:45:09 +0100181 p_dev->state |= DEV_PRESENT;
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200182 return com20020_config(p_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183
184fail_alloc_dev:
185 kfree(info);
186fail_alloc_info:
Dominik Brodowskif8cfa612005-11-14 21:25:51 +0100187 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188} /* com20020_attach */
189
190/*======================================================================
191
192 This deletes a driver "instance". The device is de-registered
193 with Card Services. If it has been released, all local data
194 structures are freed. Otherwise, the structures will be freed
195 when the device is released.
196
197======================================================================*/
198
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200199static void com20020_detach(struct pcmcia_device *link)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200{
201 struct com20020_dev_t *info = link->priv;
Dominik Brodowskib4635812005-11-14 21:25:35 +0100202 struct net_device *dev = info->dev;
203
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204 DEBUG(1,"detach...\n");
205
206 DEBUG(0, "com20020_detach(0x%p)\n", link);
207
Dominik Brodowskifd238232006-03-05 10:45:09 +0100208 if (link->dev_node) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209 DEBUG(1,"unregister...\n");
210
211 unregister_netdev(dev);
Dominik Brodowskib4635812005-11-14 21:25:35 +0100212
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213 /*
214 * this is necessary because we register our IRQ separately
215 * from card services.
216 */
217 if (dev->irq)
218 free_irq(dev->irq, dev);
219 }
220
221 if (link->state & DEV_CONFIG)
222 com20020_release(link);
223
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224 /* Unlink device structure, free bits */
225 DEBUG(1,"unlinking...\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226 if (link->priv)
227 {
228 dev = info->dev;
229 if (dev)
230 {
231 DEBUG(1,"kfree...\n");
232 free_netdev(dev);
233 }
234 DEBUG(1,"kfree2...\n");
235 kfree(info);
236 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237
238} /* com20020_detach */
239
240/*======================================================================
241
242 com20020_config() is scheduled to run after a CARD_INSERTION event
243 is received, to configure the PCMCIA socket, and to make the
244 device available to the system.
245
246======================================================================*/
247
248#define CS_CHECK(fn, ret) \
249do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
250
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200251static int com20020_config(struct pcmcia_device *link)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252{
253 struct arcnet_local *lp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254 tuple_t tuple;
255 cisparse_t parse;
256 com20020_dev_t *info;
257 struct net_device *dev;
258 int i, last_ret, last_fn;
259 u_char buf[64];
260 int ioaddr;
261
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262 info = link->priv;
263 dev = info->dev;
264
265 DEBUG(1,"config...\n");
266
267 DEBUG(0, "com20020_config(0x%p)\n", link);
268
269 tuple.Attributes = 0;
270 tuple.TupleData = buf;
271 tuple.TupleDataMax = 64;
272 tuple.TupleOffset = 0;
273 tuple.DesiredTuple = CISTPL_CONFIG;
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200274 CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
275 CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple));
276 CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277 link->conf.ConfigBase = parse.config.base;
278
279 /* Configure card */
280 link->state |= DEV_CONFIG;
281
282 DEBUG(1,"arcnet: baseport1 is %Xh\n", link->io.BasePort1);
283 i = !CS_SUCCESS;
284 if (!link->io.BasePort1)
285 {
286 for (ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x10)
287 {
288 link->io.BasePort1 = ioaddr;
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200289 i = pcmcia_request_io(link, &link->io);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290 if (i == CS_SUCCESS)
291 break;
292 }
293 }
294 else
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200295 i = pcmcia_request_io(link, &link->io);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296
297 if (i != CS_SUCCESS)
298 {
299 DEBUG(1,"arcnet: requestIO failed totally!\n");
300 goto failed;
301 }
302
303 ioaddr = dev->base_addr = link->io.BasePort1;
304 DEBUG(1,"arcnet: got ioaddr %Xh\n", ioaddr);
305
306 DEBUG(1,"arcnet: request IRQ %d (%Xh/%Xh)\n",
307 link->irq.AssignedIRQ,
308 link->irq.IRQInfo1, link->irq.IRQInfo2);
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200309 i = pcmcia_request_irq(link, &link->irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310 if (i != CS_SUCCESS)
311 {
312 DEBUG(1,"arcnet: requestIRQ failed totally!\n");
313 goto failed;
314 }
315
316 dev->irq = link->irq.AssignedIRQ;
317
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200318 CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319
320 if (com20020_check(dev))
321 {
322 regdump(dev);
323 goto failed;
324 }
325
326 lp = dev->priv;
327 lp->card_name = "PCMCIA COM20020";
328 lp->card_flags = ARC_CAN_10MBIT; /* pretend all of them can 10Mbit */
329
Dominik Brodowskifd238232006-03-05 10:45:09 +0100330 link->dev_node = &info->node;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331 link->state &= ~DEV_CONFIG_PENDING;
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200332 SET_NETDEV_DEV(dev, &handle_to_dev(link));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333
334 i = com20020_found(dev, 0); /* calls register_netdev */
335
336 if (i != 0) {
337 DEBUG(1,KERN_NOTICE "com20020_cs: com20020_found() failed\n");
Dominik Brodowskifd238232006-03-05 10:45:09 +0100338 link->dev_node = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339 goto failed;
340 }
341
342 strcpy(info->node.dev_name, dev->name);
343
344 DEBUG(1,KERN_INFO "%s: port %#3lx, irq %d\n",
345 dev->name, dev->base_addr, dev->irq);
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200346 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347
348cs_failed:
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200349 cs_error(link, last_fn, last_ret);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350failed:
351 DEBUG(1,"com20020_config failed...\n");
352 com20020_release(link);
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200353 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354} /* com20020_config */
355
356/*======================================================================
357
358 After a card is removed, com20020_release() will unregister the net
359 device, and release the PCMCIA configuration. If the device is
360 still open, this will be postponed until it is closed.
361
362======================================================================*/
363
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200364static void com20020_release(struct pcmcia_device *link)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365{
Dominik Brodowski5f2a71f2006-01-15 09:32:39 +0100366 DEBUG(0, "com20020_release(0x%p)\n", link);
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200367 pcmcia_disable_device(link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368}
369
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200370static int com20020_suspend(struct pcmcia_device *link)
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100371{
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100372 com20020_dev_t *info = link->priv;
373 struct net_device *dev = info->dev;
374
Dominik Brodowski8661bb52006-03-02 00:02:33 +0100375 if ((link->state & DEV_CONFIG) && (link->open))
376 netif_device_detach(dev);
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100377
378 return 0;
379}
380
Dominik Brodowskifba395e2006-03-31 17:21:06 +0200381static int com20020_resume(struct pcmcia_device *link)
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100382{
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100383 com20020_dev_t *info = link->priv;
384 struct net_device *dev = info->dev;
385
Dominik Brodowski8661bb52006-03-02 00:02:33 +0100386 if ((link->state & DEV_CONFIG) && (link->open)) {
387 int ioaddr = dev->base_addr;
388 struct arcnet_local *lp = dev->priv;
389 ARCRESET;
390 }
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100391
392 return 0;
393}
394
Dominik Brodowski7fb22bb2005-06-27 16:28:38 -0700395static struct pcmcia_device_id com20020_ids[] = {
396 PCMCIA_DEVICE_PROD_ID12("Contemporary Control Systems, Inc.", "PCM20 Arcnet Adapter", 0x59991666, 0x95dfffaf),
397 PCMCIA_DEVICE_NULL
398};
399MODULE_DEVICE_TABLE(pcmcia, com20020_ids);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400
401static struct pcmcia_driver com20020_cs_driver = {
402 .owner = THIS_MODULE,
403 .drv = {
404 .name = "com20020_cs",
405 },
Dominik Brodowski15b99ac2006-03-31 17:26:06 +0200406 .probe = com20020_probe,
Dominik Brodowskicc3b4862005-11-14 21:23:14 +0100407 .remove = com20020_detach,
Dominik Brodowski7fb22bb2005-06-27 16:28:38 -0700408 .id_table = com20020_ids,
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100409 .suspend = com20020_suspend,
410 .resume = com20020_resume,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411};
412
413static int __init init_com20020_cs(void)
414{
415 return pcmcia_register_driver(&com20020_cs_driver);
416}
417
418static void __exit exit_com20020_cs(void)
419{
420 pcmcia_unregister_driver(&com20020_cs_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421}
422
423module_init(init_com20020_cs);
424module_exit(exit_com20020_cs);