Battery charge threshold: BIOS vs OS

For a long time I could only set the charge threshold for my FW13 in the BIOS.

That changed recently when KDE added an option in the energy settings to set a threshold. This UI required a kernel parameter to be set, in order to show up.
Also, the value set here was was only enforced by KDE (or rather, the kernel, I guess), but it was completely independent/out of sync with whatever was set in the BIOS.

But with a recent update (dunno which one exactly, only noticed today), the KDE UI shows up even without me setting any kernel parameter, and what’s more is that the value I see in the KDE energy module reflects whatever I set in the BIOS!

I was very excited at this point, thinking I could finally manage the charging threshold straight from the OS and have the value read and written back from/to the BIOS.

While the reading part is true, the writing part is unfortunately not.

If I change the value in the BIOS, the next KDE boot will correctly reflect that change.

But when I set this to a different value from the KDE UI, shutdown and enter BIOS, I still see the previous value in the BIOS, as if KDE isn’t able to change that.

I would need this last bit to work too, in order to free myself of entering the BIOS every time I want to change the threshold. Doing so from the OS would be extremely handy to preserve battery health while allowing for the occasional full charge to 100%, for example when you’re on a trip or away from a charger for a longer time.

What’s your experience with this?

Does it work both ways in other DEs?

Thanks

I’m using opensuse tumbleweed with kernel 6.14.6-1-default, Plasma 6.3.5, Frameworks 6.14.0

No, this doesn’t work. Battery charge thresholds are a bit finicky IMO.
Also tracked here.

Updating the bios value from the OS has never been a thing on Framework. Any values you change in the OS, whether it be from ectool, or echoing some value to /sys/class/power_supply/BAT1/charge_control_end_threshold (which is what KDE is essentially doing), the values in the BIOS will never change.

I leave the bios set to 100% and just let the OS set the values with systemd timers or manually through KDEs config setting when needed. This way, the only time the bios value of 100% takes over is if I have powered the laptop completely off, in which case I would almost always want to charge to 100%.

1 Like

I do something similar. Instead of systemd timers, I use the following systemd service (stored in /etc/systemd/system/charge-control-threshold@.service).

[Unit]
Description=Set battery charge limit
After=systemd-modules-load.service
Requires=systemd-modules-load.service

[Service]
Type=oneshot
ExecStart=/usr/bin/bash -c 'echo %i > /sys/class/power_supply/BAT1/charge_control_end_threshold'

[Install]
WantedBy=multi-user.target

You can then enable it with systemctl enable charge-control-threshold@VALUE where you replace VALUE with whatever charge threshold you want. This will set the charge threshold every time your laptop boots.

Note that this requires GitHub - DHowett/framework-laptop-kmod: Kernel module to expose more Framework Laptop stuff.

Really nice trick with the systemd instance!

On 6.14 framework-laptop-kmod should no longer be needed

1 Like

That kernel module is apparently deprecated. See here:

My setup is similar to yours, but I don’t set it on boot. The timers still set it on boot if it is booted up within the timeframes I have set. For example, from 7am to 4pm on a weekday I have it set to 80%, all other times it is set to 100% based on how my timers are set up:

/etc/systemd/system/battery-level@.service

[Unit]
Description=Set Battery charge level to %i

[Service]
Type=oneshot
#ExecStart=/usr/bin/ectool fwchargelimit 80
ExecStart=/usr/bin/bash -c "echo %i > /sys/class/power_supply/BAT1/charge_control_end_threshold"

For example, here is the 80% timer:
/etc/systemd/system/battery-level@80.timer

[Unit]
Description=Set battery level 80% while working.

[Timer]
OnCalendar=Mon,Tue,Wed,Thu,Fri *-*-* 7:00:00
Persistent=true

[Install]
WantedBy=timers.target
2 Likes

Yet people criticize systemd, go figure…

Variation on the theme above for those that may want to use bios and the framework_tool for battery charge control. Note that using the framework_tool requires having kernel 6.12 or higher.

This and other topics caution against using the bios charge control feature and/or the framework_tool when using /sys/class/power_supply/BAT1/charge_control_end_threshold to control the battery charge limit.

While I think using the cros_charge_control kernel module parameter charge_control_end_threshold and the systemd unit described above is (more) straight forward, I prefer to use the bios and the framework_tool.

The recent beta bios updates (for both intel e.g. here and ryzen e.g. here) that include the new “automatic battery lifetime extender” feature further push me to use the framework_tool (i.e. is this new bios feature also incompatible with the charge_control_end_threshold parameter method?).

A bash function included in ~/.bashrc and a systemd unit:

batcc() {
    # installation:
    # cd ${HOME}
    # git clone https://github.com/FrameworkComputer/framework-system.git
    # cd framework-system
    # cargo clean
    # cargo clippy
    # cargo fmt --check
    # cargo fmt
    # cargo build
    # sudo cp target/debug/framework_tool /usr/local/bin
    # sudo cp ${HOME}/batcc@.service /etc/systemd/system/
    # sudo systemctl daemon-reload                                                        
    # sudo systemctl enable batcc@60
    # sudo systemctl start batcc@60
    # sudo systemctl status batcc@60
    # sudo journalctl -xeu batcc@60.service
    lower_limit=40
    upper_limit=100
    batcc_status="/usr/bin/sudo /usr/local/bin/framework_tool --charge-limit"
    batcc_usage="usage: batcc <percent>; ${lower_limit} <= <percent> <= ${upper_limit}"
    if [ -z "${1}" ]; then
        echo "${batcc_usage}"
        $batcc_status
        return 1
    else
        limit="${1}"
    fi

    if [ "${limit}" -ge "${lower_limit}" -a "${limit}" -le "${upper_limit}" ]; then       
        /usr/bin/sudo systemctl restart batcc@${limit} 
        $batcc_status
    else
        echo "${batcc_usage}"
        return 1
    fi
} # END batcc

batcc@.service:

[Unit]
Description=Set a maximum percent charge limit via framework_tool

[Service]
Type=oneshot
ExecStart=/bin/sh -c '/usr/local/bin/framework_tool --charge-limit %I'
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target

The above has been working for me with kubuntu 24.04 lts and a 6.14 kernel.

Yes, when using cros_charge_control

Was anyone one archlinux 6.16.x able to make the thresholds work for them?
if got the systemd services running and the files are created on boot, without journal errors.

But they seem to get ignored