1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (C) 2016 MediaTek Inc. |
4 | * |
5 | * Author: Chunfeng Yun <chunfeng.yun@mediatek.com> |
6 | */ |
7 | |
8 | #include <linux/dma-mapping.h> |
9 | #include <linux/iopoll.h> |
10 | #include <linux/kernel.h> |
11 | #include <linux/module.h> |
12 | #include <linux/of_address.h> |
13 | #include <linux/of_irq.h> |
14 | #include <linux/platform_device.h> |
15 | #include <linux/pm_wakeirq.h> |
16 | #include <linux/reset.h> |
17 | |
18 | #include "mtu3.h" |
19 | #include "mtu3_dr.h" |
20 | #include "mtu3_debug.h" |
21 | |
22 | /* u2-port0 should be powered on and enabled; */ |
23 | int ssusb_check_clocks(struct ssusb_mtk *ssusb, u32 ex_clks) |
24 | { |
25 | void __iomem *ibase = ssusb->ippc_base; |
26 | u32 value, check_val; |
27 | int ret; |
28 | |
29 | check_val = ex_clks | SSUSB_SYS125_RST_B_STS | SSUSB_SYSPLL_STABLE | |
30 | SSUSB_REF_RST_B_STS; |
31 | |
32 | ret = readl_poll_timeout(ibase + U3D_SSUSB_IP_PW_STS1, value, |
33 | (check_val == (value & check_val)), 100, 20000); |
34 | if (ret) { |
35 | dev_err(ssusb->dev, "clks of sts1 are not stable!\n" ); |
36 | return ret; |
37 | } |
38 | |
39 | ret = readl_poll_timeout(ibase + U3D_SSUSB_IP_PW_STS2, value, |
40 | (value & SSUSB_U2_MAC_SYS_RST_B_STS), 100, 10000); |
41 | if (ret) { |
42 | dev_err(ssusb->dev, "mac2 clock is not stable\n" ); |
43 | return ret; |
44 | } |
45 | |
46 | return 0; |
47 | } |
48 | |
49 | static int wait_for_ip_sleep(struct ssusb_mtk *ssusb) |
50 | { |
51 | bool sleep_check = true; |
52 | u32 value; |
53 | int ret; |
54 | |
55 | if (!ssusb->is_host) |
56 | sleep_check = ssusb_gadget_ip_sleep_check(ssusb); |
57 | |
58 | if (!sleep_check) |
59 | return 0; |
60 | |
61 | /* wait for ip enter sleep mode */ |
62 | ret = readl_poll_timeout(ssusb->ippc_base + U3D_SSUSB_IP_PW_STS1, value, |
63 | (value & SSUSB_IP_SLEEP_STS), 100, 100000); |
64 | if (ret) { |
65 | dev_err(ssusb->dev, "ip sleep failed!!!\n" ); |
66 | ret = -EBUSY; |
67 | } else { |
68 | /* workaround: avoid wrong wakeup signal latch for some soc */ |
69 | usleep_range(min: 100, max: 200); |
70 | } |
71 | |
72 | return ret; |
73 | } |
74 | |
75 | static int ssusb_phy_init(struct ssusb_mtk *ssusb) |
76 | { |
77 | int i; |
78 | int ret; |
79 | |
80 | for (i = 0; i < ssusb->num_phys; i++) { |
81 | ret = phy_init(phy: ssusb->phys[i]); |
82 | if (ret) |
83 | goto exit_phy; |
84 | } |
85 | return 0; |
86 | |
87 | exit_phy: |
88 | for (; i > 0; i--) |
89 | phy_exit(phy: ssusb->phys[i - 1]); |
90 | |
91 | return ret; |
92 | } |
93 | |
94 | static int ssusb_phy_exit(struct ssusb_mtk *ssusb) |
95 | { |
96 | int i; |
97 | |
98 | for (i = 0; i < ssusb->num_phys; i++) |
99 | phy_exit(phy: ssusb->phys[i]); |
100 | |
101 | return 0; |
102 | } |
103 | |
104 | static int ssusb_phy_power_on(struct ssusb_mtk *ssusb) |
105 | { |
106 | int i; |
107 | int ret; |
108 | |
109 | for (i = 0; i < ssusb->num_phys; i++) { |
110 | ret = phy_power_on(phy: ssusb->phys[i]); |
111 | if (ret) |
112 | goto power_off_phy; |
113 | } |
114 | return 0; |
115 | |
116 | power_off_phy: |
117 | for (; i > 0; i--) |
118 | phy_power_off(phy: ssusb->phys[i - 1]); |
119 | |
120 | return ret; |
121 | } |
122 | |
123 | static void ssusb_phy_power_off(struct ssusb_mtk *ssusb) |
124 | { |
125 | unsigned int i; |
126 | |
127 | for (i = 0; i < ssusb->num_phys; i++) |
128 | phy_power_off(phy: ssusb->phys[i]); |
129 | } |
130 | |
131 | static int ssusb_rscs_init(struct ssusb_mtk *ssusb) |
132 | { |
133 | int ret = 0; |
134 | |
135 | ret = regulator_enable(regulator: ssusb->vusb33); |
136 | if (ret) { |
137 | dev_err(ssusb->dev, "failed to enable vusb33\n" ); |
138 | goto vusb33_err; |
139 | } |
140 | |
141 | ret = clk_bulk_prepare_enable(BULK_CLKS_CNT, clks: ssusb->clks); |
142 | if (ret) |
143 | goto clks_err; |
144 | |
145 | ret = ssusb_phy_init(ssusb); |
146 | if (ret) { |
147 | dev_err(ssusb->dev, "failed to init phy\n" ); |
148 | goto phy_init_err; |
149 | } |
150 | |
151 | ret = ssusb_phy_power_on(ssusb); |
152 | if (ret) { |
153 | dev_err(ssusb->dev, "failed to power on phy\n" ); |
154 | goto phy_err; |
155 | } |
156 | |
157 | return 0; |
158 | |
159 | phy_err: |
160 | ssusb_phy_exit(ssusb); |
161 | phy_init_err: |
162 | clk_bulk_disable_unprepare(BULK_CLKS_CNT, clks: ssusb->clks); |
163 | clks_err: |
164 | regulator_disable(regulator: ssusb->vusb33); |
165 | vusb33_err: |
166 | return ret; |
167 | } |
168 | |
169 | static void ssusb_rscs_exit(struct ssusb_mtk *ssusb) |
170 | { |
171 | clk_bulk_disable_unprepare(BULK_CLKS_CNT, clks: ssusb->clks); |
172 | regulator_disable(regulator: ssusb->vusb33); |
173 | ssusb_phy_power_off(ssusb); |
174 | ssusb_phy_exit(ssusb); |
175 | } |
176 | |
177 | static void ssusb_ip_sw_reset(struct ssusb_mtk *ssusb) |
178 | { |
179 | /* reset whole ip (xhci & u3d) */ |
180 | mtu3_setbits(base: ssusb->ippc_base, U3D_SSUSB_IP_PW_CTRL0, SSUSB_IP_SW_RST); |
181 | udelay(1); |
182 | mtu3_clrbits(base: ssusb->ippc_base, U3D_SSUSB_IP_PW_CTRL0, SSUSB_IP_SW_RST); |
183 | |
184 | /* |
185 | * device ip may be powered on in firmware/BROM stage before entering |
186 | * kernel stage; |
187 | * power down device ip, otherwise ip-sleep will fail when working as |
188 | * host only mode |
189 | */ |
190 | mtu3_setbits(base: ssusb->ippc_base, U3D_SSUSB_IP_PW_CTRL2, SSUSB_IP_DEV_PDN); |
191 | } |
192 | |
193 | static void ssusb_u3_drd_check(struct ssusb_mtk *ssusb) |
194 | { |
195 | struct otg_switch_mtk *otg_sx = &ssusb->otg_switch; |
196 | u32 dev_u3p_num; |
197 | u32 host_u3p_num; |
198 | u32 value; |
199 | |
200 | /* u3 port0 is disabled */ |
201 | if (ssusb->u3p_dis_msk & BIT(0)) { |
202 | otg_sx->is_u3_drd = false; |
203 | goto out; |
204 | } |
205 | |
206 | value = mtu3_readl(base: ssusb->ippc_base, U3D_SSUSB_IP_DEV_CAP); |
207 | dev_u3p_num = SSUSB_IP_DEV_U3_PORT_NUM(value); |
208 | |
209 | value = mtu3_readl(base: ssusb->ippc_base, U3D_SSUSB_IP_XHCI_CAP); |
210 | host_u3p_num = SSUSB_IP_XHCI_U3_PORT_NUM(value); |
211 | |
212 | otg_sx->is_u3_drd = !!(dev_u3p_num && host_u3p_num); |
213 | |
214 | out: |
215 | dev_info(ssusb->dev, "usb3-drd: %d\n" , otg_sx->is_u3_drd); |
216 | } |
217 | |
218 | static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb) |
219 | { |
220 | struct device_node *node = pdev->dev.of_node; |
221 | struct otg_switch_mtk *otg_sx = &ssusb->otg_switch; |
222 | struct clk_bulk_data *clks = ssusb->clks; |
223 | struct device *dev = &pdev->dev; |
224 | int i; |
225 | int ret; |
226 | |
227 | ssusb->vusb33 = devm_regulator_get(dev, id: "vusb33" ); |
228 | if (IS_ERR(ptr: ssusb->vusb33)) { |
229 | dev_err(dev, "failed to get vusb33\n" ); |
230 | return PTR_ERR(ptr: ssusb->vusb33); |
231 | } |
232 | |
233 | clks[0].id = "sys_ck" ; |
234 | clks[1].id = "ref_ck" ; |
235 | clks[2].id = "mcu_ck" ; |
236 | clks[3].id = "dma_ck" ; |
237 | clks[4].id = "xhci_ck" ; |
238 | clks[5].id = "frmcnt_ck" ; |
239 | ret = devm_clk_bulk_get_optional(dev, BULK_CLKS_CNT, clks); |
240 | if (ret) |
241 | return ret; |
242 | |
243 | ssusb->num_phys = of_count_phandle_with_args(np: node, |
244 | list_name: "phys" , cells_name: "#phy-cells" ); |
245 | if (ssusb->num_phys > 0) { |
246 | ssusb->phys = devm_kcalloc(dev, n: ssusb->num_phys, |
247 | size: sizeof(*ssusb->phys), GFP_KERNEL); |
248 | if (!ssusb->phys) |
249 | return -ENOMEM; |
250 | } else { |
251 | ssusb->num_phys = 0; |
252 | } |
253 | |
254 | for (i = 0; i < ssusb->num_phys; i++) { |
255 | ssusb->phys[i] = devm_of_phy_get_by_index(dev, np: node, index: i); |
256 | if (IS_ERR(ptr: ssusb->phys[i])) { |
257 | dev_err(dev, "failed to get phy-%d\n" , i); |
258 | return PTR_ERR(ptr: ssusb->phys[i]); |
259 | } |
260 | } |
261 | |
262 | ssusb->ippc_base = devm_platform_ioremap_resource_byname(pdev, name: "ippc" ); |
263 | if (IS_ERR(ptr: ssusb->ippc_base)) |
264 | return PTR_ERR(ptr: ssusb->ippc_base); |
265 | |
266 | ssusb->wakeup_irq = platform_get_irq_byname_optional(dev: pdev, name: "wakeup" ); |
267 | if (ssusb->wakeup_irq == -EPROBE_DEFER) |
268 | return ssusb->wakeup_irq; |
269 | |
270 | ssusb->dr_mode = usb_get_dr_mode(dev); |
271 | if (ssusb->dr_mode == USB_DR_MODE_UNKNOWN) |
272 | ssusb->dr_mode = USB_DR_MODE_OTG; |
273 | |
274 | of_property_read_u32(np: node, propname: "mediatek,u3p-dis-msk" , out_value: &ssusb->u3p_dis_msk); |
275 | |
276 | if (ssusb->dr_mode == USB_DR_MODE_PERIPHERAL) |
277 | goto out; |
278 | |
279 | /* if host role is supported */ |
280 | ret = ssusb_wakeup_of_property_parse(ssusb, dn: node); |
281 | if (ret) { |
282 | dev_err(dev, "failed to parse uwk property\n" ); |
283 | return ret; |
284 | } |
285 | |
286 | /* optional property, ignore the error if it does not exist */ |
287 | of_property_read_u32(np: node, propname: "mediatek,u2p-dis-msk" , |
288 | out_value: &ssusb->u2p_dis_msk); |
289 | |
290 | otg_sx->vbus = devm_regulator_get(dev, id: "vbus" ); |
291 | if (IS_ERR(ptr: otg_sx->vbus)) { |
292 | dev_err(dev, "failed to get vbus\n" ); |
293 | return PTR_ERR(ptr: otg_sx->vbus); |
294 | } |
295 | |
296 | if (ssusb->dr_mode == USB_DR_MODE_HOST) |
297 | goto out; |
298 | |
299 | /* if dual-role mode is supported */ |
300 | otg_sx->manual_drd_enabled = |
301 | of_property_read_bool(np: node, propname: "enable-manual-drd" ); |
302 | otg_sx->role_sw_used = of_property_read_bool(np: node, propname: "usb-role-switch" ); |
303 | |
304 | /* can't disable port0 when use dual-role mode */ |
305 | ssusb->u2p_dis_msk &= ~0x1; |
306 | |
307 | if (otg_sx->role_sw_used || otg_sx->manual_drd_enabled) |
308 | goto out; |
309 | |
310 | if (of_property_read_bool(np: node, propname: "extcon" )) { |
311 | otg_sx->edev = extcon_get_edev_by_phandle(dev: ssusb->dev, index: 0); |
312 | if (IS_ERR(ptr: otg_sx->edev)) { |
313 | return dev_err_probe(dev, err: PTR_ERR(ptr: otg_sx->edev), |
314 | fmt: "couldn't get extcon device\n" ); |
315 | } |
316 | } |
317 | |
318 | out: |
319 | dev_info(dev, "dr_mode: %d, drd: %s\n" , ssusb->dr_mode, |
320 | otg_sx->manual_drd_enabled ? "manual" : "auto" ); |
321 | dev_info(dev, "u2p_dis_msk: %x, u3p_dis_msk: %x\n" , |
322 | ssusb->u2p_dis_msk, ssusb->u3p_dis_msk); |
323 | |
324 | return 0; |
325 | } |
326 | |
327 | static int mtu3_probe(struct platform_device *pdev) |
328 | { |
329 | struct device_node *node = pdev->dev.of_node; |
330 | struct device *dev = &pdev->dev; |
331 | struct ssusb_mtk *ssusb; |
332 | int ret = -ENOMEM; |
333 | |
334 | /* all elements are set to ZERO as default value */ |
335 | ssusb = devm_kzalloc(dev, size: sizeof(*ssusb), GFP_KERNEL); |
336 | if (!ssusb) |
337 | return -ENOMEM; |
338 | |
339 | ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); |
340 | if (ret) { |
341 | dev_err(dev, "No suitable DMA config available\n" ); |
342 | return -ENOTSUPP; |
343 | } |
344 | |
345 | platform_set_drvdata(pdev, data: ssusb); |
346 | ssusb->dev = dev; |
347 | |
348 | ret = get_ssusb_rscs(pdev, ssusb); |
349 | if (ret) |
350 | return ret; |
351 | |
352 | ssusb_debugfs_create_root(ssusb); |
353 | |
354 | /* enable power domain */ |
355 | pm_runtime_set_active(dev); |
356 | pm_runtime_use_autosuspend(dev); |
357 | pm_runtime_set_autosuspend_delay(dev, delay: 4000); |
358 | pm_runtime_enable(dev); |
359 | pm_runtime_get_sync(dev); |
360 | |
361 | device_init_wakeup(dev, enable: true); |
362 | |
363 | ret = ssusb_rscs_init(ssusb); |
364 | if (ret) |
365 | goto comm_init_err; |
366 | |
367 | if (ssusb->wakeup_irq > 0) { |
368 | ret = dev_pm_set_dedicated_wake_irq_reverse(dev, irq: ssusb->wakeup_irq); |
369 | if (ret) { |
370 | dev_err(dev, "failed to set wakeup irq %d\n" , ssusb->wakeup_irq); |
371 | goto comm_exit; |
372 | } |
373 | dev_info(dev, "wakeup irq %d\n" , ssusb->wakeup_irq); |
374 | } |
375 | |
376 | ret = device_reset_optional(dev); |
377 | if (ret) { |
378 | dev_err_probe(dev, err: ret, fmt: "failed to reset controller\n" ); |
379 | goto comm_exit; |
380 | } |
381 | |
382 | ssusb_ip_sw_reset(ssusb); |
383 | ssusb_u3_drd_check(ssusb); |
384 | |
385 | if (IS_ENABLED(CONFIG_USB_MTU3_HOST)) |
386 | ssusb->dr_mode = USB_DR_MODE_HOST; |
387 | else if (IS_ENABLED(CONFIG_USB_MTU3_GADGET)) |
388 | ssusb->dr_mode = USB_DR_MODE_PERIPHERAL; |
389 | |
390 | /* default as host */ |
391 | ssusb->is_host = !(ssusb->dr_mode == USB_DR_MODE_PERIPHERAL); |
392 | |
393 | switch (ssusb->dr_mode) { |
394 | case USB_DR_MODE_PERIPHERAL: |
395 | ret = ssusb_gadget_init(ssusb); |
396 | if (ret) { |
397 | dev_err(dev, "failed to initialize gadget\n" ); |
398 | goto comm_exit; |
399 | } |
400 | break; |
401 | case USB_DR_MODE_HOST: |
402 | ret = ssusb_host_init(ssusb, parent_dn: node); |
403 | if (ret) { |
404 | dev_err(dev, "failed to initialize host\n" ); |
405 | goto comm_exit; |
406 | } |
407 | break; |
408 | case USB_DR_MODE_OTG: |
409 | ret = ssusb_gadget_init(ssusb); |
410 | if (ret) { |
411 | dev_err(dev, "failed to initialize gadget\n" ); |
412 | goto comm_exit; |
413 | } |
414 | |
415 | ret = ssusb_host_init(ssusb, parent_dn: node); |
416 | if (ret) { |
417 | dev_err(dev, "failed to initialize host\n" ); |
418 | goto gadget_exit; |
419 | } |
420 | |
421 | ret = ssusb_otg_switch_init(ssusb); |
422 | if (ret) { |
423 | dev_err(dev, "failed to initialize switch\n" ); |
424 | goto host_exit; |
425 | } |
426 | break; |
427 | default: |
428 | dev_err(dev, "unsupported mode: %d\n" , ssusb->dr_mode); |
429 | ret = -EINVAL; |
430 | goto comm_exit; |
431 | } |
432 | |
433 | device_enable_async_suspend(dev); |
434 | pm_runtime_mark_last_busy(dev); |
435 | pm_runtime_put_autosuspend(dev); |
436 | pm_runtime_forbid(dev); |
437 | |
438 | return 0; |
439 | |
440 | host_exit: |
441 | ssusb_host_exit(ssusb); |
442 | gadget_exit: |
443 | ssusb_gadget_exit(ssusb); |
444 | comm_exit: |
445 | ssusb_rscs_exit(ssusb); |
446 | comm_init_err: |
447 | pm_runtime_put_noidle(dev); |
448 | pm_runtime_disable(dev); |
449 | ssusb_debugfs_remove_root(ssusb); |
450 | |
451 | return ret; |
452 | } |
453 | |
454 | static void mtu3_remove(struct platform_device *pdev) |
455 | { |
456 | struct ssusb_mtk *ssusb = platform_get_drvdata(pdev); |
457 | |
458 | pm_runtime_get_sync(dev: &pdev->dev); |
459 | |
460 | switch (ssusb->dr_mode) { |
461 | case USB_DR_MODE_PERIPHERAL: |
462 | ssusb_gadget_exit(ssusb); |
463 | break; |
464 | case USB_DR_MODE_HOST: |
465 | ssusb_host_exit(ssusb); |
466 | break; |
467 | case USB_DR_MODE_OTG: |
468 | ssusb_otg_switch_exit(ssusb); |
469 | ssusb_gadget_exit(ssusb); |
470 | ssusb_host_exit(ssusb); |
471 | break; |
472 | case USB_DR_MODE_UNKNOWN: |
473 | /* |
474 | * This cannot happen because with dr_mode == |
475 | * USB_DR_MODE_UNKNOWN, .probe() doesn't succeed and so |
476 | * .remove() wouldn't be called at all. However (little |
477 | * surprising) the compiler isn't smart enough to see that, so |
478 | * we explicitly have this case item to not make the compiler |
479 | * wail about an unhandled enumeration value. |
480 | */ |
481 | break; |
482 | } |
483 | |
484 | ssusb_rscs_exit(ssusb); |
485 | ssusb_debugfs_remove_root(ssusb); |
486 | pm_runtime_disable(dev: &pdev->dev); |
487 | pm_runtime_put_noidle(dev: &pdev->dev); |
488 | pm_runtime_set_suspended(dev: &pdev->dev); |
489 | } |
490 | |
491 | static int resume_ip_and_ports(struct ssusb_mtk *ssusb, pm_message_t msg) |
492 | { |
493 | switch (ssusb->dr_mode) { |
494 | case USB_DR_MODE_PERIPHERAL: |
495 | ssusb_gadget_resume(ssusb, msg); |
496 | break; |
497 | case USB_DR_MODE_HOST: |
498 | ssusb_host_resume(ssusb, p0_skipped: false); |
499 | break; |
500 | case USB_DR_MODE_OTG: |
501 | ssusb_host_resume(ssusb, p0_skipped: !ssusb->is_host); |
502 | if (!ssusb->is_host) |
503 | ssusb_gadget_resume(ssusb, msg); |
504 | |
505 | break; |
506 | default: |
507 | return -EINVAL; |
508 | } |
509 | |
510 | return 0; |
511 | } |
512 | |
513 | static int mtu3_suspend_common(struct device *dev, pm_message_t msg) |
514 | { |
515 | struct ssusb_mtk *ssusb = dev_get_drvdata(dev); |
516 | int ret = 0; |
517 | |
518 | dev_dbg(dev, "%s\n" , __func__); |
519 | |
520 | switch (ssusb->dr_mode) { |
521 | case USB_DR_MODE_PERIPHERAL: |
522 | ret = ssusb_gadget_suspend(ssusb, msg); |
523 | if (ret) |
524 | goto err; |
525 | |
526 | break; |
527 | case USB_DR_MODE_HOST: |
528 | ssusb_host_suspend(ssusb); |
529 | break; |
530 | case USB_DR_MODE_OTG: |
531 | if (!ssusb->is_host) { |
532 | ret = ssusb_gadget_suspend(ssusb, msg); |
533 | if (ret) |
534 | goto err; |
535 | } |
536 | ssusb_host_suspend(ssusb); |
537 | break; |
538 | default: |
539 | return -EINVAL; |
540 | } |
541 | |
542 | ret = wait_for_ip_sleep(ssusb); |
543 | if (ret) |
544 | goto sleep_err; |
545 | |
546 | ssusb_phy_power_off(ssusb); |
547 | clk_bulk_disable_unprepare(BULK_CLKS_CNT, clks: ssusb->clks); |
548 | ssusb_wakeup_set(ssusb, enable: true); |
549 | return 0; |
550 | |
551 | sleep_err: |
552 | resume_ip_and_ports(ssusb, msg); |
553 | err: |
554 | return ret; |
555 | } |
556 | |
557 | static int mtu3_resume_common(struct device *dev, pm_message_t msg) |
558 | { |
559 | struct ssusb_mtk *ssusb = dev_get_drvdata(dev); |
560 | int ret; |
561 | |
562 | dev_dbg(dev, "%s\n" , __func__); |
563 | |
564 | ssusb_wakeup_set(ssusb, enable: false); |
565 | ret = clk_bulk_prepare_enable(BULK_CLKS_CNT, clks: ssusb->clks); |
566 | if (ret) |
567 | goto clks_err; |
568 | |
569 | ret = ssusb_phy_power_on(ssusb); |
570 | if (ret) |
571 | goto phy_err; |
572 | |
573 | return resume_ip_and_ports(ssusb, msg); |
574 | |
575 | phy_err: |
576 | clk_bulk_disable_unprepare(BULK_CLKS_CNT, clks: ssusb->clks); |
577 | clks_err: |
578 | return ret; |
579 | } |
580 | |
581 | static int __maybe_unused mtu3_suspend(struct device *dev) |
582 | { |
583 | return mtu3_suspend_common(dev, PMSG_SUSPEND); |
584 | } |
585 | |
586 | static int __maybe_unused mtu3_resume(struct device *dev) |
587 | { |
588 | return mtu3_resume_common(dev, PMSG_SUSPEND); |
589 | } |
590 | |
591 | static int __maybe_unused mtu3_runtime_suspend(struct device *dev) |
592 | { |
593 | if (!device_may_wakeup(dev)) |
594 | return 0; |
595 | |
596 | return mtu3_suspend_common(dev, PMSG_AUTO_SUSPEND); |
597 | } |
598 | |
599 | static int __maybe_unused mtu3_runtime_resume(struct device *dev) |
600 | { |
601 | if (!device_may_wakeup(dev)) |
602 | return 0; |
603 | |
604 | return mtu3_resume_common(dev, PMSG_AUTO_SUSPEND); |
605 | } |
606 | |
607 | static const struct dev_pm_ops mtu3_pm_ops = { |
608 | SET_SYSTEM_SLEEP_PM_OPS(mtu3_suspend, mtu3_resume) |
609 | SET_RUNTIME_PM_OPS(mtu3_runtime_suspend, |
610 | mtu3_runtime_resume, NULL) |
611 | }; |
612 | |
613 | #define DEV_PM_OPS (IS_ENABLED(CONFIG_PM) ? &mtu3_pm_ops : NULL) |
614 | |
615 | static const struct of_device_id mtu3_of_match[] = { |
616 | {.compatible = "mediatek,mt8173-mtu3" ,}, |
617 | {.compatible = "mediatek,mtu3" ,}, |
618 | {}, |
619 | }; |
620 | MODULE_DEVICE_TABLE(of, mtu3_of_match); |
621 | |
622 | static struct platform_driver mtu3_driver = { |
623 | .probe = mtu3_probe, |
624 | .remove_new = mtu3_remove, |
625 | .driver = { |
626 | .name = MTU3_DRIVER_NAME, |
627 | .pm = DEV_PM_OPS, |
628 | .of_match_table = mtu3_of_match, |
629 | }, |
630 | }; |
631 | module_platform_driver(mtu3_driver); |
632 | |
633 | MODULE_AUTHOR("Chunfeng Yun <chunfeng.yun@mediatek.com>" ); |
634 | MODULE_LICENSE("GPL v2" ); |
635 | MODULE_DESCRIPTION("MediaTek USB3 DRD Controller Driver" ); |
636 | |