i3 / i3status

Generates status bar to use with i3bar, dzen2 or xmobar
BSD 3-Clause "New" or "Revised" License
602 stars 254 forks source link

Bogus battery time left (nine hundred hours) – POWER_SUPPLY_TIME_TO_EMPTY_NOW taken as minutes when it is seconds: driver compatibility? #521

Open nabijaczleweli opened 8 months ago

nabijaczleweli commented 8 months ago

I have a Google Hana device with a battery which is compatible = "sbs,sbs-battery". This yields a sysfs entry as /sys/class/power_supply/sbs-6-000b:

POWER_SUPPLY_NAME=sbs-6-000b
POWER_SUPPLY_TYPE=Battery
POWER_SUPPLY_STATUS=Discharging
POWER_SUPPLY_CAPACITY_LEVEL=Normal
POWER_SUPPLY_HEALTH=Unknown
POWER_SUPPLY_PRESENT=1
POWER_SUPPLY_TECHNOLOGY=Li-poly
POWER_SUPPLY_CYCLE_COUNT=7
POWER_SUPPLY_VOLTAGE_NOW=11810000
POWER_SUPPLY_CURRENT_NOW=-202000
POWER_SUPPLY_CURRENT_AVG=-170000
POWER_SUPPLY_CAPACITY=71
POWER_SUPPLY_CAPACITY_ERROR_MARGIN=1
POWER_SUPPLY_TEMP=272
POWER_SUPPLY_TIME_TO_EMPTY_NOW=48300
POWER_SUPPLY_TIME_TO_EMPTY_AVG=55140
POWER_SUPPLY_TIME_TO_FULL_AVG=3932100
POWER_SUPPLY_SERIAL_NUMBER=049a
POWER_SUPPLY_VOLTAGE_MIN_DESIGN=11250000
POWER_SUPPLY_VOLTAGE_MAX_DESIGN=11250000
POWER_SUPPLY_ENERGY_NOW=28930000
POWER_SUPPLY_ENERGY_FULL=41760000
POWER_SUPPLY_ENERGY_FULL_DESIGN=42000000
POWER_SUPPLY_CHARGE_NOW=2603000
POWER_SUPPLY_CHARGE_FULL=3648000
POWER_SUPPLY_CHARGE_FULL_DESIGN=3735000
POWER_SUPPLY_CONSTANT_CHARGE_CURRENT_MAX=1839000
POWER_SUPPLY_CONSTANT_CHARGE_VOLTAGE_MAX=12900000
POWER_SUPPLY_MANUFACTURE_YEAR=2019
POWER_SUPPLY_MANUFACTURE_MONTH=7
POWER_SUPPLY_MANUFACTURE_DAY=29
POWER_SUPPLY_MANUFACTURER=SMP
POWER_SUPPLY_MODEL_NAME=L17M3PB0

But attaching i3status to it gives results of "BAT 69.56% 962:00", "974:00". It is a big battery and it is discharging at just 1.94W, but hundreds of hours is, well.

A quick grep-around shows that i3status parses POWER_SUPPLY_TIME_TO_EMPTY_NOW as batt_info->seconds_remaining = abs(atoi(walk + 1)) * 60;.

drivers/power/supply/sbs-battery.c in linux 6.6.11 shows

static void  sbs_unit_adjustment(struct i2c_client *client,
        enum power_supply_property psp, union power_supply_propval *val)
{
#define BASE_UNIT_CONVERSION            1000
#define BATTERY_MODE_CAP_MULT_WATT      (10 * BASE_UNIT_CONVERSION)
#define TIME_UNIT_CONVERSION            60
#define TEMP_KELVIN_TO_CELSIUS          2731
// ...
        case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
        case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
        case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG:
                /* sbs provides time to empty and time to full in minutes.
                 * Convert to seconds
                 */
                val->intval *= TIME_UNIT_CONVERSION;
                break;

and being off factor of 60 would put it at around 15.7h, which I'd be willing to buy (this is a Lenovo 300e Chromebook 2nd Gen MTK; I also have a Lenovo 300e Chromebook 2nd Gen, which is a similar Intel platform, and it gets around that when it hits <2W).

Additional survey of the non-staging results I got for grep -rI TIME_TO_EMPTY_NOW shows:

        case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW: {
                        int percent = twl4030_madc_bat_voltscale(bat,
                                        twl4030_madc_bat_get_voltage(bat));
                        /* in mAh */
                        int chg = (percent * (bat->pdata->capacity/1000))/100;

                        /* assume discharge with 400 mA (ca. 1.5W) */
                        val->intval = (3600l * chg) / 400;
                        break;
drivers/power/supply/twl4030_madc_battery.c lines 110-162/272 byte 4435/7515 59%  (press RETURN)

(idk)

static int rn5t618_battery_tte(struct rn5t618_power_info *info,
                               union power_supply_propval *val)
{
        u16 res;
        int ret;

        ret = rn5t618_battery_read_doublereg(info, RN5T618_TT_EMPTY_H, &res);
        if (ret)
                return ret;

        if (res == 65535)
                return -ENODATA;

        val->intval = res * 60;

        return 0;
}
drivers/power/supply/rn5t618_power.c lines 226-278/828 byte 6289/19922 32%  (press RETURN)

(EMPTY_H * 60 => minutes)

        case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
                ret = regmap_read(map, MAX17042_TTE, &data);
                if (ret < 0)
                        return ret;

                val->intval = data * 5625 / 1000;
                break;
drivers/power/supply/max17042_battery.c lines 380-432/1225 byte 10904/33247 33%  (press RETURN)

(no clue)

        case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
                val->intval = di->life_sec;
                break;
drivers/power/supply/ds2760_battery.c lines 524-576/808 byte 16594/22502 74%  (press RETURN)

(seconds)

        case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
                if (cw_battery_valid_time_to_empty(cw_bat))
                        val->intval = cw_bat->time_to_empty;
                else
                        val->intval = 0;
                break;
drivers/power/supply/cw2015_battery.c lines 445-497/759 byte 12373/19482 64%  (press RETURN)

(time_to_empty is unannotated – no clue)

        case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
                ret = bq27xxx_simple_value(di->cache.time_to_empty, val);
                break;
drivers/power/supply/bq27xxx_battery.c lines 1990-2042/2167 byte 58542/62305 94%  (press RETURN)
/*
 * Read a time register.
 * Return < 0 if something fails.
 */
static int bq27xxx_battery_read_time(struct bq27xxx_device_info *di, u8 reg)
{
        int tval;

        tval = bq27xxx_read(di, reg, false);
        if (tval < 0) {
                dev_dbg(di->dev, "error reading time register %02x: %d\n",
                        reg, tval);
                return tval;
        }

        if (tval == 65535)
                return -ENODATA;

        return tval * 60;
}

(no clue)

        /* time */

        info->units = APM_UNITS_MINS;

        if (status.intval == POWER_SUPPLY_STATUS_CHARGING) {
                if (!MPSY_PROP(TIME_TO_FULL_AVG, &time_to_full) ||
                                !MPSY_PROP(TIME_TO_FULL_NOW, &time_to_full))
                        info->time = time_to_full.intval / 60;
                else
                        info->time = calculate_time(status.intval);
        } else {
                if (!MPSY_PROP(TIME_TO_EMPTY_AVG, &time_to_empty) ||
                              !MPSY_PROP(TIME_TO_EMPTY_NOW, &time_to_empty))
                        info->time = time_to_empty.intval / 60;
                else
                        info->time = calculate_time(status.intval);
        }
drivers/power/supply/apm_power.c lines 301-353/376 byte 9902/10386 95%  (press RETURN)

(labelled as minutes, divides by 60 so can only be minutes really)

or, tabularly: seconds minutes idk
sbs-battery rn5t618_power twl4030_madc_battery
ds2760_battery apm_power max17042_battery
cw2015_battery
bq27xxx_battery

which is an even split (with ~100% of x86 platforms using the APM driver and ~100% of ARM platforms using the SBS driver 🙈).

So i3status's scaling produces obviously-incorrect results by a factor of 60.

nabijaczleweli commented 8 months ago

I was also just quoted "CHR 1032:15" (also unlikely given it's charging at 22W).