root/drivers/net/can/cc770/cc770_isa.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. cc770_isa_mem_read_reg
  2. cc770_isa_mem_write_reg
  3. cc770_isa_port_read_reg
  4. cc770_isa_port_write_reg
  5. cc770_isa_port_read_reg_indirect
  6. cc770_isa_port_write_reg_indirect
  7. cc770_isa_probe
  8. cc770_isa_remove
  9. cc770_isa_init
  10. cc770_isa_exit

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * Driver for CC770 and AN82527 CAN controllers on the legacy ISA bus
   4  *
   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 via
  11  * module parameters:
  12  *
  13  *   insmod cc770_isa.ko port=0x310,0x380 irq=7,11
  14  *
  15  * for ISA devices using I/O ports or:
  16  *
  17  *   insmod cc770_isa.ko mem=0xd1000,0xd1000 irq=7,11
  18  *
  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,11
  24  *
  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 the
  33  * default for all other devices, e.g.:
  34  *
  35  *   insmod cc770_isa.ko mem=0xd1000,0xd1000 irq=7,11 clk=24000000
  36  *
  37  * is equivalent to
  38  *
  39  *   insmod cc770_isa.ko mem=0xd1000,0xd1000 irq=7,11 clk=24000000,24000000
  40  */
  41 
  42 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  43 
  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 8
  59 
  60 MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>");
  61 MODULE_DESCRIPTION("Socket-CAN driver for CC770 on the ISA bus");
  62 MODULE_LICENSE("GPL v2");
  63 
  64 #define CLK_DEFAULT     16000000        /* 16 MHz */
  65 #define COR_DEFAULT     0x00
  66 #define BCR_DEFAULT     BUSCFG_CBY
  67 
  68 static unsigned long port[MAXDEV];
  69 static unsigned long mem[MAXDEV];
  70 static int irq[MAXDEV];
  71 static int clk[MAXDEV];
  72 static u8 cir[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff};
  73 static u8 cor[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff};
  74 static u8 bcr[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff};
  75 static int indirect[MAXDEV] = {[0 ... (MAXDEV - 1)] = -1};
  76 
  77 module_param_hw_array(port, ulong, ioport, NULL, 0444);
  78 MODULE_PARM_DESC(port, "I/O port number");
  79 
  80 module_param_hw_array(mem, ulong, iomem, NULL, 0444);
  81 MODULE_PARM_DESC(mem, "I/O memory address");
  82 
  83 module_param_hw_array(indirect, int, ioport, NULL, 0444);
  84 MODULE_PARM_DESC(indirect, "Indirect access via address and data port");
  85 
  86 module_param_hw_array(irq, int, irq, NULL, 0444);
  87 MODULE_PARM_DESC(irq, "IRQ number");
  88 
  89 module_param_array(clk, int, NULL, 0444);
  90 MODULE_PARM_DESC(clk, "External oscillator clock frequency "
  91                  "(default=16000000 [16 MHz])");
  92 
  93 module_param_array(cir, byte, NULL, 0444);
  94 MODULE_PARM_DESC(cir, "CPU interface register (default=0x40 [DSC])");
  95 
  96 module_param_array(cor, byte, NULL, 0444);
  97 MODULE_PARM_DESC(cor, "Clockout register (default=0x00)");
  98 
  99 module_param_array(bcr, byte, NULL, 0444);
 100 MODULE_PARM_DESC(bcr, "Bus configuration register (default=0x40 [CBY])");
 101 
 102 #define CC770_IOSIZE          0x20
 103 #define CC770_IOSIZE_INDIRECT 0x02
 104 
 105 /* Spinlock for cc770_isa_port_write_reg_indirect
 106  * and cc770_isa_port_read_reg_indirect
 107  */
 108 static DEFINE_SPINLOCK(cc770_isa_port_lock);
 109 
 110 static struct platform_device *cc770_isa_devs[MAXDEV];
 111 
 112 static u8 cc770_isa_mem_read_reg(const struct cc770_priv *priv, int reg)
 113 {
 114         return readb(priv->reg_base + reg);
 115 }
 116 
 117 static 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 
 123 static 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 
 128 static 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 
 134 static 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 
 149 static 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 
 161 static 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_nocache(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         else
 225                 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         else
 251                 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         else
 258                 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_unmap;
 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 
 274  exit_unmap:
 275         if (mem[idx])
 276                 iounmap(base);
 277  exit_release:
 278         if (mem[idx])
 279                 release_mem_region(mem[idx], iosize);
 280         else
 281                 release_region(port[idx], iosize);
 282  exit:
 283         return err;
 284 }
 285 
 286 static int cc770_isa_remove(struct platform_device *pdev)
 287 {
 288         struct net_device *dev = platform_get_drvdata(pdev);
 289         struct cc770_priv *priv = netdev_priv(dev);
 290         int idx = pdev->id;
 291 
 292         unregister_cc770dev(dev);
 293 
 294         if (mem[idx]) {
 295                 iounmap(priv->reg_base);
 296                 release_mem_region(mem[idx], CC770_IOSIZE);
 297         } else {
 298                 if (priv->read_reg == cc770_isa_port_read_reg_indirect)
 299                         release_region(port[idx], CC770_IOSIZE_INDIRECT);
 300                 else
 301                         release_region(port[idx], CC770_IOSIZE);
 302         }
 303         free_cc770dev(dev);
 304 
 305         return 0;
 306 }
 307 
 308 static 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 
 316 static 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 
 351 exit_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 }
 359 module_init(cc770_isa_init);
 360 
 361 static 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 }
 371 module_exit(cc770_isa_exit);

/* [<][>][^][v][top][bottom][index][help] */