diff --git a/Documentation/ABI/testing/sysfs-class-power b/Documentation/ABI/testing/sysfs-class-power index 32697b926cc8c2..6b439c628b6ee1 100644 --- a/Documentation/ABI/testing/sysfs-class-power +++ b/Documentation/ABI/testing/sysfs-class-power @@ -513,6 +513,8 @@ Description: inhibit-charge: Do not charge while AC is attached inhibit-charge-awake: inhibit-charge only when device is awake force-discharge: Force discharge while AC is attached + auto-discharge: auto, but actively discharge down to + the charge control end threshold ===================== ======================================== What: /sys/class/power_supply//technology diff --git a/drivers/power/supply/macsmc-power.c b/drivers/power/supply/macsmc-power.c index cfc57eb9a471b0..0cbd747794710c 100644 --- a/drivers/power/supply/macsmc-power.c +++ b/drivers/power/supply/macsmc-power.c @@ -295,6 +295,7 @@ static int macsmc_battery_get_charge_behaviour(struct macsmc_power *power) int ret; u8 val8; u8 chte_buf[4]; + u16 vu16; if (power->has_ch0i) { ret = apple_smc_read_u8(power->smc, SMC_KEY(CH0I), &val8); @@ -319,6 +320,14 @@ static int macsmc_battery_get_charge_behaviour(struct macsmc_power *power) return POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE; } + if (power->has_chls) { + ret = apple_smc_read_u16(power->smc, SMC_KEY(CHLS), &vu16); + if (ret) + return ret; + if (vu16 & CHLS_FORCE_DISCHARGE) + return POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO_DISCHARGE; + } + return POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO; } @@ -327,6 +336,10 @@ static int macsmc_battery_set_charge_behaviour(struct macsmc_power *power, int v int ret; switch (val) { + case POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO_DISCHARGE: + if (!power->has_chls) + return -EOPNOTSUPP; + fallthrough; case POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO: /* Reset all inhibitors to a known-good 'auto' state */ if (power->has_ch0i) { @@ -344,6 +357,20 @@ static int macsmc_battery_set_charge_behaviour(struct macsmc_power *power, int v if (ret) return ret; } + + /* Set or clear force-discharge to the CHLS charge limit */ + if (power->has_chls) { + u16 vu16; + + ret = apple_smc_read_u16(power->smc, SMC_KEY(CHLS), &vu16); + if (ret) + return ret; + if (val == POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO_DISCHARGE) + vu16 |= CHLS_FORCE_DISCHARGE; + else + vu16 &= ~CHLS_FORCE_DISCHARGE; + return apple_smc_write_u16(power->smc, SMC_KEY(CHLS), vu16); + } return 0; case POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE: @@ -606,12 +633,18 @@ static int macsmc_battery_set_property(struct power_supply *psy, return 0; case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD: if (power->has_chls) { - u16 kval = 0; - /* TODO: Make CHLS_FORCE_DISCHARGE configurable */ + u16 kval; + int ret; + + /* Preserve the force-discharge (auto-discharge) charge_behaviour */ + ret = apple_smc_read_u16(power->smc, SMC_KEY(CHLS), &kval); + if (ret) + return ret; + kval &= CHLS_FORCE_DISCHARGE; if (val->intval < CHLS_MIN_END_THRESHOLD) - kval = CHLS_FORCE_DISCHARGE | CHLS_MIN_END_THRESHOLD; + kval |= CHLS_MIN_END_THRESHOLD; else if (val->intval < 100) - kval = CHLS_FORCE_DISCHARGE | (val->intval & 0xff); + kval |= val->intval & 0xff; return apple_smc_write_u16(power->smc, SMC_KEY(CHLS), kval); } else if (power->has_chwa) { return apple_smc_write_flag(power->smc, SMC_KEY(CHWA), @@ -945,10 +978,13 @@ static int macsmc_power_probe(struct platform_device *pdev) } /* Detect charge limit method (CHWA vs CHLS) */ - if (apple_smc_read_flag(power->smc, SMC_KEY(CHWA), &flag) == 0) + if (apple_smc_read_flag(power->smc, SMC_KEY(CHWA), &flag) == 0) { power->has_chwa = true; - else if (apple_smc_read_u16(power->smc, SMC_KEY(CHLS), &vu16) >= 0) + } else if (apple_smc_read_u16(power->smc, SMC_KEY(CHLS), &vu16) >= 0) { power->has_chls = true; + power->batt_desc.charge_behaviours |= + BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO_DISCHARGE); + } if (power->has_chwa || power->has_chls) { props[nprops++] = POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD; diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c index f30a7b9ccd5e93..d22541f4a4ecf6 100644 --- a/drivers/power/supply/power_supply_sysfs.c +++ b/drivers/power/supply/power_supply_sysfs.c @@ -146,6 +146,7 @@ static const char * const POWER_SUPPLY_CHARGE_BEHAVIOUR_TEXT[] = { [POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE] = "inhibit-charge", [POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE_AWAKE] = "inhibit-charge-awake", [POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE] = "force-discharge", + [POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO_DISCHARGE] = "auto-discharge", }; static struct power_supply_attr power_supply_attrs[] __ro_after_init = { diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index 7a5e4c3242a01d..82215a75385010 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -221,6 +221,7 @@ enum power_supply_charge_behaviour { POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE, POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE_AWAKE, POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE, + POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO_DISCHARGE, }; enum power_supply_notifier_events {