mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Sebastian Reichel <sebastian.reichel@collabora.com>
To: Alexey Charkov <alchark@flipper.net>
Cc: Lee Jones <lee@kernel.org>,
	Chris Morgan <macromorgan@hotmail.com>,
	 Pavel Machek <pavel@ucw.cz>,
	Krzysztof Kozlowski <krzk@kernel.org>,
	 Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>,
	linux-pm@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH v2 2/4] power: supply: core: Allow getting battery info before psy is registered
Date: Wed, 9 Sep 2026 21:52:11 +0200	[thread overview]
Message-ID: <aqG3p_GKtncRCRI8@venus> (raw)
In-Reply-To: <20260909-bq257xx-init-v2-2-deb4076b1f02@flipper.net>

[-- Attachment #1: Type: text/plain, Size: 11204 bytes --]

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 <alchark@flipper.net>
> ---
>  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
> 

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

  reply	other threads:[~2026-09-09 19:52 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-09 15:41 [PATCH v2 0/4] power: supply: Fix probe time race against driver teardown and battery parsing Alexey Charkov
2026-09-09 15:41 ` [PATCH v2 1/4] power: supply: core: prevent unregistering a power supply while a callback runs Alexey Charkov
2026-09-09 15:41 ` [PATCH v2 2/4] power: supply: core: Allow getting battery info before psy is registered Alexey Charkov
2026-09-09 19:52   ` Sebastian Reichel [this message]
2026-09-10  9:53     ` Alexey Charkov
2026-09-09 15:41 ` [PATCH v2 3/4] power: supply: bq257xx: Use psy directly instead of driver data Alexey Charkov
2026-09-09 15:41 ` [PATCH v2 4/4] power: supply: bq257xx: Parse battery info before registering power supply Alexey Charkov

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=aqG3p_GKtncRCRI8@venus \
    --to=sebastian.reichel@collabora.com \
    --cc=alchark@flipper.net \
    --cc=b.zolnierkie@samsung.com \
    --cc=krzk@kernel.org \
    --cc=lee@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pm@vger.kernel.org \
    --cc=macromorgan@hotmail.com \
    --cc=pavel@ucw.cz \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®