[RFC PATCH v3 4/9] power: supply: Add batinfo getters usable prior supply registration

From: Matti Vaittinen
Date: Tue Nov 16 2021 - 07:27:07 EST


In some cases it is beneficial to be able to query the static battery
node properties prior power_supply registration. The device-tree
parsing does not really depend on psy so add APIs which can
be used without the power-supply. Also, convert APIs to operate on
fwnode while at it.

Signed-off-by: Matti Vaittinen <matti.vaittinen@xxxxxxxxxxxxxxxxx>
---
RFCv3:
- New patch
---
drivers/power/supply/power_supply_core.c | 279 ++++++++++++++---------
include/linux/power_supply.h | 5 +
2 files changed, 176 insertions(+), 108 deletions(-)

diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c
index 1a21f692ab81..47176ed2570b 100644
--- a/drivers/power/supply/power_supply_core.c
+++ b/drivers/power/supply/power_supply_core.c
@@ -562,16 +562,63 @@ struct power_supply *devm_power_supply_get_by_phandle(struct device *dev,
EXPORT_SYMBOL_GPL(devm_power_supply_get_by_phandle);
#endif /* CONFIG_OF */

+struct psy_int_tuple {
+ int a;
+ int b;
+};
+
+static int get_fwnode_tuple_array(struct device *dev, struct fwnode_handle *fw,
+ const char *name,
+ struct psy_int_tuple **tuple, int *num_tuple)
+{
+ int num_values, i, ret;
+ u32 *tmp_table;
+
+ num_values = fwnode_property_count_u32(fw, name);
+ if (num_values <= 0) {
+ dev_err(dev, "failed to get %s\n", name);
+ return -EINVAL;
+ }
+
+ if (num_values & 0x1)
+ dev_warn(dev, "odd number of '%s' values\n", name);
+
+ tmp_table = kcalloc(num_values, sizeof(*tmp_table), GFP_KERNEL);
+ if (!tmp_table)
+ return -ENOMEM;
+
+ *tuple = devm_kcalloc(dev, num_values / 2, sizeof(**tuple),
+ GFP_KERNEL);
+ if (!*tuple) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ ret = fwnode_property_read_u32_array(fw, name, tmp_table, num_values);
+ if (ret)
+ goto out;
+
+ *num_tuple = num_values / 2;
+ for (i = 0; i < *num_tuple; i++) {
+ (*tuple)[i].a = tmp_table[i * 2];
+ (*tuple)[i].b = tmp_table[i * 2 + 1];
+ }
+
+out:
+ kfree(tmp_table);
+
+ return ret;
+}
+
#define POWER_SUPPLY_TEMP_DGRD_MAX_VALUES 100
-int power_supply_get_battery_info(struct power_supply *psy,
- struct power_supply_battery_info *info)
+int power_supply_dev_get_battery_info(struct device *dev,
+ struct fwnode_handle *node,
+ struct power_supply_battery_info *info)
{
- struct power_supply_resistance_temp_table *resist_table;
u32 *dgrd_table;
- struct device_node *battery_np;
- const char *value;
+ struct fwnode_handle *battery_node;
int err, len, index;
- const __be32 *list;
+ const char *value;
+ u32 tuple[2];

info->technology = POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
info->energy_full_design_uwh = -EINVAL;
@@ -599,21 +646,23 @@ int power_supply_get_battery_info(struct power_supply *psy,
info->ocv_table_size[index] = -EINVAL;
}

- if (!psy->of_node) {
- dev_warn(&psy->dev, "%s currently only supports devicetree\n",
- __func__);
- return -ENXIO;
- }
+ if (!node)
+ node = dev_fwnode(dev);

- battery_np = of_parse_phandle(psy->of_node, "monitored-battery", 0);
- if (!battery_np)
+ if (!node) {
+ dev_err(dev, "no charger node\n");
return -ENODEV;
+ }

- err = of_property_read_string(battery_np, "compatible", &value);
- if (err)
- goto out_put_node;
+ battery_node = fwnode_find_reference(node, "monitored-battery", 0);
+ if (IS_ERR(battery_node)) {
+ dev_err(dev, "No battery node found\n");
+ return PTR_ERR(battery_node);
+ }
+
+ if (fwnode_property_match_string(battery_node, "compatible",
+ "simple-battery")) {

- if (strcmp("simple-battery", value)) {
err = -ENODEV;
goto out_put_node;
}
@@ -622,8 +671,7 @@ int power_supply_get_battery_info(struct power_supply *psy,
* in enum power_supply_property. For reasoning, see
* Documentation/power/power_supply_class.rst.
*/
-
- if (!of_property_read_string(battery_np, "device-chemistry", &value)) {
+ if (!fwnode_property_read_string(battery_node, "device-chemistry", &value)) {
if (!strcmp("nickel-cadmium", value))
info->technology = POWER_SUPPLY_TECHNOLOGY_NiCd;
else if (!strcmp("nickel-metal-hydride", value))
@@ -638,67 +686,73 @@ 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);
}

- of_property_read_u32(battery_np, "energy-full-design-microwatt-hours",
+ fwnode_property_read_u32(battery_node, "energy-full-design-microwatt-hours",
&info->energy_full_design_uwh);
- of_property_read_u32(battery_np, "charge-full-design-microamp-hours",
+ fwnode_property_read_u32(battery_node, "charge-full-design-microamp-hours",
&info->charge_full_design_uah);
- of_property_read_u32(battery_np, "voltage-min-design-microvolt",
+ fwnode_property_read_u32(battery_node, "voltage-min-design-microvolt",
&info->voltage_min_design_uv);
- of_property_read_u32(battery_np, "voltage-max-design-microvolt",
+ fwnode_property_read_u32(battery_node, "voltage-max-design-microvolt",
&info->voltage_max_design_uv);
- of_property_read_u32(battery_np, "trickle-charge-current-microamp",
+ fwnode_property_read_u32(battery_node, "trickle-charge-current-microamp",
&info->tricklecharge_current_ua);
- of_property_read_u32(battery_np, "precharge-current-microamp",
+ fwnode_property_read_u32(battery_node, "precharge-current-microamp",
&info->precharge_current_ua);
- of_property_read_u32(battery_np, "precharge-upper-limit-microvolt",
+ fwnode_property_read_u32(battery_node, "precharge-upper-limit-microvolt",
&info->precharge_voltage_max_uv);
- of_property_read_u32(battery_np, "charge-term-current-microamp",
+ fwnode_property_read_u32(battery_node, "charge-term-current-microamp",
&info->charge_term_current_ua);
- of_property_read_u32(battery_np, "re-charge-voltage-microvolt",
+ fwnode_property_read_u32(battery_node, "re-charge-voltage-microvolt",
&info->charge_restart_voltage_uv);
- of_property_read_u32(battery_np, "over-voltage-threshold-microvolt",
+ fwnode_property_read_u32(battery_node, "over-voltage-threshold-microvolt",
&info->overvoltage_limit_uv);
- of_property_read_u32(battery_np, "constant-charge-current-max-microamp",
+ fwnode_property_read_u32(battery_node, "constant-charge-current-max-microamp",
&info->constant_charge_current_max_ua);
- of_property_read_u32(battery_np, "constant-charge-voltage-max-microvolt",
+ fwnode_property_read_u32(battery_node, "constant-charge-voltage-max-microvolt",
&info->constant_charge_voltage_max_uv);
- of_property_read_u32(battery_np, "factory-internal-resistance-micro-ohms",
+ fwnode_property_read_u32(battery_node, "factory-internal-resistance-micro-ohms",
&info->factory_internal_resistance_uohm);

- of_property_read_u32_index(battery_np, "ambient-celsius",
- 0, &info->temp_ambient_alert_min);
- of_property_read_u32_index(battery_np, "ambient-celsius",
- 1, &info->temp_ambient_alert_max);
- of_property_read_u32_index(battery_np, "alert-celsius",
- 0, &info->temp_alert_min);
- of_property_read_u32_index(battery_np, "alert-celsius",
- 1, &info->temp_alert_max);
- of_property_read_u32_index(battery_np, "operating-range-celsius",
- 0, &info->temp_min);
- of_property_read_u32_index(battery_np, "operating-range-celsius",
- 1, &info->temp_max);
-
- len = of_property_count_u32_elems(battery_np, "temp-degrade-table");
+ if (!fwnode_property_read_u32_array(battery_node, "ambient-celsius",
+ &tuple[0], 2)) {
+ info->temp_ambient_alert_min = tuple[0];
+ info->temp_ambient_alert_max = tuple[1];
+ }
+
+ if (!fwnode_property_read_u32_array(battery_node, "alert-celsius",
+ &tuple[0], 2)) {
+ info->temp_alert_min = tuple[0];
+ info->temp_alert_max = tuple[1];
+ }
+
+ if (!fwnode_property_read_u32_array(battery_node,
+ "operating-range-celsius",
+ &tuple[0], 2)) {
+ info->temp_min = tuple[0];
+ info->temp_max = tuple[1];
+ }
+
+ len = fwnode_property_count_u32(battery_node, "temp-degrade-table");
if (len == -EINVAL)
len = 0;
if (len < 0) {
+ dev_err(dev, "malformed temp-degrade-table %d\n", len);
err = len;
goto out_put_node;
}
- /* table should consist of value pairs - maximum of 100 pairs */
+ /* table should consist of value triplets - maximum of 100 triplets */
if (len % 3 || len / 3 > POWER_SUPPLY_TEMP_DGRD_MAX_VALUES) {
- dev_warn(&psy->dev,
+ dev_warn(dev,
"bad amount of temperature-capacity degrade values\n");
err = -EINVAL;
goto out_put_node;
}
info->temp_dgrd_values = len / 3;
if (info->temp_dgrd_values) {
- info->temp_dgrd = devm_kcalloc(&psy->dev,
- info->temp_dgrd_values,
+ info->temp_dgrd = devm_kcalloc(dev, info->temp_dgrd_values,
sizeof(*info->temp_dgrd),
GFP_KERNEL);
if (!info->temp_dgrd) {
@@ -710,12 +764,13 @@ int power_supply_get_battery_info(struct power_supply *psy,
err = -ENOMEM;
goto out_put_node;
}
- err = of_property_read_u32_array(battery_np,
- "temp-degrade-table",
- dgrd_table, len);
+ err = fwnode_property_read_u32_array(battery_node,
+ "temp-degrade-table",
+ dgrd_table, len);
if (err) {
- dev_warn(&psy->dev,
- "bad temperature - capacity degrade values %d\n", err);
+ dev_warn(dev,
+ "bad temperature - capacity degrade values %d\n",
+ err);
kfree(dgrd_table);
info->temp_dgrd_values = 0;
goto out_put_node;
@@ -730,92 +785,100 @@ int power_supply_get_battery_info(struct power_supply *psy,
kfree(dgrd_table);
}

- len = of_property_count_u32_elems(battery_np, "ocv-capacity-celsius");
- if (len < 0 && len != -EINVAL) {
+ len = fwnode_property_count_u32(battery_node, "ocv-capacity-celsius");
+ if (len == -EINVAL)
+ len = 0;
+
+ if (len < 0) {
+ dev_err(dev, "malformed ocv-capacity-celsius table\n");
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) {
- of_property_read_u32_array(battery_np, "ocv-capacity-celsius",
- info->ocv_temp, len);
+ u32 *tmp;
+
+ tmp = kcalloc(len, sizeof(*tmp), GFP_KERNEL);
+ if (!tmp)
+ return -ENOMEM;
+
+ fwnode_property_read_u32_array(battery_node,
+ "ocv-capacity-celsius",
+ tmp, len);
+ for (index = 0; index < len; index++)
+ info->ocv_temp[index] = tmp[index];
+
+ kfree(tmp);
}

for (index = 0; index < len; index++) {
- struct power_supply_battery_ocv_table *table;
char *propname;
- int i, tab_len, size;

propname = kasprintf(GFP_KERNEL, "ocv-capacity-table-%d", index);
- list = of_get_property(battery_np, propname, &size);
- if (!list || !size) {
- dev_err(&psy->dev, "failed to get %s\n", propname);
+
+ err = get_fwnode_tuple_array(dev, battery_node, propname,
+ (struct psy_int_tuple **)
+ &info->ocv_table[index],
+ &info->ocv_table_size[index]);
+ if (err) {
kfree(propname);
- power_supply_put_battery_info(psy, info);
- err = -EINVAL;
+ power_supply_dev_put_battery_info(dev, info);
goto out_put_node;
}
-
kfree(propname);
- tab_len = size / (2 * sizeof(__be32));
- info->ocv_table_size[index] = tab_len;
-
- table = info->ocv_table[index] =
- devm_kcalloc(&psy->dev, tab_len, sizeof(*table), GFP_KERNEL);
- if (!info->ocv_table[index]) {
- power_supply_put_battery_info(psy, info);
- err = -ENOMEM;
- goto out_put_node;
- }
-
- for (i = 0; i < tab_len; i++) {
- table[i].ocv = be32_to_cpu(*list);
- list++;
- table[i].capacity = be32_to_cpu(*list);
- list++;
- }
}

- list = of_get_property(battery_np, "resistance-temp-table", &len);
- if (!list || !len)
- goto out_put_node;
-
- info->resist_table_size = len / (2 * sizeof(__be32));
- resist_table = info->resist_table = devm_kcalloc(&psy->dev,
- info->resist_table_size,
- sizeof(*resist_table),
- GFP_KERNEL);
- if (!info->resist_table) {
- power_supply_put_battery_info(psy, info);
- err = -ENOMEM;
+ err = get_fwnode_tuple_array(dev, battery_node,
+ "resistance-temp-table",
+ (struct psy_int_tuple **)&info->resist_table,
+ &info->resist_table_size);
+ if (err == -ENOMEM) {
+ power_supply_dev_put_battery_info(dev, info);
goto out_put_node;
}
-
- for (index = 0; index < info->resist_table_size; index++) {
- resist_table[index].temp = be32_to_cpu(*list++);
- resist_table[index].resistance = be32_to_cpu(*list++);
- }
+ err = 0;

out_put_node:
- of_node_put(battery_np);
+ fwnode_handle_put(battery_node);
+
return err;
+
+}
+EXPORT_SYMBOL_GPL(power_supply_dev_get_battery_info);
+
+int power_supply_get_battery_info(struct power_supply *psy,
+ struct power_supply_battery_info *info)
+{
+ struct fwnode_handle *fw = NULL;
+
+ if (psy->of_node)
+ fw = of_fwnode_handle(psy->of_node);
+
+ return power_supply_dev_get_battery_info(&psy->dev, fw, info);
}
EXPORT_SYMBOL_GPL(power_supply_get_battery_info);

-void power_supply_put_battery_info(struct power_supply *psy,
- struct power_supply_battery_info *info)
+void power_supply_dev_put_battery_info(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);
+}
+EXPORT_SYMBOL_GPL(power_supply_dev_put_battery_info);
+
+void power_supply_put_battery_info(struct power_supply *psy,
+ struct power_supply_battery_info *info)
+{
+ power_supply_dev_put_battery_info(&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 fbc07d403f41..ef7db73a5bd1 100644
--- a/include/linux/power_supply.h
+++ b/include/linux/power_supply.h
@@ -430,6 +430,11 @@ devm_power_supply_get_by_phandle(struct device *dev, const char *property)
{ return NULL; }
#endif /* CONFIG_OF */

+void power_supply_dev_put_battery_info(struct device *dev,
+ struct power_supply_battery_info *info);
+int power_supply_dev_get_battery_info(struct device *dev,
+ struct fwnode_handle *node,
+ struct power_supply_battery_info *info);
extern int power_supply_get_battery_info(struct power_supply *psy,
struct power_supply_battery_info *info);
extern void power_supply_put_battery_info(struct power_supply *psy,
--
2.31.1


--
Matti Vaittinen, Linux device drivers
ROHM Semiconductors, Finland SWDC
Kiviharjunlenkki 1E
90220 OULU
FINLAND

~~~ "I don't think so," said Rene Descartes. Just then he vanished ~~~
Simon says - in Latin please.
~~~ "non cogito me" dixit Rene Descarte, deinde evanescavit ~~~
Thanks to Simon Glass for the translation =]

Attachment: signature.asc
Description: PGP signature