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 irq[MAXDEV]; 79 static int clk[MAXDEV]; 80 static u8 cir[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff}; 81 static u8 cor[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff}; 82 static u8 bcr[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff}; 83 static int 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 /* Spinlock for cc770_isa_port_write_reg_indirect 114 * and cc770_isa_port_read_reg_indirect 115 */ 116 static DEFINE_SPINLOCK(cc770_isa_port_lock); 117 118 static struct platform_device *cc770_isa_devs[MAXDEV]; 119 120 static u8 cc770_isa_mem_read_reg(const struct cc770_priv *priv, int reg) 121 { 122 return readb(priv->reg_base + reg); 123 } 124 125 static void cc770_isa_mem_write_reg(const struct cc770_priv *priv, 126 int reg, u8 val) 127 { 128 writeb(val, priv->reg_base + reg); 129 } 130 131 static u8 cc770_isa_port_read_reg(const struct cc770_priv *priv, int reg) 132 { 133 return inb((unsigned long)priv->reg_base + reg); 134 } 135 136 static void cc770_isa_port_write_reg(const struct cc770_priv *priv, 137 int reg, u8 val) 138 { 139 outb(val, (unsigned long)priv->reg_base + reg); 140 } 141 142 static u8 cc770_isa_port_read_reg_indirect(const struct cc770_priv *priv, 143 int reg) 144 { 145 unsigned long base = (unsigned long)priv->reg_base; 146 unsigned long flags; 147 u8 val; 148 149 spin_lock_irqsave(&cc770_isa_port_lock, flags); 150 outb(reg, base); 151 val = inb(base + 1); 152 spin_unlock_irqrestore(&cc770_isa_port_lock, flags); 153 154 return val; 155 } 156 157 static void cc770_isa_port_write_reg_indirect(const struct cc770_priv *priv, 158 int reg, u8 val) 159 { 160 unsigned long base = (unsigned long)priv->reg_base; 161 unsigned long flags; 162 163 spin_lock_irqsave(&cc770_isa_port_lock, flags); 164 outb(reg, base); 165 outb(val, base + 1); 166 spin_unlock_irqrestore(&cc770_isa_port_lock, flags); 167 } 168 169 static int cc770_isa_probe(struct platform_device *pdev) 170 { 171 struct net_device *dev; 172 struct cc770_priv *priv; 173 void __iomem *base = NULL; 174 int iosize = CC770_IOSIZE; 175 int idx = pdev->id; 176 int err; 177 u32 clktmp; 178 179 dev_dbg(&pdev->dev, "probing idx=%d: port=%#lx, mem=%#lx, irq=%d\n", 180 idx, port[idx], mem[idx], irq[idx]); 181 if (mem[idx]) { 182 if (!request_mem_region(mem[idx], iosize, KBUILD_MODNAME)) { 183 err = -EBUSY; 184 goto exit; 185 } 186 base = ioremap_nocache(mem[idx], iosize); 187 if (!base) { 188 err = -ENOMEM; 189 goto exit_release; 190 } 191 } else { 192 if (indirect[idx] > 0 || 193 (indirect[idx] == -1 && indirect[0] > 0)) 194 iosize = CC770_IOSIZE_INDIRECT; 195 if (!request_region(port[idx], iosize, KBUILD_MODNAME)) { 196 err = -EBUSY; 197 goto exit; 198 } 199 } 200 201 dev = alloc_cc770dev(0); 202 if (!dev) { 203 err = -ENOMEM; 204 goto exit_unmap; 205 } 206 priv = netdev_priv(dev); 207 208 dev->irq = irq[idx]; 209 priv->irq_flags = IRQF_SHARED; 210 if (mem[idx]) { 211 priv->reg_base = base; 212 dev->base_addr = mem[idx]; 213 priv->read_reg = cc770_isa_mem_read_reg; 214 priv->write_reg = cc770_isa_mem_write_reg; 215 } else { 216 priv->reg_base = (void __iomem *)port[idx]; 217 dev->base_addr = port[idx]; 218 219 if (iosize == CC770_IOSIZE_INDIRECT) { 220 priv->read_reg = cc770_isa_port_read_reg_indirect; 221 priv->write_reg = cc770_isa_port_write_reg_indirect; 222 } else { 223 priv->read_reg = cc770_isa_port_read_reg; 224 priv->write_reg = cc770_isa_port_write_reg; 225 } 226 } 227 228 if (clk[idx]) 229 clktmp = clk[idx]; 230 else if (clk[0]) 231 clktmp = clk[0]; 232 else 233 clktmp = CLK_DEFAULT; 234 priv->can.clock.freq = clktmp; 235 236 if (cir[idx] != 0xff) { 237 priv->cpu_interface = cir[idx]; 238 } else if (cir[0] != 0xff) { 239 priv->cpu_interface = cir[0]; 240 } else { 241 /* The system clock may not exceed 10 MHz */ 242 if (clktmp > 10000000) { 243 priv->cpu_interface |= CPUIF_DSC; 244 clktmp /= 2; 245 } 246 /* The memory clock may not exceed 8 MHz */ 247 if (clktmp > 8000000) 248 priv->cpu_interface |= CPUIF_DMC; 249 } 250 251 if (priv->cpu_interface & CPUIF_DSC) 252 priv->can.clock.freq /= 2; 253 254 if (bcr[idx] != 0xff) 255 priv->bus_config = bcr[idx]; 256 else if (bcr[0] != 0xff) 257 priv->bus_config = bcr[0]; 258 else 259 priv->bus_config = BCR_DEFAULT; 260 261 if (cor[idx] != 0xff) 262 priv->clkout = cor[idx]; 263 else if (cor[0] != 0xff) 264 priv->clkout = cor[0]; 265 else 266 priv->clkout = COR_DEFAULT; 267 268 platform_set_drvdata(pdev, dev); 269 SET_NETDEV_DEV(dev, &pdev->dev); 270 271 err = register_cc770dev(dev); 272 if (err) { 273 dev_err(&pdev->dev, 274 "couldn't register device (err=%d)\n", err); 275 goto exit_unmap; 276 } 277 278 dev_info(&pdev->dev, "device registered (reg_base=0x%p, irq=%d)\n", 279 priv->reg_base, dev->irq); 280 return 0; 281 282 exit_unmap: 283 if (mem[idx]) 284 iounmap(base); 285 exit_release: 286 if (mem[idx]) 287 release_mem_region(mem[idx], iosize); 288 else 289 release_region(port[idx], iosize); 290 exit: 291 return err; 292 } 293 294 static int cc770_isa_remove(struct platform_device *pdev) 295 { 296 struct net_device *dev = platform_get_drvdata(pdev); 297 struct cc770_priv *priv = netdev_priv(dev); 298 int idx = pdev->id; 299 300 unregister_cc770dev(dev); 301 302 if (mem[idx]) { 303 iounmap(priv->reg_base); 304 release_mem_region(mem[idx], CC770_IOSIZE); 305 } else { 306 if (priv->read_reg == cc770_isa_port_read_reg_indirect) 307 release_region(port[idx], CC770_IOSIZE_INDIRECT); 308 else 309 release_region(port[idx], CC770_IOSIZE); 310 } 311 free_cc770dev(dev); 312 313 return 0; 314 } 315 316 static struct platform_driver cc770_isa_driver = { 317 .probe = cc770_isa_probe, 318 .remove = cc770_isa_remove, 319 .driver = { 320 .name = KBUILD_MODNAME, 321 .owner = THIS_MODULE, 322 }, 323 }; 324 325 static int __init cc770_isa_init(void) 326 { 327 int idx, err; 328 329 for (idx = 0; idx < ARRAY_SIZE(cc770_isa_devs); idx++) { 330 if ((port[idx] || mem[idx]) && irq[idx]) { 331 cc770_isa_devs[idx] = 332 platform_device_alloc(KBUILD_MODNAME, idx); 333 if (!cc770_isa_devs[idx]) { 334 err = -ENOMEM; 335 goto exit_free_devices; 336 } 337 err = platform_device_add(cc770_isa_devs[idx]); 338 if (err) { 339 platform_device_put(cc770_isa_devs[idx]); 340 goto exit_free_devices; 341 } 342 pr_debug("platform device %d: port=%#lx, mem=%#lx, " 343 "irq=%d\n", 344 idx, port[idx], mem[idx], irq[idx]); 345 } else if (idx == 0 || port[idx] || mem[idx]) { 346 pr_err("insufficient parameters supplied\n"); 347 err = -EINVAL; 348 goto exit_free_devices; 349 } 350 } 351 352 err = platform_driver_register(&cc770_isa_driver); 353 if (err) 354 goto exit_free_devices; 355 356 pr_info("driver for max. %d devices registered\n", MAXDEV); 357 358 return 0; 359 360 exit_free_devices: 361 while (--idx >= 0) { 362 if (cc770_isa_devs[idx]) 363 platform_device_unregister(cc770_isa_devs[idx]); 364 } 365 366 return err; 367 } 368 module_init(cc770_isa_init); 369 370 static void __exit cc770_isa_exit(void) 371 { 372 int idx; 373 374 platform_driver_unregister(&cc770_isa_driver); 375 for (idx = 0; idx < ARRAY_SIZE(cc770_isa_devs); idx++) { 376 if (cc770_isa_devs[idx]) 377 platform_device_unregister(cc770_isa_devs[idx]); 378 } 379 } 380 module_exit(cc770_isa_exit); 381