mbox series

[0/4] MAX17055 model configuration via DT

Message ID 20220318001048.20922-1-sebastian.krzyszkowiak@puri.sm
Headers show
Series MAX17055 model configuration via DT | expand

Message

Sebastian Krzyszkowiak March 18, 2022, 12:10 a.m. UTC
Currently, there's no way to supply battery characteristics to max17042
driver on device tree platforms. This series changes that in a way that's
sufficient to configure MAX17055's m5 EZ algorithm, by using a standard
"monitored-battery" phandle.

Sebastian Krzyszkowiak (4):
  power: supply: max17042_battery: Add unit conversion macros
  power: supply: max17042_battery: use ModelCfg refresh on max17055
  dt-bindings: power: supply: max17042: Add monitored-battery phandle
  power: supply: max17042_battery: read battery properties from device
    tree

 .../bindings/power/supply/maxim,max17042.yaml |   4 +
 drivers/power/supply/max17042_battery.c       | 163 ++++++++++++------
 include/linux/power/max17042_battery.h        |   4 +
 3 files changed, 116 insertions(+), 55 deletions(-)

Comments

Krzysztof Kozlowski March 18, 2022, 8:16 a.m. UTC | #1
On 18/03/2022 01:10, Sebastian Krzyszkowiak wrote:
> Instead of sprinkling the code with magic numbers, put the unit
> definitions used by the gauge into a set of macros. Macros are
> used instead of simple defines in order to not require floating
> point operations for divisions.
> 
> Signed-off-by: Sebastian Krzyszkowiak <sebastian.krzyszkowiak@puri.sm>
> ---
>  drivers/power/supply/max17042_battery.c | 40 +++++++++++++++----------
>  1 file changed, 24 insertions(+), 16 deletions(-)
> 
> diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c
> index ab031bbfbe78..c019d6c52363 100644
> --- a/drivers/power/supply/max17042_battery.c
> +++ b/drivers/power/supply/max17042_battery.c
> @@ -51,6 +51,15 @@
>  
>  #define MAX17042_VMAX_TOLERANCE		50 /* 50 mV */
>  
> +#define MAX17042_CURRENT_LSB		1562500ll /* µV */

Is this really long long? The usage in max17042_get_status() is with int
operand and result.

> +#define MAX17042_CURRENT_RSENSE(x)	(x * MAX17042_CURRENT_LSB) /* µV */
> +#define MAX17042_CAPACITY_LSB		5000000ll /* µVh */
> +#define MAX17042_CAPACITY_RSENSE(x)	(x * MAX17042_CAPACITY_LSB) /* µVh */
> +#define MAX17042_TIME(x)		(x * 5625 / 1000) /* s */
> +#define MAX17042_VOLTAGE(x)		(x * 625 / 8) /* µV */
> +#define MAX17042_RESISTANCE(x)		(x / 4096) /* Ω */
> +#define MAX17042_TEMPERATURE(x)		(x / 256) /* °C */

Please enclose the "x" in (), in each macro


Best regards,
Krzysztof
Krzysztof Kozlowski March 18, 2022, 8:40 a.m. UTC | #2
On 18/03/2022 01:10, Sebastian Krzyszkowiak wrote:
> So far configuring the gauge was only possible using platform data,
> with no way to provide the configuration on device tree-based platforms.
> 
> Change that by looking up the configuration values from monitored-battery
> property. This is especially useful on models implementing ModelGauge m5 EZ
> algorithm, such as MAX17055, as all the required configuration can be
> derived from a "simple-battery" DT node there.
> 
> In order to be able to access power supply framework in get_of_pdata,
> move devm_power_supply_register earlier in max17042_probe.
> 
> Signed-off-by: Sebastian Krzyszkowiak <sebastian.krzyszkowiak@puri.sm>
> ---
>  drivers/power/supply/max17042_battery.c | 50 +++++++++++++++++++------
>  include/linux/power/max17042_battery.h  |  1 +
>  2 files changed, 40 insertions(+), 11 deletions(-)
> 
> diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c
> index c39250349a1d..4c33565802d5 100644
> --- a/drivers/power/supply/max17042_battery.c
> +++ b/drivers/power/supply/max17042_battery.c
> @@ -937,7 +937,9 @@ max17042_get_of_pdata(struct max17042_chip *chip)
>  	struct device *dev = &chip->client->dev;
>  	struct device_node *np = dev->of_node;
>  	u32 prop;
> +	u64 data64;
>  	struct max17042_platform_data *pdata;
> +	struct power_supply_battery_info *info;
>  
>  	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
>  	if (!pdata)
> @@ -961,6 +963,32 @@ max17042_get_of_pdata(struct max17042_chip *chip)
>  	if (of_property_read_s32(np, "maxim,over-volt", &pdata->vmax))
>  		pdata->vmax = INT_MAX;
>  
> +	if (pdata->enable_current_sense &&
> +	    power_supply_get_battery_info(chip->battery, &info) == 0) {
> +		pdata->config_data = devm_kzalloc(dev, sizeof(*pdata->config_data), GFP_KERNEL);
> +		if (!pdata->config_data)
> +			return NULL;
> +
> +		if (info->charge_full_design_uah != -EINVAL) {
> +			data64 = (u64)info->charge_full_design_uah * pdata->r_sns;
> +			do_div(data64, MAX17042_CAPACITY_LSB);
> +			pdata->config_data->design_cap = (u16)data64;
> +			pdata->enable_por_init = true;
> +		}
> +		if (info->charge_term_current_ua != -EINVAL) {
> +			data64 = (u64)info->charge_term_current_ua * pdata->r_sns;
> +			do_div(data64, MAX17042_CURRENT_LSB);
> +			pdata->config_data->ichgt_term = (u16)data64;
> +			pdata->enable_por_init = true;
> +		}
> +		if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055) {
> +			if (info->voltage_max_design_uv > 4250000) {
> +				pdata->config_data->model_cfg = MAX17055_MODELCFG_VCHG_BIT;
> +				pdata->enable_por_init = true;
> +			}
> +		}
> +	}
> +
>  	return pdata;
>  }
>  #endif
> @@ -1092,16 +1120,23 @@ static int max17042_probe(struct i2c_client *client,
>  		return -EINVAL;
>  	}
>  
> +	i2c_set_clientdata(client, chip);
> +	psy_cfg.drv_data = chip;
> +	psy_cfg.of_node = dev->of_node;
> +
> +	chip->battery = devm_power_supply_register(&client->dev, max17042_desc,
> +						   &psy_cfg);
> +	if (IS_ERR(chip->battery)) {
> +		dev_err(&client->dev, "failed: power supply register\n");
> +		return PTR_ERR(chip->battery);
> +	}

I don't think it is correct. You register power supply, thus making it
available for system, before configuring most of the data. For short
time the chip might report to the system bogus results and events.

Instead I think you should split it into two parts - init which happens
before registering power supply and after.


Best regards,
Krzysztof
Hans de Goede March 18, 2022, 9 a.m. UTC | #3
Hi,

On 3/18/22 09:16, Krzysztof Kozlowski wrote:
> On 18/03/2022 01:10, Sebastian Krzyszkowiak wrote:
>> Instead of sprinkling the code with magic numbers, put the unit
>> definitions used by the gauge into a set of macros. Macros are
>> used instead of simple defines in order to not require floating
>> point operations for divisions.
>>
>> Signed-off-by: Sebastian Krzyszkowiak <sebastian.krzyszkowiak@puri.sm>
>> ---
>>  drivers/power/supply/max17042_battery.c | 40 +++++++++++++++----------
>>  1 file changed, 24 insertions(+), 16 deletions(-)
>>
>> diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c
>> index ab031bbfbe78..c019d6c52363 100644
>> --- a/drivers/power/supply/max17042_battery.c
>> +++ b/drivers/power/supply/max17042_battery.c
>> @@ -51,6 +51,15 @@
>>  
>>  #define MAX17042_VMAX_TOLERANCE		50 /* 50 mV */
>>  
>> +#define MAX17042_CURRENT_LSB		1562500ll /* µV */
> 
> Is this really long long? The usage in max17042_get_status() is with int
> operand and result.

The "ll" is part of the original code which these macros replace,
dropping the "ll" is IMHO out of scope for this patch, it would
clearly break the only change 1 thing per patch/commit rule.

>> +#define MAX17042_CURRENT_RSENSE(x)	(x * MAX17042_CURRENT_LSB) /* µV */
>> +#define MAX17042_CAPACITY_LSB		5000000ll /* µVh */
>> +#define MAX17042_CAPACITY_RSENSE(x)	(x * MAX17042_CAPACITY_LSB) /* µVh */
>> +#define MAX17042_TIME(x)		(x * 5625 / 1000) /* s */
>> +#define MAX17042_VOLTAGE(x)		(x * 625 / 8) /* µV */
>> +#define MAX17042_RESISTANCE(x)		(x / 4096) /* Ω */
>> +#define MAX17042_TEMPERATURE(x)		(x / 256) /* °C */
> 
> Please enclose the "x" in (), in each macro

Ack, right I should have spotted that in my own review.

Regards,

Hans
Krzysztof Kozlowski March 18, 2022, 9:06 a.m. UTC | #4
On 18/03/2022 10:00, Hans de Goede wrote:
> Hi,
> 
> On 3/18/22 09:16, Krzysztof Kozlowski wrote:
>> On 18/03/2022 01:10, Sebastian Krzyszkowiak wrote:
>>> Instead of sprinkling the code with magic numbers, put the unit
>>> definitions used by the gauge into a set of macros. Macros are
>>> used instead of simple defines in order to not require floating
>>> point operations for divisions.
>>>
>>> Signed-off-by: Sebastian Krzyszkowiak <sebastian.krzyszkowiak@puri.sm>
>>> ---
>>>  drivers/power/supply/max17042_battery.c | 40 +++++++++++++++----------
>>>  1 file changed, 24 insertions(+), 16 deletions(-)
>>>
>>> diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c
>>> index ab031bbfbe78..c019d6c52363 100644
>>> --- a/drivers/power/supply/max17042_battery.c
>>> +++ b/drivers/power/supply/max17042_battery.c
>>> @@ -51,6 +51,15 @@
>>>  
>>>  #define MAX17042_VMAX_TOLERANCE		50 /* 50 mV */
>>>  
>>> +#define MAX17042_CURRENT_LSB		1562500ll /* µV */
>>
>> Is this really long long? The usage in max17042_get_status() is with int
>> operand and result.
> 
> The "ll" is part of the original code which these macros replace,
> dropping the "ll" is IMHO out of scope for this patch, it would
> clearly break the only change 1 thing per patch/commit rule.

Not in max17042_get_status(). The usage there is without ll. Three other
places use it in 64-bit context (result is 64-bit), so there indeed. But
in max17042_get_status() this is now different.


Best regards,
Krzysztof
Sebastian Krzyszkowiak March 18, 2022, 7:58 p.m. UTC | #5
On piątek, 18 marca 2022 09:22:16 CET Krzysztof Kozlowski wrote:
> On 18/03/2022 01:10, Sebastian Krzyszkowiak wrote:
> > Unlike other models, max17055 doesn't require cell characterization
> > data and operates on smaller amount of input variables (DesignCap,
> > VEmpty, IChgTerm and ModelCfg). Input data can already be filled in
> > by max17042_override_por_values, however model refresh bit has to be
> > set after adjusting input variables in order to make them apply.
> > 
> > Signed-off-by: Sebastian Krzyszkowiak <sebastian.krzyszkowiak@puri.sm>
> > ---
> > 
> >  drivers/power/supply/max17042_battery.c | 73 +++++++++++++++----------
> >  include/linux/power/max17042_battery.h  |  3 +
> >  2 files changed, 48 insertions(+), 28 deletions(-)
> > 
> > diff --git a/drivers/power/supply/max17042_battery.c
> > b/drivers/power/supply/max17042_battery.c index
> > c019d6c52363..c39250349a1d 100644
> > --- a/drivers/power/supply/max17042_battery.c
> > +++ b/drivers/power/supply/max17042_battery.c
> > @@ -806,6 +806,13 @@ 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);
> > +		// VChg is 1 by default, so allow it to be set to 0
> 
> Consistent comment, so /* */
> 
> I actually do not understand fully the comment and the code. You write
> entire model_cfg to MAX17055_ModelCfg and then immediately do it again,
> but with smaller mask. Why?

That's because VChg is 1 on POR, and max17042_override_por doesn't do anything 
when value equals 0 - which means that if the whole config->model_cfg is 0, 
VChg won't get unset (which is needed for 4.2V batteries).

This could actually be replaced with a single regmap_write.

> > +		regmap_update_bits(map, MAX17055_ModelCfg,
> > +				MAX17055_MODELCFG_VCHG_BIT, 
config->model_cfg);
> 
> Can you align the continued line with previous line? Same in other
> places if it is not aligned.
> 
> > +	}
> > 
> >  }
> >  
> >  static int max17042_init_chip(struct max17042_chip *chip)
> > 
> > @@ -814,44 +821,54 @@ static int max17042_init_chip(struct max17042_chip
> > *chip)> 
> >  	int ret;
> 
> Best regards,
> Krzysztof
Sebastian Krzyszkowiak March 18, 2022, 8:06 p.m. UTC | #6
Hi Krzysztof, hi Hans,

thanks for the review!

On piątek, 18 marca 2022 10:51:26 CET Hans de Goede wrote:
> Hi,
> 
> On 3/18/22 10:06, Krzysztof Kozlowski wrote:
> > On 18/03/2022 10:00, Hans de Goede wrote:
> >> Hi,
> >> 
> >> On 3/18/22 09:16, Krzysztof Kozlowski wrote:
> >>> On 18/03/2022 01:10, Sebastian Krzyszkowiak wrote:
> >>>> Instead of sprinkling the code with magic numbers, put the unit
> >>>> definitions used by the gauge into a set of macros. Macros are
> >>>> used instead of simple defines in order to not require floating
> >>>> point operations for divisions.
> >>>> 
> >>>> Signed-off-by: Sebastian Krzyszkowiak <sebastian.krzyszkowiak@puri.sm>
> >>>> ---
> >>>> 
> >>>>  drivers/power/supply/max17042_battery.c | 40 +++++++++++++++----------
> >>>>  1 file changed, 24 insertions(+), 16 deletions(-)
> >>>> 
> >>>> diff --git a/drivers/power/supply/max17042_battery.c
> >>>> b/drivers/power/supply/max17042_battery.c index
> >>>> ab031bbfbe78..c019d6c52363 100644
> >>>> --- a/drivers/power/supply/max17042_battery.c
> >>>> +++ b/drivers/power/supply/max17042_battery.c
> >>>> @@ -51,6 +51,15 @@
> >>>> 
> >>>>  #define MAX17042_VMAX_TOLERANCE		50 /* 50 mV */
> >>>> 
> >>>> +#define MAX17042_CURRENT_LSB		1562500ll /* µV */
> >>> 
> >>> Is this really long long? The usage in max17042_get_status() is with int
> >>> operand and result.
> >> 
> >> The "ll" is part of the original code which these macros replace,
> >> dropping the "ll" is IMHO out of scope for this patch, it would
> >> clearly break the only change 1 thing per patch/commit rule.
> > 
> > Not in max17042_get_status(). The usage there is without ll. Three other
> > places use it in 64-bit context (result is 64-bit), so there indeed. But
> > in max17042_get_status() this is now different.
> 
> Ah, good catch and there is a reason why it is not a ll there, a divide
> is done on it, which is now a 64 bit divide which will break on 32 bit
> builds...
> 
> Note that e.g. this existing block:
> 
>         case POWER_SUPPLY_PROP_CURRENT_NOW:
>                 if (chip->pdata->enable_current_sense) {
>                         ret = regmap_read(map, MAX17042_Current, &data);
>                         if (ret < 0)
>                                 return ret;
> 
>                         data64 = sign_extend64(data, 15) * 1562500ll;
>                         val->intval = div_s64(data64, chip->pdata->r_sns);
>                 } else {
>                         return -EINVAL;
>                 }
>                 break;
> 
> Solves this by using the div_s64 helper. So the code in
> max17042_get_status() needs to be fixed to do the same.
> 
> The "ll" is necessary because 32768 * 1562500 = 51200000000 which does not
> fit in a 32 bit integer.
> 
> So fixing max17042_get_status() to use sign_extend64 + div_s64 fixes
> a potential bug there and as such that really should be done in
> a separate preparation patch with a Cc stable.
> 
> Regards,
> 
> Hans

Yes, I've already noticed that max17042_get_status was broken, but it managed 
to slip out of my mind before sending the series - although I haven't caught 
that I'm introducing a yet another breakage there :) I've actually thought 
about removing the unit conversion from this place whatsoever, because this 
function only ever cares about the sign of what's in MAX17042_Current, so it 
doesn't really need to do any division at all.

Best regards,
Sebastian
Hans de Goede March 19, 2022, 1:47 p.m. UTC | #7
Hi,

On 3/18/22 21:06, Sebastian Krzyszkowiak wrote:
> Hi Krzysztof, hi Hans,
> 
> thanks for the review!
> 
> On piątek, 18 marca 2022 10:51:26 CET Hans de Goede wrote:
>> Hi,
>>
>> On 3/18/22 10:06, Krzysztof Kozlowski wrote:
>>> On 18/03/2022 10:00, Hans de Goede wrote:
>>>> Hi,
>>>>
>>>> On 3/18/22 09:16, Krzysztof Kozlowski wrote:
>>>>> On 18/03/2022 01:10, Sebastian Krzyszkowiak wrote:
>>>>>> Instead of sprinkling the code with magic numbers, put the unit
>>>>>> definitions used by the gauge into a set of macros. Macros are
>>>>>> used instead of simple defines in order to not require floating
>>>>>> point operations for divisions.
>>>>>>
>>>>>> Signed-off-by: Sebastian Krzyszkowiak <sebastian.krzyszkowiak@puri.sm>
>>>>>> ---
>>>>>>
>>>>>>  drivers/power/supply/max17042_battery.c | 40 +++++++++++++++----------
>>>>>>  1 file changed, 24 insertions(+), 16 deletions(-)
>>>>>>
>>>>>> diff --git a/drivers/power/supply/max17042_battery.c
>>>>>> b/drivers/power/supply/max17042_battery.c index
>>>>>> ab031bbfbe78..c019d6c52363 100644
>>>>>> --- a/drivers/power/supply/max17042_battery.c
>>>>>> +++ b/drivers/power/supply/max17042_battery.c
>>>>>> @@ -51,6 +51,15 @@
>>>>>>
>>>>>>  #define MAX17042_VMAX_TOLERANCE		50 /* 50 mV */
>>>>>>
>>>>>> +#define MAX17042_CURRENT_LSB		1562500ll /* µV */
>>>>>
>>>>> Is this really long long? The usage in max17042_get_status() is with int
>>>>> operand and result.
>>>>
>>>> The "ll" is part of the original code which these macros replace,
>>>> dropping the "ll" is IMHO out of scope for this patch, it would
>>>> clearly break the only change 1 thing per patch/commit rule.
>>>
>>> Not in max17042_get_status(). The usage there is without ll. Three other
>>> places use it in 64-bit context (result is 64-bit), so there indeed. But
>>> in max17042_get_status() this is now different.
>>
>> Ah, good catch and there is a reason why it is not a ll there, a divide
>> is done on it, which is now a 64 bit divide which will break on 32 bit
>> builds...
>>
>> Note that e.g. this existing block:
>>
>>         case POWER_SUPPLY_PROP_CURRENT_NOW:
>>                 if (chip->pdata->enable_current_sense) {
>>                         ret = regmap_read(map, MAX17042_Current, &data);
>>                         if (ret < 0)
>>                                 return ret;
>>
>>                         data64 = sign_extend64(data, 15) * 1562500ll;
>>                         val->intval = div_s64(data64, chip->pdata->r_sns);
>>                 } else {
>>                         return -EINVAL;
>>                 }
>>                 break;
>>
>> Solves this by using the div_s64 helper. So the code in
>> max17042_get_status() needs to be fixed to do the same.
>>
>> The "ll" is necessary because 32768 * 1562500 = 51200000000 which does not
>> fit in a 32 bit integer.
>>
>> So fixing max17042_get_status() to use sign_extend64 + div_s64 fixes
>> a potential bug there and as such that really should be done in
>> a separate preparation patch with a Cc stable.
>>
>> Regards,
>>
>> Hans
> 
> Yes, I've already noticed that max17042_get_status was broken, but it managed 
> to slip out of my mind before sending the series - although I haven't caught 
> that I'm introducing a yet another breakage there :) I've actually thought 
> about removing the unit conversion from this place whatsoever, because this 
> function only ever cares about the sign of what's in MAX17042_Current, so it 
> doesn't really need to do any division at all.

Removing the value conversion (in a separate patch) would be a good
solution too.

Regards,

Hans
Krzysztof Kozlowski March 20, 2022, 12:18 p.m. UTC | #8
On 18/03/2022 20:58, Sebastian Krzyszkowiak wrote:
> On piątek, 18 marca 2022 09:22:16 CET Krzysztof Kozlowski wrote:
>> On 18/03/2022 01:10, Sebastian Krzyszkowiak wrote:
>>> Unlike other models, max17055 doesn't require cell characterization
>>> data and operates on smaller amount of input variables (DesignCap,
>>> VEmpty, IChgTerm and ModelCfg). Input data can already be filled in
>>> by max17042_override_por_values, however model refresh bit has to be
>>> set after adjusting input variables in order to make them apply.
>>>
>>> Signed-off-by: Sebastian Krzyszkowiak <sebastian.krzyszkowiak@puri.sm>
>>> ---
>>>
>>>  drivers/power/supply/max17042_battery.c | 73 +++++++++++++++----------
>>>  include/linux/power/max17042_battery.h  |  3 +
>>>  2 files changed, 48 insertions(+), 28 deletions(-)
>>>
>>> diff --git a/drivers/power/supply/max17042_battery.c
>>> b/drivers/power/supply/max17042_battery.c index
>>> c019d6c52363..c39250349a1d 100644
>>> --- a/drivers/power/supply/max17042_battery.c
>>> +++ b/drivers/power/supply/max17042_battery.c
>>> @@ -806,6 +806,13 @@ 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);
>>> +		// VChg is 1 by default, so allow it to be set to 0
>>
>> Consistent comment, so /* */
>>
>> I actually do not understand fully the comment and the code. You write
>> entire model_cfg to MAX17055_ModelCfg and then immediately do it again,
>> but with smaller mask. Why?
> 
> That's because VChg is 1 on POR, and max17042_override_por doesn't do anything 
> when value equals 0 - which means that if the whole config->model_cfg is 0, 
> VChg won't get unset (which is needed for 4.2V batteries).
> 
> This could actually be replaced with a single regmap_write.
> 

I got it now. But if config->model_cfg is 0, should VChg be unset?


Best regards,
Krzysztof
Sebastian Krzyszkowiak March 20, 2022, 8:44 p.m. UTC | #9
On niedziela, 20 marca 2022 13:18:49 CET Krzysztof Kozlowski wrote:
> On 18/03/2022 20:58, Sebastian Krzyszkowiak wrote:
> > On piątek, 18 marca 2022 09:22:16 CET Krzysztof Kozlowski wrote:
> >> On 18/03/2022 01:10, Sebastian Krzyszkowiak wrote:
> >>> Unlike other models, max17055 doesn't require cell characterization
> >>> data and operates on smaller amount of input variables (DesignCap,
> >>> VEmpty, IChgTerm and ModelCfg). Input data can already be filled in
> >>> by max17042_override_por_values, however model refresh bit has to be
> >>> set after adjusting input variables in order to make them apply.
> >>> 
> >>> Signed-off-by: Sebastian Krzyszkowiak <sebastian.krzyszkowiak@puri.sm>
> >>> ---
> >>> 
> >>>  drivers/power/supply/max17042_battery.c | 73 +++++++++++++++----------
> >>>  include/linux/power/max17042_battery.h  |  3 +
> >>>  2 files changed, 48 insertions(+), 28 deletions(-)
> >>> 
> >>> diff --git a/drivers/power/supply/max17042_battery.c
> >>> b/drivers/power/supply/max17042_battery.c index
> >>> c019d6c52363..c39250349a1d 100644
> >>> --- a/drivers/power/supply/max17042_battery.c
> >>> +++ b/drivers/power/supply/max17042_battery.c
> >>> @@ -806,6 +806,13 @@ 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);
> >> 
> >>> +		// VChg is 1 by default, so allow it to be set to 0
> >> 
> >> Consistent comment, so /* */
> >> 
> >> I actually do not understand fully the comment and the code. You write
> >> entire model_cfg to MAX17055_ModelCfg and then immediately do it again,
> >> but with smaller mask. Why?
> > 
> > That's because VChg is 1 on POR, and max17042_override_por doesn't do
> > anything when value equals 0 - which means that if the whole
> > config->model_cfg is 0, VChg won't get unset (which is needed for 4.2V
> > batteries).
> > 
> > This could actually be replaced with a single regmap_write.
> 
> I got it now. But if config->model_cfg is 0, should VChg be unset?

That's a good question.

max17042_override_por doesn't override the register value when the given value 
equals zero in order to not override POR defaults with unset platform data. 
This way one can set only the registers that they want to change in `config` 
and the rest are untouched. This, however, only works if we assume that zero 
means "don't touch", which isn't the case for ModelCfg.

On the Librem 5, we need to unset VChg bit because our battery is only being 
charged up to 4.2V. Allowing to unset this bit only without having to touch 
the rest of the register was the motivation behind the current version of this 
patch, however, thinking about it now I can see that it fails to do that in 
the opposite case - when the DT contains a simple-battery with maximum voltage 
higher than 4.25V, VChg will be set in config->model_cfg causing the whole 
register to be overwritten.

So, I see two possible solutions:

1) move VChg handling to a separate variable in struct max17042_config_data. 
This way model_cfg can stay zero when there's no need to touch the rest of the 
register. This minimizes changes over current code.

2) remove max17042_override_por_values in its current shape altogether and 
make it only deal with the values that are actually being set by the driver 
(and only extend it in the future as it gains more ability). Currently most of 
this function is only usable with platform data - is there actually any user 
of max17042 that would need to configure the gauge without DT in the mainline 
kernel? My quick search didn't find any. Do we need or want to keep platform 
data support at all?

I'm leaning towards option 2, as it seems to me that currently this driver is 
being cluttered quite a lot by what's essentially a dead code. Adding new 
parameters to read from DT for POR initialization (which is necessary for 
other models than MAX17055) should be rather easy, but trying to fit them into 
current platform_data-oriented code may be not.

Regards,
Sebastian
Sebastian Krzyszkowiak March 20, 2022, 9:24 p.m. UTC | #10
On piątek, 18 marca 2022 09:40:36 CET Krzysztof Kozlowski wrote:
> On 18/03/2022 01:10, Sebastian Krzyszkowiak wrote:
> > So far configuring the gauge was only possible using platform data,
> > with no way to provide the configuration on device tree-based platforms.
> > 
> > Change that by looking up the configuration values from monitored-battery
> > property. This is especially useful on models implementing ModelGauge m5
> > EZ
> > algorithm, such as MAX17055, as all the required configuration can be
> > derived from a "simple-battery" DT node there.
> > 
> > In order to be able to access power supply framework in get_of_pdata,
> > move devm_power_supply_register earlier in max17042_probe.
> > 
> > Signed-off-by: Sebastian Krzyszkowiak <sebastian.krzyszkowiak@puri.sm>
> > ---
> > 
> >  drivers/power/supply/max17042_battery.c | 50 +++++++++++++++++++------
> >  include/linux/power/max17042_battery.h  |  1 +
> >  2 files changed, 40 insertions(+), 11 deletions(-)
> > 
> > diff --git a/drivers/power/supply/max17042_battery.c
> > b/drivers/power/supply/max17042_battery.c index
> > c39250349a1d..4c33565802d5 100644
> > --- a/drivers/power/supply/max17042_battery.c
> > +++ b/drivers/power/supply/max17042_battery.c
> > @@ -937,7 +937,9 @@ max17042_get_of_pdata(struct max17042_chip *chip)
> > 
> >  	struct device *dev = &chip->client->dev;
> >  	struct device_node *np = dev->of_node;
> >  	u32 prop;
> > 
> > +	u64 data64;
> > 
> >  	struct max17042_platform_data *pdata;
> > 
> > +	struct power_supply_battery_info *info;
> > 
> >  	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
> >  	if (!pdata)
> > 
> > @@ -961,6 +963,32 @@ max17042_get_of_pdata(struct max17042_chip *chip)
> > 
> >  	if (of_property_read_s32(np, "maxim,over-volt", &pdata->vmax))
> >  	
> >  		pdata->vmax = INT_MAX;
> > 
> > +	if (pdata->enable_current_sense &&
> > +	    power_supply_get_battery_info(chip->battery, &info) == 0) {
> > +		pdata->config_data = devm_kzalloc(dev, sizeof(*pdata-
>config_data),
> > GFP_KERNEL); +		if (!pdata->config_data)
> > +			return NULL;
> > +
> > +		if (info->charge_full_design_uah != -EINVAL) {
> > +			data64 = (u64)info->charge_full_design_uah * 
pdata->r_sns;
> > +			do_div(data64, MAX17042_CAPACITY_LSB);
> > +			pdata->config_data->design_cap = (u16)data64;
> > +			pdata->enable_por_init = true;
> > +		}
> > +		if (info->charge_term_current_ua != -EINVAL) {
> > +			data64 = (u64)info->charge_term_current_ua * 
pdata->r_sns;
> > +			do_div(data64, MAX17042_CURRENT_LSB);
> > +			pdata->config_data->ichgt_term = (u16)data64;
> > +			pdata->enable_por_init = true;
> > +		}
> > +		if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055) {
> > +			if (info->voltage_max_design_uv > 4250000) {
> > +				pdata->config_data->model_cfg = 
MAX17055_MODELCFG_VCHG_BIT;
> > +				pdata->enable_por_init = true;
> > +			}
> > +		}
> > +	}
> > +
> > 
> >  	return pdata;
> >  
> >  }
> >  #endif
> > 
> > @@ -1092,16 +1120,23 @@ static int max17042_probe(struct i2c_client
> > *client,> 
> >  		return -EINVAL;
> >  	
> >  	}
> > 
> > +	i2c_set_clientdata(client, chip);
> > +	psy_cfg.drv_data = chip;
> > +	psy_cfg.of_node = dev->of_node;
> > +
> > +	chip->battery = devm_power_supply_register(&client->dev, 
max17042_desc,
> > +						   
&psy_cfg);
> > +	if (IS_ERR(chip->battery)) {
> > +		dev_err(&client->dev, "failed: power supply 
register\n");
> > +		return PTR_ERR(chip->battery);
> > +	}
> 
> I don't think it is correct. You register power supply, thus making it
> available for system, before configuring most of the data. For short
> time the chip might report to the system bogus results and events.
> 
> Instead I think you should split it into two parts - init which happens
> before registering power supply and after.

Simply splitting initialization into two parts won't really help. If you set 
capacity, current, Vchg and refresh the model after registering power supply, 
you will still end up having a short time window with bogus results. Looking 
at other drivers, they seem to deal with it in the same way - they register 
the power supply early, before the driver can fully configure the device.

To actually fix the problem with bogus data on init, it seems like we would 
either need some support from the power supply framework to notify it when can 
it actually start expecting correct data, or have a way to access the battery 
information without having to register power supply beforehand.

Since power_supply_get_battery_info doesn't actually seem to depend on 
power_supply device at all - it uses psy->dev for devm functions and psy-
>of_node to read the data from - I wonder if it could be split into a function 
that only takes an of_node?

Cheers,
Sebastian
Krzysztof Kozlowski March 23, 2022, 9:47 a.m. UTC | #11
On 20/03/2022 21:44, Sebastian Krzyszkowiak wrote:
> On niedziela, 20 marca 2022 13:18:49 CET Krzysztof Kozlowski wrote:
>> On 18/03/2022 20:58, Sebastian Krzyszkowiak wrote:
>>> On piątek, 18 marca 2022 09:22:16 CET Krzysztof Kozlowski wrote:
>>>> On 18/03/2022 01:10, Sebastian Krzyszkowiak wrote:
>>>>> Unlike other models, max17055 doesn't require cell characterization
>>>>> data and operates on smaller amount of input variables (DesignCap,
>>>>> VEmpty, IChgTerm and ModelCfg). Input data can already be filled in
>>>>> by max17042_override_por_values, however model refresh bit has to be
>>>>> set after adjusting input variables in order to make them apply.
>>>>>
>>>>> Signed-off-by: Sebastian Krzyszkowiak <sebastian.krzyszkowiak@puri.sm>
>>>>> ---
>>>>>
>>>>>  drivers/power/supply/max17042_battery.c | 73 +++++++++++++++----------
>>>>>  include/linux/power/max17042_battery.h  |  3 +
>>>>>  2 files changed, 48 insertions(+), 28 deletions(-)
>>>>>
>>>>> diff --git a/drivers/power/supply/max17042_battery.c
>>>>> b/drivers/power/supply/max17042_battery.c index
>>>>> c019d6c52363..c39250349a1d 100644
>>>>> --- a/drivers/power/supply/max17042_battery.c
>>>>> +++ b/drivers/power/supply/max17042_battery.c
>>>>> @@ -806,6 +806,13 @@ 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);
>>>>
>>>>> +		// VChg is 1 by default, so allow it to be set to 0
>>>>
>>>> Consistent comment, so /* */
>>>>
>>>> I actually do not understand fully the comment and the code. You write
>>>> entire model_cfg to MAX17055_ModelCfg and then immediately do it again,
>>>> but with smaller mask. Why?
>>>
>>> That's because VChg is 1 on POR, and max17042_override_por doesn't do
>>> anything when value equals 0 - which means that if the whole
>>> config->model_cfg is 0, VChg won't get unset (which is needed for 4.2V
>>> batteries).
>>>
>>> This could actually be replaced with a single regmap_write.
>>
>> I got it now. But if config->model_cfg is 0, should VChg be unset?
> 
> That's a good question.
> 
> max17042_override_por doesn't override the register value when the given value 
> equals zero in order to not override POR defaults with unset platform data. 
> This way one can set only the registers that they want to change in `config` 
> and the rest are untouched. This, however, only works if we assume that zero 
> means "don't touch", which isn't the case for ModelCfg.
> 
> On the Librem 5, we need to unset VChg bit because our battery is only being 
> charged up to 4.2V. Allowing to unset this bit only without having to touch 
> the rest of the register was the motivation behind the current version of this 
> patch, however, thinking about it now I can see that it fails to do that in 
> the opposite case - when the DT contains a simple-battery with maximum voltage 
> higher than 4.25V, VChg will be set in config->model_cfg causing the whole 
> register to be overwritten.

This is actually nice description which could be put into a comment there.

> 
> So, I see two possible solutions:
> 
> 1) move VChg handling to a separate variable in struct max17042_config_data. 
> This way model_cfg can stay zero when there's no need to touch the rest of the 
> register. This minimizes changes over current code.
> 
> 2) remove max17042_override_por_values in its current shape altogether and 
> make it only deal with the values that are actually being set by the driver 
> (and only extend it in the future as it gains more ability). Currently most of 
> this function is only usable with platform data - is there actually any user 
> of max17042 that would need to configure the gauge without DT in the mainline 
> kernel? My quick search didn't find any. Do we need or want to keep platform 
> data support at all?
> 
> I'm leaning towards option 2, as it seems to me that currently this driver is 
> being cluttered quite a lot by what's essentially a dead code. Adding new 
> parameters to read from DT for POR initialization (which is necessary for 
> other models than MAX17055) should be rather easy, but trying to fit them into 
> current platform_data-oriented code may be not.

I am in for removal of platform data.

Best regards,
Krzysztof
Krzysztof Kozlowski March 23, 2022, 9:48 a.m. UTC | #12
On 20/03/2022 22:24, Sebastian Krzyszkowiak wrote:
> On piątek, 18 marca 2022 09:40:36 CET Krzysztof Kozlowski wrote:
>> On 18/03/2022 01:10, Sebastian Krzyszkowiak wrote:
>>> So far configuring the gauge was only possible using platform data,
>>> with no way to provide the configuration on device tree-based platforms.
>>>
>>> Change that by looking up the configuration values from monitored-battery
>>> property. This is especially useful on models implementing ModelGauge m5
>>> EZ
>>> algorithm, such as MAX17055, as all the required configuration can be
>>> derived from a "simple-battery" DT node there.
>>>
>>> In order to be able to access power supply framework in get_of_pdata,
>>> move devm_power_supply_register earlier in max17042_probe.
>>>
>>> Signed-off-by: Sebastian Krzyszkowiak <sebastian.krzyszkowiak@puri.sm>
>>> ---
>>>
>>>  drivers/power/supply/max17042_battery.c | 50 +++++++++++++++++++------
>>>  include/linux/power/max17042_battery.h  |  1 +
>>>  2 files changed, 40 insertions(+), 11 deletions(-)
>>>
>>> diff --git a/drivers/power/supply/max17042_battery.c
>>> b/drivers/power/supply/max17042_battery.c index
>>> c39250349a1d..4c33565802d5 100644
>>> --- a/drivers/power/supply/max17042_battery.c
>>> +++ b/drivers/power/supply/max17042_battery.c
>>> @@ -937,7 +937,9 @@ max17042_get_of_pdata(struct max17042_chip *chip)
>>>
>>>  	struct device *dev = &chip->client->dev;
>>>  	struct device_node *np = dev->of_node;
>>>  	u32 prop;
>>>
>>> +	u64 data64;
>>>
>>>  	struct max17042_platform_data *pdata;
>>>
>>> +	struct power_supply_battery_info *info;
>>>
>>>  	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
>>>  	if (!pdata)
>>>
>>> @@ -961,6 +963,32 @@ max17042_get_of_pdata(struct max17042_chip *chip)
>>>
>>>  	if (of_property_read_s32(np, "maxim,over-volt", &pdata->vmax))
>>>  	
>>>  		pdata->vmax = INT_MAX;
>>>
>>> +	if (pdata->enable_current_sense &&
>>> +	    power_supply_get_battery_info(chip->battery, &info) == 0) {
>>> +		pdata->config_data = devm_kzalloc(dev, sizeof(*pdata-
>> config_data),
>>> GFP_KERNEL); +		if (!pdata->config_data)
>>> +			return NULL;
>>> +
>>> +		if (info->charge_full_design_uah != -EINVAL) {
>>> +			data64 = (u64)info->charge_full_design_uah * 
> pdata->r_sns;
>>> +			do_div(data64, MAX17042_CAPACITY_LSB);
>>> +			pdata->config_data->design_cap = (u16)data64;
>>> +			pdata->enable_por_init = true;
>>> +		}
>>> +		if (info->charge_term_current_ua != -EINVAL) {
>>> +			data64 = (u64)info->charge_term_current_ua * 
> pdata->r_sns;
>>> +			do_div(data64, MAX17042_CURRENT_LSB);
>>> +			pdata->config_data->ichgt_term = (u16)data64;
>>> +			pdata->enable_por_init = true;
>>> +		}
>>> +		if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055) {
>>> +			if (info->voltage_max_design_uv > 4250000) {
>>> +				pdata->config_data->model_cfg = 
> MAX17055_MODELCFG_VCHG_BIT;
>>> +				pdata->enable_por_init = true;
>>> +			}
>>> +		}
>>> +	}
>>> +
>>>
>>>  	return pdata;
>>>  
>>>  }
>>>  #endif
>>>
>>> @@ -1092,16 +1120,23 @@ static int max17042_probe(struct i2c_client
>>> *client,> 
>>>  		return -EINVAL;
>>>  	
>>>  	}
>>>
>>> +	i2c_set_clientdata(client, chip);
>>> +	psy_cfg.drv_data = chip;
>>> +	psy_cfg.of_node = dev->of_node;
>>> +
>>> +	chip->battery = devm_power_supply_register(&client->dev, 
> max17042_desc,
>>> +						   
> &psy_cfg);
>>> +	if (IS_ERR(chip->battery)) {
>>> +		dev_err(&client->dev, "failed: power supply 
> register\n");
>>> +		return PTR_ERR(chip->battery);
>>> +	}
>>
>> I don't think it is correct. You register power supply, thus making it
>> available for system, before configuring most of the data. For short
>> time the chip might report to the system bogus results and events.
>>
>> Instead I think you should split it into two parts - init which happens
>> before registering power supply and after.
> 
> Simply splitting initialization into two parts won't really help. If you set 
> capacity, current, Vchg and refresh the model after registering power supply, 
> you will still end up having a short time window with bogus results. Looking 
> at other drivers, they seem to deal with it in the same way - they register 
> the power supply early, before the driver can fully configure the device.
> 
> To actually fix the problem with bogus data on init, it seems like we would 
> either need some support from the power supply framework to notify it when can 
> it actually start expecting correct data, or have a way to access the battery 
> information without having to register power supply beforehand.

Indeed I spotted similar pattern in other drivers, so this might be a
common issue.

> 
> Since power_supply_get_battery_info doesn't actually seem to depend on 
> power_supply device at all - it uses psy->dev for devm functions and psy-
> of_node to read the data from - I wonder if it could be split into a function 
> that only takes an of_node?

That might be the best approach.


Best regards,
Krzysztof