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/netdevice.h> 22 #include <linux/of.h> 23 #include <linux/platform_device.h> 24 #include <linux/pm_runtime.h> 25 26 #include "ctucanfd.h" 27 28 #define DRV_NAME "ctucanfd" 29 30 static void ctucan_platform_set_drvdata(struct device *dev, 31 struct net_device *ndev) 32 { 33 struct platform_device *pdev = container_of(dev, struct platform_device, 34 dev); 35 36 platform_set_drvdata(pdev, ndev); 37 } 38 39 /** 40 * ctucan_platform_probe - Platform registration call 41 * @pdev: Handle to the platform device structure 42 * 43 * This function does all the memory allocation and registration for the CAN 44 * device. 45 * 46 * Return: 0 on success and failure value on error 47 */ 48 static int ctucan_platform_probe(struct platform_device *pdev) 49 { 50 struct resource *res; /* IO mem resources */ 51 struct device *dev = &pdev->dev; 52 void __iomem *addr; 53 int ret; 54 unsigned int ntxbufs; 55 int irq; 56 57 /* Get the virtual base address for the device */ 58 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 59 addr = devm_ioremap_resource(dev, res); 60 if (IS_ERR(addr)) { 61 dev_err(dev, "Cannot remap address.\n"); 62 ret = PTR_ERR(addr); 63 goto err; 64 } 65 irq = platform_get_irq(pdev, 0); 66 if (irq < 0) { 67 dev_err(dev, "Cannot find interrupt.\n"); 68 ret = irq; 69 goto err; 70 } 71 72 /* Number of tx bufs might be change in HW for future. If so, 73 * it will be passed as property via device tree 74 */ 75 ntxbufs = 4; 76 ret = ctucan_probe_common(dev, addr, irq, ntxbufs, 0, 77 1, ctucan_platform_set_drvdata); 78 79 if (ret < 0) 80 platform_set_drvdata(pdev, NULL); 81 82 err: 83 return ret; 84 } 85 86 /** 87 * ctucan_platform_remove - Unregister the device after releasing the resources 88 * @pdev: Handle to the platform device structure 89 * 90 * This function frees all the resources allocated to the device. 91 * Return: 0 always 92 */ 93 static int ctucan_platform_remove(struct platform_device *pdev) 94 { 95 struct net_device *ndev = platform_get_drvdata(pdev); 96 struct ctucan_priv *priv = netdev_priv(ndev); 97 98 netdev_dbg(ndev, "ctucan_remove"); 99 100 unregister_candev(ndev); 101 pm_runtime_disable(&pdev->dev); 102 netif_napi_del(&priv->napi); 103 free_candev(ndev); 104 105 return 0; 106 } 107 108 static SIMPLE_DEV_PM_OPS(ctucan_platform_pm_ops, ctucan_suspend, ctucan_resume); 109 110 /* Match table for OF platform binding */ 111 static const struct of_device_id ctucan_of_match[] = { 112 { .compatible = "ctu,ctucanfd-2", }, 113 { .compatible = "ctu,ctucanfd", }, 114 { /* end of list */ }, 115 }; 116 MODULE_DEVICE_TABLE(of, ctucan_of_match); 117 118 static struct platform_driver ctucanfd_driver = { 119 .probe = ctucan_platform_probe, 120 .remove = ctucan_platform_remove, 121 .driver = { 122 .name = DRV_NAME, 123 .pm = &ctucan_platform_pm_ops, 124 .of_match_table = ctucan_of_match, 125 }, 126 }; 127 128 module_platform_driver(ctucanfd_driver); 129 130 MODULE_LICENSE("GPL"); 131 MODULE_AUTHOR("Martin Jerabek"); 132 MODULE_DESCRIPTION("CTU CAN FD for platform"); 133