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
;
26 struct phy
*phy
= devm_of_phy_get_by_index(dev
, dev
->of_node
, index
);
28 if (IS_ERR_OR_NULL(phy
)) {
29 if (!phy
|| PTR_ERR(phy
) == -ENODEV
)
35 roothub_entry
= devm_kzalloc(dev
, sizeof(*roothub_entry
), GFP_KERNEL
);
39 INIT_LIST_HEAD(&roothub_entry
->list
);
41 roothub_entry
->phy
= phy
;
43 list_add_tail(&roothub_entry
->list
, list
);
48 struct usb_phy_roothub
*usb_phy_roothub_alloc(struct device
*dev
)
50 struct usb_phy_roothub
*phy_roothub
;
53 if (!IS_ENABLED(CONFIG_GENERIC_PHY
))
56 num_phys
= of_count_phandle_with_args(dev
->of_node
, "phys",
61 phy_roothub
= devm_kzalloc(dev
, sizeof(*phy_roothub
), GFP_KERNEL
);
63 return ERR_PTR(-ENOMEM
);
65 INIT_LIST_HEAD(&phy_roothub
->list
);
67 for (i
= 0; i
< num_phys
; i
++) {
68 err
= usb_phy_roothub_add_phy(dev
, i
, &phy_roothub
->list
);
75 EXPORT_SYMBOL_GPL(usb_phy_roothub_alloc
);
77 int usb_phy_roothub_init(struct usb_phy_roothub
*phy_roothub
)
79 struct usb_phy_roothub
*roothub_entry
;
80 struct list_head
*head
;
86 head
= &phy_roothub
->list
;
88 list_for_each_entry(roothub_entry
, head
, list
) {
89 err
= phy_init(roothub_entry
->phy
);
97 list_for_each_entry_continue_reverse(roothub_entry
, head
, list
)
98 phy_exit(roothub_entry
->phy
);
102 EXPORT_SYMBOL_GPL(usb_phy_roothub_init
);
104 int usb_phy_roothub_exit(struct usb_phy_roothub
*phy_roothub
)
106 struct usb_phy_roothub
*roothub_entry
;
107 struct list_head
*head
;
113 head
= &phy_roothub
->list
;
115 list_for_each_entry(roothub_entry
, head
, list
) {
116 err
= phy_exit(roothub_entry
->phy
);
123 EXPORT_SYMBOL_GPL(usb_phy_roothub_exit
);
125 int usb_phy_roothub_power_on(struct usb_phy_roothub
*phy_roothub
)
127 struct usb_phy_roothub
*roothub_entry
;
128 struct list_head
*head
;
134 head
= &phy_roothub
->list
;
136 list_for_each_entry(roothub_entry
, head
, list
) {
137 err
= phy_power_on(roothub_entry
->phy
);
145 list_for_each_entry_continue_reverse(roothub_entry
, head
, list
)
146 phy_power_off(roothub_entry
->phy
);
150 EXPORT_SYMBOL_GPL(usb_phy_roothub_power_on
);
152 void usb_phy_roothub_power_off(struct usb_phy_roothub
*phy_roothub
)
154 struct usb_phy_roothub
*roothub_entry
;
159 list_for_each_entry_reverse(roothub_entry
, &phy_roothub
->list
, list
)
160 phy_power_off(roothub_entry
->phy
);
162 EXPORT_SYMBOL_GPL(usb_phy_roothub_power_off
);
164 int usb_phy_roothub_suspend(struct device
*controller_dev
,
165 struct usb_phy_roothub
*phy_roothub
)
167 usb_phy_roothub_power_off(phy_roothub
);
169 /* keep the PHYs initialized so the device can wake up the system */
170 if (device_may_wakeup(controller_dev
))
173 return usb_phy_roothub_exit(phy_roothub
);
175 EXPORT_SYMBOL_GPL(usb_phy_roothub_suspend
);
177 int usb_phy_roothub_resume(struct device
*controller_dev
,
178 struct usb_phy_roothub
*phy_roothub
)
182 /* if the device can't wake up the system _exit was called */
183 if (!device_may_wakeup(controller_dev
)) {
184 err
= usb_phy_roothub_init(phy_roothub
);
189 err
= usb_phy_roothub_power_on(phy_roothub
);
191 /* undo _init if _power_on failed */
192 if (err
&& !device_may_wakeup(controller_dev
))
193 usb_phy_roothub_exit(phy_roothub
);
197 EXPORT_SYMBOL_GPL(usb_phy_roothub_resume
);