Power management: low-power sleep (s2idle) investigation #3

Open
opened 2026-08-20 13:17:10 +00:00 by kit · 1 comment
Owner

Goal: usable battery life via suspend. Constraints discovered so far:

  • Samsung's PSCI is nonstandard (psci: failed to set PC mode: -3 every boot), so deep PSCI suspend states may be off-limits; s2idle (suspend-to-idle) with cpuidle + RPMh sleep/wake state voting is the realistic first target.
  • cpuidle currently runs (cpuidle.off crutch long dropped); need to check which idle states actually enter (/sys/devices/system/cpu/cpu*/cpuidle/state*/usage).
  • RPMh sleep votes need the sync_state situation resolved (chronic sync_state() pending for gcc/rpmhpd/gpucc due to gmu/dispcc consumers) — related to the regulator_ignore_unused crutch still in the cmdline; proper rail ownership is a prerequisite.
  • Watchdogs must be suspend-aware (APSS watchdog + our boot/reachability layers) or a sleeping tablet gets rebooted.
  • Measure baseline idle power first (battery fuel gauge readable?) so improvements are quantifiable.
Goal: usable battery life via suspend. Constraints discovered so far: - Samsung's PSCI is nonstandard (`psci: failed to set PC mode: -3` every boot), so deep PSCI suspend states may be off-limits; s2idle (suspend-to-idle) with cpuidle + RPMh sleep/wake state voting is the realistic first target. - cpuidle currently runs (cpuidle.off crutch long dropped); need to check which idle states actually enter (`/sys/devices/system/cpu/cpu*/cpuidle/state*/usage`). - RPMh sleep votes need the `sync_state` situation resolved (chronic `sync_state() pending` for gcc/rpmhpd/gpucc due to gmu/dispcc consumers) — related to the `regulator_ignore_unused` crutch still in the cmdline; proper rail ownership is a prerequisite. - Watchdogs must be suspend-aware (APSS watchdog + our boot/reachability layers) or a sleeping tablet gets rebooted. - Measure baseline idle power first (battery fuel gauge readable?) so improvements are quantifiable.
Author
Owner

Correction: the sync_state prerequisite in the description is mostly a non-issue

The description lists "RPMh sleep votes need the sync_state situation resolved (chronic sync_state() pending for gcc/rpmhpd/gpucc due to gmu/dispcc consumers)" as a prerequisite. Investigated 2026-08-25 — it is much smaller than it looks, and two of the three parts are not real.

gcc and gpucc have no sync_state callback at all. gcc-sm8150.c and gpucc-sm8150.c define plain platform_drivers with only .probe; nothing in drivers/clk/qcom/ sets .sync_state except the ipq parts. So there is nothing being skipped for those two — the warning is noise.

Unused clocks and power domains are already being cleaned up, just by a different mechanism than sync_state. Both run normally at 0.65 s:

[    0.652567] clk: Disabling unused clocks
[    0.652840] PM: genpd: Disabling unused power domains

That is clk_disable_unused / genpd_power_off_unused, which are global late_initcalls and unrelated to sync_state. (Note the current debug image passes clk_ignore_unused pd_ignore_unused, which suppresses them — but that is a deliberate bring-up flag, not a defect.)

Only rpmhpd genuinely has a sync_state, and we deliberately stubbed it out — commit 11c730d573b7, with the reason recorded in the code:

/*
 * gts6l BRING-UP HACK - remove when real consumers exist. With this
 * minimal DT "all consumers probed" arrives while the display scanout
 * and most of the SoC still run on XBL's boot votes; releasing them
 * here collapses the corners mid-boot (crash right after the last
 * initcalls, matching the 6.14-era "dies after Btrfs loaded" note).
 */

So RPMh power-domain corners staying at XBL's boot values is our own choice, made to avoid a specific crash — not an accident of fw_devlink.

Why it never fires anyway: the pending consumers are 2c6a000.gmu and ad00000.clock-controller. Both are permanently unbound — 0xad00000 is camcc, which we have no driver for, and the a6xx GPU driver uses the GMU node directly without binding a driver to it. Neither will ever probe, so sync_state can never run regardless of the stub.

What this means for this issue

The real prerequisite is narrower than written: rpmhpd needs to be able to apply real corners, which means un-stubbing rpmhpd_sync_state() and having enough real consumers in the DT that releasing XBL's votes does not collapse the SoC. fw_devlink.sync_state=timeout would force it to fire, and is a cheap way to find out what breaks — worth trying once the display situation (#17) is settled, since the stub's comment blames the display scanout specifically.

regulator_ignore_unused in the cmdline is a separate crutch and still stands as written.

Also relevant to the baseline-power measurement in the description: the fuel gauge (#9) is a pure SM5705 I2C port — pm8150b is not fitted on this board, so there is no PMIC ADC route to battery current either.

## Correction: the `sync_state` prerequisite in the description is mostly a non-issue The description lists *"RPMh sleep votes need the `sync_state` situation resolved (chronic `sync_state() pending` for gcc/rpmhpd/gpucc due to gmu/dispcc consumers)"* as a prerequisite. Investigated 2026-08-25 — it is much smaller than it looks, and two of the three parts are not real. **gcc and gpucc have no `sync_state` callback at all.** `gcc-sm8150.c` and `gpucc-sm8150.c` define plain `platform_driver`s with only `.probe`; nothing in `drivers/clk/qcom/` sets `.sync_state` except the ipq parts. So there is nothing being skipped for those two — the warning is noise. **Unused clocks and power domains are already being cleaned up**, just by a different mechanism than sync_state. Both run normally at 0.65 s: ``` [ 0.652567] clk: Disabling unused clocks [ 0.652840] PM: genpd: Disabling unused power domains ``` That is `clk_disable_unused` / `genpd_power_off_unused`, which are global late_initcalls and unrelated to `sync_state`. (Note the current debug image passes `clk_ignore_unused pd_ignore_unused`, which suppresses them — but that is a deliberate bring-up flag, not a defect.) **Only `rpmhpd` genuinely has a `sync_state`, and we deliberately stubbed it out** — commit `11c730d573b7`, with the reason recorded in the code: ```c /* * gts6l BRING-UP HACK - remove when real consumers exist. With this * minimal DT "all consumers probed" arrives while the display scanout * and most of the SoC still run on XBL's boot votes; releasing them * here collapses the corners mid-boot (crash right after the last * initcalls, matching the 6.14-era "dies after Btrfs loaded" note). */ ``` So RPMh power-domain corners staying at XBL's boot values is our own choice, made to avoid a specific crash — not an accident of fw_devlink. **Why it never fires anyway:** the pending consumers are `2c6a000.gmu` and `ad00000.clock-controller`. Both are permanently unbound — `0xad00000` is **camcc**, which we have no driver for, and the a6xx GPU driver uses the GMU node directly without binding a driver to it. Neither will ever probe, so `sync_state` can never run regardless of the stub. ### What this means for this issue The real prerequisite is narrower than written: **rpmhpd needs to be able to apply real corners**, which means un-stubbing `rpmhpd_sync_state()` *and* having enough real consumers in the DT that releasing XBL's votes does not collapse the SoC. `fw_devlink.sync_state=timeout` would force it to fire, and is a cheap way to find out what breaks — worth trying once the display situation (#17) is settled, since the stub's comment blames the display scanout specifically. `regulator_ignore_unused` in the cmdline is a separate crutch and still stands as written. Also relevant to the baseline-power measurement in the description: the fuel gauge (#9) is a pure SM5705 I2C port — pm8150b is not fitted on this board, so there is no PMIC ADC route to battery current either.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
kit/linux-gts6l#3
No description provided.