Repository navigation
Apple SoC suspend-to-RAM on T8140 - #1445
Conversation
ae8031d to
2ad5c88
Compare
ef0c53b to
03cf90f
Compare
d7ff106 to
51a7ed6
Compare
Coordinate device preparation, irreversible coprocessor stop and the EL2 reset-return mailbox. Recover the CPU, interrupt, memory, storage, display, radio, USB and audio state on a real firmware return. Keep pre-send SEP cancellation returnable and reject unsafe post-send unwind. Retain explicit s2idle and selected deep mode on readiness refusal. Preserve shared cold-probe and M5 integration behavior; unqualified T6050 GPU sleep restart remains disabled. Accept only SPMI parent dispatches whose original pending-bank samples are all empty, matching the native no-work contract. Preserve existing handling and diagnostics for every nonempty sampled set. This does not classify the historical IRQ64 events or claim hardware qualification. Signed-off-by: Diljit Singh <hey@djdev.me>
51a7ed6 to
d9b438c
Compare
…r its cut The cut waited a fixed second after the port controller's cold reset before its first look at the contract, and in every logged sleep the contract was already back at that first look. That second was most of a cut sleep's entry. Look from 50 ms on instead. The contract reads back as before for a moment after the reset, so count it as back only once it was seen gone (no contract, or no answer while the controller restarts), or after the old second if it never seemed to go. Log both times. Signed-off-by: Diljit Singh <hey@djdev.me>
A sleep that cuts the debug link waits for the SMC's outbox to be quiet for 500 ms after the cut, then warns it late and leaves macOS's 770 ms before slpa. No message came in the quiet window in any logged sleep, and the sleeps that warn at .prepare, with no cable, commit 150-500 ms after the warning and run to their alarm (154 of 154). Use 100 ms of quiet and a 200 ms gap. Signed-off-by: Diljit Singh <hey@djdev.me>
In every logged cut sleep the partner's contract was back 660-704 ms after the port controller's cold reset, or not at all: 42 first resets never got it back within 6 s, and the next reset did, at about 0.7 s. Each of those waited the full 5 s before trying again. Wait 1.5 s per reset instead. Signed-off-by: Diljit Singh <hey@djdev.me>
#1485 changed apple_pmp_dvfs_word() to take a main level and class pairs. Keep it, and keep the sleep series' apple_pmp_request_word() next to it. T8140's apple,dvfs entries carry no request class, so their words are the same as before. Signed-off-by: Diljit Singh <hey@djdev.me>
The asahi Makefile gained legacy_sched.o in integration and g17/sleep.o in this series: build both. Signed-off-by: Diljit Singh <hey@djdev.me>
A T8140 whose boot tables aren't the T6041 ones left the SIO unbound, so nothing idled it for a sleep. macOS idles it in the sleep's last step, 38 ms before the commit and just before the PMP: AP power idle, IOP power idle, then the mailbox's receive interrupt closed. Run the firmware without DMA service in that case, as T8140 did before the T6041 tables, and leave its idle and stop to the sleep's last step. A sleep refused before that step finds the firmware still running. Signed-off-by: Diljit Singh <hey@djdev.me>
An unbound coprocessor that must stop only warned, because the gate moved on before its run-bit test. Nothing will stop it, so refuse the sleep if its domain is fully on and its CPU runs. Signed-off-by: Diljit Singh <hey@djdev.me>
DjDeveloperr
left a comment
There was a problem hiding this comment.
Codex (GPT-6.1 Sol), reviewing on @DjDeveloperr’s behalf.
This remains an experimental draft. The body still records rare DRAM-kept wake resets before Linux returns (~1/60, two observations), safe ANS suspend refusal, and exact-tree qualification only for f65bce9 rather than the later integration additions on current db58075. CI success does not establish reliable suspend-to-RAM or close the latest-head hardware gap. I will not merge or relabel this as Working while those reset/boot-path gates remain. No new speaker tests, default-loader change, Stage1 write or physical sleep test is requested here; any future work must retain the owner's existing dead-man/rollback constraints.
The DRAM-kept wake's M2A_1B writes 44 power words and gives each switch 171 us to report closed. Old SOCD records show it stopping on ane_sys and on isp_cpu_core1, then WD2 resetting the SoC. macOS leaves these domains off with BUSY clear. The PMGR driver pulses a PS reset to clear a latch when a domain goes off, but not in a system sleep, which is when genpd takes ane_sys down. Clear a latch on each polled word that is off, just before the commit. If one stays latched, take the deep path for that sleep, which cuts their power: a refused sleep would be a failed suspend. Signed-off-by: Diljit Singh <hey@djdev.me>
A domain under automatic power management went down in one write that cleared the auto bit and the target together. macOS clears the auto bit first, sees the domain back at full power, then writes the target and clears the history bits (traced for AVD, VENC1 and JPEG). ane_sys went off with BUSY latched in about one sleep in 80, and the sleep engine's wake then stalled on it until the watchdog reset the SoC. Lower T8140 domains the macOS way, and clear a latch when a domain goes off even in a system sleep, while the domain is off and long before the commit. Signed-off-by: Diljit Singh <hey@djdev.me>
On a failing sleep the commit map read ane_sys off with BUSY latched and the wake's engine stopped in the ANE's power-on. Linux's power-down waits only for the actual state to reach zero, so it can't see a BUSY latch, and it skips a step macOS takes. macOS's H17 path (DisableSubblocksClocksAndPower and DisableCPUClocksAndPower, ane-type 0x200, 25G83) writes each power word whole as 0x300 and waits for (word & 0x8ff) == 0, which covers target, actual and BUSY. Between the PWGATE request and the CPU word it clears the word at PWGATE + 0x30 and waits for bit 0 to read zero. Do the same. A word that stays BUSY now fails the power-down, which refuses the sleep instead of carrying the latch into the commit. Signed-off-by: Diljit Singh <hey@djdev.me>
The H17 sleep path cuts the engine's power with the firmware running. macOS never does that. In ANE_deInit, on this chip, it sends command 1 (12 bytes, with a reply), writes 0xf7fbdff9 to SCRATCH0, posts POWER_DOWN (0x0b, 8 bytes) on the IO ring, and waits up to a second for the firmware to answer 0x08042006 in SCRATCH0. It then waits up to 200 ms for CPU_STATUS & 3, and goes on without it if that doesn't come. It takes the firmware out of Exclave mode (0x31) first, and sends SET_SNE_PMU_BASE2 (0x29) at every start. SUSPEND (0x24), the command this driver tried, is only chosen when a per-chip flag is set, and the H17 configuration doesn't set it. That is the likely reason 0x24 was never taken, along with waiting on the ring instead of on SCRATCH0. Before the power words macOS also deactivates the DARTs, whose power-down path waits for every port to read idle at DART + 0x230. Check the same registers here. The deactivation itself goes through SPTM and isn't visible in the kernelcache, so the DARTs are left programmed. macOS doesn't clear the CPU's run bit here, and neither does this. Any failure after the first command quarantines the engine and refuses this sleep. ane.clean_stop=0 restores the old path. Signed-off-by: Diljit Singh <hey@djdev.me>
A refused sleep is a failed suspend. If a power word stays BUSY after a PS reset, or the firmware doesn't stop, ask the sleep driver for the deep path for this sleep, which cuts the block's power. Refuse only if that isn't available. Signed-off-by: Diljit Singh <hey@djdev.me>
On the Neo the stop command got no answer. A recording of macOS's sleep under aura with the ANE visible (25G83) shows three things the static reading missed: - once the firmware is up, macOS sets SCRATCH14 = 1 and SCRATCH16 = 0, and before the stop command it sets them the other way; - after SCRATCH0 answers it writes 0xfeedbabe to SCRATCH1; - before the power words it clears each DART's counters and error status, disables every enabled stream one at a time, and flushes the TLB. Do the same, in that order. Signed-off-by: Diljit Singh <hey@djdev.me>
The recording of macOS's sleep shows its AP ringing only the IO, and three other, channels: never the one EXCLAVE_MODE_STOP (0x31) goes on. With 0x31 sent first, the Neo's firmware didn't answer the stop command on two boots. Send the stop as macOS does, without it. Signed-off-by: Diljit Singh <hey@djdev.me>
DjDeveloperr
left a comment
There was a problem hiding this comment.
Codex (GPT-6.1 Sol), reviewing on @DjDeveloperr’s behalf.
Host job 130886997274 completed the runner: 269/284 passed. The 15 failures are fixture integration problems introduced by combining the current production interfaces; they are separate from the SEP log-policy failure in review5867347666.
- j613-mtp-s2idle-tests.py fails extraction with ValueError before compiling. It still requests apple_dart_system_suspend/resume, while this head wires apple_dart_sys_suspend/resume and delegates to apple_dart_live_mtp_suspend/resume. Its extractor also normalizes only the other __maybe_unused placement. Adapt the fixture to the actual callbacks/delegation and qualifier order, retaining live-MTP normal/abort/refusal/runtime checks; do not revert the production PM composition to satisfy an old name.
- Four normal ISP cases and ten negative-control cases fail to compile. The session fixture lacks hardware.fw_restart, apple_isp.fw.started and the isp_check_restarted boundary; the extractor omits the new isp_cmd_send_chan helper. Buffer/return fixtures lack ISP_FW_RUNNING, and the queue fixture lacks clear_bit. Extend the fixtures/extraction for the real restart/command interfaces and preserve source guards, ownership and shutdown ordering. The overwrite control must still exercise the effective guard after the shared send-helper refactor; a success stub must not bypass it.
The runner correctly rejected compilation failures as 'without a control failure'. Keep that behavior: normal cases must compile/run successfully and controls must compile then fail their intended runtime assertions. Do not count these ten compiler errors as killed mutants, skip registrations or weaken assertions. Relevant production is not shown failing by these diagnostics; the separate kernel build remains its own evidence. No unchanged CI retry will fix the fixtures.
The keeper owns the next patch/push and validation. I have not changed that branch, started another build, adjusted its queue or requested another physical test. Exact-head green CI and the existing owner-approved fast Neo retest remain the merge gates.
…r the merge The SEP's request-path source test from integration (#1590) wants rate-limited or debug messages throughout sep.rs. Five in this series' sleep path were plain: use dev_warn_ratelimited!, dev_err_ratelimited! and dev_info_ratelimited! for them. No logic changes. The last integration merge renamed the DART system-sleep callbacks that tools/asahi/j613-mtp-s2idle-tests.py extracts. Name them apple_dart_system_suspend() and apple_dart_system_resume() again, with the J613 live-MTP code inline as integration has it and this series' firmware-sleep phase choice for every other DART. Give the test stubs for pm_suspend_via_firmware() and the rtkit-helper AP notices, which a firmware-driven sleep alone reaches. The ISP host harness (#1560) didn't model this series' ISP state. Extract isp_cmd_send_chan() with isp_cmd_send(), and take the latch guard out of both for the overwrite control. Give the session fixture fw_restart, fw.started and isp_check_restarted(), and run its capture stop with the firmware running, as a capture only stops while it streams. Give the buffer fixture ISP_FW_RUNNING and clear_bit(). Every check and control stays as it was. Signed-off-by: Diljit Singh <hey@djdev.me>
DjDeveloperr
left a comment
There was a problem hiding this comment.
Codex (GPT-6.1 Sol), reviewing on @DjDeveloperr’s behalf.
Host job 130899624898 passed 262/284 cases. The previous MTP/ISP fixture failures are gone. All 22 remaining failures belong to sep-suspend-errors-tests.py: its normal case and 21 mutations stop at Rust fixture compilation, so none provides a runtime result yet.
The fixture’s Rust prelude still defines only dev_err!, dev_warn!, dev_dbg! and dev_info!. The extracted production sleep/wake code now correctly uses the rate-limited forms after the source-policy fix. Add corresponding fixture-only logging boundaries alongside the existing ones, for example:
macro_rules! dev_err_ratelimited { ($($a:tt)*) => {{}}; }
macro_rules! dev_warn_ratelimited { ($($a:tt)*) => {{}}; }
macro_rules! dev_info_ratelimited { ($($a:tt)*) => {{}}; }Keep the real production rate limiting and the policy test intact. Then require the normal SEP case to compile/pass and each mutation to compile and fail its intended runtime check; the current compiler errors are not killed controls. Do not remove registrations, weaken assertions or retry unchanged CI. This is a host-fixture boundary update, not evidence that the kernel sleep implementation failed to compile.
The keeper retains patch/push/build ownership. No competing source change, build, queue adjustment or physical test was started here. The existing green-CI and owner-approved fast-retest merge gates remain, with the keeper’s stated logging/callback-rename equivalence for the retest retaining its exact scope.
The SEP sleep path now logs through dev_err_ratelimited!, dev_warn_ratelimited! and dev_info_ratelimited!, as integration's request-path rule asks, and the harness's Rust prefix had no-op stubs only for the plain macros, so rustc stopped on the extracted code and the test and its 21 mutations all failed to build. Add no-op stubs for the three, beside the plain ones, which other extracted code may still use. Signed-off-by: Diljit Singh <hey@djdev.me>
2b05915f is integration/aurora with aurora-silicon/linux#1445 merged: suspend-to-RAM on T8140 (CONFIG_APPLE_SOC_SLEEP) with the PMU RTC wake alarm built in (CONFIG_RTC_DRV_APPLE_PMU=y), both of which the source's asahi.config now sets, and the integration changes since 84d091f7. olddefconfig at the new pin keeps both sleep options at y and adds four lines for new symbols at their defaults: CONFIG_APPLE_PIODMA=m, CONFIG_APPLE_CLUSTER_PWRGATE=y, and CONFIG_SND_SOC_APPLE_T8152_AOP_AUDIO and CONFIG_CRYPTO_DEV_APPLE_SIO_AES not set. The pending-pin TODO goes. Signed-off-by: Diljit Singh <hey@djdev.me>
2b05915f is integration/aurora with aurora-silicon/linux#1445 merged: suspend-to-RAM on T8140 (CONFIG_APPLE_SOC_SLEEP) and the built-in PMU RTC wake alarm (CONFIG_RTC_DRV_APPLE_PMU=y), which the source's asahi.config now sets. kernel/config is the ALARM config resolved at that pin, byte for byte, from alarm-aurora 1b1eb124. Besides both sleep options at y, it takes ALARM's built-in Apple GPIO (CONFIG_PINCTRL_APPLE_GPIO=y, for early macsmc RTC probing), which the previous snapshot predates, and four new symbols at their defaults. With asahi.config asking for the built-in RTC itself, kernel/nixos.config's override and the pending-pin note go. Signed-off-by: Diljit Singh <hey@djdev.me>
…d091f7 2b05915f is integration/aurora with aurora-silicon/linux#1445 merged: suspend-to-RAM on T8140, whose asahi.config sets CONFIG_APPLE_SOC_SLEEP=y and the built-in PMU RTC wake alarm (CONFIG_RTC_DRV_APPLE_PMU=y), the two options this branch's mem-only sleep policy relies on, and 67 other integration merges since 84d091f7. config and fedora.config are unchanged. The resolved config passes validate-config.py with both options at y, and olddefconfig adds four new symbols at their defaults, listed in the changelog. Signed-off-by: Diljit Singh <hey@djdev.me>
Adds coordinated suspend-to-RAM for T8140 (MacBook Neo, J700), with Linux running directly at EL2. A sleep that is refused finishes unwinding before any irreversible SEP, GPU, PMP or ANS stop. After the SEP SLEEP request goes out, the path always completes a physical sleep and wake.
Ready for review. The soaked tree
b8f5b1e7passed its confirmation soak (below). This head adds two integration merges and the two commits that move the T8140 ISP to macOS's model. It merges once CI is green on this exact head and the Neo fast retest of it passes.What's in it
feat/s2rtargetsintegration/aurora. Head2573357fb92d, which contains integration608f76fd5068.Sleep stack and suspend speed:
d9b438c4: the squashed T8140 sleep stack (EL2 sleep provider, CPU, power, interrupt and memory state, device firmware stop and restart, kept USB host and DRAM, RTC and radio wake, display, audio, storage and camera recovery).2a57a36f,38e9c6de,3224a675: shorter debug-link and SMC waits before the commit (from soc: apple: reduce measured debug-link suspend entry waits #1437), and a debug-link reset gives up after 1.5 s.What macOS does at the commit, and what the DRAM-kept wake needs (the fixes for the ~1 in 60 reset):
fbe6f664dmaengine: apple-sio: run the T8140 SIO firmware without DMA service, and stop it in the sleep's last step, as macOS does.30c3dafdsleep: refuse a commit over an unbound coprocessor whose CPU still runs.adcdcbbcsleep: clear a power word left off with BUSY latched just before the commit; if one stays latched, take the deep path.d82906e2,05799966,0b5617bfpmdomain: apple: lower T8140 domains as macOS does (auto off first, then the target), count a domain off only once BUSY clears, and finish a loader-left power-off before a sleep.042933d1,60581160,0a80c389,7302a10b,ce8192d4,942c0088,589646b6,74b070cc,992923c9accel/ane: h17: take the engine down in macOS's order, stop its firmware before its power goes (STOP, then POWER_DOWN answered in SCRATCH0), send SET_SNE_PMU_BASE2 on the IO ring, deactivate the ANE DARTs before every ANE_SYS power-off, and keep the engine off with no firmware until its first request. A stuck power word sends that sleep deep instead of refusing it.c2b4f4b4iommu/apple-dart: the cold ANE DARTs runtime-suspend, so ANE_SYS stays off while the ANE has no client, as on macOS.5801e1f1,8ee99a76nvme-apple: send a sleep's shutdown notification at suspend, as macOS does, and wait as long as macOS (35 s). The 1 s limit at the commit refused 1 to 2 sleeps in 50 without the debug cable.Merges and review follow-ups:
f65bce91,db580751,970ec8e2: integration merges.970ec8e2resolves three conflicts: apple-dart resume skips cold ANE, live J613 MTP and consumer-saved DARTs; h16 session init keeps integration's dma-buf export state and this series' parked state; dcp-fabric applies integration's T8142 crossbar handling, and a route parked for a sleep skips the crossbar.78edcab1: merge of integrationf9f14be0, nine conflicts, plus T8142's new GPU config givensleep_restart: false, as every SoC but T8140. T8140's ISP keeps this series' soaked firmware model. media: apple: isp: Power the T8140 ISP down between streams #1580's T8132-style model isn't selected on T8140, and the rest of media: apple: isp: Power the T8140 ISP down between streams #1580 is guarded off there. The ISP's sensor domains stay after FE, listed once. admac: a resume with lost registers restarts from the last reclaimed period, otherwise integration's capture realign. aop: every configuration has both the sleep/wake endpoint orders and the setup-port EPMAP fields. asahi: bothg17/sleep.oandg17/pmpdash.o.2573357f: merge of integration608f76fd, five conflicts and one duplicate resume callback:macos_sleep, set on T8140 only, keeps the macOS-order power-down, the first-request firmware start, the park after a wake and the start-up host flags there. T6050 runs accel/ane: run the M5 Pro (T6050) Neural Engine on the H17 backend #1595's staged boot-time start and power-down unchanged.269dec72,a2ec625b,b8f5b1e7: review follow-ups, no functional change.T8140 camera ISP, macOS's model (from the macOS recordings and kernelcache,
isp-model-macos/README.md):7fedcbe2media: apple: isp: quiesce the T8140 coprocessor before its power-down, as macOS does before the core words go off. Without it, isp_cpu_core0/1 reach off with BUSY latched at every stream stop.e0549e4dmedia: apple: isp: keep the T8140 firmware up 5 s after a stream, as macOS does (5.07 s, measured twice). A stream in that window reuses it. A PM notifier stops idle firmware before any sleep, so a sleep still finds it off.Supersedes
This PR carries the final diff of #1208, #1217, #1265, #1266, #1278, #1285, #1310, #1313, #1333, #1334, #1358, #1374, #1377, #1393, #1398, #1437 and #1443. Those PRs can close once this one merges. #1303 (H17 ANE resume retry) is obsolete under it: the latch it recovers from no longer forms, and a resume no longer boots the ANE.
Hardware validation (Neo J700, production Stage 2, distro boot path)
Confirmation soak on
b8f5b1e7: kernel pr1445b =b8f5b1e7, no test patches. Production Stage 2, booted from the SSD with no upload, on the user path with no debug session.Fast retest of this head: running on the Neo now: ISP camera streams and the three ISP tests (runtime stops, sleeps right after camera use, the 5 s idle), ANE use and sleep, and a short DRAM-kept run. Merge waits for it and for CI on this exact head.
Earlier soaks on the same series, before the last commits:
8ee99a76, withoutc2b4f4b4and without integrationcdd7ccf5): 200/200 DRAM-kept sleeps, 150 without and 50 with the debug-link cut. 0 resets, 0 safety nets fired (PMGR latch clears, commit clears, ANE retries, deep fallbacks), 0 NVMe refusals. NVMe shutdown median 0.93 ms; one took 4.63 s, inside macOS's 35 s.ane_sysoff with BUSY latched (fast4e, PMU mark 0x51, SOCD on the ane_sys poll).Suspend speed with the debug cable attached:
systemctl suspendto the sleep commit went from 3.24 s to a median of 1.77 s. Without the debug cable, request to commit is a median of 0.68 s.Evidence:
migration/neo-continuation-20261108/final-validation/RESULT.md(top section) andfinal-validation/fast1/RESETS.md. Display wake-start latency is a follow-up (dcp-wake-macos/), not in this PR.Changed since the soaked tree
78edcab1merges integrationcdd7ccf5..f9f14be0into the soaked treeb8f5b1e7, and7fedcbe2,e0549e4dmove the T8140 ISP to macOS's model (quiesce, 5 s idle).2573357fmerges integrationf9f14be0..608f76fd: on the Neo, the ANE on per-SoC offsets (same values on T8140), media: apple: isp: contain IPC failures #1560's ISP IPC containment, SEP (soc: apple: sep: rate-limit the two verify messages from the fresh bring-up #1590, soc: apple: sep: bring the Touch ID sensor up afresh for each prompt #1536, soc: apple: sep: MacBook Air 15-inch M5 (J815) profile #1531) and rtkit-helper. It touches code the Neo runs: media: apple: isp: Power the T8140 ISP down between streams #1580 (T8140 ISP, resolved to the soaked model), HID: dockchannel-hid: write each frame to the FIFO under one lock #1010 (MTP TX lock), arm64: dts: apple: t8140-j700: enable uart0 #1574 (J700 uart0), J813: AOP sound on integration/aurora (speakers, jack, microphones) #1423 (admac capture realign, audio codecs), soc: apple: sep: rate-limit request-path log messages #1578 (SEP log rate limit), soc: apple: clpc: name the vote reasons and share the sample work #1555 and soc: apple: clpc: T6050: sample on time with no vote held #1556 (CLPC), crypto: apple: SIO AES engine skcipher driver for T6000 and T8112 #1572 (SIO AES), soc: apple: display-gate: enable T6041 external chains independently (fixes j614s external displays) #1551, t6041-j575c: USB-C display on typec1, USB3 and the fourth back Type-C port #1546 and t6041-j614c-j616c: USB 3 on the USB-C ports #1476 (display gate), and drm/asahi, drm/apple: J813 (M5 MacBook Air) GPU and display on the macOS 27.0 firmware #1413 (common G17 GPU changes). The Neo fast retest covers them.Known gaps
mt7932_bt_pcie.cbuilds with five clang lock-analysis warnings. They come from integration's own code (881448c4,6356a799,d561b4f9), not this PR.c0f59af9, "kboot: T8140: hand over whichever video encoder the ADT describes".parity-ave-avd/).apple_sep.sleep_refuse_before_sendcovers the supported refusal.Owner-only tests (physical)
Lid close and open, the power button, and keyboard wake.
🤖 Generated with Claude Code