linux/drivers/net/can/cc770/cc770_isa.c
<<
>>
Prefs
   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
  68MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>");
  69MODULE_DESCRIPTION("Socket-CAN driver for CC770 on the ISA bus");
  70MODULE_LICENSE("GPL v2");
  71
  72#define CLK_DEFAULT     16000000        /* 16 MHz */
  73#define COR_DEFAULT     0x00
  74#define BCR_DEFAULT     BUSCFG_CBY
  75
  76static unsigned long port[MAXDEV];
  77static unsigned long mem[MAXDEV];
  78static int irq[MAXDEV];
  79static int clk[MAXDEV];
  80static u8 cir[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff};
  81static u8 cor[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff};
  82static u8 bcr[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff};
  83static int indirect[MAXDEV] = {[0 ... (MAXDEV - 1)] = -1};
  84
  85module_param_hw_array(port, ulong, ioport, NULL, 0444);
  86MODULE_PARM_DESC(port, "I/O port number");
  87
  88module_param_hw_array(mem, ulong, iomem, NULL, 0444);
  89MODULE_PARM_DESC(mem, "I/O memory address");
  90
  91module_param_hw_array(indirect, int, ioport, NULL, 0444);
  92MODULE_PARM_DESC(indirect, "Indirect access via address and data port");
  93
  94module_param_hw_array(irq, int, irq, NULL, 0444);
  95MODULE_PARM_DESC(irq, "IRQ number");
  96
  97module_param_array(clk, int, NULL, 0444);
  98MODULE_PARM_DESC(clk, "External oscillator clock frequency "
  99                 "(default=16000000 [16 MHz])");
 100
 101module_param_array(cir, byte, NULL, 0444);
 102MODULE_PARM_DESC(cir, "CPU interface register (default=0x40 [DSC])");
 103
 104module_param_array(cor, byte, NULL, 0444);
 105MODULE_PARM_DESC(cor, "Clockout register (default=0x00)");
 106
 107module_param_array(bcr, byte, NULL, 0444);
 108MODULE_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 */
 116static DEFINE_SPINLOCK(cc770_isa_port_lock);
 117
 118static struct platform_device *cc770_isa_devs[MAXDEV];
 119
 120static u8 cc770_isa_mem_read_reg(const struct cc770_priv *priv, int reg)
 121{
 122        return readb(priv->reg_base + reg);
 123}
 124
 125static 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
 131static 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
 136static 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
 142static 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
 157static 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
 169static 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
 294static 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
 316static struct platform_driver cc770_isa_driver = {
 317        .probe = cc770_isa_probe,
 318        .remove = cc770_isa_remove,
 319        .driver = {
 320                .name = KBUILD_MODNAME,
 321        },
 322};
 323
 324static int __init cc770_isa_init(void)
 325{
 326        int idx, err;
 327
 328        for (idx = 0; idx < ARRAY_SIZE(cc770_isa_devs); idx++) {
 329                if ((port[idx] || mem[idx]) && irq[idx]) {
 330                        cc770_isa_devs[idx] =
 331                                platform_device_alloc(KBUILD_MODNAME, idx);
 332                        if (!cc770_isa_devs[idx]) {
 333                                err = -ENOMEM;
 334                                goto exit_free_devices;
 335                        }
 336                        err = platform_device_add(cc770_isa_devs[idx]);
 337                        if (err) {
 338                                platform_device_put(cc770_isa_devs[idx]);
 339                                goto exit_free_devices;
 340                        }
 341                        pr_debug("platform device %d: port=%#lx, mem=%#lx, "
 342                                 "irq=%d\n",
 343                                 idx, port[idx], mem[idx], irq[idx]);
 344                } else if (idx == 0 || port[idx] || mem[idx]) {
 345                        pr_err("insufficient parameters supplied\n");
 346                        err = -EINVAL;
 347                        goto exit_free_devices;
 348                }
 349        }
 350
 351        err = platform_driver_register(&cc770_isa_driver);
 352        if (err)
 353                goto exit_free_devices;
 354
 355        pr_info("driver for max. %d devices registered\n", MAXDEV);
 356
 357        return 0;
 358
 359exit_free_devices:
 360        while (--idx >= 0) {
 361                if (cc770_isa_devs[idx])
 362                        platform_device_unregister(cc770_isa_devs[idx]);
 363        }
 364
 365        return err;
 366}
 367module_init(cc770_isa_init);
 368
 369static void __exit cc770_isa_exit(void)
 370{
 371        int idx;
 372
 373        platform_driver_unregister(&cc770_isa_driver);
 374        for (idx = 0; idx < ARRAY_SIZE(cc770_isa_devs); idx++) {
 375                if (cc770_isa_devs[idx])
 376                        platform_device_unregister(cc770_isa_devs[idx]);
 377        }
 378}
 379module_exit(cc770_isa_exit);
 380