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:
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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) || \
|
||||
|
||||
Reference in New Issue
Block a user