Hello Alexey, On Wed, Sep 09, 2026 at 07:41:45PM +0400, Alexey Charkov wrote: > Some power supplies, such as battery chargers, may need to program the > device parameters based on what their connected battery allows. Current > API requires registering the power supply to access battery information, > which is problematic because a registered power supply is immediately > available to the rest of the system, but the battery parameters may not > be set yet in the charger. > > Given that the battery info helpers really only need a fwnode and a struct > device to hang devres-allocated resourses on, add a pure dev-based get/put > API alongside the existing psy-based one, which can be used to query the > battery information before registering the power supply. Use the new init callback for that, which got introduced in the v7.3 cycle: c1eb5905fdce ("power: supply: Add registration init callback") See for example fdece8642eca ("power: supply: bq25630: Initialize hardware before exposing the power supply") for a driver that was converted to this. Greetings, -- Sebastian > > Signed-off-by: Alexey Charkov > --- > drivers/power/supply/power_supply_core.c | 102 ++++++++++++++++++++++--------- > include/linux/power_supply.h | 4 ++ > 2 files changed, 77 insertions(+), 29 deletions(-) > > diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c > index 1279785645fb..09473361772f 100644 > --- a/drivers/power/supply/power_supply_core.c > +++ b/drivers/power/supply/power_supply_core.c > @@ -725,21 +725,18 @@ struct power_supply *devm_power_supply_get_by_reference(struct device *dev, > } > EXPORT_SYMBOL_GPL(devm_power_supply_get_by_reference); > > -int power_supply_get_battery_info(struct power_supply *psy, > - struct power_supply_battery_info **info_out) > +static int __power_supply_get_battery_info(struct device *dev, > + struct fwnode_handle *srcnode, > + struct power_supply_battery_info **info_out) > { > struct power_supply_resistance_temp_table *resist_table; > struct power_supply_battery_info *info; > - struct fwnode_handle *srcnode, *fwnode; > + struct fwnode_handle *fwnode; > const char *value; > int err, len, index, proplen; > u32 *propdata __free(kfree) = NULL; > u32 min_max[2]; > > - srcnode = dev_fwnode(&psy->dev); > - if (!srcnode && psy->dev.parent) > - srcnode = dev_fwnode(psy->dev.parent); > - > fwnode = fwnode_find_reference(srcnode, "monitored-battery", 0); > if (IS_ERR(fwnode)) > return PTR_ERR(fwnode); > @@ -750,7 +747,7 @@ int power_supply_get_battery_info(struct power_supply *psy, > > > /* Try static batteries first */ > - err = samsung_sdi_battery_get_info(&psy->dev, value, &info); > + err = samsung_sdi_battery_get_info(dev, value, &info); > if (!err) > goto out_ret_pointer; > else if (err == -ENODEV) > @@ -765,7 +762,7 @@ int power_supply_get_battery_info(struct power_supply *psy, > goto out_put_node; > } > > - info = devm_kzalloc(&psy->dev, sizeof(*info), GFP_KERNEL); > + info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL); > if (!info) { > err = -ENOMEM; > goto out_put_node; > @@ -826,7 +823,7 @@ int power_supply_get_battery_info(struct power_supply *psy, > else if (!strcmp("lithium-ion-manganese-oxide", value)) > info->technology = POWER_SUPPLY_TECHNOLOGY_LiMn; > else > - dev_warn(&psy->dev, "%s unknown battery type\n", value); > + dev_warn(dev, "%s unknown battery type\n", value); > } > > fwnode_property_read_u32(fwnode, "energy-full-design-microwatt-hours", > @@ -877,7 +874,7 @@ int power_supply_get_battery_info(struct power_supply *psy, > err = len; > goto out_put_node; > } else if (len > POWER_SUPPLY_OCV_TEMP_MAX) { > - dev_err(&psy->dev, "Too many temperature values\n"); > + dev_err(dev, "Too many temperature values\n"); > err = -EINVAL; > goto out_put_node; > } else if (len > 0) { > @@ -892,28 +889,28 @@ int power_supply_get_battery_info(struct power_supply *psy, > char *propname __free(kfree) = kasprintf(GFP_KERNEL, "ocv-capacity-table-%d", > index); > if (!propname) { > - power_supply_put_battery_info(psy, info); > + power_supply_put_battery_info_from_dev(dev, info); > err = -ENOMEM; > goto out_put_node; > } > proplen = fwnode_property_count_u32(fwnode, propname); > if (proplen < 0 || proplen % 2 != 0) { > - dev_err(&psy->dev, "failed to get %s\n", propname); > - power_supply_put_battery_info(psy, info); > + dev_err(dev, "failed to get %s\n", propname); > + power_supply_put_battery_info_from_dev(dev, info); > err = -EINVAL; > goto out_put_node; > } > > u32 *propdata __free(kfree) = kzalloc_objs(*propdata, proplen); > if (!propdata) { > - power_supply_put_battery_info(psy, info); > + power_supply_put_battery_info_from_dev(dev, info); > err = -EINVAL; > goto out_put_node; > } > err = fwnode_property_read_u32_array(fwnode, propname, propdata, proplen); > if (err < 0) { > - dev_err(&psy->dev, "failed to get %s\n", propname); > - power_supply_put_battery_info(psy, info); > + dev_err(dev, "failed to get %s\n", propname); > + power_supply_put_battery_info_from_dev(dev, info); > goto out_put_node; > } > > @@ -921,9 +918,9 @@ int power_supply_get_battery_info(struct power_supply *psy, > info->ocv_table_size[index] = tab_len; > > info->ocv_table[index] = table = > - devm_kcalloc(&psy->dev, tab_len, sizeof(*table), GFP_KERNEL); > + devm_kcalloc(dev, tab_len, sizeof(*table), GFP_KERNEL); > if (!info->ocv_table[index]) { > - power_supply_put_battery_info(psy, info); > + power_supply_put_battery_info_from_dev(dev, info); > err = -ENOMEM; > goto out_put_node; > } > @@ -939,14 +936,14 @@ int power_supply_get_battery_info(struct power_supply *psy, > err = 0; > goto out_ret_pointer; > } else if (proplen < 0 || proplen % 2 != 0) { > - power_supply_put_battery_info(psy, info); > + power_supply_put_battery_info_from_dev(dev, info); > err = (proplen < 0) ? proplen : -EINVAL; > goto out_put_node; > } > > propdata = kzalloc_objs(*propdata, proplen); > if (!propdata) { > - power_supply_put_battery_info(psy, info); > + power_supply_put_battery_info_from_dev(dev, info); > err = -ENOMEM; > goto out_put_node; > } > @@ -954,17 +951,17 @@ int power_supply_get_battery_info(struct power_supply *psy, > err = fwnode_property_read_u32_array(fwnode, "resistance-temp-table", > propdata, proplen); > if (err < 0) { > - power_supply_put_battery_info(psy, info); > + power_supply_put_battery_info_from_dev(dev, info); > goto out_put_node; > } > > info->resist_table_size = proplen / 2; > - info->resist_table = resist_table = devm_kcalloc(&psy->dev, > + info->resist_table = resist_table = devm_kcalloc(dev, > info->resist_table_size, > sizeof(*resist_table), > GFP_KERNEL); > if (!info->resist_table) { > - power_supply_put_battery_info(psy, info); > + power_supply_put_battery_info_from_dev(dev, info); > err = -ENOMEM; > goto out_put_node; > } > @@ -982,22 +979,69 @@ int power_supply_get_battery_info(struct power_supply *psy, > fwnode_handle_put(fwnode); > return err; > } > + > +int power_supply_get_battery_info(struct power_supply *psy, > + struct power_supply_battery_info **info_out) > +{ > + struct fwnode_handle *srcnode; > + > + srcnode = dev_fwnode(&psy->dev); > + if (!srcnode && psy->dev.parent) > + srcnode = dev_fwnode(psy->dev.parent); > + > + return __power_supply_get_battery_info(&psy->dev, srcnode, info_out); > +} > EXPORT_SYMBOL_GPL(power_supply_get_battery_info); > > -void power_supply_put_battery_info(struct power_supply *psy, > - struct power_supply_battery_info *info) > +/** > + * power_supply_get_battery_info_from_dev() - Get battery info without a supply > + * @dev: Device holding the "monitored-battery" reference, which also owns the > + * devres allocations made for the returned info > + * @info_out: Pointer to store the resulting battery info > + * > + * Same as power_supply_get_battery_info(), but keyed off a plain device rather > + * than a registered power supply. Chargers that program hardware limits taken > + * from the battery node need those values *before* they can safely register > + * their power supply: registering makes the supply callable, so a later probe > + * failure would free driver data underneath a running callback. > + * > + * Release the result with power_supply_put_battery_info_from_dev(). > + * > + * Return: 0 on success or an error code on failure. > + */ > +int power_supply_get_battery_info_from_dev(struct device *dev, > + struct power_supply_battery_info **info_out) > +{ > + return __power_supply_get_battery_info(dev, dev_fwnode(dev), info_out); > +} > +EXPORT_SYMBOL_GPL(power_supply_get_battery_info_from_dev); > + > +/** > + * power_supply_put_battery_info_from_dev() - Release battery info > + * @dev: Device passed to power_supply_get_battery_info_from_dev() > + * @info: Battery info to release > + */ > +void power_supply_put_battery_info_from_dev(struct device *dev, > + struct power_supply_battery_info *info) > { > int i; > > for (i = 0; i < POWER_SUPPLY_OCV_TEMP_MAX; i++) { > if (info->ocv_table[i]) > - devm_kfree(&psy->dev, info->ocv_table[i]); > + devm_kfree(dev, info->ocv_table[i]); > } > > if (info->resist_table) > - devm_kfree(&psy->dev, info->resist_table); > + devm_kfree(dev, info->resist_table); > + > + devm_kfree(dev, info); > +} > +EXPORT_SYMBOL_GPL(power_supply_put_battery_info_from_dev); > > - devm_kfree(&psy->dev, info); > +void power_supply_put_battery_info(struct power_supply *psy, > + struct power_supply_battery_info *info) > +{ > + power_supply_put_battery_info_from_dev(&psy->dev, info); > } > EXPORT_SYMBOL_GPL(power_supply_put_battery_info); > > diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h > index 131cafded72f..f42ae4e3bf81 100644 > --- a/include/linux/power_supply.h > +++ b/include/linux/power_supply.h > @@ -865,6 +865,10 @@ extern int power_supply_get_battery_info(struct power_supply *psy, > struct power_supply_battery_info **info_out); > extern void power_supply_put_battery_info(struct power_supply *psy, > struct power_supply_battery_info *info); > +extern int power_supply_get_battery_info_from_dev(struct device *dev, > + struct power_supply_battery_info **info_out); > +extern void power_supply_put_battery_info_from_dev(struct device *dev, > + struct power_supply_battery_info *info); > extern bool power_supply_battery_info_has_prop(struct power_supply_battery_info *info, > enum power_supply_property psp); > extern int power_supply_battery_info_get_prop(struct power_supply_battery_info *info, > > -- > 2.55.0 >