Re: [BUG] Redmi Book Pro 14 2024 (TM2307): fans never spin under Linux — root cause: EC gates fan engine on OSTP byte (tested workaround)
From: 高 俊文
Date: Wed Sep 16 2026 - 06:11:59 EST
Hi Armin,
Thanks for looking at this. Answers to your two questions first, then a
correction to my original report that your questions prompted — you were
right to be suspicious, though it is not a probe failure on this unit.
> what is the output of "cat /sys/bus/acpi/devices/PNP0C09:*/status"?
15
(single EC: PNP0C09:00 under \_SB.PC00.LPCB)
> Can you share the output of "dmesg"?
Attached, captured on the current boot (kernel 7.2.5-3-omarchy; the
original report was on 7.2.3, same behavior). EC-relevant excerpt:
ACPI: EC: EC started
ACPI: EC: interrupt blocked
ACPI: EC: EC_CMD/EC_SC=0x66, EC_DATA=0x62
ACPI: \_SB_.PC00.LPCB.Q_EC: Boot DSDT EC used to handle transactions
ACPI: EC: interrupt unblocked
ACPI: EC: event unblocked
ACPI: EC: EC_CMD/EC_SC=0x66, EC_DATA=0x62
ACPI: EC: GPE=0x6e
ACPI: \_SB_.PC00.LPCB.Q_EC: Boot DSDT EC initialization complete
ACPI: \_SB_.PC00.LPCB.Q_EC: EC: Used to handle transactions and events
input: Lid Switch as .../0000:00:1f.0/PNP0C09:00/PNP0C0D:00/input/input0
The EC driver probes and binds fine here: PNP0C09:00 has a physical
platform device bound to the acpi-ec driver, there is no ECDT (the EC is
DSDT-only), and EC transactions demonstrably work at runtime (BAT0 _BST
reports live voltage/energy/serial; the lid switch and GPE 0x6e events
work). The only ACPI errors in the log are unrelated _PLD/_UPC failures
in the XHCI/TXHC hubs (AE_AML_TOO_FEW_ARGUMENTS in a RUCC helper).
Your other concern — that any firmware attempt to access the EC would
error out — does not apply to this board, and chasing it down exposed a
real mistake in my original report:
1) No AML anywhere in our tables (DSDT + all 27 SSDTs) uses EC address
space. All 308 OperationRegions are 242 SystemMemory, 50 PCI_Config,
14 SystemIO, 2 SystemCMOS. The EC's data surface for AML is a plain
SystemMemory window:
OperationRegion (ERAM, SystemMemory, 0xFE0B0300, 0x0100)
2) Consequence: Q_EC._REG can never execute for an EC-space connect,
because no EC-space region object exists for ACPICA to fire it on —
on any OS. The OSYS->OSTP mapping inside _REG is dead code (vendor
cruft from a reference design). Q_EC._INI sets ECAV=1 when
_REV>=2, and all methods proceed through the ERAM window instead.
3) I misread the window layout in my report. The iasl field layout is:
Offset (0x60): QFAN, 8
Offset (0x62): SCFG (0x62), HBPF (0x63), PCMD (0x64), OSTP (0x65)
The byte I called "OSTP" (0x64) is actually declared "PCMD" in the
vendor AML — and PCMD is referenced by no AML method anywhere. The
real OSTP at 0x65 already reads 6 at every Linux boot with no help
from us (pre-fix dumps: 0x64=0x00, 0x65=0x06), so it is written by
the EC/UEFI firmware itself, not by AML.
4) Corrected root cause: the EC's autonomous fan engine is gated on
ERAM+0x64 ("PCMD"), which nothing ever sets under Linux. The
original experiment stands with cleaner isolation than I realized:
with OSTP (0x65) already 6 the whole time, control load runs stay
fan-silent; writing 6 to 0x64 engages the engine and the fans follow
the EC's own temperature curve; QFAN (0x60)=4 forces full speed.
That one-byte write has been running as a boot+resume oneshot since
2026-09-15 and the machine behaves exactly like the Windows side.
So on this unit the EC driver is healthy, and "replicate the _REG
OSYS mapping" (my original proposal) would be the wrong fix — _REG
never runs on this platform regardless of OS. The open question is
narrower now: who sets ERAM+0x64 under Windows? Not AML (no
references on any OS), so presumably EC-internal logic or an SMI
path (there is an event-driven one: _Q35 writes 0xC2 to the port-0xB2
SMI port). If Miloš (dual-boot) could dump ERAM 0x60..0x6C from
Windows — or even just byte 0x64 at idle and under load — that would
pin down what state the EC expects to see.
Happy to test any patch. If a quirk approach is still wanted, the
effective workaround is "write ERAM+0x64=6 at boot/resume", not
"replicate _REG". Full acpidump and the raw ERAM dumps available any
time.
Transparency note: the ACPI analysis, the experiments, and this email
were prepared by an AI coding agent (Claude Code running the GLM 5.3
Flash model) working on this machine. To be honest, I still find much
of the ACPI detail confusing myself — what I can vouch for firsthand
is the hardware behavior: the fans were always silent under Linux, and
after the one-byte write they spin and track the load exactly like on
Windows. For anything deeper than that, please ask and I will relay
it to the agent.
Thanks,
dista
On Wed, Sep 16, 2026 at 5:47 AM Armin Wolf <W_Armin@xxxxxx> wrote:
>
> Am 15.09.26 um 14:09 schrieb 高 俊文:
>
> > Hi,
> >
> > This follows up on the report from Miloš Vlku about the Redmi Book Pro 14
> > 2024 (TM2307), where the fans never spin under Linux although the same
> > hardware works fine under Windows:
> >
> > https://lkml.iu.edu/hypermail/linux/kernel/2608.1/13832.html
> >
> > We have now root-caused the problem on an identical machine (second
> > TM2307,
> > Core Ultra 7 155H) and have a one-byte, experimentally verified
> > workaround.
> > Summary: the EC only runs its autonomous fan engine when the OSTP "OS
> > type"
> > byte in its shared memory window indicates a detected host OS. Under
> > Windows
> > the DSDT _REG handler writes OSTP=6; under Linux nothing writes it, OSTP
> > stays 0, and the EC never spins the fans. All OS-side fan paths (MIFS WMI
> > fan functions, ACPI PNP0C0B fans) are separately broken on this board, so
> > fans are unreachable from Linux even at 100 degC.
> >
> Nice work, what is the output of "cat /sys/bus/acpi/devices/PNP0C09:*/status"?
> Can you share the output of "dmesg"?
>
> > Machine / software
> > ==================
> >
> > DMI: XIAOMI / Redmi Book Pro 14 2024 / TM2307
> > CPU: Intel Core Ultra 7 155H (Meteor Lake-H)
> > BIOS: XIAOMI RMAMT4B0P0A0A (06/04/2024)
> > Kernel: 7.2.3-arch1-3 (bitland-mifs-wmi incl. d3666875c75e)
> >
> > Symptoms (all verified)
> > =======================
> >
> > - Fans physically never spin under Linux, even with the package at
> > 101 degC for minutes (verified by microphone recording: no acoustic
> > change; user-confirmed silence; multiple independent load tests).
> > - hwmon (bitland_mifs): fan1/2/3_input always 0, temp1 always 0.0 degC.
> > - fan_boost sysfs attribute (WMI function 20): write succeeds, no
> > effect.
> > - No WMI fan events (26/32) ever fire (no hwmon uevents under load).
> > - Writing cur_state=1 to all five PNP0C0B cooling devices: no effect.
> > - acpitz thermal zone (SSDT23 TZ00) reports a constant ~26.9 degC while
> > coretemp reads 56-101 degC.
> > - Under Windows, fans work normally (per Miloš Vlku's dual-boot test on
> > the same board).
> >
> > Root cause — DSDT/SSDT evidence chain
> > =====================================
> >
> > 1) The EC's memory window and the OSTP byte
> >
> > Under \_SB.PC00.LPCB.Q_EC (PNP0C09):
> >
> > OperationRegion (ERAM, SystemMemory, 0xFE0B0300, 0x0100)
> >
> > Q_EC._REG (fires when EC region space becomes available) maps the OS
> > version (OSYS) to an OSTP byte and writes it:
> >
> > OSYS 0x03E8 -> OSTP = 2 (Linux)
> > OSYS 0x07D9 -> OSTP = 3 (Windows 2009)
> > OSYS 0x07DC -> OSTP = 4 (Windows 2012)
> > OSYS 0x07DD -> OSTP = 5 (Windows 2015)
> > OSYS 0x07DF -> OSTP = 6 (Windows 2021)
> >
> > On this machine OSTP (ERAM offset 0x64) reads 0x00 under Linux: the
> > _REG write never happens (verified by dumping the window via /dev/mem;
> > the rest of the window is demonstrably live, e.g. live temperature
> > fields TSR0-TSRB at offsets 0x08-0x13, exposed to Linux via the SENx
> > DPTF sensors whose _TMP read these bytes directly).
> >
> > 2) OSTP gates the EC's autonomous fan engine — experimentally verified
> >
> > Writing OSTP=6 (/dev/mem, single byte) at idle, then running an
> > all-core load:
> >
> > OSTP=0 (control): 101 -> 83 degC clamp, fans silent (no airflow)
> > OSTP=6: fans audibly engage and follow the EC's own
> > temperature curve; package temperature bends down
> > mid-load (83 -> 70 degC) instead of plateauing
> > QFAN (ERAM 0x60)=4 ("full speed" performance mode value): fans spin
> > at full speed regardless of temperature
> >
> > After writing OSTP=6 once, fan behavior matches Windows (moderate ramp,
> > full speed only near the EC's high thresholds). A systemd oneshot that
> > writes OSTP=6 at boot and after resume has kept the machine cool and
> > correctly fan-cooled since installation.
> >
> > 3) The MIFS WMI fan functions are not implemented in this firmware
> >
> > The MIFS WMI method is WMAA in SSDT20 (device WMID, _UID "MIFS"; _WDG
> > entry for GUID B60BFB48-3E5B-49E4-A0E9-8CFFE1B3434B, object id "AA").
> > Its GET (0xFA) and SET (0xFB) switches implement only functions 0x08
> > (SystemPerMode), 0x0A (country/kbd, sub 5) and 0x10 (misc). The fan
> > functions used by bitland-mifs-wmi fall through to
> >
> > Default { SGER = 0xE000 } /* unsupported, zeroed buffer */
> >
> > i.e. function 0x0D (WMI_FN_FAN_SPEEDS) returns an all-zero buffer
> > (hwmon zeros incl. WMI_FN_CPU_THERMOMETER 0x16), and functions 0x14
> > (WMI_FN_MAX_FAN_SWITCH / fan_boost) and 0x15 (WMI_FN_MAX_FAN_SPEED)
> > are silently ignored — exactly the observed behavior. So this board's
> > firmware is "MIFS reduced" in the same sense as the MIFS v2 machines
> > discussed in Yuming Sun's series, except here the WMI method exists
> > but the fan function cases are absent.
> >
> > 4) The ACPI fan path is gated on a device this board does not declare
> >
> > SSDT23 declares the five PNP0C0B fans (FAN0-FAN4) with PowerResources
> > FN00-FN04. Their _ON/_OFF only set software flags CVF0-4 and call
> > FNCL(), which ends in:
> >
> > \_SB.PC00.LPCB.UPFS (ETMD, Local0, Local1)
> >
> > and LPCB.UPFS (DSDT) does nothing unless
> >
> > If ((CondRefOf (\_SB.PC00.LPCB.H_EC) && Arg0)) ...
> >
> > H_EC is not declared anywhere in any table on this machine, so the
> > entire ACPI fan-control path is a silent no-op. This is why writing
> > cur_state=1 to the cooling devices changes nothing.
> >
> > 5) The ACPI thermal zone yields to the (disabled) firmware controller
> >
> > SSDT23 TZ00._TMP:
> >
> > If (!ETMD) { Return (0x0BB8) } /* constant 26.85 degC */
> > Return (\_SB.PC00.LPCB.MXTP ())
> >
> > SSDT16 IETM._OSC sets \_TZ.ETMD = Zero when the firmware reports
> > native trip-point capability (CAP1 bit0 | bit1), selecting "firmware
> > manages thermals" mode. Combined with (1), the firmware never enables
> > fan control and the ACPI zone never reports real heat — the kernel
> > never sees a reason to act.
> >
> > 6) Tachometer caveat for this generation
> >
> > The ERAM offsets 0x69/0x6B used by the MIFS v2 fan-tach patch do
> > exist here, but they do NOT live-update on TM2307: they hold stale
> > values (fan1=2, fan2=0) even while the fans audibly spin. Reading
> > tach on this model will need a different mechanism (or none).
> >
> > Proposed fix
> > ============
> >
> > Short term (upstream): have bitland-mifs-wmi write OSTP=6 (or evaluate/
> > replicate the _REG OSYS mapping) at probe time for boards where the
> > firmware leaves it at 0 — ideally behind a DMI quirk. Note the pending
> > DMI-matching problem reported by Miloš: this board (DMI_SYS_VENDOR
> > "XIAOMI", DMI_BOARD_NAME "TM2307") matches neither "Redmi" nor "TIMI"
> > entries, so any Redmi-specific quirk table needs an explicit TM2307/XIAOMI
> > entry.
> >
> > Longer term: a hwmon pwm/fan interface for boards with the reduced
> > firmware could drive QFAN (0x60) and read the SENx temperatures (ERAM
> > 0x08-0x13 / TSR0-TSRB), giving Linux users real fan control on this
> > generation.
> >
> I do not think this would really solve the problem, because any attempt by the
> ACPI firmware to access the embedded controller would still result in an error.
>
> The most important thing right now is to determine why the embedded controller
> driver fails to probe.
>
> Thanks,
> Armin Wolf
>
> > Current workaround in place (for reference):
> >
> > systemd oneshot service, runs at boot and after resume:
> >
> > python3 - <<'EOF'
> > import os, mmap
> > f = os.open('/dev/mem', os.O_RDWR | os.O_SYNC)
> > m = mmap.mmap(f, 0x1000, offset=0xFE0B0000)
> > m[0x300 + 0x64] = 6 # ERAM offset 0x64 = OSTP
> > m.close()
> > EOF
> >
> > I have full acpidumps (DSDT + all 27 SSDTs, disassembled), the ERAM
> > window dumps before/during/after experiments, and the load-test
> > temperature logs. Happy to provide any of these, test patches, or help
> > however useful.
> >
> > Thanks
> >
> > --
> > @gaojunwen
--
@gaojunwen