Skip to content

[4/7] phy: apple: atc: Take the T8132 CIO PHY through a deep sleep - #1417

Closed
erikwb wants to merge 2 commits into
j713/dock-s2r-dartfrom
j713/dock-s2r-phy
Closed

erikwb wants to merge 2 commits into
j713/dock-s2r-dartfrom
j713/dock-s2r-phy

Conversation

@erikwb

@erikwb erikwb commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor

Two changes to the T8132 Type-C PHY for a deep sleep with a USB4 link up.

  • Keep a PHY that is in a CIO mode as it is when the DWC3 host on its port stops. Until now it returned to its off mode, which dropped the CIO lanes and every tunnel through the dock although the cable was still connected. The Type-C mux still takes it down on a disconnect, ordinary USB modes still end with their host, and a CIO mode whose last transition failed is still taken down. This applies on every T8132 board (J604, J623/J624, J713/J715, J773g), outside the J713 deep-sleep opt-in.
  • Add a host-driven sleep transaction (prepare, off, on, finish) for the ACIO host: power the PHY down with its lane mode and crossbar saved, and bring it back in CIO mode with the same configuration. It takes part in the same sleeps as its host. A DP tunnel clock that does not stop refuses the sleep before anything is powered down, and the DP inputs get their clocks back. After the power cycle the tunnel clock's command register reads with its acknowledge bit set and no request pending; the first clock start after it issues the release a stop ends with before starting the clock.

Part 4/7 of J713 docked S2R, stacked on the native radio series (#1384), which is stacked on #1314.

Series order: #1414 → #1415 → #1416 → #1417 → #1418 → #1419 → #1420.

Depends on #1416.

Validation of the series on this base:

  • Full arm64 Image, modules and DTBs at every PR tip of the series.
  • Ten focused host suites with negative controls (DART owner and register sequence, PHY sleep transaction, USB3 host model, router state transfer, tunnel replay including a dock lost during the sleep, NHI and tunneled-PCIe ownership including its failure paths, dock PM callback ordering, DP repair, DART writer gate and its callers) plus the device-tree policy check. checkpatch is clean for the driver code; it notes the Based-on-work-by: trailers and style in the tools/asahi fixtures.
  • J713 with a CalDigit TS5 (USB4 link, PCIe tunnel with the dock's Ethernet, USB hub with two keyboards, two 4K DisplayPort monitors), on the first version of the series: one suspend-abort test at the platform stage, then one real deep sleep with an RTC wake on the same boot. After the wake the dock link was at 20 Gb/s, dock Ethernet passed traffic at 2.5G, the USB devices were back, both monitors were lit at their previous mode about 10 s after the wake, Wi-Fi and Bluetooth answered, all ten CPUs were online, and there were no kernel warnings or taint. Repeated with one monitor unplugged; a monitor plugged in after the wake came up.

The changes since that version (the DT-selected DART owners and memory-only updates while closed, the unplug handling, the failure unwinds, the stock tb.c resume restored, the PMGR and RTKit hardening) are built and covered by the host suites, but have not run on the J713 yet.

Not covered: repeated sleeps on one boot, wake sources other than the RTC, the desktop's own sleep path (lid, logind), a dock unplugged during the sleep on hardware, and docks other than the TS5. Both native parameters (pcie_apple.cio_s2r, thunderbolt_apple.cio_s2r) default to off.

This series was developed and tested by Claude (Anthropic), working with me on the J713.

@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.

[4/7] phy: apple: atc: review at 3464d30

Gating: the sleep transaction only acts while cio_sleep.prepared. Only #1419's J713 NHI sets it, with thunderbolt_apple.cio_s2r=1, and atcphy_from_cio_phy() limits the API to T8132.

The one unconditional change is the early return in atcphy_usb3_power_off(). It applies on every T8132 board (J604, J623/J624, J713/J715, J773g) whatever cio_s2r says. The behaviour looks right, and it's now its own commit (6ca9291); its message should say it applies on every T8132 board, outside the opt-in. No M5 impact: T6050 uses atc-t6050-*, and atc.c merges cleanly.

Findings

  1. That early return also skips the old mode_dirty cleanup (mode != OFF || mode_dirty reconfigured to OFF). A failed transition in a CIO mode now stays dirty after DWC3 stops. Suggest && !atcphy->mode_dirty.
  2. apple_atcphy_cio_prepare() gates on pm_suspend_via_firmware(), while apple_nhi_cio_prepare() gates on pm_suspend_target_state == PM_SUSPEND_MEM. A MEM sleep without the firmware flag makes the NHI's prepare fail with -EOPNOTSUPP, which refuses the sleep instead of falling back to the stock path. Use one predicate in both.
  3. If atc_t8122_tunnel_stop() fails in cio_off(), it sets t8132_failed, cio_on() then returns -EUCLEAN, and #1419's unwind panics. A tunnel-clock stop timeout shouldn't cost the machine. Fail before changed = true, or let cio_on() restore power and only refuse the clock restart.
  4. Nit: dev_info in tunnel stop/start on every CIO sleep.

Verdict: OK with minor changes.


Review only: read at the head commit, not built or run (no J713 here). Written with Claude Code.

Comment thread drivers/phy/apple/atc.c Outdated
* tunnels down before their drivers suspend. The Type-C mux owns the
* CIO mode and releases it when the connection ends.
*/
if (atcphy->hw->gen == ATCPHY_GENERATION_T8132 && atcphy_mode_is_cio(atcphy->mode))

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.

This is unconditional on T8132, outside the cio_s2r opt-in, which deserves a line in the commit message. It also skips the mode_dirty recovery below. Before, a dirty mode was always reconfigured to OFF here; now a CIO mode whose last transition failed stays dirty after DWC3 stops. Suggest atcphy_mode_is_cio(atcphy->mode) && !atcphy->mode_dirty.

Comment thread drivers/phy/apple/atc.c Outdated
struct apple_atcphy *atcphy = atcphy_from_cio_phy(phy);
struct atcphy_cio_sleep *s;

if (!atcphy || !pm_suspend_via_firmware())

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.

The NHI decides whether to run this transaction from pm_suspend_target_state == PM_SUSPEND_MEM (apple_nhi_cio_prepare()), but this checks pm_suspend_via_firmware(). If they ever disagree, the NHI's .prepare fails with -EOPNOTSUPP and refuses the sleep instead of taking the stock path. Suggest one shared predicate.

Comment thread drivers/phy/apple/atc.c
}
s->changed = true;
/* This PLL belongs to the saved DP streams, not to the CIO link. */
ret = atc_t8122_tunnel_stop(atcphy);

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.

If this fails, t8132_failed is set after s->changed = true. The NHI unwind's apple_atcphy_cio_on() then returns -EUCLEAN, and apple_nhi_cio_suspend() panics (suspend %d, recovery %d). Nothing has been powered down yet at this point, so failing before changed = true (or not marking the PHY failed for this step) would let the sleep be refused cleanly.

Comment thread drivers/phy/apple/atc.c Outdated
if (stale_ack) {
/* The same release a clock stop ends with, so the start sees a real ACK. */
ret = atc_t8122_tunnel_apb(atcphy, 0x10000018, false);
dev_info(atcphy->dev, "CIO tunnel clock ACK release: %d (APB %#x)\n", ret,

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.

Nit: this and the stop-side dev_info print on every CIO sleep; dev_dbg would do.

On T8132 the PHY returns to its off mode when the DWC3 host on the port
stops. With a USB4 or Thunderbolt link up that also drops the CIO lanes
and with them every tunnel through the dock, although the cable is
still connected and only the USB host went away, as it does for a
system sleep.

Leave a PHY that is in a CIO mode as it is when its USB host stops, and
keep it busy for runtime PM. The Type-C mux still takes it down on a
real disconnect, an ordinary USB mode still ends with its host, and a
CIO mode whose last transition failed is still taken down.

This applies on every T8132 board (J604, J623/J624, J713/J715, J773g),
outside the J713 deep-sleep opt-in.

Extend the USB3 host model with the T8132 cases and three mutations.

Signed-off-by: Erik Bourget <erik@ebourget.net>
A deep sleep with a USB4 link up has to power the PHY down and bring it
back in CIO mode with the same lane configuration. The system suspend
callback refuses a deep sleep while the common block is in use, and
only the ACIO host knows when the link's users are stopped.

Add an interface for that host: prepare records the mode and
orientation and lets the system suspend through, off stops the DP
tunnel clock, saves the lane mode and crossbar and powers the PHY down,
on powers it up and restores them, and finish ends the transaction. The
tunnel clock is started again for the DP inputs that were using it.
prepare takes part in the same sleeps as its host (a suspend to RAM).
A tunnel clock that does not stop refuses the sleep before anything is
powered down, and the DP inputs using it get their clocks back.

After the power cycle the tunnel clock's command register reads with
its acknowledge bit set and no request pending. The first clock start
after it, in the restore or whenever a DP tunnel next asks for the
clock, accepts that and issues the release command a stop ends with
before it starts the clock, so the start's own acknowledge is real.

Add a host-side model of the transaction with negative controls.

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

erikwb commented Nov 13, 2026

Copy link
Copy Markdown
Contributor Author

Closing: this commit set landed in integration/aurora through #1314, which carried the whole docked deep-sleep series (#1414 to #1420). The follow-up that turns the docked deep sleep on by default is #1855.

@erikwb erikwb closed this Nov 13, 2026
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.

2 participants