[PATCH v2 4/4] platform/x86: thinkpad_acpi: support inhibit-charge

From: Thomas Weißschuh
Date: Tue Nov 23 2021 - 18:27:22 EST


This adds support for the inhibit-charge charge_behaviour through the
embedded controller of ThinkPads.

Co-developed-by: Thomas Koch <linrunner@xxxxxxx>
Signed-off-by: Thomas Koch <linrunner@xxxxxxx>
Co-developed-by: Nicolò Piazzalunga <nicolopiazzalunga@xxxxxxxxx>
Signed-off-by: Nicolò Piazzalunga <nicolopiazzalunga@xxxxxxxxx>
Signed-off-by: Thomas Weißschuh <linux@xxxxxxxxxxxxxx>

---

This patch is based on https://lore.kernel.org/platform-driver-x86/d2808930-5840-6ffb-3a59-d235cdb1fe16@xxxxxxxxx/
---
drivers/platform/x86/thinkpad_acpi.c | 64 +++++++++++++++++++++++++++-
1 file changed, 62 insertions(+), 2 deletions(-)

diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c
index e3567cc686fa..7f2f46c71482 100644
--- a/drivers/platform/x86/thinkpad_acpi.c
+++ b/drivers/platform/x86/thinkpad_acpi.c
@@ -9321,6 +9321,8 @@ static struct ibm_struct mute_led_driver_data = {
#define SET_STOP "BCSS"
#define GET_DISCHARGE "BDSG"
#define SET_DISCHARGE "BDSS"
+#define GET_INHIBIT "BICG"
+#define SET_INHIBIT "BICS"

enum {
BAT_ANY = 0,
@@ -9338,6 +9340,7 @@ enum {
THRESHOLD_START,
THRESHOLD_STOP,
FORCE_DISCHARGE,
+ INHIBIT_CHARGE,
};

struct tpacpi_battery_data {
@@ -9409,6 +9412,12 @@ static int tpacpi_battery_get(int what, int battery, int *ret)
/* The force discharge status is in bit 0 */
*ret = *ret & 0x01;
return 0;
+ case INHIBIT_CHARGE:
+ if ACPI_FAILURE(tpacpi_battery_acpi_eval(GET_INHIBIT, ret, battery))
+ return -ENODEV;
+ /* The inhibit charge status is in bit 0 */
+ *ret = *ret & 0x01;
+ return 0;
default:
pr_crit("wrong parameter: %d", what);
return -EINVAL;
@@ -9447,6 +9456,22 @@ static int tpacpi_battery_set(int what, int battery, int value)
return -ENODEV;
}
return 0;
+ case INHIBIT_CHARGE:
+ /* When setting inhibit charge, we set a default value of
+ * always breaking on AC detach and the effective time is set to
+ * be permanent.
+ * The battery ID is in bits 4-5, 2 bits,
+ * the effective time is in bits 8-23, 2 bytes.
+ * A time of FFFF indicates forever.
+ */
+ param = value;
+ param |= battery << 4;
+ param |= 0xFFFF << 8;
+ if (ACPI_FAILURE(tpacpi_battery_acpi_eval(SET_INHIBIT, &ret, param))) {
+ pr_err("failed to set inhibit charge on %d", battery);
+ return -ENODEV;
+ }
+ return 0;
default:
pr_crit("wrong parameter: %d", what);
return -EINVAL;
@@ -9494,6 +9519,8 @@ static int tpacpi_battery_probe(int battery)
* 4) Check for support
* 5) Get the current force discharge status
* 6) Check for support
+ * 7) Get the current inhibit charge status
+ * 8) Check for support
*/
if (acpi_has_method(hkey_handle, GET_START)) {
if ACPI_FAILURE(tpacpi_battery_acpi_eval(GET_START, &ret, battery)) {
@@ -9540,6 +9567,16 @@ static int tpacpi_battery_probe(int battery)
battery_info.batteries[battery].charge_behaviours |=
BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE);
}
+ if (acpi_has_method(hkey_handle, GET_INHIBIT)) {
+ if (ACPI_FAILURE(tpacpi_battery_acpi_eval(GET_INHIBIT, &ret, battery))) {
+ pr_err("Error probing battery inhibit charge; %d\n", battery);
+ return -ENODEV;
+ }
+ /* Support is marked in bit 5 */
+ if (ret & BIT(5))
+ battery_info.batteries[battery].charge_behaviours |=
+ BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE);
+ }

battery_info.batteries[battery].charge_behaviours |=
BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO);
@@ -9698,10 +9735,22 @@ static ssize_t charge_behaviour_show(struct device *dev,
if (available & BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE)) {
if (tpacpi_battery_get(FORCE_DISCHARGE, battery, &ret))
return -ENODEV;
- if (ret)
+ if (ret) {
active = POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE;
+ goto out;
+ }
+ }
+
+ if (available & BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE)) {
+ if (tpacpi_battery_get(INHIBIT_CHARGE, battery, &ret))
+ return -ENODEV;
+ if (ret) {
+ active = POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE;
+ goto out;
+ }
}

+out:
return power_supply_charge_behaviour_show(dev, available, active, buf);
}

@@ -9738,11 +9787,22 @@ static ssize_t charge_behaviour_store(struct device *dev,
case POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO:
if (available & BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE))
ret = tpacpi_battery_set_validate(FORCE_DISCHARGE, battery, 0);
+ if (available & BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE))
+ ret = min(ret, tpacpi_battery_set_validate(INHIBIT_CHARGE, battery, 0));
if (ret < 0)
return ret;
break;
case POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE:
- ret = tpacpi_battery_set_validate(FORCE_DISCHARGE, battery, 1);
+ if (available & BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE))
+ ret = tpacpi_battery_set_validate(INHIBIT_CHARGE, battery, 0);
+ ret = min(ret, tpacpi_battery_set_validate(FORCE_DISCHARGE, battery, 1));
+ if (ret < 0)
+ return ret;
+ break;
+ case POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE:
+ if (available & BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE))
+ ret = tpacpi_battery_set_validate(FORCE_DISCHARGE, battery, 0);
+ ret = min(ret, tpacpi_battery_set_validate(INHIBIT_CHARGE, battery, 1));
if (ret < 0)
return ret;
break;
--
2.34.0