Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * PCI Specific M_CAN Glue
0004  *
0005  * Copyright (C) 2018-2020 Intel Corporation
0006  * Author: Felipe Balbi (Intel)
0007  * Author: Jarkko Nikula <jarkko.nikula@linux.intel.com>
0008  * Author: Raymond Tan <raymond.tan@intel.com>
0009  */
0010 
0011 #include <linux/kernel.h>
0012 #include <linux/module.h>
0013 #include <linux/netdevice.h>
0014 #include <linux/pci.h>
0015 #include <linux/pm_runtime.h>
0016 
0017 #include "m_can.h"
0018 
0019 #define M_CAN_PCI_MMIO_BAR      0
0020 
0021 #define M_CAN_CLOCK_FREQ_EHL        200000000
0022 #define CTL_CSR_INT_CTL_OFFSET      0x508
0023 
0024 struct m_can_pci_priv {
0025     struct m_can_classdev cdev;
0026 
0027     void __iomem *base;
0028 };
0029 
0030 static inline struct m_can_pci_priv *cdev_to_priv(struct m_can_classdev *cdev)
0031 {
0032     return container_of(cdev, struct m_can_pci_priv, cdev);
0033 }
0034 
0035 static u32 iomap_read_reg(struct m_can_classdev *cdev, int reg)
0036 {
0037     struct m_can_pci_priv *priv = cdev_to_priv(cdev);
0038 
0039     return readl(priv->base + reg);
0040 }
0041 
0042 static int iomap_read_fifo(struct m_can_classdev *cdev, int offset, void *val, size_t val_count)
0043 {
0044     struct m_can_pci_priv *priv = cdev_to_priv(cdev);
0045     void __iomem *src = priv->base + offset;
0046 
0047     while (val_count--) {
0048         *(unsigned int *)val = ioread32(src);
0049         val += 4;
0050         src += 4;
0051     }
0052 
0053     return 0;
0054 }
0055 
0056 static int iomap_write_reg(struct m_can_classdev *cdev, int reg, int val)
0057 {
0058     struct m_can_pci_priv *priv = cdev_to_priv(cdev);
0059 
0060     writel(val, priv->base + reg);
0061 
0062     return 0;
0063 }
0064 
0065 static int iomap_write_fifo(struct m_can_classdev *cdev, int offset,
0066                 const void *val, size_t val_count)
0067 {
0068     struct m_can_pci_priv *priv = cdev_to_priv(cdev);
0069     void __iomem *dst = priv->base + offset;
0070 
0071     while (val_count--) {
0072         iowrite32(*(unsigned int *)val, dst);
0073         val += 4;
0074         dst += 4;
0075     }
0076 
0077     return 0;
0078 }
0079 
0080 static struct m_can_ops m_can_pci_ops = {
0081     .read_reg = iomap_read_reg,
0082     .write_reg = iomap_write_reg,
0083     .write_fifo = iomap_write_fifo,
0084     .read_fifo = iomap_read_fifo,
0085 };
0086 
0087 static int m_can_pci_probe(struct pci_dev *pci, const struct pci_device_id *id)
0088 {
0089     struct device *dev = &pci->dev;
0090     struct m_can_classdev *mcan_class;
0091     struct m_can_pci_priv *priv;
0092     void __iomem *base;
0093     int ret;
0094 
0095     ret = pcim_enable_device(pci);
0096     if (ret)
0097         return ret;
0098 
0099     pci_set_master(pci);
0100 
0101     ret = pcim_iomap_regions(pci, BIT(M_CAN_PCI_MMIO_BAR), pci_name(pci));
0102     if (ret)
0103         return ret;
0104 
0105     base = pcim_iomap_table(pci)[M_CAN_PCI_MMIO_BAR];
0106 
0107     if (!base) {
0108         dev_err(dev, "failed to map BARs\n");
0109         return -ENOMEM;
0110     }
0111 
0112     mcan_class = m_can_class_allocate_dev(&pci->dev,
0113                           sizeof(struct m_can_pci_priv));
0114     if (!mcan_class)
0115         return -ENOMEM;
0116 
0117     priv = cdev_to_priv(mcan_class);
0118 
0119     priv->base = base;
0120 
0121     ret = pci_alloc_irq_vectors(pci, 1, 1, PCI_IRQ_ALL_TYPES);
0122     if (ret < 0)
0123         return ret;
0124 
0125     mcan_class->dev = &pci->dev;
0126     mcan_class->net->irq = pci_irq_vector(pci, 0);
0127     mcan_class->pm_clock_support = 1;
0128     mcan_class->can.clock.freq = id->driver_data;
0129     mcan_class->ops = &m_can_pci_ops;
0130 
0131     pci_set_drvdata(pci, mcan_class);
0132 
0133     ret = m_can_class_register(mcan_class);
0134     if (ret)
0135         goto err;
0136 
0137     /* Enable interrupt control at CAN wrapper IP */
0138     writel(0x1, base + CTL_CSR_INT_CTL_OFFSET);
0139 
0140     pm_runtime_set_autosuspend_delay(dev, 1000);
0141     pm_runtime_use_autosuspend(dev);
0142     pm_runtime_put_noidle(dev);
0143     pm_runtime_allow(dev);
0144 
0145     return 0;
0146 
0147 err:
0148     pci_free_irq_vectors(pci);
0149     return ret;
0150 }
0151 
0152 static void m_can_pci_remove(struct pci_dev *pci)
0153 {
0154     struct m_can_classdev *mcan_class = pci_get_drvdata(pci);
0155     struct m_can_pci_priv *priv = cdev_to_priv(mcan_class);
0156 
0157     pm_runtime_forbid(&pci->dev);
0158     pm_runtime_get_noresume(&pci->dev);
0159 
0160     /* Disable interrupt control at CAN wrapper IP */
0161     writel(0x0, priv->base + CTL_CSR_INT_CTL_OFFSET);
0162 
0163     m_can_class_unregister(mcan_class);
0164     pci_free_irq_vectors(pci);
0165 }
0166 
0167 static __maybe_unused int m_can_pci_suspend(struct device *dev)
0168 {
0169     return m_can_class_suspend(dev);
0170 }
0171 
0172 static __maybe_unused int m_can_pci_resume(struct device *dev)
0173 {
0174     return m_can_class_resume(dev);
0175 }
0176 
0177 static SIMPLE_DEV_PM_OPS(m_can_pci_pm_ops,
0178              m_can_pci_suspend, m_can_pci_resume);
0179 
0180 static const struct pci_device_id m_can_pci_id_table[] = {
0181     { PCI_VDEVICE(INTEL, 0x4bc1), M_CAN_CLOCK_FREQ_EHL, },
0182     { PCI_VDEVICE(INTEL, 0x4bc2), M_CAN_CLOCK_FREQ_EHL, },
0183     {  }    /* Terminating Entry */
0184 };
0185 MODULE_DEVICE_TABLE(pci, m_can_pci_id_table);
0186 
0187 static struct pci_driver m_can_pci_driver = {
0188     .name = "m_can_pci",
0189     .probe = m_can_pci_probe,
0190     .remove = m_can_pci_remove,
0191     .id_table = m_can_pci_id_table,
0192     .driver = {
0193         .pm = &m_can_pci_pm_ops,
0194     },
0195 };
0196 
0197 module_pci_driver(m_can_pci_driver);
0198 
0199 MODULE_AUTHOR("Felipe Balbi (Intel)");
0200 MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@linux.intel.com>");
0201 MODULE_AUTHOR("Raymond Tan <raymond.tan@intel.com>");
0202 MODULE_LICENSE("GPL");
0203 MODULE_DESCRIPTION("CAN bus driver for Bosch M_CAN controller on PCI bus");