// SPDX-License-Identifier: GPL-2.0-or-later /******************************************************************************* * * CTU CAN FD IP Core * * Copyright (C) 2015-2018 Ondrej Ille FEE CTU * Copyright (C) 2018-2021 Ondrej Ille self-funded * Copyright (C) 2018-2019 Martin Jerabek FEE CTU * Copyright (C) 2018-2022 Pavel Pisa FEE CTU/self-funded * * Project advisors: * Jiri Novak * Pavel Pisa * * Department of Measurement (http://meas.fel.cvut.cz/) * Faculty of Electrical Engineering (http://www.fel.cvut.cz) * Czech Technical University (http://www.cvut.cz/) ******************************************************************************/ #include #include #include "ctucanfd.h" #ifndef PCI_DEVICE_DATA #define PCI_DEVICE_DATA(vend, dev, data) \ .vendor = PCI_VENDOR_ID_##vend, \ .device = PCI_DEVICE_ID_##vend##_##dev, \ .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, 0, 0, \ .driver_data = (kernel_ulong_t)(data) #endif #ifndef PCI_VENDOR_ID_TEDIA #define PCI_VENDOR_ID_TEDIA 0x1760 #endif #ifndef PCI_DEVICE_ID_TEDIA_CTUCAN_VER21 #define PCI_DEVICE_ID_TEDIA_CTUCAN_VER21 0xff00 #endif #define CTUCAN_BAR0_CTUCAN_ID 0x0000 #define CTUCAN_BAR0_CRA_BASE 0x4000 #define CYCLONE_IV_CRA_A2P_IE (0x0050) #define CTUCAN_WITHOUT_CTUCAN_ID 0 #define CTUCAN_WITH_CTUCAN_ID 1 struct ctucan_pci_board_data { void __iomem *bar0_base; void __iomem *cra_base; void __iomem *bar1_base; struct list_head ndev_list_head; int use_msi; }; static struct ctucan_pci_board_data *ctucan_pci_get_bdata(struct pci_dev *pdev) { return (struct ctucan_pci_board_data *)pci_get_drvdata(pdev); } static void ctucan_pci_set_drvdata(struct device *dev, struct net_device *ndev) { struct pci_dev *pdev = container_of(dev, struct pci_dev, dev); struct ctucan_priv *priv = netdev_priv(ndev); struct ctucan_pci_board_data *bdata = ctucan_pci_get_bdata(pdev); list_add(&priv->peers_on_pdev, &bdata->ndev_list_head); priv->irq_flags = IRQF_SHARED; } /** * ctucan_pci_probe - PCI registration call * @pdev: Handle to the pci device structure * @ent: Pointer to the entry from ctucan_pci_tbl * * This function does all the memory allocation and registration for the CAN * device. * * Return: 0 on success and failure value on error */ static int ctucan_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { struct device *dev = &pdev->dev; unsigned long driver_data = ent->driver_data; struct ctucan_pci_board_data *bdata; void __iomem *addr; void __iomem *cra_addr; void __iomem *bar0_base; u32 cra_a2p_ie; u32 ctucan_id = 0; int ret; unsigned int ntxbufs; unsigned int num_cores = 1; unsigned int core_i = 0; int irq; int msi_ok = 0; ret = pci_enable_device(pdev); if (ret) { dev_err(dev, "pci_enable_device FAILED\n"); goto err; } ret = pci_request_regions(pdev, KBUILD_MODNAME); if (ret) { dev_err(dev, "pci_request_regions FAILED\n"); goto err_disable_device; } ret = pci_enable_msi(pdev); if (!ret) { dev_info(dev, "MSI enabled\n"); pci_set_master(pdev); msi_ok = 1; } dev_info(dev, "ctucan BAR0 0x%08llx 0x%08llx\n", (long long)pci_resource_start(pdev, 0), (long long)pci_resource_len(pdev, 0)); dev_info(dev, "ctucan BAR1 0x%08llx 0x%08llx\n", (long long)pci_resource_start(pdev, 1), (long long)pci_resource_len(pdev, 1)); addr = pci_iomap(pdev, 1, pci_resource_len(pdev, 1)); if (!addr) { dev_err(dev, "PCI BAR 1 cannot be mapped\n"); ret = -ENOMEM; goto err_release_regions; } /* Cyclone IV PCI Express Control Registers Area */ bar0_base = pci_iomap(pdev, 0, pci_resource_len(pdev, 0)); if (!bar0_base) { dev_err(dev, "PCI BAR 0 cannot be mapped\n"); ret = -EIO; goto err_pci_iounmap_bar1; } if (driver_data == CTUCAN_WITHOUT_CTUCAN_ID) { cra_addr = bar0_base; num_cores = 2; } else { cra_addr = bar0_base + CTUCAN_BAR0_CRA_BASE; ctucan_id = ioread32(bar0_base + CTUCAN_BAR0_CTUCAN_ID); dev_info(dev, "ctucan_id 0x%08lx\n", (unsigned long)ctucan_id); num_cores = ctucan_id & 0xf; } irq = pdev->irq; ntxbufs = 4; bdata = kzalloc(sizeof(*bdata), GFP_KERNEL); if (!bdata) { ret = -ENOMEM; goto err_pci_iounmap_bar0; } INIT_LIST_HEAD(&bdata->ndev_list_head); bdata->bar0_base = bar0_base; bdata->cra_base = cra_addr; bdata->bar1_base = addr; bdata->use_msi = msi_ok; pci_set_drvdata(pdev, bdata); ret = ctucan_probe_common(dev, addr, irq, ntxbufs, 100000000, 0, ctucan_pci_set_drvdata); if (ret < 0) goto err_free_board; core_i++; while (core_i < num_cores) { addr += 0x4000; ret = ctucan_probe_common(dev, addr, irq, ntxbufs, 100000000, 0, ctucan_pci_set_drvdata); if (ret < 0) { dev_info(dev, "CTU CAN FD core %d initialization failed\n", core_i); break; } core_i++; } /* enable interrupt in * Avalon-MM to PCI Express Interrupt Enable Register */ cra_a2p_ie = ioread32(cra_addr + CYCLONE_IV_CRA_A2P_IE); dev_info(dev, "cra_a2p_ie 0x%08x\n", cra_a2p_ie); cra_a2p_ie |= 1; iowrite32(cra_a2p_ie, cra_addr + CYCLONE_IV_CRA_A2P_IE); cra_a2p_ie = ioread32(cra_addr + CYCLONE_IV_CRA_A2P_IE); dev_info(dev, "cra_a2p_ie 0x%08x\n", cra_a2p_ie); return 0; err_free_board: pci_set_drvdata(pdev, NULL); kfree(bdata); err_pci_iounmap_bar0: pci_iounmap(pdev, cra_addr); err_pci_iounmap_bar1: pci_iounmap(pdev, addr); err_release_regions: if (msi_ok) pci_disable_msi(pdev); pci_release_regions(pdev); err_disable_device: pci_disable_device(pdev); err: return ret; } /** * ctucan_pci_remove - Unregister the device after releasing the resources * @pdev: Handle to the pci device structure * * This function frees all the resources allocated to the device. * Return: 0 always */ static void ctucan_pci_remove(struct pci_dev *pdev) { struct net_device *ndev; struct ctucan_priv *priv = NULL; struct ctucan_pci_board_data *bdata = ctucan_pci_get_bdata(pdev); dev_dbg(&pdev->dev, "ctucan_remove"); if (!bdata) { dev_err(&pdev->dev, "%s: no list of devices\n", __func__); return; } /* disable interrupt in * Avalon-MM to PCI Express Interrupt Enable Register */ if (bdata->cra_base) iowrite32(0, bdata->cra_base + CYCLONE_IV_CRA_A2P_IE); while ((priv = list_first_entry_or_null(&bdata->ndev_list_head, struct ctucan_priv, peers_on_pdev)) != NULL) { ndev = priv->can.dev; unregister_candev(ndev); netif_napi_del(&priv->napi); list_del_init(&priv->peers_on_pdev); free_candev(ndev); } pci_iounmap(pdev, bdata->bar1_base); if (bdata->use_msi) pci_disable_msi(pdev); pci_release_regions(pdev); pci_disable_device(pdev); pci_iounmap(pdev, bdata->bar0_base); pci_set_drvdata(pdev, NULL); kfree(bdata); } static SIMPLE_DEV_PM_OPS(ctucan_pci_pm_ops, ctucan_suspend, ctucan_resume); static const struct pci_device_id ctucan_pci_tbl[] = { {PCI_DEVICE_DATA(TEDIA, CTUCAN_VER21, CTUCAN_WITH_CTUCAN_ID)}, {}, }; static struct pci_driver ctucan_pci_driver = { .name = KBUILD_MODNAME, .id_table = ctucan_pci_tbl, .probe = ctucan_pci_probe, .remove = ctucan_pci_remove, .driver.pm = &ctucan_pci_pm_ops, }; module_pci_driver(ctucan_pci_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Pavel Pisa "); MODULE_DESCRIPTION("CTU CAN FD for PCI bus");