Fixing an Ignored Battery Charge Threshold on a Huawei MateBook in Linux
This tutorial documents how I diagnosed and fixed a battery charge threshold that was stored as 40 80 but initially did not stop charging at 80%. The case occurred on the following system:
Case study: HUAWEI NBD-WXX9, CachyOS, KDE Plasma, and huawei_wmi
This tutorial documents how I diagnosed and fixed a battery charge threshold that was stored as 40 80 but initially did not stop charging at 80%. The case occurred on the following system:
- Laptop: HUAWEI NBD-WXX9, board NBD-WXX9-PCB-B4
- BIOS: 2.31, dated 2 November 2022
- OS: CachyOS (Arch Linux based) with KDE Plasma
- Kernels tested:
7.2.6-1-cachyosand6.18.52-1-cachyos-lts - Battery:
BAT1; AC adapter:ACAD - Driver: the
huawei_wmikernel module - Battery condition: roughly 67% of design capacity and 1,532 charge cycles
Warning: The Smart Charge reset section uses the debugfs interface and an undocumented Huawei WMI command. Only use it on Huawei/Honor laptops that use the
huawei_wmidriver, expose the same debugfs paths, and show similar symptoms. Do not run it on other brands.
Summary
The problem was not caused by kernel 7.2.6. The most likely cause is that the Smart/Adaptive Charge mode in the Embedded Controller (EC) takes priority over the manual threshold.
Before the fix, the EC stored and reported a 40 80 threshold, yet the battery kept drawing about 2 A and charged past 80%. After a specific WMI command was executed twice, the EC switched to Battery Protection (Home) mode. The threshold was then written back to 40 80 and enforced correctly on both the regular and LTS kernels.
Conceptually, the EC state probably looked like this:
Before:
EC charging mode = Smart/Adaptive Charge
stored threshold = 40–80
result = manual threshold ignored
After the WMI reset:
EC charging mode = Battery Protection (Home Mode)
threshold = 40–70
After rewriting the threshold:
EC charging mode = Battery Protection
threshold = 40–80
result = charging stops at 80%
1. Verify that the driver and interface are available
Check the module and kernel messages:
lsmod | grep huawei_wmi
journalctl -b -k | grep -Ei 'huawei|battery extension'
On this system, the relevant message is:
ACPI: battery: new hook: Huawei Battery Extension
Check the threshold interfaces:
cat /sys/devices/platform/huawei-wmi/charge_control_thresholds
cat /sys/class/power_supply/BAT1/charge_control_start_threshold
cat /sys/class/power_supply/BAT1/charge_control_end_threshold
Initial output:
40 80
40
80
With the huawei_wmi driver, reading these files performs the BATTERY_THRESH_GET WMI operation against the firmware. In other words, 40 80 is not just a value cached by KDE or user space; the EC really does report that pair. However, a stored pair does not prove that the active charging mode actually enforces it.
2. Prove whether the battery is still charging
The Charging status alone is not enough, because the ACPI status or the KDE display can lag behind. A stronger indicator is how charge_now changes over time.
Use the following monitoring loop:
while :; do
printf '%s online=%s status=%s cap=%s charge_now=%s current_now=%s threshold="%s"\n' \
"$(date '+%F %T')" \
"$(cat /sys/class/power_supply/ACAD/online)" \
"$(cat /sys/class/power_supply/BAT1/status)" \
"$(cat /sys/class/power_supply/BAT1/capacity)" \
"$(cat /sys/class/power_supply/BAT1/charge_now)" \
"$(cat /sys/class/power_supply/BAT1/current_now)" \
"$(cat /sys/devices/platform/huawei-wmi/charge_control_thresholds)"
sleep 30
done | tee /tmp/huawei-charge.log
Press Ctrl+C to stop monitoring.
Results before the fix
10:19:52 cap=78 charge_now=3735000 current_now=371000 threshold="40 80"
10:21:22 cap=79 charge_now=3793000 current_now=2233000 threshold="40 80"
10:22:52 cap=80 charge_now=3846000 current_now=2021000 threshold="40 80"
10:24:22 cap=81 charge_now=3894000 current_now=1875000 threshold="40 80"
Over about 4.5 minutes, charge_now rose from 3,735,000 to 3,894,000 µAh. That 159,000 µAh increase corresponds to an average of roughly 2.1 A, which matches the reported current_now.
This demonstrates three things:
- The battery was genuinely still accepting charge; this was not merely a lagging KDE status.
- The threshold stayed at
40 80throughout charging, so it is unlikely that another service was overwriting it. - Fuel gauge drift in an aging battery cannot explain both the rise in
charge_nowand a current of about 2 A.
3. Reset Smart Charge mode through WMI
The Huawei driver provides a debugfs interface for sending raw arguments to the firmware's WMI method. Make sure debugfs is available:
test -e /sys/kernel/debug/huawei-wmi/arg || \
sudo mount -t debugfs debugfs /sys/kernel/debug
Set the WMI argument:
printf '%s\n' 0x462848011503 |
sudo tee /sys/kernel/debug/huawei-wmi/arg
Writing to arg only stores the argument. The WMI operation is executed when the call file is read:
sudo cat /sys/kernel/debug/huawei-wmi/call
sudo cat /sys/kernel/debug/huawei-wmi/call
The call is made twice because some Huawei firmware needs the first call as a kind of preparation step before it accepts the command.
In this test:
- the first response had an initial status of
0x01, meaning it had not succeeded; - the second response had an initial status of
0x00, meaning it succeeded; - the EC threshold then changed from
40 80to40 70.
Verify the change:
cat /sys/devices/platform/huawei-wmi/charge_control_thresholds
Result:
40 70
The 40 70 pair is Huawei's Battery Protection (Home or Family) preset. The effect of this command is best described as switching from Smart/Adaptive Charge to Battery Protection mode. Huawei has not published what the individual bits of 0x462848011503 mean; this interpretation comes from community reverse engineering and is confirmed by the change in EC behavior on this device.
4. Restore the 40–80 threshold
Once Battery Protection mode is active, write the desired threshold back:
printf '40 80\n' |
sudo tee /sys/devices/platform/huawei-wmi/charge_control_thresholds
Check it again:
cat /sys/devices/platform/huawei-wmi/charge_control_thresholds
Expected result:
40 80
If necessary, unplug the adapter for about 10 seconds and plug it back in so the EC re-evaluates the charging state.
5. Verify the fix
Run the monitoring loop from section 2 again.
Results after the fix
10:44:13 online=1 status=Charging cap=79 charge_now=3809000 current_now=345000 threshold="40 80"
10:44:43 online=1 status=Charging cap=79 charge_now=3829000 current_now=2097000 threshold="40 80"
10:45:13 online=1 status=Not charging cap=80 charge_now=3831000 current_now=0 threshold="40 80"
The behavior is now correct:
- the battery charges while below the end threshold;
- capacity reaches 80%;
- the status changes to
Not charging; current_nowdrops to zero;charge_nowstops increasing.
Before and after:
| Condition | Threshold read | At 80% | current_now | charge_now |
|---|---|---|---|---|
| Before WMI reset | 40 80 | Kept charging up to 81% | ~1.9–2.1 A | Kept rising |
| After WMI reset | 40 80 | Stopped exactly at 80% | 0 A | Flat |
6. Test whether the kernel was the cause
To separate a kernel problem from a firmware problem, two kernels were tested:
linux-cachyos 7.2.6-1-cachyos
linux-cachyos-lts 6.18.52-1-cachyos-lts
The threshold worked on both once Smart Charge mode had been reset. Because the regular kernel also stopped charging exactly at 80%, a huawei_wmi regression in kernel 7.2 can be ruled out for this case.
The charging mode is stored in the EC and can persist across reboots and kernel changes. That is why it briefly looked as though the LTS kernel had fixed the problem, when in fact the WMI reset happened at about the same time.
7. Limine default kernel change (optional and unnecessary)
CachyOS on this system uses Limine. For testing, the kernel order in /etc/default/limine was changed from:
BOOT_ORDER="*, *lts, *fallback, Snapshots"
to:
BOOT_ORDER="*lts, *, *fallback, Snapshots"
The boot configuration and initramfs were then regenerated:
sudo limine-update
Resulting menu order:
CachyOS
├─ linux-cachyos-lts
├─ linux-cachyos
└─ Snapshots
Backup created before the change:
/etc/default/limine.codex-backup-20260924
This change is not needed to fix the charge threshold, since the regular kernel was proven to work. To make the regular kernel the first choice again, restore the original BOOT_ORDER and run sudo limine-update.
8. The threshold service and possible recurrence
The existing battery-threshold.service writes 40 80 at boot. That is sufficient as long as the EC stays in Battery Protection mode. However, the service does not change the separate Smart Charge mode on its own.
The problem may return after:
- an EC reset or a long loss of power;
- a BIOS update;
- Huawei PC Manager on Windows changing the charging mode;
- certain threshold changes, especially values involving zero;
- the firmware automatically restoring Smart/Adaptive Charge.
If the problem returns, first confirm it by checking whether charge_now keeps increasing past 80%. Then repeat the WMI call twice and write 40 80 again.
Optional automation
If the EC keeps reverting to Smart Charge, the service can be set up to run this sequence:
- wait for debugfs to become available;
- write
0x462848011503toarg; - read
calltwice; - write
40 80tocharge_control_thresholds.
Example systemd unit:
[Unit]
Description=Configure Huawei battery protection and charge thresholds
Wants=sys-kernel-debug.mount
After=sys-kernel-debug.mount
ConditionPathExists=/sys/kernel/debug/huawei-wmi/arg
[Service]
Type=oneshot
ExecStart=/usr/bin/sh -c 'printf "%%s\n" 0x462848011503 > /sys/kernel/debug/huawei-wmi/arg'
ExecStart=/usr/bin/sh -c 'cat /sys/kernel/debug/huawei-wmi/call > /dev/null'
ExecStart=/usr/bin/sh -c 'cat /sys/kernel/debug/huawei-wmi/call > /dev/null'
ExecStart=/usr/bin/sh -c 'printf "40 80\n" > /sys/devices/platform/huawei-wmi/charge_control_thresholds'
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
Note: In systemd unit files, a literal
%must be written as%%, which is why the firstExecStartline uses%%s. The\nsequences are passed through toprintfunchanged.
This automation is not necessary while the EC state remains stable. Use it only if the problem is proven to return after a reboot or EC reset.
9. Technical conclusion
This case shows that the Huawei firmware has two separate states:
- the start/end threshold values that can be read through
huawei_wmi; - the EC charging mode, which decides whether the manual threshold is used or ignored.
Therefore, successfully writing and reading back 40 80 does not always mean the threshold is active. Evidence of enforcement must come from the trend of charge_now and current_now when capacity reaches the limit.
The final diagnosis:
Not the cause: KDE, a systemd oneshot, the fuel gauge alone, or a kernel 7.2 regression.
Most likely cause: EC Smart/Adaptive Charge overriding the manual threshold.
Fix: switch the EC to Battery Protection (Home) mode via WMI,
then write the 40–80 threshold again.