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/hrtimer.h> |
9 | #include <linux/phy/phy.h> |
10 | #include <linux/platform_device.h> |
11 | |
12 | #include "m_can.h" |
13 | |
14 | struct m_can_plat_priv { |
15 | struct m_can_classdev cdev; |
16 | |
17 | void __iomem *base; |
18 | void __iomem *mram_base; |
19 | }; |
20 | |
21 | static inline struct m_can_plat_priv *cdev_to_priv(struct m_can_classdev *cdev) |
22 | { |
23 | return container_of(cdev, struct m_can_plat_priv, cdev); |
24 | } |
25 | |
26 | static u32 iomap_read_reg(struct m_can_classdev *cdev, int reg) |
27 | { |
28 | struct m_can_plat_priv *priv = cdev_to_priv(cdev); |
29 | |
30 | return readl(addr: priv->base + reg); |
31 | } |
32 | |
33 | static int iomap_read_fifo(struct m_can_classdev *cdev, int offset, void *val, size_t val_count) |
34 | { |
35 | struct m_can_plat_priv *priv = cdev_to_priv(cdev); |
36 | void __iomem *src = priv->mram_base + offset; |
37 | |
38 | while (val_count--) { |
39 | *(unsigned int *)val = ioread32(src); |
40 | val += 4; |
41 | src += 4; |
42 | } |
43 | |
44 | return 0; |
45 | } |
46 | |
47 | static int iomap_write_reg(struct m_can_classdev *cdev, int reg, int val) |
48 | { |
49 | struct m_can_plat_priv *priv = cdev_to_priv(cdev); |
50 | |
51 | writel(val, addr: priv->base + reg); |
52 | |
53 | return 0; |
54 | } |
55 | |
56 | static int iomap_write_fifo(struct m_can_classdev *cdev, int offset, |
57 | const void *val, size_t val_count) |
58 | { |
59 | struct m_can_plat_priv *priv = cdev_to_priv(cdev); |
60 | void __iomem *dst = priv->mram_base + offset; |
61 | |
62 | while (val_count--) { |
63 | iowrite32(*(unsigned int *)val, dst); |
64 | val += 4; |
65 | dst += 4; |
66 | } |
67 | |
68 | return 0; |
69 | } |
70 | |
71 | static struct m_can_ops m_can_plat_ops = { |
72 | .read_reg = iomap_read_reg, |
73 | .write_reg = iomap_write_reg, |
74 | .write_fifo = iomap_write_fifo, |
75 | .read_fifo = iomap_read_fifo, |
76 | }; |
77 | |
78 | static int m_can_plat_probe(struct platform_device *pdev) |
79 | { |
80 | struct m_can_classdev *mcan_class; |
81 | struct m_can_plat_priv *priv; |
82 | struct resource *res; |
83 | void __iomem *addr; |
84 | void __iomem *mram_addr; |
85 | struct phy *transceiver; |
86 | int irq = 0, ret = 0; |
87 | |
88 | mcan_class = m_can_class_allocate_dev(dev: &pdev->dev, |
89 | sizeof_priv: sizeof(struct m_can_plat_priv)); |
90 | if (!mcan_class) |
91 | return -ENOMEM; |
92 | |
93 | priv = cdev_to_priv(cdev: mcan_class); |
94 | |
95 | ret = m_can_class_get_clocks(cdev: mcan_class); |
96 | if (ret) |
97 | goto probe_fail; |
98 | |
99 | addr = devm_platform_ioremap_resource_byname(pdev, name: "m_can" ); |
100 | if (IS_ERR(ptr: addr)) { |
101 | ret = PTR_ERR(ptr: addr); |
102 | goto probe_fail; |
103 | } |
104 | |
105 | if (device_property_present(dev: mcan_class->dev, propname: "interrupts" ) || |
106 | device_property_present(dev: mcan_class->dev, propname: "interrupt-names" )) { |
107 | irq = platform_get_irq_byname(pdev, "int0" ); |
108 | if (irq < 0) { |
109 | ret = irq; |
110 | goto probe_fail; |
111 | } |
112 | } |
113 | |
114 | /* message ram could be shared */ |
115 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "message_ram" ); |
116 | if (!res) { |
117 | ret = -ENODEV; |
118 | goto probe_fail; |
119 | } |
120 | |
121 | mram_addr = devm_ioremap(dev: &pdev->dev, offset: res->start, size: resource_size(res)); |
122 | if (!mram_addr) { |
123 | ret = -ENOMEM; |
124 | goto probe_fail; |
125 | } |
126 | |
127 | transceiver = devm_phy_optional_get(dev: &pdev->dev, NULL); |
128 | if (IS_ERR(ptr: transceiver)) { |
129 | ret = PTR_ERR(ptr: transceiver); |
130 | dev_err_probe(dev: &pdev->dev, err: ret, fmt: "failed to get phy\n" ); |
131 | goto probe_fail; |
132 | } |
133 | |
134 | if (transceiver) |
135 | mcan_class->can.bitrate_max = transceiver->attrs.max_link_rate; |
136 | |
137 | priv->base = addr; |
138 | priv->mram_base = mram_addr; |
139 | |
140 | mcan_class->net->irq = irq; |
141 | mcan_class->pm_clock_support = 1; |
142 | mcan_class->pm_wake_source = 0; |
143 | mcan_class->can.clock.freq = clk_get_rate(clk: mcan_class->cclk); |
144 | mcan_class->dev = &pdev->dev; |
145 | mcan_class->transceiver = transceiver; |
146 | |
147 | mcan_class->ops = &m_can_plat_ops; |
148 | |
149 | mcan_class->is_peripheral = false; |
150 | |
151 | platform_set_drvdata(pdev, data: mcan_class); |
152 | |
153 | pm_runtime_enable(dev: mcan_class->dev); |
154 | ret = m_can_class_register(cdev: mcan_class); |
155 | if (ret) |
156 | goto out_runtime_disable; |
157 | |
158 | return ret; |
159 | |
160 | out_runtime_disable: |
161 | pm_runtime_disable(dev: mcan_class->dev); |
162 | probe_fail: |
163 | m_can_class_free_dev(net: mcan_class->net); |
164 | return ret; |
165 | } |
166 | |
167 | static __maybe_unused int m_can_suspend(struct device *dev) |
168 | { |
169 | return m_can_class_suspend(dev); |
170 | } |
171 | |
172 | static __maybe_unused int m_can_resume(struct device *dev) |
173 | { |
174 | return m_can_class_resume(dev); |
175 | } |
176 | |
177 | static void m_can_plat_remove(struct platform_device *pdev) |
178 | { |
179 | struct m_can_plat_priv *priv = platform_get_drvdata(pdev); |
180 | struct m_can_classdev *mcan_class = &priv->cdev; |
181 | |
182 | m_can_class_unregister(cdev: mcan_class); |
183 | |
184 | m_can_class_free_dev(net: mcan_class->net); |
185 | } |
186 | |
187 | static int __maybe_unused m_can_runtime_suspend(struct device *dev) |
188 | { |
189 | struct m_can_plat_priv *priv = dev_get_drvdata(dev); |
190 | struct m_can_classdev *mcan_class = &priv->cdev; |
191 | |
192 | clk_disable_unprepare(clk: mcan_class->cclk); |
193 | clk_disable_unprepare(clk: mcan_class->hclk); |
194 | |
195 | return 0; |
196 | } |
197 | |
198 | static int __maybe_unused m_can_runtime_resume(struct device *dev) |
199 | { |
200 | struct m_can_plat_priv *priv = dev_get_drvdata(dev); |
201 | struct m_can_classdev *mcan_class = &priv->cdev; |
202 | int err; |
203 | |
204 | err = clk_prepare_enable(clk: mcan_class->hclk); |
205 | if (err) |
206 | return err; |
207 | |
208 | err = clk_prepare_enable(clk: mcan_class->cclk); |
209 | if (err) |
210 | clk_disable_unprepare(clk: mcan_class->hclk); |
211 | |
212 | return err; |
213 | } |
214 | |
215 | static const struct dev_pm_ops m_can_pmops = { |
216 | SET_RUNTIME_PM_OPS(m_can_runtime_suspend, |
217 | m_can_runtime_resume, NULL) |
218 | SET_SYSTEM_SLEEP_PM_OPS(m_can_suspend, m_can_resume) |
219 | }; |
220 | |
221 | static const struct of_device_id m_can_of_table[] = { |
222 | { .compatible = "bosch,m_can" , .data = NULL }, |
223 | { /* sentinel */ }, |
224 | }; |
225 | MODULE_DEVICE_TABLE(of, m_can_of_table); |
226 | |
227 | static struct platform_driver m_can_plat_driver = { |
228 | .driver = { |
229 | .name = KBUILD_MODNAME, |
230 | .of_match_table = m_can_of_table, |
231 | .pm = &m_can_pmops, |
232 | }, |
233 | .probe = m_can_plat_probe, |
234 | .remove_new = m_can_plat_remove, |
235 | }; |
236 | |
237 | module_platform_driver(m_can_plat_driver); |
238 | |
239 | MODULE_AUTHOR("Dong Aisheng <b29396@freescale.com>" ); |
240 | MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>" ); |
241 | MODULE_LICENSE("GPL v2" ); |
242 | MODULE_DESCRIPTION("M_CAN driver for IO Mapped Bosch controllers" ); |
243 | |