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