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 <ondrej.ille@gmail.com> FEE CTU
7 * Copyright (C) 2018-2021 Ondrej Ille <ondrej.ille@gmail.com> self-funded
8 * Copyright (C) 2018-2019 Martin Jerabek <martin.jerabek01@gmail.com> FEE CTU
9 * Copyright (C) 2018-2022 Pavel Pisa <pisa@cmp.felk.cvut.cz> FEE CTU/self-funded
10 *
11 * Project advisors:
12 * Jiri Novak <jnovak@fel.cvut.cz>
13 * Pavel Pisa <pisa@cmp.felk.cvut.cz>
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
48struct ctucan_pci_board_data {
49 void __iomem *bar0_base;
50 void __iomem *cra_base;
51 void __iomem *bar1_base;
52 struct list_head ndev_list_head;
53 int use_msi;
54};
55
56static struct ctucan_pci_board_data *ctucan_pci_get_bdata(struct pci_dev *pdev)
57{
58 return (struct ctucan_pci_board_data *)pci_get_drvdata(pdev);
59}
60
61static void ctucan_pci_set_drvdata(struct device *dev,
62 struct net_device *ndev)
63{
64 struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
65 struct ctucan_priv *priv = netdev_priv(dev: ndev);
66 struct ctucan_pci_board_data *bdata = ctucan_pci_get_bdata(pdev);
67
68 list_add(new: &priv->peers_on_pdev, head: &bdata->ndev_list_head);
69 priv->irq_flags = IRQF_SHARED;
70}
71
72/**
73 * ctucan_pci_probe - PCI registration call
74 * @pdev: Handle to the pci device structure
75 * @ent: Pointer to the entry from ctucan_pci_tbl
76 *
77 * This function does all the memory allocation and registration for the CAN
78 * device.
79 *
80 * Return: 0 on success and failure value on error
81 */
82static int ctucan_pci_probe(struct pci_dev *pdev,
83 const struct pci_device_id *ent)
84{
85 struct device *dev = &pdev->dev;
86 unsigned long driver_data = ent->driver_data;
87 struct ctucan_pci_board_data *bdata;
88 void __iomem *addr;
89 void __iomem *cra_addr;
90 void __iomem *bar0_base;
91 u32 cra_a2p_ie;
92 u32 ctucan_id = 0;
93 int ret;
94 unsigned int ntxbufs;
95 unsigned int num_cores = 1;
96 unsigned int core_i = 0;
97 int irq;
98 int msi_ok = 0;
99
100 ret = pci_enable_device(dev: pdev);
101 if (ret) {
102 dev_err(dev, "pci_enable_device FAILED\n");
103 goto err;
104 }
105
106 ret = pci_request_regions(pdev, KBUILD_MODNAME);
107 if (ret) {
108 dev_err(dev, "pci_request_regions FAILED\n");
109 goto err_disable_device;
110 }
111
112 ret = pci_enable_msi(dev: pdev);
113 if (!ret) {
114 dev_info(dev, "MSI enabled\n");
115 pci_set_master(dev: pdev);
116 msi_ok = 1;
117 }
118
119 dev_info(dev, "ctucan BAR0 0x%08llx 0x%08llx\n",
120 (long long)pci_resource_start(pdev, 0),
121 (long long)pci_resource_len(pdev, 0));
122
123 dev_info(dev, "ctucan BAR1 0x%08llx 0x%08llx\n",
124 (long long)pci_resource_start(pdev, 1),
125 (long long)pci_resource_len(pdev, 1));
126
127 addr = pci_iomap(dev: pdev, bar: 1, pci_resource_len(pdev, 1));
128 if (!addr) {
129 dev_err(dev, "PCI BAR 1 cannot be mapped\n");
130 ret = -ENOMEM;
131 goto err_release_regions;
132 }
133
134 /* Cyclone IV PCI Express Control Registers Area */
135 bar0_base = pci_iomap(dev: pdev, bar: 0, pci_resource_len(pdev, 0));
136 if (!bar0_base) {
137 dev_err(dev, "PCI BAR 0 cannot be mapped\n");
138 ret = -EIO;
139 goto err_pci_iounmap_bar1;
140 }
141
142 if (driver_data == CTUCAN_WITHOUT_CTUCAN_ID) {
143 cra_addr = bar0_base;
144 num_cores = 2;
145 } else {
146 cra_addr = bar0_base + CTUCAN_BAR0_CRA_BASE;
147 ctucan_id = ioread32(bar0_base + CTUCAN_BAR0_CTUCAN_ID);
148 dev_info(dev, "ctucan_id 0x%08lx\n", (unsigned long)ctucan_id);
149 num_cores = ctucan_id & 0xf;
150 }
151
152 irq = pdev->irq;
153
154 ntxbufs = 4;
155
156 bdata = kzalloc(size: sizeof(*bdata), GFP_KERNEL);
157 if (!bdata) {
158 ret = -ENOMEM;
159 goto err_pci_iounmap_bar0;
160 }
161
162 INIT_LIST_HEAD(list: &bdata->ndev_list_head);
163 bdata->bar0_base = bar0_base;
164 bdata->cra_base = cra_addr;
165 bdata->bar1_base = addr;
166 bdata->use_msi = msi_ok;
167
168 pci_set_drvdata(pdev, data: bdata);
169
170 ret = ctucan_probe_common(dev, addr, irq, ntxbufs, can_clk_rate: 100000000,
171 pm_enable_call: 0, set_drvdata_fnc: ctucan_pci_set_drvdata);
172 if (ret < 0)
173 goto err_free_board;
174
175 core_i++;
176
177 while (core_i < num_cores) {
178 addr += 0x4000;
179 ret = ctucan_probe_common(dev, addr, irq, ntxbufs, can_clk_rate: 100000000,
180 pm_enable_call: 0, set_drvdata_fnc: ctucan_pci_set_drvdata);
181 if (ret < 0) {
182 dev_info(dev, "CTU CAN FD core %d initialization failed\n",
183 core_i);
184 break;
185 }
186 core_i++;
187 }
188
189 /* enable interrupt in
190 * Avalon-MM to PCI Express Interrupt Enable Register
191 */
192 cra_a2p_ie = ioread32(cra_addr + CYCLONE_IV_CRA_A2P_IE);
193 dev_info(dev, "cra_a2p_ie 0x%08x\n", cra_a2p_ie);
194 cra_a2p_ie |= 1;
195 iowrite32(cra_a2p_ie, cra_addr + CYCLONE_IV_CRA_A2P_IE);
196 cra_a2p_ie = ioread32(cra_addr + CYCLONE_IV_CRA_A2P_IE);
197 dev_info(dev, "cra_a2p_ie 0x%08x\n", cra_a2p_ie);
198
199 return 0;
200
201err_free_board:
202 pci_set_drvdata(pdev, NULL);
203 kfree(objp: bdata);
204err_pci_iounmap_bar0:
205 pci_iounmap(dev: pdev, cra_addr);
206err_pci_iounmap_bar1:
207 pci_iounmap(dev: pdev, addr);
208err_release_regions:
209 if (msi_ok)
210 pci_disable_msi(dev: pdev);
211 pci_release_regions(pdev);
212err_disable_device:
213 pci_disable_device(dev: pdev);
214err:
215 return ret;
216}
217
218/**
219 * ctucan_pci_remove - Unregister the device after releasing the resources
220 * @pdev: Handle to the pci device structure
221 *
222 * This function frees all the resources allocated to the device.
223 * Return: 0 always
224 */
225static void ctucan_pci_remove(struct pci_dev *pdev)
226{
227 struct net_device *ndev;
228 struct ctucan_priv *priv = NULL;
229 struct ctucan_pci_board_data *bdata = ctucan_pci_get_bdata(pdev);
230
231 dev_dbg(&pdev->dev, "ctucan_remove");
232
233 if (!bdata) {
234 dev_err(&pdev->dev, "%s: no list of devices\n", __func__);
235 return;
236 }
237
238 /* disable interrupt in
239 * Avalon-MM to PCI Express Interrupt Enable Register
240 */
241 if (bdata->cra_base)
242 iowrite32(0, bdata->cra_base + CYCLONE_IV_CRA_A2P_IE);
243
244 while ((priv = list_first_entry_or_null(&bdata->ndev_list_head, struct ctucan_priv,
245 peers_on_pdev)) != NULL) {
246 ndev = priv->can.dev;
247
248 unregister_candev(dev: ndev);
249
250 netif_napi_del(napi: &priv->napi);
251
252 list_del_init(entry: &priv->peers_on_pdev);
253 free_candev(dev: ndev);
254 }
255
256 pci_iounmap(dev: pdev, bdata->bar1_base);
257
258 if (bdata->use_msi)
259 pci_disable_msi(dev: pdev);
260
261 pci_release_regions(pdev);
262 pci_disable_device(dev: pdev);
263
264 pci_iounmap(dev: pdev, bdata->bar0_base);
265
266 pci_set_drvdata(pdev, NULL);
267 kfree(objp: bdata);
268}
269
270static SIMPLE_DEV_PM_OPS(ctucan_pci_pm_ops, ctucan_suspend, ctucan_resume);
271
272static const struct pci_device_id ctucan_pci_tbl[] = {
273 {PCI_DEVICE_DATA(TEDIA, CTUCAN_VER21,
274 CTUCAN_WITH_CTUCAN_ID)},
275 {},
276};
277
278static struct pci_driver ctucan_pci_driver = {
279 .name = KBUILD_MODNAME,
280 .id_table = ctucan_pci_tbl,
281 .probe = ctucan_pci_probe,
282 .remove = ctucan_pci_remove,
283 .driver.pm = &ctucan_pci_pm_ops,
284};
285
286module_pci_driver(ctucan_pci_driver);
287
288MODULE_LICENSE("GPL");
289MODULE_AUTHOR("Pavel Pisa <pisa@cmp.felk.cvut.cz>");
290MODULE_DESCRIPTION("CTU CAN FD for PCI bus");
291

source code of linux/drivers/net/can/ctucanfd/ctucanfd_pci.c