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