diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index cc9c93af..3ff41d87 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -1021,6 +1021,7 @@ struct dwc2_hsotg { u16 frame_number; struct phy *phy; + struct work_struct phy_rst_work; struct usb_phy *uphy; struct dwc2_hsotg_plat *plat; struct regulator_bulk_data supplies[DWC2_NUM_SUPPLIES]; diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c index 19ae2595..f1270bf1 100644 --- a/drivers/usb/dwc2/core_intr.c +++ b/drivers/usb/dwc2/core_intr.c @@ -396,6 +396,7 @@ static void dwc2_wakeup_from_lpm_l1(struct dwc2_hsotg *hsotg) static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg) { int ret; + struct device_node *np = hsotg->dev->of_node; /* Clear interrupt */ dwc2_writel(hsotg, GINTSTS_WKUPINT, GINTSTS); @@ -435,6 +436,16 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg) /* Restart the Phy Clock */ pcgcctl &= ~PCGCTL_STOPPCLK; dwc2_writel(hsotg, pcgcctl, PCGCTL); + + /* + * It is a quirk in Rockchip RK3288, causing by + * a hardware bug. This will propagate out and + * eventually we'll re-enumerate the device. + * Not great but the best we can do. + */ + if (of_device_is_compatible(np, "rockchip,rk3288-usb")) + schedule_work(&hsotg->phy_rst_work); + mod_timer(&hsotg->wkp_timer, jiffies + msecs_to_jiffies(71)); } else { diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index 57764289..748763bd 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -208,6 +208,14 @@ int dwc2_lowlevel_hw_disable(struct dwc2_hsotg *hsotg) return ret; } +/* Only used to reset usb phy at interrupter runtime */ +static void dwc2_reset_phy_work(struct work_struct *data) +{ + struct dwc2_hsotg *hsotg = container_of(data, struct dwc2_hsotg, + phy_rst_work); + phy_reset(hsotg->phy); +} + static int dwc2_lowlevel_hw_init(struct dwc2_hsotg *hsotg) { int i, ret; @@ -252,6 +260,7 @@ static int dwc2_lowlevel_hw_init(struct dwc2_hsotg *hsotg) return ret; } } + INIT_WORK(&hsotg->phy_rst_work, dwc2_reset_phy_work); if (!hsotg->phy) { hsotg->uphy = devm_usb_get_phy(hsotg->dev, USB_PHY_TYPE_USB2);