* [PATCH v1 1/9] power: supply: max17042_battery: Put LSB units into defines
2026-04-06 20:57 [PATCH v1 0/9] power: supply: max17042_battery: improve MAX17055 support Vincent Cloutier
@ 2026-04-06 20:57 ` Vincent Cloutier
2026-04-06 20:57 ` [PATCH v1 2/9] power: supply: max17042_battery: Use Current register in get_status Vincent Cloutier
` (8 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Vincent Cloutier @ 2026-04-06 20:57 UTC (permalink / raw)
To: Sebastian Reichel
Cc: Hans de Goede, Krzysztof Kozlowski, Marek Szyprowski,
Sebastian Krzyszkowiak, Purism Kernel Team, linux-pm,
linux-kernel
From: Sebastian Krzyszkowiak <sebastian.krzyszkowiak@puri.sm>
Signed-off-by: Sebastian Krzyszkowiak <sebastian.krzyszkowiak@puri.sm>
---
drivers/power/supply/max17042_battery.c | 36 ++++++++++++++-----------
1 file changed, 21 insertions(+), 15 deletions(-)
diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c
index acea176101fa..a6798b9a1c82 100644
--- a/drivers/power/supply/max17042_battery.c
+++ b/drivers/power/supply/max17042_battery.c
@@ -52,6 +52,13 @@
#define MAX17042_VMAX_TOLERANCE 50 /* 50 mV */
+#define MAX17042_CURRENT_LSB 1562500ll /* 1.5625µV/Rsense */
+#define MAX17042_CAPACITY_LSB 5000000ll /* 5.0µVH/Rsense */
+#define MAX17042_TIME_LSB 5625 / 1000 /* s */
+#define MAX17042_VOLTAGE_LSB 625 / 8 /* µV */
+#define MAX17042_RESISTANCE_LSB 1 / 4096 /* Ω */
+#define MAX17042_TEMPERATURE_LSB 1 / 256 /* °C */
+
struct max17042_chip {
struct device *dev;
struct regmap *regmap;
@@ -105,8 +112,7 @@ static int max17042_get_temperature(struct max17042_chip *chip, int *temp)
*temp = sign_extend32(data, 15);
/* The value is converted into deci-centigrade scale */
- /* Units of LSB = 1 / 256 degree Celsius */
- *temp = *temp * 10 / 256;
+ *temp = *temp * 10 * MAX17042_TEMPERATURE_LSB;
return 0;
}
@@ -183,7 +189,7 @@ static int max17042_get_battery_health(struct max17042_chip *chip, int *health)
goto health_error;
/* bits [0-3] unused */
- vavg = val * 625 / 8;
+ vavg = val * MAX17042_VOLTAGE_LSB;
/* Convert to millivolts */
vavg /= 1000;
@@ -192,7 +198,7 @@ static int max17042_get_battery_health(struct max17042_chip *chip, int *health)
goto health_error;
/* bits [0-3] unused */
- vbatt = val * 625 / 8;
+ vbatt = val * MAX17042_VOLTAGE_LSB;
/* Convert to millivolts */
vbatt /= 1000;
@@ -299,21 +305,21 @@ static int max17042_get_property(struct power_supply *psy,
if (ret < 0)
return ret;
- val->intval = data * 625 / 8;
+ val->intval = data * MAX17042_VOLTAGE_LSB;
break;
case POWER_SUPPLY_PROP_VOLTAGE_AVG:
ret = regmap_read(map, MAX17042_AvgVCELL, &data);
if (ret < 0)
return ret;
- val->intval = data * 625 / 8;
+ val->intval = data * MAX17042_VOLTAGE_LSB;
break;
case POWER_SUPPLY_PROP_VOLTAGE_OCV:
ret = regmap_read(map, MAX17042_OCVInternal, &data);
if (ret < 0)
return ret;
- val->intval = data * 625 / 8;
+ val->intval = data * MAX17042_VOLTAGE_LSB;
break;
case POWER_SUPPLY_PROP_CAPACITY:
if (chip->pdata->enable_current_sense)
@@ -330,7 +336,7 @@ static int max17042_get_property(struct power_supply *psy,
if (ret < 0)
return ret;
- data64 = data * 5000000ll;
+ data64 = data * MAX17042_CAPACITY_LSB;
do_div(data64, chip->pdata->r_sns);
val->intval = data64;
break;
@@ -339,7 +345,7 @@ static int max17042_get_property(struct power_supply *psy,
if (ret < 0)
return ret;
- data64 = data * 5000000ll;
+ data64 = data * MAX17042_CAPACITY_LSB;
do_div(data64, chip->pdata->r_sns);
val->intval = data64;
break;
@@ -348,7 +354,7 @@ static int max17042_get_property(struct power_supply *psy,
if (ret < 0)
return ret;
- data64 = data * 5000000ll;
+ data64 = data * MAX17042_CAPACITY_LSB;
do_div(data64, chip->pdata->r_sns);
val->intval = data64;
break;
@@ -357,7 +363,7 @@ static int max17042_get_property(struct power_supply *psy,
if (ret < 0)
return ret;
- data64 = sign_extend64(data, 15) * 5000000ll;
+ data64 = sign_extend64(data, 15) * MAX17042_CAPACITY_LSB;
val->intval = div_s64(data64, chip->pdata->r_sns);
break;
case POWER_SUPPLY_PROP_TEMP:
@@ -399,7 +405,7 @@ static int max17042_get_property(struct power_supply *psy,
if (ret < 0)
return ret;
- data64 = sign_extend64(data, 15) * 1562500ll;
+ data64 = sign_extend64(data, 15) * MAX17042_CURRENT_LSB;
val->intval = div_s64(data64, chip->pdata->r_sns);
} else {
return -EINVAL;
@@ -411,7 +417,7 @@ static int max17042_get_property(struct power_supply *psy,
if (ret < 0)
return ret;
- data64 = sign_extend64(data, 15) * 1562500ll;
+ data64 = sign_extend64(data, 15) * MAX17042_CURRENT_LSB;
val->intval = div_s64(data64, chip->pdata->r_sns);
} else {
return -EINVAL;
@@ -422,7 +428,7 @@ static int max17042_get_property(struct power_supply *psy,
if (ret < 0)
return ret;
- data64 = data * 1562500ll;
+ data64 = data * MAX17042_CURRENT_LSB;
val->intval = div_s64(data64, chip->pdata->r_sns);
break;
case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
@@ -430,7 +436,7 @@ static int max17042_get_property(struct power_supply *psy,
if (ret < 0)
return ret;
- val->intval = data * 5625 / 1000;
+ val->intval = data * MAX17042_TIME_LSB;
break;
default:
return -EINVAL;
--
2.53.0
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH v1 2/9] power: supply: max17042_battery: Use Current register in get_status
2026-04-06 20:57 [PATCH v1 0/9] power: supply: max17042_battery: improve MAX17055 support Vincent Cloutier
2026-04-06 20:57 ` [PATCH v1 1/9] power: supply: max17042_battery: Put LSB units into defines Vincent Cloutier
@ 2026-04-06 20:57 ` Vincent Cloutier
2026-04-06 20:57 ` [PATCH v1 3/9] power: supply: max17042_battery: Use dev_err_probe for power supply registration Vincent Cloutier
` (7 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Vincent Cloutier @ 2026-04-06 20:57 UTC (permalink / raw)
To: Sebastian Reichel
Cc: Hans de Goede, Krzysztof Kozlowski, Marek Szyprowski,
Sebastian Krzyszkowiak, Purism Kernel Team, linux-pm,
linux-kernel
From: Sebastian Krzyszkowiak <sebastian.krzyszkowiak@puri.sm>
It can take a while for AvgCurrent to adjust after (un)plugging the charger. Use the instantaneous value in order to not confuse the userspace.
While at that, don't do unit conversion of the read value. The current code was prone to overflows and we only care about the sign anyway.
Signed-off-by: Sebastian Krzyszkowiak <sebastian.krzyszkowiak@puri.sm>
---
drivers/power/supply/max17042_battery.c | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c
index a6798b9a1c82..ddeba965ab5b 100644
--- a/drivers/power/supply/max17042_battery.c
+++ b/drivers/power/supply/max17042_battery.c
@@ -119,7 +119,7 @@ static int max17042_get_temperature(struct max17042_chip *chip, int *temp)
static int max17042_get_status(struct max17042_chip *chip, int *status)
{
int ret, charge_full, charge_now;
- int avg_current;
+ int current_now;
u32 data;
ret = power_supply_am_i_supplied(chip->battery);
@@ -164,14 +164,13 @@ static int max17042_get_status(struct max17042_chip *chip, int *status)
return 0;
}
- ret = regmap_read(chip->regmap, MAX17042_AvgCurrent, &data);
+ ret = regmap_read(chip->regmap, MAX17042_Current, &data);
if (ret < 0)
return ret;
- avg_current = sign_extend32(data, 15);
- avg_current *= 1562500 / chip->pdata->r_sns;
+ current_now = sign_extend32(data, 15);
- if (avg_current > 0)
+ if (current_now > 0)
*status = POWER_SUPPLY_STATUS_CHARGING;
else
*status = POWER_SUPPLY_STATUS_DISCHARGING;
--
2.53.0
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH v1 3/9] power: supply: max17042_battery: Use dev_err_probe for power supply registration
2026-04-06 20:57 [PATCH v1 0/9] power: supply: max17042_battery: improve MAX17055 support Vincent Cloutier
2026-04-06 20:57 ` [PATCH v1 1/9] power: supply: max17042_battery: Put LSB units into defines Vincent Cloutier
2026-04-06 20:57 ` [PATCH v1 2/9] power: supply: max17042_battery: Use Current register in get_status Vincent Cloutier
@ 2026-04-06 20:57 ` Vincent Cloutier
2026-04-06 20:57 ` [PATCH v1 4/9] power: supply: max17042_battery: Route MAX17055 SOC alerts through dSOCi Vincent Cloutier
` (6 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Vincent Cloutier @ 2026-04-06 20:57 UTC (permalink / raw)
To: Sebastian Reichel
Cc: Hans de Goede, Krzysztof Kozlowski, Marek Szyprowski,
Sebastian Krzyszkowiak, Purism Kernel Team, linux-pm,
linux-kernel, Vincent Cloutier
From: Vincent Cloutier <vincent@cloutier.co>
Use dev_err_probe() for power-supply registration failures so deferred probe does not spam the log and the original error code is preserved.
Signed-off-by: Vincent Cloutier <vincent@cloutier.co>
---
drivers/power/supply/max17042_battery.c | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c
index ddeba965ab5b..31007ba122e5 100644
--- a/drivers/power/supply/max17042_battery.c
+++ b/drivers/power/supply/max17042_battery.c
@@ -1095,10 +1095,9 @@ static int max17042_probe(struct i2c_client *client, struct device *dev, int irq
chip->battery = devm_power_supply_register(dev, max17042_desc,
&psy_cfg);
- if (IS_ERR(chip->battery)) {
- dev_err(dev, "failed: power supply register\n");
- return PTR_ERR(chip->battery);
- }
+ if (IS_ERR(chip->battery))
+ return dev_err_probe(dev, PTR_ERR(chip->battery),
+ "failed: power supply register\n");
if (irq) {
unsigned int flags = IRQF_ONESHOT | IRQF_SHARED | IRQF_PROBE_SHARED;
--
2.53.0
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH v1 4/9] power: supply: max17042_battery: Route MAX17055 SOC alerts through dSOCi
2026-04-06 20:57 [PATCH v1 0/9] power: supply: max17042_battery: improve MAX17055 support Vincent Cloutier
` (2 preceding siblings ...)
2026-04-06 20:57 ` [PATCH v1 3/9] power: supply: max17042_battery: Use dev_err_probe for power supply registration Vincent Cloutier
@ 2026-04-06 20:57 ` Vincent Cloutier
2026-04-06 20:57 ` [PATCH v1 5/9] power: supply: max17042_battery: Keep only critical alerts during suspend Vincent Cloutier
` (5 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Vincent Cloutier @ 2026-04-06 20:57 UTC (permalink / raw)
To: Sebastian Reichel
Cc: Hans de Goede, Krzysztof Kozlowski, Marek Szyprowski,
Sebastian Krzyszkowiak, Purism Kernel Team, linux-pm,
linux-kernel, Vincent Cloutier
From: Vincent Cloutier <vincent@cloutier.co>
Use MAX17055 dSOCi for ordinary 1% state-of-charge notifications and leave SALRT configured for the critical low-battery threshold instead of reprogramming the SALRT window on every alert.
Signed-off-by: Vincent Cloutier <vincent@cloutier.co>
---
drivers/power/supply/max17042_battery.c | 39 +++++++++++++++++++++++--
1 file changed, 36 insertions(+), 3 deletions(-)
diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c
index 31007ba122e5..09bb1babb0d6 100644
--- a/drivers/power/supply/max17042_battery.c
+++ b/drivers/power/supply/max17042_battery.c
@@ -27,6 +27,7 @@
/* Status register bits */
#define STATUS_POR_BIT (1 << 1)
#define STATUS_BST_BIT (1 << 3)
+#define STATUS_DSOCI_BIT (1 << 7)
#define STATUS_VMN_BIT (1 << 8)
#define STATUS_TMN_BIT (1 << 9)
#define STATUS_SMN_BIT (1 << 10)
@@ -38,6 +39,7 @@
/* Interrupt mask bits */
#define CFG_ALRT_BIT_ENBL (1 << 2)
+#define CFG2_DSOCI_BIT_ENBL (1 << 7)
#define VFSOC0_LOCK 0x0000
#define VFSOC0_UNLOCK 0x0080
@@ -52,6 +54,8 @@
#define MAX17042_VMAX_TOLERANCE 50 /* 50 mV */
+#define MAX17042_CRITICAL_SOC 0x03
+
#define MAX17042_CURRENT_LSB 1562500ll /* 1.5625µV/Rsense */
#define MAX17042_CAPACITY_LSB 5000000ll /* 5.0µVH/Rsense */
#define MAX17042_TIME_LSB 5625 / 1000 /* s */
@@ -871,6 +875,34 @@ static void max17042_set_soc_threshold(struct max17042_chip *chip, u16 off)
regmap_write(map, MAX17042_SALRT_Th, soc_tr);
}
+static void max17042_set_critical_soc_threshold(struct max17042_chip *chip)
+{
+ struct regmap *map = chip->regmap;
+ u32 soc;
+
+ if (chip->pdata->enable_current_sense)
+ regmap_read(map, MAX17042_RepSOC, &soc);
+ else
+ regmap_read(map, MAX17042_VFSOC, &soc);
+
+ regmap_write(map, MAX17042_SALRT_Th,
+ ((soc >> 8) >= MAX17042_CRITICAL_SOC) ?
+ 0xff00 + MAX17042_CRITICAL_SOC : 0xff00);
+}
+
+static void max17042_enable_soc_alerts(struct max17042_chip *chip)
+{
+ if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055) {
+ regmap_update_bits(chip->regmap, MAX17055_Config2,
+ CFG2_DSOCI_BIT_ENBL,
+ CFG2_DSOCI_BIT_ENBL);
+ max17042_set_critical_soc_threshold(chip);
+ return;
+ }
+
+ max17042_set_soc_threshold(chip, 1);
+}
+
static irqreturn_t max17042_thread_handler(int id, void *dev)
{
struct max17042_chip *chip = dev;
@@ -881,9 +913,10 @@ static irqreturn_t max17042_thread_handler(int id, void *dev)
if (ret)
return IRQ_HANDLED;
- if ((val & STATUS_SMN_BIT) || (val & STATUS_SMX_BIT)) {
+ if ((val & STATUS_SMN_BIT) || (val & STATUS_SMX_BIT) ||
+ (val & STATUS_DSOCI_BIT)) {
dev_dbg(chip->dev, "SOC threshold INTR\n");
- max17042_set_soc_threshold(chip, 1);
+ max17042_enable_soc_alerts(chip);
}
/* we implicitly handle all alerts via power_supply_changed */
@@ -1111,7 +1144,7 @@ static int max17042_probe(struct i2c_client *client, struct device *dev, int irq
regmap_update_bits(chip->regmap, MAX17042_CONFIG,
CFG_ALRT_BIT_ENBL,
CFG_ALRT_BIT_ENBL);
- max17042_set_soc_threshold(chip, 1);
+ max17042_enable_soc_alerts(chip);
} else {
irq = 0;
if (ret != -EBUSY)
--
2.53.0
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH v1 5/9] power: supply: max17042_battery: Keep only critical alerts during suspend
2026-04-06 20:57 [PATCH v1 0/9] power: supply: max17042_battery: improve MAX17055 support Vincent Cloutier
` (3 preceding siblings ...)
2026-04-06 20:57 ` [PATCH v1 4/9] power: supply: max17042_battery: Route MAX17055 SOC alerts through dSOCi Vincent Cloutier
@ 2026-04-06 20:57 ` Vincent Cloutier
2026-04-06 20:57 ` [PATCH v1 6/9] power: supply: max17042_battery: Remove unused platform-data plumbing Vincent Cloutier
` (4 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Vincent Cloutier @ 2026-04-06 20:57 UTC (permalink / raw)
To: Sebastian Reichel
Cc: Hans de Goede, Krzysztof Kozlowski, Marek Szyprowski,
Sebastian Krzyszkowiak, Purism Kernel Team, linux-pm,
linux-kernel, Vincent Cloutier
From: Vincent Cloutier <vincent@cloutier.co>
Disable MAX17055 dSOCi while the system is suspended so state-of-charge changes do not wake the system repeatedly. Leave SALRT armed for the critical low-battery threshold and restore runtime alert handling on resume.
Signed-off-by: Vincent Cloutier <vincent@cloutier.co>
---
drivers/power/supply/max17042_battery.c | 15 +++++++++++++--
1 file changed, 13 insertions(+), 2 deletions(-)
diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c
index 09bb1babb0d6..b93fedf183b1 100644
--- a/drivers/power/supply/max17042_battery.c
+++ b/drivers/power/supply/max17042_battery.c
@@ -903,6 +903,16 @@ static void max17042_enable_soc_alerts(struct max17042_chip *chip)
max17042_set_soc_threshold(chip, 1);
}
+static void max17042_suspend_soc_alerts(struct max17042_chip *chip)
+{
+ if (chip->chip_type != MAXIM_DEVICE_TYPE_MAX17055)
+ return;
+
+ regmap_update_bits(chip->regmap, MAX17055_Config2,
+ CFG2_DSOCI_BIT_ENBL, 0);
+ max17042_set_critical_soc_threshold(chip);
+}
+
static irqreturn_t max17042_thread_handler(int id, void *dev)
{
struct max17042_chip *chip = dev;
@@ -1220,6 +1230,7 @@ static int max17042_suspend(struct device *dev)
*/
if (chip->irq) {
disable_irq(chip->irq);
+ max17042_suspend_soc_alerts(chip);
enable_irq_wake(chip->irq);
}
@@ -1233,8 +1244,8 @@ static int max17042_resume(struct device *dev)
if (chip->irq) {
disable_irq_wake(chip->irq);
enable_irq(chip->irq);
- /* re-program the SOC thresholds to 1% change */
- max17042_set_soc_threshold(chip, 1);
+ /* re-arm runtime SOC alerts */
+ max17042_enable_soc_alerts(chip);
}
return 0;
--
2.53.0
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH v1 6/9] power: supply: max17042_battery: Remove unused platform-data plumbing
2026-04-06 20:57 [PATCH v1 0/9] power: supply: max17042_battery: improve MAX17055 support Vincent Cloutier
` (4 preceding siblings ...)
2026-04-06 20:57 ` [PATCH v1 5/9] power: supply: max17042_battery: Keep only critical alerts during suspend Vincent Cloutier
@ 2026-04-06 20:57 ` Vincent Cloutier
2026-04-06 20:57 ` [PATCH v1 7/9] power: supply: max17042_battery: use ModelCfg refresh on max17055 Vincent Cloutier
` (3 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Vincent Cloutier @ 2026-04-06 20:57 UTC (permalink / raw)
To: Sebastian Reichel
Cc: Hans de Goede, Krzysztof Kozlowski, Marek Szyprowski,
Sebastian Krzyszkowiak, Purism Kernel Team, linux-pm,
linux-kernel, Vincent Cloutier
From: Vincent Cloutier <vincent@cloutier.co>
No in-tree user still provides `max17042_platform_data` or
`max17042_reg_data`. Move the simple runtime fields into
`struct max17042_chip`, populate them directly from DT or the default
hardware state, and drop the unused public platform-data interface.
While here, write the MAX17047/MAX17050 default `FullSOCThr` value
directly in probe instead of carrying it through an `init_data` table.
Signed-off-by: Vincent Cloutier <vincent@cloutier.co>
---
drivers/power/supply/max17042_battery.c | 204 +++++++++++-------------
include/linux/power/max17042_battery.h | 29 ----
2 files changed, 89 insertions(+), 144 deletions(-)
diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c
index b93fedf183b1..a96427bf07d0 100644
--- a/drivers/power/supply/max17042_battery.c
+++ b/drivers/power/supply/max17042_battery.c
@@ -68,10 +68,17 @@ struct max17042_chip {
struct regmap *regmap;
struct power_supply *battery;
enum max170xx_chip_type chip_type;
- struct max17042_platform_data *pdata;
+ struct max17042_config_data *config_data;
struct work_struct work;
int init_complete;
int irq;
+ bool enable_current_sense;
+ bool enable_por_init;
+ unsigned int r_sns;
+ int vmin; /* in millivolts */
+ int vmax; /* in millivolts */
+ int temp_min; /* in tenths of degree Celsius */
+ int temp_max; /* in tenths of degree Celsius */
};
static enum power_supply_property max17042_battery_props[] = {
@@ -163,7 +170,7 @@ static int max17042_get_status(struct max17042_chip *chip, int *status)
* Even though we are supplied, we may still be discharging if the
* supply is e.g. only delivering 5V 0.5A. Check current if available.
*/
- if (!chip->pdata->enable_current_sense) {
+ if (!chip->enable_current_sense) {
*status = POWER_SUPPLY_STATUS_CHARGING;
return 0;
}
@@ -205,12 +212,12 @@ static int max17042_get_battery_health(struct max17042_chip *chip, int *health)
/* Convert to millivolts */
vbatt /= 1000;
- if (vavg < chip->pdata->vmin) {
+ if (vavg < chip->vmin) {
*health = POWER_SUPPLY_HEALTH_DEAD;
goto out;
}
- if (vbatt > chip->pdata->vmax + MAX17042_VMAX_TOLERANCE) {
+ if (vbatt > chip->vmax + MAX17042_VMAX_TOLERANCE) {
*health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
goto out;
}
@@ -219,12 +226,12 @@ static int max17042_get_battery_health(struct max17042_chip *chip, int *health)
if (ret < 0)
goto health_error;
- if (temp < chip->pdata->temp_min) {
+ if (temp < chip->temp_min) {
*health = POWER_SUPPLY_HEALTH_COLD;
goto out;
}
- if (temp > chip->pdata->temp_max) {
+ if (temp > chip->temp_max) {
*health = POWER_SUPPLY_HEALTH_OVERHEAT;
goto out;
}
@@ -325,7 +332,7 @@ static int max17042_get_property(struct power_supply *psy,
val->intval = data * MAX17042_VOLTAGE_LSB;
break;
case POWER_SUPPLY_PROP_CAPACITY:
- if (chip->pdata->enable_current_sense)
+ if (chip->enable_current_sense)
ret = regmap_read(map, MAX17042_RepSOC, &data);
else
ret = regmap_read(map, MAX17042_VFSOC, &data);
@@ -340,7 +347,7 @@ static int max17042_get_property(struct power_supply *psy,
return ret;
data64 = data * MAX17042_CAPACITY_LSB;
- do_div(data64, chip->pdata->r_sns);
+ do_div(data64, chip->r_sns);
val->intval = data64;
break;
case POWER_SUPPLY_PROP_CHARGE_FULL:
@@ -349,7 +356,7 @@ static int max17042_get_property(struct power_supply *psy,
return ret;
data64 = data * MAX17042_CAPACITY_LSB;
- do_div(data64, chip->pdata->r_sns);
+ do_div(data64, chip->r_sns);
val->intval = data64;
break;
case POWER_SUPPLY_PROP_CHARGE_NOW:
@@ -358,7 +365,7 @@ static int max17042_get_property(struct power_supply *psy,
return ret;
data64 = data * MAX17042_CAPACITY_LSB;
- do_div(data64, chip->pdata->r_sns);
+ do_div(data64, chip->r_sns);
val->intval = data64;
break;
case POWER_SUPPLY_PROP_CHARGE_COUNTER:
@@ -367,7 +374,7 @@ static int max17042_get_property(struct power_supply *psy,
return ret;
data64 = sign_extend64(data, 15) * MAX17042_CAPACITY_LSB;
- val->intval = div_s64(data64, chip->pdata->r_sns);
+ val->intval = div_s64(data64, chip->r_sns);
break;
case POWER_SUPPLY_PROP_TEMP:
ret = max17042_get_temperature(chip, &val->intval);
@@ -389,10 +396,10 @@ static int max17042_get_property(struct power_supply *psy,
val->intval = sign_extend32(data >> 8, 7) * 10;
break;
case POWER_SUPPLY_PROP_TEMP_MIN:
- val->intval = chip->pdata->temp_min;
+ val->intval = chip->temp_min;
break;
case POWER_SUPPLY_PROP_TEMP_MAX:
- val->intval = chip->pdata->temp_max;
+ val->intval = chip->temp_max;
break;
case POWER_SUPPLY_PROP_HEALTH:
ret = max17042_get_battery_health(chip, &val->intval);
@@ -403,25 +410,25 @@ static int max17042_get_property(struct power_supply *psy,
val->intval = POWER_SUPPLY_SCOPE_SYSTEM;
break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
- if (chip->pdata->enable_current_sense) {
+ if (chip->enable_current_sense) {
ret = regmap_read(map, MAX17042_Current, &data);
if (ret < 0)
return ret;
data64 = sign_extend64(data, 15) * MAX17042_CURRENT_LSB;
- val->intval = div_s64(data64, chip->pdata->r_sns);
+ val->intval = div_s64(data64, chip->r_sns);
} else {
return -EINVAL;
}
break;
case POWER_SUPPLY_PROP_CURRENT_AVG:
- if (chip->pdata->enable_current_sense) {
+ if (chip->enable_current_sense) {
ret = regmap_read(map, MAX17042_AvgCurrent, &data);
if (ret < 0)
return ret;
data64 = sign_extend64(data, 15) * MAX17042_CURRENT_LSB;
- val->intval = div_s64(data64, chip->pdata->r_sns);
+ val->intval = div_s64(data64, chip->r_sns);
} else {
return -EINVAL;
}
@@ -432,7 +439,7 @@ static int max17042_get_property(struct power_supply *psy,
return ret;
data64 = data * MAX17042_CURRENT_LSB;
- val->intval = div_s64(data64, chip->pdata->r_sns);
+ val->intval = div_s64(data64, chip->r_sns);
break;
case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
ret = regmap_read(map, MAX17042_TTE, &data);
@@ -562,7 +569,7 @@ static inline void max17042_write_model_data(struct max17042_chip *chip,
for (i = 0; i < size; i++)
regmap_write(map, addr + i,
- chip->pdata->config_data->cell_char_tbl[i]);
+ chip->config_data->cell_char_tbl[i]);
}
static inline void max17042_read_model_data(struct max17042_chip *chip,
@@ -597,7 +604,7 @@ static inline int max17042_model_data_compare(struct max17042_chip *chip,
static int max17042_init_model(struct max17042_chip *chip)
{
int ret;
- int table_size = ARRAY_SIZE(chip->pdata->config_data->cell_char_tbl);
+ int table_size = ARRAY_SIZE(chip->config_data->cell_char_tbl);
u16 *temp_data;
temp_data = kcalloc(table_size, sizeof(*temp_data), GFP_KERNEL);
@@ -612,7 +619,7 @@ static int max17042_init_model(struct max17042_chip *chip)
ret = max17042_model_data_compare(
chip,
- chip->pdata->config_data->cell_char_tbl,
+ chip->config_data->cell_char_tbl,
temp_data,
table_size);
@@ -625,7 +632,7 @@ static int max17042_init_model(struct max17042_chip *chip)
static int max17042_verify_model_lock(struct max17042_chip *chip)
{
int i;
- int table_size = ARRAY_SIZE(chip->pdata->config_data->cell_char_tbl);
+ int table_size = ARRAY_SIZE(chip->config_data->cell_char_tbl);
u16 *temp_data;
int ret = 0;
@@ -645,7 +652,7 @@ static int max17042_verify_model_lock(struct max17042_chip *chip)
static void max17042_write_config_regs(struct max17042_chip *chip)
{
- struct max17042_config_data *config = chip->pdata->config_data;
+ struct max17042_config_data *config = chip->config_data;
struct regmap *map = chip->regmap;
regmap_write(map, MAX17042_CONFIG, config->config);
@@ -662,7 +669,7 @@ static void max17042_write_config_regs(struct max17042_chip *chip)
static void max17042_write_custom_regs(struct max17042_chip *chip)
{
- struct max17042_config_data *config = chip->pdata->config_data;
+ struct max17042_config_data *config = chip->config_data;
struct regmap *map = chip->regmap;
max17042_write_verify_reg(map, MAX17042_RCOMP0, config->rcomp0);
@@ -686,7 +693,7 @@ static void max17042_write_custom_regs(struct max17042_chip *chip)
static void max17042_update_capacity_regs(struct max17042_chip *chip)
{
- struct max17042_config_data *config = chip->pdata->config_data;
+ struct max17042_config_data *config = chip->config_data;
struct regmap *map = chip->regmap;
max17042_write_verify_reg(map, MAX17042_FullCAP,
@@ -712,7 +719,7 @@ static void max17042_load_new_capacity_params(struct max17042_chip *chip)
u32 full_cap0, rep_cap, dq_acc, vfSoc;
u32 rem_cap;
- struct max17042_config_data *config = chip->pdata->config_data;
+ struct max17042_config_data *config = chip->config_data;
struct regmap *map = chip->regmap;
regmap_read(map, MAX17042_FullCAP0, &full_cap0);
@@ -744,14 +751,14 @@ static void max17042_load_new_capacity_params(struct max17042_chip *chip)
}
/*
- * Block write all the override values coming from platform data.
+ * Block write all the override values coming from config_data.
* This function MUST be called before the POR initialization procedure
* specified by maxim.
*/
static inline void max17042_override_por_values(struct max17042_chip *chip)
{
struct regmap *map = chip->regmap;
- struct max17042_config_data *config = chip->pdata->config_data;
+ struct max17042_config_data *config = chip->config_data;
max17042_override_por(map, MAX17042_TGAIN, config->tgain);
max17042_override_por(map, MAX17042_TOFF, config->toff);
@@ -864,7 +871,7 @@ static void max17042_set_soc_threshold(struct max17042_chip *chip, u16 off)
/* program interrupt thresholds such that we should
* get interrupt for every 'off' perc change in the soc
*/
- if (chip->pdata->enable_current_sense)
+ if (chip->enable_current_sense)
regmap_read(map, MAX17042_RepSOC, &soc);
else
regmap_read(map, MAX17042_VFSOC, &soc);
@@ -880,7 +887,7 @@ static void max17042_set_critical_soc_threshold(struct max17042_chip *chip)
struct regmap *map = chip->regmap;
u32 soc;
- if (chip->pdata->enable_current_sense)
+ if (chip->enable_current_sense)
regmap_read(map, MAX17042_RepSOC, &soc);
else
regmap_read(map, MAX17042_VFSOC, &soc);
@@ -943,8 +950,8 @@ static void max17042_init_worker(struct work_struct *work)
struct max17042_chip, work);
int ret;
- /* Initialize registers according to values from the platform data */
- if (chip->pdata->enable_por_init && chip->pdata->config_data) {
+ /* Initialize registers according to values from config_data */
+ if (chip->enable_por_init && chip->config_data) {
ret = max17042_init_chip(chip);
if (ret)
return;
@@ -954,102 +961,56 @@ static void max17042_init_worker(struct work_struct *work)
}
#ifdef CONFIG_OF
-static struct max17042_platform_data *
-max17042_get_of_pdata(struct max17042_chip *chip)
+static int max17042_parse_dt(struct max17042_chip *chip)
{
- struct device *dev = chip->dev;
- struct device_node *np = dev->of_node;
+ struct device_node *np = chip->dev->of_node;
u32 prop;
- struct max17042_platform_data *pdata;
-
- pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
- if (!pdata)
- return NULL;
/*
* Require current sense resistor value to be specified for
* current-sense functionality to be enabled at all.
*/
if (of_property_read_u32(np, "maxim,rsns-microohm", &prop) == 0) {
- pdata->r_sns = prop;
- pdata->enable_current_sense = true;
+ chip->r_sns = prop;
+ chip->enable_current_sense = true;
}
- if (of_property_read_s32(np, "maxim,cold-temp", &pdata->temp_min))
- pdata->temp_min = INT_MIN;
- if (of_property_read_s32(np, "maxim,over-heat-temp", &pdata->temp_max))
- pdata->temp_max = INT_MAX;
- if (of_property_read_s32(np, "maxim,dead-volt", &pdata->vmin))
- pdata->vmin = INT_MIN;
- if (of_property_read_s32(np, "maxim,over-volt", &pdata->vmax))
- pdata->vmax = INT_MAX;
+ if (of_property_read_s32(np, "maxim,cold-temp", &chip->temp_min))
+ chip->temp_min = INT_MIN;
+ if (of_property_read_s32(np, "maxim,over-heat-temp", &chip->temp_max))
+ chip->temp_max = INT_MAX;
+ if (of_property_read_s32(np, "maxim,dead-volt", &chip->vmin))
+ chip->vmin = INT_MIN;
+ if (of_property_read_s32(np, "maxim,over-volt", &chip->vmax))
+ chip->vmax = INT_MAX;
- return pdata;
+ return 0;
}
#endif
-static struct max17042_reg_data max17047_default_pdata_init_regs[] = {
- /*
- * Some firmwares do not set FullSOCThr, Enable End-of-Charge Detection
- * when the voltage FG reports 95%, as recommended in the datasheet.
- */
- { MAX17047_FullSOCThr, MAX17042_BATTERY_FULL << 8 },
-};
-
-static struct max17042_platform_data *
-max17042_get_default_pdata(struct max17042_chip *chip)
+static int max17042_init_defaults(struct max17042_chip *chip)
{
- struct device *dev = chip->dev;
- struct max17042_platform_data *pdata;
int ret, misc_cfg;
/*
- * The MAX17047 gets used on x86 where we might not have pdata, assume
+ * The MAX17047 gets used on x86 where we might not have DT, assume
* the firmware will already have initialized the fuel-gauge and provide
* default values for the non init bits to make things work.
*/
- pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
- if (!pdata)
- return pdata;
-
- if ((chip->chip_type == MAXIM_DEVICE_TYPE_MAX17047) ||
- (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17050)) {
- pdata->init_data = max17047_default_pdata_init_regs;
- pdata->num_init_data =
- ARRAY_SIZE(max17047_default_pdata_init_regs);
- }
ret = regmap_read(chip->regmap, MAX17042_MiscCFG, &misc_cfg);
if (ret < 0)
- return NULL;
+ return ret;
/* If bits 0-1 are set to 3 then only Voltage readings are used */
- if ((misc_cfg & 0x3) == 0x3)
- pdata->enable_current_sense = false;
- else
- pdata->enable_current_sense = true;
+ chip->enable_current_sense = (misc_cfg & 0x3) != 0x3;
- pdata->vmin = MAX17042_DEFAULT_VMIN;
- pdata->vmax = MAX17042_DEFAULT_VMAX;
- pdata->temp_min = MAX17042_DEFAULT_TEMP_MIN;
- pdata->temp_max = MAX17042_DEFAULT_TEMP_MAX;
+ chip->vmin = MAX17042_DEFAULT_VMIN;
+ chip->vmax = MAX17042_DEFAULT_VMAX;
+ chip->temp_min = MAX17042_DEFAULT_TEMP_MIN;
+ chip->temp_max = MAX17042_DEFAULT_TEMP_MAX;
- return pdata;
-}
-
-static struct max17042_platform_data *
-max17042_get_pdata(struct max17042_chip *chip)
-{
- struct device *dev = chip->dev;
-
-#ifdef CONFIG_OF
- if (dev->of_node)
- return max17042_get_of_pdata(chip);
-#endif
- if (dev->platform_data)
- return dev->platform_data;
-
- return max17042_get_default_pdata(chip);
+ return 0;
}
static const struct regmap_config max17042_regmap_config = {
@@ -1088,8 +1049,8 @@ static int max17042_probe(struct i2c_client *client, struct device *dev, int irq
struct power_supply_config psy_cfg = {};
struct max17042_chip *chip;
int ret;
- int i;
u32 val;
+ bool use_default_config = false;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA))
return -EIO;
@@ -1106,10 +1067,19 @@ static int max17042_probe(struct i2c_client *client, struct device *dev, int irq
return -EINVAL;
}
- chip->pdata = max17042_get_pdata(chip);
- if (!chip->pdata) {
- dev_err(dev, "no platform data provided\n");
- return -EINVAL;
+
+#ifdef CONFIG_OF
+ if (dev->of_node) {
+ ret = max17042_parse_dt(chip);
+ if (ret)
+ return ret;
+ } else
+#endif
+ {
+ ret = max17042_init_defaults(chip);
+ if (ret)
+ return ret;
+ use_default_config = true;
}
dev_set_drvdata(dev, chip);
@@ -1118,19 +1088,13 @@ static int max17042_probe(struct i2c_client *client, struct device *dev, int irq
/* When current is not measured,
* CURRENT_NOW and CURRENT_AVG properties should be invisible. */
- if (!chip->pdata->enable_current_sense)
+ if (!chip->enable_current_sense)
max17042_desc = &max17042_no_current_sense_psy_desc;
- if (chip->pdata->r_sns == 0)
- chip->pdata->r_sns = MAX17042_DEFAULT_SNS_RESISTOR;
+ if (chip->r_sns == 0)
+ chip->r_sns = MAX17042_DEFAULT_SNS_RESISTOR;
- if (chip->pdata->init_data)
- for (i = 0; i < chip->pdata->num_init_data; i++)
- regmap_write(chip->regmap,
- chip->pdata->init_data[i].addr,
- chip->pdata->init_data[i].data);
-
- if (!chip->pdata->enable_current_sense) {
+ if (!chip->enable_current_sense) {
regmap_write(chip->regmap, MAX17042_CGAIN, 0x0000);
regmap_write(chip->regmap, MAX17042_MiscCFG, 0x0003);
regmap_write(chip->regmap, MAX17042_LearnCFG, 0x0007);
@@ -1142,6 +1106,16 @@ static int max17042_probe(struct i2c_client *client, struct device *dev, int irq
return dev_err_probe(dev, PTR_ERR(chip->battery),
"failed: power supply register\n");
+ /*
+ * Some firmwares do not set FullSOCThr, Enable End-of-Charge Detection
+ * when the voltage FG reports 95%, as recommended in the datasheet.
+ */
+ if (use_default_config &&
+ (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17047 ||
+ chip->chip_type == MAXIM_DEVICE_TYPE_MAX17050))
+ regmap_write(chip->regmap, MAX17047_FullSOCThr,
+ MAX17042_BATTERY_FULL << 8);
+
if (irq) {
unsigned int flags = IRQF_ONESHOT | IRQF_SHARED | IRQF_PROBE_SHARED;
diff --git a/include/linux/power/max17042_battery.h b/include/linux/power/max17042_battery.h
index c417abd2ab70..36817da50548 100644
--- a/include/linux/power/max17042_battery.h
+++ b/include/linux/power/max17042_battery.h
@@ -177,16 +177,6 @@ enum max170xx_chip_type {
MAXIM_DEVICE_TYPE_NUM
};
-/*
- * used for setting a register to a desired value
- * addr : address for a register
- * data : setting value for the register
- */
-struct max17042_reg_data {
- u8 addr;
- u16 data;
-};
-
struct max17042_config_data {
/* External current sense resistor value in milli-ohms */
u32 cur_sense_val;
@@ -244,23 +234,4 @@ struct max17042_config_data {
u16 cell_char_tbl[MAX17042_CHARACTERIZATION_DATA_SIZE];
} __packed;
-struct max17042_platform_data {
- struct max17042_reg_data *init_data;
- struct max17042_config_data *config_data;
- int num_init_data; /* Number of enties in init_data array */
- bool enable_current_sense;
- bool enable_por_init; /* Use POR init from Maxim appnote */
-
- /*
- * R_sns in micro-ohms.
- * default 10000 (if r_sns = 0) as it is the recommended value by
- * the datasheet although it can be changed by board designers.
- */
- unsigned int r_sns;
- int vmin; /* in millivolts */
- int vmax; /* in millivolts */
- int temp_min; /* in tenths of degree Celsius */
- int temp_max; /* in tenths of degree Celsius */
-};
-
#endif /* __MAX17042_BATTERY_H_ */
--
2.53.0
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH v1 7/9] power: supply: max17042_battery: use ModelCfg refresh on max17055
2026-04-06 20:57 [PATCH v1 0/9] power: supply: max17042_battery: improve MAX17055 support Vincent Cloutier
` (5 preceding siblings ...)
2026-04-06 20:57 ` [PATCH v1 6/9] power: supply: max17042_battery: Remove unused platform-data plumbing Vincent Cloutier
@ 2026-04-06 20:57 ` Vincent Cloutier
2026-04-06 20:57 ` [PATCH v1 8/9] power: supply: Read MAX17042 battery info before registration Vincent Cloutier
` (2 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Vincent Cloutier @ 2026-04-06 20:57 UTC (permalink / raw)
To: Sebastian Reichel
Cc: Hans de Goede, Krzysztof Kozlowski, Marek Szyprowski,
Sebastian Krzyszkowiak, Purism Kernel Team, linux-pm,
linux-kernel, Vincent Cloutier
From: Sebastian Krzyszkowiak <sebastian.krzyszkowiak@puri.sm>
Unlike other models, max17055 doesn't require cell characterization data
and operates on a smaller set of input variables (`DesignCap`, `VEmpty`,
`IChgTerm`, and `ModelCfg`). Those values can be filled in through
`max17042_override_por_values()`, but the refresh bit has to be set
afterward in order to make them apply.
Signed-off-by: Sebastian Krzyszkowiak <sebastian.krzyszkowiak@puri.sm>
Signed-off-by: Vincent Cloutier <vincent@cloutier.co>
---
drivers/power/supply/max17042_battery.c | 66 +++++++++++++++----------
include/linux/power/max17042_battery.h | 3 ++
2 files changed, 42 insertions(+), 27 deletions(-)
diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c
index a96427bf07d0..793164bd55d9 100644
--- a/drivers/power/supply/max17042_battery.c
+++ b/drivers/power/supply/max17042_battery.c
@@ -811,6 +811,9 @@ static inline void max17042_override_por_values(struct max17042_chip *chip)
(chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055)) {
max17042_override_por(map, MAX17047_V_empty, config->vempty);
}
+
+ if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055)
+ max17042_override_por(map, MAX17055_ModelCfg, config->model_cfg);
}
static int max17042_init_chip(struct max17042_chip *chip)
@@ -819,44 +822,53 @@ static int max17042_init_chip(struct max17042_chip *chip)
int ret;
max17042_override_por_values(chip);
+
+ if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055) {
+ regmap_write_bits(map, MAX17055_ModelCfg,
+ MAX17055_MODELCFG_REFRESH_BIT,
+ MAX17055_MODELCFG_REFRESH_BIT);
+ }
+
/* After Power up, the MAX17042 requires 500mS in order
* to perform signal debouncing and initial SOC reporting
*/
msleep(500);
- /* Initialize configuration */
- max17042_write_config_regs(chip);
+ if (chip->chip_type != MAXIM_DEVICE_TYPE_MAX17055) {
+ /* Initialize configuration */
+ max17042_write_config_regs(chip);
- /* write cell characterization data */
- ret = max17042_init_model(chip);
- if (ret) {
- dev_err(chip->dev, "%s init failed\n",
- __func__);
- return -EIO;
- }
+ /* write cell characterization data */
+ ret = max17042_init_model(chip);
+ if (ret) {
+ dev_err(chip->dev, "%s init failed\n",
+ __func__);
+ return -EIO;
+ }
- ret = max17042_verify_model_lock(chip);
- if (ret) {
- dev_err(chip->dev, "%s lock verify failed\n",
- __func__);
- return -EIO;
- }
- /* write custom parameters */
- max17042_write_custom_regs(chip);
+ ret = max17042_verify_model_lock(chip);
+ if (ret) {
+ dev_err(chip->dev, "%s lock verify failed\n",
+ __func__);
+ return -EIO;
+ }
+ /* write custom parameters */
+ max17042_write_custom_regs(chip);
- /* update capacity params */
- max17042_update_capacity_regs(chip);
+ /* update capacity params */
+ max17042_update_capacity_regs(chip);
- /* delay must be atleast 350mS to allow VFSOC
- * to be calculated from the new configuration
- */
- msleep(350);
+ /* delay must be atleast 350mS to allow VFSOC
+ * to be calculated from the new configuration
+ */
+ msleep(350);
- /* reset vfsoc0 reg */
- max17042_reset_vfsoc0_reg(chip);
+ /* reset vfsoc0 reg */
+ max17042_reset_vfsoc0_reg(chip);
- /* load new capacity params */
- max17042_load_new_capacity_params(chip);
+ /* load new capacity params */
+ max17042_load_new_capacity_params(chip);
+ }
/* Init complete, Clear the POR bit */
regmap_update_bits(map, MAX17042_STATUS, STATUS_POR_BIT, 0x0);
diff --git a/include/linux/power/max17042_battery.h b/include/linux/power/max17042_battery.h
index 36817da50548..6b6e01d3e499 100644
--- a/include/linux/power/max17042_battery.h
+++ b/include/linux/power/max17042_battery.h
@@ -23,6 +23,8 @@
#define MAX17042_CHARACTERIZATION_DATA_SIZE 48
+#define MAX17055_MODELCFG_REFRESH_BIT BIT(15)
+
enum max17042_register {
MAX17042_STATUS = 0x00,
MAX17042_VALRT_Th = 0x01,
@@ -198,6 +200,7 @@ struct max17042_config_data {
u16 full_soc_thresh; /* 0x13 */
u16 design_cap; /* 0x18 */
u16 ichgt_term; /* 0x1E */
+ u16 model_cfg; /* 0xDB */
/* MG3 config */
u16 at_rate; /* 0x04 */
--
2.53.0
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH v1 8/9] power: supply: Read MAX17042 battery info before registration
2026-04-06 20:57 [PATCH v1 0/9] power: supply: max17042_battery: improve MAX17055 support Vincent Cloutier
` (6 preceding siblings ...)
2026-04-06 20:57 ` [PATCH v1 7/9] power: supply: max17042_battery: use ModelCfg refresh on max17055 Vincent Cloutier
@ 2026-04-06 20:57 ` Vincent Cloutier
2026-06-04 16:37 ` Sebastian Reichel
2026-04-06 20:57 ` [PATCH v1 9/9] power: supply: max17042_battery: Treat MAX17055 VChg as explicit DT setting Vincent Cloutier
2026-06-04 16:39 ` [PATCH v1 0/9] power: supply: max17042_battery: improve MAX17055 support Sebastian Reichel
9 siblings, 1 reply; 12+ messages in thread
From: Vincent Cloutier @ 2026-04-06 20:57 UTC (permalink / raw)
To: Sebastian Reichel
Cc: Hans de Goede, Krzysztof Kozlowski, Marek Szyprowski,
Sebastian Krzyszkowiak, Purism Kernel Team, linux-pm,
linux-kernel, Vincent Cloutier
From: Vincent Cloutier <vincent@cloutier.co>
Rework the MAX17042 `monitored-battery` path so the driver can consume DT
battery information before registering its power-supply device.
To do that, factor the parser in `power_supply_core` behind a new
`devm_power_supply_get_battery_info()` helper and let battery drivers
pass the pre-parsed `battery_info` through `struct power_supply_config`.
MAX17042 can then apply the design-capacity and termination-current data
before registration without exposing the battery too early or reparsing
the same node later in the class core.
Signed-off-by: Vincent Cloutier <vincent@cloutier.co>
---
drivers/power/supply/max17042_battery.c | 62 ++++++++
drivers/power/supply/power_supply_core.c | 188 ++++++++++++++---------
include/linux/power/max17042_battery.h | 1 +
include/linux/power_supply.h | 10 ++
4 files changed, 187 insertions(+), 74 deletions(-)
diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c
index 793164bd55d9..5bf15c19fc5d 100644
--- a/drivers/power/supply/max17042_battery.c
+++ b/drivers/power/supply/max17042_battery.c
@@ -1025,6 +1025,57 @@ static int max17042_init_defaults(struct max17042_chip *chip)
return 0;
}
+static int max17042_apply_battery_properties(struct max17042_chip *chip,
+ struct power_supply_battery_info *info)
+{
+ struct device *dev = chip->dev;
+ bool needs_config;
+ u64 data64;
+
+ if (!info)
+ return 0;
+
+ needs_config = (chip->enable_current_sense &&
+ (info->charge_full_design_uah != -EINVAL ||
+ info->charge_term_current_ua != -EINVAL)) ||
+ (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055 &&
+ info->voltage_max_design_uv > 4250000);
+ if (!needs_config)
+ return 0;
+
+ if (!chip->config_data) {
+ chip->config_data = devm_kzalloc(dev,
+ sizeof(*chip->config_data),
+ GFP_KERNEL);
+ if (!chip->config_data)
+ return -ENOMEM;
+ }
+
+ if (chip->enable_current_sense &&
+ info->charge_full_design_uah != -EINVAL) {
+ data64 = (u64)info->charge_full_design_uah * chip->r_sns;
+ do_div(data64, MAX17042_CAPACITY_LSB);
+ chip->config_data->design_cap = (u16)data64;
+ chip->enable_por_init = true;
+ }
+
+ if (chip->enable_current_sense &&
+ info->charge_term_current_ua != -EINVAL) {
+ data64 = (u64)info->charge_term_current_ua * chip->r_sns;
+ do_div(data64, MAX17042_CURRENT_LSB);
+ chip->config_data->ichgt_term = (u16)data64;
+ chip->enable_por_init = true;
+ }
+
+ if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055 &&
+ info->voltage_max_design_uv > 4250000) {
+ chip->config_data->model_cfg = MAX17055_MODELCFG_VCHG_BIT;
+ chip->enable_por_init = true;
+ }
+
+ return 0;
+}
+
static const struct regmap_config max17042_regmap_config = {
.name = "max17042",
.reg_bits = 8,
@@ -1060,6 +1111,7 @@ static int max17042_probe(struct i2c_client *client, struct device *dev, int irq
const struct power_supply_desc *max17042_desc = &max17042_psy_desc;
struct power_supply_config psy_cfg = {};
struct max17042_chip *chip;
+ struct power_supply_battery_info *battery_info = NULL;
int ret;
u32 val;
bool use_default_config = false;
@@ -1106,12 +1158,22 @@ static int max17042_probe(struct i2c_client *client, struct device *dev, int irq
if (chip->r_sns == 0)
chip->r_sns = MAX17042_DEFAULT_SNS_RESISTOR;
+ ret = devm_power_supply_get_battery_info(dev, &battery_info);
+ if (ret && ret != -ENODEV && ret != -ENOENT)
+ return ret;
+
+ ret = max17042_apply_battery_properties(chip, battery_info);
+ if (ret)
+ return ret;
+
if (!chip->enable_current_sense) {
regmap_write(chip->regmap, MAX17042_CGAIN, 0x0000);
regmap_write(chip->regmap, MAX17042_MiscCFG, 0x0003);
regmap_write(chip->regmap, MAX17042_LearnCFG, 0x0007);
}
+ psy_cfg.battery_info = battery_info;
+
chip->battery = devm_power_supply_register(dev, max17042_desc,
&psy_cfg);
if (IS_ERR(chip->battery))
diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c
index a446d3d086fc..7ca3ed34095e 100644
--- a/drivers/power/supply/power_supply_core.c
+++ b/drivers/power/supply/power_supply_core.c
@@ -577,20 +577,76 @@ 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 void __power_supply_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(dev, info->ocv_table[i]);
+ }
+
+ if (info->resist_table)
+ devm_kfree(dev, info->resist_table);
+
+ devm_kfree(dev, info);
+}
+
+static void power_supply_init_battery_info(struct power_supply_battery_info *info)
+{
+ int index;
+
+ info->technology = POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
+ info->energy_full_design_uwh = -EINVAL;
+ info->charge_full_design_uah = -EINVAL;
+ info->voltage_min_design_uv = -EINVAL;
+ info->voltage_max_design_uv = -EINVAL;
+ info->precharge_current_ua = -EINVAL;
+ info->charge_term_current_ua = -EINVAL;
+ info->constant_charge_current_max_ua = -EINVAL;
+ info->constant_charge_voltage_max_uv = -EINVAL;
+ info->tricklecharge_current_ua = -EINVAL;
+ info->precharge_voltage_max_uv = -EINVAL;
+ info->charge_restart_voltage_uv = -EINVAL;
+ info->overvoltage_limit_uv = -EINVAL;
+ info->maintenance_charge = NULL;
+ info->alert_low_temp_charge_current_ua = -EINVAL;
+ info->alert_low_temp_charge_voltage_uv = -EINVAL;
+ info->alert_high_temp_charge_current_ua = -EINVAL;
+ info->alert_high_temp_charge_voltage_uv = -EINVAL;
+ info->temp_ambient_alert_min = INT_MIN;
+ info->temp_ambient_alert_max = INT_MAX;
+ info->temp_alert_min = INT_MIN;
+ info->temp_alert_max = INT_MAX;
+ info->temp_min = INT_MIN;
+ info->temp_max = INT_MAX;
+ info->factory_internal_resistance_uohm = -EINVAL;
+ info->resist_table = NULL;
+ info->bti_resistance_ohm = -EINVAL;
+ info->bti_resistance_tolerance = -EINVAL;
+
+ for (index = 0; index < POWER_SUPPLY_OCV_TEMP_MAX; index++) {
+ info->ocv_table[index] = NULL;
+ info->ocv_temp[index] = -EINVAL;
+ info->ocv_table_size[index] = -EINVAL;
+ }
+}
+
+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);
+ if (!srcnode)
+ return -ENODEV;
fwnode = fwnode_find_reference(srcnode, "monitored-battery", 0);
if (IS_ERR(fwnode))
@@ -602,7 +658,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)
@@ -617,46 +673,13 @@ 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;
}
- info->technology = POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
- info->energy_full_design_uwh = -EINVAL;
- info->charge_full_design_uah = -EINVAL;
- info->voltage_min_design_uv = -EINVAL;
- info->voltage_max_design_uv = -EINVAL;
- info->precharge_current_ua = -EINVAL;
- info->charge_term_current_ua = -EINVAL;
- info->constant_charge_current_max_ua = -EINVAL;
- info->constant_charge_voltage_max_uv = -EINVAL;
- info->tricklecharge_current_ua = -EINVAL;
- info->precharge_voltage_max_uv = -EINVAL;
- info->charge_restart_voltage_uv = -EINVAL;
- info->overvoltage_limit_uv = -EINVAL;
- info->maintenance_charge = NULL;
- info->alert_low_temp_charge_current_ua = -EINVAL;
- info->alert_low_temp_charge_voltage_uv = -EINVAL;
- info->alert_high_temp_charge_current_ua = -EINVAL;
- info->alert_high_temp_charge_voltage_uv = -EINVAL;
- info->temp_ambient_alert_min = INT_MIN;
- info->temp_ambient_alert_max = INT_MAX;
- info->temp_alert_min = INT_MIN;
- info->temp_alert_max = INT_MAX;
- info->temp_min = INT_MIN;
- info->temp_max = INT_MAX;
- info->factory_internal_resistance_uohm = -EINVAL;
- info->resist_table = NULL;
- info->bti_resistance_ohm = -EINVAL;
- info->bti_resistance_tolerance = -EINVAL;
-
- for (index = 0; index < POWER_SUPPLY_OCV_TEMP_MAX; index++) {
- info->ocv_table[index] = NULL;
- info->ocv_temp[index] = -EINVAL;
- info->ocv_table_size[index] = -EINVAL;
- }
+ power_supply_init_battery_info(info);
/* The property and field names below must correspond to elements
* in enum power_supply_property. For reasoning, see
@@ -678,7 +701,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",
@@ -729,7 +752,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) {
@@ -744,28 +767,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(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(dev, info);
err = -EINVAL;
goto out_put_node;
}
u32 *propdata __free(kfree) = kcalloc(proplen, sizeof(*propdata), GFP_KERNEL);
if (!propdata) {
- power_supply_put_battery_info(psy, info);
+ __power_supply_put_battery_info(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(dev, info);
goto out_put_node;
}
@@ -773,9 +796,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(dev, info);
err = -ENOMEM;
goto out_put_node;
}
@@ -791,14 +814,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(dev, info);
err = (proplen < 0) ? proplen : -EINVAL;
goto out_put_node;
}
propdata = kcalloc(proplen, sizeof(*propdata), GFP_KERNEL);
if (!propdata) {
- power_supply_put_battery_info(psy, info);
+ __power_supply_put_battery_info(dev, info);
err = -ENOMEM;
goto out_put_node;
}
@@ -806,17 +829,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(dev, info);
goto out_put_node;
}
info->resist_table_size = proplen / 2;
- info->resist_table = resist_table = devm_kcalloc(&psy->dev,
- info->resist_table_size,
- sizeof(*resist_table),
- GFP_KERNEL);
+ 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(dev, info);
err = -ENOMEM;
goto out_put_node;
}
@@ -834,22 +857,35 @@ int power_supply_get_battery_info(struct power_supply *psy,
fwnode_handle_put(fwnode);
return err;
}
-EXPORT_SYMBOL_GPL(power_supply_get_battery_info);
-void power_supply_put_battery_info(struct power_supply *psy,
- struct power_supply_battery_info *info)
+int devm_power_supply_get_battery_info(struct device *dev,
+ struct power_supply_battery_info **info_out)
{
- int i;
+ struct fwnode_handle *srcnode = dev_fwnode(dev);
- for (i = 0; i < POWER_SUPPLY_OCV_TEMP_MAX; i++) {
- if (info->ocv_table[i])
- devm_kfree(&psy->dev, info->ocv_table[i]);
- }
+ if (!srcnode && dev->parent)
+ srcnode = dev_fwnode(dev->parent);
- if (info->resist_table)
- devm_kfree(&psy->dev, info->resist_table);
+ return __power_supply_get_battery_info(dev, srcnode, info_out);
+}
+EXPORT_SYMBOL_GPL(devm_power_supply_get_battery_info);
+
+int power_supply_get_battery_info(struct power_supply *psy,
+ struct power_supply_battery_info **info_out)
+{
+ struct fwnode_handle *srcnode = dev_fwnode(&psy->dev);
+
+ if (!srcnode && psy->dev.parent)
+ srcnode = dev_fwnode(psy->dev.parent);
- devm_kfree(&psy->dev, info);
+ 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_put_battery_info(&psy->dev, info);
}
EXPORT_SYMBOL_GPL(power_supply_put_battery_info);
@@ -1620,9 +1656,13 @@ __power_supply_register(struct device *parent,
* expose battery data to userspace for battery devices.
*/
if (desc->type == POWER_SUPPLY_TYPE_BATTERY) {
- rc = power_supply_get_battery_info(psy, &psy->battery_info);
- if (rc && rc != -ENODEV && rc != -ENOENT)
- goto check_supplies_failed;
+ if (cfg && cfg->battery_info) {
+ psy->battery_info = cfg->battery_info;
+ } else {
+ rc = power_supply_get_battery_info(psy, &psy->battery_info);
+ if (rc && rc != -ENODEV && rc != -ENOENT)
+ goto check_supplies_failed;
+ }
}
spin_lock_init(&psy->changed_lock);
diff --git a/include/linux/power/max17042_battery.h b/include/linux/power/max17042_battery.h
index 6b6e01d3e499..e9f2118e57c8 100644
--- a/include/linux/power/max17042_battery.h
+++ b/include/linux/power/max17042_battery.h
@@ -24,6 +24,7 @@
#define MAX17042_CHARACTERIZATION_DATA_SIZE 48
#define MAX17055_MODELCFG_REFRESH_BIT BIT(15)
+#define MAX17055_MODELCFG_VCHG_BIT BIT(10)
enum max17042_register {
MAX17042_STATUS = 0x00,
diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
index 360ffdf272da..f9fca7bb6e24 100644
--- a/include/linux/power_supply.h
+++ b/include/linux/power_supply.h
@@ -231,6 +231,7 @@ union power_supply_propval {
struct device_node;
struct power_supply;
+struct power_supply_battery_info;
/* Run-time specific power supply configuration */
struct power_supply_config {
@@ -239,6 +240,12 @@ struct power_supply_config {
/* Driver private data */
void *drv_data;
+ /*
+ * Optional pre-parsed battery info for battery supplies.
+ * The pointed-to data must remain valid for the power supply lifetime.
+ */
+ struct power_supply_battery_info *battery_info;
+
/* Device specific sysfs attributes */
const struct attribute_group **attr_grp;
@@ -814,6 +821,9 @@ extern struct power_supply *power_supply_get_by_reference(struct fwnode_handle *
extern struct power_supply *devm_power_supply_get_by_reference(
struct device *dev, const char *property);
+extern int devm_power_supply_get_battery_info(struct device *dev,
+ struct power_supply_battery_info **info_out);
+
extern const enum power_supply_property power_supply_battery_info_properties[];
extern const size_t power_supply_battery_info_properties_size;
extern int power_supply_get_battery_info(struct power_supply *psy,
--
2.53.0
^ permalink raw reply related [flat|nested] 12+ messages in thread* Re: [PATCH v1 8/9] power: supply: Read MAX17042 battery info before registration
2026-04-06 20:57 ` [PATCH v1 8/9] power: supply: Read MAX17042 battery info before registration Vincent Cloutier
@ 2026-06-04 16:37 ` Sebastian Reichel
0 siblings, 0 replies; 12+ messages in thread
From: Sebastian Reichel @ 2026-06-04 16:37 UTC (permalink / raw)
To: Vincent Cloutier
Cc: Hans de Goede, Krzysztof Kozlowski, Marek Szyprowski,
Sebastian Krzyszkowiak, Purism Kernel Team, linux-pm,
linux-kernel, Vincent Cloutier
[-- Attachment #1: Type: text/plain, Size: 19066 bytes --]
Hi,
On Mon, Apr 06, 2026 at 04:57:56PM -0400, Vincent Cloutier wrote:
> From: Vincent Cloutier <vincent@cloutier.co>
>
> Rework the MAX17042 `monitored-battery` path so the driver can consume DT
> battery information before registering its power-supply device.
>
> To do that, factor the parser in `power_supply_core` behind a new
> `devm_power_supply_get_battery_info()` helper and let battery drivers
> pass the pre-parsed `battery_info` through `struct power_supply_config`.
> MAX17042 can then apply the design-capacity and termination-current data
> before registration without exposing the battery too early or reparsing
> the same node later in the class core.
>
> Signed-off-by: Vincent Cloutier <vincent@cloutier.co>
> ---
This is multiple patches. One to change the power-supply core and
one to change the driver. Apart from that I would prefer a slightly
different solution:
Create a new init callback, which is called from the power-supply
registration method (__power_supply_register) before device_add() is
being called. At this point it's okay to call
power_supply_get_battery_info() and for POWER_SUPPLY_TYPE_BATTERY
devices psy->battery_info is already initialized.
Greetings,
-- Sebastian
> drivers/power/supply/max17042_battery.c | 62 ++++++++
> drivers/power/supply/power_supply_core.c | 188 ++++++++++++++---------
> include/linux/power/max17042_battery.h | 1 +
> include/linux/power_supply.h | 10 ++
> 4 files changed, 187 insertions(+), 74 deletions(-)
>
> diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c
> index 793164bd55d9..5bf15c19fc5d 100644
> --- a/drivers/power/supply/max17042_battery.c
> +++ b/drivers/power/supply/max17042_battery.c
> @@ -1025,6 +1025,57 @@ static int max17042_init_defaults(struct max17042_chip *chip)
> return 0;
> }
>
> +static int max17042_apply_battery_properties(struct max17042_chip *chip,
> + struct power_supply_battery_info *info)
> +{
> + struct device *dev = chip->dev;
> + bool needs_config;
> + u64 data64;
> +
> + if (!info)
> + return 0;
> +
> + needs_config = (chip->enable_current_sense &&
> + (info->charge_full_design_uah != -EINVAL ||
> + info->charge_term_current_ua != -EINVAL)) ||
> + (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055 &&
> + info->voltage_max_design_uv > 4250000);
> + if (!needs_config)
> + return 0;
> +
> + if (!chip->config_data) {
> + chip->config_data = devm_kzalloc(dev,
> + sizeof(*chip->config_data),
> + GFP_KERNEL);
> + if (!chip->config_data)
> + return -ENOMEM;
> + }
> +
> + if (chip->enable_current_sense &&
> + info->charge_full_design_uah != -EINVAL) {
> + data64 = (u64)info->charge_full_design_uah * chip->r_sns;
> + do_div(data64, MAX17042_CAPACITY_LSB);
> + chip->config_data->design_cap = (u16)data64;
> + chip->enable_por_init = true;
> + }
> +
> + if (chip->enable_current_sense &&
> + info->charge_term_current_ua != -EINVAL) {
> + data64 = (u64)info->charge_term_current_ua * chip->r_sns;
> + do_div(data64, MAX17042_CURRENT_LSB);
> + chip->config_data->ichgt_term = (u16)data64;
> + chip->enable_por_init = true;
> + }
> +
> + if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055 &&
> + info->voltage_max_design_uv > 4250000) {
> + chip->config_data->model_cfg = MAX17055_MODELCFG_VCHG_BIT;
> + chip->enable_por_init = true;
> + }
> +
> + return 0;
> +}
> +
> static const struct regmap_config max17042_regmap_config = {
> .name = "max17042",
> .reg_bits = 8,
> @@ -1060,6 +1111,7 @@ static int max17042_probe(struct i2c_client *client, struct device *dev, int irq
> const struct power_supply_desc *max17042_desc = &max17042_psy_desc;
> struct power_supply_config psy_cfg = {};
> struct max17042_chip *chip;
> + struct power_supply_battery_info *battery_info = NULL;
> int ret;
> u32 val;
> bool use_default_config = false;
> @@ -1106,12 +1158,22 @@ static int max17042_probe(struct i2c_client *client, struct device *dev, int irq
> if (chip->r_sns == 0)
> chip->r_sns = MAX17042_DEFAULT_SNS_RESISTOR;
>
> + ret = devm_power_supply_get_battery_info(dev, &battery_info);
> + if (ret && ret != -ENODEV && ret != -ENOENT)
> + return ret;
> +
> + ret = max17042_apply_battery_properties(chip, battery_info);
> + if (ret)
> + return ret;
> +
> if (!chip->enable_current_sense) {
> regmap_write(chip->regmap, MAX17042_CGAIN, 0x0000);
> regmap_write(chip->regmap, MAX17042_MiscCFG, 0x0003);
> regmap_write(chip->regmap, MAX17042_LearnCFG, 0x0007);
> }
>
> + psy_cfg.battery_info = battery_info;
> +
> chip->battery = devm_power_supply_register(dev, max17042_desc,
> &psy_cfg);
> if (IS_ERR(chip->battery))
> diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c
> index a446d3d086fc..7ca3ed34095e 100644
> --- a/drivers/power/supply/power_supply_core.c
> +++ b/drivers/power/supply/power_supply_core.c
> @@ -577,20 +577,76 @@ 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 void __power_supply_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(dev, info->ocv_table[i]);
> + }
> +
> + if (info->resist_table)
> + devm_kfree(dev, info->resist_table);
> +
> + devm_kfree(dev, info);
> +}
> +
> +static void power_supply_init_battery_info(struct power_supply_battery_info *info)
> +{
> + int index;
> +
> + info->technology = POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
> + info->energy_full_design_uwh = -EINVAL;
> + info->charge_full_design_uah = -EINVAL;
> + info->voltage_min_design_uv = -EINVAL;
> + info->voltage_max_design_uv = -EINVAL;
> + info->precharge_current_ua = -EINVAL;
> + info->charge_term_current_ua = -EINVAL;
> + info->constant_charge_current_max_ua = -EINVAL;
> + info->constant_charge_voltage_max_uv = -EINVAL;
> + info->tricklecharge_current_ua = -EINVAL;
> + info->precharge_voltage_max_uv = -EINVAL;
> + info->charge_restart_voltage_uv = -EINVAL;
> + info->overvoltage_limit_uv = -EINVAL;
> + info->maintenance_charge = NULL;
> + info->alert_low_temp_charge_current_ua = -EINVAL;
> + info->alert_low_temp_charge_voltage_uv = -EINVAL;
> + info->alert_high_temp_charge_current_ua = -EINVAL;
> + info->alert_high_temp_charge_voltage_uv = -EINVAL;
> + info->temp_ambient_alert_min = INT_MIN;
> + info->temp_ambient_alert_max = INT_MAX;
> + info->temp_alert_min = INT_MIN;
> + info->temp_alert_max = INT_MAX;
> + info->temp_min = INT_MIN;
> + info->temp_max = INT_MAX;
> + info->factory_internal_resistance_uohm = -EINVAL;
> + info->resist_table = NULL;
> + info->bti_resistance_ohm = -EINVAL;
> + info->bti_resistance_tolerance = -EINVAL;
> +
> + for (index = 0; index < POWER_SUPPLY_OCV_TEMP_MAX; index++) {
> + info->ocv_table[index] = NULL;
> + info->ocv_temp[index] = -EINVAL;
> + info->ocv_table_size[index] = -EINVAL;
> + }
> +}
> +
> +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);
> + if (!srcnode)
> + return -ENODEV;
>
> fwnode = fwnode_find_reference(srcnode, "monitored-battery", 0);
> if (IS_ERR(fwnode))
> @@ -602,7 +658,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)
> @@ -617,46 +673,13 @@ 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;
> }
>
> - info->technology = POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
> - info->energy_full_design_uwh = -EINVAL;
> - info->charge_full_design_uah = -EINVAL;
> - info->voltage_min_design_uv = -EINVAL;
> - info->voltage_max_design_uv = -EINVAL;
> - info->precharge_current_ua = -EINVAL;
> - info->charge_term_current_ua = -EINVAL;
> - info->constant_charge_current_max_ua = -EINVAL;
> - info->constant_charge_voltage_max_uv = -EINVAL;
> - info->tricklecharge_current_ua = -EINVAL;
> - info->precharge_voltage_max_uv = -EINVAL;
> - info->charge_restart_voltage_uv = -EINVAL;
> - info->overvoltage_limit_uv = -EINVAL;
> - info->maintenance_charge = NULL;
> - info->alert_low_temp_charge_current_ua = -EINVAL;
> - info->alert_low_temp_charge_voltage_uv = -EINVAL;
> - info->alert_high_temp_charge_current_ua = -EINVAL;
> - info->alert_high_temp_charge_voltage_uv = -EINVAL;
> - info->temp_ambient_alert_min = INT_MIN;
> - info->temp_ambient_alert_max = INT_MAX;
> - info->temp_alert_min = INT_MIN;
> - info->temp_alert_max = INT_MAX;
> - info->temp_min = INT_MIN;
> - info->temp_max = INT_MAX;
> - info->factory_internal_resistance_uohm = -EINVAL;
> - info->resist_table = NULL;
> - info->bti_resistance_ohm = -EINVAL;
> - info->bti_resistance_tolerance = -EINVAL;
> -
> - for (index = 0; index < POWER_SUPPLY_OCV_TEMP_MAX; index++) {
> - info->ocv_table[index] = NULL;
> - info->ocv_temp[index] = -EINVAL;
> - info->ocv_table_size[index] = -EINVAL;
> - }
> + power_supply_init_battery_info(info);
>
> /* The property and field names below must correspond to elements
> * in enum power_supply_property. For reasoning, see
> @@ -678,7 +701,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",
> @@ -729,7 +752,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) {
> @@ -744,28 +767,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(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(dev, info);
> err = -EINVAL;
> goto out_put_node;
> }
>
> u32 *propdata __free(kfree) = kcalloc(proplen, sizeof(*propdata), GFP_KERNEL);
> if (!propdata) {
> - power_supply_put_battery_info(psy, info);
> + __power_supply_put_battery_info(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(dev, info);
> goto out_put_node;
> }
>
> @@ -773,9 +796,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(dev, info);
> err = -ENOMEM;
> goto out_put_node;
> }
> @@ -791,14 +814,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(dev, info);
> err = (proplen < 0) ? proplen : -EINVAL;
> goto out_put_node;
> }
>
> propdata = kcalloc(proplen, sizeof(*propdata), GFP_KERNEL);
> if (!propdata) {
> - power_supply_put_battery_info(psy, info);
> + __power_supply_put_battery_info(dev, info);
> err = -ENOMEM;
> goto out_put_node;
> }
> @@ -806,17 +829,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(dev, info);
> goto out_put_node;
> }
>
> info->resist_table_size = proplen / 2;
> - info->resist_table = resist_table = devm_kcalloc(&psy->dev,
> - info->resist_table_size,
> - sizeof(*resist_table),
> - GFP_KERNEL);
> + 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(dev, info);
> err = -ENOMEM;
> goto out_put_node;
> }
> @@ -834,22 +857,35 @@ int power_supply_get_battery_info(struct power_supply *psy,
> fwnode_handle_put(fwnode);
> return err;
> }
> -EXPORT_SYMBOL_GPL(power_supply_get_battery_info);
>
> -void power_supply_put_battery_info(struct power_supply *psy,
> - struct power_supply_battery_info *info)
> +int devm_power_supply_get_battery_info(struct device *dev,
> + struct power_supply_battery_info **info_out)
> {
> - int i;
> + struct fwnode_handle *srcnode = dev_fwnode(dev);
>
> - for (i = 0; i < POWER_SUPPLY_OCV_TEMP_MAX; i++) {
> - if (info->ocv_table[i])
> - devm_kfree(&psy->dev, info->ocv_table[i]);
> - }
> + if (!srcnode && dev->parent)
> + srcnode = dev_fwnode(dev->parent);
>
> - if (info->resist_table)
> - devm_kfree(&psy->dev, info->resist_table);
> + return __power_supply_get_battery_info(dev, srcnode, info_out);
> +}
> +EXPORT_SYMBOL_GPL(devm_power_supply_get_battery_info);
> +
> +int power_supply_get_battery_info(struct power_supply *psy,
> + struct power_supply_battery_info **info_out)
> +{
> + struct fwnode_handle *srcnode = dev_fwnode(&psy->dev);
> +
> + if (!srcnode && psy->dev.parent)
> + srcnode = dev_fwnode(psy->dev.parent);
>
> - devm_kfree(&psy->dev, info);
> + 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_put_battery_info(&psy->dev, info);
> }
> EXPORT_SYMBOL_GPL(power_supply_put_battery_info);
>
> @@ -1620,9 +1656,13 @@ __power_supply_register(struct device *parent,
> * expose battery data to userspace for battery devices.
> */
> if (desc->type == POWER_SUPPLY_TYPE_BATTERY) {
> - rc = power_supply_get_battery_info(psy, &psy->battery_info);
> - if (rc && rc != -ENODEV && rc != -ENOENT)
> - goto check_supplies_failed;
> + if (cfg && cfg->battery_info) {
> + psy->battery_info = cfg->battery_info;
> + } else {
> + rc = power_supply_get_battery_info(psy, &psy->battery_info);
> + if (rc && rc != -ENODEV && rc != -ENOENT)
> + goto check_supplies_failed;
> + }
> }
>
> spin_lock_init(&psy->changed_lock);
> diff --git a/include/linux/power/max17042_battery.h b/include/linux/power/max17042_battery.h
> index 6b6e01d3e499..e9f2118e57c8 100644
> --- a/include/linux/power/max17042_battery.h
> +++ b/include/linux/power/max17042_battery.h
> @@ -24,6 +24,7 @@
> #define MAX17042_CHARACTERIZATION_DATA_SIZE 48
>
> #define MAX17055_MODELCFG_REFRESH_BIT BIT(15)
> +#define MAX17055_MODELCFG_VCHG_BIT BIT(10)
>
> enum max17042_register {
> MAX17042_STATUS = 0x00,
> diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
> index 360ffdf272da..f9fca7bb6e24 100644
> --- a/include/linux/power_supply.h
> +++ b/include/linux/power_supply.h
> @@ -231,6 +231,7 @@ union power_supply_propval {
>
> struct device_node;
> struct power_supply;
> +struct power_supply_battery_info;
>
> /* Run-time specific power supply configuration */
> struct power_supply_config {
> @@ -239,6 +240,12 @@ struct power_supply_config {
> /* Driver private data */
> void *drv_data;
>
> + /*
> + * Optional pre-parsed battery info for battery supplies.
> + * The pointed-to data must remain valid for the power supply lifetime.
> + */
> + struct power_supply_battery_info *battery_info;
> +
> /* Device specific sysfs attributes */
> const struct attribute_group **attr_grp;
>
> @@ -814,6 +821,9 @@ extern struct power_supply *power_supply_get_by_reference(struct fwnode_handle *
> extern struct power_supply *devm_power_supply_get_by_reference(
> struct device *dev, const char *property);
>
> +extern int devm_power_supply_get_battery_info(struct device *dev,
> + struct power_supply_battery_info **info_out);
> +
> extern const enum power_supply_property power_supply_battery_info_properties[];
> extern const size_t power_supply_battery_info_properties_size;
> extern int power_supply_get_battery_info(struct power_supply *psy,
> --
> 2.53.0
>
>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v1 9/9] power: supply: max17042_battery: Treat MAX17055 VChg as explicit DT setting
2026-04-06 20:57 [PATCH v1 0/9] power: supply: max17042_battery: improve MAX17055 support Vincent Cloutier
` (7 preceding siblings ...)
2026-04-06 20:57 ` [PATCH v1 8/9] power: supply: Read MAX17042 battery info before registration Vincent Cloutier
@ 2026-04-06 20:57 ` Vincent Cloutier
2026-06-04 16:39 ` [PATCH v1 0/9] power: supply: max17042_battery: improve MAX17055 support Sebastian Reichel
9 siblings, 0 replies; 12+ messages in thread
From: Vincent Cloutier @ 2026-04-06 20:57 UTC (permalink / raw)
To: Sebastian Reichel
Cc: Hans de Goede, Krzysztof Kozlowski, Marek Szyprowski,
Sebastian Krzyszkowiak, Purism Kernel Team, linux-pm,
linux-kernel, Vincent Cloutier
From: Vincent Cloutier <vincent@cloutier.co>
Handle `voltage-max-design-microvolt` as an explicit `ModelCfg.VChg`
setting for MAX17055 so batteries at or below 4.25 V clear the default
bit instead of silently keeping it set.
Signed-off-by: Vincent Cloutier <vincent@cloutier.co>
---
drivers/power/supply/max17042_battery.c | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c
index 5bf15c19fc5d..ad6a341b8a4e 100644
--- a/drivers/power/supply/max17042_battery.c
+++ b/drivers/power/supply/max17042_battery.c
@@ -812,8 +812,13 @@ static inline void max17042_override_por_values(struct max17042_chip *chip)
max17042_override_por(map, MAX17047_V_empty, config->vempty);
}
- if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055)
+ if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055) {
max17042_override_por(map, MAX17055_ModelCfg, config->model_cfg);
+ /* VChg is 1 by default, so allow it to be set to 0 */
+ regmap_update_bits(map, MAX17055_ModelCfg,
+ MAX17055_MODELCFG_VCHG_BIT,
+ config->model_cfg);
+ }
}
static int max17042_init_chip(struct max17042_chip *chip)
@@ -1039,7 +1044,7 @@ static int max17042_apply_battery_properties(struct max17042_chip *chip,
(info->charge_full_design_uah != -EINVAL ||
info->charge_term_current_ua != -EINVAL)) ||
(chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055 &&
- info->voltage_max_design_uv > 4250000);
+ info->voltage_max_design_uv != -EINVAL);
if (!needs_config)
return 0;
@@ -1068,8 +1073,9 @@ static int max17042_apply_battery_properties(struct max17042_chip *chip,
}
if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055 &&
- info->voltage_max_design_uv > 4250000) {
- chip->config_data->model_cfg = MAX17055_MODELCFG_VCHG_BIT;
+ info->voltage_max_design_uv != -EINVAL) {
+ if (info->voltage_max_design_uv > 4250000)
+ chip->config_data->model_cfg = MAX17055_MODELCFG_VCHG_BIT;
chip->enable_por_init = true;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 12+ messages in thread* Re: [PATCH v1 0/9] power: supply: max17042_battery: improve MAX17055 support
2026-04-06 20:57 [PATCH v1 0/9] power: supply: max17042_battery: improve MAX17055 support Vincent Cloutier
` (8 preceding siblings ...)
2026-04-06 20:57 ` [PATCH v1 9/9] power: supply: max17042_battery: Treat MAX17055 VChg as explicit DT setting Vincent Cloutier
@ 2026-06-04 16:39 ` Sebastian Reichel
9 siblings, 0 replies; 12+ messages in thread
From: Sebastian Reichel @ 2026-06-04 16:39 UTC (permalink / raw)
To: Vincent Cloutier
Cc: Hans de Goede, Krzysztof Kozlowski, Marek Szyprowski,
Sebastian Krzyszkowiak, Purism Kernel Team, linux-pm,
linux-kernel, Vincent Cloutier
[-- Attachment #1: Type: text/plain, Size: 2396 bytes --]
Hi,
On Mon, Apr 06, 2026 at 04:57:48PM -0400, Vincent Cloutier wrote:
> From: Vincent Cloutier <vincent@cloutier.co>
>
> This series refreshes the Librem 5 MAX17055 fuel-gauge work for upstream
> on top of current `mainline/master`.
>
> Original series:
> https://lore.kernel.org/lkml/20220318001048.20922-1-sebastian.krzyszkowiak@puri.sm/
>
> Compared to the earlier posted version, the main follow-up changes are:
>
> - keep the original arithmetic form of the fractional `MAX17042_*_LSB`
> expressions while still moving the unit conversions into shared defines
> - remove the unused board-file style platform-data wrapper from the driver
> - factor the `monitored-battery` parser behind a generic
> `devm_power_supply_get_battery_info()` helper so MAX17042 can read DT
> battery data before registration
> - keep `voltage-max-design-microvolt` as an explicit MAX17055
> `ModelCfg.VChg` setting so batteries at or below 4.25 V clear the
> default bit
>
> The attribution split is intentional:
>
> - patches 1, 2, and 7 preserve the original signed Purism downstream work
> from Sebastian Krzyszkowiak
> - the remaining patches are cleanups and follow-ups on top of current
> mainline code
>
> Tested on Librem 5.
>
> Sebastian Krzyszkowiak (3):
> power: supply: max17042_battery: Put LSB units into defines
> power: supply: max17042_battery: Use Current register in get_status
> power: supply: max17042_battery: use ModelCfg refresh on max17055
>
> Vincent Cloutier (6):
> power: supply: max17042_battery: Use dev_err_probe for power supply
> registration
> power: supply: max17042_battery: Route MAX17055 SOC alerts through
> dSOCi
> power: supply: max17042_battery: Keep only critical alerts during
> suspend
> power: supply: max17042_battery: Remove unused platform-data plumbing
> power: supply: Read MAX17042 battery info before registration
> power: supply: max17042_battery: Treat MAX17055 VChg as explicit DT
> setting
>
> drivers/power/supply/max17042_battery.c | 428 ++++++++++++++---------
> drivers/power/supply/power_supply_core.c | 188 ++++++----
> include/linux/power/max17042_battery.h | 33 +-
> include/linux/power_supply.h | 10 +
> 4 files changed, 393 insertions(+), 266 deletions(-)
I merged patches 1-7.
Greetings,
-- Sebastian
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 12+ messages in thread