-
Notifications
You must be signed in to change notification settings - Fork 0
Description
Device Information
- Framework Laptop 13 (AMD Ryzen™ 7040 Series)
- BIOS 03.17 (11/14/2025)
- Arch Linux kernel 6.18.3
The Bug
My battery has 300-some charge-cycles after 2.5 years, but sysfs and thus other linux tools report 65 charge cycles:
$ upower -b | grep -i cycle
charge-cycles: 65
$ cat /sys/class/power_supply/BAT1/cycle_count
65
$ sudo ectool battery | grep -i cycle
Cycle count 321
That is 256 off - it appears that the value was truncated to 8 bits.
Credit to Nicolas Masse for initially noticing this in https://community.frame.work/t/battery-cycle-count-wrong-readings-on-linux-when-cycle-count-256/79474/1
Anyway, this sysfs value comes from ACPI:
[ 0.338572] ACPI: battery: Slot [BAT1] (battery present)
Linux copies this as a 32-bit integer from the ACPI response buffer (in drivers/acpi/battery.c):
struct acpi_battery {
...
int cycle_count;static const struct acpi_offsets extended_info_offsets[] = {
...
{offsetof(struct acpi_battery, cycle_count), 0},static int extract_package(struct acpi_battery *battery,
union acpi_object *package,
const struct acpi_offsets *offsets, int num)
{
...
int *x = (int *)((u8 *)battery + offsets[i].offset);
*x = (element->type == ACPI_TYPE_INTEGER) ?
element->integer.value : -1;and it ultimately comes from the "Battery Info Extended" object/method _BIX from ACPI, which I've disassembled a bit:
Method (_BIX, 0, NotSerialized) // _BIX: Battery Information Extended
{
Return (BIFX (One))
}
Method (BIFX, 1, NotSerialized)
{
Name (STAX, Package (0x14)
{
Zero,
Zero,
0x0DF4,
0x0DB7,
One,
0x3C28,
0x015E,
0x69,
Zero,
Zero,
Zero,
Zero,
Zero,
Zero,
0x0108,
0x0EC4,
0xFFFFFFFF,
0xFFFFFFFF,
"Li-Ion ",
0xFFFFFFFF
})
...
STAX [One] = BSMD /* \_SB_.BSMD */
Local0 = BTDC /* \_SB_.BTDC */
STAX [0x02] = (Local0 * BASC)
STAX [0x05] = BTDV /* \_SB_.BTDV */
Local2 = BTLC /* \_SB_.BTLC */
Local2 = (Local2 * BASC)
STAX [0x03] = Local2
Divide (Local2, 0x64, Local0, Local1)
Local1 *= 0x0A
STAX [0x06] = Local1
Divide (Local2, 0x64, Local0, Local1)
Local1 *= 0x03
STAX [0x07] = Local1
STAX [0x11] = ToString (Concatenate (BTSN, Zero), Ones)
STAX [0x08] = BTCC /* \_SB_.BTCC */
...
That looks like it: STAX [0x08] = BTCC /* \_SB_.BTCC */
(the cycle_count is index 8 in the extended_info_offsets above, and BTCC seems to stand for "battery cycle count")
That BTCC seems to be defined as an 8 bit field in NVRM (nvram?):
OperationRegion (NVRM, SystemIO, 0x0E00, 0x0200)
Field (NVRM, ByteAcc, Lock, Preserve)
{
TMPL, 8,
TMPD, 8,
TMPC, 8,
TMPA, 8,
TDGV, 8,
TDVR, 8,
TAMB, 8,
TMPG, 8,
Offset (0x30),
ECSW, 8,
Offset (0x40),
BTVO, 32,
BTAC, 32,
BTRC, 32,
BTFG, 8,
BTCT, 8,
Offset (0x50),
BTDC, 32,
BTDV, 32,
BTLC, 32,
BTCC, 8,
...
Looks like that should be 32 bits?