From 9ff60d60db3ba945eea575d800f411fa63da4ba7 Mon Sep 17 00:00:00 2001 From: Noah Date: Tue, 22 Sep 2026 16:54:54 -0600 Subject: [PATCH] Do not re-init OTG host registers on a live controller dwc3_otg_host_init() carries the comment "should be called before Host controller driver is started". It rewrites OCTL/OCFG, touches GUSB2PHYCFG and re-asserts port power (OCTL.PrtPwrCtl). dwc3_otg_irq() sets otg_restart_host ONLY when current_otg_role is already DWC3_OTG_ROLE_HOST. The threaded handler then calls dwc3_otg_host_init() unconditionally, so that path always ran against a live, running host -- the exact condition the comment forbids. Re-asserting port power under a running xHCI corrupts an in-flight command. Observed on the RV1106 86-panel as the controller failing to answer a Stop Endpoint command, after which xhci-hcd declares it dead and tears the bus down. An r8152 USB NIC stalls a transfer and the controller died 8 times out of 8 between 452s and 587s. Compiling the OTG path out entirely (dr_mode=host) survived 1810s with zero deaths, which localised it here. On this board the event is always spurious: the dwc3 node deliberately carries no extcon, because 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 and log it at debug level. This does not remove the restart capability. dwc3_set_mode() still runs immediately below, and if the role has genuinely changed dwc3_otg_update() performs a proper dwc3_host_exit() before re-initialising. The only behaviour removed is poking a running controller, which was never legitimate. Refs flare-edge#160 Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01MVGTC78dgCGfRANNKjoPea --- patches/28-dwc3-otg-no-live-reinit.patch | 40 ++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 patches/28-dwc3-otg-no-live-reinit.patch diff --git a/patches/28-dwc3-otg-no-live-reinit.patch b/patches/28-dwc3-otg-no-live-reinit.patch new file mode 100644 index 0000000..c8c7871 --- /dev/null +++ b/patches/28-dwc3-otg-no-live-reinit.patch @@ -0,0 +1,40 @@ +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; + } +