xref: /openbmc/linux/drivers/net/arcnet/com20020_cs.c (revision 7ae9fb1b7ecbb5d85d07857943f677fd1a559b18)
1330278cdSJeff Kirsher /*
2330278cdSJeff Kirsher  * Linux ARCnet driver - COM20020 PCMCIA support
3330278cdSJeff Kirsher  *
4330278cdSJeff Kirsher  * Written 1994-1999 by Avery Pennarun,
5330278cdSJeff Kirsher  *    based on an ISA version by David Woodhouse.
6330278cdSJeff Kirsher  * Derived from ibmtr_cs.c by Steve Kipisz (pcmcia-cs 3.1.4)
7330278cdSJeff Kirsher  *    which was derived from pcnet_cs.c by David Hinds.
8330278cdSJeff Kirsher  * Some additional portions derived from skeleton.c by Donald Becker.
9330278cdSJeff Kirsher  *
10330278cdSJeff Kirsher  * Special thanks to Contemporary Controls, Inc. (www.ccontrols.com)
11330278cdSJeff Kirsher  *  for sponsoring the further development of this driver.
12330278cdSJeff Kirsher  *
13330278cdSJeff Kirsher  * **********************
14330278cdSJeff Kirsher  *
15330278cdSJeff Kirsher  * The original copyright of skeleton.c was as follows:
16330278cdSJeff Kirsher  *
17330278cdSJeff Kirsher  * skeleton.c Written 1993 by Donald Becker.
18330278cdSJeff Kirsher  * Copyright 1993 United States Government as represented by the
19330278cdSJeff Kirsher  * Director, National Security Agency.  This software may only be used
20330278cdSJeff Kirsher  * and distributed according to the terms of the GNU General Public License as
21330278cdSJeff Kirsher  * modified by SRC, incorporated herein by reference.
22330278cdSJeff Kirsher  *
23330278cdSJeff Kirsher  * **********************
24330278cdSJeff Kirsher  * Changes:
25330278cdSJeff Kirsher  * Arnaldo Carvalho de Melo <acme@conectiva.com.br> - 08/08/2000
26330278cdSJeff Kirsher  * - reorganize kmallocs in com20020_attach, checking all for failure
27330278cdSJeff Kirsher  *   and releasing the previous allocations if one fails
28330278cdSJeff Kirsher  * **********************
29330278cdSJeff Kirsher  *
30330278cdSJeff Kirsher  * For more details, see drivers/net/arcnet.c
31330278cdSJeff Kirsher  *
32330278cdSJeff Kirsher  * **********************
33330278cdSJeff Kirsher  */
3405a24b23SJoe Perches 
3505a24b23SJoe Perches #define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt
3605a24b23SJoe Perches 
37330278cdSJeff Kirsher #include <linux/kernel.h>
38330278cdSJeff Kirsher #include <linux/ptrace.h>
39330278cdSJeff Kirsher #include <linux/slab.h>
40330278cdSJeff Kirsher #include <linux/string.h>
41330278cdSJeff Kirsher #include <linux/timer.h>
42330278cdSJeff Kirsher #include <linux/delay.h>
43330278cdSJeff Kirsher #include <linux/module.h>
44330278cdSJeff Kirsher #include <linux/netdevice.h>
4526c6d281SJoe Perches #include <linux/io.h>
46330278cdSJeff Kirsher #include <pcmcia/cistpl.h>
47330278cdSJeff Kirsher #include <pcmcia/ds.h>
48330278cdSJeff Kirsher 
4926c6d281SJoe Perches #include "arcdevice.h"
5026c6d281SJoe Perches #include "com20020.h"
51330278cdSJeff Kirsher 
regdump(struct net_device * dev)52330278cdSJeff Kirsher static void regdump(struct net_device *dev)
53330278cdSJeff Kirsher {
54330278cdSJeff Kirsher #ifdef DEBUG
55330278cdSJeff Kirsher 	int ioaddr = dev->base_addr;
56330278cdSJeff Kirsher 	int count;
57330278cdSJeff Kirsher 
58330278cdSJeff Kirsher 	netdev_dbg(dev, "register dump:\n");
590fec6513SJoe Perches 	for (count = 0; count < 16; count++) {
60330278cdSJeff Kirsher 		if (!(count % 16))
610fec6513SJoe Perches 			pr_cont("%04X:", ioaddr + count);
620fec6513SJoe Perches 		pr_cont(" %02X", arcnet_inb(ioaddr, count));
63330278cdSJeff Kirsher 	}
64330278cdSJeff Kirsher 	pr_cont("\n");
65330278cdSJeff Kirsher 
66330278cdSJeff Kirsher 	netdev_dbg(dev, "buffer0 dump:\n");
67330278cdSJeff Kirsher 	/* set up the address register */
68330278cdSJeff Kirsher 	count = 0;
690fec6513SJoe Perches 	arcnet_outb((count >> 8) | RDDATAflag | AUTOINCflag,
707cfabe4fSTom Rix 		    ioaddr, COM20020_REG_W_ADDR_HI);
710fec6513SJoe Perches 	arcnet_outb(count & 0xff, ioaddr, COM20020_REG_W_ADDR_LO);
72330278cdSJeff Kirsher 
737f5e760cSJoe Perches 	for (count = 0; count < 256 + 32; count++) {
74330278cdSJeff Kirsher 		if (!(count % 16))
75330278cdSJeff Kirsher 			pr_cont("%04X:", count);
76330278cdSJeff Kirsher 
77330278cdSJeff Kirsher 		/* copy the data */
780fec6513SJoe Perches 		pr_cont(" %02X", arcnet_inb(ioaddr, COM20020_REG_RW_MEMDATA));
79330278cdSJeff Kirsher 	}
80330278cdSJeff Kirsher 	pr_cont("\n");
81330278cdSJeff Kirsher #endif
82330278cdSJeff Kirsher }
83330278cdSJeff Kirsher 
84330278cdSJeff Kirsher /*====================================================================*/
85330278cdSJeff Kirsher 
86330278cdSJeff Kirsher /* Parameters that can be set with 'insmod' */
87330278cdSJeff Kirsher 
88330278cdSJeff Kirsher static int node;
89330278cdSJeff Kirsher static int timeout = 3;
90330278cdSJeff Kirsher static int backplane;
91330278cdSJeff Kirsher static int clockp;
92330278cdSJeff Kirsher static int clockm;
93330278cdSJeff Kirsher 
94330278cdSJeff Kirsher module_param(node, int, 0);
95330278cdSJeff Kirsher module_param(timeout, int, 0);
96330278cdSJeff Kirsher module_param(backplane, int, 0);
97330278cdSJeff Kirsher module_param(clockp, int, 0);
98330278cdSJeff Kirsher module_param(clockm, int, 0);
99330278cdSJeff Kirsher 
100330278cdSJeff Kirsher MODULE_LICENSE("GPL");
101330278cdSJeff Kirsher 
102330278cdSJeff Kirsher /*====================================================================*/
103330278cdSJeff Kirsher 
104330278cdSJeff Kirsher static int com20020_config(struct pcmcia_device *link);
105330278cdSJeff Kirsher static void com20020_release(struct pcmcia_device *link);
106330278cdSJeff Kirsher 
107330278cdSJeff Kirsher static void com20020_detach(struct pcmcia_device *p_dev);
108330278cdSJeff Kirsher 
109330278cdSJeff Kirsher /*====================================================================*/
110330278cdSJeff Kirsher 
com20020_probe(struct pcmcia_device * p_dev)111330278cdSJeff Kirsher static int com20020_probe(struct pcmcia_device *p_dev)
112330278cdSJeff Kirsher {
1132dfd2533SHimangi Saraogi 	struct com20020_dev *info;
114330278cdSJeff Kirsher 	struct net_device *dev;
115330278cdSJeff Kirsher 	struct arcnet_local *lp;
116*1c40cde6SWang Hai 	int ret = -ENOMEM;
117330278cdSJeff Kirsher 
118330278cdSJeff Kirsher 	dev_dbg(&p_dev->dev, "com20020_attach()\n");
119330278cdSJeff Kirsher 
120330278cdSJeff Kirsher 	/* Create new network device */
1212dfd2533SHimangi Saraogi 	info = kzalloc(sizeof(*info), GFP_KERNEL);
122330278cdSJeff Kirsher 	if (!info)
123330278cdSJeff Kirsher 		goto fail_alloc_info;
124330278cdSJeff Kirsher 
125330278cdSJeff Kirsher 	dev = alloc_arcdev("");
126330278cdSJeff Kirsher 	if (!dev)
127330278cdSJeff Kirsher 		goto fail_alloc_dev;
128330278cdSJeff Kirsher 
129330278cdSJeff Kirsher 	lp = netdev_priv(dev);
130330278cdSJeff Kirsher 	lp->timeout = timeout;
131330278cdSJeff Kirsher 	lp->backplane = backplane;
132330278cdSJeff Kirsher 	lp->clockp = clockp;
133330278cdSJeff Kirsher 	lp->clockm = clockm & 3;
134330278cdSJeff Kirsher 	lp->hw.owner = THIS_MODULE;
135330278cdSJeff Kirsher 
136330278cdSJeff Kirsher 	/* fill in our module parameters as defaults */
13713b5ffa0SJakub Kicinski 	arcnet_set_addr(dev, node);
138330278cdSJeff Kirsher 
139330278cdSJeff Kirsher 	p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
140330278cdSJeff Kirsher 	p_dev->resource[0]->end = 16;
141330278cdSJeff Kirsher 	p_dev->config_flags |= CONF_ENABLE_IRQ;
142330278cdSJeff Kirsher 
143330278cdSJeff Kirsher 	info->dev = dev;
144330278cdSJeff Kirsher 	p_dev->priv = info;
145330278cdSJeff Kirsher 
146*1c40cde6SWang Hai 	ret = com20020_config(p_dev);
147*1c40cde6SWang Hai 	if (ret)
148*1c40cde6SWang Hai 		goto fail_config;
149330278cdSJeff Kirsher 
150*1c40cde6SWang Hai 	return 0;
151*1c40cde6SWang Hai 
152*1c40cde6SWang Hai fail_config:
153*1c40cde6SWang Hai 	free_arcdev(dev);
154330278cdSJeff Kirsher fail_alloc_dev:
155330278cdSJeff Kirsher 	kfree(info);
156330278cdSJeff Kirsher fail_alloc_info:
157*1c40cde6SWang Hai 	return ret;
158330278cdSJeff Kirsher } /* com20020_attach */
159330278cdSJeff Kirsher 
com20020_detach(struct pcmcia_device * link)160330278cdSJeff Kirsher static void com20020_detach(struct pcmcia_device *link)
161330278cdSJeff Kirsher {
1622dfd2533SHimangi Saraogi 	struct com20020_dev *info = link->priv;
163330278cdSJeff Kirsher 	struct net_device *dev = info->dev;
164330278cdSJeff Kirsher 
165330278cdSJeff Kirsher 	dev_dbg(&link->dev, "detach...\n");
166330278cdSJeff Kirsher 
167330278cdSJeff Kirsher 	dev_dbg(&link->dev, "com20020_detach\n");
168330278cdSJeff Kirsher 
169330278cdSJeff Kirsher 	dev_dbg(&link->dev, "unregister...\n");
170330278cdSJeff Kirsher 
171330278cdSJeff Kirsher 	unregister_netdev(dev);
172330278cdSJeff Kirsher 
173f2f0a16bSJoe Perches 	/* this is necessary because we register our IRQ separately
174330278cdSJeff Kirsher 	 * from card services.
175330278cdSJeff Kirsher 	 */
176330278cdSJeff Kirsher 	if (dev->irq)
177330278cdSJeff Kirsher 		free_irq(dev->irq, dev);
178330278cdSJeff Kirsher 
179330278cdSJeff Kirsher 	com20020_release(link);
180330278cdSJeff Kirsher 
181330278cdSJeff Kirsher 	/* Unlink device structure, free bits */
182330278cdSJeff Kirsher 	dev_dbg(&link->dev, "unlinking...\n");
1837f5e760cSJoe Perches 	if (link->priv) {
184330278cdSJeff Kirsher 		dev = info->dev;
1857f5e760cSJoe Perches 		if (dev) {
186330278cdSJeff Kirsher 			dev_dbg(&link->dev, "kfree...\n");
18701365633SAhmed S. Darwish 			free_arcdev(dev);
188330278cdSJeff Kirsher 		}
189330278cdSJeff Kirsher 		dev_dbg(&link->dev, "kfree2...\n");
190330278cdSJeff Kirsher 		kfree(info);
191330278cdSJeff Kirsher 	}
192330278cdSJeff Kirsher 
193330278cdSJeff Kirsher } /* com20020_detach */
194330278cdSJeff Kirsher 
com20020_config(struct pcmcia_device * link)195330278cdSJeff Kirsher static int com20020_config(struct pcmcia_device *link)
196330278cdSJeff Kirsher {
197330278cdSJeff Kirsher 	struct arcnet_local *lp;
1982dfd2533SHimangi Saraogi 	struct com20020_dev *info;
199330278cdSJeff Kirsher 	struct net_device *dev;
200330278cdSJeff Kirsher 	int i, ret;
201330278cdSJeff Kirsher 	int ioaddr;
202330278cdSJeff Kirsher 
203330278cdSJeff Kirsher 	info = link->priv;
204330278cdSJeff Kirsher 	dev = info->dev;
205330278cdSJeff Kirsher 
206330278cdSJeff Kirsher 	dev_dbg(&link->dev, "config...\n");
207330278cdSJeff Kirsher 
208330278cdSJeff Kirsher 	dev_dbg(&link->dev, "com20020_config\n");
209330278cdSJeff Kirsher 
210330278cdSJeff Kirsher 	dev_dbg(&link->dev, "baseport1 is %Xh\n",
211330278cdSJeff Kirsher 		(unsigned int)link->resource[0]->start);
212330278cdSJeff Kirsher 
213330278cdSJeff Kirsher 	i = -ENODEV;
214330278cdSJeff Kirsher 	link->io_lines = 16;
215330278cdSJeff Kirsher 
2167f5e760cSJoe Perches 	if (!link->resource[0]->start) {
2177f5e760cSJoe Perches 		for (ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x10) {
218330278cdSJeff Kirsher 			link->resource[0]->start = ioaddr;
219330278cdSJeff Kirsher 			i = pcmcia_request_io(link);
220330278cdSJeff Kirsher 			if (i == 0)
221330278cdSJeff Kirsher 				break;
222330278cdSJeff Kirsher 		}
2237f5e760cSJoe Perches 	} else {
224330278cdSJeff Kirsher 		i = pcmcia_request_io(link);
2257f5e760cSJoe Perches 	}
226330278cdSJeff Kirsher 
2277f5e760cSJoe Perches 	if (i != 0) {
228330278cdSJeff Kirsher 		dev_dbg(&link->dev, "requestIO failed totally!\n");
229330278cdSJeff Kirsher 		goto failed;
230330278cdSJeff Kirsher 	}
231330278cdSJeff Kirsher 
232330278cdSJeff Kirsher 	ioaddr = dev->base_addr = link->resource[0]->start;
233330278cdSJeff Kirsher 	dev_dbg(&link->dev, "got ioaddr %Xh\n", ioaddr);
234330278cdSJeff Kirsher 
235330278cdSJeff Kirsher 	dev_dbg(&link->dev, "request IRQ %d\n",
236330278cdSJeff Kirsher 		link->irq);
2377f5e760cSJoe Perches 	if (!link->irq) {
238330278cdSJeff Kirsher 		dev_dbg(&link->dev, "requestIRQ failed totally!\n");
239330278cdSJeff Kirsher 		goto failed;
240330278cdSJeff Kirsher 	}
241330278cdSJeff Kirsher 
242330278cdSJeff Kirsher 	dev->irq = link->irq;
243330278cdSJeff Kirsher 
244330278cdSJeff Kirsher 	ret = pcmcia_enable_device(link);
245330278cdSJeff Kirsher 	if (ret)
246330278cdSJeff Kirsher 		goto failed;
247330278cdSJeff Kirsher 
2487f5e760cSJoe Perches 	if (com20020_check(dev)) {
249330278cdSJeff Kirsher 		regdump(dev);
250330278cdSJeff Kirsher 		goto failed;
251330278cdSJeff Kirsher 	}
252330278cdSJeff Kirsher 
253330278cdSJeff Kirsher 	lp = netdev_priv(dev);
254330278cdSJeff Kirsher 	lp->card_name = "PCMCIA COM20020";
255330278cdSJeff Kirsher 	lp->card_flags = ARC_CAN_10MBIT; /* pretend all of them can 10Mbit */
256330278cdSJeff Kirsher 
257330278cdSJeff Kirsher 	SET_NETDEV_DEV(dev, &link->dev);
258330278cdSJeff Kirsher 
259330278cdSJeff Kirsher 	i = com20020_found(dev, 0);	/* calls register_netdev */
260330278cdSJeff Kirsher 
261330278cdSJeff Kirsher 	if (i != 0) {
262330278cdSJeff Kirsher 		dev_notice(&link->dev,
263330278cdSJeff Kirsher 			   "com20020_found() failed\n");
264330278cdSJeff Kirsher 		goto failed;
265330278cdSJeff Kirsher 	}
266330278cdSJeff Kirsher 
267330278cdSJeff Kirsher 	netdev_dbg(dev, "port %#3lx, irq %d\n",
268330278cdSJeff Kirsher 		   dev->base_addr, dev->irq);
269330278cdSJeff Kirsher 	return 0;
270330278cdSJeff Kirsher 
271330278cdSJeff Kirsher failed:
272330278cdSJeff Kirsher 	dev_dbg(&link->dev, "com20020_config failed...\n");
273330278cdSJeff Kirsher 	com20020_release(link);
274330278cdSJeff Kirsher 	return -ENODEV;
275330278cdSJeff Kirsher } /* com20020_config */
276330278cdSJeff Kirsher 
com20020_release(struct pcmcia_device * link)277330278cdSJeff Kirsher static void com20020_release(struct pcmcia_device *link)
278330278cdSJeff Kirsher {
279330278cdSJeff Kirsher 	dev_dbg(&link->dev, "com20020_release\n");
280330278cdSJeff Kirsher 	pcmcia_disable_device(link);
281330278cdSJeff Kirsher }
282330278cdSJeff Kirsher 
com20020_suspend(struct pcmcia_device * link)283330278cdSJeff Kirsher static int com20020_suspend(struct pcmcia_device *link)
284330278cdSJeff Kirsher {
2852dfd2533SHimangi Saraogi 	struct com20020_dev *info = link->priv;
286330278cdSJeff Kirsher 	struct net_device *dev = info->dev;
287330278cdSJeff Kirsher 
288330278cdSJeff Kirsher 	if (link->open)
289330278cdSJeff Kirsher 		netif_device_detach(dev);
290330278cdSJeff Kirsher 
291330278cdSJeff Kirsher 	return 0;
292330278cdSJeff Kirsher }
293330278cdSJeff Kirsher 
com20020_resume(struct pcmcia_device * link)294330278cdSJeff Kirsher static int com20020_resume(struct pcmcia_device *link)
295330278cdSJeff Kirsher {
2962dfd2533SHimangi Saraogi 	struct com20020_dev *info = link->priv;
297330278cdSJeff Kirsher 	struct net_device *dev = info->dev;
298330278cdSJeff Kirsher 
299330278cdSJeff Kirsher 	if (link->open) {
300330278cdSJeff Kirsher 		int ioaddr = dev->base_addr;
301330278cdSJeff Kirsher 		struct arcnet_local *lp = netdev_priv(dev);
30201a1d5acSJoe Perches 
3030fec6513SJoe Perches 		arcnet_outb(lp->config | 0x80, ioaddr, COM20020_REG_W_CONFIG);
3040fec6513SJoe Perches 		udelay(5);
3050fec6513SJoe Perches 		arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG);
306330278cdSJeff Kirsher 	}
307330278cdSJeff Kirsher 
308330278cdSJeff Kirsher 	return 0;
309330278cdSJeff Kirsher }
310330278cdSJeff Kirsher 
311330278cdSJeff Kirsher static const struct pcmcia_device_id com20020_ids[] = {
312330278cdSJeff Kirsher 	PCMCIA_DEVICE_PROD_ID12("Contemporary Control Systems, Inc.",
313330278cdSJeff Kirsher 				"PCM20 Arcnet Adapter", 0x59991666, 0x95dfffaf),
314330278cdSJeff Kirsher 	PCMCIA_DEVICE_PROD_ID12("SoHard AG",
315330278cdSJeff Kirsher 				"SH ARC PCMCIA", 0xf8991729, 0x69dff0c7),
316330278cdSJeff Kirsher 	PCMCIA_DEVICE_NULL
317330278cdSJeff Kirsher };
318330278cdSJeff Kirsher MODULE_DEVICE_TABLE(pcmcia, com20020_ids);
319330278cdSJeff Kirsher 
320330278cdSJeff Kirsher static struct pcmcia_driver com20020_cs_driver = {
321330278cdSJeff Kirsher 	.owner		= THIS_MODULE,
322330278cdSJeff Kirsher 	.name		= "com20020_cs",
323330278cdSJeff Kirsher 	.probe		= com20020_probe,
324330278cdSJeff Kirsher 	.remove		= com20020_detach,
325330278cdSJeff Kirsher 	.id_table	= com20020_ids,
326330278cdSJeff Kirsher 	.suspend	= com20020_suspend,
327330278cdSJeff Kirsher 	.resume		= com20020_resume,
328330278cdSJeff Kirsher };
329fdd3f29eSH Hartley Sweeten module_pcmcia_driver(com20020_cs_driver);
330