Fixing an Ignored Battery Charge Thing an Ignored Battery reshold on a Huawei MateBook in Linux
Case study: HUAWEI NBD-WXX9, CachyOS, KDE Plasma, and huawei_wmi
This guide documents the diagnosis and permanent workaround for a battery charge threshold that was stored as 40 80 but did not initially stop charging at 80%.
The affected system was:
- Laptop: HUAWEI NBD-WXX9, board NBD-WXX9-PCB-B4
- BIOS: 2.31, dated November 2, 2022
- Operating system: CachyOS, based on Arch Linux, with KDE Plasma
- Kernels tested:
7.2.6-1-cachyosand6.18.52-1-cachyos-lts - Battery:
BAT1; AC adapter:ACAD - Kernel driver:
huawei_wmi - Battery condition: approximately 67% of design capacity and 1,532 cycles
Warning: The Smart Charge reset in this guide uses debugfs and an undocumented Huawei WMI command. Use it only on Huawei or Honor laptops that use the
huawei_wmidriver, expose the same debugfs interface, and exhibit the same behavior. Do not run it on hardware from another manufacturer.
Result summary
The problem was not caused by Linux kernel 7.2.6. The strongest explanation is that the Embedded Controller, or EC, had returned to Smart/Adaptive Charge mode. That mode took precedence over the manually stored thresholds.
Before the fix, the EC stored and reported 40 80, but the battery still received approximately 2 A and charged beyond 80%. After sending a raw WMI command twice, the EC switched to Huawei Battery Protection/Home mode. The desired 40 80 thresholds were then written again and enforced correctly on both the regular and LTS kernels.
The EC state can be represented conceptually as follows:
Before:
EC charging mode = Smart/Adaptive Charge
stored thresholds = 40-80
result = manual thresholds ignored
After the WMI reset:
EC charging mode = Battery Protection/Home mode
thresholds = 40-70
After restoring the desired thresholds:
EC charging mode = Battery Protection
thresholds = 40-80
result = charging stops at 80%
1. Confirm that the driver and interfaces are available
Check the driver and kernel messages:
lsmod | grep huawei_wmi
journalctl -b -k | grep -Ei 'huawei|battery extension'
The relevant kernel message on this system was:
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
The reported values were:
40 80
40
80
In the huawei_wmi driver, reading these attributes invokes the firmware's BATTERY_THRESH_GET WMI operation. Therefore, 40 80 was not merely a value cached by KDE or userspace. The EC itself reported the pair. However, a stored pair does not prove that the active firmware charging mode is enforcing it.
2. Prove whether the battery is actually charging
The Charging status alone is insufficient because ACPI or the KDE interface may update late. A stronger indicator is the change in charge_now over time.
Use this 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"
charge_now increased from 3,735,000 to 3,894,000 µAh in approximately 4.5 minutes. The 159,000 µAh increase corresponds to an average rate of roughly 2.1 A, consistent with the reported current_now value.
This proved that:
- The battery was genuinely receiving charge; this was not merely a delayed KDE status.
- The thresholds remained
40 80while charging, making another service overwriting them unlikely. - Fuel-gauge drift from the old battery could not explain both the rising
charge_nowvalue and the approximately 2 A charging current.
3. Reset Smart Charge through Huawei WMI
The Huawei driver exposes a debugfs interface that can pass a raw argument to the firmware WMI method. First, ensure that debugfs is mounted:
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. Reading call causes the WMI method to be evaluated:
sudo cat /sys/kernel/debug/huawei-wmi/call
sudo cat /sys/kernel/debug/huawei-wmi/call
The method is evaluated twice because some Huawei firmware requires an initial call before the command is accepted.
On this laptop:
- the first response began with status
0x01, meaning it had not completed successfully; - the second response began with status
0x00, indicating success; - the EC thresholds then changed from
40 80to40 70.
Verify the change:
cat /sys/devices/platform/huawei-wmi/charge_control_thresholds
Expected result:
40 70
The 40 70 pair is Huawei's Battery Protection Home or Family preset. The practical effect of the command is best described as switching the EC from Smart/Adaptive Charge to Battery Protection mode.
Huawei does not publish the exact meaning of the bits in 0x462848011503. The interpretation comes from community reverse engineering and was confirmed on this laptop by the immediate threshold and charging-behavior changes.
4. Restore the desired 40-80 thresholds
After Battery Protection mode is active, write the desired thresholds again:
printf '40 80\n' |
sudo tee /sys/devices/platform/huawei-wmi/charge_control_thresholds
Verify them:
cat /sys/devices/platform/huawei-wmi/charge_control_thresholds
Expected result:
40 80
If necessary, disconnect the power adapter for about ten seconds and reconnect it so that the EC reevaluates the charging state.
5. Verify that enforcement now works
Run the monitoring loop 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 was now correct:
- the battery charged while below the end threshold;
- capacity reached 80%;
- status changed to
Not charging; current_nowdropped to zero;charge_nowstopped increasing.
| State | Reported thresholds | Behavior at 80% | current_now | charge_now |
|---|---|---|---|---|
| Before the WMI reset | 40 80 | Continued charging to 81% and beyond | Approximately 1.9-2.1 A | Continued rising |
| After the WMI reset | 40 80 | Stopped at 80% | 0 A | Flat |
6. Determine whether the kernel is responsible
Two installed kernels were tested:
linux-cachyos 7.2.6-1-cachyos
linux-cachyos-lts 6.18.52-1-cachyos-lts
After the Smart Charge reset, the threshold worked under both kernels. Because the regular kernel also stopped charging precisely at 80%, a huawei_wmi regression in kernel 7.2 was ruled out for this case.
The charging mode is stored in the EC and can survive a reboot or kernel switch. This initially made the LTS kernel appear to have fixed the issue, although the WMI reset had occurred at nearly the same time.
7. Temporary Limine default-kernel change
CachyOS used Limine on this system. For kernel comparison, /etc/default/limine was changed from:
BOOT_ORDER="*, *lts, *fallback, Snapshots"
to:
BOOT_ORDER="*lts, *, *fallback, Snapshots"
The boot configuration and initramfs images were then updated:
sudo limine-update
This produced the following menu order:
CachyOS
├─ linux-cachyos-lts
├─ linux-cachyos
└─ Snapshots
A backup was saved as:
/etc/default/limine.codex-backup-20260924
This kernel-order change is not required for the charge-threshold fix because the regular kernel was subsequently proven to work. To restore the regular kernel as the first choice, change BOOT_ORDER back and run sudo limine-update again.
8. Why the problem returned after reboot
The original /etc/systemd/system/battery-threshold.service only wrote 40 80:
ExecStart=/bin/sh -c 'echo "40 80" > /sys/devices/platform/huawei-wmi/charge_control_thresholds'
It did not execute the raw WMI command that activates Battery Protection mode. After a later boot and several suspend cycles, the battery reached 98% even though the EC still reported 40 80.
The original sleep hook had the same limitation. It only restored the threshold numbers after resume and did not restore the EC charging mode. It also returned exit status 1 during the pre phase because of its shell condition, creating misleading failure messages in the journal.
The EC may revert to Smart/Adaptive Charge after:
- an EC reset or a long period without power;
- a reboot or firmware-controlled state transition;
- a BIOS update;
- Huawei PC Manager changing the mode under Windows;
- certain threshold changes, especially values involving zero;
- suspend, hibernation, or resume on affected firmware.
9. Permanent boot and resume workaround
A helper was installed at:
/usr/local/sbin/huawei-battery-threshold
It performs the following sequence:
- Waits for debugfs and the
huawei_wmiinterfaces to become available. - Writes
0x462848011503to the WMI argument. - Evaluates the WMI method twice.
- Writes
40 80after Battery Protection mode is active. - Reads the threshold back and fails if it is not exactly
40 80. - Records success or failure in the system journal.
Boot service
/etc/systemd/system/battery-threshold.service now contains:
[Unit]
Description=Enable Huawei Battery Protection and set charge thresholds
Wants=sys-kernel-debug.mount
After=sys-kernel-debug.mount systemd-modules-load.service
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/huawei-battery-threshold
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
The service is enabled and runs once during boot.
Resume hook
/usr/lib/systemd/system-sleep/battery-threshold contains:
#!/bin/sh
case "${1:-}" in
post)
exec /usr/local/sbin/huawei-battery-threshold
;;
*)
exit 0
;;
esac
The hook runs the same helper after resume. During the pre phase it now exits successfully instead of generating a false failure message.
Backups
The previous configurations were saved as:
/etc/systemd/system/battery-threshold.service.codex-backup-20260925
/usr/lib/systemd/system-sleep/battery-threshold.codex-backup-20260925
Verification
The new service completed with status=0/SUCCESS. Its journal entry was:
Huawei Battery Protection active; threshold verified as 40 80
The live battery state after installation was:
threshold=40 80
online=1
status=Not charging
capacity=80
charge_now=3813000
current_now=0
After a future reboot, check the helper result with:
journalctl -b -t battery-threshold
Also verify the live state:
cat /sys/devices/platform/huawei-wmi/charge_control_thresholds
cat /sys/class/power_supply/BAT1/status
cat /sys/class/power_supply/BAT1/capacity
cat /sys/class/power_supply/BAT1/current_now
10. Technical conclusion
This case demonstrates that Huawei firmware maintains at least two distinct pieces of charging state:
- the start and end threshold values exposed through
huawei_wmi; - an EC charging mode that determines whether manual thresholds are enforced or ignored.
Successfully writing and reading 40 80 therefore does not prove that the thresholds are active. Enforcement must be verified using the charge_now and current_now trends when the battery reaches the configured limit.
The final diagnosis was:
Not the cause: KDE, the oneshot service itself, fuel-gauge drift alone,
or a Linux 7.2 kernel regression.
Most likely cause: Smart/Adaptive Charge in the EC overrode the stored
manual thresholds.
Fix: Switch the EC to Battery Protection/Home mode through WMI,
then restore 40-80 and repeat the sequence at boot and resume.
References
Reader feedback
Was this article helpful?
1 person found this article helpful.
