Kernel patches that expose battery charge thresholds on Intel Apple laptops
running Linux. Adds charge_control_end_threshold (and friends) to
/sys/class/power_supply/BAT0/, mapping writes to the SMC BCLM key so the
limit is stored in SMC NVRAM and survives reboots — even back into macOS.
This is the netlinux-ai fork of c---/applesmc-next.
It carries fixes and packaging for recent Debian / Ubuntu kernels in addition
to the upstream features.
Once the modules are loaded, the kernel battery exposes:
/sys/class/power_supply/BAT0/charge_control_end_threshold # BCLM
/sys/class/power_supply/BAT0/charge_control_full_threshold # BFCL (cosmetic)
/sys/class/power_supply/BAT0/charge_control_start_threshold # stock kernel; not driven by BCLM
Writing an integer 20–100 to charge_control_end_threshold makes the SMC stop
charging at that percentage. The setting is persistent in SMC NVRAM across
reboots and operating systems unless the SMC is reset.
echo 80 | sudo tee /sys/class/power_supply/BAT0/charge_control_end_thresholdcharge_control_full_threshold only controls when the charging LED turns
green and is cosmetic. For it to work, set it at least 2% below the end
threshold, because the battery floats slightly under its target.
- Two-module DKMS package (
applesmc-next-dkms) — replaces stockapplesmcandsbswith patched copies in/lib/modules/.../updates/dkms/.AUTOINSTALL=yesmeans it rebuilds on every kernel upgrade. - GitHub Actions that build
.debpackages for Debian Bookworm and Ubuntu Resolute (26.04) on every release. - Fix for kernels where
sbshcis aplatform_driver(notably the Ubuntu 7.0 kernel in Resolute). On those kernels the host controller pointer lives in the parent's platform device drvdata rather than its ACPI driver_data; without this fix the probe oopses on a NULLacpi_smb_hcpointer and the module is unrecoverable until reboot. - TLP integration — drop
45-appleinto/usr/share/tlp/bat.d/for charge-threshold control via TLP profiles.
Grab the .deb for your release from the releases page,
then:
sudo apt install ./applesmc-next-dkms_*.debThis pulls in dkms, builds and signs both modules against your running
kernel, and installs them into /lib/modules/$(uname -r)/updates/dkms/.
After install:
sudo modprobe -r applesmc sbs # drop the stock modules
sudo modprobe sbs # load the patched sbs first
sudo modprobe applesmc # then applesmc (it hooks into sbs)
ls /sys/class/power_supply/BAT0/ # should now contain charge_control_*_thresholdsudo apt install dkms linux-headers-$(uname -r)
sudo git clone https://github.com/netlinux-ai/applesmc-next /usr/src/applesmc-next-0.1.6
sudo dkms add -m applesmc-next -v 0.1.6
sudo dkms install -m applesmc-next -v 0.1.6applesmc-next-dkms
on the AUR points at the original c---/applesmc-next upstream.
Direct sysfs:
echo 80 | sudo tee /sys/class/power_supply/BAT0/charge_control_end_threshold
cat /sys/class/power_supply/BAT0/charge_control_end_threshold # -> 80To remove the limit, write 100.
Copy 45-apple into TLP's plugin directory:
sudo install -Dm755 45-apple /usr/share/tlp/bat.d/45-appleThen add the standard TLP keys to /etc/tlp.conf:
START_CHARGE_THRESH_BAT0=0 # not used; SMC BCLM has no start threshold
STOP_CHARGE_THRESH_BAT0=80If you want a tray app to drive the same sysfs file, see
netlinux-ai/battery-tray.
| Kernel | Status |
|---|---|
| 6.1 – 6.17 | builds against acpi_driver-based sbshc |
| 6.18+ / Ubuntu 7.0 (Resolute) | builds against platform_driver-based sbshc — needs the fix in this fork |
| < 6.1 | legacy device->parent lookup path |
Tested on:
- MacBookPro9,2 (Ivy Bridge HD 4000) — Ubuntu 26.04, kernel 7.0.0-14-generic
- MacBookPro models with Smart Battery System (ACPI device
SBS0underSBSH)
-
charge_control_end_thresholddoesn't appear — patched modules aren't loaded. Checkdkms status applesmc-nextandmodinfo sbs | head -1(it should resolve to/lib/modules/.../updates/dkms/sbs.ko.zst, not the in-tree/kernel/drivers/acpi/sbs.ko.zst). If stock modules are still loaded,modprobe -rthem andmodprobeagain. -
modprobe sbsreturns 0 but BAT0 is missing — sbs probably hit-EPROBE_DEFER. Checksudo cat /sys/kernel/debug/devices_deferred— ifACPI0002:00is parked there and you're on a recent kernel without this fork's fix, you need to update. -
Kernel oops with
mutex_lock+0x1candCR2: 0x0000000000000008— this fork's commit history fixes exactly that. Pre-fix builds (0.1.6 and earlier) are unrecoverable without a reboot once they hit this. -
Writes to
charge_control_full_thresholdreturn I/O error — the BFCL SMC key doesn't exist on all models (e.g. MacBookPro9,2). The end threshold still works; full is cosmetic-only.
GPL-2.0 — see LICENSE.
- Original work by @c--- and contributors.
- Platform-driver / Ubuntu-Resolute fix and Debian/Ubuntu packaging by netlinux-ai.