Skip to content

Neo: 0.21 s faster wake from DRAM-kept sleep, and the PHY set up again after a deep sleep - #1658

Merged
DjDeveloperr merged 4 commits into
integration/aurorafrom
fix/t8140-resume-trim
Nov 12, 2026
Merged

DjDeveloperr merged 4 commits into
integration/aurorafrom
fix/t8140-resume-trim

Conversation

@DjDeveloperr

Copy link
Copy Markdown
Contributor

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)

  • 26a82aa6 PCI: apple: train the Neo's radio link right after PERST#
    • The radio's link waited 100 ms after PERST# before training, then another 100 ms after link up: 230 ms of the noirq resume, on the main thread.
    • Boot waits only the first. macOS starts the training about 2 ms after it releases PERST# (hooked trace).
    • PCIe r5.0 section 6.6.1: on a port of 5 GT/s or less (this one is 5 GT/s), the 100 ms runs from PERST# to the first configuration request.
    • Now the training starts at once. What's left of the 100 ms is waited out after link up, before the bootstrap's configuration read.
  • cbdf6b4b soc: apple: sleep: say whether the radios woke the SMC
    • apple_sleep_radio_woke() is set from the SMC's MBSW record in its noirq resume: 1 if it has wifibt, 0 if it doesn't, -ENODATA if it wasn't read (or after suspend-to-idle).
    • A record read as zero counts as 0. That's what a PMU RTC wake gives: no SMC source at all.
  • 8cd18d66 Bluetooth: mt7932: wait for a wake report only when the radio woke
    • 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 lid, key, power-button and RTC wake spent the full 200 ms there. That was the longest device resume of a wake.
    • It now waits only when the SMC's record has the radios' source, or wasn't read.
  • 5a62cab9 phy: apple: atc: set the PHY up again after any committed sleep
    • A sleep that reaches its commit lowers ATC0_COMMON under a PHY that didn't sleep in place, behind genpd's back. The resume set the mode up again only for DisplayPort modes.
    • With dwc3 unbound, genpd keeps the domain on, and a deep sleep cleared the PIPE's dummy-PHY enable (NONSELECTED_OVERRIDE read 0x5). The driver still took the PIPE for parked.
    • At the rebind the PIPE lock wasn't acknowledged ("Host starts with its PIPE not parked"), the PIPE stayed pending, and the next sleep stopped the host and went deep.
    • Now every mode is set up again by the next PHY operation, as after its domain was off.

Measured on the Neo (J700), 2026-11-12

The four commits were tested on integration 2b05915f (head fb50f989). They were then moved onto 69c201ca. Each file's added and removed lines are identical; the only conflict was the context next to #1631's lid helpers in sleep.c/sleep.h. The kernel was installed over SSH and booted from the SSD without an upload. The baseline is the 2573357f default on the same Neo.

Per-device resume callbacks (power trace events, median of 3 RTC wakes):

before this PR
dpm_resume_noirq phase 243.9 ms 138.1 ms
pcie-apple 1cb0000000.pcie (main thread) 230.1 ms 124.4 ms
dpm_resume phase 262.1 ms 157.6 ms
mt7932_bt_pcie 0000:01:00.1 (async, critical path) 210.5 ms 3.8 ms

Wake timing, seconds after the PMU RTC alarm on the ARM generic counter (medians: 12 wakes before, 6 with this PR):

before this PR
Linux back 0.014 0.014
Suspend exit 0.530 0.319
Internal DCP firmware up 0.596 0.378
KWin's mode set 0.653 0.436
Backlight on 0.873 0.657

The display rows are today's path, without #1631; #1631 moves those earlier on its own.

Safety, same boot:

  • 25 DRAM-kept sleeps: 25/25 returned, all DRAM-kept. No commit latch, deep fallback, refusal or reset. No radio, transport or PIPE error.
  • Wi-Fi pinged and Bluetooth was powered at boot, after the traces and wakes, after the 25 sleeps, and at the end.
  • dwc3 case: unbind 40a280000.usb, one deep sleep, rebind. No PIPE lock error at the rebind, and the next 2 sleeps were both DRAM-kept.
    • Before this PR, the same sequence gave "Pipehandler lock not acked" and "Host starts with its PIPE not parked" at the rebind, and the next sleep went deep.
  • All 25 RTC wakes logged an all-zero MBSW record, so Bluetooth didn't wait.

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-0048 is now the longest device resume, at 101 ms async. Kprobes put 94 ms of it in regcache_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

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

1 participant