MrChromebox / firmware

Issue tracker for firmware issues
78 stars 15 forks source link

EC firmware feature request: battery charge limiting #420

Open mpeter50 opened 1 year ago

mpeter50 commented 1 year ago

I wanted to set up limiting the charge level of the battery of my chromebook, so that it may remain useful for a longer time, but it seems currently it may not be possible.

So far I have found out that on Linux systems where the kernel supports controlling how the battery is charged, control and monitoring happens through virtual files of the power supply device class, in the sysfs file system, more specifically these (usually):

<battery name> is usually BAT0, but sometimes slightly different.

Source is the Linux kernel documentation and this issue at a software's (TLP) repository that deals with power management of laptops on Linux systems.

However, on my system installation, these files do not exist, and the only writeable files in that directory are alarm and uevent. The same is true for a live system I tried, which used kernel version 6.1.2. After installing TLP from the package manager, running tlp-stat --battery confirms that my device is not supported by this tool. The tool uses a standard Linux kernel mechanism for accessing battery information and control interfaces, so I suspect this also means that battery charge control is currently not supported by the system.


Device model: Lenovo 500e 2nd Gen (Code name: Phaser360S, Board name: Octopus) Firmware: UEFI firmware 20221129 Kernel version: 5.14.21 Operating system: openSUSE Leap 15.4

mpeter50 commented 1 year ago

In the /sys/class/power_supply/ directory I have subdirectories AC, BAT0, CROS_USBPD_CHARGER0 and CROS_USBPD_CHARGER1. The 2nd one seems to be the actual battery, and the 1st, 3rd and 4th seem to be related to charging controllers. The 3rd indicates charging only if I plug in the charger to the left side USB-C port, the 4th only if I plug in the charger to the right side USB-C port, and the 1st one indicated charging in both cases, just as BAT0.

ghost commented 9 months ago

So apparently, this feature is supported on at least some ChromeOS devices. The most important source of information seems to be the ChromeOS Adaptive Charging page. A brief summary.

Caveat: I'm a prospective Chromebook buyer and do not have any ChromeOS device to test.

ghost commented 9 months ago

@MrChromebox Do you know of any device supporting the chargecontrol and chargecurrentlimit sub-commands? Can we change this before flashing custom firmware and retain the settings after flashing?

MrChromebox commented 9 months ago

I'm not sure when this feature was introduced, nor how easily it could be backported. ectool isn't a part of ChromeOS anymore even, so I don't know how you'd change it before flashing - it would need to be done from the OS afterwards

ghost commented 9 months ago

I have no idea about the details, but this documentation page was added about two years ago. Based on what I've read, some devices do support this feature.

https://chromium.googlesource.com/chromiumos/platform2/+/d31efe96afbe52c7edfd857f988108b521c252fd

ghost commented 9 months ago

Oh, and the Charge Limit section in particular was added six months ago, so this feature is very new. https://chromium.googlesource.com/chromiumos/platform2/+/647763a9faa54d81793bc9ae91c44fd17e1eca35

mpeter50 commented 9 months ago

Oh I found some additional info, but totally forgot to comment it here. Thanks for reminding me!

For the past few months I have been using the ectool made for the framework laptop to limit charging. If I remember it right, those use the same EC firmware. I think I have built it on the machine from framework's source, but if more accurate details are needed I think I have it written up somewhere..

ectool chargecontrol normal <lower> <upper> on my laptop works by disabling charging as soon as the charge percentage reaches the <upper> value. After that, the battery starts discharging, and charging is probably re-enabled when the percentage reaches the <lower> value. Probably, as I didn't test it, because I didn't like this way of conserving battery life. Sure its better than without, but there's an even better way! ectool chargecontrol idle disables charging, and as I understand, the laptop will draw power from the charger instead of the battery. Charge percentage does not change when this is in use, and /sys/class/power_supply/BAT0/current_now will say 0 (while it says something higher when it is using the battery when charger is not connected). I think I have also read it somewhere that this does not work with any USB-C charger, but so far I haven't tried it with anything other than it's original one.

Right now, it also means I have to manually put the EC into idle or normal charging mode when I want to change it, even though I think KDE has a toggle for it, for when the kernel supports it. The plan for myself was to attempt to add support for this to the kernel. I have read the relevant source and I think I have an idea where would this be added, but so far I have never written kernel modules and didn't have the time to continue it. In the meantime I had made helper commands that shorten (and limit the power of) the ectool commands, and marked these in the sudoers as executable without any password, and am using it from the Yakuake drop down terminal. It's a bit inconvenient, even more when I would use it folded away, but I mostly keep it in idle charging mode.

A few more things I have noticed. If you turn it to normal mode with battery sustainer, but it was in idle mode, it wont apply the battery sustainer, and it will charge all the way to 100%. If you rerun the same command it will also apply the battery sustainer. Idle charging can be only enabled (with the ectool at least) while the device is charging, otherwise it will print an error (EC result 2 (ERROR), Is AC connected?), but it can be switched back to normal mode any time.

I'm not sure when this feature was introduced

I was using it since ~february, I think with version 4.18.2 of your firmware package.


If you want to test something, let me know and I'll take a look at it. Currently I use the MrChromebox 4.21.0 firmware, with openSUSE 15.5 and kernel version 5.14.21

ghost commented 9 months ago

From that page:

If lower and upper are the same, the charger will idle (no current to or from the battery) while at that charge.

Did you try this? What laptop model do you have?

mpeter50 commented 9 months ago

Did you try this?

Not yet, I think. I'll try it out.

What laptop model do you have?

I have a Lenovo 500e 2nd Gen (Code name: Phaser360S, Board name: Octopus)

mpeter50 commented 9 months ago

If I run ectool chargecontrol normal 0 0, /sys/class/power_supply/BAT0/current_now will read 0 (it had a higher value before running the command).

However, the help page of the command suggests to me that this way its not actually not charging, but that its trying to keep it in balance:

# ectool chargecontrol help
[...]
Usage: chargecontrol normal <lower> <upper>
    Enable battery sustainer. <lower> and <upper> are battery SoC
    between which EC tries to keep the battery level.

The documentation linked by you says explicitely that this is not whats happening (below is a quote from it), but I thought its worth to mention that these contradict.

If lower and upper are the same, the charger will idle (no current to or from the battery) while at that charge.

ghost commented 9 months ago

I thought you should use

ectool chargecontrol normal 70 70

to achieve: do not charge past 70 percent, and charge to 70 percent if under 70 percent. If currently is 70 percent, draw power from AC.

Or,

ectool chargecontrol normal 30 70

to achieve: do not charge past 70 percent, and charge to 70 percent if under 30 percent. If currently is between 30 and 70 percent, draw power from AC.

Here is the source code:

 https://chromium.googlesource.com/chromiumos/platform/ec/+/refs/heads/main/common/charge_state.c

Quote from line 912:

/*
 * The sustain range is defined by 'lower' and 'upper' where the equal
 * values are inclusive:
 *
 * |------------NORMAL------------+--IDLE--+---DISCHARGE---|
 * 0%                             ^        ^              100%
 *                              lower    upper
 *
 * The switch statement below allows the sustainer to start with any soc
 * (0% ~ 100%) and any previous lower & upper limits. It sets mode to
 * NORMAL to charge till the soc hits the upper limit or sets mode to
 * DISCHARGE to discharge till the soc hits the upper limit.
 *
 * Once the soc enters in the sustain range, it'll switch to IDLE. In
 * IDLE mode, the system power is supplied from the AC. Thus, the soc
 * normally should stay in the sustain range unless there is high load
 * on the system or the charger is too weak.
 *
 * Some boards have a sing capacitor problem with mode == IDLE. For such
 * boards, a host can specify EC_CHARGE_CONTROL_FLAG_NO_IDLE, which
 * makes the sustainer use DISCHARGE instead of IDLE. This is done by
 * setting lower != upper in V2, which doesn't support the flag.
 */

case CHARGE_CONTROL_NORMAL:
    /* Currently charging */
    if (sustain_soc.upper < soc) {
        /*
         * We come here only if the soc is already above the
         * upper limit at the time the sustainer started.
         */
        new_mode = CHARGE_CONTROL_DISCHARGE;
    } else if (sustain_soc.upper == soc) {
        /*
         * We've been charging and finally reached the upper.
         * Let's switch to IDLE to stay.
         */
        if (sustain_soc.flags & EC_CHARGE_CONTROL_FLAG_NO_IDLE)
            new_mode = CHARGE_CONTROL_DISCHARGE;
        else
            new_mode = CHARGE_CONTROL_IDLE;
    }
    break;
case CHARGE_CONTROL_IDLE:
    /* Discharging naturally */
    if (soc < sustain_soc.lower)
        /*
         * Presumably, we stayed in the sustain range for a
         * while but finally fell off the range. Let's charge to
         * the upper.
         */
        new_mode = CHARGE_CONTROL_NORMAL;
    else if (sustain_soc.upper < soc)
        /*
         * This can happen only if sustainer is restarted with
         * decreased upper limit. Let's discharge to the upper.
         */
        new_mode = CHARGE_CONTROL_DISCHARGE;
    break;
case CHARGE_CONTROL_DISCHARGE:
    /* Discharging actively. */
    if (soc <= sustain_soc.upper &&
        !(sustain_soc.flags & EC_CHARGE_CONTROL_FLAG_NO_IDLE))
        /*
         * Normal case. We've been discharging and finally
         * reached the upper. Let's switch to IDLE to stay.
         */
        new_mode = CHARGE_CONTROL_IDLE;
    else if (soc < sustain_soc.lower)
        /*
         * This can happen only if sustainer is restarted with
         * increase lower limit. Let's charge to the upper (then
         * switch to IDLE).
         */
        new_mode = CHARGE_CONTROL_NORMAL;
    break;

If I understood correctly, this would provide us one of the most flexible charging controls available.

mpeter50 @.***> writes:

However, the help page of the command suggests to me that this way its not actually not charging, but that its trying to keep it in balance:

ectool chargecontrol help

[...] Usage: chargecontrol normal Enable battery sustainer. and are battery SoC between which EC tries to keep the battery level.

Maybe the help page is outdated.

P.S. From their test cases: https://chromium.googlesource.com/chromiumos/platform/ec/+/refs/heads/main/test/sbs_charging.c

Using ``ectool chargecontrol normal 79 80'' might be useful:

/* Enable sustainer. */
rv = battery_sustainer_set(version, 79, 80, flags);
mpeter50 commented 9 months ago

ectool chargecontrol normal 70 70

to achieve: do not charge past 70 percent, and charge to 70 percent if under 70 percent. If currently is 70 percent, draw power from AC.

Or,

ectool chargecontrol normal 30 70

to achieve: do not charge past 70 percent, and charge to 70 percent if under 30 percent. If currently is between 30 and 70 percent, draw power from AC.

I think that should be right. I know that the latter is working that way, and the former should work that way based on how ectool chargecontrol normal 0 0 works.

  • Once the soc enters in the sustain range, it'll switch to IDLE. In
  • IDLE mode, the system power is supplied from the AC. Thus, the soc
  • normally should stay in the sustain range unless there is high load
  • on the system or the charger is too weak

Oh, so this is known that it basically automates IDLE mode. That's good to know.

If I understood correctly, this would provide us one of the most flexible charging controls available.

Yeah, based on the code you quoted I might not even need to manually set idle and normal modes, as it switches to IDLE when it has reached the upper limit specified in the command. Such a command could also be set up to run at every boot. Maybe I could use a second "profile" thatr sets the lower limit higher when I know I'll need a little more until I can charge it again.

I will need to test this. Honestly I have a faint memory as if the battery sustainer would have set the charging state to DISHCHARGE not IDLE when I was testing it, but maybe I'm wrong, or they have changed it since I have tested it (almost a year ago) and I already have the new code.

Maybe the help page is outdated.

Yeah, possibly, that is coming from ectool, which I haven't rebuilt since ~march.

Using ``ectool chargecontrol normal 79 80'' might be useful:

The code you quoted mentions the case when power consumption is larger than what the charger can provide. For that case, this (or even a larger range) would be useful I think, as it wouldn't recharge to 80% immediately when it falls to 79, but with e.g. ectool chargecontrol normal 70 80 only after it has fallen a whole 10%, to 69%.

ghost commented 9 months ago

mpeter50 @.***> writes:

I will need to test this. Honestly I have a faint memory as if the battery sustainer would have set the charging state to DISHCHARGE not IDLE when I was testing it, but maybe I'm wrong, or they have changed it since I have tested it (almost a year ago) and I already have the new code.

This last sentence from the quote above might be related:

Yeah, possibly, that is coming from ectool, which I haven't rebuilt since ~march.

No, the master branch from Google has the exact help message as yours. I guess the developers haven't synced the help message with new features. In any case, reading the source code is a more reliable way to determine how does ectool and EC interact.

mpeter50 commented 9 months ago

This last sentence from the quote above might be related:

  • Some boards have a sing capacitor problem with mode == IDLE. For
  • such boards, a host can specify EC_CHARGE_CONTROL_FLAG_NO_IDLE,
  • which makes the sustainer use DISCHARGE instead of IDLE. This is
  • done by setting lower != upper in V2, which doesn't support the
  • flag.

Yeah, thats a possibility, and that would be great, to say the least. By the way there is certainly a capacitor that sings in it, but that certainly does not depend on IDLE mode, but instead as I have noticed it depends on power consmuption. It does that independently of whether it is connected to a charger or not.

ChocolateLoverRaj commented 6 months ago

What exactly is this issue requesting? I can think of two possibilities:

  1. Make charge control more standard by making it show up on /sys/class/power_supply/<battery name>/charge_start_threshold so that it can be used with TLP
  2. Add a charge control setting to the firmware so that it controls charging even white it's off and after a EC reset.
t-8ch commented 3 months ago

Here is a kernel patch that hooks up the ChromeOS EC charge control to the standard Linux sysfs files: https://lore.kernel.org/lkml/20240519-cros_ec-charge-control-v1-0-baf305dc79b8@weissschuh.net/

Testing would be welcome.

ChocolateLoverRaj commented 3 months ago

I'll try to test it. But I have a few questions about it:

t-8ch commented 3 months ago

I'll try to test it.

Thanks!

Is it supposed to work on Chromebooks?

Yes, it does not use any Framework specific APIs. Also on my FW 13 AMD ectool chargecontrol works just fine.

Does it work with Chromebooks that don't support charge control sustainer?

I don't think so.

ChocolateLoverRaj commented 3 months ago

My chromebook (Jinlon) doesn't support charge control sustainer. I can only do chargecontrol idle. I can't do chargecontrol normal 80 80.

t-8ch commented 3 months ago

It would need extensions in the driver to use the old protocol. I hoped not needing this. I'll take a look.

ChocolateLoverRaj commented 3 months ago

I think that supporting charge control for Chromebooks without sustainer is pretty important because the sustainer only started with 11th Gen Chromebooks (I'm pretty sure), and a lot of people who use Linux on Chromebooks use Chromebooks which are 2020 or older models.

t-8ch commented 3 months ago

Please try https://git.sr.ht/~t-8ch/linux/log/b4/cros_ec-charge-control

It should only show the charge_behaviour sysfs file.

Can your device do force discharge?

ChocolateLoverRaj commented 3 months ago

Can your device do force discharge?

Yes. normal, idle, and discharge all work. It doesn't support GET or sustainer.

Please try https://git.sr.ht/~t-8ch/linux/log/b4/cros_ec-charge-control

I will. I'm on NixOS btw so it should be easy to experiment with the kernel patches without messing up my normal system. This is my first time using custom kernel packages so it may take a while for me to figure it out.

t-8ch commented 3 months ago

This is my first time using custom kernel packages so it may take a while for me to figure it out.

Good luck!

Note: I have some other drivers for which I would also be grateful to get some testing. https://community.frame.work/t/request-testing-of-linux-drivers-on-all-laptop-models/51283

ChocolateLoverRaj commented 3 months ago

Note: I have some other drivers for which I would also be grateful to get some testing. https://community.frame.work/t/request-testing-of-linux-drivers-on-all-laptop-models/51283

I am happy to test all drivers that I can on my Jinlon.

LED Driver - I'm guessing from what I read from your post that this will let me control the two LEDs on Jinlon, next to the USB-C port on the left and right side. This would be very cool.

Keyboard backlight - It works just fine OOTB, I'm guessing I should test if it continues to work just fine with your patches.

Sensors - I'm not sure what this patch does. I'm guessing that it will show

[rajas@nixos:~]$ sudo ectool temps all
--sensor name -------- temperature -------- ratio (fan_off and fan_max) --
Charger               300 K (= 27 C)           4% (298 K and 343 K)
5v Reg                302 K (= 29 C)          16% (298 K and 323 K)
CPU                   304 K (= 31 C)        N/A (fan_off=0 K, fan_max=0 K)
IR Sensor             301 K (= 28 C)        N/A (fan_off=0 K, fan_max=0 K)

through the file system, and maybe adjusting the thresholds that activate fans in the future.

t-8ch commented 3 months ago

All your guesses are correct. The keyboard backlight is probably already probed through ACPI.

ChocolateLoverRaj commented 3 months ago

I got the kernel to build, I haven't tested it yet. Is there a git repo for the keyboard backlight patch like the other sourcehut branches?

t-8ch commented 3 months ago

I pushed it now to the same repo, branch b4/cros_ec-kbd-led-framework. Could you first test the tag sent/20240505-cros_ec-kbd-led-framework-7e2e831bc79c-v2 instead?

With it you may see the following warning in dmesg:

Led cros_ec::kbd_backlight renamed to cros_ec::kbd_backlight_1 due to name collision.

Can you confirm this? It should not happen anymore with the branch.

ChocolateLoverRaj commented 3 months ago

I will continue editing this comment as I test the other kernels:

sent/20240505-cros_ec-kbd-led-framework-7e2e831bc79c-v2

I can control thee keyboard light using my Chromebook's keyboard brightness up and down keys. I get this message:

[rajas@nixos:~]$ dmesg | grep rename
[    7.406100] cros-keyboard-leds cros-keyboard-leds.6.auto: Led chromeos::kbd_backlight renamed to chromeos::kbd_backlight_1 due to name collision

I can change the keyboard light by writing to either /sys/class/leds/chromeos::kbd_backlight_1/brightness or /sys/class/leds/chromeos::kbd_backlight/brightness

cros_ec-charge-control

/sys/class/power_supply/BAT0/charge_behaviour shows up. Thresholds don't show up. The charge control was idle when I booted my Chromebook, but ig since the EC doesn't support GET there's no way of knowing (besides guessing based on battery current).

Writing to the file sets the charge control as expected. However, I think my Chromebook can't do force discharge cuz if I set it to force discharge (with either the /sys file or ectool), it only discharges a tiny bit for 2 seconds and then the current becomes 0mA. I will see if this happens on a normal kernel too.

cros_ec-hwmon

The sensors command shows this:

cros_ec-isa-0000
Adapter: ISA adapter
fan1:                                0 RPM
fan2:                                0 RPM
                         Charger:  +36.9°C  
                          5v Reg:  +34.9°C  
                             CPU:  +39.9°C  
                       IR Sensor:  +32.9°C

which matches with ectool temps all, so nice. Fan speeds also work.

cros_ec-led

cros_ec:multicolor:left, cros_ec:multicolor:right, and cros_ec:white:power show up in /sys/class/leds.

Power LED

trigger

Setting none or cros_ec-auto works. However, there are a bunch of other options that seem to do the same as none. The options are none rc-feedback kbd-scrolllock kbd-numlock kbd-capslock kbd-kanalock kbd-shiftlock kbd-altgrlock kbd-ctrllock kbd-altlock kbd-shiftllock kbd-shiftrlock kbd-ctrlllock kbd-ctrlrlock mmc1 rfkill-any rfkill-none mmc0 BAT0-charging-or-full BAT0-charging BAT0-full BAT0-charging-blink-full-solid AC-online CROS_USBPD_CHARGER0-online CROS_USBPD_CHARGER1-online [cros_ec-auto] hid-0018:27C6:0E0C.0001-battery-charging-or-full hid-0018:27C6:0E0C.0001-battery-charging hid-0018:27C6:0E0C.0001-battery-full hid-0018:27C6:0E0C.0001-battery-charging-blink-full-solid phy0rx phy0tx phy0assoc phy0radio rfkill1 rfkill8. I tried setting it to AC-online, expecting that the light would be on if the charger is plugged in. The charger was plugged in when I did that, but the LED did not turn on. Plugging in the charger did not turn it on either.

Other than that, setting brightness to 0 or 1 turns it on or off as expected

Left and right charging indicators (white and amber colors)

brightness

Works as expected

trigger

Setting to none or cros_ec-auto works. Same comments as the power LED's trigger file.

multi_intensity

By default the value of this is 100 100. From experimenting, if set to 0 0, the LED will be off. If set to not-zero anything, the LED will be white. If set to 0 not-zero, the LED will be amber. However, the file seems to have no valid range. It accepts any number, even >100 and negative numbers. And even 1 1 is confusing because it seems to be doing white + amber = white. The LED cannot mix white and amber together. It can only do white, amber, or off.

cros_ec-kbd-led-framework

Keyboard backlight works as normal, no "renamed" message.

t-8ch commented 3 months ago

Thanks for your tests!

It would be great to get some Tested-by tags for them on LKML. If you give me an email address I'll add you to Cc for future submissions. And you could test those formally, providing the Tested-by tag.

ChocolateLoverRaj commented 3 months ago

My email address is paranjperajas@gmail.com

ChocolateLoverRaj commented 3 months ago

I updated my comment

t-8ch commented 3 months ago

I tried setting it to AC-online ...

That one is a (very old) bug: https://lore.kernel.org/lkml/20240526-acpi-ac-changed-v1-1-f4b5997753bb@weissschuh.net/

mpeter50 commented 1 month ago

Hi! Sorry for not replying so far, I'll also test the driver on my phaser360s. Thanks for your work! It might take some time as I'm a first time kernel patcher too.

Which version should I test? These on the mailing list, or an other set of them?

You mentioned a few other patches. Do those also apply for chromebooks? If so, I would patch whichever applies for my hardware.

t-8ch commented 1 month ago

@mpeter50 All of these changes are in Linus' master branch. If you try https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/ you get everything without any manual patching. Just make sure to enable the "CHROMEOS EC" modules.

mpeter50 commented 1 week ago

I must have made some mistake, because I dont see anything new in the /sys/class/power_supply/BAT0/ directory. For example there is no charge_behaviour file, and the only writable files are alarm and uevent.

I'm testing on a KDE Neon installation, I have got the kernel from here: git://git.launchpad.net/~ubuntu-kernel-test/ubuntu/+source/linux/+git/mainline-crack At the time I have checked and as I remember I have found your patches in it. For the rest, I have basically followed this Debian manual. Checked out the v6.10 ref, edited the .config file where necessary. I assumed you meant to enable the CONFIG_CROS_EC kernel config setting, I have left it as m, in my understanding that enables it too. This is the kernel config that I was using: https://gist.github.com/mpeter50/98df1a0d5f8804caab5567c273c81498

Then built the kernel by running make deb-pkg LOCALVERSION=-custom. The kernel was built, I grabbed the linux-image-6.10.0-custom_6.10.0-8_amd64.deb from the outer directory, and put that on a pendrive. Booted the KDE Neon system on by chromebook, installed the package with dpkg -i linux-image-6.10.0-custom_6.10.0-8_amd64.deb. Other than a warning about the i915 module missing some firmware, there was no other complaint.

Then I booted to my main system, openSUSE Leap, and begged to the bootloader configurator to reconfigure GRUB, so that it gets the entry for KDE Neon's new kernel. When that was done, I have booted KDE Neon as normal. Some diagnostic info from it:

uname -a
Linux teszt-phaser360 6.10.0-custom #8 SMP PREEMPT_DYNAMIC Tue Jul 23 01:26:08 CEST 2024 x86_64 x86_64 x86_64 GNU/Linux
lsmod | grep cros
cros_ec_lid_angle      12288  0
cros_ec_sensors        12288  0
cros_ec_sensors_core    20480  2 cros_ec_sensors,cros_ec_lid_angle
industrialio_triggered_buffer    12288  2 cros_ec_lid_angle,cros_ec_sensors_core
kfifo_buf              12288  2 industrialio_triggered_buffer,cros_ec_sensors_core
industrialio          122880  5 industrialio_triggered_buffer,cros_ec_sensors,cros_ec_lid_angle,kfifo_buf,cros_ec_sensors_core
cros_usbpd_logger      16384  0
cros_usbpd_charger     24576  0
cros_ec_chardev        12288  0
cros_ec_sysfs          12288  0
cros_ec_sensorhub      36864  1 cros_ec_sensors_core
cros_ec_debugfs        12288  0
cros_usbpd_notify      20480  1 cros_usbpd_charger
cros_ec_dev            12288  0
cros_ec_keyb           20480  0
matrix_keymap          12288  1 cros_ec_keyb
cros_ec_lpcs           20480  0
cros_ec                20480  1 cros_ec_lpcs
ls -hal /sys/class/power_supply/BAT0/
total 0
drwxr-xr-x 4 root root    0 aug   24 07:40 .
drwxr-xr-x 3 root root    0 aug   24 07:40 ..
-rw-r--r-- 1 root root 4,0K aug   24 07:43 alarm
-r--r--r-- 1 root root 4,0K aug   24 07:40 capacity
-r--r--r-- 1 root root 4,0K aug   24 07:43 capacity_level
-r--r--r-- 1 root root 4,0K aug   24 07:40 charge_full
-r--r--r-- 1 root root 4,0K aug   24 07:40 charge_full_design
-r--r--r-- 1 root root 4,0K aug   24 07:40 charge_now
-r--r--r-- 1 root root 4,0K aug   24 07:40 current_now
-r--r--r-- 1 root root 4,0K aug   24 07:40 cycle_count
lrwxrwxrwx 1 root root    0 aug   24 07:43 device -> ../../../PNP0C0A:00
drwxr-xr-x 3 root root    0 aug   24 07:40 hwmon1
-r--r--r-- 1 root root 4,0K aug   24 07:40 manufacturer
-r--r--r-- 1 root root 4,0K aug   24 07:40 model_name
drwxr-xr-x 2 root root    0 aug   24 07:43 power
-r--r--r-- 1 root root 4,0K aug   24 07:40 present
-r--r--r-- 1 root root 4,0K aug   24 07:40 serial_number
-r--r--r-- 1 root root 4,0K aug   24 07:40 status
lrwxrwxrwx 1 root root    0 aug   24 07:40 subsystem -> ../../../../../../../../../class/power_supply
-r--r--r-- 1 root root 4,0K aug   24 07:40 technology
-r--r--r-- 1 root root 4,0K aug   24 07:40 type
-rw-r--r-- 1 root root 4,0K aug   24 07:40 uevent
-r--r--r-- 1 root root 4,0K aug   24 07:40 voltage_min_design
-r--r--r-- 1 root root 4,0K aug   24 07:40 voltage_now

By the output of lsmod, it looks to me it has loaded the cros kernel modules. I also see devices like CROS_USBPD_CHARGER0 in /sys/class/power_supply/, so I think the cros modules should be present and loaded.

What did I miss? If it cannot be known from the above, please let me know what should I do to get to know it.

t-8ch commented 1 week ago

uname -a Linux teszt-phaser360 6.10.0-custom #8 SMP PREEMPTDYNAMIC Tue Jul 23 01:26:08 CEST 2024 x8664 x8664 x8664 GNU/Linux

Should be 6.11.

mpeter50 commented 1 week ago

Oh. Well, since then they have started releasing rc-s of 6.11, so I'll build the latest one, 6.11-rc4. It'll be easier now that I have done that once already :D

mpeter50 commented 1 week ago

With 6.11 I see the control files, and KDE in the advanced power settings also shows the charging threshold controls. I'll try how it works here and will report back with the results.

mpeter50 commented 1 week ago

All tests are executed with the original charger connected.

Test checklist: (this list will be updated in place)

Additional good-to-know tests:


When the charge controller was set to idle mode with the ectool on the other system, after rebooting to the test system /sys/class/power_supply/BAT0/charge_behaviour says it is in auto mode. Rebooting back to the other system, ectool chargecontrol says thats in normal mode now.

Setting charge_behaviour to inhibit-charge results in current_now going 0, and setting it to auto results in it getting a positive value, so this indicates that charge inhibition works as expected.

Rebooting to the same system results in the charge_behaviour being set to auto. current_now has a positive value at this point (charger is connected).

Rebooting to the old system results in the IDLE status being preserved, if the new system has set charge_behaviour to inhibit-charge. Rebooting back to the test system will result in the charge_behaviour being set to auto. current_now has a positive value at this point (charger is connected).

mpeter50 commented 1 week ago

force-discharge mode works, it does not charge the battery when on charger.

start and end thresholds dont seem to work. A minute ago I was on 90%, in force-discharge mode. I have set charge_control_start_threshold to 70, charge_control_end_threshold to 85, charge_behaviour to auto (in this order), and now I'm on 92%.

anshshettyy commented 3 days ago

I've taken the following steps to try and resolve the issue with setting battery charge thresholds on the Lenovo 500e 2nd Gen Chromebook (openSUSE Leap 15.4, Kernel 5.14.21):

Kernel Update:

Update the kernel to the latest stable version using the Kernel: stable repository: bash

sudo zypper ar -f http://download.opensuse.org/repositories/Kernel:/stable/standard/ kernel-repo sudo zypper ref sudo zypper in --from kernel-repo kernel-default sudo reboot After the reboot, the new kernel version is active. Checked for Sysfs Battery Control Files:

Navigated to the following: /sys/class/power_supply/ and found the battery directory (e.g., BAT0). However, charge_start_threshold and charge_stop_threshold files were still not present. Installed and Configured TLP (Optional):

Installed TLP to manage power settings: bash

sudo zypper install tlp sudo tlp-stat --battery Unfortunately, TLP does not support this device, confirming that battery charge control is not possible via TLP. Checked BIOS/UEFI Settings (Optional):

I entered the BIOS/UEFI settings but did not find any options for setting battery charge thresholds.

The outcome of this is that despite updating the kernel and checking all the possible configurations, the required sysfs files for battery charge control (charge_start_threshold, charge_stop_threshold) are not available. This indicates that this feature is not currently supported for this device under Linux. It may require specific driver support or a newer kernel release.

Akrai commented 3 days ago

I've taken the following steps to try and resolve the issue with setting battery charge thresholds on the Lenovo 500e 2nd Gen Chromebook (openSUSE Leap 15.4, Kernel 5.14.21):

Kernel Update:

Update the kernel to the latest stable version using the Kernel: stable repository: bash

As far as I know, the feature is available in Linux 6.11

mpeter50 commented 3 days ago

@anshshettyy

I'm also using leap 15.4 as my main OS, and this is expected. Leap maintains and uses very old kernels, as with almost everything else, and it will take some time (1-2 years?) until they will transition to the 6.11 version of the kernel.

You'll have to build a new kernel, from the sources of the 6.11 version, if you want to make use of t-8ch's additions.

I entered the BIOS/UEFI settings but did not find any options for setting battery charge thresholds.

If you are using the Coreboot firmware, yeah, thats very barebones, it does not have hardware-specific settings.

But also be aware, that currently these do not work properly:

t-8ch commented 3 days ago

@mpeter50

Can you provide the output of sudo ectool cmdversions 0x0096?

charge start and end tresholds

The work correctly when set via ectool, correct?

charge_behaviour is always reset to auto when booting the system that has the new driver

This is intentional. The kernel has no place to store the settings and read-back from the EC does not work.

mpeter50 commented 1 day ago

Can you provide the output of sudo ectool cmdversions 0x0096?

Here it is:

$ sudo ectool cmdversions 0x0096
Command 0x96 supports version mask 0x00000006

charge start and end tresholds

The work correctly when set via ectool, correct?

Yes, it does. If I set up charge thresholds with ectool chargecontrol normal 80 85, then it will start charging a little after it reached 79, and stop charging at around 85.

But, I always have had to issue the ectool chargecontrol normal command twice if I also want to set the charge limits, when the charging mode wasn't already NORMAL before running the command. If I only do it once, despite passing the numbers, it does not get applied, and reading the status with ectool chargecontrol will say this:

Charge mode = NORMAL (0)
Battery sustainer = off (-1% ~ -1%)

charge_behaviour is always reset to auto when booting the system that has the new driver

This is intentional. The kernel has no place to store the settings and read-back from the EC does not work.

Oh, I see. Would it be feasible to have a kernel parameter that can override the value it sets at startup? I would prefer to always have it in idle mode, instead of charging to 100% every time I reboot the system. But also, which part of it does not work? The ectool command can do that, I (relatively) frequently use it to see the current setting.


To be clear, I have answered all of the above according to the system that runs the old kernel.

t-8ch commented 1 day ago

But, I always have had to issue the ectool chargecontrol normal command twice if I also want to set the charge limits, when the charging mode wasn't already NORMAL before running the command.

That is very useful information. Let me cook up a patch for you to test that does the same for the driver.

Would it be feasible to have a kernel parameter that can override the value it sets at startup?

That would be possible, but why not use a custom script?

But also, which part of it does not work?

If charge limits are applied and normal mode is active, while charging is inhibited, ectool will show "IDLE" instead of normal. This is a mismatch with the kernel APIs.

t-8ch commented 1 day ago

@mpeter50 Can you try this:

diff --git a/drivers/power/supply/cros_charge-control.c b/drivers/power/supply/cros_charge-control.c
index 17c53591ce19..7313cbfc5f2e 100644
--- a/drivers/power/supply/cros_charge-control.c
+++ b/drivers/power/supply/cros_charge-control.c
@@ -84,6 +84,7 @@ static int cros_chctl_send_charge_control_cmd(struct cros_ec_device *cros_ec,
 static int cros_chctl_configure_ec(struct cros_chctl_priv *priv)
 {
        struct ec_params_charge_control req = {};
+       int ret;

        req.cmd = EC_CHARGE_CONTROL_CMD_SET;

@@ -111,6 +112,13 @@ static int cros_chctl_configure_ec(struct cros_chctl_priv *priv)
                req.sustain_soc.upper = -1;
        }

+       /* Some devices need the command to be sent twice to apply */
+       if (req.sustain_soc.lower != -1) {
+               ret = cros_chctl_send_charge_control_cmd(priv->cros_ec, priv->cmd_version, &req);
+               if (ret)
+                       return ret;
+       }
+
        return cros_chctl_send_charge_control_cmd(priv->cros_ec, priv->cmd_version, &req);
 }