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 | |
48 | struct 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 | |
56 | static 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 | |
61 | static 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 | */ |
82 | static 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 | |
201 | err_free_board: |
202 | pci_set_drvdata(pdev, NULL); |
203 | kfree(objp: bdata); |
204 | err_pci_iounmap_bar0: |
205 | pci_iounmap(dev: pdev, cra_addr); |
206 | err_pci_iounmap_bar1: |
207 | pci_iounmap(dev: pdev, addr); |
208 | err_release_regions: |
209 | if (msi_ok) |
210 | pci_disable_msi(dev: pdev); |
211 | pci_release_regions(pdev); |
212 | err_disable_device: |
213 | pci_disable_device(dev: pdev); |
214 | err: |
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 | */ |
225 | static 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 | |
270 | static SIMPLE_DEV_PM_OPS(ctucan_pci_pm_ops, ctucan_suspend, ctucan_resume); |
271 | |
272 | static const struct pci_device_id ctucan_pci_tbl[] = { |
273 | {PCI_DEVICE_DATA(TEDIA, CTUCAN_VER21, |
274 | CTUCAN_WITH_CTUCAN_ID)}, |
275 | {}, |
276 | }; |
277 | |
278 | static 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 | |
286 | module_pci_driver(ctucan_pci_driver); |
287 | |
288 | MODULE_LICENSE("GPL" ); |
289 | MODULE_AUTHOR("Pavel Pisa <pisa@cmp.felk.cvut.cz>" ); |
290 | MODULE_DESCRIPTION("CTU CAN FD for PCI bus" ); |
291 | |