usb: core: hcd: integrate the PHY wrapper into the HCD core
This integrates the PHY wrapper into the core hcd infrastructure. Multiple PHYs which are part of the HCD's device tree node are now managed (= powered on/off when needed), by the new usb_phy_roothub code. Suspend and resume is also supported, however not for runtime/auto-suspend (which is triggered for example when no devices are connected to the USB bus). This is needed on some SoCs (for example Amlogic Meson GXL) because if the PHYs are disabled during auto-suspend then devices which are plugged in afterwards are not seen by the host. One example where this is required is the Amlogic GXL and GXM SoCs: They are using a dwc3 USB controller with up to three ports enabled on the internal roothub. Each port has it's own PHY which must be enabled (if one of the PHYs is left disabled then none of the USB ports works at all). The new logic works on the Amlogic GXL and GXM SoCs because the dwc3 driver internally creates a xhci-hcd which then registers a HCD which then triggers our new PHY wrapper. USB controller drivers can opt out of this by setting "skip_phy_initialization" in struct usb_hcd to true. This is identical to how it works for a single USB PHY, so the "multiple PHY" handling is disabled for drivers that opted out of the management logic of a single PHY. Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com> Acked-by: Alan Stern <stern@rowland.harvard.edu> Acked-by: Chunfeng Yun <chunfeng.yun@mediatek.com> Tested-by: Yixun Lan <yixun.lan@amlogic.com> Tested-by: Neil Armstrong <narmstrong@baylibre.con> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Родитель
07dbff0ddb
Коммит
178a0bce05
|
@ -37,6 +37,7 @@
|
||||||
#include <linux/usb/otg.h>
|
#include <linux/usb/otg.h>
|
||||||
|
|
||||||
#include "usb.h"
|
#include "usb.h"
|
||||||
|
#include "phy.h"
|
||||||
|
|
||||||
|
|
||||||
/*-------------------------------------------------------------------------*/
|
/*-------------------------------------------------------------------------*/
|
||||||
|
@ -2260,6 +2261,9 @@ int hcd_bus_suspend(struct usb_device *rhdev, pm_message_t msg)
|
||||||
usb_set_device_state(rhdev, USB_STATE_SUSPENDED);
|
usb_set_device_state(rhdev, USB_STATE_SUSPENDED);
|
||||||
hcd->state = HC_STATE_SUSPENDED;
|
hcd->state = HC_STATE_SUSPENDED;
|
||||||
|
|
||||||
|
if (!PMSG_IS_AUTO(msg))
|
||||||
|
usb_phy_roothub_power_off(hcd->phy_roothub);
|
||||||
|
|
||||||
/* Did we race with a root-hub wakeup event? */
|
/* Did we race with a root-hub wakeup event? */
|
||||||
if (rhdev->do_remote_wakeup) {
|
if (rhdev->do_remote_wakeup) {
|
||||||
char buffer[6];
|
char buffer[6];
|
||||||
|
@ -2296,6 +2300,13 @@ int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg)
|
||||||
dev_dbg(&rhdev->dev, "skipped %s of dead bus\n", "resume");
|
dev_dbg(&rhdev->dev, "skipped %s of dead bus\n", "resume");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!PMSG_IS_AUTO(msg)) {
|
||||||
|
status = usb_phy_roothub_power_on(hcd->phy_roothub);
|
||||||
|
if (status)
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
if (!hcd->driver->bus_resume)
|
if (!hcd->driver->bus_resume)
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
if (HCD_RH_RUNNING(hcd))
|
if (HCD_RH_RUNNING(hcd))
|
||||||
|
@ -2333,6 +2344,7 @@ int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
hcd->state = old_state;
|
hcd->state = old_state;
|
||||||
|
usb_phy_roothub_power_off(hcd->phy_roothub);
|
||||||
dev_dbg(&rhdev->dev, "bus %s fail, err %d\n",
|
dev_dbg(&rhdev->dev, "bus %s fail, err %d\n",
|
||||||
"resume", status);
|
"resume", status);
|
||||||
if (status != -ESHUTDOWN)
|
if (status != -ESHUTDOWN)
|
||||||
|
@ -2769,6 +2781,18 @@ int usb_add_hcd(struct usb_hcd *hcd,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!hcd->skip_phy_initialization) {
|
||||||
|
hcd->phy_roothub = usb_phy_roothub_init(hcd->self.sysdev);
|
||||||
|
if (IS_ERR(hcd->phy_roothub)) {
|
||||||
|
retval = PTR_ERR(hcd->phy_roothub);
|
||||||
|
goto err_phy_roothub_init;
|
||||||
|
}
|
||||||
|
|
||||||
|
retval = usb_phy_roothub_power_on(hcd->phy_roothub);
|
||||||
|
if (retval)
|
||||||
|
goto err_usb_phy_roothub_power_on;
|
||||||
|
}
|
||||||
|
|
||||||
dev_info(hcd->self.controller, "%s\n", hcd->product_desc);
|
dev_info(hcd->self.controller, "%s\n", hcd->product_desc);
|
||||||
|
|
||||||
/* Keep old behaviour if authorized_default is not in [0, 1]. */
|
/* Keep old behaviour if authorized_default is not in [0, 1]. */
|
||||||
|
@ -2933,6 +2957,10 @@ err_allocate_root_hub:
|
||||||
err_register_bus:
|
err_register_bus:
|
||||||
hcd_buffer_destroy(hcd);
|
hcd_buffer_destroy(hcd);
|
||||||
err_create_buf:
|
err_create_buf:
|
||||||
|
usb_phy_roothub_power_off(hcd->phy_roothub);
|
||||||
|
err_usb_phy_roothub_power_on:
|
||||||
|
usb_phy_roothub_exit(hcd->phy_roothub);
|
||||||
|
err_phy_roothub_init:
|
||||||
if (IS_ENABLED(CONFIG_GENERIC_PHY) && hcd->remove_phy && hcd->phy) {
|
if (IS_ENABLED(CONFIG_GENERIC_PHY) && hcd->remove_phy && hcd->phy) {
|
||||||
phy_power_off(hcd->phy);
|
phy_power_off(hcd->phy);
|
||||||
phy_exit(hcd->phy);
|
phy_exit(hcd->phy);
|
||||||
|
@ -3017,6 +3045,9 @@ void usb_remove_hcd(struct usb_hcd *hcd)
|
||||||
usb_deregister_bus(&hcd->self);
|
usb_deregister_bus(&hcd->self);
|
||||||
hcd_buffer_destroy(hcd);
|
hcd_buffer_destroy(hcd);
|
||||||
|
|
||||||
|
usb_phy_roothub_power_off(hcd->phy_roothub);
|
||||||
|
usb_phy_roothub_exit(hcd->phy_roothub);
|
||||||
|
|
||||||
if (IS_ENABLED(CONFIG_GENERIC_PHY) && hcd->remove_phy && hcd->phy) {
|
if (IS_ENABLED(CONFIG_GENERIC_PHY) && hcd->remove_phy && hcd->phy) {
|
||||||
phy_power_off(hcd->phy);
|
phy_power_off(hcd->phy);
|
||||||
phy_exit(hcd->phy);
|
phy_exit(hcd->phy);
|
||||||
|
|
|
@ -104,6 +104,7 @@ struct usb_hcd {
|
||||||
*/
|
*/
|
||||||
struct usb_phy *usb_phy;
|
struct usb_phy *usb_phy;
|
||||||
struct phy *phy;
|
struct phy *phy;
|
||||||
|
struct usb_phy_roothub *phy_roothub;
|
||||||
|
|
||||||
/* Flags that need to be manipulated atomically because they can
|
/* Flags that need to be manipulated atomically because they can
|
||||||
* change while the host controller is running. Always use
|
* change while the host controller is running. Always use
|
||||||
|
|
Загрузка…
Ссылка в новой задаче