Skip to content

PCI: apple: adopt the T6050 root ports the boot loader trained - #937

Merged
RyanTheTide merged 7 commits into
integration/aurorafrom
j716c/t6050-pcie
Nov 9, 2026
Merged

RyanTheTide merged 7 commits into
integration/aurorafrom
j716c/t6050-pcie

Conversation

@erikwb

@erikwb erikwb commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

On T6050 the boot loader powers the internal PCIe host, trains each port and leaves the links up. Re-running reset and training from Linux isn't needed and isn't known to be safe, so without an adopting host there's no Ethernet, rear USB-A or Wi-Fi.

  • apple,t6050-pcie: adopt ports marked apple,link-trained-by-bootloader as they are (no reset, tunable replay, power change or retraining), with every resource taken from the DT by port index.
  • pci_trylock_rescan_remove() (Dj's), so a worker cancelled by removal doesn't wait on the remover's lock.
  • The T6050 port disable, reset, enable, configure and train sequences, in pcie-apple.c on the port's own register blocks and the root port's config space. They are Dj's sequences (co-developed), now native to the driver.
  • Port 0, the N1 Wi-Fi/Bluetooth: multi-function routing (one DART stream per function), Gen1 to Gen3 links, and a root-port reset for the N1 driver's post-ROM cycle, using those sequences, with PERST# driven through the pin controller.

Commits by Jeff Queisser, Dj and Erik Bourget. On J716c (MacBook Pro 16-inch, M5 Max) this runs as part of the full T6050 stack (j716c/t6050-platform): the machine boots to userspace from NVMe with all 18 CPUs, N1 Wi-Fi up and CPU frequency scaling active. The N1 drivers that sit on port 0 are the next PR, stacked on this one.

Rebased onto current integration/aurora. The T6050 adoption/reset paths are preserved alongside the newer PCIe host definitions. Validation of the rebased series: arm64 LLVM driver builds with W=1, binding schema/example checks, and PCIe binding validation of J716c, J775D, T8152 J873g and T6040 J616s device trees. The exact rebased kernel has not been installed or booted.

The PCIe-C policy test now preserves multiline driver macros in its compiled fixture. The previously failing production case and both negative controls pass locally.

Reviewed with sol-loop: 2 rounds with GPT-6.1 Sol. Sol verdict: approved (rebase and subsequent test fix).

Created with Claude Opus 5.5 in Claude Code.

funkzy and others added 6 commits November 8, 2026 00:50
…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>

@DjDeveloperr DjDeveloperr left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Works on the M5 Pro (MacBook Pro 14", J714sAP, T6050), bare metal, once the boot loader trains the link.

Our M5 aura had no T6050 PCIe bring-up, so I put erikwb's on it from ewb/j716c-daily: e7d6b647, e7f2008d, d28dedf2, 33af8bff, 859595d6, 67b111e6, 59c2d169, and 37c0da7d's SMC function reset. That is a local test image; the only change is a size flag. On J714s it finds port 0's bridge only (ports 2 and 3: "no bridge on this board") and releases the N1's gP13 reset. It then trains port 0 at Gen1 x1 to endpoint 106b:1900, and storage is unchanged after every stage.

This head (911da8c), with #938 and #1199 in the same kernel on PR1012's base:

  • adopts the link: port 0: RID 0100 -> SID 1, slot 1, adopted port 0, endpoint 01:00. The root port (106b:100c) and the N1 enumerate with no PCIe or DART errors.
  • does the N1's post-ROM root-port reset: port 0: link retrained after a root-port reset. Functions 01:00.1 (1902) and 01:00.2 (1903) then appear and get their streams (e.g. RID 0102 -> SID 3).
  • carries the N1's traffic for #938: 64 MiB in and 32 MiB out over Wi-Fi, byte-exact.

Same result on three boots.

Note: the port's reset-gpios needs pinctrl-apple-gpio, so the host defers until that module loads. That's expected, but a system that doesn't load it stays without PCIe. This head merges cleanly into current integration/aurora.

@RyanTheTide
RyanTheTide merged commit 26049db into integration/aurora Nov 9, 2026
2 checks passed
DjDeveloperr added a commit that referenced this pull request Nov 9, 2026
#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>
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