Repository navigation
Neo: 0.21 s faster wake from DRAM-kept sleep, and the PHY set up again after a deep sleep - #1658
Merged
Merged
Conversation
At a wake, the radio's link waited 100 ms after PERST# before the training started and another 100 ms after the link came up: about 230 ms of the noirq resume, on the main thread. macOS starts the training about 2 ms after it releases PERST#. The 100 ms of PCIe r5.0 section 6.6.1 runs from PERST# to the first configuration request on a port of 5 GT/s or less, as this one is. Start the training at once and wait out what is left of the 100 ms after the link is up, before the bootstrap's configuration read. Signed-off-by: Diljit Singh <hey@djdev.me>
The SMC's noirq resume reads its record of what woke it. Keep whether that record has the Wi-Fi/Bluetooth source, as the lid is kept, so the radios' drivers know at their resume whether a wake report from the controller is coming. A record read as zero, as after the PMU's RTC woke the AP, has no SMC source at all, the radios' included. Forgotten as each sleep begins, and unknown after a suspend-to-idle or when the record couldn't be read. Signed-off-by: Diljit Singh <hey@djdev.me>
After a sleep with Bluetooth wake armed, the resume waited up to 200 ms for the controller's wake report. The controller sends one only when it woke the host, so every other wake (the lid, a key, the power button, the RTC) spent the full 200 ms there, the longest device resume of a wake. Wait only when the SMC's record of the wake has the radios' source, or when it wasn't read. Signed-off-by: Diljit Singh <hey@djdev.me>
A sleep that reaches its commit lowers ATC0_COMMON under a PHY that
didn't sleep in place, behind its power domain's back. The resume
had the mode set up again only for the DisplayPort modes.
With the USB controller unbound, the domain stays on as far as genpd
knows, and the PHY idles in mode off. A deep sleep then cleared the
PIPE's dummy PHY enable (NONSELECTED_OVERRIDE read 0x5), which the
driver still took for parked. When the controller was bound again,
its PIPE lock wasn't acknowledged ("Pipehandler lock not acked",
"Host starts with its PIPE not parked"), the PIPE stayed pending,
and the next sleep couldn't keep the host in place, so it went deep
as well.
Do the same for every mode: the next PHY operation sets the PHY up
as after its domain was off.
Signed-off-by: Diljit Singh <hey@djdev.me>
This was referenced Nov 12, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
On a wake from a DRAM-kept sleep, the Neo (J700) spent about 0.5 s resuming devices before the freezer let userspace go. Two waits made up most of it. This takes 0.21 s off the wake. It also fixes the case where the sleep after a dwc3 rebind couldn't keep the USB host in place and went deep.
Commits (on integration
69c201ca)26a82aa6PCI: apple: train the Neo's radio link right after PERST#cbdf6b4bsoc: apple: sleep: say whether the radios woke the SMCapple_sleep_radio_woke()is set from the SMC's MBSW record in its noirq resume: 1 if it haswifibt, 0 if it doesn't,-ENODATAif it wasn't read (or after suspend-to-idle).8cd18d66Bluetooth: mt7932: wait for a wake report only when the radio woke5a62cab9phy: apple: atc: set the PHY up again after any committed sleepNONSELECTED_OVERRIDEread0x5). The driver still took the PIPE for parked.Measured on the Neo (J700), 2026-11-12
The four commits were tested on integration
2b05915f(headfb50f989). They were then moved onto69c201ca. Each file's added and removed lines are identical; the only conflict was the context next to #1631's lid helpers insleep.c/sleep.h. The kernel was installed over SSH and booted from the SSD without an upload. The baseline is the2573357fdefault on the same Neo.Per-device resume callbacks (power trace events, median of 3 RTC wakes):
dpm_resume_noirqphase1cb0000000.pcie(main thread)dpm_resumephase0000:01:00.1(async, critical path)Wake timing, seconds after the PMU RTC alarm on the ARM generic counter (medians: 12 wakes before, 6 with this PR):
The display rows are today's path, without #1631; #1631 moves those earlier on its own.
Safety, same boot:
40a280000.usb, one deep sleep, rebind. No PIPE lock error at the rebind, and the next 2 sleeps were both DRAM-kept.Not covered: a Bluetooth wake from a DRAM-kept sleep. That path still waits for the report as before, and returns as soon as it comes.
Left
cs42l83 3-0048is now the longest device resume, at 101 ms async. Kprobes put 94 ms of it inregcache_sync, over i2c@385010000 at the driver's 100 kHz default. The ADT gives no faster speed to copy, so this PR leaves it.🤖 Generated with Claude Code