Skip to content

Commit

Permalink
can: m_can: add PCI glue driver for Intel Elkhart Lake
Browse files Browse the repository at this point in the history
Add support for M_CAN controller on Intel Elkhart Lake attached to the PCI bus.
It integrates the Bosch M_CAN controller with Message RAM and the wrapper IP
block with additional registers which all of them are within the same MMIO
range.

Currently only interrupt control register from wrapper IP is used and the MRAM
configuration is expected to come from the firmware via "bosch,mram-cfg" device
property and parsed by m_can.c core.

Initial implementation is done by Felipe Balbi while he was working at Intel
with later changes from Raymond Tan and me.

Co-developed-by: Felipe Balbi (Intel) <[email protected]>
Co-developed-by: Raymond Tan <[email protected]>
Signed-off-by: Felipe Balbi (Intel) <[email protected]>
Signed-off-by: Raymond Tan <[email protected]>
Signed-off-by: Jarkko Nikula <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Marc Kleine-Budde <[email protected]>
  • Loading branch information
jhnikula authored and marckleinebudde committed Dec 10, 2020
1 parent 227619c commit cab7ffc
Show file tree
Hide file tree
Showing 3 changed files with 194 additions and 0 deletions.
7 changes: 7 additions & 0 deletions drivers/net/can/m_can/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ menuconfig CAN_M_CAN

if CAN_M_CAN

config CAN_M_CAN_PCI
tristate "Generic PCI Bus based M_CAN driver"
depends on PCI
help
Say Y here if you want to support Bosch M_CAN controller connected
to the pci bus.

config CAN_M_CAN_PLATFORM
tristate "Bosch M_CAN support for io-mapped devices"
depends on HAS_IOMEM
Expand Down
1 change: 1 addition & 0 deletions drivers/net/can/m_can/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
#

obj-$(CONFIG_CAN_M_CAN) += m_can.o
obj-$(CONFIG_CAN_M_CAN_PCI) += m_can_pci.o
obj-$(CONFIG_CAN_M_CAN_PLATFORM) += m_can_platform.o
obj-$(CONFIG_CAN_M_CAN_TCAN4X5X) += tcan4x5x.o
186 changes: 186 additions & 0 deletions drivers/net/can/m_can/m_can_pci.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
// SPDX-License-Identifier: GPL-2.0
/*
* PCI Specific M_CAN Glue
*
* Copyright (C) 2018-2020 Intel Corporation
* Author: Felipe Balbi (Intel)
* Author: Jarkko Nikula <[email protected]>
* Author: Raymond Tan <[email protected]>
*/

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/pci.h>
#include <linux/pm_runtime.h>

#include "m_can.h"

#define M_CAN_PCI_MMIO_BAR 0

#define M_CAN_CLOCK_FREQ_EHL 100000000
#define CTL_CSR_INT_CTL_OFFSET 0x508

struct m_can_pci_priv {
void __iomem *base;
};

static u32 iomap_read_reg(struct m_can_classdev *cdev, int reg)
{
struct m_can_pci_priv *priv = cdev->device_data;

return readl(priv->base + reg);
}

static u32 iomap_read_fifo(struct m_can_classdev *cdev, int offset)
{
struct m_can_pci_priv *priv = cdev->device_data;

return readl(priv->base + offset);
}

static int iomap_write_reg(struct m_can_classdev *cdev, int reg, int val)
{
struct m_can_pci_priv *priv = cdev->device_data;

writel(val, priv->base + reg);

return 0;
}

static int iomap_write_fifo(struct m_can_classdev *cdev, int offset, int val)
{
struct m_can_pci_priv *priv = cdev->device_data;

writel(val, priv->base + offset);

return 0;
}

static struct m_can_ops m_can_pci_ops = {
.read_reg = iomap_read_reg,
.write_reg = iomap_write_reg,
.write_fifo = iomap_write_fifo,
.read_fifo = iomap_read_fifo,
};

static int m_can_pci_probe(struct pci_dev *pci, const struct pci_device_id *id)
{
struct device *dev = &pci->dev;
struct m_can_classdev *mcan_class;
struct m_can_pci_priv *priv;
void __iomem *base;
int ret;

ret = pcim_enable_device(pci);
if (ret)
return ret;

pci_set_master(pci);

ret = pcim_iomap_regions(pci, BIT(M_CAN_PCI_MMIO_BAR), pci_name(pci));
if (ret)
return ret;

base = pcim_iomap_table(pci)[M_CAN_PCI_MMIO_BAR];

if (!base) {
dev_err(dev, "failed to map BARs\n");
return -ENOMEM;
}

priv = devm_kzalloc(&pci->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;

mcan_class = m_can_class_allocate_dev(&pci->dev);
if (!mcan_class)
return -ENOMEM;

priv->base = base;

ret = pci_alloc_irq_vectors(pci, 1, 1, PCI_IRQ_ALL_TYPES);
if (ret < 0)
return ret;

mcan_class->device_data = priv;
mcan_class->dev = &pci->dev;
mcan_class->net->irq = pci_irq_vector(pci, 0);
mcan_class->pm_clock_support = 1;
mcan_class->can.clock.freq = id->driver_data;
mcan_class->ops = &m_can_pci_ops;

pci_set_drvdata(pci, mcan_class->net);

ret = m_can_class_register(mcan_class);
if (ret)
goto err;

/* Enable interrupt control at CAN wrapper IP */
writel(0x1, base + CTL_CSR_INT_CTL_OFFSET);

pm_runtime_set_autosuspend_delay(dev, 1000);
pm_runtime_use_autosuspend(dev);
pm_runtime_put_noidle(dev);
pm_runtime_allow(dev);

return 0;

err:
pci_free_irq_vectors(pci);
return ret;
}

static void m_can_pci_remove(struct pci_dev *pci)
{
struct net_device *dev = pci_get_drvdata(pci);
struct m_can_classdev *mcan_class = netdev_priv(dev);
struct m_can_pci_priv *priv = mcan_class->device_data;

pm_runtime_forbid(&pci->dev);
pm_runtime_get_noresume(&pci->dev);

/* Disable interrupt control at CAN wrapper IP */
writel(0x0, priv->base + CTL_CSR_INT_CTL_OFFSET);

m_can_class_unregister(mcan_class);
pci_free_irq_vectors(pci);
}

static __maybe_unused int m_can_pci_suspend(struct device *dev)
{
return m_can_class_suspend(dev);
}

static __maybe_unused int m_can_pci_resume(struct device *dev)
{
return m_can_class_resume(dev);
}

static SIMPLE_DEV_PM_OPS(m_can_pci_pm_ops,
m_can_pci_suspend, m_can_pci_resume);

static const struct pci_device_id m_can_pci_id_table[] = {
{ PCI_VDEVICE(INTEL, 0x4bc1), M_CAN_CLOCK_FREQ_EHL, },
{ PCI_VDEVICE(INTEL, 0x4bc2), M_CAN_CLOCK_FREQ_EHL, },
{ } /* Terminating Entry */
};
MODULE_DEVICE_TABLE(pci, m_can_pci_id_table);

static struct pci_driver m_can_pci_driver = {
.name = "m_can_pci",
.probe = m_can_pci_probe,
.remove = m_can_pci_remove,
.id_table = m_can_pci_id_table,
.driver = {
.pm = &m_can_pci_pm_ops,
},
};

module_pci_driver(m_can_pci_driver);

MODULE_AUTHOR("Felipe Balbi (Intel)");
MODULE_AUTHOR("Jarkko Nikula <[email protected]>");
MODULE_AUTHOR("Raymond Tan <[email protected]>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("CAN bus driver for Bosch M_CAN controller on PCI bus");

0 comments on commit cab7ffc

Please sign in to comment.