1 // SPDX-License-Identifier: GPL-2.0
3 * PCI Specific M_CAN Glue
5 * Copyright (C) 2018-2020 Intel Corporation
6 * Author: Felipe Balbi (Intel)
7 * Author: Jarkko Nikula <jarkko.nikula@linux.intel.com>
8 * Author: Raymond Tan <raymond.tan@intel.com>
11 #include <linux/kernel.h>
12 #include <linux/module.h>
13 #include <linux/netdevice.h>
14 #include <linux/pci.h>
15 #include <linux/pm_runtime.h>
19 #define M_CAN_PCI_MMIO_BAR 0
21 #define M_CAN_CLOCK_FREQ_EHL 200000000
22 #define CTL_CSR_INT_CTL_OFFSET 0x508
24 struct m_can_pci_priv
{
25 struct m_can_classdev cdev
;
30 static inline struct m_can_pci_priv
*cdev_to_priv(struct m_can_classdev
*cdev
)
32 return container_of(cdev
, struct m_can_pci_priv
, cdev
);
35 static u32
iomap_read_reg(struct m_can_classdev
*cdev
, int reg
)
37 struct m_can_pci_priv
*priv
= cdev_to_priv(cdev
);
39 return readl(priv
->base
+ reg
);
42 static int iomap_read_fifo(struct m_can_classdev
*cdev
, int offset
, void *val
, size_t val_count
)
44 struct m_can_pci_priv
*priv
= cdev_to_priv(cdev
);
46 ioread32_rep(priv
->base
+ offset
, val
, val_count
);
51 static int iomap_write_reg(struct m_can_classdev
*cdev
, int reg
, int val
)
53 struct m_can_pci_priv
*priv
= cdev_to_priv(cdev
);
55 writel(val
, priv
->base
+ reg
);
60 static int iomap_write_fifo(struct m_can_classdev
*cdev
, int offset
,
61 const void *val
, size_t val_count
)
63 struct m_can_pci_priv
*priv
= cdev_to_priv(cdev
);
65 iowrite32_rep(priv
->base
+ offset
, val
, val_count
);
70 static struct m_can_ops m_can_pci_ops
= {
71 .read_reg
= iomap_read_reg
,
72 .write_reg
= iomap_write_reg
,
73 .write_fifo
= iomap_write_fifo
,
74 .read_fifo
= iomap_read_fifo
,
77 static int m_can_pci_probe(struct pci_dev
*pci
, const struct pci_device_id
*id
)
79 struct device
*dev
= &pci
->dev
;
80 struct m_can_classdev
*mcan_class
;
81 struct m_can_pci_priv
*priv
;
85 ret
= pcim_enable_device(pci
);
91 ret
= pcim_iomap_regions(pci
, BIT(M_CAN_PCI_MMIO_BAR
), pci_name(pci
));
95 base
= pcim_iomap_table(pci
)[M_CAN_PCI_MMIO_BAR
];
98 dev_err(dev
, "failed to map BARs\n");
102 mcan_class
= m_can_class_allocate_dev(&pci
->dev
,
103 sizeof(struct m_can_pci_priv
));
107 priv
= cdev_to_priv(mcan_class
);
111 ret
= pci_alloc_irq_vectors(pci
, 1, 1, PCI_IRQ_ALL_TYPES
);
115 mcan_class
->dev
= &pci
->dev
;
116 mcan_class
->net
->irq
= pci_irq_vector(pci
, 0);
117 mcan_class
->pm_clock_support
= 1;
118 mcan_class
->can
.clock
.freq
= id
->driver_data
;
119 mcan_class
->ops
= &m_can_pci_ops
;
121 pci_set_drvdata(pci
, mcan_class
);
123 ret
= m_can_class_register(mcan_class
);
127 /* Enable interrupt control at CAN wrapper IP */
128 writel(0x1, base
+ CTL_CSR_INT_CTL_OFFSET
);
130 pm_runtime_set_autosuspend_delay(dev
, 1000);
131 pm_runtime_use_autosuspend(dev
);
132 pm_runtime_put_noidle(dev
);
133 pm_runtime_allow(dev
);
138 pci_free_irq_vectors(pci
);
142 static void m_can_pci_remove(struct pci_dev
*pci
)
144 struct m_can_classdev
*mcan_class
= pci_get_drvdata(pci
);
145 struct m_can_pci_priv
*priv
= cdev_to_priv(mcan_class
);
147 pm_runtime_forbid(&pci
->dev
);
148 pm_runtime_get_noresume(&pci
->dev
);
150 /* Disable interrupt control at CAN wrapper IP */
151 writel(0x0, priv
->base
+ CTL_CSR_INT_CTL_OFFSET
);
153 m_can_class_unregister(mcan_class
);
154 pci_free_irq_vectors(pci
);
157 static __maybe_unused
int m_can_pci_suspend(struct device
*dev
)
159 return m_can_class_suspend(dev
);
162 static __maybe_unused
int m_can_pci_resume(struct device
*dev
)
164 return m_can_class_resume(dev
);
167 static SIMPLE_DEV_PM_OPS(m_can_pci_pm_ops
,
168 m_can_pci_suspend
, m_can_pci_resume
);
170 static const struct pci_device_id m_can_pci_id_table
[] = {
171 { PCI_VDEVICE(INTEL
, 0x4bc1), M_CAN_CLOCK_FREQ_EHL
, },
172 { PCI_VDEVICE(INTEL
, 0x4bc2), M_CAN_CLOCK_FREQ_EHL
, },
173 { } /* Terminating Entry */
175 MODULE_DEVICE_TABLE(pci
, m_can_pci_id_table
);
177 static struct pci_driver m_can_pci_driver
= {
179 .probe
= m_can_pci_probe
,
180 .remove
= m_can_pci_remove
,
181 .id_table
= m_can_pci_id_table
,
183 .pm
= &m_can_pci_pm_ops
,
187 module_pci_driver(m_can_pci_driver
);
189 MODULE_AUTHOR("Felipe Balbi (Intel)");
190 MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@linux.intel.com>");
191 MODULE_AUTHOR("Raymond Tan <raymond.tan@intel.com>");
192 MODULE_LICENSE("GPL");
193 MODULE_DESCRIPTION("CAN bus driver for Bosch M_CAN controller on PCI bus");