Commit Graph
1 Commits
Author SHA1 Message Date
NoahandClaude Opus 5 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
2026-09-23 00:25:15 -06:00