PS5 Slim (CFI-2015): autoservo table never uploaded and boot target temp does not take — console runs 11.7 C hotter than intended #32

Open
opened 2026-08-03 17:24:26 -05:00 by BrenoRippel · 1 comment
BrenoRippel commented 2026-08-03 17:24:26 -05:00 (Migrated from github.com)

Summary

On a PS5 Slim (CFI-2015 B01X) the fan autoservo table is never uploaded, and the
boot-time target temperature write reports success but does not take effect. The
console ends up running with an EMC fan setpoint of 91 °C instead of the
intended 80 °C.

Measured cost: 11.7 °C mean / 14.2 °C peak under real game load. Peak
temperature was 94.25 °C against a Zen 2 TjMax of 95 °C — i.e. 0.75 °C of
headroom.

Setting the setpoint by hand via the existing target_temp sysfs attribute
fixes it completely and costs nothing in performance.

Environment

Console PlayStation 5 Slim, CFI-2015 B01X, firmware 7.61
Kernel linux-ps5 7.1.3-1
Image Bazzite 44.20260629.0, built from the mia fork (git.etawen.dev)
APU Ariel — Zen 2 (family 23, model 72) + CYAN_SKILLFISH gfx1013
Sensor k10temp / Tctl (the only temperature sensor present; no SMU)

Boot log:

FanServo: D-Eiger/Denali Unknown Vendor/Revision 0x1/0xb9
FanServo: Not update

Bug 1 — GDDR6 revision is decoded without a nibble mask

In icc_fan_update_autoservo_param():

bootparam_get_config_gddr6_id(&gddr6_id);   /* readw(bootparam + 0x1184) */
gddr6_vendor   = gddr6_id & 0xf;            /* -> 0x1  = GDDR6_SAMSUNG, correct */
gddr6_revision = gddr6_id >> 4;             /* -> 0xb9, matches nothing          */

The board is identified correctly as D-Eiger/Denali (board_id decoding is fine
— it uses be64_to_cpu). The vendor nibble is also correct. Only the revision
fails to match, so control reaches goto not_updated and the autoservo table is
never uploaded.

Every revision the match table tests against is a single nibble (0, 1, 2, 8, 9),
and the vendor field is masked to a nibble. Masked the same way, 0xb9 & 0xf is
9 — and D-Eiger/Denali + Samsung + revision 9 maps to
autoservo_param_d_eiger_denali_samsung_d1z, which already exists in the tree.

Equivalently, the field may be a single byte at 0x1184 (0x91 → vendor 1,
revision 9) and readw is pulling in an unrelated neighbouring byte. Both
readings give revision 9.

Caveat, stated honestly: I tested this. I uploaded
autoservo_param_d_eiger_denali_samsung_d1z directly via the exported
icc_fan_update_param() from an out-of-tree module. The EMC accepted it
(returned 0) but there was no measurable thermal change — a controlled A/B
against an otherwise identical run gave an identical 86.51 °C mean. So please
treat the missing mask as a hypothesis rather than a confirmed fix. The table
genuinely is never uploaded, which seems worth fixing regardless, but it is not
what was causing the temperature problem.

Note also that on phat consoles the unmasked shift evidently works, so bits 8-11
must be zero there and are 0xb here. Either those bits are a new field on Slim
or the layout moved — someone with the bootparam layout can settle this
instantly.

Secondary: the failure is invisible

icc_fan_update_autoservo_param() returns 0 on the not_updated path, so
this in ps5_fan_probe() never fires:

int ret = icc_fan_update_autoservo_param();
if (ret)
        dev_warn(&pdev->dev, "autoservo param load failed: %d\n", ret);

A complete failure to configure the fan is reported as success. Only the
pr_info("FanServo: Not update\n") hints at it.


Bug 2 — boot-time fan_set_target_temp() reports success but does not take

This is the one that costs the 11.7 °C.

#define PS5_FAN_BOOT_TARGET_TEMP  80

ret = fan_set_target_temp(PS5_ZONE_MAINSOC, 0, PS5_FAN_BOOT_TARGET_TEMP);
if (ret)
        dev_warn(&pdev->dev, "boot target temp set failed: %d\n", ret);

This returns success — there is no boot target temp set failed anywhere in
dmesg — and yet:

$ cat /sys/devices/platform/ps5-fan/target_temp
91

The EMC holds 91 °C and does so accurately. All the "the fan doesn't work"
symptoms on this console were actually a working servo tracking a setpoint
11 °C higher than the driver asked for.

Runtime writes to the same attribute do work. 91 → 75 → 60 → 75 all stuck
and all took effect within seconds, with the fan audibly responding. So the ICC
curset path is functional; something about boot time is not. Possibly ordering —
this runs immediately after the autoservo step, which had just failed.


Measurements

Three runs of the same game (007 First Light), same chapter section, same
settings (1440p, FSR Quality, all Low), boost ON, 2 Hz sampling. The only
variable is the fan setpoint.

run A run B run C
target_temp 91 (default) 91 (default) 75 (set by hand)
autoservo table none D1z uploaded D1z uploaded
in-game minutes 13.5 14.5 19.7
mean CPU temp 86.51 °C 86.51 °C 74.80 °C
median 89.50 °C 89.25 °C 75.00 °C
max 94.25 °C 94.00 °C 80.00 °C
samples > 90 °C 39.9 % 34.8 % 0.0 %
samples > 80 °C 81.3 % 86.0 % 0.0 %

Run C's median landed on exactly 75.00 — the servo tracks its setpoint
precisely once it has the right one.

Frametimes were statistically unchanged across all three (median 17.6 / 17.2 /
17.9 ms; >100 ms stalls per minute 0.72 / 0.88 / 0.64), so the 94 °C peaks were
not causing HTC throttling and the improvement is free.

Note run A vs run B: uploading the autoservo table changed nothing. Run B vs
run C is the meaningful comparison.


Suggested fixes

  1. Mask the revision nibble — gddr6_revision = (gddr6_id >> 4) & 0xf; (or
    readb). Offered as a hypothesis; see the caveat above.
  2. Make both failures visible. Have icc_fan_update_autoservo_param()
    return an error on the not_updated path so the existing dev_warn fires,
    and verify the target-temp write by reading it back.
  3. Re-apply or retry the boot target temp, since the value demonstrably
    sticks later at runtime.

Documentation gap

/sys/devices/platform/ps5-fan/target_temp is the only fan interface on this
console that works, and it is undocumented — it is not mentioned in
ps5_control's usage text or in the ps5-linux guide. Users reach for
ps5_control --fan, which is a silent no-op here, and reasonably conclude the
fan cannot be controlled at all. I spent two days on that conclusion before
finding this attribute.

Documenting it, and/or adding a ps5_control --target-temp <celsius> wrapper,
would likely help a lot of Slim owners. (The ps5_control --fan no-op is a
separate issue against ps5-linux-tools.)

Workaround for other Slim owners

$ echo 75 | sudo tee /sys/devices/platform/ps5-fan/target_temp

Takes effect immediately, does not persist across reboot. Lower is cooler and
louder; on this console the fan saturates around a 75 °C target under heavy
load (peaks reached 80 °C), so ~75 is roughly the practical floor.

## Summary On a PS5 Slim (CFI-2015 B01X) the fan autoservo table is never uploaded, and the boot-time target temperature write reports success but does not take effect. The console ends up running with an EMC fan setpoint of **91 °C** instead of the intended 80 °C. Measured cost: **11.7 °C mean / 14.2 °C peak** under real game load. Peak temperature was 94.25 °C against a Zen 2 TjMax of 95 °C — i.e. 0.75 °C of headroom. Setting the setpoint by hand via the existing `target_temp` sysfs attribute fixes it completely and costs nothing in performance. ## Environment | | | |---|---| | Console | PlayStation 5 Slim, CFI-2015 B01X, firmware 7.61 | | Kernel | `linux-ps5` 7.1.3-1 | | Image | Bazzite 44.20260629.0, built from the `mia` fork (git.etawen.dev) | | APU | Ariel — Zen 2 (family 23, model 72) + CYAN_SKILLFISH gfx1013 | | Sensor | `k10temp` / Tctl (the only temperature sensor present; no SMU) | Boot log: ``` FanServo: D-Eiger/Denali Unknown Vendor/Revision 0x1/0xb9 FanServo: Not update ``` --- ## Bug 1 — GDDR6 revision is decoded without a nibble mask In `icc_fan_update_autoservo_param()`: ```c bootparam_get_config_gddr6_id(&gddr6_id); /* readw(bootparam + 0x1184) */ gddr6_vendor = gddr6_id & 0xf; /* -> 0x1 = GDDR6_SAMSUNG, correct */ gddr6_revision = gddr6_id >> 4; /* -> 0xb9, matches nothing */ ``` The board is identified correctly as `D-Eiger/Denali` (board_id decoding is fine — it uses `be64_to_cpu`). The vendor nibble is also correct. Only the revision fails to match, so control reaches `goto not_updated` and the autoservo table is never uploaded. Every revision the match table tests against is a single nibble (0, 1, 2, 8, 9), and the vendor field is masked to a nibble. Masked the same way, `0xb9 & 0xf` is **9** — and `D-Eiger/Denali` + Samsung + revision 9 maps to `autoservo_param_d_eiger_denali_samsung_d1z`, which already exists in the tree. Equivalently, the field may be a single byte at `0x1184` (`0x91` → vendor 1, revision 9) and `readw` is pulling in an unrelated neighbouring byte. Both readings give revision 9. **Caveat, stated honestly:** I tested this. I uploaded `autoservo_param_d_eiger_denali_samsung_d1z` directly via the exported `icc_fan_update_param()` from an out-of-tree module. The EMC accepted it (returned 0) but there was **no measurable thermal change** — a controlled A/B against an otherwise identical run gave an identical 86.51 °C mean. So please treat the missing mask as a hypothesis rather than a confirmed fix. The table genuinely is never uploaded, which seems worth fixing regardless, but it is not what was causing the temperature problem. Note also that on phat consoles the unmasked shift evidently works, so bits 8-11 must be zero there and are `0xb` here. Either those bits are a new field on Slim or the layout moved — someone with the bootparam layout can settle this instantly. ### Secondary: the failure is invisible `icc_fan_update_autoservo_param()` **returns 0** on the `not_updated` path, so this in `ps5_fan_probe()` never fires: ```c int ret = icc_fan_update_autoservo_param(); if (ret) dev_warn(&pdev->dev, "autoservo param load failed: %d\n", ret); ``` A complete failure to configure the fan is reported as success. Only the `pr_info("FanServo: Not update\n")` hints at it. --- ## Bug 2 — boot-time `fan_set_target_temp()` reports success but does not take **This is the one that costs the 11.7 °C.** ```c #define PS5_FAN_BOOT_TARGET_TEMP 80 ret = fan_set_target_temp(PS5_ZONE_MAINSOC, 0, PS5_FAN_BOOT_TARGET_TEMP); if (ret) dev_warn(&pdev->dev, "boot target temp set failed: %d\n", ret); ``` This returns success — there is no `boot target temp set failed` anywhere in dmesg — and yet: ```console $ cat /sys/devices/platform/ps5-fan/target_temp 91 ``` The EMC holds 91 °C and does so accurately. All the "the fan doesn't work" symptoms on this console were actually a *working* servo tracking a setpoint 11 °C higher than the driver asked for. **Runtime writes to the same attribute do work.** 91 → 75 → 60 → 75 all stuck and all took effect within seconds, with the fan audibly responding. So the ICC curset path is functional; something about boot time is not. Possibly ordering — this runs immediately after the autoservo step, which had just failed. --- ## Measurements Three runs of the same game (007 First Light), same chapter section, same settings (1440p, FSR Quality, all Low), boost ON, 2 Hz sampling. The only variable is the fan setpoint. | | run A | run B | run C | |---|---|---|---| | `target_temp` | 91 (default) | 91 (default) | **75 (set by hand)** | | autoservo table | none | D1z uploaded | D1z uploaded | | in-game minutes | 13.5 | 14.5 | 19.7 | | **mean CPU temp** | 86.51 °C | 86.51 °C | **74.80 °C** | | median | 89.50 °C | 89.25 °C | **75.00 °C** | | **max** | 94.25 °C | 94.00 °C | **80.00 °C** | | samples > 90 °C | 39.9 % | 34.8 % | **0.0 %** | | samples > 80 °C | 81.3 % | 86.0 % | **0.0 %** | Run C's median landed on exactly 75.00 — the servo tracks its setpoint precisely once it has the right one. Frametimes were statistically unchanged across all three (median 17.6 / 17.2 / 17.9 ms; >100 ms stalls per minute 0.72 / 0.88 / 0.64), so the 94 °C peaks were not causing HTC throttling and the improvement is free. Note run A vs run B: uploading the autoservo table changed nothing. Run B vs run C is the meaningful comparison. --- ## Suggested fixes 1. **Mask the revision nibble** — `gddr6_revision = (gddr6_id >> 4) & 0xf;` (or `readb`). Offered as a hypothesis; see the caveat above. 2. **Make both failures visible.** Have `icc_fan_update_autoservo_param()` return an error on the `not_updated` path so the existing `dev_warn` fires, and verify the target-temp write by reading it back. 3. **Re-apply or retry the boot target temp**, since the value demonstrably sticks later at runtime. ## Documentation gap `/sys/devices/platform/ps5-fan/target_temp` is the only fan interface on this console that works, and it is undocumented — it is not mentioned in `ps5_control`'s usage text or in the ps5-linux guide. Users reach for `ps5_control --fan`, which is a silent no-op here, and reasonably conclude the fan cannot be controlled at all. I spent two days on that conclusion before finding this attribute. Documenting it, and/or adding a `ps5_control --target-temp <celsius>` wrapper, would likely help a lot of Slim owners. (The `ps5_control --fan` no-op is a separate issue against `ps5-linux-tools`.) ## Workaround for other Slim owners ```console $ echo 75 | sudo tee /sys/devices/platform/ps5-fan/target_temp ``` Takes effect immediately, does not persist across reboot. Lower is cooler and louder; on this console the fan saturates around a 75 °C target under heavy load (peaks reached 80 °C), so ~75 is roughly the practical floor.
BrenoRippel commented 2026-08-03 17:24:53 -05:00 (Migrated from github.com)

Userspace counterpart filed as ps5-linux/ps5-linux-tools#7 — ps5_control --fan silently reporting success, plus a request to expose target_temp from the tool.

Userspace counterpart filed as ps5-linux/ps5-linux-tools#7 — `ps5_control --fan` silently reporting success, plus a request to expose `target_temp` from the tool.
Sign in to join this conversation.
No milestone
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
gh-ps5-linux/ps5-linux-patches#32
No description provided.