Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Wolfgang Grandegger and committed by
David S. Miller
edd2c26f 2a367c3a

+379
+11
drivers/net/can/cc770/Kconfig
··· 1 1 menuconfig CAN_CC770 2 2 tristate "Bosch CC770 and Intel AN82527 devices" 3 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
+1
drivers/net/can/cc770/Makefile
··· 3 3 # 4 4 5 5 obj-$(CONFIG_CAN_CC770) += cc770.o 6 + obj-$(CONFIG_CAN_CC770_ISA) += cc770_isa.o 6 7 7 8 ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
+367
drivers/net/can/cc770/cc770_isa.c
··· 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);