cc770_isa.c revision edd2c26ffb7742bf0d3bd324694d220281844a01
1/*
2 * Driver for CC770 and AN82527 CAN controllers on the legacy ISA bus
3 *
4 * Copyright (C) 2009, 2011 Wolfgang Grandegger <wg@grandegger.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the version 2 of the GNU General Public License
8 * as published by the Free Software Foundation
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 */
15
16/*
17 * Bosch CC770 and Intel AN82527 CAN controllers on the ISA or PC-104 bus.
18 * The I/O port or memory address and the IRQ number must be specified via
19 * module parameters:
20 *
21 *   insmod cc770_isa.ko port=0x310,0x380 irq=7,11
22 *
23 * for ISA devices using I/O ports or:
24 *
25 *   insmod cc770_isa.ko mem=0xd1000,0xd1000 irq=7,11
26 *
27 * for memory mapped ISA devices.
28 *
29 * Indirect access via address and data port is supported as well:
30 *
31 *   insmod cc770_isa.ko port=0x310,0x380 indirect=1 irq=7,11
32 *
33 * Furthermore, the following mode parameter can be defined:
34 *
35 *   clk: External oscillator clock frequency (default=16000000 [16 MHz])
36 *   cir: CPU interface register (default=0x40 [DSC])
37 *   bcr: Bus configuration register (default=0x40 [CBY])
38 *   cor: Clockout register (default=0x00)
39 *
40 * Note: for clk, cir, bcr and cor, the first argument re-defines the
41 * default for all other devices, e.g.:
42 *
43 *   insmod cc770_isa.ko mem=0xd1000,0xd1000 irq=7,11 clk=24000000
44 *
45 * is equivalent to
46 *
47 *   insmod cc770_isa.ko mem=0xd1000,0xd1000 irq=7,11 clk=24000000,24000000
48 */
49
50#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
51
52#include <linux/kernel.h>
53#include <linux/module.h>
54#include <linux/platform_device.h>
55#include <linux/interrupt.h>
56#include <linux/netdevice.h>
57#include <linux/delay.h>
58#include <linux/irq.h>
59#include <linux/io.h>
60#include <linux/can.h>
61#include <linux/can/dev.h>
62#include <linux/can/platform/cc770.h>
63
64#include "cc770.h"
65
66#define MAXDEV 8
67
68MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>");
69MODULE_DESCRIPTION("Socket-CAN driver for CC770 on the ISA bus");
70MODULE_LICENSE("GPL v2");
71
72#define CLK_DEFAULT	16000000	/* 16 MHz */
73#define COR_DEFAULT	0x00
74#define BCR_DEFAULT	BUSCFG_CBY
75
76static unsigned long port[MAXDEV];
77static unsigned long mem[MAXDEV];
78static int __devinitdata irq[MAXDEV];
79static int __devinitdata clk[MAXDEV];
80static u8 __devinitdata cir[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff};
81static u8 __devinitdata cor[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff};
82static u8 __devinitdata bcr[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff};
83static int __devinitdata indirect[MAXDEV] = {[0 ... (MAXDEV - 1)] = -1};
84
85module_param_array(port, ulong, NULL, S_IRUGO);
86MODULE_PARM_DESC(port, "I/O port number");
87
88module_param_array(mem, ulong, NULL, S_IRUGO);
89MODULE_PARM_DESC(mem, "I/O memory address");
90
91module_param_array(indirect, int, NULL, S_IRUGO);
92MODULE_PARM_DESC(indirect, "Indirect access via address and data port");
93
94module_param_array(irq, int, NULL, S_IRUGO);
95MODULE_PARM_DESC(irq, "IRQ number");
96
97module_param_array(clk, int, NULL, S_IRUGO);
98MODULE_PARM_DESC(clk, "External oscillator clock frequency "
99		 "(default=16000000 [16 MHz])");
100
101module_param_array(cir, byte, NULL, S_IRUGO);
102MODULE_PARM_DESC(cir, "CPU interface register (default=0x40 [DSC])");
103
104module_param_array(cor, byte, NULL, S_IRUGO);
105MODULE_PARM_DESC(cor, "Clockout register (default=0x00)");
106
107module_param_array(bcr, byte, NULL, S_IRUGO);
108MODULE_PARM_DESC(bcr, "Bus configuration register (default=0x40 [CBY])");
109
110#define CC770_IOSIZE          0x20
111#define CC770_IOSIZE_INDIRECT 0x02
112
113static struct platform_device *cc770_isa_devs[MAXDEV];
114
115static u8 cc770_isa_mem_read_reg(const struct cc770_priv *priv, int reg)
116{
117	return readb(priv->reg_base + reg);
118}
119
120static void cc770_isa_mem_write_reg(const struct cc770_priv *priv,
121				      int reg, u8 val)
122{
123	writeb(val, priv->reg_base + reg);
124}
125
126static u8 cc770_isa_port_read_reg(const struct cc770_priv *priv, int reg)
127{
128	return inb((unsigned long)priv->reg_base + reg);
129}
130
131static void cc770_isa_port_write_reg(const struct cc770_priv *priv,
132				       int reg, u8 val)
133{
134	outb(val, (unsigned long)priv->reg_base + reg);
135}
136
137static u8 cc770_isa_port_read_reg_indirect(const struct cc770_priv *priv,
138					     int reg)
139{
140	unsigned long base = (unsigned long)priv->reg_base;
141
142	outb(reg, base);
143	return inb(base + 1);
144}
145
146static void cc770_isa_port_write_reg_indirect(const struct cc770_priv *priv,
147						int reg, u8 val)
148{
149	unsigned long base = (unsigned long)priv->reg_base;
150
151	outb(reg, base);
152	outb(val, base + 1);
153}
154
155static int __devinit cc770_isa_probe(struct platform_device *pdev)
156{
157	struct net_device *dev;
158	struct cc770_priv *priv;
159	void __iomem *base = NULL;
160	int iosize = CC770_IOSIZE;
161	int idx = pdev->id;
162	int err;
163	u32 clktmp;
164
165	dev_dbg(&pdev->dev, "probing idx=%d: port=%#lx, mem=%#lx, irq=%d\n",
166		idx, port[idx], mem[idx], irq[idx]);
167	if (mem[idx]) {
168		if (!request_mem_region(mem[idx], iosize, KBUILD_MODNAME)) {
169			err = -EBUSY;
170			goto exit;
171		}
172		base = ioremap_nocache(mem[idx], iosize);
173		if (!base) {
174			err = -ENOMEM;
175			goto exit_release;
176		}
177	} else {
178		if (indirect[idx] > 0 ||
179		    (indirect[idx] == -1 && indirect[0] > 0))
180			iosize = CC770_IOSIZE_INDIRECT;
181		if (!request_region(port[idx], iosize, KBUILD_MODNAME)) {
182			err = -EBUSY;
183			goto exit;
184		}
185	}
186
187	dev = alloc_cc770dev(0);
188	if (!dev) {
189		err = -ENOMEM;
190		goto exit_unmap;
191	}
192	priv = netdev_priv(dev);
193
194	dev->irq = irq[idx];
195	priv->irq_flags = IRQF_SHARED;
196	if (mem[idx]) {
197		priv->reg_base = base;
198		dev->base_addr = mem[idx];
199		priv->read_reg = cc770_isa_mem_read_reg;
200		priv->write_reg = cc770_isa_mem_write_reg;
201	} else {
202		priv->reg_base = (void __iomem *)port[idx];
203		dev->base_addr = port[idx];
204
205		if (iosize == CC770_IOSIZE_INDIRECT) {
206			priv->read_reg = cc770_isa_port_read_reg_indirect;
207			priv->write_reg = cc770_isa_port_write_reg_indirect;
208		} else {
209			priv->read_reg = cc770_isa_port_read_reg;
210			priv->write_reg = cc770_isa_port_write_reg;
211		}
212	}
213
214	if (clk[idx])
215		clktmp = clk[idx];
216	else if (clk[0])
217		clktmp = clk[0];
218	else
219		clktmp = CLK_DEFAULT;
220	priv->can.clock.freq = clktmp;
221
222	if (cir[idx] != 0xff) {
223		priv->cpu_interface = cir[idx];
224	} else if (cir[0] != 0xff) {
225		priv->cpu_interface = cir[0];
226	} else {
227		/* The system clock may not exceed 10 MHz */
228		if (clktmp > 10000000) {
229			priv->cpu_interface |= CPUIF_DSC;
230			clktmp /= 2;
231		}
232		/* The memory clock may not exceed 8 MHz */
233		if (clktmp > 8000000)
234			priv->cpu_interface |= CPUIF_DMC;
235	}
236
237	if (priv->cpu_interface & CPUIF_DSC)
238		priv->can.clock.freq /= 2;
239
240	if (bcr[idx] != 0xff)
241		priv->bus_config = bcr[idx];
242	else if (bcr[0] != 0xff)
243		priv->bus_config = bcr[0];
244	else
245		priv->bus_config = BCR_DEFAULT;
246
247	if (cor[idx] != 0xff)
248		priv->clkout = cor[idx];
249	else if (cor[0] != 0xff)
250		priv->clkout = cor[0];
251	else
252		priv->clkout = COR_DEFAULT;
253
254	dev_set_drvdata(&pdev->dev, dev);
255	SET_NETDEV_DEV(dev, &pdev->dev);
256
257	err = register_cc770dev(dev);
258	if (err) {
259		dev_err(&pdev->dev,
260			"couldn't register device (err=%d)\n", err);
261		goto exit_unmap;
262	}
263
264	dev_info(&pdev->dev, "device registered (reg_base=0x%p, irq=%d)\n",
265		 priv->reg_base, dev->irq);
266	return 0;
267
268 exit_unmap:
269	if (mem[idx])
270		iounmap(base);
271 exit_release:
272	if (mem[idx])
273		release_mem_region(mem[idx], iosize);
274	else
275		release_region(port[idx], iosize);
276 exit:
277	return err;
278}
279
280static int __devexit cc770_isa_remove(struct platform_device *pdev)
281{
282	struct net_device *dev = dev_get_drvdata(&pdev->dev);
283	struct cc770_priv *priv = netdev_priv(dev);
284	int idx = pdev->id;
285
286	unregister_cc770dev(dev);
287	dev_set_drvdata(&pdev->dev, NULL);
288
289	if (mem[idx]) {
290		iounmap(priv->reg_base);
291		release_mem_region(mem[idx], CC770_IOSIZE);
292	} else {
293		if (priv->read_reg == cc770_isa_port_read_reg_indirect)
294			release_region(port[idx], CC770_IOSIZE_INDIRECT);
295		else
296			release_region(port[idx], CC770_IOSIZE);
297	}
298	free_cc770dev(dev);
299
300	return 0;
301}
302
303static struct platform_driver cc770_isa_driver = {
304	.probe = cc770_isa_probe,
305	.remove = __devexit_p(cc770_isa_remove),
306	.driver = {
307		.name = KBUILD_MODNAME,
308		.owner = THIS_MODULE,
309	},
310};
311
312static int __init cc770_isa_init(void)
313{
314	int idx, err;
315
316	for (idx = 0; idx < ARRAY_SIZE(cc770_isa_devs); idx++) {
317		if ((port[idx] || mem[idx]) && irq[idx]) {
318			cc770_isa_devs[idx] =
319				platform_device_alloc(KBUILD_MODNAME, idx);
320			if (!cc770_isa_devs[idx]) {
321				err = -ENOMEM;
322				goto exit_free_devices;
323			}
324			err = platform_device_add(cc770_isa_devs[idx]);
325			if (err) {
326				platform_device_put(cc770_isa_devs[idx]);
327				goto exit_free_devices;
328			}
329			pr_debug("platform device %d: port=%#lx, mem=%#lx, "
330				 "irq=%d\n",
331				 idx, port[idx], mem[idx], irq[idx]);
332		} else if (idx == 0 || port[idx] || mem[idx]) {
333			pr_err("insufficient parameters supplied\n");
334			err = -EINVAL;
335			goto exit_free_devices;
336		}
337	}
338
339	err = platform_driver_register(&cc770_isa_driver);
340	if (err)
341		goto exit_free_devices;
342
343	pr_info("driver for max. %d devices registered\n", MAXDEV);
344
345	return 0;
346
347exit_free_devices:
348	while (--idx >= 0) {
349		if (cc770_isa_devs[idx])
350			platform_device_unregister(cc770_isa_devs[idx]);
351	}
352
353	return err;
354}
355module_init(cc770_isa_init);
356
357static void __exit cc770_isa_exit(void)
358{
359	int idx;
360
361	platform_driver_unregister(&cc770_isa_driver);
362	for (idx = 0; idx < ARRAY_SIZE(cc770_isa_devs); idx++) {
363		if (cc770_isa_devs[idx])
364			platform_device_unregister(cc770_isa_devs[idx]);
365	}
366}
367module_exit(cc770_isa_exit);
368