Commit | Line | Data |
---|---|---|
f524f829 DM |
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 | ||
441ac340 | 17 | static u32 iomap_read_reg(struct m_can_classdev *cdev, int reg) |
f524f829 | 18 | { |
28b0ffe9 | 19 | struct m_can_plat_priv *priv = cdev->device_data; |
f524f829 DM |
20 | |
21 | return readl(priv->base + reg); | |
22 | } | |
23 | ||
441ac340 | 24 | static u32 iomap_read_fifo(struct m_can_classdev *cdev, int offset) |
f524f829 | 25 | { |
28b0ffe9 | 26 | struct m_can_plat_priv *priv = cdev->device_data; |
f524f829 DM |
27 | |
28 | return readl(priv->mram_base + offset); | |
29 | } | |
30 | ||
441ac340 | 31 | static int iomap_write_reg(struct m_can_classdev *cdev, int reg, int val) |
f524f829 | 32 | { |
28b0ffe9 | 33 | struct m_can_plat_priv *priv = cdev->device_data; |
f524f829 DM |
34 | |
35 | writel(val, priv->base + reg); | |
36 | ||
37 | return 0; | |
38 | } | |
39 | ||
441ac340 | 40 | static int iomap_write_fifo(struct m_can_classdev *cdev, int offset, int val) |
f524f829 | 41 | { |
28b0ffe9 | 42 | struct m_can_plat_priv *priv = cdev->device_data; |
f524f829 DM |
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 | { | |
441ac340 | 58 | struct m_can_classdev *mcan_class; |
f524f829 DM |
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 | priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); | |
67 | if (!priv) | |
68 | return -ENOMEM; | |
69 | ||
70 | mcan_class->device_data = priv; | |
71 | ||
72 | m_can_class_get_clocks(mcan_class); | |
73 | ||
74 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "m_can"); | |
75 | addr = devm_ioremap_resource(&pdev->dev, res); | |
76 | irq = platform_get_irq_byname(pdev, "int0"); | |
77 | if (IS_ERR(addr) || irq < 0) { | |
78 | ret = -EINVAL; | |
79 | goto failed_ret; | |
80 | } | |
81 | ||
82 | /* message ram could be shared */ | |
83 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "message_ram"); | |
84 | if (!res) { | |
85 | ret = -ENODEV; | |
86 | goto failed_ret; | |
87 | } | |
88 | ||
89 | mram_addr = devm_ioremap(&pdev->dev, res->start, resource_size(res)); | |
90 | if (!mram_addr) { | |
91 | ret = -ENOMEM; | |
92 | goto failed_ret; | |
93 | } | |
94 | ||
95 | priv->base = addr; | |
96 | priv->mram_base = mram_addr; | |
97 | ||
98 | mcan_class->net->irq = irq; | |
99 | mcan_class->pm_clock_support = 1; | |
100 | mcan_class->can.clock.freq = clk_get_rate(mcan_class->cclk); | |
101 | mcan_class->dev = &pdev->dev; | |
102 | ||
103 | mcan_class->ops = &m_can_plat_ops; | |
104 | ||
105 | mcan_class->is_peripheral = false; | |
106 | ||
107 | platform_set_drvdata(pdev, mcan_class->dev); | |
108 | ||
109 | m_can_init_ram(mcan_class); | |
110 | ||
111 | ret = m_can_class_register(mcan_class); | |
112 | ||
113 | failed_ret: | |
114 | return ret; | |
115 | } | |
116 | ||
117 | static __maybe_unused int m_can_suspend(struct device *dev) | |
118 | { | |
119 | return m_can_class_suspend(dev); | |
120 | } | |
121 | ||
122 | static __maybe_unused int m_can_resume(struct device *dev) | |
123 | { | |
124 | return m_can_class_resume(dev); | |
125 | } | |
126 | ||
127 | static int m_can_plat_remove(struct platform_device *pdev) | |
128 | { | |
129 | struct net_device *dev = platform_get_drvdata(pdev); | |
441ac340 | 130 | struct m_can_classdev *mcan_class = netdev_priv(dev); |
f524f829 DM |
131 | |
132 | m_can_class_unregister(mcan_class); | |
133 | ||
134 | platform_set_drvdata(pdev, NULL); | |
135 | ||
136 | return 0; | |
137 | } | |
138 | ||
139 | static int __maybe_unused m_can_runtime_suspend(struct device *dev) | |
140 | { | |
141 | struct net_device *ndev = dev_get_drvdata(dev); | |
441ac340 | 142 | struct m_can_classdev *mcan_class = netdev_priv(ndev); |
f524f829 DM |
143 | |
144 | m_can_class_suspend(dev); | |
145 | ||
146 | clk_disable_unprepare(mcan_class->cclk); | |
147 | clk_disable_unprepare(mcan_class->hclk); | |
148 | ||
149 | return 0; | |
150 | } | |
151 | ||
152 | static int __maybe_unused m_can_runtime_resume(struct device *dev) | |
153 | { | |
154 | struct net_device *ndev = dev_get_drvdata(dev); | |
441ac340 | 155 | struct m_can_classdev *mcan_class = netdev_priv(ndev); |
f524f829 DM |
156 | int err; |
157 | ||
158 | err = clk_prepare_enable(mcan_class->hclk); | |
159 | if (err) | |
160 | return err; | |
161 | ||
162 | err = clk_prepare_enable(mcan_class->cclk); | |
163 | if (err) | |
164 | clk_disable_unprepare(mcan_class->hclk); | |
165 | ||
166 | m_can_class_resume(dev); | |
167 | ||
168 | return err; | |
169 | } | |
170 | ||
171 | static const struct dev_pm_ops m_can_pmops = { | |
172 | SET_RUNTIME_PM_OPS(m_can_runtime_suspend, | |
173 | m_can_runtime_resume, NULL) | |
174 | SET_SYSTEM_SLEEP_PM_OPS(m_can_suspend, m_can_resume) | |
175 | }; | |
176 | ||
177 | static const struct of_device_id m_can_of_table[] = { | |
178 | { .compatible = "bosch,m_can", .data = NULL }, | |
179 | { /* sentinel */ }, | |
180 | }; | |
181 | MODULE_DEVICE_TABLE(of, m_can_of_table); | |
182 | ||
183 | static struct platform_driver m_can_plat_driver = { | |
184 | .driver = { | |
185 | .name = KBUILD_MODNAME, | |
186 | .of_match_table = m_can_of_table, | |
187 | .pm = &m_can_pmops, | |
188 | }, | |
189 | .probe = m_can_plat_probe, | |
190 | .remove = m_can_plat_remove, | |
191 | }; | |
192 | ||
193 | module_platform_driver(m_can_plat_driver); | |
194 | ||
195 | MODULE_AUTHOR("Dong Aisheng <b29396@freescale.com>"); | |
196 | MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>"); | |
197 | MODULE_LICENSE("GPL v2"); | |
198 | MODULE_DESCRIPTION("M_CAN driver for IO Mapped Bosch controllers"); |