ctucanfd_pci.c (7516B)
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_clear_master(pdev); 212 } 213 pci_release_regions(pdev); 214err_disable_device: 215 pci_disable_device(pdev); 216err: 217 return ret; 218} 219 220/** 221 * ctucan_pci_remove - Unregister the device after releasing the resources 222 * @pdev: Handle to the pci device structure 223 * 224 * This function frees all the resources allocated to the device. 225 * Return: 0 always 226 */ 227static void ctucan_pci_remove(struct pci_dev *pdev) 228{ 229 struct net_device *ndev; 230 struct ctucan_priv *priv = NULL; 231 struct ctucan_pci_board_data *bdata = ctucan_pci_get_bdata(pdev); 232 233 dev_dbg(&pdev->dev, "ctucan_remove"); 234 235 if (!bdata) { 236 dev_err(&pdev->dev, "%s: no list of devices\n", __func__); 237 return; 238 } 239 240 /* disable interrupt in 241 * Avalon-MM to PCI Express Interrupt Enable Register 242 */ 243 if (bdata->cra_base) 244 iowrite32(0, bdata->cra_base + CYCLONE_IV_CRA_A2P_IE); 245 246 while ((priv = list_first_entry_or_null(&bdata->ndev_list_head, struct ctucan_priv, 247 peers_on_pdev)) != NULL) { 248 ndev = priv->can.dev; 249 250 unregister_candev(ndev); 251 252 netif_napi_del(&priv->napi); 253 254 list_del_init(&priv->peers_on_pdev); 255 free_candev(ndev); 256 } 257 258 pci_iounmap(pdev, bdata->bar1_base); 259 260 if (bdata->use_msi) { 261 pci_disable_msi(pdev); 262 pci_clear_master(pdev); 263 } 264 265 pci_release_regions(pdev); 266 pci_disable_device(pdev); 267 268 pci_iounmap(pdev, bdata->bar0_base); 269 270 pci_set_drvdata(pdev, NULL); 271 kfree(bdata); 272} 273 274static SIMPLE_DEV_PM_OPS(ctucan_pci_pm_ops, ctucan_suspend, ctucan_resume); 275 276static const struct pci_device_id ctucan_pci_tbl[] = { 277 {PCI_DEVICE_DATA(TEDIA, CTUCAN_VER21, 278 CTUCAN_WITH_CTUCAN_ID)}, 279 {}, 280}; 281 282static struct pci_driver ctucan_pci_driver = { 283 .name = KBUILD_MODNAME, 284 .id_table = ctucan_pci_tbl, 285 .probe = ctucan_pci_probe, 286 .remove = ctucan_pci_remove, 287 .driver.pm = &ctucan_pci_pm_ops, 288}; 289 290module_pci_driver(ctucan_pci_driver); 291 292MODULE_LICENSE("GPL"); 293MODULE_AUTHOR("Pavel Pisa <pisa@cmp.felk.cvut.cz>"); 294MODULE_DESCRIPTION("CTU CAN FD for PCI bus");