Skip to content

J713: hold the memory and fabric floor for GPU work, not whenever GFX is powered - #582

Merged
erykwieliczko merged 6 commits into
aurora-silicon:integration/aurorafrom
erikwb:j713/g16-memory-floor
Nov 4, 2026
Merged

erykwieliczko merged 6 commits into
aurora-silicon:integration/aurorafrom
erikwb:j713/g16-memory-floor

Conversation

@erikwb

@erikwb erikwb commented Oct 29, 2026

Copy link
Copy Markdown
Contributor

Problem

On the J713 (MacBook Air 13" M4) the DRAM controller (DCS) and the SoC fabric run at their top states the whole time the system is awake, also at an idle desktop.

The T8132 CLPC votes the qualified M5/F3 floor (DCS 5, fabric 3) whenever the GFX power domain is on. The G16 firmware keeps GFX powered while the GPU idles, so that floor is never released.

Releasing it outright at an idle desktop cuts system power from 6.3 to 5.9 W on the same boot, with the 3.8 V SoC rail going from 1.93 to 1.55 W. The display is unaffected. But the floor is needed for GPU work: released outright, glmark2 drops from about 520 to 289.

Fix

T6040 has the same problem, with firmware that keeps GFX powered for its whole lifetime, and the CLPC already solves it there with a host input: the GPU driver reports its work through apple_clpc_gpu_set_active(), and that report, not the power state, drives the GPU's floor. This series uses the same path for T8132:

  1. soc: apple: clpc: Let the T8132 GPU driver report its work: accept the T8132 CLPC as a GPU-demand provider and allocate its input at probe. The floor still follows GFX power.
  2. drm/asahi: g16: Report work in flight to the CLPC: take the DT-selected CLPC's input (apple,clpc) at probe, as G17 does, and report whether work is in flight after each publication pass. Polling retires before it publishes, so the report sees work start and drain; a fail-stop reports from fail_flights(). Only changes are reported.
    • The handle is taken before the PMP vote, so a CLPC that hasn't probed yet defers the GPU before anything is set up, with the reason recorded.
    • The floor is a performance aid: without apple,clpc, or in a kernel built without the CLPC, the GPU runs without it (with a warning in the latter case). Kconfig keeps a CLPC that is built reachable from the GPU driver.
  3. arm64: dts: apple: t8132-j713: Link the GPU to its CLPC: apple,clpc on the J713 GPU node. The other T8132 boards leave the PMC disabled and get no link, so their GPU never waits for a CLPC that doesn't probe.
  4. soc: apple: clpc: Hold the T8132 GPU floor for reported GPU work: the T8132 table drops the GFX power-state rule and uses the reported work, as on T6040. The floor is held while work is in flight and released HOLD_MS after the last of it retires. CPU demand is unchanged. KUnit case for the T8132 input gate.

Each commit builds and leaves a working system: the reports exist from commit 2, arrive from commit 3 and are used from commit 4.

apple,gpu-power-state stays on the T8132 CLPC node for older kernels; nothing reads it for T8132 any more.

Testing

On a J713, running our J713 tree with integration's clpc.c and this series on top (integration/aurora alone doesn't bring up the J713 display yet):

Before (floor follows GFX power) After (floor follows reported work)
Idle desktop, floor held 100% of the time 5-12% (CPU bursts)
Idle desktop, system power 6.3 W about 0.45 W lower (same-boot A/B with the floor released)
glmark2 (six scenes, off-screen) 511 / 537 536 / 541, floor held 92% while running
Video playback, 1080p30 and 4K60 in mpv floor held floor held throughout, same GPU and system power
Suspend-to-idle and resume unaffected; reports continue after resume

Continuous rendering such as video or animations keeps work coming more often than HOLD_MS, so it holds the floor as before. The saving is at a desktop where nothing redraws, which is most of the time on a laptop.

  • KUnit: the apple-clpc-gpu suite, now 5 cases, passes under arm64 QEMU with lockdep.
  • Every commit builds with W=1 on the touched C files. CHECK_DTBS shows no new warnings for the J713 DTB, and dt_binding_check passes for apple,agx.yaml.
  • checkpatch --strict: clean, except three "externs should be avoided in .c files" warnings in g16/glue.c. Those follow the file's existing pattern for its Rust-called helpers.
  • rustfmt: clean on the changed Rust files.

Not covered

  • The other T8132 boards (J604, J623, J624, J715, J773g) don't enable the PMC, so they get no link and keep their current behaviour.
  • Intermediate DCS/fabric states for light GPU work. T8132 only has the qualified M5/F3 floor; nothing here invents a table.

This series was developed and tested by Claude (Anthropic), working with me on the J713.

@erikwb

erikwb commented Oct 29, 2026

Copy link
Copy Markdown
Contributor Author

cc @DjDeveloperr - what do you think of this clpc-gpu messaging mechanism?

erikwb and others added 6 commits November 4, 2026 10:38
The power work treats a reading of less than 0.1 W across the P-CPU,
E-CPU and GPU rails as a firmware reset image: it reports -ENODATA,
retains the minimum CPU and GPU ceilings and fails the hwmon rails with
-EAGAIN. An idle J713 reads below that once its GPU idles at a low
performance state (P-CPU 40-55 mW, E-CPU 9 mW, GPU 27 mW): the
ceilings then sat at the minimum while idle, the CPUs ran at 900 MHz,
and the GPU budget climbed back one state at a time once work arrived.

A reset image reads zero on both CPU rails, which the running reader
cannot. Reject only that.

Signed-off-by: Erik Bourget <erik@ebourget.net>
The firmware runs at the state the three Globals selectors publish,
busy or idle, and the host published the thermal ceiling: on the J713
the GPU sat at S9 (1470 MHz, 1000 mV) with nothing to draw. Its rail
drew 1.06 W idle and 1.25 W for a 60 fps desktop, against 68-90 mW
for the same desktop at S1-S3.

Request a state from the GPU's busy share of each 20 ms window, between
S3 and the thermal and user ceiling, which still bound it: the top state
when a window was at least 70% busy, one step up at 40%, and down on a
utilization averaged over about four windows, one step under 25% and to
S3 under 10%. Busy time runs from publication to drain. An idle engine
polls once more per window until it is back at S3. S3 draws no more than
S1 on a desktop load and runs at 2.4 times its clock; S4 and up cost
several times as much.

The accounting is policy::Governor, which the host policy test runs
directly: publication to drain, busy spans carried across windows, flights
dropped by recovery without a drain, the idle re-arm back to S3, and
thermal and user ceilings, including those below S3.

Paired on the J713, on battery with the backlight at half (idle 5 x 60 s,
three 60 Hz scenes 3 x 30 s each, the glmark2 suite 3 times): idle draw
7.32 -> 6.06 W, the GPU rail 1.09 W -> 27 mW. The 60 Hz build scene stays
at S3 with no missed frame, and the glmark2 score is 574 -> 573.

Signed-off-by: Erik Bourget <erik@ebourget.net>
[eryk: don't re-arm the idle poll on a failed engine: its poll returns
 before service_power(), so the demand never decays, and an engine that
 failed while idle above S3 re-armed its poll every 20 ms until reboot
 once anything kicked it; the host test's call-site check follows]
Signed-off-by: Eryk Wieliczko <eryk@wieliczko.ninja>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
T8132's G16 firmware keeps GFX powered while the GPU idles, as T6040's
does for its whole lifetime, so the GFX power state says nothing about GPU
work. Let a T8132 GPU driver take the CLPC's host input, as on T6040 and
T8140: accept the T8132 CLPC as a provider, and allocate the input at
probe.

The T8132 floor still follows the GFX power state; a later change makes it
follow the reported work.

Signed-off-by: Erik Bourget <erik@ebourget.net>
The G16 firmware keeps GFX powered while the GPU idles, so a host
performance controller cannot tell GPU work from its power state. Take the
DT-selected CLPC's input (apple,clpc) at probe, as G17 does, and report
whether work is in flight. Polling retires finished work before it
publishes, so reporting after each publication pass sees work start and
drain; a fail-stop, which empties the flights without another pass,
reports from fail_flights(). Only changes are reported.

The handle is taken before the PMP vote, so a CLPC that has not probed yet
defers the GPU before anything is set up, with the reason recorded; any
other error is reported.

The floor is a performance aid: without apple,clpc, or in a kernel built
without the CLPC, the GPU runs without one, with a warning in the latter
case. Kconfig keeps a CLPC that is built reachable from the GPU driver.

Signed-off-by: Erik Bourget <erik@ebourget.net>
The J713 enables the PMC and its CLPC. Point the GPU at that CLPC with
apple,clpc, so the GPU driver reports its work there.

The link stays on the J713: the other T8132 boards leave the PMC disabled,
and a GPU linked to a CLPC that never probes would wait for it.

Signed-off-by: Erik Bourget <erik@ebourget.net>
The T8132 CLPC votes the qualified M5/F3 floor (DCS 5, fabric 3) whenever
GFX is powered. The G16 firmware keeps GFX powered while the GPU idles, so
on a J713 the DRAM controller and the fabric stay at their top states the
whole time the system is awake, also at an idle desktop.

Take the GPU's share of the floor from the work the GPU driver reports
instead, as on T6040: the floor is held while work is in flight, and
released HOLD_MS after the last of it retires. CPU demand is unchanged.
Continuous rendering, such as video playback or animations, keeps work
coming more often than HOLD_MS and holds the floor as before; the saving is
at a desktop where nothing redraws.

On a J713:
- at an idle desktop the floor is released about 90% of the time, and
  system power drops by about 0.45 W, from 6.3 to 5.9 W on the same boot;
- under glmark2 the floor is held while the GPU works and the score is
  unchanged. With the floor released outright it drops by about 45%, so
  GPU work does need it;
- video playback holds the floor throughout, as before;
- suspend-to-idle and resume are unaffected.

The GPU's floor now depends on the J713 device tree linking the GPU to the
CLPC (apple,clpc): without that link nothing reports GPU work and it gets
no floor. The J713 device tree is built and shipped with its kernel. It
keeps apple,gpu-power-state on the T8132 CLPC for older kernels; it is no
longer used. A KUnit case covers the T8132 input gate.

Signed-off-by: Erik Bourget <erik@ebourget.net>
[eryk: follow the change in the host model, tools/asahi/clpc-gpu-demand-tests.py,
 which failed to build: extract apple_clpc_gpu_input_demand(), aim the
 omit-host-demand mutation at its call, model T8132 on reported work, and
 move the read-error check (bf86f72) to T8140, the table that still
 reads the GFX power state]
Signed-off-by: Eryk Wieliczko <eryk@wieliczko.ninja>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@erykwieliczko

Copy link
Copy Markdown
Member

Merging with a test fix and a conflict resolved. This comment is from Claude Opus 5.5, working for Eryk, not from Eryk himself.

Rebased onto integration/aurora 4b948209c61e and force-pushed, as Erik agreed. It now sits on #455, because both add a paragraph to the power.rs module comment. Both paragraphs are kept.

Changes

  • soc: apple: clpc: Hold the T8132 GPU floor for reported GPU work: tools/asahi/clpc-gpu-demand-tests.py no longer compiled. It now extracts apple_clpc_gpu_input_demand(), models T8132 on reported work, and the read-error check moved to T8140. It passes, with all 9 negative controls killed.

Open

The question to DjDeveloperr about the CLPC-GPU messaging is still unanswered.

Testing


🤖 Written by Claude Opus 5.5 (in Claude Code) for Eryk.

@erykwieliczko
erykwieliczko merged commit 7d581f6 into aurora-silicon:integration/aurora Nov 4, 2026
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.

2 participants