1 // SPDX-License-Identifier: GPL-2.0+
3 * A wrapper for multiple PHYs which passes all phy_* function calls to
4 * multiple (actual) PHY devices. This is comes handy when initializing
5 * all PHYs on a HCD and to keep them all in the same state.
7 * Copyright (C) 2018 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
10 #include <linux/device.h>
11 #include <linux/list.h>
12 #include <linux/phy/phy.h>
17 struct usb_phy_roothub
{
19 struct list_head list
;
22 static int usb_phy_roothub_add_phy(struct device
*dev
, int index
,
23 struct list_head
*list
)
25 struct usb_phy_roothub
*roothub_entry
;
28 phy
= devm_of_phy_get_by_index(dev
, dev
->of_node
, index
);
30 if (PTR_ERR(phy
) == -ENODEV
)
36 roothub_entry
= devm_kzalloc(dev
, sizeof(*roothub_entry
), GFP_KERNEL
);
40 INIT_LIST_HEAD(&roothub_entry
->list
);
42 roothub_entry
->phy
= phy
;
44 list_add_tail(&roothub_entry
->list
, list
);
49 struct usb_phy_roothub
*usb_phy_roothub_alloc(struct device
*dev
)
51 struct usb_phy_roothub
*phy_roothub
;
54 if (!IS_ENABLED(CONFIG_GENERIC_PHY
))
57 num_phys
= of_count_phandle_with_args(dev
->of_node
, "phys",
62 phy_roothub
= devm_kzalloc(dev
, sizeof(*phy_roothub
), GFP_KERNEL
);
64 return ERR_PTR(-ENOMEM
);
66 INIT_LIST_HEAD(&phy_roothub
->list
);
68 for (i
= 0; i
< num_phys
; i
++) {
69 err
= usb_phy_roothub_add_phy(dev
, i
, &phy_roothub
->list
);
76 EXPORT_SYMBOL_GPL(usb_phy_roothub_alloc
);
78 int usb_phy_roothub_init(struct usb_phy_roothub
*phy_roothub
)
80 struct usb_phy_roothub
*roothub_entry
;
81 struct list_head
*head
;
87 head
= &phy_roothub
->list
;
89 list_for_each_entry(roothub_entry
, head
, list
) {
90 err
= phy_init(roothub_entry
->phy
);
98 list_for_each_entry_continue_reverse(roothub_entry
, head
, list
)
99 phy_exit(roothub_entry
->phy
);
103 EXPORT_SYMBOL_GPL(usb_phy_roothub_init
);
105 int usb_phy_roothub_exit(struct usb_phy_roothub
*phy_roothub
)
107 struct usb_phy_roothub
*roothub_entry
;
108 struct list_head
*head
;
114 head
= &phy_roothub
->list
;
116 list_for_each_entry(roothub_entry
, head
, list
) {
117 err
= phy_exit(roothub_entry
->phy
);
124 EXPORT_SYMBOL_GPL(usb_phy_roothub_exit
);
126 int usb_phy_roothub_set_mode(struct usb_phy_roothub
*phy_roothub
,
129 struct usb_phy_roothub
*roothub_entry
;
130 struct list_head
*head
;
136 head
= &phy_roothub
->list
;
138 list_for_each_entry(roothub_entry
, head
, list
) {
139 err
= phy_set_mode(roothub_entry
->phy
, mode
);
147 list_for_each_entry_continue_reverse(roothub_entry
, head
, list
)
148 phy_power_off(roothub_entry
->phy
);
152 EXPORT_SYMBOL_GPL(usb_phy_roothub_set_mode
);
154 int usb_phy_roothub_calibrate(struct usb_phy_roothub
*phy_roothub
)
156 struct usb_phy_roothub
*roothub_entry
;
157 struct list_head
*head
;
163 head
= &phy_roothub
->list
;
165 list_for_each_entry(roothub_entry
, head
, list
) {
166 err
= phy_calibrate(roothub_entry
->phy
);
173 EXPORT_SYMBOL_GPL(usb_phy_roothub_calibrate
);
175 int usb_phy_roothub_power_on(struct usb_phy_roothub
*phy_roothub
)
177 struct usb_phy_roothub
*roothub_entry
;
178 struct list_head
*head
;
184 head
= &phy_roothub
->list
;
186 list_for_each_entry(roothub_entry
, head
, list
) {
187 err
= phy_power_on(roothub_entry
->phy
);
195 list_for_each_entry_continue_reverse(roothub_entry
, head
, list
)
196 phy_power_off(roothub_entry
->phy
);
200 EXPORT_SYMBOL_GPL(usb_phy_roothub_power_on
);
202 void usb_phy_roothub_power_off(struct usb_phy_roothub
*phy_roothub
)
204 struct usb_phy_roothub
*roothub_entry
;
209 list_for_each_entry_reverse(roothub_entry
, &phy_roothub
->list
, list
)
210 phy_power_off(roothub_entry
->phy
);
212 EXPORT_SYMBOL_GPL(usb_phy_roothub_power_off
);
214 int usb_phy_roothub_suspend(struct device
*controller_dev
,
215 struct usb_phy_roothub
*phy_roothub
)
217 usb_phy_roothub_power_off(phy_roothub
);
219 /* keep the PHYs initialized so the device can wake up the system */
220 if (device_may_wakeup(controller_dev
))
223 return usb_phy_roothub_exit(phy_roothub
);
225 EXPORT_SYMBOL_GPL(usb_phy_roothub_suspend
);
227 int usb_phy_roothub_resume(struct device
*controller_dev
,
228 struct usb_phy_roothub
*phy_roothub
)
232 /* if the device can't wake up the system _exit was called */
233 if (!device_may_wakeup(controller_dev
)) {
234 err
= usb_phy_roothub_init(phy_roothub
);
239 err
= usb_phy_roothub_power_on(phy_roothub
);
241 /* undo _init if _power_on failed */
242 if (err
&& !device_may_wakeup(controller_dev
))
243 usb_phy_roothub_exit(phy_roothub
);
247 EXPORT_SYMBOL_GPL(usb_phy_roothub_resume
);