Skip to content

Commit

Permalink
Merge tag 'for-v6.4-rc' of git://git.kernel.org/pub/scm/linux/kernel/…
Browse files Browse the repository at this point in the history
…git/sre/linux-power-supply

Pull power supply fixes from Sebastian Reichel:

 - Fix power_supply_get_battery_info for devices without parent devices
   resulting in NULL pointer dereference

 - Fix desktop systems reporting to run on battery once a power-supply
   device with device scope appears (e.g. a HID keyboard with a battery)

 - Ratelimit debug print about driver not providing data

 - Fix race condition related to external_power_changed in multiple
   drivers (ab8500, axp288, bq25890, sc27xx, bq27xxx)

 - Fix LED trigger switching from blinking to solid-on when charging
   finishes

 - Fix multiple races in bq27xxx battery driver

 - mt6360: handle potential ENOMEM from devm_work_autocancel

 - sbs-charger: Fix SBS_CHARGER_STATUS_CHARGE_INHIBITED bit

 - rt9467: avoid passing 0 to dev_err_probe

* tag 'for-v6.4-rc' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply: (21 commits)
  power: supply: Fix logic checking if system is running from battery
  power: supply: mt6360: add a check of devm_work_autocancel in mt6360_charger_probe
  power: supply: sbs-charger: Fix INHIBITED bit for Status reg
  power: supply: rt9467: Fix passing zero to 'dev_err_probe'
  power: supply: Ratelimit no data debug output
  power: supply: Fix power_supply_get_battery_info() if parent is NULL
  power: supply: bq24190: Call power_supply_changed() after updating input current
  power: supply: bq25890: Call power_supply_changed() after updating input current or voltage
  power: supply: bq27xxx: Use mod_delayed_work() instead of cancel() + schedule()
  power: supply: bq27xxx: After charger plug in/out wait 0.5s for things to stabilize
  power: supply: bq27xxx: Ensure power_supply_changed() is called on current sign changes
  power: supply: bq27xxx: Move bq27xxx_battery_update() down
  power: supply: bq27xxx: Add cache parameter to bq27xxx_battery_current_and_status()
  power: supply: bq27xxx: Fix poll_interval handling and races on remove
  power: supply: bq27xxx: Fix I2C IRQ race on remove
  power: supply: bq27xxx: Fix bq27xxx_battery_update() race condition
  power: supply: leds: Fix blink to LED on transition
  power: supply: sc27xx: Fix external_power_changed race
  power: supply: bq25890: Fix external_power_changed race
  power: supply: axp288_fuel_gauge: Fix external_power_changed race
  ...
  • Loading branch information
Linus Torvalds committed May 25, 2023
2 parents 029c77f + 95339f4 commit eb03e31
Show file tree
Hide file tree
Showing 15 changed files with 136 additions and 112 deletions.
6 changes: 2 additions & 4 deletions drivers/power/supply/ab8500_btemp.c
Original file line number Diff line number Diff line change
Expand Up @@ -624,10 +624,8 @@ static int ab8500_btemp_get_ext_psy_data(struct device *dev, void *data)
*/
static void ab8500_btemp_external_power_changed(struct power_supply *psy)
{
struct ab8500_btemp *di = power_supply_get_drvdata(psy);

class_for_each_device(power_supply_class, NULL,
di->btemp_psy, ab8500_btemp_get_ext_psy_data);
class_for_each_device(power_supply_class, NULL, psy,
ab8500_btemp_get_ext_psy_data);
}

/* ab8500 btemp driver interrupts and their respective isr */
Expand Down
6 changes: 2 additions & 4 deletions drivers/power/supply/ab8500_fg.c
Original file line number Diff line number Diff line change
Expand Up @@ -2407,10 +2407,8 @@ static int ab8500_fg_init_hw_registers(struct ab8500_fg *di)
*/
static void ab8500_fg_external_power_changed(struct power_supply *psy)
{
struct ab8500_fg *di = power_supply_get_drvdata(psy);

class_for_each_device(power_supply_class, NULL,
di->fg_psy, ab8500_fg_get_ext_psy_data);
class_for_each_device(power_supply_class, NULL, psy,
ab8500_fg_get_ext_psy_data);
}

/**
Expand Down
2 changes: 1 addition & 1 deletion drivers/power/supply/axp288_fuel_gauge.c
Original file line number Diff line number Diff line change
Expand Up @@ -507,7 +507,7 @@ static void fuel_gauge_external_power_changed(struct power_supply *psy)
mutex_lock(&info->lock);
info->valid = 0; /* Force updating of the cached registers */
mutex_unlock(&info->lock);
power_supply_changed(info->bat);
power_supply_changed(psy);
}

static struct power_supply_desc fuel_gauge_desc = {
Expand Down
1 change: 1 addition & 0 deletions drivers/power/supply/bq24190_charger.c
Original file line number Diff line number Diff line change
Expand Up @@ -1262,6 +1262,7 @@ static void bq24190_input_current_limit_work(struct work_struct *work)
bq24190_charger_set_property(bdi->charger,
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
&val);
power_supply_changed(bdi->charger);
}

/* Sync the input-current-limit with our parent supply (if we have one) */
Expand Down
5 changes: 4 additions & 1 deletion drivers/power/supply/bq25890_charger.c
Original file line number Diff line number Diff line change
Expand Up @@ -750,7 +750,7 @@ static void bq25890_charger_external_power_changed(struct power_supply *psy)
if (bq->chip_version != BQ25892)
return;

ret = power_supply_get_property_from_supplier(bq->charger,
ret = power_supply_get_property_from_supplier(psy,
POWER_SUPPLY_PROP_USB_TYPE,
&val);
if (ret)
Expand All @@ -775,6 +775,7 @@ static void bq25890_charger_external_power_changed(struct power_supply *psy)
}

bq25890_field_write(bq, F_IINLIM, input_current_limit);
power_supply_changed(psy);
}

static int bq25890_get_chip_state(struct bq25890_device *bq,
Expand Down Expand Up @@ -1106,6 +1107,8 @@ static void bq25890_pump_express_work(struct work_struct *data)
dev_info(bq->dev, "Hi-voltage charging requested, input voltage is %d mV\n",
voltage);

power_supply_changed(bq->charger);

return;
error_print:
bq25890_field_write(bq, F_PUMPX_EN, 0);
Expand Down
181 changes: 98 additions & 83 deletions drivers/power/supply/bq27xxx_battery.c
Original file line number Diff line number Diff line change
Expand Up @@ -1083,10 +1083,8 @@ static int poll_interval_param_set(const char *val, const struct kernel_param *k
return ret;

mutex_lock(&bq27xxx_list_lock);
list_for_each_entry(di, &bq27xxx_battery_devices, list) {
cancel_delayed_work_sync(&di->work);
schedule_delayed_work(&di->work, 0);
}
list_for_each_entry(di, &bq27xxx_battery_devices, list)
mod_delayed_work(system_wq, &di->work, 0);
mutex_unlock(&bq27xxx_list_lock);

return ret;
Expand Down Expand Up @@ -1761,60 +1759,6 @@ static int bq27xxx_battery_read_health(struct bq27xxx_device_info *di)
return POWER_SUPPLY_HEALTH_GOOD;
}

void bq27xxx_battery_update(struct bq27xxx_device_info *di)
{
struct bq27xxx_reg_cache cache = {0, };
bool has_singe_flag = di->opts & BQ27XXX_O_ZERO;

cache.flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, has_singe_flag);
if ((cache.flags & 0xff) == 0xff)
cache.flags = -1; /* read error */
if (cache.flags >= 0) {
cache.temperature = bq27xxx_battery_read_temperature(di);
if (di->regs[BQ27XXX_REG_TTE] != INVALID_REG_ADDR)
cache.time_to_empty = bq27xxx_battery_read_time(di, BQ27XXX_REG_TTE);
if (di->regs[BQ27XXX_REG_TTECP] != INVALID_REG_ADDR)
cache.time_to_empty_avg = bq27xxx_battery_read_time(di, BQ27XXX_REG_TTECP);
if (di->regs[BQ27XXX_REG_TTF] != INVALID_REG_ADDR)
cache.time_to_full = bq27xxx_battery_read_time(di, BQ27XXX_REG_TTF);

cache.charge_full = bq27xxx_battery_read_fcc(di);
cache.capacity = bq27xxx_battery_read_soc(di);
if (di->regs[BQ27XXX_REG_AE] != INVALID_REG_ADDR)
cache.energy = bq27xxx_battery_read_energy(di);
di->cache.flags = cache.flags;
cache.health = bq27xxx_battery_read_health(di);
if (di->regs[BQ27XXX_REG_CYCT] != INVALID_REG_ADDR)
cache.cycle_count = bq27xxx_battery_read_cyct(di);

/* We only have to read charge design full once */
if (di->charge_design_full <= 0)
di->charge_design_full = bq27xxx_battery_read_dcap(di);
}

if ((di->cache.capacity != cache.capacity) ||
(di->cache.flags != cache.flags))
power_supply_changed(di->bat);

if (memcmp(&di->cache, &cache, sizeof(cache)) != 0)
di->cache = cache;

di->last_update = jiffies;
}
EXPORT_SYMBOL_GPL(bq27xxx_battery_update);

static void bq27xxx_battery_poll(struct work_struct *work)
{
struct bq27xxx_device_info *di =
container_of(work, struct bq27xxx_device_info,
work.work);

bq27xxx_battery_update(di);

if (poll_interval > 0)
schedule_delayed_work(&di->work, poll_interval * HZ);
}

static bool bq27xxx_battery_is_full(struct bq27xxx_device_info *di, int flags)
{
if (di->opts & BQ27XXX_O_ZERO)
Expand All @@ -1833,7 +1777,8 @@ static bool bq27xxx_battery_is_full(struct bq27xxx_device_info *di, int flags)
static int bq27xxx_battery_current_and_status(
struct bq27xxx_device_info *di,
union power_supply_propval *val_curr,
union power_supply_propval *val_status)
union power_supply_propval *val_status,
struct bq27xxx_reg_cache *cache)
{
bool single_flags = (di->opts & BQ27XXX_O_ZERO);
int curr;
Expand All @@ -1845,10 +1790,14 @@ static int bq27xxx_battery_current_and_status(
return curr;
}

flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, single_flags);
if (flags < 0) {
dev_err(di->dev, "error reading flags\n");
return flags;
if (cache) {
flags = cache->flags;
} else {
flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, single_flags);
if (flags < 0) {
dev_err(di->dev, "error reading flags\n");
return flags;
}
}

if (di->opts & BQ27XXX_O_ZERO) {
Expand Down Expand Up @@ -1883,6 +1832,78 @@ static int bq27xxx_battery_current_and_status(
return 0;
}

static void bq27xxx_battery_update_unlocked(struct bq27xxx_device_info *di)
{
union power_supply_propval status = di->last_status;
struct bq27xxx_reg_cache cache = {0, };
bool has_singe_flag = di->opts & BQ27XXX_O_ZERO;

cache.flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, has_singe_flag);
if ((cache.flags & 0xff) == 0xff)
cache.flags = -1; /* read error */
if (cache.flags >= 0) {
cache.temperature = bq27xxx_battery_read_temperature(di);
if (di->regs[BQ27XXX_REG_TTE] != INVALID_REG_ADDR)
cache.time_to_empty = bq27xxx_battery_read_time(di, BQ27XXX_REG_TTE);
if (di->regs[BQ27XXX_REG_TTECP] != INVALID_REG_ADDR)
cache.time_to_empty_avg = bq27xxx_battery_read_time(di, BQ27XXX_REG_TTECP);
if (di->regs[BQ27XXX_REG_TTF] != INVALID_REG_ADDR)
cache.time_to_full = bq27xxx_battery_read_time(di, BQ27XXX_REG_TTF);

cache.charge_full = bq27xxx_battery_read_fcc(di);
cache.capacity = bq27xxx_battery_read_soc(di);
if (di->regs[BQ27XXX_REG_AE] != INVALID_REG_ADDR)
cache.energy = bq27xxx_battery_read_energy(di);
di->cache.flags = cache.flags;
cache.health = bq27xxx_battery_read_health(di);
if (di->regs[BQ27XXX_REG_CYCT] != INVALID_REG_ADDR)
cache.cycle_count = bq27xxx_battery_read_cyct(di);

/*
* On gauges with signed current reporting the current must be
* checked to detect charging <-> discharging status changes.
*/
if (!(di->opts & BQ27XXX_O_ZERO))
bq27xxx_battery_current_and_status(di, NULL, &status, &cache);

/* We only have to read charge design full once */
if (di->charge_design_full <= 0)
di->charge_design_full = bq27xxx_battery_read_dcap(di);
}

if ((di->cache.capacity != cache.capacity) ||
(di->cache.flags != cache.flags) ||
(di->last_status.intval != status.intval)) {
di->last_status.intval = status.intval;
power_supply_changed(di->bat);
}

if (memcmp(&di->cache, &cache, sizeof(cache)) != 0)
di->cache = cache;

di->last_update = jiffies;

if (!di->removed && poll_interval > 0)
mod_delayed_work(system_wq, &di->work, poll_interval * HZ);
}

void bq27xxx_battery_update(struct bq27xxx_device_info *di)
{
mutex_lock(&di->lock);
bq27xxx_battery_update_unlocked(di);
mutex_unlock(&di->lock);
}
EXPORT_SYMBOL_GPL(bq27xxx_battery_update);

static void bq27xxx_battery_poll(struct work_struct *work)
{
struct bq27xxx_device_info *di =
container_of(work, struct bq27xxx_device_info,
work.work);

bq27xxx_battery_update(di);
}

/*
* Get the average power in µW
* Return < 0 if something fails.
Expand Down Expand Up @@ -1985,18 +2006,16 @@ static int bq27xxx_battery_get_property(struct power_supply *psy,
struct bq27xxx_device_info *di = power_supply_get_drvdata(psy);

mutex_lock(&di->lock);
if (time_is_before_jiffies(di->last_update + 5 * HZ)) {
cancel_delayed_work_sync(&di->work);
bq27xxx_battery_poll(&di->work.work);
}
if (time_is_before_jiffies(di->last_update + 5 * HZ))
bq27xxx_battery_update_unlocked(di);
mutex_unlock(&di->lock);

if (psp != POWER_SUPPLY_PROP_PRESENT && di->cache.flags < 0)
return -ENODEV;

switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
ret = bq27xxx_battery_current_and_status(di, NULL, val);
ret = bq27xxx_battery_current_and_status(di, NULL, val, NULL);
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
ret = bq27xxx_battery_voltage(di, val);
Expand All @@ -2005,7 +2024,7 @@ static int bq27xxx_battery_get_property(struct power_supply *psy,
val->intval = di->cache.flags < 0 ? 0 : 1;
break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
ret = bq27xxx_battery_current_and_status(di, val, NULL);
ret = bq27xxx_battery_current_and_status(di, val, NULL, NULL);
break;
case POWER_SUPPLY_PROP_CAPACITY:
ret = bq27xxx_simple_value(di->cache.capacity, val);
Expand Down Expand Up @@ -2078,8 +2097,8 @@ static void bq27xxx_external_power_changed(struct power_supply *psy)
{
struct bq27xxx_device_info *di = power_supply_get_drvdata(psy);

cancel_delayed_work_sync(&di->work);
schedule_delayed_work(&di->work, 0);
/* After charger plug in/out wait 0.5s for things to stabilize */
mod_delayed_work(system_wq, &di->work, HZ / 2);
}

int bq27xxx_battery_setup(struct bq27xxx_device_info *di)
Expand Down Expand Up @@ -2127,22 +2146,18 @@ EXPORT_SYMBOL_GPL(bq27xxx_battery_setup);

void bq27xxx_battery_teardown(struct bq27xxx_device_info *di)
{
/*
* power_supply_unregister call bq27xxx_battery_get_property which
* call bq27xxx_battery_poll.
* Make sure that bq27xxx_battery_poll will not call
* schedule_delayed_work again after unregister (which cause OOPS).
*/
poll_interval = 0;

cancel_delayed_work_sync(&di->work);

power_supply_unregister(di->bat);

mutex_lock(&bq27xxx_list_lock);
list_del(&di->list);
mutex_unlock(&bq27xxx_list_lock);

/* Set removed to avoid bq27xxx_battery_update() re-queuing the work */
mutex_lock(&di->lock);
di->removed = true;
mutex_unlock(&di->lock);

cancel_delayed_work_sync(&di->work);

power_supply_unregister(di->bat);
mutex_destroy(&di->lock);
}
EXPORT_SYMBOL_GPL(bq27xxx_battery_teardown);
Expand Down
3 changes: 2 additions & 1 deletion drivers/power/supply/bq27xxx_battery_i2c.c
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ static int bq27xxx_battery_i2c_probe(struct i2c_client *client)
i2c_set_clientdata(client, di);

if (client->irq) {
ret = devm_request_threaded_irq(&client->dev, client->irq,
ret = request_threaded_irq(client->irq,
NULL, bq27xxx_battery_irq_handler_thread,
IRQF_ONESHOT,
di->name, di);
Expand Down Expand Up @@ -209,6 +209,7 @@ static void bq27xxx_battery_i2c_remove(struct i2c_client *client)
{
struct bq27xxx_device_info *di = i2c_get_clientdata(client);

free_irq(client->irq, di);
bq27xxx_battery_teardown(di);

mutex_lock(&battery_mutex);
Expand Down
4 changes: 3 additions & 1 deletion drivers/power/supply/mt6360_charger.c
Original file line number Diff line number Diff line change
Expand Up @@ -796,7 +796,9 @@ static int mt6360_charger_probe(struct platform_device *pdev)
mci->vinovp = 6500000;
mutex_init(&mci->chgdet_lock);
platform_set_drvdata(pdev, mci);
devm_work_autocancel(&pdev->dev, &mci->chrdet_work, mt6360_chrdet_work);
ret = devm_work_autocancel(&pdev->dev, &mci->chrdet_work, mt6360_chrdet_work);
if (ret)
return dev_err_probe(&pdev->dev, ret, "Failed to set delayed work\n");

ret = device_property_read_u32(&pdev->dev, "richtek,vinovp-microvolt", &mci->vinovp);
if (ret)
Expand Down
Loading

0 comments on commit eb03e31

Please sign in to comment.