Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.10.11.
  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(ndev);
 66	struct ctucan_pci_board_data *bdata = ctucan_pci_get_bdata(pdev);
 67
 68	list_add(&priv->peers_on_pdev, &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(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(pdev);
113	if (!ret) {
114		dev_info(dev, "MSI enabled\n");
115		pci_set_master(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(pdev, 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(pdev, 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(sizeof(*bdata), GFP_KERNEL);
157	if (!bdata) {
158		ret = -ENOMEM;
159		goto err_pci_iounmap_bar0;
160	}
161
162	INIT_LIST_HEAD(&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, bdata);
169
170	ret = ctucan_probe_common(dev, addr, irq, ntxbufs, 100000000,
171				  0, 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, 100000000,
180					  0, 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(bdata);
204err_pci_iounmap_bar0:
205	pci_iounmap(pdev, cra_addr);
206err_pci_iounmap_bar1:
207	pci_iounmap(pdev, addr);
208err_release_regions:
209	if (msi_ok)
210		pci_disable_msi(pdev);
211	pci_release_regions(pdev);
212err_disable_device:
213	pci_disable_device(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(ndev);
249
250		netif_napi_del(&priv->napi);
251
252		list_del_init(&priv->peers_on_pdev);
253		free_candev(ndev);
254	}
255
256	pci_iounmap(pdev, bdata->bar1_base);
257
258	if (bdata->use_msi)
259		pci_disable_msi(pdev);
260
261	pci_release_regions(pdev);
262	pci_disable_device(pdev);
263
264	pci_iounmap(pdev, bdata->bar0_base);
265
266	pci_set_drvdata(pdev, NULL);
267	kfree(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");