Hi Thomas, On 2021-11-13 05:42, Thomas Weißschuh wrote: > This adds support for the force-discharge charge_behaviour through the > embedded controller of ThinkPads. > > Signed-off-by: Thomas Weißschuh <linux@xxxxxxxxxxxxxx> > > --- > > This patch is based on https://lore.kernel.org/platform-driver-x86/c2504700-06e9-e7d8-80f7-de90b0b6dfb5@xxxxxxxxx/>> --- > drivers/platform/x86/thinkpad_acpi.c | 103 +++++++++++++++++++++++++-- > 1 file changed, 99 insertions(+), 4 deletions(-) > > diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c > index 9c632df734bb..e8c98e9aff71 100644 > --- a/drivers/platform/x86/thinkpad_acpi.c > +++ b/drivers/platform/x86/thinkpad_acpi.c > @@ -9319,6 +9319,8 @@ static struct ibm_struct mute_led_driver_data = { > #define SET_START "BCCS" > #define GET_STOP "BCSG" > #define SET_STOP "BCSS" > +#define GET_DISCHARGE "BDSG" > +#define SET_DISCHARGE "BDSS" > > enum { > BAT_ANY = 0, > @@ -9335,6 +9337,7 @@ enum { > /* This is used in the get/set helpers */ > THRESHOLD_START, > THRESHOLD_STOP, > + FORCE_DISCHARGE, > }; > > struct tpacpi_battery_data { > @@ -9342,6 +9345,7 @@ struct tpacpi_battery_data { > int start_support; > int charge_stop; > int stop_support; > + unsigned int charge_behaviours; > }; > > struct tpacpi_battery_driver_data { > @@ -9399,6 +9403,12 @@ static int tpacpi_battery_get(int what, int battery, int *ret) > if (*ret == 0) > *ret = 100; > return 0; > + case FORCE_DISCHARGE: > + if ACPI_FAILURE(tpacpi_battery_acpi_eval(GET_DISCHARGE, ret, battery)) > + return -ENODEV; > + /* The force discharge status is in bit 0 */ > + *ret = *ret & 0x01; > + return 0; > default: > pr_crit("wrong parameter: %d", what); > return -EINVAL; > @@ -9427,6 +9437,16 @@ static int tpacpi_battery_set(int what, int battery, int value) > return -ENODEV; > } > return 0; > + case FORCE_DISCHARGE: > + /* Force discharge is in bit 0, > + * break on AC attach is in bit 1 (won't work on some ThinkPads), > + * battery ID is in bits 8-9, 2 bits. > + */ > + if (ACPI_FAILURE(tpacpi_battery_acpi_eval(SET_DISCHARGE, &ret, param))) { > + pr_err("failed to set force dischrage on %d", battery); > + return -ENODEV; > + } > + return 0; > default: > pr_crit("wrong parameter: %d", what); > return -EINVAL; > @@ -9445,6 +9465,8 @@ static int tpacpi_battery_probe(int battery) > * 2) Check for support > * 3) Get the current stop threshold > * 4) Check for support > + * 5) Get the current force discharge status > + * 6) Check for support > */ > if (acpi_has_method(hkey_handle, GET_START)) { > if ACPI_FAILURE(tpacpi_battery_acpi_eval(GET_START, &ret, battery)) { > @@ -9481,10 +9503,25 @@ static int tpacpi_battery_probe(int battery) > return -ENODEV; > } > } > - pr_info("battery %d registered (start %d, stop %d)", > - battery, > - battery_info.batteries[battery].charge_start, > - battery_info.batteries[battery].charge_stop); > + if (acpi_has_method(hkey_handle, GET_DISCHARGE)) { > + if (ACPI_FAILURE(tpacpi_battery_acpi_eval(GET_DISCHARGE, &ret, battery))) { > + pr_err("Error probing battery discharge; %d\n", battery); > + return -ENODEV; > + } > + /* Support is marked in bit 8 */ > + if (ret & BIT(8)) > + battery_info.batteries[battery].charge_behaviours |= > + BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE); > + } > + > + battery_info.batteries[battery].charge_behaviours |= > + BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO); > + > + pr_info("battery %d registered (start %d, stop %d, behaviours: 0x%x)\n", > + battery, > + battery_info.batteries[battery].charge_start, > + battery_info.batteries[battery].charge_stop, > + battery_info.batteries[battery].charge_behaviours); > > return 0; > } > @@ -9619,6 +9656,28 @@ static ssize_t charge_control_end_threshold_show(struct device *device, > return tpacpi_battery_show(THRESHOLD_STOP, device, buf); > } > > +static ssize_t charge_behaviour_show(struct device *dev, > + struct device_attribute *attr, > + char *buf) > +{ > + enum power_supply_charge_behaviour active = POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO; > + struct power_supply *supply = to_power_supply(dev); > + unsigned int available; > + int ret, battery; > + > + battery = tpacpi_battery_get_id(supply->desc->name); > + available = battery_info.batteries[battery].charge_behaviours; > + > + if (available & BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE)) { > + if (tpacpi_battery_get(FORCE_DISCHARGE, battery, &ret)) > + return -ENODEV; > + if (ret) > + active = POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE; > + } > + > + return power_supply_charge_behaviour_show(dev, available, active, buf); > +} > + > static ssize_t charge_control_start_threshold_store(struct device *dev, > struct device_attribute *attr, > const char *buf, size_t count) > @@ -9633,8 +9692,43 @@ static ssize_t charge_control_end_threshold_store(struct device *dev, > return tpacpi_battery_store(THRESHOLD_STOP, dev, buf, count); > } > > +static ssize_t charge_behaviour_store(struct device *dev, > + struct device_attribute *attr, > + const char *buf, size_t count) > +{ > + struct power_supply *supply = to_power_supply(dev); > + int selected, battery, ret; > + unsigned int available; > + > + battery = tpacpi_battery_get_id(supply->desc->name); > + available = battery_info.batteries[battery].charge_behaviours; > + selected = power_supply_charge_behaviour_parse(available, buf); > + > + if (selected < 0) > + return selected; > + > + switch (selected) { > + case POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO: > + ret = tpacpi_battery_set(FORCE_DISCHARGE, battery, 0); > + if (ret < 0) > + return ret; > + break; > + case POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE: > + ret = tpacpi_battery_set(FORCE_DISCHARGE, battery, 1); > + if (ret < 0) > + return ret; > + break; > + default: > + dev_err(dev, "Unexpected charge behaviour: %d\n", selected); > + return -EINVAL; > + } > + > + return count; > +} > + > static DEVICE_ATTR_RW(charge_control_start_threshold); > static DEVICE_ATTR_RW(charge_control_end_threshold); > +static DEVICE_ATTR_RW(charge_behaviour); > static struct device_attribute dev_attr_charge_start_threshold = __ATTR( > charge_start_threshold, > 0644, > @@ -9653,6 +9747,7 @@ static struct attribute *tpacpi_battery_attrs[] = { > &dev_attr_charge_control_end_threshold.attr, > &dev_attr_charge_start_threshold.attr, > &dev_attr_charge_stop_threshold.attr, > + &dev_attr_charge_behaviour.attr, > NULL, > }; > > Sorry for the slow review - been busy this week. Thank you for implementing this - it's great to be getting this into the kernel and it's something we have had on our todo list for a little while now. I can confirm the BDSG and BDSS APIs are correct along with the bit field defines you have used. Not sure if that's helpful but as I have access to some internal documents I figured I'd check :) I'll note that bit 10 in BDSG should flag if the 'enable breaking by AC detaching' feature is supported. You're not using it so I don't think that's important - but figured I'd mention it because of the comment that not all thinkpads support it. Mark