[RESPONDED] Battery charge control thresholds in sysfs

Currently we have an option to set the battery charge limit in BIOS. However, that setting is not propagated to the kernel and thus is not visible to the userspace.

For example my KDE battery applet still shows the battery as charging even when upper limit is reached. Also, it incorrectly calculates the battery wear.

I suppose this happens because kernel does not provide the information in sysfs:

+++ Battery Status: BAT1
/sys/class/power_supply/BAT1/manufacturer                   = NVT
/sys/class/power_supply/BAT1/model_name                     = Framewo
/sys/class/power_supply/BAT1/cycle_count                    =    126
/sys/class/power_supply/BAT1/charge_full_design             =   3572 [mAh]
/sys/class/power_supply/BAT1/charge_full                    =   3208 [mAh]
/sys/class/power_supply/BAT1/charge_now                     =   2304 [mAh]
/sys/class/power_supply/BAT1/current_now                    =    346 [mA]
/sys/class/power_supply/BAT1/status                         = Discharging

/sys/class/power_supply/BAT1/charge_control_start_threshold = (not available) 
/sys/class/power_supply/BAT1/charge_control_end_threshold   = (not available) 

Charge                                                      =   71.8 [%]
Capacity                                                    =   89.8 [%]

Note the missing thresholds and charge limit (90%) mistakenly treated as actual capacity.

Any ideas on what needs to be done to support that in the kernel? My understanding is thath it could be supported, since overall battery info is detected and is more or less accurate.

4 Likes

charge_now?/ charge_full* 100 = 71.8%

charge_full_design/charge_full *100 = 89.7%

Question:: charge_full_design = 3572 [mAh] as design is 5500 [mAh]

Different units. Design voltage is 15.4V, so
3572 mAh * 15.4V = 55.0 Wh (which is the design capacity of the battery)

1 Like

Yes :blush: that should have been 5500mWh

So, exposing the charge threshold in sysfs is something I’m working on. The limit is mediated by the EC, and exposing it in sysfs requires the installation of a battery “extension” (it’s a kernel interface that allows one driver to add sysfs entries to an ACPI battery node owned by another driver.)

I’m planning on seeking upstream approval for this patch soon. For now, though, some teasers:

(rigel) ~ % ls /sys/class/power_supply/BAT1 
alarm           charge_full_design  device        power          subsystem   voltage_min_design
capacity        charge_now          hwmon2        present        technology  voltage_now
capacity_level  current_now         manufacturer  serial_number  type
charge_full     cycle_count         model_name    status         uevent

(rigel) ~ % sudo modprobe framework_acpi

(rigel) ~ % sudo dmesg | tail -n 1
[159509.633091] ACPI: battery: new extension: Framework Laptop Battery Extension

(rigel) ~ % ls /sys/class/power_supply/BAT1/charge_control_end_threshold 
/sys/class/power_supply/BAT1/charge_control_end_threshold

(rigel) ~ % cat /sys/class/power_supply/BAT1/charge_control_end_threshold
100

(rigel) ~ % echo 80 | sudo tee /sys/class/power_supply/BAT1/charge_control_end_threshold
80

21 Likes

Does charge_full/charge_full_design represent true battery wear? I’ve been using an 11th-gen laptop for 5 months and have battery charge capped at 80%. 10% wear seems too high.

[fw ~]$ cd /sys/class/power_supply/BAT1
[fw BAT1]$ cat charge_full charge_full_design charge_now
3215000
3572000
2592000

Also cycle_count reports zero. But I guess that’s because I haven’t been using any battery manager so the kernel doesn’t know.

Hi @DHowett.

Have you got any further with your efforts to expose the battery charge limit setting to OS control?

3 Likes

Been wondering about it as well. My old-ish huawei laptop exposed those entries just fine, but the AMD framework does not - a pity. I actually used to change those depending on my use case, rebooting to BIOS is rather inconvenient, not to mention only the max charge can be changed.

You can change these with the ectool from userspace from memory (which requires david’s currently out of tree patch to support the amdfw)

Thanks for the heads up, but that’s still a workaround for me. I do use the console, but I’d like to make use of Plasma’s powerdevil / activity profiles, which means GUI. Sadly, that only works if charge_control_start_threshold and charge_control_end_threshold are exposed…

1 Like

Hello @Loell_Framework , any chance that this feature request can be taken into consideration by the DEV team?
I have the same request and several other forum users as well. Only problem is that these requests are a bit scattered around in the forum.

Given its open nature, I would totally expect FW to expose this bit via sysfs eventually.

As another user mentioned, keeping a value of 80% is great for extending battery lifespan, but being able to ramp up to 100% before a long flight/trip would be a great usability improvement, without the need to fiddle with the BIOS.

@DHowett tagging you as you kindly started working on this as well :slight_smile:

Thanks!

EDIT: other thread here

2 Likes

If you’re willing to run some rando’s third-party kernel module, you can test it out!

It also exposes things like fan speed and keyboard backlight to userland via sysfs.

5 Likes

Dhowett’s solution or bios is where were at for now.

1 Like

Thanks for the link

From the README however my understanding is that I’d need to compile my own kernel, not just the module, is that still the case?

I’m not familiar with that process at all, and of course when compared to compiling just a module, I think that it would be a different league of DIY :sweat_smile:

Hence my interest for this being upstreamed eventually. Are your upstream efforts still ongoing, and if so, is there an issue/URL I can monitor for progress?

Thanks a lot :slight_smile:

4 Likes

Thank you for this!

I’ve just built and installed the module on my 12th Gen running Fedora 39 without any problems. It was as simple as:

$ git clone https://github.com/DHowett/framework-laptop-kmod.git
$ cd framework-laptop-kmod/
$ make
$ sudo make modules_install
$ sudo modprobe framework_laptop
$ cat /sys/class/power_supply/BAT1/charge_control_end_threshold
80
# echo 100 > /sys/class/power_supply/BAT1/charge_control_end_threshold 
# cat /sys/class/power_supply/BAT1/charge_control_end_threshold 
100

After this, I could see it start charging up past 80%.

3 Likes

Love it!

Works like a charm on Gentoo.

Using linux-next kernel, which is now 6.9-rc6, no patch is needed.
lm-sensors reports the fan speed.

2 Likes

I’m on Ubuntu 22.04 with 5.15.0-97-generic and I get these errors:

korvin@sigma:~/work/framework/framework-laptop-kmod$ make
make -C /lib/modules/`uname -r`/build M=$PWD modules
make[1]: Entering directory '/usr/src/linux-headers-5.15.0-97-generic'
  CC [M]  /home/korvin/work/framework/framework-laptop-kmod/framework_laptop.o
/home/korvin/work/framework/framework-laptop-kmod/framework_laptop.c: In function ‘ec_get_fan_speed’:
/home/korvin/work/framework/framework-laptop-kmod/framework_laptop.c:268:9: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement]
  268 |         struct cros_ec_device *ec = dev_get_drvdata(ec_device);
      |         ^~~~~~
/home/korvin/work/framework/framework-laptop-kmod/framework_laptop.c: In function ‘ec_set_target_rpm’:
/home/korvin/work/framework/framework-laptop-kmod/framework_laptop.c:300:9: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement]
  300 |         struct cros_ec_device *ec = dev_get_drvdata(ec_device);
      |         ^~~~~~
/home/korvin/work/framework/framework-laptop-kmod/framework_laptop.c:307:15: error: implicit declaration of function ‘cros_ec_cmd’; did you mean ‘cros_ec_cmd_xfer’? [-Werror=implicit-function-declaration]
  307 |         ret = cros_ec_cmd(ec, 1, EC_CMD_PWM_SET_FAN_TARGET_RPM, &params,
      |               ^~~~~~~~~~~
      |               cros_ec_cmd_xfer
/home/korvin/work/framework/framework-laptop-kmod/framework_laptop.c: In function ‘ec_get_target_rpm’:
/home/korvin/work/framework/framework-laptop-kmod/framework_laptop.c:321:9: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement]
  321 |         struct cros_ec_device *ec = dev_get_drvdata(ec_device);
      |         ^~~~~~
/home/korvin/work/framework/framework-laptop-kmod/framework_laptop.c: In function ‘fw_fan_target_show’:
/home/korvin/work/framework/framework-laptop-kmod/framework_laptop.c:366:9: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement]
  366 |         u32 val;
      |         ^~~
/home/korvin/work/framework/framework-laptop-kmod/framework_laptop.c: In function ‘ec_set_auto_fan_ctrl’:
/home/korvin/work/framework/framework-laptop-kmod/framework_laptop.c:412:9: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement]
  412 |         struct cros_ec_device *ec = dev_get_drvdata(ec_device);
      |         ^~~~~~
/home/korvin/work/framework/framework-laptop-kmod/framework_laptop.c: In function ‘ec_set_fan_duty’:
/home/korvin/work/framework/framework-laptop-kmod/framework_laptop.c:455:9: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement]
  455 |         struct cros_ec_device *ec = dev_get_drvdata(ec_device);
      |         ^~~~~~
/home/korvin/work/framework/framework-laptop-kmod/framework_laptop.c: In function ‘ec_count_fans’:
/home/korvin/work/framework/framework-laptop-kmod/framework_laptop.c:505:9: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement]
  505 |         struct cros_ec_device *ec = dev_get_drvdata(ec_device);
      |         ^~~~~~
/home/korvin/work/framework/framework-laptop-kmod/framework_laptop.c:513:9: error: ‘for’ loop initial declarations are only allowed in C99 or C11 mode
  513 |         for (size_t i = 0; i < EC_FAN_SPEED_ENTRIES; i++) {
      |         ^~~
/home/korvin/work/framework/framework-laptop-kmod/framework_laptop.c:513:9: note: use option ‘-std=c99’, ‘-std=gnu99’, ‘-std=c11’ or ‘-std=gnu11’ to compile your code
/home/korvin/work/framework/framework-laptop-kmod/framework_laptop.c: In function ‘framework_probe’:
/home/korvin/work/framework/framework-laptop-kmod/framework_laptop.c:690:9: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement]
  690 |         struct cros_ec_device *ec = dev_get_drvdata(ec_device);
      |         ^~~~~~
cc1: some warnings being treated as errors
make[2]: *** [scripts/Makefile.build:297: /home/korvin/work/framework/framework-laptop-kmod/framework_laptop.o] Error 1
make[1]: *** [Makefile:1911: /home/korvin/work/framework/framework-laptop-kmod] Error 2
make[1]: Leaving directory '/usr/src/linux-headers-5.15.0-97-generic'
make: *** [Makefile:12: modules] Error 2

gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0

The module has not been tested on a kernel that old. It could probably work with some modifications.

2 Likes

Same here. I am happy compiling and installing a kernel module. A little less compiling a full kernel, especially since that means having to redo it on every (security or other) fedora supplied kernel update.

On Arch Linux, I installed linux-mainline-6.11rc4-1 from the chaotic-aur repo. I see that the keyboard backlight control and fan/temp sensors are working now, but /sys/class/power_supply/BAT1/charge_control_end_threshold is still missing, and I don’t see the option to control it in KDE. According to the pull request, this should be provided by the cros_charge_control driver, and I do see that the driver is present and loaded

filename:       /lib/modules/6.11.0-rc4-1-mainline/kernel/drivers/power/supply/cros_charge-control.ko.zst
license:        GPL
author:         Thomas Weißschuh <linux@weissschuh.net>
description:    ChromeOS EC charge control
srcversion:     92B77CD0EA61B7EE89557CE
alias:          platform:cros-charge-control
depends:        
retpoline:      Y
intree:         Y
name:           cros_charge_control
vermagic:       6.11.0-rc4-1-mainline SMP preempt mod_unload 
sig_id:         PKCS#7
signer:         Build time autogenerated kernel key
sig_key:        32:D4:0F:FB:42:23:18:F3:60:32:42:67:82:C8:52:B0:40:99:F6:17
sig_hashalgo:   sha512
signature:      30:66:02:31:00:97:7D:A2:59:7B:20:22:BF:F4:C0:38:81:0E:3C:0C:
                00:D6:82:8C:69:9B:5E:F3:4C:FA:E9:7B:A8:A1:DA:B6:62:1F:AF:88:
                90:53:3E:CA:95:1C:8D:FE:D0:90:1D:B1:A8:02:31:00:94:1A:B6:41:
                6D:FE:1A:1E:F7:6C:6C:1D:B6:12:E0:0F:08:C3:96:ED:BD:0C:95:1C:
                11:6F:FB:EF:6D:5D:DD:DC:FC:C5:D8:C3:68:9B:09:C2:1E:3C:10:D2:
                9D:0A:12:24
parm:           probe_with_fwk_charge_control:Probe the driver in the presence of the custom Framework EC charge control (bool)

Is this just something that will work once the final 6.11 release is out?