From 95b12ef4ea3124ea0b7287d8df71ebf078a90880 Mon Sep 17 00:00:00 2001 From: Frank Wang Date: Thu, 5 Mar 2020 17:30:40 +0800 Subject: [PATCH] usb: dwc2: amend phy operation process Refer to lowlevel mechanism of dwc2, amend the PHY operation process in case of unbalance for power on and off as well. This patch fix unbalanced phy power management if otg cable plug in between the completion of dwc2 probe and udc_start. Signed-off-by: Frank Wang Change-Id: Ic0c2811ed84f8f46e99e03eff44c9d20a791e05f --- drivers/usb/dwc2/core.h | 5 ++ drivers/usb/dwc2/gadget.c | 30 +++-------- drivers/usb/dwc2/hcd.c | 3 ++ drivers/usb/dwc2/platform.c | 104 ++++++++++++++++++++++++++---------- 4 files changed, 89 insertions(+), 53 deletions(-) diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index c619c3190b22..03632badaa57 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -864,6 +864,7 @@ struct dwc2_hregs_backup { * @hcd_enabled: Host mode sub-driver initialization indicator. * @gadget_enabled: Peripheral mode sub-driver initialization indicator. * @ll_hw_enabled: Status of low-level hardware resources. + * @ll_phy_enabled Status of low-level PHY resources. * @hibernated: True if core is hibernated * @reset_phy_on_wake: Quirk saying that we should assert PHY reset on a * remote wakeup. @@ -1059,6 +1060,7 @@ struct dwc2_hsotg { unsigned int hcd_enabled:1; unsigned int gadget_enabled:1; unsigned int ll_hw_enabled:1; + unsigned int ll_phy_enabled:1; unsigned int hibernated:1; unsigned int reset_phy_on_wake:1; unsigned int need_phy_for_wake:1; @@ -1344,6 +1346,9 @@ extern const struct of_device_id dwc2_of_match_table[]; int dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg); int dwc2_lowlevel_hw_disable(struct dwc2_hsotg *hsotg); +int dwc2_lowlevel_phy_enable(struct dwc2_hsotg *hsotg); +int dwc2_lowlevel_phy_disable(struct dwc2_hsotg *hsotg); + /* Common polling functions */ int dwc2_hsotg_wait_bit_set(struct dwc2_hsotg *hs_otg, u32 reg, u32 bit, u32 timeout); diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 4201256ec4e5..14a9935e767f 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -4478,21 +4478,11 @@ static int dwc2_hsotg_udc_start(struct usb_gadget *gadget, } if (hsotg->dr_mode == USB_DR_MODE_OTG && dwc2_is_device_mode(hsotg)) { - struct platform_device *pdev = to_platform_device(hsotg->dev); - - if (hsotg->uphy) { - ret = usb_phy_init(hsotg->uphy); - } else if (hsotg->plat && hsotg->plat->phy_init) { - ret = hsotg->plat->phy_init(pdev, - hsotg->plat->phy_type); - } else { - ret = phy_power_on(hsotg->phy); - if (ret == 0) - ret = phy_init(hsotg->phy); + if (!hsotg->ll_phy_enabled) { + ret = dwc2_lowlevel_phy_enable(hsotg); + if (ret) + goto err; } - - if (ret) - goto err; } if (!IS_ERR_OR_NULL(hsotg->uphy)) @@ -4555,16 +4545,8 @@ static int dwc2_hsotg_udc_stop(struct usb_gadget *gadget) dwc2_lowlevel_hw_disable(hsotg); if (hsotg->dr_mode == USB_DR_MODE_OTG && dwc2_is_device_mode(hsotg)) { - struct platform_device *pdev = to_platform_device(hsotg->dev); - - if (hsotg->uphy) { - usb_phy_shutdown(hsotg->uphy); - } else if (hsotg->plat && hsotg->plat->phy_exit) { - hsotg->plat->phy_exit(pdev, hsotg->plat->phy_type); - } else { - phy_exit(hsotg->phy); - phy_power_off(hsotg->phy); - } + if (hsotg->ll_phy_enabled) + dwc2_lowlevel_phy_disable(hsotg); } return 0; diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 70d178966666..eb1357cce876 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -3188,6 +3188,9 @@ static void dwc2_conn_id_status_change(struct work_struct *work) dev_dbg(hsotg->dev, "%s()\n", __func__); + if (!hsotg->ll_phy_enabled && dwc2_is_host_mode(hsotg)) + dwc2_lowlevel_phy_enable(hsotg); + gotgctl = dwc2_readl(hsotg, GOTGCTL); dev_dbg(hsotg->dev, "gotgctl=%0x\n", gotgctl); dev_dbg(hsotg->dev, "gotgctl.b.conidsts=%d\n", diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index 403654ec9a5a..c83eaf74f10c 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -128,6 +128,74 @@ static void __dwc2_disable_regulators(void *data) regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies), hsotg->supplies); } +static int __dwc2_lowlevel_phy_enable(struct dwc2_hsotg *hsotg) +{ + struct platform_device *pdev = to_platform_device(hsotg->dev); + int ret; + + if (hsotg->uphy) { + ret = usb_phy_init(hsotg->uphy); + } else if (hsotg->plat && hsotg->plat->phy_init) { + ret = hsotg->plat->phy_init(pdev, hsotg->plat->phy_type); + } else { + ret = phy_power_on(hsotg->phy); + if (ret == 0) + ret = phy_init(hsotg->phy); + } + + return ret; +} + +/** + * dwc2_lowlevel_phy_enable - enable lowlevel PHY resources + * @hsotg: The driver state + * + * A wrapper for platform code responsible for controlling + * low-level PHY resources. + */ +int dwc2_lowlevel_phy_enable(struct dwc2_hsotg *hsotg) +{ + int ret = __dwc2_lowlevel_phy_enable(hsotg); + + if (ret == 0) + hsotg->ll_phy_enabled = true; + return ret; +} + +static int __dwc2_lowlevel_phy_disable(struct dwc2_hsotg *hsotg) +{ + struct platform_device *pdev = to_platform_device(hsotg->dev); + int ret = 0; + + if (hsotg->uphy) { + usb_phy_shutdown(hsotg->uphy); + } else if (hsotg->plat && hsotg->plat->phy_exit) { + ret = hsotg->plat->phy_exit(pdev, hsotg->plat->phy_type); + } else { + ret = phy_exit(hsotg->phy); + if (ret == 0) + ret = phy_power_off(hsotg->phy); + } + + return ret; +} + +/** + * dwc2_lowlevel_phy_disable - disable lowlevel PHY resources + * @hsotg: The driver state + * + * A wrapper for platform code responsible for controlling + * low-level PHY platform resources. + */ +int dwc2_lowlevel_phy_disable(struct dwc2_hsotg *hsotg) +{ + int ret = __dwc2_lowlevel_phy_disable(hsotg); + + if (ret == 0) + hsotg->ll_phy_enabled = false; + return ret; +} + static int __dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg) { struct platform_device *pdev = to_platform_device(hsotg->dev); @@ -147,15 +215,8 @@ static int __dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg) if (ret) return ret; - if (hsotg->uphy) { - ret = usb_phy_init(hsotg->uphy); - } else if (hsotg->plat && hsotg->plat->phy_init) { - ret = hsotg->plat->phy_init(pdev, hsotg->plat->phy_type); - } else { - ret = phy_power_on(hsotg->phy); - if (ret == 0) - ret = phy_init(hsotg->phy); - } + if (!hsotg->ll_phy_enabled) + ret = dwc2_lowlevel_phy_enable(hsotg); return ret; } @@ -178,18 +239,11 @@ int dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg) static int __dwc2_lowlevel_hw_disable(struct dwc2_hsotg *hsotg) { - struct platform_device *pdev = to_platform_device(hsotg->dev); int ret = 0; - if (hsotg->uphy) { - usb_phy_shutdown(hsotg->uphy); - } else if (hsotg->plat && hsotg->plat->phy_exit) { - ret = hsotg->plat->phy_exit(pdev, hsotg->plat->phy_type); - } else { - ret = phy_exit(hsotg->phy); - if (ret == 0) - ret = phy_power_off(hsotg->phy); - } + if (hsotg->ll_phy_enabled) + ret = dwc2_lowlevel_phy_disable(hsotg); + if (ret) return ret; @@ -608,16 +662,8 @@ static int dwc2_driver_probe(struct platform_device *dev) dwc2_lowlevel_hw_disable(hsotg); if (hsotg->dr_mode == USB_DR_MODE_OTG && dwc2_is_device_mode(hsotg)) { - struct platform_device *pdev = to_platform_device(hsotg->dev); - - if (hsotg->uphy) { - usb_phy_shutdown(hsotg->uphy); - } else if (hsotg->plat && hsotg->plat->phy_exit) { - hsotg->plat->phy_exit(pdev, hsotg->plat->phy_type); - } else { - phy_exit(hsotg->phy); - phy_power_off(hsotg->phy); - } + if (hsotg->ll_phy_enabled) + dwc2_lowlevel_phy_disable(hsotg); } #if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || \