1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * dwc3-xilinx.c - Xilinx DWC3 controller specific glue driver |
4 | * |
5 | * Authors: Manish Narani <manish.narani@xilinx.com> |
6 | * Anurag Kumar Vulisha <anurag.kumar.vulisha@xilinx.com> |
7 | */ |
8 | |
9 | #include <linux/module.h> |
10 | #include <linux/kernel.h> |
11 | #include <linux/slab.h> |
12 | #include <linux/clk.h> |
13 | #include <linux/of.h> |
14 | #include <linux/platform_device.h> |
15 | #include <linux/dma-mapping.h> |
16 | #include <linux/gpio/consumer.h> |
17 | #include <linux/of_platform.h> |
18 | #include <linux/pm_runtime.h> |
19 | #include <linux/reset.h> |
20 | #include <linux/of_address.h> |
21 | #include <linux/delay.h> |
22 | #include <linux/firmware/xlnx-zynqmp.h> |
23 | #include <linux/io.h> |
24 | |
25 | #include <linux/phy/phy.h> |
26 | |
27 | /* USB phy reset mask register */ |
28 | #define XLNX_USB_PHY_RST_EN 0x001C |
29 | #define XLNX_PHY_RST_MASK 0x1 |
30 | |
31 | /* Xilinx USB 3.0 IP Register */ |
32 | #define XLNX_USB_TRAFFIC_ROUTE_CONFIG 0x005C |
33 | #define XLNX_USB_TRAFFIC_ROUTE_FPD 0x1 |
34 | |
35 | #define XLNX_USB_FPD_PIPE_CLK 0x7c |
36 | #define PIPE_CLK_DESELECT 1 |
37 | #define PIPE_CLK_SELECT 0 |
38 | #define XLNX_USB_FPD_POWER_PRSNT 0x80 |
39 | #define FPD_POWER_PRSNT_OPTION BIT(0) |
40 | |
41 | struct dwc3_xlnx { |
42 | int num_clocks; |
43 | struct clk_bulk_data *clks; |
44 | struct device *dev; |
45 | void __iomem *regs; |
46 | int (*pltfm_init)(struct dwc3_xlnx *data); |
47 | struct phy *usb3_phy; |
48 | }; |
49 | |
50 | static void dwc3_xlnx_mask_phy_rst(struct dwc3_xlnx *priv_data, bool mask) |
51 | { |
52 | u32 reg; |
53 | |
54 | /* |
55 | * Enable or disable ULPI PHY reset from USB Controller. |
56 | * This does not actually reset the phy, but just controls |
57 | * whether USB controller can or cannot reset ULPI PHY. |
58 | */ |
59 | reg = readl(addr: priv_data->regs + XLNX_USB_PHY_RST_EN); |
60 | |
61 | if (mask) |
62 | reg &= ~XLNX_PHY_RST_MASK; |
63 | else |
64 | reg |= XLNX_PHY_RST_MASK; |
65 | |
66 | writel(val: reg, addr: priv_data->regs + XLNX_USB_PHY_RST_EN); |
67 | } |
68 | |
69 | static int dwc3_xlnx_init_versal(struct dwc3_xlnx *priv_data) |
70 | { |
71 | struct device *dev = priv_data->dev; |
72 | struct reset_control *crst; |
73 | int ret; |
74 | |
75 | crst = devm_reset_control_get_exclusive(dev, NULL); |
76 | if (IS_ERR(ptr: crst)) |
77 | return dev_err_probe(dev, err: PTR_ERR(ptr: crst), fmt: "failed to get reset signal\n" ); |
78 | |
79 | dwc3_xlnx_mask_phy_rst(priv_data, mask: false); |
80 | |
81 | /* Assert and De-assert reset */ |
82 | ret = reset_control_assert(rstc: crst); |
83 | if (ret < 0) { |
84 | dev_err_probe(dev, err: ret, fmt: "failed to assert Reset\n" ); |
85 | return ret; |
86 | } |
87 | |
88 | ret = reset_control_deassert(rstc: crst); |
89 | if (ret < 0) { |
90 | dev_err_probe(dev, err: ret, fmt: "failed to De-assert Reset\n" ); |
91 | return ret; |
92 | } |
93 | |
94 | dwc3_xlnx_mask_phy_rst(priv_data, mask: true); |
95 | |
96 | return 0; |
97 | } |
98 | |
99 | static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data) |
100 | { |
101 | struct device *dev = priv_data->dev; |
102 | struct reset_control *crst, *hibrst, *apbrst; |
103 | struct gpio_desc *reset_gpio; |
104 | int ret = 0; |
105 | u32 reg; |
106 | |
107 | priv_data->usb3_phy = devm_phy_optional_get(dev, string: "usb3-phy" ); |
108 | if (IS_ERR(ptr: priv_data->usb3_phy)) { |
109 | ret = PTR_ERR(ptr: priv_data->usb3_phy); |
110 | dev_err_probe(dev, err: ret, |
111 | fmt: "failed to get USB3 PHY\n" ); |
112 | goto err; |
113 | } |
114 | |
115 | /* |
116 | * The following core resets are not required unless a USB3 PHY |
117 | * is used, and the subsequent register settings are not required |
118 | * unless a core reset is performed (they should be set properly |
119 | * by the first-stage boot loader, but may be reverted by a core |
120 | * reset). They may also break the configuration if USB3 is actually |
121 | * in use but the usb3-phy entry is missing from the device tree. |
122 | * Therefore, skip these operations in this case. |
123 | */ |
124 | if (!priv_data->usb3_phy) |
125 | goto skip_usb3_phy; |
126 | |
127 | crst = devm_reset_control_get_exclusive(dev, id: "usb_crst" ); |
128 | if (IS_ERR(ptr: crst)) { |
129 | ret = PTR_ERR(ptr: crst); |
130 | dev_err_probe(dev, err: ret, |
131 | fmt: "failed to get core reset signal\n" ); |
132 | goto err; |
133 | } |
134 | |
135 | hibrst = devm_reset_control_get_exclusive(dev, id: "usb_hibrst" ); |
136 | if (IS_ERR(ptr: hibrst)) { |
137 | ret = PTR_ERR(ptr: hibrst); |
138 | dev_err_probe(dev, err: ret, |
139 | fmt: "failed to get hibernation reset signal\n" ); |
140 | goto err; |
141 | } |
142 | |
143 | apbrst = devm_reset_control_get_exclusive(dev, id: "usb_apbrst" ); |
144 | if (IS_ERR(ptr: apbrst)) { |
145 | ret = PTR_ERR(ptr: apbrst); |
146 | dev_err_probe(dev, err: ret, |
147 | fmt: "failed to get APB reset signal\n" ); |
148 | goto err; |
149 | } |
150 | |
151 | ret = reset_control_assert(rstc: crst); |
152 | if (ret < 0) { |
153 | dev_err(dev, "Failed to assert core reset\n" ); |
154 | goto err; |
155 | } |
156 | |
157 | ret = reset_control_assert(rstc: hibrst); |
158 | if (ret < 0) { |
159 | dev_err(dev, "Failed to assert hibernation reset\n" ); |
160 | goto err; |
161 | } |
162 | |
163 | ret = reset_control_assert(rstc: apbrst); |
164 | if (ret < 0) { |
165 | dev_err(dev, "Failed to assert APB reset\n" ); |
166 | goto err; |
167 | } |
168 | |
169 | ret = phy_init(phy: priv_data->usb3_phy); |
170 | if (ret < 0) { |
171 | phy_exit(phy: priv_data->usb3_phy); |
172 | goto err; |
173 | } |
174 | |
175 | ret = reset_control_deassert(rstc: apbrst); |
176 | if (ret < 0) { |
177 | dev_err(dev, "Failed to release APB reset\n" ); |
178 | goto err; |
179 | } |
180 | |
181 | /* Set PIPE Power Present signal in FPD Power Present Register*/ |
182 | writel(FPD_POWER_PRSNT_OPTION, addr: priv_data->regs + XLNX_USB_FPD_POWER_PRSNT); |
183 | |
184 | /* Set the PIPE Clock Select bit in FPD PIPE Clock register */ |
185 | writel(PIPE_CLK_SELECT, addr: priv_data->regs + XLNX_USB_FPD_PIPE_CLK); |
186 | |
187 | ret = reset_control_deassert(rstc: crst); |
188 | if (ret < 0) { |
189 | dev_err(dev, "Failed to release core reset\n" ); |
190 | goto err; |
191 | } |
192 | |
193 | ret = reset_control_deassert(rstc: hibrst); |
194 | if (ret < 0) { |
195 | dev_err(dev, "Failed to release hibernation reset\n" ); |
196 | goto err; |
197 | } |
198 | |
199 | ret = phy_power_on(phy: priv_data->usb3_phy); |
200 | if (ret < 0) { |
201 | phy_exit(phy: priv_data->usb3_phy); |
202 | goto err; |
203 | } |
204 | |
205 | skip_usb3_phy: |
206 | /* ulpi reset via gpio-modepin or gpio-framework driver */ |
207 | reset_gpio = devm_gpiod_get_optional(dev, con_id: "reset" , flags: GPIOD_OUT_LOW); |
208 | if (IS_ERR(ptr: reset_gpio)) { |
209 | return dev_err_probe(dev, err: PTR_ERR(ptr: reset_gpio), |
210 | fmt: "Failed to request reset GPIO\n" ); |
211 | } |
212 | |
213 | if (reset_gpio) { |
214 | /* Toggle ulpi to reset the phy. */ |
215 | gpiod_set_value_cansleep(desc: reset_gpio, value: 1); |
216 | usleep_range(min: 5000, max: 10000); |
217 | gpiod_set_value_cansleep(desc: reset_gpio, value: 0); |
218 | usleep_range(min: 5000, max: 10000); |
219 | } |
220 | |
221 | /* |
222 | * This routes the USB DMA traffic to go through FPD path instead |
223 | * of reaching DDR directly. This traffic routing is needed to |
224 | * make SMMU and CCI work with USB DMA. |
225 | */ |
226 | if (of_dma_is_coherent(np: dev->of_node) || device_iommu_mapped(dev)) { |
227 | reg = readl(addr: priv_data->regs + XLNX_USB_TRAFFIC_ROUTE_CONFIG); |
228 | reg |= XLNX_USB_TRAFFIC_ROUTE_FPD; |
229 | writel(val: reg, addr: priv_data->regs + XLNX_USB_TRAFFIC_ROUTE_CONFIG); |
230 | } |
231 | |
232 | err: |
233 | return ret; |
234 | } |
235 | |
236 | static const struct of_device_id dwc3_xlnx_of_match[] = { |
237 | { |
238 | .compatible = "xlnx,zynqmp-dwc3" , |
239 | .data = &dwc3_xlnx_init_zynqmp, |
240 | }, |
241 | { |
242 | .compatible = "xlnx,versal-dwc3" , |
243 | .data = &dwc3_xlnx_init_versal, |
244 | }, |
245 | { /* Sentinel */ } |
246 | }; |
247 | MODULE_DEVICE_TABLE(of, dwc3_xlnx_of_match); |
248 | |
249 | static int dwc3_xlnx_probe(struct platform_device *pdev) |
250 | { |
251 | struct dwc3_xlnx *priv_data; |
252 | struct device *dev = &pdev->dev; |
253 | struct device_node *np = dev->of_node; |
254 | const struct of_device_id *match; |
255 | void __iomem *regs; |
256 | int ret; |
257 | |
258 | priv_data = devm_kzalloc(dev, size: sizeof(*priv_data), GFP_KERNEL); |
259 | if (!priv_data) |
260 | return -ENOMEM; |
261 | |
262 | regs = devm_platform_ioremap_resource(pdev, index: 0); |
263 | if (IS_ERR(ptr: regs)) { |
264 | ret = PTR_ERR(ptr: regs); |
265 | dev_err_probe(dev, err: ret, fmt: "failed to map registers\n" ); |
266 | return ret; |
267 | } |
268 | |
269 | match = of_match_node(matches: dwc3_xlnx_of_match, node: pdev->dev.of_node); |
270 | |
271 | priv_data->pltfm_init = match->data; |
272 | priv_data->regs = regs; |
273 | priv_data->dev = dev; |
274 | |
275 | platform_set_drvdata(pdev, data: priv_data); |
276 | |
277 | ret = devm_clk_bulk_get_all(dev: priv_data->dev, clks: &priv_data->clks); |
278 | if (ret < 0) |
279 | return ret; |
280 | |
281 | priv_data->num_clocks = ret; |
282 | |
283 | ret = clk_bulk_prepare_enable(num_clks: priv_data->num_clocks, clks: priv_data->clks); |
284 | if (ret) |
285 | return ret; |
286 | |
287 | ret = priv_data->pltfm_init(priv_data); |
288 | if (ret) |
289 | goto err_clk_put; |
290 | |
291 | ret = of_platform_populate(root: np, NULL, NULL, parent: dev); |
292 | if (ret) |
293 | goto err_clk_put; |
294 | |
295 | pm_runtime_set_active(dev); |
296 | ret = devm_pm_runtime_enable(dev); |
297 | if (ret < 0) |
298 | goto err_pm_set_suspended; |
299 | |
300 | pm_suspend_ignore_children(dev, enable: false); |
301 | return pm_runtime_resume_and_get(dev); |
302 | |
303 | err_pm_set_suspended: |
304 | pm_runtime_set_suspended(dev); |
305 | |
306 | err_clk_put: |
307 | clk_bulk_disable_unprepare(num_clks: priv_data->num_clocks, clks: priv_data->clks); |
308 | |
309 | return ret; |
310 | } |
311 | |
312 | static void dwc3_xlnx_remove(struct platform_device *pdev) |
313 | { |
314 | struct dwc3_xlnx *priv_data = platform_get_drvdata(pdev); |
315 | struct device *dev = &pdev->dev; |
316 | |
317 | of_platform_depopulate(parent: dev); |
318 | |
319 | clk_bulk_disable_unprepare(num_clks: priv_data->num_clocks, clks: priv_data->clks); |
320 | priv_data->num_clocks = 0; |
321 | |
322 | pm_runtime_put_noidle(dev); |
323 | pm_runtime_set_suspended(dev); |
324 | } |
325 | |
326 | static int __maybe_unused dwc3_xlnx_runtime_suspend(struct device *dev) |
327 | { |
328 | struct dwc3_xlnx *priv_data = dev_get_drvdata(dev); |
329 | |
330 | clk_bulk_disable(num_clks: priv_data->num_clocks, clks: priv_data->clks); |
331 | |
332 | return 0; |
333 | } |
334 | |
335 | static int __maybe_unused dwc3_xlnx_runtime_resume(struct device *dev) |
336 | { |
337 | struct dwc3_xlnx *priv_data = dev_get_drvdata(dev); |
338 | |
339 | return clk_bulk_enable(num_clks: priv_data->num_clocks, clks: priv_data->clks); |
340 | } |
341 | |
342 | static int __maybe_unused dwc3_xlnx_runtime_idle(struct device *dev) |
343 | { |
344 | pm_runtime_mark_last_busy(dev); |
345 | pm_runtime_autosuspend(dev); |
346 | |
347 | return 0; |
348 | } |
349 | |
350 | static int __maybe_unused dwc3_xlnx_suspend(struct device *dev) |
351 | { |
352 | struct dwc3_xlnx *priv_data = dev_get_drvdata(dev); |
353 | |
354 | phy_exit(phy: priv_data->usb3_phy); |
355 | |
356 | /* Disable the clocks */ |
357 | clk_bulk_disable(num_clks: priv_data->num_clocks, clks: priv_data->clks); |
358 | |
359 | return 0; |
360 | } |
361 | |
362 | static int __maybe_unused dwc3_xlnx_resume(struct device *dev) |
363 | { |
364 | struct dwc3_xlnx *priv_data = dev_get_drvdata(dev); |
365 | int ret; |
366 | |
367 | ret = clk_bulk_enable(num_clks: priv_data->num_clocks, clks: priv_data->clks); |
368 | if (ret) |
369 | return ret; |
370 | |
371 | ret = phy_init(phy: priv_data->usb3_phy); |
372 | if (ret < 0) |
373 | return ret; |
374 | |
375 | ret = phy_power_on(phy: priv_data->usb3_phy); |
376 | if (ret < 0) { |
377 | phy_exit(phy: priv_data->usb3_phy); |
378 | return ret; |
379 | } |
380 | |
381 | return 0; |
382 | } |
383 | |
384 | static const struct dev_pm_ops dwc3_xlnx_dev_pm_ops = { |
385 | SET_SYSTEM_SLEEP_PM_OPS(dwc3_xlnx_suspend, dwc3_xlnx_resume) |
386 | SET_RUNTIME_PM_OPS(dwc3_xlnx_runtime_suspend, |
387 | dwc3_xlnx_runtime_resume, dwc3_xlnx_runtime_idle) |
388 | }; |
389 | |
390 | static struct platform_driver dwc3_xlnx_driver = { |
391 | .probe = dwc3_xlnx_probe, |
392 | .remove_new = dwc3_xlnx_remove, |
393 | .driver = { |
394 | .name = "dwc3-xilinx" , |
395 | .of_match_table = dwc3_xlnx_of_match, |
396 | .pm = &dwc3_xlnx_dev_pm_ops, |
397 | }, |
398 | }; |
399 | |
400 | module_platform_driver(dwc3_xlnx_driver); |
401 | |
402 | MODULE_LICENSE("GPL v2" ); |
403 | MODULE_DESCRIPTION("Xilinx DWC3 controller specific glue driver" ); |
404 | MODULE_AUTHOR("Manish Narani <manish.narani@xilinx.com>" ); |
405 | MODULE_AUTHOR("Anurag Kumar Vulisha <anurag.kumar.vulisha@xilinx.com>" ); |
406 | |