Skip to content

J713 (M4 MacBook Air): suspend-to-idle - #831

Merged
erykwieliczko merged 7 commits into
aurora-silicon:integration/aurorafrom
erikwb:j713/s2idle-integ
Nov 4, 2026
Merged

erykwieliczko merged 7 commits into
aurora-silicon:integration/aurorafrom
erikwb:j713/s2idle-integ

Conversation

@erikwb

@erikwb erikwb commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

The J713 (MacBook Air 13" M4, T8132) refused system sleep. With this series and the DCP's sleep handshake in #830, it sleeps from systemctl suspend and from the lid. It wakes on the keyboard, trackpad, power button or lid, with display, GPU, Wi-Fi and audio intact.

It replaces the platform half of #449; the commits are unchanged. The pieces span iommu, PCI, pmdomain and the Apple mailbox, but they're one feature: suspend-to-idle works only with all of them.

Needs:

Retained DMA owners (commits 1–2)

The radio's PCIe port and DART, and the display DARTs, have no power domain of their own in the tree, and the DCP holds the display domains on. So suspend-to-idle leaves their live state in place, and these two commits let them through for s2idle only. Every other sleep state is still refused. The DARTs save their registers only so that resume can check nothing moved.

If something did move, the consumers' DMA is no longer what they set up, and a resume error alone doesn't stop the PM core from resuming them. So before failing the resume, the DART quarantines them through the same paths as a translation fault:

  • the managed radio streams are marked failed and their DMA is disabled, so later maps are refused;
  • the borrowed display consumers (the DCP and its frontends) are marked failed, so their later maps are refused.

GPU (commits 3–4)

asahi-g16 has no system-sleep callbacks, so genpd's noirq power-off gated GFX, and with it the TVM gate, under the live firmware. The power-on at resume then timed out (GFX target=0xf … error=-110).

A pmgr-pwrstate domain marked apple,sleep-retained declines that power-off while the system sleeps. Runtime PM is unchanged.

Wakeups (commits 5–7)

Every ASC mailbox receive IRQ is IRQF_NO_SUSPEND. The G16 firmware's idle ring-service kicks (about 1.9/s) and an AOP report (1/s) woke the CPUs about three times a second:

Sleep Wakeups Deep idle
Before 938 s 1472 512 s
After 148 s 3 146.9 s

A mailbox marked apple,hold-rx-in-sleep masks its receive IRQ from noirq suspend to noirq resume. The firmware throttles once its outbox is full: after 148 s, 11 GPU messages and 3 AOP messages were waiting. Commit 7 sets both flags for the J713 GPU, and #450 sets the mailbox flag on the AOP.

Testing

J713, on integration/aurora 41c4055 with the rest of the J713 PR stack (#830, the USB series, #429, #432, #819/#820, #450, #531, #825): an 82 s real suspend-to-idle.

  • Mailboxes: while asleep, the GPU mailbox took 8 interrupts and the AOP mailbox 5. Unheld, that would be about 3 a second.
  • After resume: display (error=0), GPU (no errors), Wi-Fi and speaker playback all back.
  • Faults: none, from the DARTs, amps or I2C.

J713, on our J713 tree with this series, the DCP handshake, #429, #432 and the AOP audio, on 26.6.2 firmware:

  • Real s2idle: systemctl suspend (up to about 16 min asleep) and the lid, through logind. Display, GPU (glmark2 630–642, unchanged), Wi-Fi and the AOP audio all come back.
  • Dry runs: pm_test=devices and pm_test=platform pass.

J713, on integration/aurora 41c4055 with these commits, #429, #432, #819/#820, #450, #531 and the DCP handshake: several real suspend-to-idle cycles (up to 352 s). Each resumed to a working desktop, with Wi-Fi and the camera working; a 600-frame stream ran across one of them. Over 222 s asleep, the AOP mailbox took 3 interrupts.

Host tests:

  • New dart-s2idle-retention.py, on the extracted production state check, quarantine and map functions:

    • kept state resumes, and the radio still maps;
    • each of these makes resume return -EIO, with the radio streams disabled, managed_failed and every borrowed consumer's failed flag set, and a later 16 KiB radio map refused:
      • a changed managed TTBR;
      • a changed TCR on another stream;
      • a DART that reads back as reset;
    • a negative control without the quarantine fails.

    This checks the failure path on the host, not physical retention on the J713; the retained path is unchanged.

  • m4-dcp-errors passes, including its case where a failed display domain refuses maps.

  • bcm4377-radio-sids.py passes, with its negative controls.

Checks (on integration/aurora 41c4055):

  • all commits are signed off;
  • checkpatch --strict is clean (apart from the MAINTAINERS note for the new test file);
  • full arm64 build (Image, modules, DTBs) with the J713 config, with no warnings in the touched files;
  • dt_binding_check is clean for apple,mailbox.yaml and apple,pmgr-pwrstate.yaml;
  • CHECK_DTBS W=1 on all six T8132 DTBs gives the same output as the base;
  • run-host-tests: 132/133. The one failing group, apple-dcp, fails the same nine cases on the bare base and nothing more.

Still to do: this changes shared DART save-state code, PMGR and mailbox PM, so it needs the Neo and M1 Pro/M2 regression boots with s2idle, display/GPU, radio and audio checks, which I can't run. The new flags are opt-in by DT, and only the J713 sets them. The retained-DART admission is restricted to the J713 at probe.

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

The J713's retained DMA owners refuse system sleep: the radio's DART
with managed streams, and the display DARTs, which borrow the running
DCP's live page-table root. Neither has a suspend boundary of its own,
and their state cannot be restored from a copy.

Suspend-to-idle does not need one. These DARTs have no power domain of
their own in the tree, and the DCP keeps the display domains on across
s2idle, so their state stays where it is. Let them through for
suspend-to-idle only, saving their registers solely for resume to check
that nothing moved. Every other sleep state is still refused.

A DART whose retained state changed cannot be trusted with its live DMA,
and a resume error does not stop the PM core from resuming consumers.
Quarantine the managed streams and the borrowed display consumers
through the same paths as a translation fault before failing resume, so
that their later maps are refused. A host test exercises the extracted
check, quarantine and map functions.

Signed-off-by: Erik Bourget <erik@ebourget.net>
The J713's radio port, whose streams the OS adopts from the boot
loader's handoff, refuses system sleep in its noirq phase. It has no
suspend boundary of its own, but suspend-to-idle does not need one:
the port has no power domain in the tree, so it stays up as any other
internal port does, whose resume does nothing. Let it through for
suspend-to-idle only; every other sleep state is still refused.

Signed-off-by: Erik Bourget <erik@ebourget.net>
A coprocessor can keep running in its power domain across system sleep
without system-sleep callbacks of its own, as the J713's GPU firmware
does. Describe such a domain, which the OS keeps on during system
sleep; runtime power management is unaffected.

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

The J713's G16 GPU has no system-sleep callbacks, so in the noirq
phase genpd powers its GFX domain off, and the TVM gate above it, under
the running GPU firmware; the power-on at resume then times out. Let a
domain marked apple,sleep-retained decline its power-off while the
system sleeps: genpd then keeps it and its parents on. Runtime power
management is unchanged.

Signed-off-by: Erik Bourget <erik@ebourget.net>
Some coprocessors message the AP periodically while idle: the J713's
GPU firmware asks for ring service about twice a second, and its AOP
sends a report every second. Describe a mailbox whose messages need not
wake the system, so the OS can leave them in the FIFO while it sleeps
and handle them at resume.

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

The receive IRQ of the ASC mailboxes is IRQF_NO_SUSPEND, so every
coprocessor message wakes a CPU out of suspend-to-idle. On the J713
the GPU firmware's idle ring-service kicks and an AOP report every
second wake it about three times a second: in 938 s of s2idle, 1472
wakeups and only 512 s in deep idle.

For a mailbox marked apple,hold-rx-in-sleep, mask the receive IRQ from
noirq suspend to noirq resume, so its messages wait in the FIFO and the
consumer handles them on resume. The mailbox is a supplier of its
consumer, so this runs after the consumer suspended and before it
resumes. The firmware throttles once its outbox fills: after 148 s of
s2idle, 11 GPU and 3 AOP messages were waiting, with 3 wakeups and
146.9 s in deep idle.

Signed-off-by: Erik Bourget <erik@ebourget.net>
The G16 firmware keeps running across suspend-to-idle: keep its GFX
domain on through system sleep, and hold its idle ring-service kicks in
the mailbox instead of waking the system about twice a second.

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

Copy link
Copy Markdown
Member

Merging, with one conflict resolved. This comment is from Claude Opus 5.5, working for Eryk, not from Eryk himself.

Rebased onto integration/aurora 0cfb3017e8a5 and force-pushed, as Erik agreed. No commit's content changed.

Changes

  • iommu/apple-dart: Keep the retained J713 DARTs through suspend-to-idle: dabc991510ac added a cold ANE early return to apple_dart_suspend() and apple_dart_resume(). Both sides are kept, the cold ANE return first. Only context changed.

For Erik

A false quarantine looks possible when the radio has no paging domain at suspend, because the SID16 bypass reads back without BYPASS_DAPF. Not seen on hardware.

Testing

  • On Eryk's J713, in a test kernel with the other J713 PRs: one real suspend-to-idle cycle of 2 min 20 s, with timekeeping stopped. suspend_stats shows no failure, there are no DART faults or firmware crashes in the log, and Eryk confirmed it woke up.
  • It has only been through that one short sleep so far.
  • Built together with the rest of this batch on integration/aurora (arm64: Image, modules, DTB).

🤖 Written by Claude Opus 5.5 (in Claude Code) for Eryk.

@erykwieliczko
erykwieliczko merged commit 0edd20e into aurora-silicon:integration/aurora Nov 4, 2026
erykwieliczko added a commit that referenced this pull request Nov 4, 2026
arm64: dts: apple: t8132-j713: Hold AOP messages in system sleep (depends on #820, #831)
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