otg-live-reinit-fix
4
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
8066ef7d79 |
Stop the OTG state machines running blind in either role
Widens the #160 fix, which was scoped too narrowly to host role, and fixes flare-edge#168. Device role was deliberately left alone on the reasoning that the state machine is only meaningless when the port is a host. That was wrong. On a board with no extcon phandle the machine has no external role signal in EITHER direction, and with bvalid pinned high by the VBUS bypass its charger detection cannot be meaningful at all. In device role its verdict suspends the phy underneath a bound gadget, so the USB recovery link dies. Reproduced deterministically on warden-c8a3 with a host attached: device role, host attached ccf=1 phy_sus=0x0 udc=configured works -> host role ccf=1 phy_sus=0x0 udc=(none) -> back to gadget ccf=0 phy_sus=0x1d1 udc=configured SUSPENDED -> +15s / +30s ccf=0 phy_sus=0x1d1 udc=configured never recovers and the 2207:0019 device disappears from the host for good. Nothing notices, because every obvious signal lies: udc state reads "configured", usb0 carrier reads 1, and DCTL RUN_STOP reads 1, so the gadget really is asserting its pullup. It asserts into a phy whose analog front end and 480M clock are off, so the SoC sees SE0 and the host sees no device at all, not even an enumeration error. It cannot self-heal either. bvalid is pinned high, so a host attaching later produces no VBUS edge for the machine to trigger on: it ran once, powered the phy off, and nothing re-evaluates. That is why a reboot appears to fix it and why this read as a cable fault for weeks. It is not, and the cables were fine. Gated on the extcon being self-allocated rather than on the board compatible, so it describes the actual precondition: no external role signal exists. Renamed from 29-usb2phy-no-otg-sm-in-host.patch, whose name now misstated the scope. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MVGTC78dgCGfRANNKjoPea |
||
|
|
f6add60a9c |
Keep the usb2 phy OTG state machines out of host role
Issue #160: the USB ethernet link on the OTG port stops passing traffic and the host controller is later declared dead ("xHCI host not responding to stop endpoint command", "HC died"). Root cause. The phy's OTG state machine and charger detection only make sense while the port is a peripheral. This board has no ID source, so in host role their inputs are meaningless: rockchip_usb2phy_otg_sm_work() reaches OTG_STATE_B_IDLE with vbus asserted, schedules rockchip_chg_detect_work(), and a DCP verdict calls rockchip_usb2phy_power_off(). That suspends the phy and drops the 480M clkout the controller's core runs on, while a device is enumerated and working. The controller then stops clocking with its register block still readable, so it reports itself healthy: USBCMD keeps RS set, USBSTS reads 0, PORTSC still shows CCS and PED. Only MFINDEX gives it away by no longer advancing. Nothing completes from that point, and the first transmit afterwards times out. The recovery path cannot help either, because xhci_halt() needs the core clock to update USBSTS.HCH, so it times out and xhci_reset() returns early without ever writing USBCMD.HCRST. dwc3 already calls phy_set_mode() on every role change, but this driver implemented no .set_mode, so the phy never learned the role. Implement it, track the role per port, and return early from both work functions in host role. The check is inside the functions because both have direct callers besides the scheduled work. This keeps dr_mode "otg" and runtime role switching. Setting dr_mode to "host" also avoids the fault, by skipping the same state machine, but compiles out the gadget needed for flashing, recovery and pairing. Measured on warden-c8a3, per arm, transmit packets and xhci interrupts delivered after the stated idle: idle 0 s +9, +10, +9 packets +170, +176, +188 interrupts idle 120 s +0, +0, +0 packets +0, +0, +0 interrupts Also drops 28-dwc3-otg-no-live-reinit.patch. It guarded dwc3_otg_host_init() against re-entry on a live controller, but that path never executes here: GSTS reads OTG IP clear and instrumentation logged no entries on either the hardware irq or the guard site. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MVGTC78dgCGfRANNKjoPea |
||
|
|
4a5481b6a5 |
Instrument the dwc3 OTG path to test the guard
The guard alone did not fix flare-edge#160: with dr_mode="otg" and the live re-init skipped, the controller still died 14s after carrier-on under load (carrier 54.1s, first Tx timeout 63.4s, HC died 68.4s). That means either the OTG event path is not the trigger, or the harm comes from somewhere else in it. dr_mode="host" removes far more than this one call: it also skips dwc3_otgregs_init() and hands port-power control to xHCI instead of the OTG block. IRQ 47 is shared between dwc3-otg and xhci-hcd, so /proc/interrupts cannot say whether OTG events fire at all. CONFIG_DYNAMIC_DEBUG is off, so dev_dbg is compiled out and invisible. So this build logs, ratelimited and always compiled: - every OTG hardware event with its OEVT value, role and restart flag - each time the guard skips a re-init on a live host Run under load, that answers whether the OTG path is even active during the failure window, instead of guessing a third patch. Also worth measuring: load accelerates this dramatically -- 452-587s idle versus 9-14s under line-rate traffic -- so idle soaks are not comparable to load tests and earlier comparisons need re-reading with that in mind. Refs flare-edge#160 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MVGTC78dgCGfRANNKjoPea |
||
|
|
9ff60d60db |
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 |