Deep sleep is blocked by a 5.3k/s tick inside the frozen window, not by a missing RPM voter
scope: device:google-taimen · severity: finding · confidence: proven · subsystem: power
The question — qcom_stats has always read vmin Count: 0 and
vlow Count: 0, and the standing explanation was that mainline msm8998 has no
RPM sleep-set handshake at all, so deep sleep “cannot be fixed here”. Is that
true?
The answer — no. The handshake is there and the AP does reach system power collapse. What stops RPM is that the AP will not stay down: the timer tick fires about 5,300 times a second inside the frozen suspend window, so RPM never gets a quiet interval in which to swap to its sleep sets.
Three measurements, each replacing a wrong one —
-
system-pcis entered. It shipsdisable=1; enable it for one cycle and the counter to read isstate3/s2idle/usage, notstate3/usage–enter_s2idle_proper()incrementss2idle_usageands2idle_timeonly, sousagestays 0 no matter how many times s2idle enters the state. Read the right one and it is ~1.38M entries per CPU with ~49.8 s of residency, whilestate2/s2idle/usageis 0. Readingusageis what produced the earlier “never entered” conclusion. -
The wakes are the tick.
/proc/interruptsdifferenced across a 120 s sleep:660852 arch_timer,237 glink-rpm,1712 IPI1, everything else in the tens.events/timer/hrtimer_expire_entryover a 30 s sleep: 163158 of 163239 events aretick_nohz_handler, on<idle>. -
They are inside the frozen window, not loop churn around it. Counting
events/irq/irq_handler_entryonly betweenmachine_suspendbegin and end:160056 arch_timer28 IPIOver 30 s, against
CONFIG_HZ=1000on 8 CPUs. This directly refutes the “8 IPIs and 2 arch_timer inside the window” figure recorded earlier.
The mechanism — the idle loop’s s2idle branch goto exit_idles past
tick_nohz_idle_stop_tick(), so nothing puts the CPU into dynticks-idle; s2idle
relies on tick_freeze() instead, and tick_freeze() only suspends timekeeping
once all online CPUs are in it simultaneously. With eight CPUs and a 1 ms
tick they do not converge: each tick_unfreeze() leaves a tick armed that
becomes the next wake, which is self-sustaining at roughly HZ.
What this rules out —
- “No RPM sleep-set handshake exists, so this is separate work.” The state, the DT, and the handshake are in the tree (patches 0144, 0148, 0150, 0151).
- “The idle state is never entered.” It is, with real residency.
- “Some master has not voted sleep.” Possible but unproven and not the first problem: the AP’s own residency is ~36 us average against a 25 ms min-residency, so nothing downstream ever gets a chance.
Where to go next — make the tick stop rather than be re-armed: fewer online
CPUs across suspend, a lower HZ, or getting the s2idle path to stop the tick.
Do not chase RPM voters until the AP stays down longer than system-pc’s
25 ms min-residency. And note that system-pc is deliberately
idle-state-disabled-by-default because XO shutdown stops the QTIMER, so only
an MPM pin (PMIC PON, RTC alarm via SPMI pin 87) can wake it – it is meant to
be enabled around s2idle by a hook, which does not exist yet.
