Message ID | 20240104183516.312044-3-hdegoede@redhat.com |
---|---|
State | New |
Headers | show |
Series | None | expand |
Hello Hans, On Thu, Jan 04, 2024 at 07:35:16PM +0100, Hans de Goede wrote: > Add charge-inhibit support by adding a rw status attribute and > inhibiting charging when "Not charging" or "Discharging" > is written to it. > > The userspace API with status being writable is a bit weird, > but this is already done this way in the following psy drivers: > axp20x_battery.c, bq24735-charger.c, bq25980_charger.c, ip5xxx_power.c, > rt9467-charger.c, rt9471.c. We have since then added a new property for this: POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR That can have auto (0), inhibit charge (1) or force discharge (2). Greetings, -- Sebastian > Note on systems with an AXP288 some (about 400mA depending on load) > current will be drawn from the battery when charging is disabled > even if there is an external power-supply which can provide all > the necessary current. So unfortunately one cannot simply disable > charging to keep the battery in good health when using a tablet > with an AXP288 permanently connected to an external power-supply. > > Signed-off-by: Hans de Goede <hdegoede@redhat.com> > --- > drivers/power/supply/axp288_charger.c | 43 +++++++++++++++++++++++++-- > 1 file changed, 40 insertions(+), 3 deletions(-) > > diff --git a/drivers/power/supply/axp288_charger.c b/drivers/power/supply/axp288_charger.c > index a327933cfd6a..351431f3cf0e 100644 > --- a/drivers/power/supply/axp288_charger.c > +++ b/drivers/power/supply/axp288_charger.c > @@ -2,7 +2,7 @@ > /* > * axp288_charger.c - X-power AXP288 PMIC Charger driver > * > - * Copyright (C) 2016-2017 Hans de Goede <hdegoede@redhat.com> > + * Copyright (C) 2016-2024 Hans de Goede <hdegoede@redhat.com> > * Copyright (C) 2014 Intel Corporation > * Author: Ramakrishna Pallala <ramakrishna.pallala@intel.com> > */ > @@ -148,6 +148,8 @@ struct axp288_chrg_info { > unsigned int op_mode; > unsigned int backend_control; > bool valid; > + bool charge_enable; > + bool charge_inhibit; > }; > > static inline int axp288_charger_set_cc(struct axp288_chrg_info *info, int cc) > @@ -285,9 +287,9 @@ static int axp288_charger_vbus_path_select(struct axp288_chrg_info *info, > return ret; > } > > -static int axp288_charger_enable_charger(struct axp288_chrg_info *info, > - bool enable) > +static int axp288_charger_update_charge_en(struct axp288_chrg_info *info) > { > + bool enable = info->charge_enable && !info->charge_inhibit; > int ret; > > if (enable) > @@ -302,6 +304,18 @@ static int axp288_charger_enable_charger(struct axp288_chrg_info *info, > return ret; > } > > +static int axp288_charger_enable_charger(struct axp288_chrg_info *info, bool enable) > +{ > + info->charge_enable = enable; > + return axp288_charger_update_charge_en(info); > +} > + > +static int axp288_charger_inhibit_charger(struct axp288_chrg_info *info, bool inhibit) > +{ > + info->charge_inhibit = inhibit; > + return axp288_charger_update_charge_en(info); > +} > + > static int axp288_get_charger_health(struct axp288_chrg_info *info) > { > if (!(info->input_status & PS_STAT_VBUS_PRESENT)) > @@ -327,6 +341,19 @@ static int axp288_charger_usb_set_property(struct power_supply *psy, > > mutex_lock(&info->lock); > switch (psp) { > + case POWER_SUPPLY_PROP_STATUS: > + switch (val->intval) { > + case POWER_SUPPLY_STATUS_CHARGING: > + ret = axp288_charger_inhibit_charger(info, false); > + break; > + case POWER_SUPPLY_STATUS_DISCHARGING: > + case POWER_SUPPLY_STATUS_NOT_CHARGING: > + ret = axp288_charger_inhibit_charger(info, true); > + break; > + default: > + ret = -EINVAL; > + } > + break; > case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: > scaled_val = min(val->intval, info->max_cc); > scaled_val = DIV_ROUND_CLOSEST(scaled_val, 1000); > @@ -423,6 +450,14 @@ static int axp288_charger_usb_get_property(struct power_supply *psy, > goto out; > > switch (psp) { > + case POWER_SUPPLY_PROP_STATUS: > + if (info->charge_enable && !info->charge_inhibit) > + val->intval = POWER_SUPPLY_STATUS_CHARGING; > + else if (info->charge_enable && info->charge_inhibit) > + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; > + else /* !info->charge_enable && xxx */ > + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; > + break; > case POWER_SUPPLY_PROP_PRESENT: > /* Check for OTG case first */ > if (info->otg.id_short) { > @@ -472,6 +507,7 @@ static int axp288_charger_property_is_writeable(struct power_supply *psy, > int ret; > > switch (psp) { > + case POWER_SUPPLY_PROP_STATUS: > case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: > case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: > case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: > @@ -485,6 +521,7 @@ static int axp288_charger_property_is_writeable(struct power_supply *psy, > } > > static enum power_supply_property axp288_usb_props[] = { > + POWER_SUPPLY_PROP_STATUS, > POWER_SUPPLY_PROP_PRESENT, > POWER_SUPPLY_PROP_ONLINE, > POWER_SUPPLY_PROP_TYPE, > -- > 2.43.0 > >
Hi Sebastian, On 1/27/24 01:46, Sebastian Reichel wrote: > Hello Hans, > > On Thu, Jan 04, 2024 at 07:35:16PM +0100, Hans de Goede wrote: >> Add charge-inhibit support by adding a rw status attribute and >> inhibiting charging when "Not charging" or "Discharging" >> is written to it. >> >> The userspace API with status being writable is a bit weird, >> but this is already done this way in the following psy drivers: >> axp20x_battery.c, bq24735-charger.c, bq25980_charger.c, ip5xxx_power.c, >> rt9467-charger.c, rt9471.c. > > We have since then added a new property for this: > > POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR > > That can have auto (0), inhibit charge (1) or force discharge (2). Right I'm aware of that, but that is a property which so far has only been used on batteries, not chargers. So putting it on the charger would make things more complicated when adding support for this property to say upower (which is something which people are looking into to allow a fuel-gauge calibration UI). I did think about using this and then putting it in the axp288_fuel_gauge.c driver where this IMHO would belong. The problem is that the very same register bit is also toggled on/off when plugging in an external charger, so the control of the bit needs to stay in axp288_charger.c as is done in this patch. So one possible solution which I came up with when originally preparing this series would be for axp288_charger.c to export a: int axp288_charger_inhibit_charger(struct power_supply *psy, bool inhibit); And then have axp288_fuel_gauge.c call power_supply_get_by_name("axp288_charger"); and then call axp288_charger_inhibit_charger() with on the retrieved "axp288_charger" psy. And add a POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR prop to the fuel-gauge/battery driver this way. Putting a POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR prop directly on the charger (type = POWER_SUPPLY_TYPE_USB) would be another option. This would actually make sense based on the property having charge in the name, but as mentioned so far we have only been using POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR on type = POWER_SUPPLY_TYPE_BATTERY supplies. My own preference would be my first suggestion of exporting axp288_charger_inhibit_charger() and adding POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR on the battery/fuel-gauge psy class device, so as to keep things consistent for userspace. Regards, Hans > > Greetings, > > -- Sebastian > >> Note on systems with an AXP288 some (about 400mA depending on load) >> current will be drawn from the battery when charging is disabled >> even if there is an external power-supply which can provide all >> the necessary current. So unfortunately one cannot simply disable >> charging to keep the battery in good health when using a tablet >> with an AXP288 permanently connected to an external power-supply. >> >> Signed-off-by: Hans de Goede <hdegoede@redhat.com> >> --- >> drivers/power/supply/axp288_charger.c | 43 +++++++++++++++++++++++++-- >> 1 file changed, 40 insertions(+), 3 deletions(-) >> >> diff --git a/drivers/power/supply/axp288_charger.c b/drivers/power/supply/axp288_charger.c >> index a327933cfd6a..351431f3cf0e 100644 >> --- a/drivers/power/supply/axp288_charger.c >> +++ b/drivers/power/supply/axp288_charger.c >> @@ -2,7 +2,7 @@ >> /* >> * axp288_charger.c - X-power AXP288 PMIC Charger driver >> * >> - * Copyright (C) 2016-2017 Hans de Goede <hdegoede@redhat.com> >> + * Copyright (C) 2016-2024 Hans de Goede <hdegoede@redhat.com> >> * Copyright (C) 2014 Intel Corporation >> * Author: Ramakrishna Pallala <ramakrishna.pallala@intel.com> >> */ >> @@ -148,6 +148,8 @@ struct axp288_chrg_info { >> unsigned int op_mode; >> unsigned int backend_control; >> bool valid; >> + bool charge_enable; >> + bool charge_inhibit; >> }; >> >> static inline int axp288_charger_set_cc(struct axp288_chrg_info *info, int cc) >> @@ -285,9 +287,9 @@ static int axp288_charger_vbus_path_select(struct axp288_chrg_info *info, >> return ret; >> } >> >> -static int axp288_charger_enable_charger(struct axp288_chrg_info *info, >> - bool enable) >> +static int axp288_charger_update_charge_en(struct axp288_chrg_info *info) >> { >> + bool enable = info->charge_enable && !info->charge_inhibit; >> int ret; >> >> if (enable) >> @@ -302,6 +304,18 @@ static int axp288_charger_enable_charger(struct axp288_chrg_info *info, >> return ret; >> } >> >> +static int axp288_charger_enable_charger(struct axp288_chrg_info *info, bool enable) >> +{ >> + info->charge_enable = enable; >> + return axp288_charger_update_charge_en(info); >> +} >> + >> +static int axp288_charger_inhibit_charger(struct axp288_chrg_info *info, bool inhibit) >> +{ >> + info->charge_inhibit = inhibit; >> + return axp288_charger_update_charge_en(info); >> +} >> + >> static int axp288_get_charger_health(struct axp288_chrg_info *info) >> { >> if (!(info->input_status & PS_STAT_VBUS_PRESENT)) >> @@ -327,6 +341,19 @@ static int axp288_charger_usb_set_property(struct power_supply *psy, >> >> mutex_lock(&info->lock); >> switch (psp) { >> + case POWER_SUPPLY_PROP_STATUS: >> + switch (val->intval) { >> + case POWER_SUPPLY_STATUS_CHARGING: >> + ret = axp288_charger_inhibit_charger(info, false); >> + break; >> + case POWER_SUPPLY_STATUS_DISCHARGING: >> + case POWER_SUPPLY_STATUS_NOT_CHARGING: >> + ret = axp288_charger_inhibit_charger(info, true); >> + break; >> + default: >> + ret = -EINVAL; >> + } >> + break; >> case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: >> scaled_val = min(val->intval, info->max_cc); >> scaled_val = DIV_ROUND_CLOSEST(scaled_val, 1000); >> @@ -423,6 +450,14 @@ static int axp288_charger_usb_get_property(struct power_supply *psy, >> goto out; >> >> switch (psp) { >> + case POWER_SUPPLY_PROP_STATUS: >> + if (info->charge_enable && !info->charge_inhibit) >> + val->intval = POWER_SUPPLY_STATUS_CHARGING; >> + else if (info->charge_enable && info->charge_inhibit) >> + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; >> + else /* !info->charge_enable && xxx */ >> + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; >> + break; >> case POWER_SUPPLY_PROP_PRESENT: >> /* Check for OTG case first */ >> if (info->otg.id_short) { >> @@ -472,6 +507,7 @@ static int axp288_charger_property_is_writeable(struct power_supply *psy, >> int ret; >> >> switch (psp) { >> + case POWER_SUPPLY_PROP_STATUS: >> case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: >> case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: >> case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: >> @@ -485,6 +521,7 @@ static int axp288_charger_property_is_writeable(struct power_supply *psy, >> } >> >> static enum power_supply_property axp288_usb_props[] = { >> + POWER_SUPPLY_PROP_STATUS, >> POWER_SUPPLY_PROP_PRESENT, >> POWER_SUPPLY_PROP_ONLINE, >> POWER_SUPPLY_PROP_TYPE, >> -- >> 2.43.0 >> >>
Hi Hans, On Sat, Jan 27, 2024 at 02:06:23PM +0100, Hans de Goede wrote: > > We have since then added a new property for this: > > POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR > > Right I'm aware of that, but that is a property which so far > has only been used on batteries, not chargers. [...] Right. It's a design that leaked from the added ACPI vendor solutions added to the kernel. > So one possible solution which I came up with when originally > preparing this series would be for axp288_charger.c to export a: > > int axp288_charger_inhibit_charger(struct power_supply *psy, bool inhibit); > > And then have axp288_fuel_gauge.c call > power_supply_get_by_name("axp288_charger"); > and then call axp288_charger_inhibit_charger() > with on the retrieved "axp288_charger" psy. > > And add a POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR prop > to the fuel-gauge/battery driver this way. I think at some point we might have to add the charge inhibit property to a USB/MAINS typed device, considering not all fuel-gauges come bundled with the charger counterpart, but let's go with your suggested route for now. Greetings, -- Sebastian
diff --git a/drivers/power/supply/axp288_charger.c b/drivers/power/supply/axp288_charger.c index a327933cfd6a..351431f3cf0e 100644 --- a/drivers/power/supply/axp288_charger.c +++ b/drivers/power/supply/axp288_charger.c @@ -2,7 +2,7 @@ /* * axp288_charger.c - X-power AXP288 PMIC Charger driver * - * Copyright (C) 2016-2017 Hans de Goede <hdegoede@redhat.com> + * Copyright (C) 2016-2024 Hans de Goede <hdegoede@redhat.com> * Copyright (C) 2014 Intel Corporation * Author: Ramakrishna Pallala <ramakrishna.pallala@intel.com> */ @@ -148,6 +148,8 @@ struct axp288_chrg_info { unsigned int op_mode; unsigned int backend_control; bool valid; + bool charge_enable; + bool charge_inhibit; }; static inline int axp288_charger_set_cc(struct axp288_chrg_info *info, int cc) @@ -285,9 +287,9 @@ static int axp288_charger_vbus_path_select(struct axp288_chrg_info *info, return ret; } -static int axp288_charger_enable_charger(struct axp288_chrg_info *info, - bool enable) +static int axp288_charger_update_charge_en(struct axp288_chrg_info *info) { + bool enable = info->charge_enable && !info->charge_inhibit; int ret; if (enable) @@ -302,6 +304,18 @@ static int axp288_charger_enable_charger(struct axp288_chrg_info *info, return ret; } +static int axp288_charger_enable_charger(struct axp288_chrg_info *info, bool enable) +{ + info->charge_enable = enable; + return axp288_charger_update_charge_en(info); +} + +static int axp288_charger_inhibit_charger(struct axp288_chrg_info *info, bool inhibit) +{ + info->charge_inhibit = inhibit; + return axp288_charger_update_charge_en(info); +} + static int axp288_get_charger_health(struct axp288_chrg_info *info) { if (!(info->input_status & PS_STAT_VBUS_PRESENT)) @@ -327,6 +341,19 @@ static int axp288_charger_usb_set_property(struct power_supply *psy, mutex_lock(&info->lock); switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + switch (val->intval) { + case POWER_SUPPLY_STATUS_CHARGING: + ret = axp288_charger_inhibit_charger(info, false); + break; + case POWER_SUPPLY_STATUS_DISCHARGING: + case POWER_SUPPLY_STATUS_NOT_CHARGING: + ret = axp288_charger_inhibit_charger(info, true); + break; + default: + ret = -EINVAL; + } + break; case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: scaled_val = min(val->intval, info->max_cc); scaled_val = DIV_ROUND_CLOSEST(scaled_val, 1000); @@ -423,6 +450,14 @@ static int axp288_charger_usb_get_property(struct power_supply *psy, goto out; switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + if (info->charge_enable && !info->charge_inhibit) + val->intval = POWER_SUPPLY_STATUS_CHARGING; + else if (info->charge_enable && info->charge_inhibit) + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + else /* !info->charge_enable && xxx */ + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + break; case POWER_SUPPLY_PROP_PRESENT: /* Check for OTG case first */ if (info->otg.id_short) { @@ -472,6 +507,7 @@ static int axp288_charger_property_is_writeable(struct power_supply *psy, int ret; switch (psp) { + case POWER_SUPPLY_PROP_STATUS: case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: @@ -485,6 +521,7 @@ static int axp288_charger_property_is_writeable(struct power_supply *psy, } static enum power_supply_property axp288_usb_props[] = { + POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_ONLINE, POWER_SUPPLY_PROP_TYPE,
Add charge-inhibit support by adding a rw status attribute and inhibiting charging when "Not charging" or "Discharging" is written to it. The userspace API with status being writable is a bit weird, but this is already done this way in the following psy drivers: axp20x_battery.c, bq24735-charger.c, bq25980_charger.c, ip5xxx_power.c, rt9467-charger.c, rt9471.c. Note on systems with an AXP288 some (about 400mA depending on load) current will be drawn from the battery when charging is disabled even if there is an external power-supply which can provide all the necessary current. So unfortunately one cannot simply disable charging to keep the battery in good health when using a tablet with an AXP288 permanently connected to an external power-supply. Signed-off-by: Hans de Goede <hdegoede@redhat.com> --- drivers/power/supply/axp288_charger.c | 43 +++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 3 deletions(-)