power: supply: bq256xx: drain usb_work before freeing the charger

The USB-PHY notifier queues usb_work, whose handler calls
power_supply_changed(bq->charger). The reset devm action only unregisters
the notifier and was registered before the power supplies, so devm frees
bq->charger on unwind before the action runs; a usb_work still queued can
then dereference it.

Register the reset action after the power supplies, so it unregisters
the notifiers and drains usb_work before the supplies are released.
Initialize usb_work and obtain the PHY references before registering
the notifiers, so the worker cannot run before the supplies exist.

Found by static analysis.

Fixes: 32e4978bb9 ("power: supply: bq256xx: Introduce the BQ256XX charger driver")
Cc: stable@vger.kernel.org
Assisted-by: Codex:gpt-5.6
Signed-off-by: Fan Wu <fanwu01@zju.edu.cn>
Link: https://patch.msgid.link/20260804145511.103470-1-fanwu01@zju.edu.cn
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
This commit is contained in:
Fan Wu 2026-08-04 14:55:11 +00:00 committed by Sebastian Reichel
parent 37689bcbc4
commit 2dd6cd8237

View File

@ -892,6 +892,8 @@ static void bq256xx_charger_reset(void *data)
if (!IS_ERR_OR_NULL(bq->usb3_phy))
usb_unregister_notifier(bq->usb3_phy, &bq->usb_nb);
cancel_work_sync(&bq->usb_work);
}
static int bq256xx_set_charger_property(struct power_supply *psy,
@ -1717,24 +1719,12 @@ static int bq256xx_probe(struct i2c_client *client)
return ret;
}
ret = devm_add_action_or_reset(dev, bq256xx_charger_reset, bq);
if (ret)
return ret;
INIT_WORK(&bq->usb_work, bq256xx_usb_work);
bq->usb_nb.notifier_call = bq256xx_usb_notifier;
/* OTG reporting */
bq->usb2_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
if (!IS_ERR_OR_NULL(bq->usb2_phy)) {
INIT_WORK(&bq->usb_work, bq256xx_usb_work);
bq->usb_nb.notifier_call = bq256xx_usb_notifier;
usb_register_notifier(bq->usb2_phy, &bq->usb_nb);
}
bq->usb3_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB3);
if (!IS_ERR_OR_NULL(bq->usb3_phy)) {
INIT_WORK(&bq->usb_work, bq256xx_usb_work);
bq->usb_nb.notifier_call = bq256xx_usb_notifier;
usb_register_notifier(bq->usb3_phy, &bq->usb_nb);
}
ret = bq256xx_power_supply_init(bq, &psy_cfg, dev);
if (ret) {
@ -1742,6 +1732,17 @@ static int bq256xx_probe(struct i2c_client *client)
return ret;
}
/* Register after the power supplies so devm runs it first. */
ret = devm_add_action_or_reset(dev, bq256xx_charger_reset, bq);
if (ret)
return ret;
if (!IS_ERR_OR_NULL(bq->usb2_phy))
usb_register_notifier(bq->usb2_phy, &bq->usb_nb);
if (!IS_ERR_OR_NULL(bq->usb3_phy))
usb_register_notifier(bq->usb3_phy, &bq->usb_nb);
if (client->irq) {
ret = devm_request_threaded_irq(dev, client->irq, NULL,
bq256xx_irq_handler_thread,