diff options
author | Wolfgang Grandegger <wg@grandegger.com> | 2011-11-30 18:41:19 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-12-01 13:09:31 -0500 |
commit | edd2c26ffb7742bf0d3bd324694d220281844a01 (patch) | |
tree | 33d00224c3cd59439176fb3ca122747c5034f444 /drivers/net/can/cc770 | |
parent | 2a367c3a82557cd11a04949ef2160658987fa772 (diff) |
can: cc770: add legacy ISA bus driver for the CC770 and AN82527
This patch adds support for legacy Bosch CC770 and Intel AN82527 CAN
controllers on the ISA or PC-104 bus. The I/O port or memory address
and the IRQ number must be specified via module parameters:
insmod cc770_isa.ko port=0x310,0x380 irq=7,11
for ISA devices using I/O ports or:
insmod cc770_isa.ko mem=0xd1000,0xd1000 irq=7,11
for memory mapped ISA devices.
Indirect access via address and data port is supported as well:
insmod cc770_isa.ko port=0x310,0x380 indirect=1 irq=7,11
Furthermore, the following mode parameter can be defined:
clk: External oscillator clock frequency (default=16000000 [16 MHz])
cir: CPU interface register (default=0x40 [DSC])
bcr: Bus configuration register (default=0x40 [CBY])
cor: Clockout register (default=0x00)
Note: for clk, cir, bcr and cor, the first argument re-defines the
default for all other devices, e.g.:
insmod cc770_isa.ko mem=0xd1000,0xd1000 irq=7,11 clk=24000000
is equivalent to
insmod cc770_isa.ko mem=0xd1000,0xd1000 irq=7,11 clk=24000000,24000000
Signed-off-by: Wolfgang Grandegger <wg@grandegger.com>
Acked-by: Marc Kleine-Budde <mkl@pengutronix.de>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/can/cc770')
-rw-r--r-- | drivers/net/can/cc770/Kconfig | 11 | ||||
-rw-r--r-- | drivers/net/can/cc770/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/can/cc770/cc770_isa.c | 367 |
3 files changed, 379 insertions, 0 deletions
diff --git a/drivers/net/can/cc770/Kconfig b/drivers/net/can/cc770/Kconfig index 225131b7ac9..28e4d4810f4 100644 --- a/drivers/net/can/cc770/Kconfig +++ b/drivers/net/can/cc770/Kconfig | |||
@@ -1,3 +1,14 @@ | |||
1 | menuconfig CAN_CC770 | 1 | menuconfig CAN_CC770 |
2 | tristate "Bosch CC770 and Intel AN82527 devices" | 2 | tristate "Bosch CC770 and Intel AN82527 devices" |
3 | depends on CAN_DEV && HAS_IOMEM | 3 | depends on CAN_DEV && HAS_IOMEM |
4 | |||
5 | if CAN_CC770 | ||
6 | |||
7 | config CAN_CC770_ISA | ||
8 | tristate "ISA Bus based legacy CC770 driver" | ||
9 | ---help--- | ||
10 | This driver adds legacy support for CC770 and AN82527 chips | ||
11 | connected to the ISA bus using I/O port, memory mapped or | ||
12 | indirect access. | ||
13 | |||
14 | endif | ||
diff --git a/drivers/net/can/cc770/Makefile b/drivers/net/can/cc770/Makefile index 34e81802615..872ecffa80b 100644 --- a/drivers/net/can/cc770/Makefile +++ b/drivers/net/can/cc770/Makefile | |||
@@ -3,5 +3,6 @@ | |||
3 | # | 3 | # |
4 | 4 | ||
5 | obj-$(CONFIG_CAN_CC770) += cc770.o | 5 | obj-$(CONFIG_CAN_CC770) += cc770.o |
6 | obj-$(CONFIG_CAN_CC770_ISA) += cc770_isa.o | ||
6 | 7 | ||
7 | ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG | 8 | ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG |
diff --git a/drivers/net/can/cc770/cc770_isa.c b/drivers/net/can/cc770/cc770_isa.c new file mode 100644 index 00000000000..4be5fe2c40a --- /dev/null +++ b/drivers/net/can/cc770/cc770_isa.c | |||
@@ -0,0 +1,367 @@ | |||
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 | |||
68 | MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>"); | ||
69 | MODULE_DESCRIPTION("Socket-CAN driver for CC770 on the ISA bus"); | ||
70 | MODULE_LICENSE("GPL v2"); | ||
71 | |||
72 | #define CLK_DEFAULT 16000000 /* 16 MHz */ | ||
73 | #define COR_DEFAULT 0x00 | ||
74 | #define BCR_DEFAULT BUSCFG_CBY | ||
75 | |||
76 | static unsigned long port[MAXDEV]; | ||
77 | static unsigned long mem[MAXDEV]; | ||
78 | static int __devinitdata irq[MAXDEV]; | ||
79 | static int __devinitdata clk[MAXDEV]; | ||
80 | static u8 __devinitdata cir[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff}; | ||
81 | static u8 __devinitdata cor[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff}; | ||
82 | static u8 __devinitdata bcr[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff}; | ||
83 | static int __devinitdata indirect[MAXDEV] = {[0 ... (MAXDEV - 1)] = -1}; | ||
84 | |||
85 | module_param_array(port, ulong, NULL, S_IRUGO); | ||
86 | MODULE_PARM_DESC(port, "I/O port number"); | ||
87 | |||
88 | module_param_array(mem, ulong, NULL, S_IRUGO); | ||
89 | MODULE_PARM_DESC(mem, "I/O memory address"); | ||
90 | |||
91 | module_param_array(indirect, int, NULL, S_IRUGO); | ||
92 | MODULE_PARM_DESC(indirect, "Indirect access via address and data port"); | ||
93 | |||
94 | module_param_array(irq, int, NULL, S_IRUGO); | ||
95 | MODULE_PARM_DESC(irq, "IRQ number"); | ||
96 | |||
97 | module_param_array(clk, int, NULL, S_IRUGO); | ||
98 | MODULE_PARM_DESC(clk, "External oscillator clock frequency " | ||
99 | "(default=16000000 [16 MHz])"); | ||
100 | |||
101 | module_param_array(cir, byte, NULL, S_IRUGO); | ||
102 | MODULE_PARM_DESC(cir, "CPU interface register (default=0x40 [DSC])"); | ||
103 | |||
104 | module_param_array(cor, byte, NULL, S_IRUGO); | ||
105 | MODULE_PARM_DESC(cor, "Clockout register (default=0x00)"); | ||
106 | |||
107 | module_param_array(bcr, byte, NULL, S_IRUGO); | ||
108 | MODULE_PARM_DESC(bcr, "Bus configuration register (default=0x40 [CBY])"); | ||
109 | |||
110 | #define CC770_IOSIZE 0x20 | ||
111 | #define CC770_IOSIZE_INDIRECT 0x02 | ||
112 | |||
113 | static struct platform_device *cc770_isa_devs[MAXDEV]; | ||
114 | |||
115 | static u8 cc770_isa_mem_read_reg(const struct cc770_priv *priv, int reg) | ||
116 | { | ||
117 | return readb(priv->reg_base + reg); | ||
118 | } | ||
119 | |||
120 | static 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 | |||
126 | static 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 | |||
131 | static 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 | |||
137 | static 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 | |||
146 | static 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 | |||
155 | static 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 | |||
280 | static 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 | |||
303 | static 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 | |||
312 | static 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 | |||
347 | exit_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 | } | ||
355 | module_init(cc770_isa_init); | ||
356 | |||
357 | static 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 | } | ||
367 | module_exit(cc770_isa_exit); | ||