Re: [PATCH 2/2] usb: dwc3: Notify XHCI core of tunneled status

From: Sven Peter

Date: Sun May 17 2026 - 09:31:51 EST


cc: asahi@xxxxxxxxxxxxxxx

On 12.05.26 13:56, Konrad Dybcio wrote:
On 5/11/26 8:44 PM, Sven Peter wrote:
Hi,

On 11.05.26 11:06, Konrad Dybcio wrote:
On 5/9/26 1:31 AM, Thinh Nguyen wrote:
On Fri, May 08, 2026, Konrad Dybcio wrote:
On 5/8/26 12:46 AM, Thinh Nguyen wrote:
On Thu, May 07, 2026, Jack Pham wrote:
On Thu, May 07, 2026 at 12:34:50PM +0200, Konrad Dybcio wrote:
On 5/7/26 1:40 AM, Thinh Nguyen wrote:
On Tue, May 05, 2026, Konrad Dybcio wrote:
From: Konrad Dybcio <konrad.dybcio@xxxxxxxxxxxxxxxx>

[...]

4. drivers/phy/qualcomm/phy-qcom-qmp-combo.c reprograms the PHY based
    on typec_mux events in native cases, or to USB4/TBT mode if the router
    driver requests it [that last part is not yet upstream]
5. [optionally] retimer drivers in between (most often Parade PS883x
    series via drivers/usb/typec/mux/ps883x.c), which act as an
    additional typec_mux/switch in the chain
6. [not upstream yet] USB4 router driver consumes some typec_mux
    parameters (orientation, cable and partner capabilities) and sends a
    command to another MCU to high-speed link establishment. It also sets
    the aforementioned magic register.

I'm not sure if mux is the correct framework here. On Apple Silicon we also need an out-of-band notification from the PD controller to the USB4 NHI here but the NHI isn't a mux in the typec sense. And how do you ensure that 4 happens before 6 if you use the typec mux framework or does that not matter for your platform?

Some mux provider drivers (e.g. the PS883x one) call typec_mux_get/set()
explicitly, and combining that with the right of_graph things end up
working naturally (perhaps to some degree by luck?).

It's luck, it was the wrong order for Apple Silicon :-)
I just don't think it's appropriate to describe the native host interface as a mux (at least on Apple Silicon) since the mux is inside the Type-C PHY even though the mux framework happens to transport almost all the correct information.
I also don't think it makes sense to describe that thing as a mux inside the device tree.


With the QMPPHY specifically, it already exposes a separate struct phy
for the USB3 sub-block and another one for DP. I added a third one for
USB4, which when activated, programs it into USB4 mode and de facto
blocks the driver from acting as a typec_mux (simply via an early return
from mux_set) for the duration of the USB4PHY being in use (the router
takes care of lane assignment and orientation, when active).

Making this predictable (and controllable from the router driver) was
paramount, as the device will crash if the router is attempted to be
brought online at the wrong time, since most of its clocks are derived
from the on-PHY PLL. Similarly, the suspend flows also require tight
control of the PHY's power state.

Yeah, that's similar to Apple Silicon except that we drive all the synchronization from the Type-C PD controller because we have even more components that all have to kept in sync on those platforms.



I ended up modeling the router as a mux&switch, since like I mentioned,
it needs a number of parameters that already come via these frameworks
(cable type/speed, orientation, etc.) and the drivers needs to talk to
the MCU immediately after the PHY and retimer are set up, so that only
made sense.

Currently I use [1] and [2] in my work-in-progress tree though I'm not quite happy with it yet.

I took a look at your branch and predictably we faced some common
obstacles.. Although I'm jealous of RTKIT..

At a glance, 2. seems like a reasonably fitting place to set it, however:
* it does not have any sort of a handle to the typec_connector (it
only acts like a mux that sets another mux), and
* it may be going away in the future

so I'd much prefer to keep this logic somewhere near where this iteration
of the patch does - I think it'll be useful for more implementations, as
I'd imagine it'd be fairly commonplace to hardwire CIOCTRL_CIO_EN and
another part of the pipeline that must logically be online for USB4 to
be useful

+Sven, on ASi, is CIOCTRL_CIO_EN (dwc3base + (0xcd20 + ((port) * 0x30))
written to manually?

I don't think so, but we need a manual out-of-band notification to both PCIe, tunneled DisplayPort and USB3 once the tunnel has been brought upside the NHI (i.e. long after the typec altmode is entered) and all this has to be represented in the device tree as well.

The DP situation in our impl is.. colorful as well.
Generally I think we only need to reset the protocol adapters, which
happen to live within the router's address space, making it contained
within the TBT driver.

Yeah, for us it's separate and there's the display co-processor which has to get an OOB notification for any established USB4/TBT tunnel.
It's a big mess essentially.

DWC3 on Apple platforms is very cursed and has to be fully offline while the Type-C switches modes and must only be brought up then once the tunnel inside the NHI has already been established.

My current WIP code uses a tbt_oob_notify for all that that I introduced and something like this in the dt to represent the connections:

/* USB4 */
&usb4_1_acio {
    ports {
        #address-cells = <1>;
        #size-cells = <0>;

        /* 1: USB4 port */
        port@1 {
            reg = <1>;
            usb4_1_acio_tbt: endpoint {
                remote-endpoint = <&typec1_con_tbt>;
            };
        };

        /* 2: unused(?) USB4 port */

Any chance port2 is used for lane bonding?

None, port 2 isn't connected to anything. That (?) is just a leftover. The host router block can probably support two ports with shared bandwidth but Apple instead decided to create a separate host router for each port.


        /* 3: PCIe, TBD */
        /* 4: USB3, TBD */
        /* 5: DP0, TBD */
        /* 6: DP1, TBD */
    };
};

Still not completely happy with that as well.
Does PCIe tunneling also need additional OOB notifications for you?

No, simply enabling the path works, after which we get the normal hotplug
events

Lucky you! We need to enable the PCIe tunnels inside the pcie driver *after* the tunnel is up inside the NHI because otherwise the co-processor firmware will crash. There's also some weirdness going on with PCIe hotplug events below the root bridge that I have yet to figure out. From what I can tell XNU manually injects these events whenever the TBT tunnel changes. We can see that weirdness with long chains like a USB4 hub -> Another TBT dock -> Apple's TBT display -> TBT-to-ethernet adapter.
I've added generic out-of-bound notifications for that but not quite happy with it yet.


Please also cc me on any thunderbolt submissions. If your work goes in first I can just rebase mine on top of your tree and see if we need anything more.


Sven