xref: /linux/drivers/net/can/ctucanfd/ctucanfd_pci.c (revision fab183d632628381b466a41479489541ac0e29a0)
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