Skip to content

Commit d591b50

Browse files
author
Al Stone
committed
power: supply: Support VBAT-to-Ri lookup tables
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2071846 Tested: This is one of a series of patch sets to enable Arm SystemReady IR support in the kernel for NXP i.MX8 platforms. This set updates the power subsystem. This set has been tested via simple boot tests and the CI loop. commit e9e7d16 Author: Linus Walleij <linus.walleij@linaro.org> Date: Sat Feb 26 00:27:58 2022 +0100 power: supply: Support VBAT-to-Ri lookup tables In Samsung devices, the method used to compensate for temperature, age, load etc is by way of VBAT to Ri tables, which correlates the battery voltage under load (VBAT) to an internal resistance (Ri). Using this Ri and a measurement of the current out of the battery (IBAT) the open circuit voltage (OCV) can be calculated as: OCV = VBAT - (Ri * IBAT) The details are described in comments to struct power_supply_battery_info in the commit. Since not all batteries supply this VBAT-to-Ri data, the fallback method to use the temperature-to-Ri lookup table can also be used as a fallback. Add two helper functions to check if we have the tables needed for using power_supply_vbat2ri() or power_supply_temp2resist_simple() respectively, so capacity estimation code can choose which one to employ. Signed-off-by: Linus Walleij <linus.walleij@linaro.org> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> (cherry picked from commit e9e7d16) Signed-off-by: Al Stone <ahs3@redhat.com>
1 parent 37fe599 commit d591b50

File tree

2 files changed

+177
-3
lines changed

2 files changed

+177
-3
lines changed

drivers/power/supply/power_supply_core.c

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -814,7 +814,7 @@ EXPORT_SYMBOL_GPL(power_supply_put_battery_info);
814814

815815
/**
816816
* power_supply_temp2resist_simple() - find the battery internal resistance
817-
* percent
817+
* percent from temperature
818818
* @table: Pointer to battery resistance temperature table
819819
* @table_len: The table length
820820
* @temp: Current temperature
@@ -851,6 +851,71 @@ int power_supply_temp2resist_simple(struct power_supply_resistance_temp_table *t
851851
}
852852
EXPORT_SYMBOL_GPL(power_supply_temp2resist_simple);
853853

854+
/**
855+
* power_supply_vbat2ri() - find the battery internal resistance
856+
* from the battery voltage
857+
* @info: The battery information container
858+
* @table: Pointer to battery resistance temperature table
859+
* @vbat_uv: The battery voltage in microvolt
860+
* @charging: If we are charging (true) or not (false)
861+
*
862+
* This helper function is used to look up battery internal resistance
863+
* according to current battery voltage. Depending on whether the battery
864+
* is currently charging or not, different resistance will be returned.
865+
*
866+
* Returns the internal resistance in microohm or negative error code.
867+
*/
868+
int power_supply_vbat2ri(struct power_supply_battery_info *info,
869+
int vbat_uv, bool charging)
870+
{
871+
struct power_supply_vbat_ri_table *vbat2ri;
872+
int table_len;
873+
int i, high, low;
874+
875+
/*
876+
* If we are charging, and the battery supplies a separate table
877+
* for this state, we use that in order to compensate for the
878+
* charging voltage. Otherwise we use the main table.
879+
*/
880+
if (charging && info->vbat2ri_charging) {
881+
vbat2ri = info->vbat2ri_charging;
882+
table_len = info->vbat2ri_charging_size;
883+
} else {
884+
vbat2ri = info->vbat2ri_discharging;
885+
table_len = info->vbat2ri_discharging_size;
886+
}
887+
888+
/*
889+
* If no tables are specified, or if we are above the highest voltage in
890+
* the voltage table, just return the factory specified internal resistance.
891+
*/
892+
if (!vbat2ri || (table_len <= 0) || (vbat_uv > vbat2ri[0].vbat_uv)) {
893+
if (charging && (info->factory_internal_resistance_charging_uohm > 0))
894+
return info->factory_internal_resistance_charging_uohm;
895+
else
896+
return info->factory_internal_resistance_uohm;
897+
}
898+
899+
/* Break loop at table_len - 1 because that is the highest index */
900+
for (i = 0; i < table_len - 1; i++)
901+
if (vbat_uv > vbat2ri[i].vbat_uv)
902+
break;
903+
904+
/* The library function will deal with high == low */
905+
if ((i == 0) || (i == (table_len - 1)))
906+
high = i;
907+
else
908+
high = i - 1;
909+
low = i;
910+
911+
return fixp_linear_interpolate(vbat2ri[low].vbat_uv,
912+
vbat2ri[low].ri_uohm,
913+
vbat2ri[high].vbat_uv,
914+
vbat2ri[high].ri_uohm,
915+
vbat_uv);
916+
}
917+
EXPORT_SYMBOL_GPL(power_supply_vbat2ri);
918+
854919
struct power_supply_maintenance_charge_table *
855920
power_supply_get_maintenance_charging_setting(struct power_supply_battery_info *info,
856921
int index)

include/linux/power_supply.h

Lines changed: 111 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,11 @@ struct power_supply_resistance_temp_table {
349349
int resistance; /* internal resistance percent */
350350
};
351351

352+
struct power_supply_vbat_ri_table {
353+
int vbat_uv; /* Battery voltage in microvolt */
354+
int ri_uohm; /* Internal resistance in microohm */
355+
};
356+
352357
/**
353358
* struct power_supply_maintenance_charge_table - setting for maintenace charging
354359
* @charge_current_max_ua: maintenance charging current that is used to keep
@@ -460,7 +465,14 @@ struct power_supply_maintenance_charge_table {
460465
* @factory_internal_resistance_uohm: the internal resistance of the battery
461466
* at fabrication time, expressed in microohms. This resistance will vary
462467
* depending on the lifetime and charge of the battery, so this is just a
463-
* nominal ballpark figure.
468+
* nominal ballpark figure. This internal resistance is given for the state
469+
* when the battery is discharging.
470+
* @factory_internal_resistance_charging_uohm: the internal resistance of the
471+
* battery at fabrication time while charging, expressed in microohms.
472+
* The charging process will affect the internal resistance of the battery
473+
* so this value provides a better resistance under these circumstances.
474+
* This resistance will vary depending on the lifetime and charge of the
475+
* battery, so this is just a nominal ballpark figure.
464476
* @ocv_temp: array indicating the open circuit voltage (OCV) capacity
465477
* temperature indices. This is an array of temperatures in degrees Celsius
466478
* indicating which capacity table to use for a certain temperature, since
@@ -498,6 +510,21 @@ struct power_supply_maintenance_charge_table {
498510
* by temperature: highest temperature with lowest resistance first, lowest
499511
* temperature with highest resistance last.
500512
* @resist_table_size: the number of items in the resist_table.
513+
* @vbat2ri_discharging: this is a table that correlates Battery voltage (VBAT)
514+
* to internal resistance (Ri). The resistance is given in microohm for the
515+
* corresponding voltage in microvolts. The internal resistance is used to
516+
* determine the open circuit voltage so that we can determine the capacity
517+
* of the battery. These voltages to resistance tables apply when the battery
518+
* is discharging. The table must be ordered descending by voltage: highest
519+
* voltage first.
520+
* @vbat2ri_discharging_size: the number of items in the vbat2ri_discharging
521+
* table.
522+
* @vbat2ri_charging: same function as vbat2ri_discharging but for the state
523+
* when the battery is charging. Being under charge changes the battery's
524+
* internal resistance characteristics so a separate table is needed.*
525+
* The table must be ordered descending by voltage: highest voltage first.
526+
* @vbat2ri_charging_size: the number of items in the vbat2ri_charging
527+
* table.
501528
* @bti_resistance_ohm: The Battery Type Indicator (BIT) nominal resistance
502529
* in ohms for this battery, if an identification resistor is mounted
503530
* between a third battery terminal and ground. This scheme is used by a lot
@@ -512,7 +539,9 @@ struct power_supply_maintenance_charge_table {
512539
* use these for consistency.
513540
*
514541
* Its field names must correspond to elements in enum power_supply_property.
515-
* The default field value is -EINVAL.
542+
* The default field value is -EINVAL or NULL for pointers.
543+
*
544+
* CC/CV CHARGING:
516545
*
517546
* The charging parameters here assume a CC/CV charging scheme. This method
518547
* is most common with Lithium Ion batteries (other methods are possible) and
@@ -597,6 +626,66 @@ struct power_supply_maintenance_charge_table {
597626
* Overcharging Lithium Ion cells can be DANGEROUS and lead to fire or
598627
* explosions.
599628
*
629+
* DETERMINING BATTERY CAPACITY:
630+
*
631+
* Several members of the struct deal with trying to determine the remaining
632+
* capacity in the battery, usually as a percentage of charge. In practice
633+
* many chargers uses a so-called fuel gauge or coloumb counter that measure
634+
* how much charge goes into the battery and how much goes out (+/- leak
635+
* consumption). This does not help if we do not know how much capacity the
636+
* battery has to begin with, such as when it is first used or was taken out
637+
* and charged in a separate charger. Therefore many capacity algorithms use
638+
* the open circuit voltage with a look-up table to determine the rough
639+
* capacity of the battery. The open circuit voltage can be conceptualized
640+
* with an ideal voltage source (V) in series with an internal resistance (Ri)
641+
* like this:
642+
*
643+
* +-------> IBAT >----------------+
644+
* | ^ |
645+
* [ ] Ri | |
646+
* | | VBAT |
647+
* o <---------- | |
648+
* +| ^ | [ ] Rload
649+
* .---. | | |
650+
* | V | | OCV | |
651+
* '---' | | |
652+
* | | | |
653+
* GND +-------------------------------+
654+
*
655+
* If we disconnect the load (here simplified as a fixed resistance Rload)
656+
* and measure VBAT with a infinite impedance voltage meter we will get
657+
* VBAT = OCV and this assumption is sometimes made even under load, assuming
658+
* Rload is insignificant. However this will be of dubious quality because the
659+
* load is rarely that small and Ri is strongly nonlinear depending on
660+
* temperature and how much capacity is left in the battery due to the
661+
* chemistry involved.
662+
*
663+
* In many practical applications we cannot just disconnect the battery from
664+
* the load, so instead we often try to measure the instantaneous IBAT (the
665+
* current out from the battery), estimate the Ri and thus calculate the
666+
* voltage drop over Ri and compensate like this:
667+
*
668+
* OCV = VBAT - (IBAT * Ri)
669+
*
670+
* The tables vbat2ri_discharging and vbat2ri_charging are used to determine
671+
* (by interpolation) the Ri from the VBAT under load. These curves are highly
672+
* nonlinear and may need many datapoints but can be found in datasheets for
673+
* some batteries. This gives the compensated open circuit voltage (OCV) for
674+
* the battery even under load. Using this method will also compensate for
675+
* temperature changes in the environment: this will also make the internal
676+
* resistance change, and it will affect the VBAT under load, so correlating
677+
* VBAT to Ri takes both remaining capacity and temperature into consideration.
678+
*
679+
* Alternatively a manufacturer can specify how the capacity of the battery
680+
* is dependent on the battery temperature which is the main factor affecting
681+
* Ri. As we know all checmical reactions are faster when it is warm and slower
682+
* when it is cold. You can put in 1500mAh and only get 800mAh out before the
683+
* voltage drops too low for example. This effect is also highly nonlinear and
684+
* the purpose of the table resist_table: this will take a temperature and
685+
* tell us how big percentage of Ri the specified temperature correlates to.
686+
* Usually we have 100% of the factory_internal_resistance_uohm at 25 degrees
687+
* Celsius.
688+
*
600689
* The power supply class itself doesn't use this struct as of now.
601690
*/
602691

@@ -621,6 +710,7 @@ struct power_supply_battery_info {
621710
int alert_high_temp_charge_current_ua;
622711
int alert_high_temp_charge_voltage_uv;
623712
int factory_internal_resistance_uohm;
713+
int factory_internal_resistance_charging_uohm;
624714
int ocv_temp[POWER_SUPPLY_OCV_TEMP_MAX];
625715
int temp_ambient_alert_min;
626716
int temp_ambient_alert_max;
@@ -632,6 +722,10 @@ struct power_supply_battery_info {
632722
int ocv_table_size[POWER_SUPPLY_OCV_TEMP_MAX];
633723
struct power_supply_resistance_temp_table *resist_table;
634724
int resist_table_size;
725+
struct power_supply_vbat_ri_table *vbat2ri_discharging;
726+
int vbat2ri_discharging_size;
727+
struct power_supply_vbat_ri_table *vbat2ri_charging;
728+
int vbat2ri_charging_size;
635729
int bti_resistance_ohm;
636730
int bti_resistance_tolerance;
637731
};
@@ -675,6 +769,8 @@ extern int power_supply_batinfo_ocv2cap(struct power_supply_battery_info *info,
675769
extern int
676770
power_supply_temp2resist_simple(struct power_supply_resistance_temp_table *table,
677771
int table_len, int temp);
772+
extern int power_supply_vbat2ri(struct power_supply_battery_info *info,
773+
int vbat_uv, bool charging);
678774
extern struct power_supply_maintenance_charge_table *
679775
power_supply_get_maintenance_charging_setting(struct power_supply_battery_info *info, int index);
680776
extern bool power_supply_battery_bti_in_range(struct power_supply_battery_info *info,
@@ -696,6 +792,19 @@ power_supply_supports_maintenance_charging(struct power_supply_battery_info *inf
696792
return (mt != NULL);
697793
}
698794

795+
static inline bool
796+
power_supply_supports_vbat2ri(struct power_supply_battery_info *info)
797+
{
798+
return ((info->vbat2ri_discharging != NULL) &&
799+
info->vbat2ri_discharging_size > 0);
800+
}
801+
802+
static inline bool
803+
power_supply_supports_temp2ri(struct power_supply_battery_info *info)
804+
{
805+
return ((info->resist_table != NULL) &&
806+
info->resist_table_size > 0);
807+
}
699808

700809
#ifdef CONFIG_POWER_SUPPLY
701810
extern int power_supply_is_system_supplied(void);

0 commit comments

Comments
 (0)