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 /* Allocate the roothub_entry by specific name of phy */
23 static int usb_phy_roothub_add_phy_by_name(struct device
*dev
, const char *name
,
24 struct list_head
*list
)
26 struct usb_phy_roothub
*roothub_entry
;
29 phy
= devm_of_phy_get(dev
, dev
->of_node
, name
);
33 roothub_entry
= devm_kzalloc(dev
, sizeof(*roothub_entry
), GFP_KERNEL
);
37 INIT_LIST_HEAD(&roothub_entry
->list
);
39 roothub_entry
->phy
= phy
;
41 list_add_tail(&roothub_entry
->list
, list
);
46 static int usb_phy_roothub_add_phy(struct device
*dev
, int index
,
47 struct list_head
*list
)
49 struct usb_phy_roothub
*roothub_entry
;
52 phy
= devm_of_phy_get_by_index(dev
, dev
->of_node
, index
);
54 if (PTR_ERR(phy
) == -ENODEV
)
60 roothub_entry
= devm_kzalloc(dev
, sizeof(*roothub_entry
), GFP_KERNEL
);
64 INIT_LIST_HEAD(&roothub_entry
->list
);
66 roothub_entry
->phy
= phy
;
68 list_add_tail(&roothub_entry
->list
, list
);
73 struct usb_phy_roothub
*usb_phy_roothub_alloc(struct device
*dev
)
75 struct usb_phy_roothub
*phy_roothub
;
78 if (!IS_ENABLED(CONFIG_GENERIC_PHY
))
81 num_phys
= of_count_phandle_with_args(dev
->of_node
, "phys",
86 phy_roothub
= devm_kzalloc(dev
, sizeof(*phy_roothub
), GFP_KERNEL
);
88 return ERR_PTR(-ENOMEM
);
90 INIT_LIST_HEAD(&phy_roothub
->list
);
92 if (!usb_phy_roothub_add_phy_by_name(dev
, "usb2-phy", &phy_roothub
->list
))
95 for (i
= 0; i
< num_phys
; i
++) {
96 err
= usb_phy_roothub_add_phy(dev
, i
, &phy_roothub
->list
);
103 EXPORT_SYMBOL_GPL(usb_phy_roothub_alloc
);
106 * usb_phy_roothub_alloc_usb3_phy - alloc the roothub
107 * @dev: the device of the host controller
109 * Allocate the usb phy roothub if the host use a generic usb3-phy.
111 * Return: On success, a pointer to the usb_phy_roothub. Otherwise,
112 * %NULL if no use usb3 phy or %-ENOMEM if out of memory.
114 struct usb_phy_roothub
*usb_phy_roothub_alloc_usb3_phy(struct device
*dev
)
116 struct usb_phy_roothub
*phy_roothub
;
119 if (!IS_ENABLED(CONFIG_GENERIC_PHY
))
122 num_phys
= of_count_phandle_with_args(dev
->of_node
, "phys",
127 phy_roothub
= devm_kzalloc(dev
, sizeof(*phy_roothub
), GFP_KERNEL
);
129 return ERR_PTR(-ENOMEM
);
131 INIT_LIST_HEAD(&phy_roothub
->list
);
133 if (!usb_phy_roothub_add_phy_by_name(dev
, "usb3-phy", &phy_roothub
->list
))
138 EXPORT_SYMBOL_GPL(usb_phy_roothub_alloc_usb3_phy
);
140 int usb_phy_roothub_init(struct usb_phy_roothub
*phy_roothub
)
142 struct usb_phy_roothub
*roothub_entry
;
143 struct list_head
*head
;
149 head
= &phy_roothub
->list
;
151 list_for_each_entry(roothub_entry
, head
, list
) {
152 err
= phy_init(roothub_entry
->phy
);
160 list_for_each_entry_continue_reverse(roothub_entry
, head
, list
)
161 phy_exit(roothub_entry
->phy
);
165 EXPORT_SYMBOL_GPL(usb_phy_roothub_init
);
167 int usb_phy_roothub_exit(struct usb_phy_roothub
*phy_roothub
)
169 struct usb_phy_roothub
*roothub_entry
;
170 struct list_head
*head
;
176 head
= &phy_roothub
->list
;
178 list_for_each_entry(roothub_entry
, head
, list
) {
179 err
= phy_exit(roothub_entry
->phy
);
186 EXPORT_SYMBOL_GPL(usb_phy_roothub_exit
);
188 int usb_phy_roothub_set_mode(struct usb_phy_roothub
*phy_roothub
,
191 struct usb_phy_roothub
*roothub_entry
;
192 struct list_head
*head
;
198 head
= &phy_roothub
->list
;
200 list_for_each_entry(roothub_entry
, head
, list
) {
201 err
= phy_set_mode(roothub_entry
->phy
, mode
);
209 list_for_each_entry_continue_reverse(roothub_entry
, head
, list
)
210 phy_power_off(roothub_entry
->phy
);
214 EXPORT_SYMBOL_GPL(usb_phy_roothub_set_mode
);
216 int usb_phy_roothub_calibrate(struct usb_phy_roothub
*phy_roothub
)
218 struct usb_phy_roothub
*roothub_entry
;
219 struct list_head
*head
;
225 head
= &phy_roothub
->list
;
227 list_for_each_entry(roothub_entry
, head
, list
) {
228 err
= phy_calibrate(roothub_entry
->phy
);
235 EXPORT_SYMBOL_GPL(usb_phy_roothub_calibrate
);
238 * usb_phy_roothub_notify_connect() - connect notification
239 * @phy_roothub: the phy of roothub, if the host use a generic phy.
240 * @port: the port index for connect
242 * If the phy needs to get connection status, the callback can be used.
243 * Returns: %0 if successful, a negative error code otherwise
245 int usb_phy_roothub_notify_connect(struct usb_phy_roothub
*phy_roothub
, int port
)
247 struct usb_phy_roothub
*roothub_entry
;
248 struct list_head
*head
;
254 head
= &phy_roothub
->list
;
256 list_for_each_entry(roothub_entry
, head
, list
) {
257 err
= phy_notify_connect(roothub_entry
->phy
, port
);
264 EXPORT_SYMBOL_GPL(usb_phy_roothub_notify_connect
);
267 * usb_phy_roothub_notify_disconnect() - disconnect notification
268 * @phy_roothub: the phy of roothub, if the host use a generic phy.
269 * @port: the port index for disconnect
271 * If the phy needs to get connection status, the callback can be used.
272 * Returns: %0 if successful, a negative error code otherwise
274 int usb_phy_roothub_notify_disconnect(struct usb_phy_roothub
*phy_roothub
, int port
)
276 struct usb_phy_roothub
*roothub_entry
;
277 struct list_head
*head
;
283 head
= &phy_roothub
->list
;
285 list_for_each_entry(roothub_entry
, head
, list
) {
286 err
= phy_notify_disconnect(roothub_entry
->phy
, port
);
293 EXPORT_SYMBOL_GPL(usb_phy_roothub_notify_disconnect
);
295 int usb_phy_roothub_power_on(struct usb_phy_roothub
*phy_roothub
)
297 struct usb_phy_roothub
*roothub_entry
;
298 struct list_head
*head
;
304 head
= &phy_roothub
->list
;
306 list_for_each_entry(roothub_entry
, head
, list
) {
307 err
= phy_power_on(roothub_entry
->phy
);
315 list_for_each_entry_continue_reverse(roothub_entry
, head
, list
)
316 phy_power_off(roothub_entry
->phy
);
320 EXPORT_SYMBOL_GPL(usb_phy_roothub_power_on
);
322 void usb_phy_roothub_power_off(struct usb_phy_roothub
*phy_roothub
)
324 struct usb_phy_roothub
*roothub_entry
;
329 list_for_each_entry_reverse(roothub_entry
, &phy_roothub
->list
, list
)
330 phy_power_off(roothub_entry
->phy
);
332 EXPORT_SYMBOL_GPL(usb_phy_roothub_power_off
);
334 int usb_phy_roothub_suspend(struct device
*controller_dev
,
335 struct usb_phy_roothub
*phy_roothub
)
337 usb_phy_roothub_power_off(phy_roothub
);
339 /* keep the PHYs initialized so the device can wake up the system */
340 if (device_may_wakeup(controller_dev
))
343 return usb_phy_roothub_exit(phy_roothub
);
345 EXPORT_SYMBOL_GPL(usb_phy_roothub_suspend
);
347 int usb_phy_roothub_resume(struct device
*controller_dev
,
348 struct usb_phy_roothub
*phy_roothub
)
352 /* if the device can't wake up the system _exit was called */
353 if (!device_may_wakeup(controller_dev
)) {
354 err
= usb_phy_roothub_init(phy_roothub
);
359 err
= usb_phy_roothub_power_on(phy_roothub
);
361 /* undo _init if _power_on failed */
362 if (err
&& !device_may_wakeup(controller_dev
))
363 usb_phy_roothub_exit(phy_roothub
);
367 EXPORT_SYMBOL_GPL(usb_phy_roothub_resume
);