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