From: Alexey Charkov <alchark@flipper.net>
To: Sebastian Reichel <sre@kernel.org>, 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>
Cc: Sebastian Reichel <sebastian.reichel@collabora.com>,
linux-pm@vger.kernel.org, linux-kernel@vger.kernel.org,
Alexey Charkov <alchark@flipper.net>
Subject: [PATCH v2 2/4] power: supply: core: Allow getting battery info before psy is registered
Date: Wed, 09 Sep 2026 19:41:45 +0400 [thread overview]
Message-ID: <20260909-bq257xx-init-v2-2-deb4076b1f02@flipper.net> (raw)
In-Reply-To: <20260909-bq257xx-init-v2-0-deb4076b1f02@flipper.net>
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.
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
next prev parent reply other threads:[~2026-09-09 15:42 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 ` Alexey Charkov [this message]
2026-09-09 19:52 ` [PATCH v2 2/4] power: supply: core: Allow getting battery info before psy is registered Sebastian Reichel
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=20260909-bq257xx-init-v2-2-deb4076b1f02@flipper.net \
--to=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 \
--cc=sebastian.reichel@collabora.com \
--cc=sre@kernel.org \
/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®