From 5ac62b80f7471ee9858ab0459af07180de938b51 Mon Sep 17 00:00:00 2001 From: William Wu Date: Tue, 7 Dec 2021 15:14:34 +0800 Subject: [PATCH] usb: dwc3: fix runtime pm for rockchip The default autosuspend delay of runtime pm is 5000ms, it's too long for the application scenario of hot plug. If usb plug out and plug in again in 5000ms, it will fail to do runtime suspend/resume, and also fail to do usb phy ops. For RK3588 Type-C USB3.1 Gen1, it needs to do usb3 phy init for normal or flip orientation during dwc3 runtime process. This patch sets the runtime autosuspend delay to 100ms for user's normal hotplug operation. Given the autosuspend delay 100ms is very short, it may cause the usb device role fail to connect to usb host in the following case: 1. Use Type-C to Type-A cable, connenct RK3588 usb to PC. 2. The TCPM set the usb role as PERIPHERAL and call dwc3_usb_role_switch_set() -> __dwc3_set_mode() -> dwc3_runtime_resume() -> power on u2/u3 phy -> delay 100ms -> dwc3_runtime_suspend(). 3. In the dwc3_runtime_suspend(), it will check the dwc->connected which can only be set to true in the dwc3_gadget_reset_interrupt(). 4. If the PC usb host doesn't send reset signal within 100ms, the dwc3 will do dwc3_runtime_suspend() and power off u2/u3 phy. This cause usb device not to be detected. So this patch fix the runtime pm mechanism for usb device role to forbid dwc3 enter runtime suspend if the desired role is PERIPHERAL. And allow dwc3 to enter runtime suspend again after the desired role is not PERIPHERAL. For the usb host role, the xHCI platform driver has forbidden it to do autosuspend, so we don't care the usb host role here. Signed-off-by: William Wu Change-Id: I195a5fd7d8264108dd32f56f5b9bdb7f0e83da90 --- drivers/usb/dwc3/core.c | 15 +++++++++++++++ drivers/usb/dwc3/core.h | 6 ++++++ drivers/usb/dwc3/drd.c | 13 +++++++++++++ 3 files changed, 34 insertions(+) diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 8e4f5a2cd259..03b390b0fa5a 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -126,6 +126,18 @@ static void __dwc3_set_mode(struct work_struct *work) pm_runtime_get_sync(dwc->dev); +#if defined(CONFIG_ARCH_ROCKCHIP) && defined(CONFIG_NO_GKI) + if (dwc->desired_role_sw_mode == USB_DR_MODE_PERIPHERAL && + dwc->desired_role_sw_mode != dwc->current_role_sw_mode) + pm_runtime_get(dwc->dev); + else if ((dwc->desired_role_sw_mode == USB_DR_MODE_UNKNOWN || + dwc->desired_role_sw_mode == USB_DR_MODE_HOST) && + dwc->current_role_sw_mode == USB_DR_MODE_PERIPHERAL) + pm_runtime_put(dwc->dev); + + dwc->current_role_sw_mode = dwc->desired_role_sw_mode; +#endif + if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_OTG) dwc3_otg_update(dwc, 0); @@ -1664,6 +1676,9 @@ static int dwc3_probe(struct platform_device *pdev) if (dwc->dr_mode == USB_DR_MODE_OTG && of_device_is_compatible(dev->parent->of_node, "rockchip,rk3399-dwc3")) { +#if defined(CONFIG_ARCH_ROCKCHIP) && defined(CONFIG_NO_GKI) + pm_runtime_set_autosuspend_delay(dev, 100); +#endif pm_runtime_allow(dev); pm_runtime_put_sync_suspend(dev); } else { diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 2b44bb38bb7a..ed3fddbc5e04 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -1010,6 +1010,8 @@ struct dwc3_scratchpad_array { * @role_sw: usb_role_switch handle * @role_switch_default_mode: default operation mode of controller while * usb role is USB_ROLE_NONE. + * @current_role_sw_mode: current usb role switch mode. + * @desired_role_sw_mode: desired usb role switch mode. * @usb_psy: pointer to power supply interface. * @usb2_phy: pointer to USB2 PHY * @usb3_phy: pointer to USB3 PHY @@ -1160,6 +1162,10 @@ struct dwc3 { enum usb_phy_interface hsphy_mode; struct usb_role_switch *role_sw; enum usb_dr_mode role_switch_default_mode; +#if defined(CONFIG_ARCH_ROCKCHIP) && defined(CONFIG_NO_GKI) + u32 current_role_sw_mode; + u32 desired_role_sw_mode; +#endif struct power_supply *usb_psy; diff --git a/drivers/usb/dwc3/drd.c b/drivers/usb/dwc3/drd.c index e2b68bb770d1..f50255a71770 100644 --- a/drivers/usb/dwc3/drd.c +++ b/drivers/usb/dwc3/drd.c @@ -431,6 +431,15 @@ static int dwc3_drd_notifier(struct notifier_block *nb, { struct dwc3 *dwc = container_of(nb, struct dwc3, edev_nb); +#if defined(CONFIG_ARCH_ROCKCHIP) && defined(CONFIG_NO_GKI) + if (extcon_get_state(dwc->edev, EXTCON_USB)) + dwc->desired_role_sw_mode = USB_DR_MODE_PERIPHERAL; + else if (extcon_get_state(dwc->edev, EXTCON_USB_HOST)) + dwc->desired_role_sw_mode = USB_DR_MODE_HOST; + else + dwc->desired_role_sw_mode = USB_DR_MODE_UNKNOWN; +#endif + dwc3_set_mode(dwc, event ? DWC3_GCTL_PRTCAP_HOST : DWC3_GCTL_PRTCAP_DEVICE); @@ -491,6 +500,10 @@ static int dwc3_usb_role_switch_set(struct usb_role_switch *sw, struct dwc3 *dwc = usb_role_switch_get_drvdata(sw); u32 mode; +#if defined(CONFIG_ARCH_ROCKCHIP) && defined(CONFIG_NO_GKI) + dwc->desired_role_sw_mode = role; +#endif + switch (role) { case USB_ROLE_HOST: mode = DWC3_GCTL_PRTCAP_HOST;