After reading up on a eye-opening blog from Petter Reinholdtsen about laptop batteries, or more specifically Lithium-Ion laptop batteries, I figured I needed to try out the "TLP Linux Advanced Power Management" that I had been keeping an eye on for a while. tlp is yet another tool to control power usage on laptops (mostly Thinkpad, mine is a X120e). The novelty of tlp is the "hands off" approach: everything should be automatically configured for you...

Obviously, that means I then went on working for a few hours on breaking and fixing my laptop in random operations. I opened a bunch of pull requests on the interesting battery status package that Petter produced to make it work with my setup and make it display graphs directly (instead of into a file). I also rewrote the graphing tool in Python with SciPy in order to have cleaner labels and be able to deduce the date at which a battery would be completely unusuable because it can't recharge high enough. (At the time of writing, the battery estimated death time is december 7th, but that data is skewed because of a quick change in the battery charge after the BIOS upgrade, below.)

I then went on to try to limit my laptop charging to 80%, since this seems to make the battery last longer (sources from Petter: 1, 2, 3). Unfortunately, even after building a local (and trivial) backport of the tlp package to Debian stable (8.2/Jessie), I still couldn't access those controls, as TLP is really just a set of shell scripts that glue a bunch of stuff together.

The backport was simply:

apt-get source tlp
cd tlp*/
debuild
sudo dpkg -i ../tlp*.deb

I read here and there (and in Petter's post) that I needed the tp-smapi-dkms package, so I went ahead an installed it:

sudo apt install tp-smapi-dkms

(Yes, Jessie has a neat apt command now, it's great, upgrade now.)

Unfortunately, this still didn't work. I think the error back then was something like:

thinkpad_ec: thinkpad_ec_request_row: arg0 rejected: (0x01:0x00)->0x00
thinkpad_ec: thinkpad_ec_read_row: failed requesting row: (0x01:0x00)->0xfffffffb
thinkpad_ec: initial ec test failed

I have seen suggestions here and there to try the acpi-call-dkms package, but that was useless as it doesn't support my model (but may work with others!). The error there was:

acpi_call: Cannot get handle: Error: AE_NOT_FOUND

Note: I still have it installed - it's unclear what impact it has, and I do not want to break my current setup.

So I then started to look at upgrading my BIOS, for some reason. I was running version 1.13 (8FET29WW) from 05/06/2011. I was able to update to 1.17 (8FET33WW) from 11/07/2012, using the memdisk binary from the syslinux-common package, with some help from the quite useful grub-imageboot package:

sudo apt install syslinux-common grub-imageboot
wget https://download.lenovo.com/ibmdl/pub/pc/pccbbs/mobiles/8fuj10uc.iso
sudo mkdir /boot/images
sudo mv 8fuj10uc.iso /boot/images
sudo reboot

I found the image on the Thinkpad x120e support page from Lenovo (which happily bounces around, so don't rely on the above URLs too much). When I rebooted, I was offered to boot from the image by grub, which went on fine, considering it was running some version of DOS, which is always a bit scary considering it is software that is somewhat almost as old as me.

I wish I could have installed some free software in the BIOS instead of the outdated crap that Lenovo provides, but unfortunately, it seems this will never be possible with Libreboot or Coreboot, mostly because Intel is evil and installs backdoors in all their computers. Fun times.

Fortunately and surprisingly, the update worked and went on pretty smoothly. After that, I was able to set the charge limit with:

echo 80 | sudo tee /sys/devices/platform/smapi/BAT0/stop_charge_thresh

Amazing! I had almost forgotten why I almost bricked my system on a thursday, good thing that worked! Now the fun part was that, after some reboots or something I did, I am not sure what, the above stopped working: I couldn't load the drivers at all anymore, dmesg treating me with a nasty:

thinkpad_ec: thinkpad_ec_read_row: failed requesting row: (0x01:0x00)->0xfffffff0
thinkpad_ec: initial ec test failed

Now that is some obscure error message material! Fun stuff. I tried uninstalling tlp, the smapi modules, the acpi-call modules, rebooting, turning the machine off, removing the battery, nothing worked. Even more hilarious, the charge controler was now stuck at 80%: I had artificially destroyed 20% of the battery capacity in software. Ouch.

I think this may have been related to uninstalling the tp-smapi-dkms package without unloading it at first. I found some weird entries in my kern.log like this:

tp_smapi unloaded.
thinkpad_ec: thinkpad_ec_read_row: failed requesting row: (0x14:0x00)->0xfffffff0
hdaps: cannot power off
hdaps: driver unloaded.
thinkpad_ec: unloaded.

I think that after that point, I couldn't load either module, not even thinkpad_ec...

After tearing out a few more hairs and hammering my head on the keyboard randomly, I thought I could just try another BIOS upgrade, just for the fun of it. Turns out you actually can't rerun the upgrade, but you can change the model number through the same software, and this seems to reset some stuff. So I went back in the ISO image I had loaded earlier, and went on to change the model number (actually setting it to the same value, but whatever, it still ran the update). It turns out this seems to have reset a bunch of stuff and now everything works. I can use tlp setcharge and all the neat tools go well.

The two key commands are:

# limit charging to 80% of the battery, but not lower than 40%
sudo tlp setcharge 40 80
# clear the above setting and just charge the battery to 100%
sudo tlp fullcharge

It seems that the 40% bit isn't supported by my laptop, but whatever: the battery stays charged when on AC power anyways, so I don't really understand what the setting is for in the first place. The error there is:

smapi smapi: smapi_request: SMAPI error: Function is not supported by SMAPI BIOS (func=2116)
smapi smapi: __get_real_thresh: cannot get start_thresh of bat=0: Function is not supported by SMAPI BIOS

The second command is what I will need to remember to run before I unplug the laptop for a trip. I suspect this will be really annoying and I may end up disabling all this stuff and just yank the power cable out when the battery reaches 80%, by hand, when I need to.

But it was a fun geeking out, and hopefully this will be useful for others. And of course, the graphs from Petter will be interesting in a few months... Before the BIOS upgrade, the battery capacity was reported as 100% (actually, at 100.03%, which was strange). Now, the capacity is at 98.09%, which is probably just a more accurate reading that was fixed in the BIOS upgrade.

Finally, also see the useful thinkwiki troubleshooting page and especially their interesting BIOS upgrade documentation which inspired me to write my own version. I would have gladly contributed to theirs, but I seem to have lost my password on this site, with no recovery possible... The arch linux wiki has obviously excellent documentation as well, as usual.

before and after python graphing tool

I actually spent a fair bit of time learning Numpy and Matplotlib... I originally tried to build the graphs with R but that never quite worked and besides, it seems that R has its own incomprehensible DSL that I can't wrap my head around. The only reason I was able to use it at all is because people helped me in IRC. With Numpy, I feel I connect with my already existing knowledge of Python, which makes way more sense.

Also, you can see the different between the old graph and the new graph in this comment. The data was generated with another Python tool I wrote for this, so I ended up spending a lot of time working on this. :)

Comment by anarcat [id.koumbit.net]
On the lower battery threshold

It seems that the 40% bit isn't supported by my laptop, but whatever: the battery stays charged when on AC power anyways, so I don't really understand what the setting is for in the first place. The error there is:

Just to mention that I also have the same problem with the low battery threshold with tpsmapi, the only software I am using for battery management, and a thinkpad T520; but I can confirm that it worked fine on a R60 I used in the past. In both cases, the upper threshold works as expected, stopping the charge at that point.

As I understand it, Li-Ion batteries can last longer if they are not usually reaching both full discharge and full charge and staying at that point for a long time; in this context the low battery threshold is trying to prevent that the battery starts charging when it is at a greater point than the 40% (i.e. only charging after reaching a point lower than 40% and therefore trying to prevent a new unneeded small charge, e.g. no charge if at 78%).

Comment by Marco