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) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MVGTC78dgCGfRANNKjoPea
This commit is contained in:
Noah
2026-09-22 16:54:54 -06:00
co-authored by Claude Opus 5
parent e12bb2c554
commit 9ff60d60db
+40
View File
@@ -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;
}