linux/drivers/net/can/c_can/c_can_pci.c
<<
>>
Prefs
   1/*
   2 * PCI bus driver for Bosch C_CAN/D_CAN controller
   3 *
   4 * Copyright (C) 2012 Federico Vaga <federico.vaga@gmail.com>
   5 *
   6 * Borrowed from c_can_platform.c
   7 *
   8 * This file is licensed under the terms of the GNU General Public
   9 * License version 2. This program is licensed "as is" without any
  10 * warranty of any kind, whether express or implied.
  11 */
  12
  13#include <linux/kernel.h>
  14#include <linux/module.h>
  15#include <linux/netdevice.h>
  16#include <linux/pci.h>
  17
  18#include <linux/can/dev.h>
  19
  20#include "c_can.h"
  21
  22#define PCI_DEVICE_ID_PCH_CAN   0x8818
  23#define PCH_PCI_SOFT_RESET      0x01fc
  24
  25enum c_can_pci_reg_align {
  26        C_CAN_REG_ALIGN_16,
  27        C_CAN_REG_ALIGN_32,
  28        C_CAN_REG_32,
  29};
  30
  31struct c_can_pci_data {
  32        /* Specify if is C_CAN or D_CAN */
  33        enum c_can_dev_id type;
  34        /* Set the register alignment in the memory */
  35        enum c_can_pci_reg_align reg_align;
  36        /* Set the frequency */
  37        unsigned int freq;
  38        /* PCI bar number */
  39        int bar;
  40        /* Callback for reset */
  41        void (*init)(const struct c_can_priv *priv, bool enable);
  42};
  43
  44/*
  45 * 16-bit c_can registers can be arranged differently in the memory
  46 * architecture of different implementations. For example: 16-bit
  47 * registers can be aligned to a 16-bit boundary or 32-bit boundary etc.
  48 * Handle the same by providing a common read/write interface.
  49 */
  50static u16 c_can_pci_read_reg_aligned_to_16bit(const struct c_can_priv *priv,
  51                                                enum reg index)
  52{
  53        return readw(priv->base + priv->regs[index]);
  54}
  55
  56static void c_can_pci_write_reg_aligned_to_16bit(const struct c_can_priv *priv,
  57                                                enum reg index, u16 val)
  58{
  59        writew(val, priv->base + priv->regs[index]);
  60}
  61
  62static u16 c_can_pci_read_reg_aligned_to_32bit(const struct c_can_priv *priv,
  63                                                enum reg index)
  64{
  65        return readw(priv->base + 2 * priv->regs[index]);
  66}
  67
  68static void c_can_pci_write_reg_aligned_to_32bit(const struct c_can_priv *priv,
  69                                                enum reg index, u16 val)
  70{
  71        writew(val, priv->base + 2 * priv->regs[index]);
  72}
  73
  74static u16 c_can_pci_read_reg_32bit(const struct c_can_priv *priv,
  75                                    enum reg index)
  76{
  77        return (u16)ioread32(priv->base + 2 * priv->regs[index]);
  78}
  79
  80static void c_can_pci_write_reg_32bit(const struct c_can_priv *priv,
  81                                      enum reg index, u16 val)
  82{
  83        iowrite32((u32)val, priv->base + 2 * priv->regs[index]);
  84}
  85
  86static u32 c_can_pci_read_reg32(const struct c_can_priv *priv, enum reg index)
  87{
  88        u32 val;
  89
  90        val = priv->read_reg(priv, index);
  91        val |= ((u32) priv->read_reg(priv, index + 1)) << 16;
  92
  93        return val;
  94}
  95
  96static void c_can_pci_write_reg32(const struct c_can_priv *priv, enum reg index,
  97                u32 val)
  98{
  99        priv->write_reg(priv, index + 1, val >> 16);
 100        priv->write_reg(priv, index, val);
 101}
 102
 103static void c_can_pci_reset_pch(const struct c_can_priv *priv, bool enable)
 104{
 105        if (enable) {
 106                u32 __iomem *addr = priv->base + PCH_PCI_SOFT_RESET;
 107
 108                /* write to sw reset register */
 109                iowrite32(1, addr);
 110                iowrite32(0, addr);
 111        }
 112}
 113
 114static int c_can_pci_probe(struct pci_dev *pdev,
 115                           const struct pci_device_id *ent)
 116{
 117        struct c_can_pci_data *c_can_pci_data = (void *)ent->driver_data;
 118        struct c_can_priv *priv;
 119        struct net_device *dev;
 120        void __iomem *addr;
 121        int ret;
 122
 123        ret = pci_enable_device(pdev);
 124        if (ret) {
 125                dev_err(&pdev->dev, "pci_enable_device FAILED\n");
 126                goto out;
 127        }
 128
 129        ret = pci_request_regions(pdev, KBUILD_MODNAME);
 130        if (ret) {
 131                dev_err(&pdev->dev, "pci_request_regions FAILED\n");
 132                goto out_disable_device;
 133        }
 134
 135        ret = pci_enable_msi(pdev);
 136        if (!ret) {
 137                dev_info(&pdev->dev, "MSI enabled\n");
 138                pci_set_master(pdev);
 139        }
 140
 141        addr = pci_iomap(pdev, c_can_pci_data->bar,
 142                         pci_resource_len(pdev, c_can_pci_data->bar));
 143        if (!addr) {
 144                dev_err(&pdev->dev,
 145                        "device has no PCI memory resources, "
 146                        "failing adapter\n");
 147                ret = -ENOMEM;
 148                goto out_release_regions;
 149        }
 150
 151        /* allocate the c_can device */
 152        dev = alloc_c_can_dev();
 153        if (!dev) {
 154                ret = -ENOMEM;
 155                goto out_iounmap;
 156        }
 157
 158        priv = netdev_priv(dev);
 159        pci_set_drvdata(pdev, dev);
 160        SET_NETDEV_DEV(dev, &pdev->dev);
 161
 162        dev->irq = pdev->irq;
 163        priv->base = addr;
 164        priv->device = &pdev->dev;
 165
 166        if (!c_can_pci_data->freq) {
 167                dev_err(&pdev->dev, "no clock frequency defined\n");
 168                ret = -ENODEV;
 169                goto out_free_c_can;
 170        } else {
 171                priv->can.clock.freq = c_can_pci_data->freq;
 172        }
 173
 174        /* Configure CAN type */
 175        switch (c_can_pci_data->type) {
 176        case BOSCH_C_CAN:
 177                priv->regs = reg_map_c_can;
 178                break;
 179        case BOSCH_D_CAN:
 180                priv->regs = reg_map_d_can;
 181                break;
 182        default:
 183                ret = -EINVAL;
 184                goto out_free_c_can;
 185        }
 186
 187        priv->type = c_can_pci_data->type;
 188
 189        /* Configure access to registers */
 190        switch (c_can_pci_data->reg_align) {
 191        case C_CAN_REG_ALIGN_32:
 192                priv->read_reg = c_can_pci_read_reg_aligned_to_32bit;
 193                priv->write_reg = c_can_pci_write_reg_aligned_to_32bit;
 194                break;
 195        case C_CAN_REG_ALIGN_16:
 196                priv->read_reg = c_can_pci_read_reg_aligned_to_16bit;
 197                priv->write_reg = c_can_pci_write_reg_aligned_to_16bit;
 198                break;
 199        case C_CAN_REG_32:
 200                priv->read_reg = c_can_pci_read_reg_32bit;
 201                priv->write_reg = c_can_pci_write_reg_32bit;
 202                break;
 203        default:
 204                ret = -EINVAL;
 205                goto out_free_c_can;
 206        }
 207        priv->read_reg32 = c_can_pci_read_reg32;
 208        priv->write_reg32 = c_can_pci_write_reg32;
 209
 210        priv->raminit = c_can_pci_data->init;
 211
 212        ret = register_c_can_dev(dev);
 213        if (ret) {
 214                dev_err(&pdev->dev, "registering %s failed (err=%d)\n",
 215                        KBUILD_MODNAME, ret);
 216                goto out_free_c_can;
 217        }
 218
 219        dev_dbg(&pdev->dev, "%s device registered (regs=%p, irq=%d)\n",
 220                 KBUILD_MODNAME, priv->regs, dev->irq);
 221
 222        return 0;
 223
 224out_free_c_can:
 225        free_c_can_dev(dev);
 226out_iounmap:
 227        pci_iounmap(pdev, addr);
 228out_release_regions:
 229        pci_disable_msi(pdev);
 230        pci_clear_master(pdev);
 231        pci_release_regions(pdev);
 232out_disable_device:
 233        pci_disable_device(pdev);
 234out:
 235        return ret;
 236}
 237
 238static void c_can_pci_remove(struct pci_dev *pdev)
 239{
 240        struct net_device *dev = pci_get_drvdata(pdev);
 241        struct c_can_priv *priv = netdev_priv(dev);
 242
 243        unregister_c_can_dev(dev);
 244
 245        free_c_can_dev(dev);
 246
 247        pci_iounmap(pdev, priv->base);
 248        pci_disable_msi(pdev);
 249        pci_clear_master(pdev);
 250        pci_release_regions(pdev);
 251        pci_disable_device(pdev);
 252}
 253
 254static const struct c_can_pci_data c_can_sta2x11= {
 255        .type = BOSCH_C_CAN,
 256        .reg_align = C_CAN_REG_ALIGN_32,
 257        .freq = 52000000, /* 52 Mhz */
 258        .bar = 0,
 259};
 260
 261static const struct c_can_pci_data c_can_pch = {
 262        .type = BOSCH_C_CAN,
 263        .reg_align = C_CAN_REG_32,
 264        .freq = 50000000, /* 50 MHz */
 265        .init = c_can_pci_reset_pch,
 266        .bar = 1,
 267};
 268
 269#define C_CAN_ID(_vend, _dev, _driverdata) {            \
 270        PCI_DEVICE(_vend, _dev),                        \
 271        .driver_data = (unsigned long)&_driverdata,     \
 272}
 273
 274static const struct pci_device_id c_can_pci_tbl[] = {
 275        C_CAN_ID(PCI_VENDOR_ID_STMICRO, PCI_DEVICE_ID_STMICRO_CAN,
 276                 c_can_sta2x11),
 277        C_CAN_ID(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PCH_CAN,
 278                 c_can_pch),
 279        {},
 280};
 281static struct pci_driver c_can_pci_driver = {
 282        .name = KBUILD_MODNAME,
 283        .id_table = c_can_pci_tbl,
 284        .probe = c_can_pci_probe,
 285        .remove = c_can_pci_remove,
 286};
 287
 288module_pci_driver(c_can_pci_driver);
 289
 290MODULE_AUTHOR("Federico Vaga <federico.vaga@gmail.com>");
 291MODULE_LICENSE("GPL v2");
 292MODULE_DESCRIPTION("PCI CAN bus driver for Bosch C_CAN/D_CAN controller");
 293MODULE_DEVICE_TABLE(pci, c_can_pci_tbl);
 294