Skip to content

J716c: USB4 and Thunderbolt 5 on integration - #1323

Merged
DjDeveloperr merged 43 commits into
integration/aurorafrom
mergetrain/j716c-usb4-20261109
Nov 9, 2026
Merged

DjDeveloperr merged 43 commits into
integration/aurorafrom
mergetrain/j716c-usb4-20261109

Conversation

@RyanTheTide

Copy link
Copy Markdown
Contributor

The J716c (MacBook Pro 16-inch, M5 Max, T6050) right-hand USB-C port as a USB4/Thunderbolt 5 host on integration/aurora: the T6050 combo PHY's CIO mode, the host router, its DART, the PCIe-C tunnel and USB3 over the tunnel, plus the SN201202x dock start-up fixes. This is #946 and the parts of the #1268-#1273 stack that stand on their own, rebased onto integration/aurora with every commit's author and Signed-off-by kept.

What it carries

Source Commits here Notes
#937 (PCIe T6050 adopt/train) 653f5375f..704b66f70 (7) prerequisite of #946's PCIe-C tunnel commit; authors Jeff Queisser, Dj, Erik Bourget
#939 (T6050 combo PHY, DWC3) d1ccc5868..931f247be (8) prerequisite: #946 builds the CIO mode on this PHY; authors Dj, Jeff Queisser
#946 (Thunderbolt 5/USB4, right port) 03a047554..ec5485290 (18) all 18 commits
#1269 (crossbar busy status) 5a99d43ab
#1272 (tipd dock start-up) 0c906ebf6, b4d99c2f6, a442efa44, 0471ea2c5 all 4 commits
test models 498e9b1a3, 371a42c0d host-test model updates for the new calls, see below

Commit list (original commit in brackets):

653f5375f Jeff Queisser  dt-bindings: pci: apple,pcie: Add T6050 hosts adopted from the boot loader   [#937 95e9c52a0875]
ca6cac821 Jeff Queisser  PCI: apple: Adopt T6050 root ports the boot loader trained                     [#937 500d01601720]
3bb54de2d Dj             PCI: Add pci_trylock_rescan_remove()                                           [#937 9522dc08d514]
d3ea57796 Erik Bourget   PCI: apple: Add the T6050 port reset, enable, configure and train sequences    [#937 847c7493f870]
554bacbb3 Jeff Queisser  PCI: apple: Adopt T6050 port 0's multi-function N1 and reset its root port    [#937 b8c92012dfe3]
5843113c5 Jeff Queisser  PCI: apple: Drive T6050 port 0's PERST# through the pin controller             [#937 2b7607519da9]
704b66f70 Erik Bourget   selftests: apple: Preserve multiline PCIe macros in policy tests               [#937 911da8c5008a]
d1ccc5868 Dj             phy: apple: Add T6050 DisplayPort and USB combo PHY                            [#939 a154cc3a0bd0] (R1)
ad990d38d Jeff Queisser  dt-bindings: phy: apple,t6050-dp-phy: Allow the USB domain on combo PHYs      [#939 3feea85f677d]
42b87ef7d Jeff Queisser  phy: apple: t6050: Hold the USB domain for the combo PHY's USB2 banks          [#939 baa4a4a832dd]
4ef9db44d Jeff Queisser  dt-bindings: phy: apple,t6050-dp-phy: Type apple,port-index as uint32         [#939 b3c77b8e3b4d]
8b23bd4d3 Jeff Queisser  usb: dwc3: apple: Add the bare-metal T6050 controller                          [#939 f8e75aea4ed0] (R2)
498e9b1a3 Ryan Murray    selftests: apple: Model phy_reset() in the dwc3-apple host tests               [new]
44826a01e Jeff Queisser  phy: apple: t6050: Enable the dummy PIPE PHY for the USB3 lock                 [#939 4a4bde29621d]
4e780eb1f Jeff Queisser  phy: apple: t6050: Stay on USB2 when the PIPE never left the dummy             [#939 be7daa4a6cfa]
931f247be Jeff Queisser  phy: apple: t6050: Accept every Type-C port but the debug port                 [#939 c24e75ca9d99]
03a047554 Erik Bourget   arm64: dts: apple: j716c: Add the right USB-C port (ATC2)                      [#946 ea01625cf551] (R3)
a1d086a6f Erik Bourget   phy: apple: t6050: carry on when the PIPE lock is not acknowledged             [#946 e604b7f414b4]
1856c663b Erik Bourget   reset: apple-cio: add the T6050 CIO reconfig request                           [#946 35c0d1d6bc96]
4aee080f4 Erik Bourget   dt-bindings: phy: apple,t6050-dp-phy: Add the CIO tunables                     [#946 40429ec5df2a]
8726ee82d Erik Bourget   phy: apple: t6050: add the CIO (USB4/Thunderbolt) mode                         [#946 97c3364d9650]
024beea2b Erik Bourget   dt-bindings: thunderbolt: apple: Add the T6050 router and NHI                  [#946 9db0872d2d39] (R4)
ea31a9a60 Erik Bourget   thunderbolt: apple: support the T6050 host routers                             [#946 7321eb31ef8e] (R5)
ab6654bbb Erik Bourget   dt-bindings: iommu: apple,dart: Describe the setup of a DART nothing initialises [#946 59f66285f697]
331043fa7 Erik Bourget   iommu: apple-dart: set up a T8110 DART that nothing initialised                [#946 b9fcf33fd803] (R6)
1102e101f Erik Bourget   arm64: dts: apple: j716c: Add the right port's USB4/Thunderbolt router         [#946 e54fb2cae14f] (R3)
a0b688d65 Erik Bourget   phy: apple: t6050: park the PIPE from a dummy producer on any clock            [#946 6f93def30893]
178e80cc2 Erik Bourget   thunderbolt: Ignore DROM entries for ports the router does not implement      [#946 1778241e18c7]
a1d6ef971 Erik Bourget   phy: apple: t6050: restart the PHY firmware on its original data              [#946 b8f2be0fef93]
625229d38 Erik Bourget   phy: apple: t6050: carry USB3 over a USB4 router's tunnel                      [#946 141ffcf90ac4]
37f3967b6 Erik Bourget   dt-bindings: pci: apple,pcie: Add the T6050 PCIe-C tunnel                      [#946 1052463981f6] (R7)
b1ffa4722 Erik Bourget   PCI: apple: support the T6050 PCIe-C tunnel                                    [#946 dafbb4d57e25] (R8)
79432bf93 Erik Bourget   thunderbolt: apple: recognize the T6050 PCIe-C host                            [#946 b5e07386e510] (R9)
ec5485290 Erik Bourget   arm64: dts: apple: j716c: Add the right port's PCIe tunnel                     [#946 31785b39df6b] (R3)
5a99d43ab Erik Bourget   mux: apple-display-crossbar: Accept busy T6050 connection status               [#1269 c7c09ce82ba7]
0c906ebf6 Erik Bourget   usb: typec: tipd: Restore J716c dock USB across boot                           [#1272 31e53177818e]
371a42c0d Ryan Murray    selftests: apple-dcp: Stub the inherited-connection reset in the tipd probe test [new]
b4d99c2f6 Erik Bourget   usb: typec: tipd: Wait for the inherited disconnect task                       [#1272 dd3b8c894c0c]
a442efa44 Erik Bourget   usb: typec: tipd: preserve dry result during LDCM startup                      [#1272 435d8fe464b7]
0471ea2c5 Erik Bourget   usb: typec: tipd: disable LDCM work during removal                             [#1272 f2010ce45dc8]

Not carried yet

These four can go on top of this branch once #1012 and #1276 are in.

Conflict resolutions

  • R1 d1ccc5868 crossbar: kept integration's T8132 profile and added .n_mux = MUX_MAX to it. This commit adds the n_mux field, and T8132 needs the same value as before. Added the T8142/T6050 profile beside it. apple,display-crossbar.yaml keeps integration's oneOf (which already lists apple,t6050-display-crossbar with the t8142 fallback) and its 1-2 power-domains.
  • R2 8b23bd4d3 apple,dwc3.yaml: kept integration's status-gated resets/usb-role-switch rule and added the T6050 register-count rule as a separate if. The apple,tunable-cio description and the new apple,tunable-bulk/apple,tunable-ausbc properties come from the commit.
  • R3 03a047554, 1102e101f, ec5485290 J716c DT. In integration, t6050-max.dtsi is shared by J714c, J714s, J716s, J775c, J873s and K114/K116, and t6050-j716c.dts includes the disabled t6050-peripherals.dtsi inventory.
    • #define T6050_J716C moves above #include "t6050-max.dtsi" in t6050-j716c.dts. Everything these commits add to t6050-max.dtsi (nub-spmi-a1, the ATC2 router, its domains and LSTX pins, the PCIe tunnel) is wrapped in #ifdef T6050_J716C. The other T6050 trees therefore build exactly as before, and the added content is unchanged.
    • The ATC2 port block goes after the inventory include.
    • In t6050-peripherals.dtsi, J716C is dropped from the guard of the inventory's nub_spmi_a1, and the inventory's apciec2_dart is skipped for J716C (#ifndef T6050_J716C). On J716c both nodes now come from t6050-max.dtsi. This is the same pattern J716S uses.
  • R4 024beea2b Thunderbolt bindings:
    • Kept integration's T6040 compatibles and its three-register T6050 router variant, which the ATC3 inventory node uses. The five-register T6050 router is added beside it: router-ap-fw is appended to the reg-names list, and the five reg descriptions come from the commit.
    • Kept integration's T6040 sram-parity interrupt.
    • The reg minItems 4 rule stays T6040/T8132. T6050 joins the LSTX rule and gets its own rule (four power domains; apple,tunable-router-ap-fw only on T6050).
    • The NHI binding keeps integration's compatible lists, which already include t6040 and t6050.
  • R5 ea31a9a60 thunderbolt/apple.c: integration detected Type 7 by compatible (t8142, t6040). The commit moves type7 into apple_cio_hw, so apple_cio_t6040_hw gets .type7 = true, which keeps T6040 behaviour the same. The NHI generation check lists t8142, t6040 and t6050, and both match tables keep their T6040 and T6050 entries.
  • R6 331043fa7 apple-dart.c: kept integration's apple_dart_has_live_rtkit_client() and the new apple_dart_parse_own_setup(). They are two adjacent new functions.
  • R7 37f3967b6 apple,pcie.yaml: the PCIe-C enum lists both apple,t6040-pciec and apple,t6050-pciec.
  • R8 b1ffa4722 pcie-apple.c: hw_info keeps integration's s2idle_hold_domain/refbuf_off/pipe_rst beside the new no_port_reset/mps_timers, and the match table has both t6040-pciec and t6050-pciec.
  • R9 79432bf93 thunderbolt/apple.c: integration already factors the PCIe-C host test as apple_cio_pciec_eligible() (with t6040). apple,t6050-pciec is added there instead of adding a second apple_cio_is_pciec().

Two small model updates keep the host tests building with the carried code. Without them the dwc3-teardown and probe-transaction-rollback models and neo-stability-tests fail to compile on this series:

  • 498e9b1a3: dwc3_apple_init() now calls phy_reset() on the USB2 PHY. Only the T6050 combo PHY has a .reset, so this is a no-op everywhere else. The dwc3 model extracts the real phy_reset() and gets a reset member in its phy_ops. neo-stability-tests gets a no-op phy_reset() and the usb2_phy member.
  • 371a42c0d: the tipd probe model stubs cd321x_reset_inherited_connection(). With the model's device_property_read_bool() returning false, the real function returns 0 straight away.

Checks

  • Host tests (tools/asahi/run-host-tests.py): 199/199 passed on this branch, and 199/199 on integration/aurora at 8f48481.
  • tools/testing/selftests/drivers/gpu/apple-dcp/run.py: 87/87 ok on this branch and 87/87 on integration/aurora.
  • DTBs: all 137 Apple DTBs were built from a defconfig + asahi.config tree on both sides. Only t6050-j716c.dtb differs. t6040, t8140, t6000 and t8112 are byte-identical, and so are the other T6050 boards (J714c/s, J716s, J775c/d, J873s, K114/K116).
  • arm64 object builds, with no warnings, of drivers/thunderbolt/, drivers/usb/typec/tipd/, atc-t6050-dp.o, apple-display-crossbar.o, apple-dart.o, pcie-apple.o, reset-apple-cio.o, dwc3-apple.o and pci/probe.o.
  • Not run here: a full kernel build, KUnit and dtbs_check.

Shared code that also runs on other SoCs:

  • drivers/mux/apple-display-crossbar.c: n_mux per profile, plus the T8142/T6050 ops.
  • drivers/thunderbolt/apple.c: type7 now comes from the hardware profile, and T6040 keeps it.
  • drivers/thunderbolt/eeprom.c: a DROM entry for a port the router does not implement is skipped.
  • drivers/usb/dwc3/dwc3-apple.c: the tunables array grows, and phy_reset() runs on the USB2 PHY after host init (a no-op without a .reset).
  • drivers/usb/typec/tipd/core.c and spmi.c: SN201202x, which J616s and J700 also use. SSPS S0 and LDCM get a 5 s timeout, a dry LDCM result is kept while a new measurement starts, and the detector work is disabled on removal. The inherited-connection reset only runs on apple,j716c with apple,reset-inherited-usb2-connection.
  • drivers/pci/probe.c / include/linux/pci.h: new pci_trylock_rescan_remove().

All other new paths are gated: drivers/iommu/apple-dart.c on its new DT properties, drivers/pci/controller/pcie-apple.c and drivers/reset/reset-apple-cio.c on T6050 compatibles, and drivers/phy/apple/atc-t6050-* is a new driver.

Hardware results from the original PRs

From #946 (J716c, CalDigit TS5), on that series:

On this code: the TS5 links at 40 Gb/s ×2 at boot, USB3 runs at 10 Gb/s through the dock, and its I225 Ethernet comes up at 2.5 GbE over the PCIe tunnel.

Earlier builds of the same series, the same apart from comments and removed bring-up switches, also passed hotplug, unplug/replug, bolt authorization (IOMMU policy) and a USB-C hub in DisplayPort alternate mode.

From #1272 (complete #1268-#1273 stack on its prerequisite base):

The final build also booted with the dock attached to rear-left: the user confirmed the internal screen, both external monitors, keyboard/mouse and Ethernet worked without unplugging or manual recovery.

From #937 and #939, on the full T6050 platform stack:

the machine boots to userspace from NVMe with all 18 CPUs, N1 Wi-Fi up and CPU frequency scaling active.

This rebased branch has not been booted.

funkzy and others added 30 commits November 9, 2026 22:39
…oader

Add apple,t6050-pcie, a T6050 (M5 Ultra) PCIe host whose root ports the
boot loader powered, trained and left running. Each such port is marked
apple,link-trained-by-bootloader and named by its port<n>, phy<n> and
intr2axi<n> register ranges and its port<n> interrupt; the host's local
MSI map starts at apple,msi-vector-base. The OS adopts the trained state
and never resets, retrains or changes the port's power: reset-gpios,
pwren-gpios and power-domains are not allowed on it.

Also name nonposted-mmio, which these hosts take.

Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>

Developed with Claude Code.
Signed-off-by: Erik Bourget <erik@ebourget.net>
On the M5 Ultra (T6050, J775D) running Linux on the hardware, the boot
loader brings up the internal PCIe host's ports itself: it powers the
shared PHY, trains each port's link and leaves it up, with the endpoint
idle. Port 2 carries the onboard RTL8127 Ethernet controller and port 3
the ASM3142 xHCI behind the rear USB-A ports. Repeating the reset and
training sequence from Linux is not needed and not known to be safe on
this SoC.

Add apple,t6050-pcie, a host whose root ports marked
apple,link-trained-by-bootloader are adopted as they are: no reset, no
tunable replay, no clock or power change and no retraining. Every
resource comes from the DT by the port's index (port<n>, phy<n>,
intr2axi<n>, interrupt port<n>, the iommu-map entry for the endpoint's
RID). Before the first PCIe register write the driver checks that the
link is up and not retraining at Gen3 x1, that the boot loader numbered
the root port's buses (primary 0, one secondary), that the endpoint on
it is device 0 function 0 with DMA and MSI off and a well-formed
capability list, and that the port's RID table holds no valid entry. A
port that fails a check is left alone and the others are still adopted.
Then the endpoint's RID is routed to its SID, the host's MSI map is set
up from apple,msi-vector-base, and only the adopted root ports and the
endpoint behind each are enumerated. The links cannot be recovered by a
user reset or removal, since the boot loader owns them.

No other SoC sets adopt_link.

Testing: on J775D, every boot adopts port 2 (RTL8127, DHCP and SSH over
it on every boot) and port 3 (ASM3142, a USB keyboard and mouse driving
the desktop session).

Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>

Developed with Claude Code.
Signed-off-by: Erik Bourget <erik@ebourget.net>
A driver's work item that rescans or resets part of a bus takes the
rescan/remove lock. When the device is being removed, the remover holds
that lock while it cancels the work, so a worker that blocks on the lock
deadlocks against its own cancellation.

Add pci_trylock_rescan_remove(), which takes the rescan/remove lock only
if it is free. A worker that cannot get it returns and retries later,
and the remover's cancellation completes.

Signed-off-by: Dj <hey@djdev.me>

Developed with Claude Code.
Signed-off-by: Erik Bourget <erik@ebourget.net>
…uences

An adopted T6050 port is left as the boot loader trained it, but an
endpoint that has to come back on a retrained link (the N1 on port 0,
after its ROM boot) needs the host to take the port down and bring it
up again. Add the sequences for that, on the port's own register blocks
(port<n>, phy<n> and intr2axi<n>) and the root port's config space:

- Disable: wait for the link to leave L1 and go idle, assert the
  port's PERST# through its handshake, then stop the lane clocks and
  the port's application clock.
- Enable: the port reset table, which also clears the RID2SID and MSI
  map tables, the port's tunables, then the application clock and the
  PERST# release handshake, until the port is ready and its link idle.
- Configure: with the root complex's read-only registers opened, its
  tunables and the per-rate shadow tunables, then a Gen3 x1 link
  (target speed, link width and capability) and a directed speed
  change; Intr2AXI enabled.
- Train: the lane's reference clocks, the endpoint's PERST# released
  through its pad, the LTSSM started, then the 100 ms wait after link
  up.

The tunables are the lists the boot loader copies onto the root port
node, groups of (offset, 4, mask, value); a list it did not copy is
skipped. The root-port reset that runs the sequences follows; until
then they are marked __maybe_unused.


Co-developed-by: Dj <hey@djdev.me>
Signed-off-by: Dj <hey@djdev.me>

Developed with Claude Code.
Signed-off-by: Erik Bourget <erik@ebourget.net>
… port

The N1 Wi-Fi/Bluetooth sits on apcie0 port 0, which the bootloader now
trains like ports 2 and 3. Two things the adoption assumed do not hold
for it:

- It is multi-function. In its ROM personality only function 0 answers;
  after its firmware starts, Control, Alpha and Beta (0-2) do. Each
  function's RID is routed to its own DART stream from the iommu-map
  (slot == SID) when it is enabled, instead of the one route set at
  adoption; config accesses on the adopted bus reach every function of
  device 0; teardown clears every slot it routed. Port 0 is accepted.
- Its ROM links at Gen1. Adoption takes an x1 link at Gen1 to Gen3 (the
  bootloader's target), not only Gen3; the endpoint picks the speed.
- After the ROM boot it must come back on a retrained link. The host
  provides reset_root_port for an adopted port that names its PERST# pad
  (perst<n>): PME_Turn_Off to L2, PERST#, then the T6050 port disable,
  reset, enable, configure and train sequences, with the routes and the
  MSI map rewritten in between. Tunables the boot loader copied onto the
  root port node are applied; absent ones are skipped.

Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>

Developed with Claude Code.
Signed-off-by: Erik Bourget <erik@ebourget.net>
The adopted port 0 named its PERST# pad as a 4-byte reg of the host
(perst0, one word inside the pin controller's block) and wrote it raw.
With the pin controller in the device tree the two nodes claim the same
MMIO word, and whichever probes second fails: when pinctrl wins, the
host's port setup stops with -EBUSY and no port is adopted, so the
Ethernet and USB behind ports 2 and 3 are gone with it.

One owner for the pad: the pin controller. The port node carries
reset-gpios, as every other Apple host's port nodes do, and the host
takes the line as the bootloader left it (GPIOD_ASIS: probe never
touches a trained link's reset) and drives it only in the root-port
reset, as a GPIO output at the logical level the reset asks for. The
raw pad write set the same things (function bits cleared, mode output,
the data bit) plus the pad's input enable, which the pin controller
leaves as the bootloader set it. Probe asks for the line before any
port is set up, so a host whose pin controller is not up yet defers
instead of adopting nothing; an adopted port without reset-gpios has no
reset, as before (reset_root_port answers -EOPNOTSUPP). The perst<n>
reg, its binding entry and the raw pad write go; the binding allows
reset-gpios on an adopted port again.

Probe names the port whose line it cannot have, as the probe of the
other hosts' ports does: a line the pin controller reserves
(gpio-reserved-ranges) is refused with -EINVAL, which the host's probe
otherwise returned with no word of which port or property.

Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>

Developed with Claude Code.
Signed-off-by: Erik Bourget <erik@ebourget.net>
The PCIe-C policy test extracts driver macro definitions to compile its
production-code fixture. Taking only the first physical line truncates
T6050_ROUTE and joins the following directive to its trailing backslash,
so the fixture fails to compile after the T6050 host support is added.

Copy continuation lines along with their define. The production fixture
and both negative controls now pass.

Developed with Claude Code.
Signed-off-by: Erik Bourget <erik@ebourget.net>
Add the T6050 DisplayPort/USB combo PHY and four-clock display crossbar profile. Carry the current Type-C, USB2, USB3 PIPE sequencing, and teardown behavior from the published T6050 source.

Source snapshot: t6050-gpu ee68abb105200e99636a5edfc647a5cfc1d89954
Integration base: 9cbdd07

Signed-off-by: Dj <hey@djdev.me>
Signed-off-by: Erik Bourget <erik@ebourget.net>
The combo PHY's USB2 PHY and digital banks are in the USB controller's
power domain. Let a combo PHY name it fourth, as "usb", so the PHY can
hold it on a port with no USB controller bound. The fixed DisplayPort
PHY keeps exactly three domains.

Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Erik Bourget <erik@ebourget.net>
The combo PHY's USB2 PHY and digital banks are in the USB controller's
power domain. Only a bound USB controller holds that domain, so on a
port with no controller (a DisplayPort-only port, as on J775D with Linux
on the hardware) the first Type-C mode switch wrote an unclocked bank
and took an SError.

A combo PHY node may now name that domain as a fourth power domain,
"usb". When it does, the PHY holds it from combo start to combo stop,
after the common domain on the way up and before it on the way down.
Nodes that name three domains probe and run exactly as before, and the
fixed DisplayPort PHY still takes exactly three.

Testing: on J775D (M5 Ultra), with the fourth domain named on each combo
port, Type-C mode switches and DisplayPort on the rear ports ran without
an SError on every boot since 2026-10-30.

Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Erik Bourget <erik@ebourget.net>
apple,port-index has an enum but no type, so dtschema cannot decode the
cell and every combo PHY node fails with "b'\x00\x00\x00\x02' is not one
of [1, 2]" and a core-schema type error, whatever port it names. Give it
the uint32 type.

Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Erik Bourget <erik@ebourget.net>
Add apple,t6050-dwc3, the T6050 (J775D) DWC3 on bare metal. It uses the
shared tunable table, extended with the tables Diljit Singh's T6050 USB
support applies, in the same order: the T6040 tables plus CIO (the Apple
region from 0xcd20, after the fixed CIO values), bulk fabric and AUSBC
debug (the optional drd-bulk and drd-ausbc regions).

It is host-only, like T6040. The USB2 port reset follows the root hub's
start (a no-op without a PHY reset op), and a USB3 PHY failure is fatal,
as on T8132.

Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Erik Bourget <erik@ebourget.net>
dwc3_apple_init() now resets the USB2 port through phy_reset() once the
host is up. The dwc3 teardown model extracts dwc3_apple_init() and the
PHY core functions it calls, so it needs phy_reset() and a reset member
in its phy_ops; none of its PHYs has one, as on every SoC before T6050.

neo-stability-tests also builds dwc3_apple_init() against a reduced
dwc3_apple. Give it the usb2_phy member and a no-op phy_reset().

Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
The PIPE lock that starts the USB3 handoff is acknowledged only while
the combo PHY's dummy PIPE PHY is enabled (pipehandler
NONSELECTED_OVERRIDE bit 15), as on the earlier ATC PHYs (T8122,
T8140), and the bit resets to clear with the controller's domain.
Without it the lock timed out, the PHY faulted and rear port 2 had no
USB at all.

Enable it before the controller leaves reset. Each PIPE-switch step
returns its own code, and every fault of the PHY is logged with its
reason (the pipehandler registers for PIPE faults); dwc3-apple logs a
fatal USB3 PHY failure.

Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Erik Bourget <erik@ebourget.net>
A USB3 PIPE switch can fail before the mux moves, with the PIPE still on
its dummy producer. The PHY then faulted and lost USB2 with it.

Leave the port on USB2 in that case, as four DisplayPort lanes do.

Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Erik Bourget <erik@ebourget.net>
The combo PHY refused any apple,port-index other than 1 or 2, the two ports a
guest description produced. Nothing else in the driver reads the index: the
firmware buffer, banks and sequences are port-agnostic. Validate it against
the board's port set instead: 1 to 5, with 0 (the port that carries the boot
loader's debug link) still excluded, here and in the binding.

Signed-off-by: Jeff Queisser <funkzy@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Erik Bourget <erik@ebourget.net>
Describe the right-hand USB-C port: its SN201202x PD controller, the
ATC2 combo PHY, usb-drd2 and its DARTs, and their power domains.

On J716c the port's HPM is ADT /arm-io/nub-spmi-a1/hpm2 (SPMI address
0xc, port-location "right"), on its own SPMI bus, not on nub-spmi-a0
at address 8 as on J775D; on J716c that address is hpm5 (port-type
0x11), not a port's controller. Add nub-spmi-a1 to t6050-max.dtsi,
polled as nub-spmi-a0 is, put the HPM on it, and enable the ATC2 chain
that J775D leaves to the display handoff.

Add the atcphy2 alias so that the boot loader fills the PHY's tunables
from /arm-io/atc-phy2, and turn dcpext0's Type-C route off with
dcpext0: a mode-switch endpoint whose mux is never registered would
defer the PD controller forever.

Developed with Claude Code.

Signed-off-by: Erik Bourget <erik@ebourget.net>
On J716c the PIPE handler does not acknowledge the lock request when
the controller switches from the dummy producer to USB3, even with the
dummy PHY behind the mux enabled. The switch then fails and the port
falls back to USB2.

The switch works without the ACK. Wait 6 ms for the lock ACK, and for
an unlock ACK to clear, then log the PIPE handler state if it is
missing and complete the switch. With this, USB3 comes up on that
port.

Developed with Claude Code.

Signed-off-by: Erik Bourget <erik@ebourget.net>
On T6050 the CPU wrapper of each USB4 router is clocked through the CIO
reconfig request (the router CPU's clock gate CIOn_RECONFIG_V). Until it
has run, any access to the wrapper faults.

The request register uses the T8142 bit positions (request BIT(2 * port),
four ports) and sits at offset 0x60 of a page in the die's PMGR block,
0x280620000 on die 0. Its odd bit is a done flag rather than a busy
flag: it is set once the request is taken, then request and done clear
by themselves about 4 ms later. The wrapper takes an SError when it is
accessed before they clear.

Wait for both bits to clear (a reconfiguration still under way), store
the request, wait for the done flag, then wait for both bits to clear
again.

Developed with Claude Code.

Signed-off-by: Erik Bourget <erik@ebourget.net>
The CIO (USB4/Thunderbolt) mode has its own firmware register and
receiver tunables. They are optional: an absent one is skipped.

Developed with Claude Code.

Signed-off-by: Erik Bourget <erik@ebourget.net>
The T6050 combo PHY rejects TYPEC_MODE_USB4 and the Thunderbolt alternate
mode, so a USB4 dock that the PD controller has already entered USB4 with
gets no link: the PHY is never configured and DWC3 cannot start.

Add the CIO mode as a step recipe in the style of the USB ones:

- the mode-independent power-up;
- the common tunables, then the CIO set (AUS40CMN_SHM, AUSPLL_CORE,
  CIO4PLL_CORE) and the CIO80 set (UC_REGS_CIO, LN0/LN1_RX_TOP_CIO,
  LN0/LN1_RX_EQ_CIO), with no USB or DP lane masks;
- the crossbar in CIO, where orientation only selects TOP +0x78 bit 0;
- the sleep-control release of both lanes;
- the firmware start, UC_TNBL_DONE and the ACIOPHY reset release, without
  the PIPE BIST and the ATCPHY_CFG +0x8 orientation bit.

The shutdown powers both transmitters and receivers down, shuts the common
block down, stops the firmware and runs the usual sleep, clamp and reset
sequence.

USB2 runs beside the link, so the USB2 PHY is brought up as for USB. USB3
arrives through a tunnel later; in CIO the USB3 PHY set_mode leaves DWC3
on its dummy PIPE producer until a tunnel exists.

The five CIO80 tunables are optional DT properties
(apple,tunable-uc-regs-cio, -lane0/1-rx-top-cio, -lane0/1-rx-eq-cio); a
missing one is skipped. On J716c only the RX_TOP pair is non-empty.

Developed with Claude Code.

Signed-off-by: Erik Bourget <erik@ebourget.net>
The T6050 host router has the T8132 MxWrap CPU wrapper and LSTX pin,
four ACIO power domains instead of three, and a PCIe router AP
firmware block with its own tunables. Its NHI has a single register
range, one DART stream per ring, and an SRAM parity interrupt after
its 24 ring interrupts.

Developed with Claude Code.

Signed-off-by: Erik Bourget <erik@ebourget.net>
The M5 Max (T6050) host routers are Type 7 routers with an MxWrap CPU
wrapper, close to the M3 ones. They differ at start:

- The wrapper is only clocked once the CIO reset (the reconfig request)
  has run; any access before it faults. The reset is required, as on
  the other generations, and runs before the wrapper is touched.
- The CPU is released with a plain store of the RUN value rather than
  a read-modify-write, and halted again when the router stops.
- Both the rc block and the wrapper take an external abort when read
  before the CPU runs, so the M3_CTRL start of the other generations is
  skipped and their state is not logged. The rc block is first touched
  to hand LSTX over, once RTKit is up.
- The router takes the LSTX pin over only once its RTKit is up: write
  the LSTX control from the level the AP held, then mux the pin to the
  router. While the router is off, hold the pin as a GPIO at the level
  the router last drove.
- The PCIe router AP firmware tunable is applied with the other router
  tunables once the firmware is ready.
- The router firmware trains Gen4 links only when the cable word says
  the cable is Gen4. For a USB4 Gen4 cable from Enter_USB it expects
  0x801 (present and Gen4), without the 20 Gb/s bit.
- The NHI has a named SRAM parity interrupt after its 24 ring
  interrupts, which is not a ring.

Match apple,t6050-usb4-acio and apple,t6050-usb4-nhi. DisplayPort
tunnels stay off on T6050 for now.

Developed with Claude Code.

Signed-off-by: Erik Bourget <erik@ebourget.net>
…initialises

A T8110 DART that no boot loader sets up, such as a USB4 router's or
its PCIe-C tunnel's, needs the device tree to describe what the boot
loader otherwise does: its tunables, its bypass streams, every error
reported and the context cache flushed.

Add apple,full-error-reporting and apple,ctc-flush, and extend
apple,bypass-sids beyond the PCIe-C tunnel DARTs.

Developed with Claude Code.

Signed-off-by: Erik Bourget <erik@ebourget.net>
A T8110 DART that the boot loader leaves alone, such as the M5 Max
USB4 router's, comes up with its tunables unapplied, its errors
masked and no stream in bypass. Its own driver otherwise only clears
the streams, enables them and flushes the TLBs.

Let the device tree describe the rest, for non-tunneled T8110 DARTs:

- apple,tunable: the DART's own tunables, applied at every reset before
  the first stream is configured. Client partition registers
  (0x800..0xfff) are skipped unless PARAMS1 bit 29 says they exist.
- apple,bypass-sids: streams set to bypass translation at every reset,
  outside any domain.
- apple,full-error-reporting: unmask every error and enable the error
  interrupt for every stream with the registers of the DART's revision
  (the error mask moved to 0x4060 in 2.2; status, enable and the
  per-stream enables moved from 0x700 to 0x4200 in 2.4), and leave the
  DART unlocked on error. On 2.4 the interrupt handler also reports
  and acknowledges the error status.
- apple,ctc-flush: where PARAMS2 bit 4 says there is a context cache,
  flush it (0x600) after a V2 stream flush, which leaves it alone.

Log the identification registers of a DART set up this way at probe.

Developed with Claude Code.

Signed-off-by: Erik Bourget <erik@ebourget.net>
Describe the USB4/Thunderbolt host router behind the right-hand USB-C
port (ADT /arm-io/acio2, acio-cpu2 and dart-acio2). In t6050-max.dtsi:

- the ATC2_CIO, ATC2_CIO_PCIE, ATC2_CIO_USB and ATC2_CIO_AUSS power
  domains;
- the router CPU's mailbox and the router's LSTX pin function on the
  AOP GPIO controller;
- the router itself, with its DART and NHI as children, so that both
  are populated only once the router runs: nothing is touched at boot;
- the DART's stream 15 in bypass, full error reporting and the context
  cache flush.

In the board: AUSS, kept as the boot loader leaves it, the CIO reconfig
request (port 2 of the die's CIO reset), the AOP GPIO controller with
only the LSTX pin exposed, the PHY's CIO80 RX_TOP tunables, the link
from the PD controller to the router, and, until the boot loader fills
them, this board's router, NHI and DART tunables and host DROM, from its
ADT.

The router node is named acio@: the boot loader disables a cio@ node
unless its first register is the third register of the matching
/arm-io/acioN, which on T6050 is not the router's rc block.

Developed with Claude Code.

Signed-off-by: Erik Bourget <erik@ebourget.net>
At the first controller reset the USB3 PIPE mux is expected unset (0),
on the dummy producer (2) or already parked (0x42). When nothing has
reset the controller domain since boot, as on a boot without the
SPTM, the mux reads 0x22: the dummy producer on another clock. The
park then refused it and dwc3 failed to probe.

The park clears the clock field before it selects the dummy producer
and its clock, so start from any clock while the producer is the
dummy one.

Developed with Claude Code.
Signed-off-by: Erik Bourget <erik@ebourget.net>
The host DROM of the M5 Max USB4 router lists port 8, which the router
reports as not implemented. Reading its port config fails with -ENODEV,
which made the whole DROM unusable and left the host router without
one, so the NHI failed to probe.

Skip such entries, as is already done for entries past the last port.

Developed with Claude Code.
Signed-off-by: Erik Bourget <erik@ebourget.net>
The PHY firmware is preloaded by the boot loader, data segment
included, and keeps its state in that segment. Restarted after a
stop, it ran on what its last run had left there and never finished
calibrating the lanes: UC_TNBL_DONE did not come, and every second
connection of a port failed, in USB as in CIO mode, until reboot.

Save the data SRAM when the firmware is first started and put it back
before every later start.

Developed with Claude Code.
Signed-off-by: Erik Bourget <erik@ebourget.net>
In CIO mode the lanes carry USB4/TBT, and USB3 reaches DWC3 through
the router's USB3 adapter. DWC3 was left on its dummy PIPE producer,
so a USB3 tunnel came up with nothing on the host side.

When DWC3 asks for USB3 in CIO mode, move its PIPE mux to the tunnel
producer (0x21: PIPE mode 1, clock 2) under the PIPE lock, as the
older Apple PHYs do in USB4 mode. A lock that is not acknowledged is
not fatal, as for native USB3. On power-off the PIPE goes back to the
dummy producer while the CIO PHY is left to the router.

Developed with Claude Code.
Signed-off-by: Erik Bourget <erik@ebourget.net>
@DjDeveloperr

Copy link
Copy Markdown
Contributor

Retested before merging on the M5 Pro (J714sAP, bare metal, SSD boot), kernel 7.3.0-m5-merge: the M5's integration kernel with this round's rebased heads in place of the old ones (every PR's own patch identical to what is merged; logs in work/m5-pr-merge-20261109). CI green.

Before merging, this round (these 14 PRs, #1323 and the rebased #1254/#1306/#938/#1316/#1199) was test-merged in order onto integration/aurora f9cf6f9 with no conflicts: apple-dt-check.sh 414 → 414 warning lines (0 new) over 137 DTBs, all 20 changed bindings pass, and a local host-test run fails only where integration itself fails with this host's newer toolchain. A full arm64 build of the same set on 8d98b7a was clean.

  • The J714s parts this carries all work: PCIe port 0 adopted (RID 0100 -> SID 1, link retrained after a root-port reset, functions .1/.2 routed) for the N1; both combo PHYs (phy-2ab000000.phy.0-2, phy-383000000.phy.3-5) and both DWC3s bind, typec port0/port1 register, the debug port (ATC0) is untouched and the KIS link stayed up; sn201202x logs no -110/-16 errors (usb: typec: tipd: Restore J716c dock startup #1272); the crossbar and thunderbolt modules load. With the J716C-only guards J714s no longer gets the ATC2 router, so nothing defers.
  • KUnit on integration + this round's merge set: sn201202x-pm 36/36.
  • dt-checks fixed by 0f2bdec; all 137 DTBs build. J716c's USB4 itself can't be tested here (no dock); the hardware results in the description stand.

🤖 Generated with Claude Code

integration/aurora now has the J713 display DART power gating (#1059),
whose register definitions sit where this series' DART commit adds the
v2.2/v2.4 error, client-partition and context-cache definitions. Keep
both blocks; they define different registers.

Signed-off-by: Diljit Singh <hey@djdev.me>
RyanTheTide added a commit that referenced this pull request Nov 9, 2026
@DjDeveloperr

Copy link
Copy Markdown
Contributor

Merging this hit a conflict after #1059 (J713 display DART power gating) landed on integration/aurora: both sides add register definitions at the same place in drivers/iommu/apple-dart.c. That's integration's DART_T8110_OUTSTANDING/APPLE_PS_PROT_* block and this series' v2.2/v2.4 error, client-partition and CTC definitions, which name different registers. 3b7ab89 on top (a merge of integration/aurora 040be05, no force-push) keeps both. apple-dart.o builds, no macro is defined twice, and the branch merges cleanly into integration again. I'll merge once CI passes on this head.

🤖 Generated with Claude Code

#937 was merged into integration/aurora on its own. This series carries
the same seven commits rebased, so every file #937 touches resolves to
this branch's version; the only conflict was this series' PCIe-C
definitions and MSI-slice check next to #937's lines in pcie-apple.c.

Signed-off-by: Diljit Singh <hey@djdev.me>
@DjDeveloperr

Copy link
Copy Markdown
Contributor

#937 was merged on its own (26049db) while this was waiting for CI. Its seven commits here are the same patches rebased, so 558d218 on top (another merge of integration/aurora, 76340e1, no force-push) resolves every #937 file (pcie-apple.c, apple,pcie.yaml, probe.c, pci.h, pciec-policy-tests.py) to this branch's existing version. The only conflict was this series' PCIe-C definitions and MSI-slice check sitting next to #937's lines. So the tree diff against integration is just #946, #939, #1269 and #1272 plus the two model updates and the schema fix, and it merges cleanly again. I'll merge after the re-run CI.

🤖 Generated with Claude Code

@DjDeveloperr

Copy link
Copy Markdown
Contributor

Merging at 558d218: CI green on this head (dt-checks, host-tests). The M5 Pro retest evidence is in the comment above; the two merge commits since then change no file this series carries.

🤖 Generated with Claude Code

@DjDeveloperr
DjDeveloperr merged commit f030e40 into integration/aurora Nov 9, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants