linux/drivers/net/can/cc770/cc770_platform.c
<<
>>
Prefs
   1/*
   2 * Driver for CC770 and AN82527 CAN controllers on the platform 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 * If platform data are used you should have similar definitions
  18 * in your board-specific code:
  19 *
  20 *   static struct cc770_platform_data myboard_cc770_pdata = {
  21 *           .osc_freq = 16000000,
  22 *           .cir = 0x41,
  23 *           .cor = 0x20,
  24 *           .bcr = 0x40,
  25 *   };
  26 *
  27 * Please see include/linux/can/platform/cc770.h for description of
  28 * above fields.
  29 *
  30 * If the device tree is used, you need a CAN node definition in your
  31 * DTS file similar to:
  32 *
  33 *   can@3,100 {
  34 *           compatible = "bosch,cc770";
  35 *           reg = <3 0x100 0x80>;
  36 *           interrupts = <2 0>;
  37 *           interrupt-parent = <&mpic>;
  38 *           bosch,external-clock-frequency = <16000000>;
  39 *   };
  40 *
  41 * See "Documentation/devicetree/bindings/net/can/cc770.txt" for further
  42 * information.
  43 */
  44
  45#include <linux/kernel.h>
  46#include <linux/module.h>
  47#include <linux/interrupt.h>
  48#include <linux/netdevice.h>
  49#include <linux/delay.h>
  50#include <linux/platform_device.h>
  51#include <linux/of.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 DRV_NAME "cc770_platform"
  59
  60MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>");
  61MODULE_DESCRIPTION("Socket-CAN driver for CC770 on the platform bus");
  62MODULE_LICENSE("GPL v2");
  63MODULE_ALIAS("platform:" DRV_NAME);
  64
  65#define CC770_PLATFORM_CAN_CLOCK  16000000
  66
  67static u8 cc770_platform_read_reg(const struct cc770_priv *priv, int reg)
  68{
  69        return ioread8(priv->reg_base + reg);
  70}
  71
  72static void cc770_platform_write_reg(const struct cc770_priv *priv, int reg,
  73                                     u8 val)
  74{
  75        iowrite8(val, priv->reg_base + reg);
  76}
  77
  78static int cc770_get_of_node_data(struct platform_device *pdev,
  79                                  struct cc770_priv *priv)
  80{
  81        struct device_node *np = pdev->dev.of_node;
  82        const u32 *prop;
  83        int prop_size;
  84        u32 clkext;
  85
  86        prop = of_get_property(np, "bosch,external-clock-frequency",
  87                               &prop_size);
  88        if (prop && (prop_size ==  sizeof(u32)))
  89                clkext = *prop;
  90        else
  91                clkext = CC770_PLATFORM_CAN_CLOCK; /* default */
  92        priv->can.clock.freq = clkext;
  93
  94        /* The system clock may not exceed 10 MHz */
  95        if (priv->can.clock.freq > 10000000) {
  96                priv->cpu_interface |= CPUIF_DSC;
  97                priv->can.clock.freq /= 2;
  98        }
  99
 100        /* The memory clock may not exceed 8 MHz */
 101        if (priv->can.clock.freq > 8000000)
 102                priv->cpu_interface |= CPUIF_DMC;
 103
 104        if (of_get_property(np, "bosch,divide-memory-clock", NULL))
 105                priv->cpu_interface |= CPUIF_DMC;
 106        if (of_get_property(np, "bosch,iso-low-speed-mux", NULL))
 107                priv->cpu_interface |= CPUIF_MUX;
 108
 109        if (!of_get_property(np, "bosch,no-comperator-bypass", NULL))
 110                priv->bus_config |= BUSCFG_CBY;
 111        if (of_get_property(np, "bosch,disconnect-rx0-input", NULL))
 112                priv->bus_config |= BUSCFG_DR0;
 113        if (of_get_property(np, "bosch,disconnect-rx1-input", NULL))
 114                priv->bus_config |= BUSCFG_DR1;
 115        if (of_get_property(np, "bosch,disconnect-tx1-output", NULL))
 116                priv->bus_config |= BUSCFG_DT1;
 117        if (of_get_property(np, "bosch,polarity-dominant", NULL))
 118                priv->bus_config |= BUSCFG_POL;
 119
 120        prop = of_get_property(np, "bosch,clock-out-frequency", &prop_size);
 121        if (prop && (prop_size == sizeof(u32)) && *prop > 0) {
 122                u32 cdv = clkext / *prop;
 123                int slew;
 124
 125                if (cdv > 0 && cdv < 16) {
 126                        priv->cpu_interface |= CPUIF_CEN;
 127                        priv->clkout |= (cdv - 1) & CLKOUT_CD_MASK;
 128
 129                        prop = of_get_property(np, "bosch,slew-rate",
 130                                               &prop_size);
 131                        if (prop && (prop_size == sizeof(u32))) {
 132                                slew = *prop;
 133                        } else {
 134                                /* Determine default slew rate */
 135                                slew = (CLKOUT_SL_MASK >>
 136                                        CLKOUT_SL_SHIFT) -
 137                                        ((cdv * clkext - 1) / 8000000);
 138                                if (slew < 0)
 139                                        slew = 0;
 140                        }
 141                        priv->clkout |= (slew << CLKOUT_SL_SHIFT) &
 142                                CLKOUT_SL_MASK;
 143                } else {
 144                        dev_dbg(&pdev->dev, "invalid clock-out-frequency\n");
 145                }
 146        }
 147
 148        return 0;
 149}
 150
 151static int cc770_get_platform_data(struct platform_device *pdev,
 152                                   struct cc770_priv *priv)
 153{
 154
 155        struct cc770_platform_data *pdata = pdev->dev.platform_data;
 156
 157        priv->can.clock.freq = pdata->osc_freq;
 158        if (priv->cpu_interface & CPUIF_DSC)
 159                priv->can.clock.freq /= 2;
 160        priv->clkout = pdata->cor;
 161        priv->bus_config = pdata->bcr;
 162        priv->cpu_interface = pdata->cir;
 163
 164        return 0;
 165}
 166
 167static int cc770_platform_probe(struct platform_device *pdev)
 168{
 169        struct net_device *dev;
 170        struct cc770_priv *priv;
 171        struct resource *mem;
 172        resource_size_t mem_size;
 173        void __iomem *base;
 174        int err, irq;
 175
 176        mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 177        irq = platform_get_irq(pdev, 0);
 178        if (!mem || irq <= 0)
 179                return -ENODEV;
 180
 181        mem_size = resource_size(mem);
 182        if (!request_mem_region(mem->start, mem_size, pdev->name))
 183                return -EBUSY;
 184
 185        base = ioremap(mem->start, mem_size);
 186        if (!base) {
 187                err = -ENOMEM;
 188                goto exit_release_mem;
 189        }
 190
 191        dev = alloc_cc770dev(0);
 192        if (!dev) {
 193                err = -ENOMEM;
 194                goto exit_unmap_mem;
 195        }
 196
 197        dev->irq = irq;
 198        priv = netdev_priv(dev);
 199        priv->read_reg = cc770_platform_read_reg;
 200        priv->write_reg = cc770_platform_write_reg;
 201        priv->irq_flags = IRQF_SHARED;
 202        priv->reg_base = base;
 203
 204        if (pdev->dev.of_node)
 205                err = cc770_get_of_node_data(pdev, priv);
 206        else if (pdev->dev.platform_data)
 207                err = cc770_get_platform_data(pdev, priv);
 208        else
 209                err = -ENODEV;
 210        if (err)
 211                goto exit_free_cc770;
 212
 213        dev_dbg(&pdev->dev,
 214                 "reg_base=0x%p irq=%d clock=%d cpu_interface=0x%02x "
 215                 "bus_config=0x%02x clkout=0x%02x\n",
 216                 priv->reg_base, dev->irq, priv->can.clock.freq,
 217                 priv->cpu_interface, priv->bus_config, priv->clkout);
 218
 219        platform_set_drvdata(pdev, dev);
 220        SET_NETDEV_DEV(dev, &pdev->dev);
 221
 222        err = register_cc770dev(dev);
 223        if (err) {
 224                dev_err(&pdev->dev,
 225                        "couldn't register CC700 device (err=%d)\n", err);
 226                goto exit_free_cc770;
 227        }
 228
 229        return 0;
 230
 231exit_free_cc770:
 232        free_cc770dev(dev);
 233exit_unmap_mem:
 234        iounmap(base);
 235exit_release_mem:
 236        release_mem_region(mem->start, mem_size);
 237
 238        return err;
 239}
 240
 241static int cc770_platform_remove(struct platform_device *pdev)
 242{
 243        struct net_device *dev = platform_get_drvdata(pdev);
 244        struct cc770_priv *priv = netdev_priv(dev);
 245        struct resource *mem;
 246
 247        unregister_cc770dev(dev);
 248        iounmap(priv->reg_base);
 249        free_cc770dev(dev);
 250
 251        mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 252        release_mem_region(mem->start, resource_size(mem));
 253
 254        return 0;
 255}
 256
 257static struct of_device_id cc770_platform_table[] = {
 258        {.compatible = "bosch,cc770"}, /* CC770 from Bosch */
 259        {.compatible = "intc,82527"},  /* AN82527 from Intel CP */
 260        {},
 261};
 262MODULE_DEVICE_TABLE(of, cc770_platform_table);
 263
 264static struct platform_driver cc770_platform_driver = {
 265        .driver = {
 266                .name = DRV_NAME,
 267                .owner = THIS_MODULE,
 268                .of_match_table = cc770_platform_table,
 269        },
 270        .probe = cc770_platform_probe,
 271        .remove = cc770_platform_remove,
 272};
 273
 274module_platform_driver(cc770_platform_driver);
 275