1 // SPDX-License-Identifier: GPL-2.0 2 // IOMapped CAN bus driver for Bosch M_CAN controller 3 // Copyright (C) 2014 Freescale Semiconductor, Inc. 4 // Dong Aisheng <b29396@freescale.com> 5 // 6 // Copyright (C) 2018-19 Texas Instruments Incorporated - http://www.ti.com/ 7 8 #include <linux/platform_device.h> 9 10 #include "m_can.h" 11 12 struct m_can_plat_priv { 13 void __iomem *base; 14 void __iomem *mram_base; 15 }; 16 17 static u32 iomap_read_reg(struct m_can_classdev *cdev, int reg) 18 { 19 struct m_can_plat_priv *priv = cdev->device_data; 20 21 return readl(priv->base + reg); 22 } 23 24 static u32 iomap_read_fifo(struct m_can_classdev *cdev, int offset) 25 { 26 struct m_can_plat_priv *priv = cdev->device_data; 27 28 return readl(priv->mram_base + offset); 29 } 30 31 static int iomap_write_reg(struct m_can_classdev *cdev, int reg, int val) 32 { 33 struct m_can_plat_priv *priv = cdev->device_data; 34 35 writel(val, priv->base + reg); 36 37 return 0; 38 } 39 40 static int iomap_write_fifo(struct m_can_classdev *cdev, int offset, int val) 41 { 42 struct m_can_plat_priv *priv = cdev->device_data; 43 44 writel(val, priv->mram_base + offset); 45 46 return 0; 47 } 48 49 static struct m_can_ops m_can_plat_ops = { 50 .read_reg = iomap_read_reg, 51 .write_reg = iomap_write_reg, 52 .write_fifo = iomap_write_fifo, 53 .read_fifo = iomap_read_fifo, 54 }; 55 56 static int m_can_plat_probe(struct platform_device *pdev) 57 { 58 struct m_can_classdev *mcan_class; 59 struct m_can_plat_priv *priv; 60 struct resource *res; 61 void __iomem *addr; 62 void __iomem *mram_addr; 63 int irq, ret = 0; 64 65 mcan_class = m_can_class_allocate_dev(&pdev->dev); 66 if (!mcan_class) 67 return -ENOMEM; 68 69 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 70 if (!priv) 71 return -ENOMEM; 72 73 mcan_class->device_data = priv; 74 75 m_can_class_get_clocks(mcan_class); 76 77 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "m_can"); 78 addr = devm_ioremap_resource(&pdev->dev, res); 79 irq = platform_get_irq_byname(pdev, "int0"); 80 if (IS_ERR(addr) || irq < 0) { 81 ret = -EINVAL; 82 goto failed_ret; 83 } 84 85 /* message ram could be shared */ 86 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "message_ram"); 87 if (!res) { 88 ret = -ENODEV; 89 goto failed_ret; 90 } 91 92 mram_addr = devm_ioremap(&pdev->dev, res->start, resource_size(res)); 93 if (!mram_addr) { 94 ret = -ENOMEM; 95 goto failed_ret; 96 } 97 98 priv->base = addr; 99 priv->mram_base = mram_addr; 100 101 mcan_class->net->irq = irq; 102 mcan_class->pm_clock_support = 1; 103 mcan_class->can.clock.freq = clk_get_rate(mcan_class->cclk); 104 mcan_class->dev = &pdev->dev; 105 106 mcan_class->ops = &m_can_plat_ops; 107 108 mcan_class->is_peripheral = false; 109 110 platform_set_drvdata(pdev, mcan_class->net); 111 112 m_can_init_ram(mcan_class); 113 114 ret = m_can_class_register(mcan_class); 115 116 failed_ret: 117 return ret; 118 } 119 120 static __maybe_unused int m_can_suspend(struct device *dev) 121 { 122 return m_can_class_suspend(dev); 123 } 124 125 static __maybe_unused int m_can_resume(struct device *dev) 126 { 127 return m_can_class_resume(dev); 128 } 129 130 static int m_can_plat_remove(struct platform_device *pdev) 131 { 132 struct net_device *dev = platform_get_drvdata(pdev); 133 struct m_can_classdev *mcan_class = netdev_priv(dev); 134 135 m_can_class_unregister(mcan_class); 136 137 platform_set_drvdata(pdev, NULL); 138 139 return 0; 140 } 141 142 static int __maybe_unused m_can_runtime_suspend(struct device *dev) 143 { 144 struct net_device *ndev = dev_get_drvdata(dev); 145 struct m_can_classdev *mcan_class = netdev_priv(ndev); 146 147 m_can_class_suspend(dev); 148 149 clk_disable_unprepare(mcan_class->cclk); 150 clk_disable_unprepare(mcan_class->hclk); 151 152 return 0; 153 } 154 155 static int __maybe_unused m_can_runtime_resume(struct device *dev) 156 { 157 struct net_device *ndev = dev_get_drvdata(dev); 158 struct m_can_classdev *mcan_class = netdev_priv(ndev); 159 int err; 160 161 err = clk_prepare_enable(mcan_class->hclk); 162 if (err) 163 return err; 164 165 err = clk_prepare_enable(mcan_class->cclk); 166 if (err) 167 clk_disable_unprepare(mcan_class->hclk); 168 169 return err; 170 } 171 172 static const struct dev_pm_ops m_can_pmops = { 173 SET_RUNTIME_PM_OPS(m_can_runtime_suspend, 174 m_can_runtime_resume, NULL) 175 SET_SYSTEM_SLEEP_PM_OPS(m_can_suspend, m_can_resume) 176 }; 177 178 static const struct of_device_id m_can_of_table[] = { 179 { .compatible = "bosch,m_can", .data = NULL }, 180 { /* sentinel */ }, 181 }; 182 MODULE_DEVICE_TABLE(of, m_can_of_table); 183 184 static struct platform_driver m_can_plat_driver = { 185 .driver = { 186 .name = KBUILD_MODNAME, 187 .of_match_table = m_can_of_table, 188 .pm = &m_can_pmops, 189 }, 190 .probe = m_can_plat_probe, 191 .remove = m_can_plat_remove, 192 }; 193 194 module_platform_driver(m_can_plat_driver); 195 196 MODULE_AUTHOR("Dong Aisheng <b29396@freescale.com>"); 197 MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>"); 198 MODULE_LICENSE("GPL v2"); 199 MODULE_DESCRIPTION("M_CAN driver for IO Mapped Bosch controllers"); 200