brintos

brintos / linux-shallow public Read only

0
0
Text · 7.3 KiB · 9da09e7 Raw
291 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*******************************************************************************3 *4 * CTU CAN FD IP Core5 *6 * Copyright (C) 2015-2018 Ondrej Ille <ondrej.ille@gmail.com> FEE CTU7 * Copyright (C) 2018-2021 Ondrej Ille <ondrej.ille@gmail.com> self-funded8 * Copyright (C) 2018-2019 Martin Jerabek <martin.jerabek01@gmail.com> FEE CTU9 * Copyright (C) 2018-2022 Pavel Pisa <pisa@cmp.felk.cvut.cz> FEE CTU/self-funded10 *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_DATA26#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#endif32 33#ifndef PCI_VENDOR_ID_TEDIA34#define PCI_VENDOR_ID_TEDIA 0x176035#endif36 37#ifndef PCI_DEVICE_ID_TEDIA_CTUCAN_VER2138#define PCI_DEVICE_ID_TEDIA_CTUCAN_VER21 0xff0039#endif40 41#define CTUCAN_BAR0_CTUCAN_ID 0x000042#define CTUCAN_BAR0_CRA_BASE  0x400043#define CYCLONE_IV_CRA_A2P_IE (0x0050)44 45#define CTUCAN_WITHOUT_CTUCAN_ID  046#define CTUCAN_WITH_CTUCAN_ID     147 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 call74 * @pdev:	Handle to the pci device structure75 * @ent:	Pointer to the entry from ctucan_pci_tbl76 *77 * This function does all the memory allocation and registration for the CAN78 * device.79 *80 * Return: 0 on success and failure value on error81 */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 in190	 * Avalon-MM to PCI Express Interrupt Enable Register191	 */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 resources220 * @pdev:	Handle to the pci device structure221 *222 * This function frees all the resources allocated to the device.223 * Return: 0 always224 */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 in239	 * Avalon-MM to PCI Express Interrupt Enable Register240	 */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");291