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 <frank.wang@rock-chips.com>
Change-Id: Ic0c2811ed84f8f46e99e03eff44c9d20a791e05f
This commit is contained in:
Frank Wang
2020-03-05 17:30:40 +08:00
parent 964d50060b
commit 95b12ef4ea
4 changed files with 89 additions and 53 deletions

View File

@@ -864,6 +864,7 @@ struct dwc2_hregs_backup {
* @hcd_enabled: Host mode sub-driver initialization indicator. * @hcd_enabled: Host mode sub-driver initialization indicator.
* @gadget_enabled: Peripheral mode sub-driver initialization indicator. * @gadget_enabled: Peripheral mode sub-driver initialization indicator.
* @ll_hw_enabled: Status of low-level hardware resources. * @ll_hw_enabled: Status of low-level hardware resources.
* @ll_phy_enabled Status of low-level PHY resources.
* @hibernated: True if core is hibernated * @hibernated: True if core is hibernated
* @reset_phy_on_wake: Quirk saying that we should assert PHY reset on a * @reset_phy_on_wake: Quirk saying that we should assert PHY reset on a
* remote wakeup. * remote wakeup.
@@ -1059,6 +1060,7 @@ struct dwc2_hsotg {
unsigned int hcd_enabled:1; unsigned int hcd_enabled:1;
unsigned int gadget_enabled:1; unsigned int gadget_enabled:1;
unsigned int ll_hw_enabled:1; unsigned int ll_hw_enabled:1;
unsigned int ll_phy_enabled:1;
unsigned int hibernated:1; unsigned int hibernated:1;
unsigned int reset_phy_on_wake:1; unsigned int reset_phy_on_wake:1;
unsigned int need_phy_for_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_enable(struct dwc2_hsotg *hsotg);
int dwc2_lowlevel_hw_disable(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 */ /* Common polling functions */
int dwc2_hsotg_wait_bit_set(struct dwc2_hsotg *hs_otg, u32 reg, u32 bit, int dwc2_hsotg_wait_bit_set(struct dwc2_hsotg *hs_otg, u32 reg, u32 bit,
u32 timeout); u32 timeout);

View File

@@ -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)) { if (hsotg->dr_mode == USB_DR_MODE_OTG && dwc2_is_device_mode(hsotg)) {
struct platform_device *pdev = to_platform_device(hsotg->dev); if (!hsotg->ll_phy_enabled) {
ret = dwc2_lowlevel_phy_enable(hsotg);
if (hsotg->uphy) { if (ret)
ret = usb_phy_init(hsotg->uphy); goto err;
} 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 (ret)
goto err;
} }
if (!IS_ERR_OR_NULL(hsotg->uphy)) 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); dwc2_lowlevel_hw_disable(hsotg);
if (hsotg->dr_mode == USB_DR_MODE_OTG && dwc2_is_device_mode(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->ll_phy_enabled)
dwc2_lowlevel_phy_disable(hsotg);
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);
}
} }
return 0; return 0;

View File

@@ -3188,6 +3188,9 @@ static void dwc2_conn_id_status_change(struct work_struct *work)
dev_dbg(hsotg->dev, "%s()\n", __func__); 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); gotgctl = dwc2_readl(hsotg, GOTGCTL);
dev_dbg(hsotg->dev, "gotgctl=%0x\n", gotgctl); dev_dbg(hsotg->dev, "gotgctl=%0x\n", gotgctl);
dev_dbg(hsotg->dev, "gotgctl.b.conidsts=%d\n", dev_dbg(hsotg->dev, "gotgctl.b.conidsts=%d\n",

View File

@@ -128,6 +128,74 @@ static void __dwc2_disable_regulators(void *data)
regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies), hsotg->supplies); 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) static int __dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg)
{ {
struct platform_device *pdev = to_platform_device(hsotg->dev); 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) if (ret)
return ret; return ret;
if (hsotg->uphy) { if (!hsotg->ll_phy_enabled)
ret = usb_phy_init(hsotg->uphy); ret = dwc2_lowlevel_phy_enable(hsotg);
} 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; 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) static int __dwc2_lowlevel_hw_disable(struct dwc2_hsotg *hsotg)
{ {
struct platform_device *pdev = to_platform_device(hsotg->dev);
int ret = 0; int ret = 0;
if (hsotg->uphy) { if (hsotg->ll_phy_enabled)
usb_phy_shutdown(hsotg->uphy); ret = dwc2_lowlevel_phy_disable(hsotg);
} 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 (ret) if (ret)
return ret; return ret;
@@ -608,16 +662,8 @@ static int dwc2_driver_probe(struct platform_device *dev)
dwc2_lowlevel_hw_disable(hsotg); dwc2_lowlevel_hw_disable(hsotg);
if (hsotg->dr_mode == USB_DR_MODE_OTG && dwc2_is_device_mode(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->ll_phy_enabled)
dwc2_lowlevel_phy_disable(hsotg);
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 IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || \ #if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || \