1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include <linux/module.h> |
3 | #include <linux/platform_device.h> |
4 | #include <linux/dma-mapping.h> |
5 | #include <linux/usb/otg.h> |
6 | #include <linux/usb/usb_phy_generic.h> |
7 | #include <linux/slab.h> |
8 | #include <linux/clk.h> |
9 | #include <linux/of.h> |
10 | #include <linux/of_address.h> |
11 | #include <linux/usb/of.h> |
12 | |
13 | #include "phy-am335x-control.h" |
14 | #include "phy-generic.h" |
15 | |
16 | struct am335x_phy { |
17 | struct usb_phy_generic usb_phy_gen; |
18 | struct phy_control *phy_ctrl; |
19 | int id; |
20 | enum usb_dr_mode dr_mode; |
21 | }; |
22 | |
23 | static int am335x_init(struct usb_phy *phy) |
24 | { |
25 | struct am335x_phy *am_phy = dev_get_drvdata(dev: phy->dev); |
26 | |
27 | phy_ctrl_power(phy_ctrl: am_phy->phy_ctrl, id: am_phy->id, dr_mode: am_phy->dr_mode, on: true); |
28 | return 0; |
29 | } |
30 | |
31 | static void am335x_shutdown(struct usb_phy *phy) |
32 | { |
33 | struct am335x_phy *am_phy = dev_get_drvdata(dev: phy->dev); |
34 | |
35 | phy_ctrl_power(phy_ctrl: am_phy->phy_ctrl, id: am_phy->id, dr_mode: am_phy->dr_mode, on: false); |
36 | } |
37 | |
38 | static int am335x_phy_probe(struct platform_device *pdev) |
39 | { |
40 | struct am335x_phy *am_phy; |
41 | struct device *dev = &pdev->dev; |
42 | int ret; |
43 | |
44 | am_phy = devm_kzalloc(dev, size: sizeof(*am_phy), GFP_KERNEL); |
45 | if (!am_phy) |
46 | return -ENOMEM; |
47 | |
48 | am_phy->phy_ctrl = am335x_get_phy_control(dev); |
49 | if (!am_phy->phy_ctrl) |
50 | return -EPROBE_DEFER; |
51 | |
52 | am_phy->id = of_alias_get_id(np: pdev->dev.of_node, stem: "phy" ); |
53 | if (am_phy->id < 0) { |
54 | dev_err(&pdev->dev, "Missing PHY id: %d\n" , am_phy->id); |
55 | return am_phy->id; |
56 | } |
57 | |
58 | am_phy->dr_mode = of_usb_get_dr_mode_by_phy(np: pdev->dev.of_node, arg0: -1); |
59 | |
60 | ret = usb_phy_gen_create_phy(dev, nop: &am_phy->usb_phy_gen); |
61 | if (ret) |
62 | return ret; |
63 | |
64 | am_phy->usb_phy_gen.phy.init = am335x_init; |
65 | am_phy->usb_phy_gen.phy.shutdown = am335x_shutdown; |
66 | |
67 | platform_set_drvdata(pdev, data: am_phy); |
68 | device_init_wakeup(dev, enable: true); |
69 | |
70 | /* |
71 | * If we leave PHY wakeup enabled then AM33XX wakes up |
72 | * immediately from DS0. To avoid this we mark dev->power.can_wakeup |
73 | * to false. The same is checked in suspend routine to decide |
74 | * on whether to enable PHY wakeup or not. |
75 | * PHY wakeup works fine in standby mode, there by allowing us to |
76 | * handle remote wakeup, wakeup on disconnect and connect. |
77 | */ |
78 | |
79 | device_set_wakeup_enable(dev, enable: false); |
80 | phy_ctrl_power(phy_ctrl: am_phy->phy_ctrl, id: am_phy->id, dr_mode: am_phy->dr_mode, on: false); |
81 | |
82 | return usb_add_phy_dev(&am_phy->usb_phy_gen.phy); |
83 | } |
84 | |
85 | static void am335x_phy_remove(struct platform_device *pdev) |
86 | { |
87 | struct am335x_phy *am_phy = platform_get_drvdata(pdev); |
88 | |
89 | usb_remove_phy(&am_phy->usb_phy_gen.phy); |
90 | } |
91 | |
92 | #ifdef CONFIG_PM_SLEEP |
93 | static int am335x_phy_suspend(struct device *dev) |
94 | { |
95 | struct am335x_phy *am_phy = dev_get_drvdata(dev); |
96 | |
97 | /* |
98 | * Enable phy wakeup only if dev->power.can_wakeup is true. |
99 | * Make sure to enable wakeup to support remote wakeup in |
100 | * standby mode ( same is not supported in OFF(DS0) mode). |
101 | * Enable it by doing |
102 | * echo enabled > /sys/bus/platform/devices/<usb-phy-id>/power/wakeup |
103 | */ |
104 | |
105 | if (device_may_wakeup(dev)) |
106 | phy_ctrl_wkup(phy_ctrl: am_phy->phy_ctrl, id: am_phy->id, on: true); |
107 | |
108 | phy_ctrl_power(phy_ctrl: am_phy->phy_ctrl, id: am_phy->id, dr_mode: am_phy->dr_mode, on: false); |
109 | |
110 | return 0; |
111 | } |
112 | |
113 | static int am335x_phy_resume(struct device *dev) |
114 | { |
115 | struct am335x_phy *am_phy = dev_get_drvdata(dev); |
116 | |
117 | phy_ctrl_power(phy_ctrl: am_phy->phy_ctrl, id: am_phy->id, dr_mode: am_phy->dr_mode, on: true); |
118 | |
119 | if (device_may_wakeup(dev)) |
120 | phy_ctrl_wkup(phy_ctrl: am_phy->phy_ctrl, id: am_phy->id, on: false); |
121 | |
122 | return 0; |
123 | } |
124 | #endif |
125 | |
126 | static SIMPLE_DEV_PM_OPS(am335x_pm_ops, am335x_phy_suspend, am335x_phy_resume); |
127 | |
128 | static const struct of_device_id am335x_phy_ids[] = { |
129 | { .compatible = "ti,am335x-usb-phy" }, |
130 | { } |
131 | }; |
132 | MODULE_DEVICE_TABLE(of, am335x_phy_ids); |
133 | |
134 | static struct platform_driver am335x_phy_driver = { |
135 | .probe = am335x_phy_probe, |
136 | .remove_new = am335x_phy_remove, |
137 | .driver = { |
138 | .name = "am335x-phy-driver" , |
139 | .pm = &am335x_pm_ops, |
140 | .of_match_table = am335x_phy_ids, |
141 | }, |
142 | }; |
143 | |
144 | module_platform_driver(am335x_phy_driver); |
145 | MODULE_LICENSE("GPL v2" ); |
146 | |