Hi @WayneWWW,
Thanks for the reply. I’d like to push back a bit on the interference/layout angle before we go spend time on a bus analyzer or compliance test, because I don’t think the evidence fits that theory.
Why we don’t think this is signal interference or a layout issue
The same physical port, same cable, same device, same board – successfully trains to SuperSpeedPlus the first time, every time. It only degrades after a specific software sequence (detach → OTG role switch to “none” → switch back to “host”). If this were EMI (2.4G sideband) or a layout/signal-integrity problem, I’d expect it to affect the first connection too, or at least be intermittent/statistical regardless of history. What we see instead is fully deterministic and tied to a specific event sequence, not to physical conditions – nothing on the board or in the environment changes between “before” and “after” that sequence. So I don’t think a compliance test or removing the 2.4G radios would tell us anything new here; the hardware is demonstrably capable of full SuperSpeedPlus link training when the software/firmware state hasn’t been perturbed by that sequence.
Given that, we’ve been focusing on the software/firmware side, and think we’ve narrowed the trigger condition down quite a bit:
Refined repro condition
The port isn’t stuck at USB2 in general – it only gets stuck after this specific sequence:
- Attach a SuperSpeedPlus (Gen2, 10Gbps) device to the OTG port while it’s in host mode – enumerates fine at SuperSpeedPlus.
- Detach it.
- Switch the OTG port’s role to “none” and back to “host” (through the normal ID/role-switch path, tegra_xhci_id_work()).
- Attach any SuperSpeed-class device (Gen1 or Gen2) to the same port.
After step 3, the port can no longer train to SuperSpeed at all (Gen1 or Gen2) – it falls back to USB2-only. USB2 devices on that same port still enumerate fine, so the port/pad itself isn’t dead.
Importantly: we confirmed this only happens if a Gen2 device was previously attached. If we only ever attach Gen1 SuperSpeed devices, the same host → none → host role-switch cycle does not reproduce the problem. That’s a second piece of evidence against interference/layout: EMI or a marginal signal path wouldn’t care whether a previous device happened to negotiate Gen1 vs Gen2 – it would affect the current connection attempt based on physical conditions at that moment, not on history. The fact that it’s gated entirely on “was a Gen2 device attached before” tells us this is state that gets created during Gen2 link training and isn’t being cleared, not a physical-layer problem.
What we’ve already tried (neither helped)
- Power-cycling the OTG port’s usb2 and usb3 phys (phy_power_off → phy_exit → phy_init → phy_power_on) when the role switches away from host, before switching back. This only touches the padctl analog macro power domain (XUSB_PADCTL_ELPG_PROGRAM_1 SSPX_ELPG_* bits) – no effect.
- A warm (BH) port reset (SetPortFeature USB_PORT_FEAT_BH_PORT_RESET) on the SS port after re-powering it on the way back into host mode, to force the xHCI-side LTSSM to retrain from scratch – no effect either.
Since neither the padctl/PHY register layer nor the xHCI-controller port-reset layer clears the problem, our current best guess is that some Gen2-specific link-training state (e.g. DFE/CTLE receiver-equalization context) is cached inside the XUSB falcon firmware for that port, and isn’t invalidated by either of the above.
Two things in the driver support this guess:
MBOX_CMD_SAVE_DFE_CTLE_CTX is a firmware->driver mailbox message for saving a port’s DFE/CTLE context after training, but the driver-side usb3_save_context padctl op is only implemented for Tegra124 – on T186/T194/T234/T264 it’s left NULL (tegra_xusb_padctl_usb3_save_context() just returns -ENOSYS). So on T264 this context looks fully firmware/hardware-owned and invisible to the kernel driver.
- The existing
otg_reset_sspi path (sends MBOX_CMD_RESET_SSPI to ask firmware to reset the SS Port Interface when re-entering host mode) doesn’t appear to be serviced by T264 firmware – the mailbox call just times out unanswered, which is why it’s currently disabled for T264 (otg_reset_sspi = false).
What we’d like to avoid
We know a full firmware reload (cycling the whole controller through our ELPG suspend/resume path, which calls xhci_suspend/xhci_resume and reloads firmware) would very likely clear this. But that’s controller-wide – it would disrupt every other USB device on both root hubs, not just the OTG port – so it’s not something we want to do on every OTG role switch.
Questions
- Is this a known limitation on T264 – per-port Gen2 link-training/DFE-CTLE state cached in firmware that survives both a port power-cycle and a warm (BH) port reset?
- Is there any other mailbox command, padctl register, or firmware API that can make firmware discard/reinitialize a single port’s Gen2 training state, short of a full controller suspend/resume + firmware reload?
- If full firmware reload is really the only way today, is there any way to scope it to a single port?
We’re happy to pull dmesg / PORTSC dumps before and after the failure, or a bus trace if you still think that’s needed once you’ve seen the above – just let us know what would be most useful given this more specific trigger condition.
Thanks again for taking the time to look into this.