diff --git a/drivers/usb/dwc3/drd.c b/drivers/usb/dwc3/drd.c --- a/drivers/usb/dwc3/drd.c +++ b/drivers/usb/dwc3/drd.c @@ -55,7 +55,35 @@ spin_lock(&dwc->lock); if (dwc->otg_restart_host) { - dwc3_otg_host_init(dwc); + /* + * dwc3_otg_host_init() is documented just above its definition as + * "should be called before Host controller driver is started": it + * rewrites OCTL/OCFG, touches GUSB2PHYCFG and re-asserts port power + * (OCTL.PrtPwrCtl). Doing that to a controller that is already + * running corrupts an in-flight command. + * + * But otg_restart_host is set in dwc3_otg_irq() ONLY when + * current_otg_role is already DWC3_OTG_ROLE_HOST, so this path + * always ran it against a live host -- the exact condition the + * comment forbids. Observed on the RV1106 86-panel as the xHCI + * controller failing to answer a Stop Endpoint command, after which + * xhci-hcd declares it dead and tears the bus down: an r8152 NIC + * would stall a transfer and the controller died 8 times out of 8 + * between 452s and 587s. (flare-edge#160) + * + * On this board the event is always spurious: the dwc3 node + * deliberately carries no extcon (wiring it pins the role to + * peripheral on D1-modded panels) and CONIDSTS reads 0, so the role + * cannot legitimately change while host is current. Skip the + * re-init in that case. dwc3_set_mode() below still re-evaluates + * the role, and if it has genuinely changed dwc3_otg_update() does + * a proper dwc3_host_exit() before re-initialising. + */ + if (dwc->current_otg_role == DWC3_OTG_ROLE_HOST) + dev_dbg(dwc->dev, + "OTG event while host is live; skipping host re-init\n"); + else + dwc3_otg_host_init(dwc); dwc->otg_restart_host = false; }