123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294 |
- // SPDX-License-Identifier: GPL-2.0-or-later
- /*******************************************************************************
- *
- * CTU CAN FD IP Core
- *
- * Copyright (C) 2015-2018 Ondrej Ille <[email protected]> FEE CTU
- * Copyright (C) 2018-2021 Ondrej Ille <[email protected]> self-funded
- * Copyright (C) 2018-2019 Martin Jerabek <[email protected]> FEE CTU
- * Copyright (C) 2018-2022 Pavel Pisa <[email protected]> FEE CTU/self-funded
- *
- * Project advisors:
- * Jiri Novak <[email protected]>
- * Pavel Pisa <[email protected]>
- *
- * 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 <linux/module.h>
- #include <linux/pci.h>
- #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_clear_master(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_clear_master(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 <[email protected]>");
- MODULE_DESCRIPTION("CTU CAN FD for PCI bus");
|