Skip to content

Commit 792a5b6

Browse files
ppisamarckleinebudde
authored andcommitted
can: ctucanfd: CTU CAN FD open-source IP core - PCI bus support.
PCI bus adaptation for CTU CAN FD open-source IP core. The project providing FPGA design for Intel EP4CGX15 based DB4CGX15 PCIe board with PiKRON.com designed transceiver riser shield is available at https://gitlab.fel.cvut.cz/canbus/pcie-ctucanfd . Link: https://lore.kernel.org/all/a81333e206a9bcf9434797f6f54d8664775542e2.1647904780.git.pisa@cmp.felk.cvut.cz Signed-off-by: Pavel Pisa <[email protected]> Signed-off-by: Martin Jerabek <[email protected]> Signed-off-by: Ondrej Ille <[email protected]> Signed-off-by: Marc Kleine-Budde <[email protected]>
1 parent 2dcb8e8 commit 792a5b6

File tree

3 files changed

+316
-0
lines changed

3 files changed

+316
-0
lines changed

drivers/net/can/ctucanfd/Kconfig

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,13 @@ config CAN_CTUCANFD
1010
from project (https://gitlab.fel.cvut.cz/canbus/pcie-ctucanfd) and
1111
on Intel SoC from project (https://gitlab.fel.cvut.cz/canbus/intel-soc-ctucanfd).
1212
Guidepost CTU FEE CAN bus projects page https://canbus.pages.fel.cvut.cz/ .
13+
14+
config CAN_CTUCANFD_PCI
15+
tristate "CTU CAN-FD IP core PCI/PCIe driver"
16+
depends on CAN_CTUCANFD
17+
depends on PCI
18+
help
19+
This driver adds PCI/PCIe support for CTU CAN-FD IP core.
20+
The project providing FPGA design for Intel EP4CGX15 based DB4CGX15
21+
PCIe board with PiKRON.com designed transceiver riser shield is available
22+
at https://gitlab.fel.cvut.cz/canbus/pcie-ctucanfd .

drivers/net/can/ctucanfd/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,5 @@
55

66
obj-$(CONFIG_CAN_CTUCANFD) := ctucanfd.o
77
ctucanfd-y := ctucanfd_base.o
8+
9+
obj-$(CONFIG_CAN_CTUCANFD_PCI) += ctucanfd_pci.o
Lines changed: 304 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,304 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
/*******************************************************************************
3+
*
4+
* CTU CAN FD IP Core
5+
*
6+
* Copyright (C) 2015-2018 Ondrej Ille <[email protected]> FEE CTU
7+
* Copyright (C) 2018-2021 Ondrej Ille <[email protected]> self-funded
8+
* Copyright (C) 2018-2019 Martin Jerabek <[email protected]> FEE CTU
9+
* Copyright (C) 2018-2022 Pavel Pisa <[email protected]> FEE CTU/self-funded
10+
*
11+
* Project advisors:
12+
* Jiri Novak <[email protected]>
13+
* Pavel Pisa <[email protected]>
14+
*
15+
* Department of Measurement (http://meas.fel.cvut.cz/)
16+
* Faculty of Electrical Engineering (http://www.fel.cvut.cz)
17+
* Czech Technical University (http://www.cvut.cz/)
18+
******************************************************************************/
19+
20+
#include <linux/module.h>
21+
#include <linux/pci.h>
22+
23+
#include "ctucanfd.h"
24+
25+
#ifndef PCI_DEVICE_DATA
26+
#define PCI_DEVICE_DATA(vend, dev, data) \
27+
.vendor = PCI_VENDOR_ID_##vend, \
28+
.device = PCI_DEVICE_ID_##vend##_##dev, \
29+
.subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, 0, 0, \
30+
.driver_data = (kernel_ulong_t)(data)
31+
#endif
32+
33+
#ifndef PCI_VENDOR_ID_TEDIA
34+
#define PCI_VENDOR_ID_TEDIA 0x1760
35+
#endif
36+
37+
#ifndef PCI_DEVICE_ID_TEDIA_CTUCAN_VER21
38+
#define PCI_DEVICE_ID_TEDIA_CTUCAN_VER21 0xff00
39+
#endif
40+
41+
#define CTUCAN_BAR0_CTUCAN_ID 0x0000
42+
#define CTUCAN_BAR0_CRA_BASE 0x4000
43+
#define CYCLONE_IV_CRA_A2P_IE (0x0050)
44+
45+
#define CTUCAN_WITHOUT_CTUCAN_ID 0
46+
#define CTUCAN_WITH_CTUCAN_ID 1
47+
48+
static bool use_msi = true;
49+
module_param(use_msi, bool, 0444);
50+
MODULE_PARM_DESC(use_msi, "PCIe implementation use MSI interrupts. Default: 1 (yes)");
51+
52+
static bool pci_use_second = true;
53+
module_param(pci_use_second, bool, 0444);
54+
MODULE_PARM_DESC(pci_use_second, "Use the second CAN core on PCIe card. Default: 1 (yes)");
55+
56+
struct ctucan_pci_board_data {
57+
void __iomem *bar0_base;
58+
void __iomem *cra_base;
59+
void __iomem *bar1_base;
60+
struct list_head ndev_list_head;
61+
int use_msi;
62+
};
63+
64+
static struct ctucan_pci_board_data *ctucan_pci_get_bdata(struct pci_dev *pdev)
65+
{
66+
return (struct ctucan_pci_board_data *)pci_get_drvdata(pdev);
67+
}
68+
69+
static void ctucan_pci_set_drvdata(struct device *dev,
70+
struct net_device *ndev)
71+
{
72+
struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
73+
struct ctucan_priv *priv = netdev_priv(ndev);
74+
struct ctucan_pci_board_data *bdata = ctucan_pci_get_bdata(pdev);
75+
76+
list_add(&priv->peers_on_pdev, &bdata->ndev_list_head);
77+
priv->irq_flags = IRQF_SHARED;
78+
}
79+
80+
/**
81+
* ctucan_pci_probe - PCI registration call
82+
* @pdev: Handle to the pci device structure
83+
* @ent: Pointer to the entry from ctucan_pci_tbl
84+
*
85+
* This function does all the memory allocation and registration for the CAN
86+
* device.
87+
*
88+
* Return: 0 on success and failure value on error
89+
*/
90+
static int ctucan_pci_probe(struct pci_dev *pdev,
91+
const struct pci_device_id *ent)
92+
{
93+
struct device *dev = &pdev->dev;
94+
unsigned long driver_data = ent->driver_data;
95+
struct ctucan_pci_board_data *bdata;
96+
void __iomem *addr;
97+
void __iomem *cra_addr;
98+
void __iomem *bar0_base;
99+
u32 cra_a2p_ie;
100+
u32 ctucan_id = 0;
101+
int ret;
102+
unsigned int ntxbufs;
103+
unsigned int num_cores = 1;
104+
unsigned int core_i = 0;
105+
int irq;
106+
int msi_ok = 0;
107+
108+
ret = pci_enable_device(pdev);
109+
if (ret) {
110+
dev_err(dev, "pci_enable_device FAILED\n");
111+
goto err;
112+
}
113+
114+
ret = pci_request_regions(pdev, KBUILD_MODNAME);
115+
if (ret) {
116+
dev_err(dev, "pci_request_regions FAILED\n");
117+
goto err_disable_device;
118+
}
119+
120+
if (use_msi) {
121+
ret = pci_enable_msi(pdev);
122+
if (!ret) {
123+
dev_info(dev, "MSI enabled\n");
124+
pci_set_master(pdev);
125+
msi_ok = 1;
126+
}
127+
}
128+
129+
dev_info(dev, "ctucan BAR0 0x%08llx 0x%08llx\n",
130+
(long long)pci_resource_start(pdev, 0),
131+
(long long)pci_resource_len(pdev, 0));
132+
133+
dev_info(dev, "ctucan BAR1 0x%08llx 0x%08llx\n",
134+
(long long)pci_resource_start(pdev, 1),
135+
(long long)pci_resource_len(pdev, 1));
136+
137+
addr = pci_iomap(pdev, 1, pci_resource_len(pdev, 1));
138+
if (!addr) {
139+
dev_err(dev, "PCI BAR 1 cannot be mapped\n");
140+
ret = -ENOMEM;
141+
goto err_release_regions;
142+
}
143+
144+
/* Cyclone IV PCI Express Control Registers Area */
145+
bar0_base = pci_iomap(pdev, 0, pci_resource_len(pdev, 0));
146+
if (!bar0_base) {
147+
dev_err(dev, "PCI BAR 0 cannot be mapped\n");
148+
ret = -EIO;
149+
goto err_pci_iounmap_bar1;
150+
}
151+
152+
if (driver_data == CTUCAN_WITHOUT_CTUCAN_ID) {
153+
cra_addr = bar0_base;
154+
num_cores = 2;
155+
} else {
156+
cra_addr = bar0_base + CTUCAN_BAR0_CRA_BASE;
157+
ctucan_id = ioread32(bar0_base + CTUCAN_BAR0_CTUCAN_ID);
158+
dev_info(dev, "ctucan_id 0x%08lx\n", (unsigned long)ctucan_id);
159+
num_cores = ctucan_id & 0xf;
160+
}
161+
162+
irq = pdev->irq;
163+
164+
ntxbufs = 4;
165+
166+
bdata = kzalloc(sizeof(*bdata), GFP_KERNEL);
167+
if (!bdata) {
168+
ret = -ENOMEM;
169+
goto err_pci_iounmap_bar0;
170+
}
171+
172+
INIT_LIST_HEAD(&bdata->ndev_list_head);
173+
bdata->bar0_base = bar0_base;
174+
bdata->cra_base = cra_addr;
175+
bdata->bar1_base = addr;
176+
bdata->use_msi = msi_ok;
177+
178+
pci_set_drvdata(pdev, bdata);
179+
180+
ret = ctucan_probe_common(dev, addr, irq, ntxbufs, 100000000,
181+
0, ctucan_pci_set_drvdata);
182+
if (ret < 0)
183+
goto err_free_board;
184+
185+
core_i++;
186+
187+
while (pci_use_second && (core_i < num_cores)) {
188+
addr += 0x4000;
189+
ret = ctucan_probe_common(dev, addr, irq, ntxbufs, 100000000,
190+
0, ctucan_pci_set_drvdata);
191+
if (ret < 0) {
192+
dev_info(dev, "CTU CAN FD core %d initialization failed\n",
193+
core_i);
194+
break;
195+
}
196+
core_i++;
197+
}
198+
199+
/* enable interrupt in
200+
* Avalon-MM to PCI Express Interrupt Enable Register
201+
*/
202+
cra_a2p_ie = ioread32(cra_addr + CYCLONE_IV_CRA_A2P_IE);
203+
dev_info(dev, "cra_a2p_ie 0x%08x\n", cra_a2p_ie);
204+
cra_a2p_ie |= 1;
205+
iowrite32(cra_a2p_ie, cra_addr + CYCLONE_IV_CRA_A2P_IE);
206+
cra_a2p_ie = ioread32(cra_addr + CYCLONE_IV_CRA_A2P_IE);
207+
dev_info(dev, "cra_a2p_ie 0x%08x\n", cra_a2p_ie);
208+
209+
return 0;
210+
211+
err_free_board:
212+
pci_set_drvdata(pdev, NULL);
213+
kfree(bdata);
214+
err_pci_iounmap_bar0:
215+
pci_iounmap(pdev, cra_addr);
216+
err_pci_iounmap_bar1:
217+
pci_iounmap(pdev, addr);
218+
err_release_regions:
219+
if (msi_ok) {
220+
pci_disable_msi(pdev);
221+
pci_clear_master(pdev);
222+
}
223+
pci_release_regions(pdev);
224+
err_disable_device:
225+
pci_disable_device(pdev);
226+
err:
227+
return ret;
228+
}
229+
230+
/**
231+
* ctucan_pci_remove - Unregister the device after releasing the resources
232+
* @pdev: Handle to the pci device structure
233+
*
234+
* This function frees all the resources allocated to the device.
235+
* Return: 0 always
236+
*/
237+
static void ctucan_pci_remove(struct pci_dev *pdev)
238+
{
239+
struct net_device *ndev;
240+
struct ctucan_priv *priv = NULL;
241+
struct ctucan_pci_board_data *bdata = ctucan_pci_get_bdata(pdev);
242+
243+
dev_dbg(&pdev->dev, "ctucan_remove");
244+
245+
if (!bdata) {
246+
dev_err(&pdev->dev, "%s: no list of devices\n", __func__);
247+
return;
248+
}
249+
250+
/* disable interrupt in
251+
* Avalon-MM to PCI Express Interrupt Enable Register
252+
*/
253+
if (bdata->cra_base)
254+
iowrite32(0, bdata->cra_base + CYCLONE_IV_CRA_A2P_IE);
255+
256+
while ((priv = list_first_entry_or_null(&bdata->ndev_list_head, struct ctucan_priv,
257+
peers_on_pdev)) != NULL) {
258+
ndev = priv->can.dev;
259+
260+
unregister_candev(ndev);
261+
262+
netif_napi_del(&priv->napi);
263+
264+
list_del_init(&priv->peers_on_pdev);
265+
free_candev(ndev);
266+
}
267+
268+
pci_iounmap(pdev, bdata->bar1_base);
269+
270+
if (bdata->use_msi) {
271+
pci_disable_msi(pdev);
272+
pci_clear_master(pdev);
273+
}
274+
275+
pci_release_regions(pdev);
276+
pci_disable_device(pdev);
277+
278+
pci_iounmap(pdev, bdata->bar0_base);
279+
280+
pci_set_drvdata(pdev, NULL);
281+
kfree(bdata);
282+
}
283+
284+
static SIMPLE_DEV_PM_OPS(ctucan_pci_pm_ops, ctucan_suspend, ctucan_resume);
285+
286+
static const struct pci_device_id ctucan_pci_tbl[] = {
287+
{PCI_DEVICE_DATA(TEDIA, CTUCAN_VER21,
288+
CTUCAN_WITH_CTUCAN_ID)},
289+
{},
290+
};
291+
292+
static struct pci_driver ctucan_pci_driver = {
293+
.name = KBUILD_MODNAME,
294+
.id_table = ctucan_pci_tbl,
295+
.probe = ctucan_pci_probe,
296+
.remove = ctucan_pci_remove,
297+
.driver.pm = &ctucan_pci_pm_ops,
298+
};
299+
300+
module_pci_driver(ctucan_pci_driver);
301+
302+
MODULE_LICENSE("GPL");
303+
MODULE_AUTHOR("Pavel Pisa <[email protected]>");
304+
MODULE_DESCRIPTION("CTU CAN FD for PCI bus");

0 commit comments

Comments
 (0)