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