All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/2] More AB8500 charging props
@ 2021-11-22 23:41 Linus Walleij
  2021-11-22 23:41 ` [PATCH 1/2] power: supply: ab8500: Standardize NTC battery temp Linus Walleij
                   ` (2 more replies)
  0 siblings, 3 replies; 8+ messages in thread
From: Linus Walleij @ 2021-11-22 23:41 UTC (permalink / raw)
  To: Sebastian Reichel, Marcus Cooper, Jean Delvare, Guenter Roeck
  Cc: linux-pm, linux-hwmon, Linus Walleij

These two patches begin to put new stuff into the
drivers/power/supply/ab8500_bmdata.c for:

A) battery temperature look-up and interpolation, and
B) maintenance charging

Some design choices can be discussed, so I included some
HWMON etc maintainers.

NTC resistor:

For the battery NTC temperature we should note the existing NTC
driver in drivers/hwmon/ntc_thermistor.c with bindings
in Documentation/devicetree/bindings/hwmon/ntc-thermistor.yaml
which is used for stand-alone NTC resistors.

It is probably possible to try to reuse the hwmon code but I
wanted to see if we have buy-in from the hwmon maintainer first.

If yes, I will try to come up with some way of representing the
NTC resistor in the device tree and use it in-tree from the
charging drivers.

Maintenance charging:

I included an infrastructure for maintenance "charging points"
which constitute of a current, a voltage and a safety timer:

struct power_supply_maintenance_charge_table {
       int charge_current_max_ua;
       int charge_voltage_max_uv;
       int charge_safety_timer_minutes;
};

This is all pretty straight-forward if just adding maintenance
charging.

For the bigger picture, however, a charging point can also
include the existing CC/CV current and voltage and the
precharging current and voltage as two charging points,
possibly without corresponding safety timer. If this is
desired I will rework the patch to refactor the existing
currents and voltages into charging points as well.

Linus Walleij (2):
  power: supply: ab8500: Standardize NTC battery temp
  power: supply: ab8500: Standardize maintenance charging

 drivers/power/supply/ab8500-bm.h         | 30 --------
 drivers/power/supply/ab8500_bmdata.c     | 68 +++++++++-------
 drivers/power/supply/ab8500_btemp.c      | 45 +----------
 drivers/power/supply/ab8500_chargalg.c   | 41 +++++++---
 drivers/power/supply/power_supply_core.c | 63 +++++++++++++++
 include/linux/power_supply.h             | 98 ++++++++++++++++++++++++
 6 files changed, 237 insertions(+), 108 deletions(-)

-- 
2.31.1


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 1/2] power: supply: ab8500: Standardize NTC battery temp
  2021-11-22 23:41 [PATCH 0/2] More AB8500 charging props Linus Walleij
@ 2021-11-22 23:41 ` Linus Walleij
  2021-12-03 20:53   ` Sebastian Reichel
  2021-11-22 23:41 ` [PATCH 2/2] power: supply: ab8500: Standardize maintenance charging Linus Walleij
  2021-11-23  0:23 ` [PATCH 0/2] More AB8500 charging props Guenter Roeck
  2 siblings, 1 reply; 8+ messages in thread
From: Linus Walleij @ 2021-11-22 23:41 UTC (permalink / raw)
  To: Sebastian Reichel, Marcus Cooper, Jean Delvare, Guenter Roeck
  Cc: linux-pm, linux-hwmon, Linus Walleij

Several batteries monitor the temperature of the battery using
an NTC resistor. Add an NTC resistor resistance to temperature
look-up table to struct power_supply_battery_info and use this
in the AB8500 battery temperature driver to look up the battery
temperature.

Add a helper function in the power supply core:
power_supply_ntc_resist2temp_simple(), which can use the
NTC resistance table to look up and interpolate a temperature
from the table.

Fix up some of the confusing wording on the temperature to
internal resistance table, and clarify what each resistance
table is for.

The AB8500 default NTC resistor is 47KOhm at 25 degrees
Celsius and serves as a good example of how this usually works.

Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
 drivers/power/supply/ab8500-bm.h         | 16 --------
 drivers/power/supply/ab8500_bmdata.c     | 41 ++++++++++---------
 drivers/power/supply/ab8500_btemp.c      | 45 ++------------------
 drivers/power/supply/power_supply_core.c | 52 ++++++++++++++++++++++++
 include/linux/power_supply.h             | 34 ++++++++++++++++
 5 files changed, 111 insertions(+), 77 deletions(-)

diff --git a/drivers/power/supply/ab8500-bm.h b/drivers/power/supply/ab8500-bm.h
index 57e1a8e27e51..90397f2a731f 100644
--- a/drivers/power/supply/ab8500-bm.h
+++ b/drivers/power/supply/ab8500-bm.h
@@ -272,18 +272,6 @@ enum ab8500_adc_therm {
 	AB8500_ADC_THERM_BATTEMP,
 };
 
-/**
- * struct ab8500_res_to_temp - defines one point in a temp to res curve. To
- * be used in battery packs that combines the identification resistor with a
- * NTC resistor.
- * @temp:			battery pack temperature in Celsius
- * @resist:			NTC resistor net total resistance
- */
-struct ab8500_res_to_temp {
-	int temp;
-	int resist;
-};
-
 /* Forward declaration */
 struct ab8500_fg;
 
@@ -363,8 +351,6 @@ struct ab8500_maxim_parameters {
  * @maint_b_chg_timer_h:	charge time in maintenance B state
  * @low_high_cur_lvl:		charger current in temp low/high state in mA
  * @low_high_vol_lvl:		charger voltage in temp low/high state in mV'
- * @n_r_t_tbl_elements:		number of elements in r_to_t_tbl
- * @r_to_t_tbl:			table containing resistance to temp points
  */
 struct ab8500_battery_type {
 	int resis_high;
@@ -377,8 +363,6 @@ struct ab8500_battery_type {
 	int maint_b_chg_timer_h;
 	int low_high_cur_lvl;
 	int low_high_vol_lvl;
-	int n_temp_tbl_elements;
-	const struct ab8500_res_to_temp *r_to_t_tbl;
 };
 
 /**
diff --git a/drivers/power/supply/ab8500_bmdata.c b/drivers/power/supply/ab8500_bmdata.c
index 62953f9cb85a..aba459393ee6 100644
--- a/drivers/power/supply/ab8500_bmdata.c
+++ b/drivers/power/supply/ab8500_bmdata.c
@@ -44,25 +44,25 @@ static struct power_supply_battery_ocv_table ocv_cap_tbl[] = {
 };
 
 /*
- * Note that the res_to_temp table must be strictly sorted by falling
+ * Note that the ntc_res_to_temp_tbl table must be strictly sorted by falling
  * resistance values to work.
  */
-static const struct ab8500_res_to_temp temp_tbl[] = {
-	{-5, 214834},
-	{ 0, 162943},
-	{ 5, 124820},
-	{10,  96520},
-	{15,  75306},
-	{20,  59254},
-	{25,  47000},
-	{30,  37566},
-	{35,  30245},
-	{40,  24520},
-	{45,  20010},
-	{50,  16432},
-	{55,  13576},
-	{60,  11280},
-	{65,   9425},
+static struct power_supply_ntc_resistance_temp_table ntc_res_to_temp_tbl[] = {
+	{ .resistance_ohm = 214834, .temp = -5},
+	{ .resistance_ohm = 162943, .temp = 0},
+	{ .resistance_ohm = 124820, .temp = 5},
+	{ .resistance_ohm = 96520, .temp = 10},
+	{ .resistance_ohm = 75306, .temp = 15},
+	{ .resistance_ohm = 59254, .temp = 20},
+	{ .resistance_ohm = 47000, .temp = 25},
+	{ .resistance_ohm = 37566, .temp = 30},
+	{ .resistance_ohm = 30245, .temp = 35},
+	{ .resistance_ohm = 24520, .temp = 40},
+	{ .resistance_ohm = 20010, .temp = 45},
+	{ .resistance_ohm = 16432, .temp = 50},
+	{ .resistance_ohm = 13576, .temp = 55},
+	{ .resistance_ohm = 11280, .temp = 60},
+	{ .resistance_ohm = 9425, .temp = 65},
 };
 
 /*
@@ -92,8 +92,6 @@ static struct ab8500_battery_type bat_type_thermistor_unknown = {
 	.maint_b_chg_timer_h = 200,
 	.low_high_cur_lvl = 300,
 	.low_high_vol_lvl = 4000,
-	.n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
-	.r_to_t_tbl = temp_tbl,
 };
 
 static const struct ab8500_bm_capacity_levels cap_levels = {
@@ -217,6 +215,11 @@ int ab8500_bm_of_probe(struct power_supply *psy,
 		bi->resist_table_size = ARRAY_SIZE(temp_to_batres_tbl_thermistor);
 	}
 
+	if (!bi->ntc_resist_table) {
+		bi->ntc_resist_table = ntc_res_to_temp_tbl;
+		bi->ntc_resist_table_size = ARRAY_SIZE(ntc_res_to_temp_tbl);
+	}
+
 	if (!bi->ocv_table[0]) {
 		/* Default capacity table at say 25 degrees Celsius */
 		bi->ocv_temp[0] = 25;
diff --git a/drivers/power/supply/ab8500_btemp.c b/drivers/power/supply/ab8500_btemp.c
index 20253b8a7fe9..79528c18618d 100644
--- a/drivers/power/supply/ab8500_btemp.c
+++ b/drivers/power/supply/ab8500_btemp.c
@@ -407,42 +407,6 @@ static int ab8500_btemp_get_batctrl_res(struct ab8500_btemp *di)
 	return res;
 }
 
-/**
- * ab8500_btemp_res_to_temp() - resistance to temperature
- * @di:		pointer to the ab8500_btemp structure
- * @tbl:	pointer to the resiatance to temperature table
- * @tbl_size:	size of the resistance to temperature table
- * @res:	resistance to calculate the temperature from
- *
- * This function returns the battery temperature in degrees Celsius
- * based on the NTC resistance.
- */
-static int ab8500_btemp_res_to_temp(struct ab8500_btemp *di,
-	const struct ab8500_res_to_temp *tbl, int tbl_size, int res)
-{
-	int i;
-	/*
-	 * Calculate the formula for the straight line
-	 * Simple interpolation if we are within
-	 * the resistance table limits, extrapolate
-	 * if resistance is outside the limits.
-	 */
-	if (res > tbl[0].resist)
-		i = 0;
-	else if (res <= tbl[tbl_size - 1].resist)
-		i = tbl_size - 2;
-	else {
-		i = 0;
-		while (!(res <= tbl[i].resist &&
-			res > tbl[i + 1].resist))
-			i++;
-	}
-
-	return fixp_linear_interpolate(tbl[i].resist, tbl[i].temp,
-				       tbl[i + 1].resist, tbl[i + 1].temp,
-				       res);
-}
-
 /**
  * ab8500_btemp_measure_temp() - measure battery temperature
  * @di:		pointer to the ab8500_btemp structure
@@ -451,6 +415,7 @@ static int ab8500_btemp_res_to_temp(struct ab8500_btemp *di,
  */
 static int ab8500_btemp_measure_temp(struct ab8500_btemp *di)
 {
+	struct power_supply_battery_info *bi = &di->bm->bi;
 	int temp, ret;
 	static int prev;
 	int rbat, rntc, vntc;
@@ -469,9 +434,7 @@ static int ab8500_btemp_measure_temp(struct ab8500_btemp *di)
 			return BTEMP_THERMAL_LOW_LIMIT;
 		}
 
-		temp = ab8500_btemp_res_to_temp(di,
-			di->bm->bat_type->r_to_t_tbl,
-			di->bm->bat_type->n_temp_tbl_elements, rbat);
+		temp = power_supply_ntc_resist2temp_simple(bi, rbat);
 	} else {
 		ret = iio_read_channel_processed(di->btemp_ball, &vntc);
 		if (ret < 0) {
@@ -486,9 +449,7 @@ static int ab8500_btemp_measure_temp(struct ab8500_btemp *di)
 		 */
 		rntc = 230000 * vntc / (VTVOUT_V - vntc);
 
-		temp = ab8500_btemp_res_to_temp(di,
-			di->bm->bat_type->r_to_t_tbl,
-			di->bm->bat_type->n_temp_tbl_elements, rntc);
+		temp = power_supply_ntc_resist2temp_simple(bi, rntc);
 		prev = temp;
 	}
 	dev_dbg(di->dev, "Battery temperature is %d\n", temp);
diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c
index 2723d7d0ced3..a870c3fe032c 100644
--- a/drivers/power/supply/power_supply_core.c
+++ b/drivers/power/supply/power_supply_core.c
@@ -589,6 +589,7 @@ int power_supply_get_battery_info(struct power_supply *psy,
 	info->temp_max                       = INT_MAX;
 	info->factory_internal_resistance_uohm  = -EINVAL;
 	info->resist_table = NULL;
+	info->ntc_resist_table = NULL;
 
 	for (index = 0; index < POWER_SUPPLY_OCV_TEMP_MAX; index++) {
 		info->ocv_table[index]       = NULL;
@@ -806,6 +807,57 @@ int power_supply_temp2resist_simple(struct power_supply_resistance_temp_table *t
 }
 EXPORT_SYMBOL_GPL(power_supply_temp2resist_simple);
 
+/**
+ * power_supply_ntc_resist2temp_simple() - find the battery temperature
+ * @info: battery information
+ * @resistance_ohm: Current NTC resistance value in microohm
+ *
+ * This helper function is used to look up battery internal temperature
+ * according to current NTC resistance value from one NTC resistance table.
+ * The NTC resistance table must be ordered descending by resistance:
+ * largest resistance with lowest temperature first, lowest resistance with
+ * highest temperature last. The function will interpolate to find the
+ * corresponding temperature.
+ *
+ * Return: the battery temperature.
+ */
+int power_supply_ntc_resist2temp_simple(struct power_supply_battery_info *info,
+					int resistance_ohm)
+{
+	struct power_supply_ntc_resistance_temp_table *table;
+	int i, high, low;
+	int table_len;
+
+	table = info->ntc_resist_table;
+	table_len = info->ntc_resist_table_size;
+
+	if (!table || !table_len) {
+		pr_err("Empty battery NTC resistance table, assume 25 degrees\n");
+		return 25;
+	}
+	if (!resistance_ohm)
+		pr_info("Battery NTC resistance 0, this is unlikely\n");
+
+	/* Break loop at table_len - 1 because that is the highest index */
+	for (i = 0; i < table_len - 1; i++)
+		if (resistance_ohm > table[i].resistance_ohm)
+			break;
+
+	/* The library function will deal with high == low */
+	if ((i == 0) || (i == (table_len - 1)))
+		high = i;
+	else
+		high = i - 1;
+	low = i;
+
+	return fixp_linear_interpolate(table[low].resistance_ohm,
+				       table[low].temp,
+				       table[high].resistance_ohm,
+				       table[high].temp,
+				       resistance_ohm);
+}
+EXPORT_SYMBOL_GPL(power_supply_ntc_resist2temp_simple);
+
 /**
  * power_supply_ocv2cap_simple() - find the battery capacity
  * @table: Pointer to battery OCV lookup table
diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
index b5079109ac00..a0da806185b5 100644
--- a/include/linux/power_supply.h
+++ b/include/linux/power_supply.h
@@ -335,11 +335,29 @@ struct power_supply_battery_ocv_table {
 	int capacity;	/* percent */
 };
 
+/**
+ * struct power_supply_resistance_temp_table - correlate temperature to resistance
+ * @temp: the internal temperature of the battery in degrees Celsius
+ * @resistance: the percentage of the factory internal resistance at this
+ *   temperature, usually nomimal factory resistance is 100 percent at 25
+ *   degrees Celsius, lower at higher temperature and higher at lower
+ *   temperature.
+ */
 struct power_supply_resistance_temp_table {
 	int temp;	/* celsius */
 	int resistance;	/* internal resistance percent */
 };
 
+/**
+ * struct power_supply_ntc_resistance_temp_table - correlate NTC to temp
+ * @resistance: the NTC resistance in ohm
+ * @temp: the corresponding temperature in degrees Celsius
+ */
+struct power_supply_ntc_resistance_temp_table {
+	int resistance_ohm;
+	int temp;
+};
+
 #define POWER_SUPPLY_OCV_TEMP_MAX 20
 
 /**
@@ -426,6 +444,18 @@ struct power_supply_resistance_temp_table {
  *   by temperature: highest temperature with lowest resistance first, lowest
  *   temperature with highest resistance last.
  * @resist_table_size: the number of items in the resist_table.
+ * @ntc_resist_table: this is a table that correlates a resistance of a negative
+ *   temperature coefficient (NTC) resistor to an internal temperature of a
+ *   battery. This can be achieved by a separate thermistor to
+ *   supply voltage on a third terminal on a battery which is the most
+ *   reliable. An external thermistor can also be used sometimes. Knowing the
+ *   temperature of the battery is usually necessary to perform a lookup in the
+ *   resist_table to determine the internal resistance of the battery, and
+ *   to find the right ocv_table to determine the capacity of the battery.
+ *   The NTC resistance table must be ordered descending by resistance:
+ *   largest resistance with lowest temperature first, lowest resistance with
+ *   highest temperature last.
+ * @ntc_resist_table_size: the number of items in the ntc_resist_table.
  *
  * This is the recommended struct to manage static battery parameters,
  * populated by power_supply_get_battery_info(). Most platform drivers should
@@ -546,6 +576,8 @@ struct power_supply_battery_info {
 	int ocv_table_size[POWER_SUPPLY_OCV_TEMP_MAX];
 	struct power_supply_resistance_temp_table *resist_table;
 	int resist_table_size;
+	struct power_supply_ntc_resistance_temp_table *ntc_resist_table;
+	int ntc_resist_table_size;
 };
 
 extern struct atomic_notifier_head power_supply_notifier;
@@ -587,6 +619,8 @@ extern int power_supply_batinfo_ocv2cap(struct power_supply_battery_info *info,
 extern int
 power_supply_temp2resist_simple(struct power_supply_resistance_temp_table *table,
 				int table_len, int temp);
+extern int power_supply_ntc_resist2temp_simple(struct power_supply_battery_info *info,
+					       int resistance_ohm);
 extern void power_supply_changed(struct power_supply *psy);
 extern int power_supply_am_i_supplied(struct power_supply *psy);
 extern int power_supply_set_input_current_limit_from_supplier(
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 2/2] power: supply: ab8500: Standardize maintenance charging
  2021-11-22 23:41 [PATCH 0/2] More AB8500 charging props Linus Walleij
  2021-11-22 23:41 ` [PATCH 1/2] power: supply: ab8500: Standardize NTC battery temp Linus Walleij
@ 2021-11-22 23:41 ` Linus Walleij
  2021-12-03 21:02   ` Sebastian Reichel
  2021-11-23  0:23 ` [PATCH 0/2] More AB8500 charging props Guenter Roeck
  2 siblings, 1 reply; 8+ messages in thread
From: Linus Walleij @ 2021-11-22 23:41 UTC (permalink / raw)
  To: Sebastian Reichel, Marcus Cooper, Jean Delvare, Guenter Roeck
  Cc: linux-pm, linux-hwmon, Linus Walleij

Maintenance charging is the phase of keeping up the charge
after the battery has charged fully using CC/CV charging.

This can be done in many successive phases and is usually
done with a slightly lower constant voltage than CV, and
a slightly lower allowed current.

Add an array of maintenance charging points each with a
current, voltage and safety timer, and add helper functions
to use these. Migrate the AB8500 code over.

This is used in several Samsung products using the AB8500
and these batteries and their complete parameters will
be added later as full examples, but the default battery
in the AB8500 code serves as a reasonable example so far.

Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
 drivers/power/supply/ab8500-bm.h         | 14 ------
 drivers/power/supply/ab8500_bmdata.c     | 27 +++++++---
 drivers/power/supply/ab8500_chargalg.c   | 41 +++++++++++----
 drivers/power/supply/power_supply_core.c | 11 ++++
 include/linux/power_supply.h             | 64 ++++++++++++++++++++++++
 5 files changed, 126 insertions(+), 31 deletions(-)

diff --git a/drivers/power/supply/ab8500-bm.h b/drivers/power/supply/ab8500-bm.h
index 90397f2a731f..67acf4c72569 100644
--- a/drivers/power/supply/ab8500-bm.h
+++ b/drivers/power/supply/ab8500-bm.h
@@ -343,24 +343,12 @@ struct ab8500_maxim_parameters {
  * struct ab8500_battery_type - different batteries supported
  * @resis_high:			battery upper resistance limit
  * @resis_low:			battery lower resistance limit
- * @maint_a_cur_lvl:		charger current in maintenance A state in mA
- * @maint_a_vol_lvl:		charger voltage in maintenance A state in mV
- * @maint_a_chg_timer_h:	charge time in maintenance A state
- * @maint_b_cur_lvl:		charger current in maintenance B state in mA
- * @maint_b_vol_lvl:		charger voltage in maintenance B state in mV
- * @maint_b_chg_timer_h:	charge time in maintenance B state
  * @low_high_cur_lvl:		charger current in temp low/high state in mA
  * @low_high_vol_lvl:		charger voltage in temp low/high state in mV'
  */
 struct ab8500_battery_type {
 	int resis_high;
 	int resis_low;
-	int maint_a_cur_lvl;
-	int maint_a_vol_lvl;
-	int maint_a_chg_timer_h;
-	int maint_b_cur_lvl;
-	int maint_b_vol_lvl;
-	int maint_b_chg_timer_h;
 	int low_high_cur_lvl;
 	int low_high_vol_lvl;
 };
@@ -405,7 +393,6 @@ struct ab8500_bm_charger_parameters {
  * @usb_safety_tmr_h	safety timer for usb charger
  * @bkup_bat_v		voltage which we charge the backup battery with
  * @bkup_bat_i		current which we charge the backup battery with
- * @no_maintenance	indicates that maintenance charging is disabled
  * @capacity_scaling    indicates whether capacity scaling is to be used
  * @ab8500_adc_therm	placement of thermistor, batctrl or battemp adc
  * @chg_unknown_bat	flag to enable charging of unknown batteries
@@ -431,7 +418,6 @@ struct ab8500_bm_data {
 	int usb_safety_tmr_h;
 	int bkup_bat_v;
 	int bkup_bat_i;
-	bool no_maintenance;
 	bool capacity_scaling;
 	bool chg_unknown_bat;
 	bool enable_overshoot;
diff --git a/drivers/power/supply/ab8500_bmdata.c b/drivers/power/supply/ab8500_bmdata.c
index aba459393ee6..fe3a316ce643 100644
--- a/drivers/power/supply/ab8500_bmdata.c
+++ b/drivers/power/supply/ab8500_bmdata.c
@@ -80,16 +80,25 @@ static struct power_supply_resistance_temp_table temp_to_batres_tbl_thermistor[]
 	{ .temp = -20, .resistance = 198 /* 595 mOhm */ },
 };
 
+struct power_supply_maintenance_charge_table maint_charge_table[] = {
+	{
+		/* Maintenance charging phase A, 60 hours */
+		.charge_current_max_ua = 400000,
+		.charge_voltage_max_uv = 4050000,
+		.charge_safety_timer_minutes = 60*60,
+	},
+	{
+		/* Maintenance charging phase B, 200 hours */
+		.charge_current_max_ua = 400000,
+		.charge_voltage_max_uv = 4000000,
+		.charge_safety_timer_minutes = 200*60,
+	}
+};
+
 /* Default battery type for reference designs is the unknown type */
 static struct ab8500_battery_type bat_type_thermistor_unknown = {
 	.resis_high = 0,
 	.resis_low = 0,
-	.maint_a_cur_lvl = 400,
-	.maint_a_vol_lvl = 4050,
-	.maint_a_chg_timer_h = 60,
-	.maint_b_cur_lvl = 400,
-	.maint_b_vol_lvl = 4000,
-	.maint_b_chg_timer_h = 200,
 	.low_high_cur_lvl = 300,
 	.low_high_vol_lvl = 4000,
 };
@@ -146,7 +155,6 @@ struct ab8500_bm_data ab8500_bm_data = {
 	.usb_safety_tmr_h       = 4,
 	.bkup_bat_v             = BUP_VCH_SEL_2P6V,
 	.bkup_bat_i             = BUP_ICH_SEL_150UA,
-	.no_maintenance         = false,
 	.capacity_scaling       = false,
 	.adc_therm              = AB8500_ADC_THERM_BATCTRL,
 	.chg_unknown_bat        = false,
@@ -204,6 +212,11 @@ int ab8500_bm_of_probe(struct power_supply *psy,
 		/* Charging stops when we drop below this current */
 		bi->charge_term_current_ua = 200000;
 
+	if (!bi->maintenance_charge || !bi->maintenance_charge_size) {
+		bi->maintenance_charge = maint_charge_table;
+		bi->maintenance_charge_size = ARRAY_SIZE(maint_charge_table);
+	}
+
 	/*
 	 * Internal resistance and factory resistance are tightly coupled
 	 * so both MUST be defined or we fall back to defaults.
diff --git a/drivers/power/supply/ab8500_chargalg.c b/drivers/power/supply/ab8500_chargalg.c
index 86d740ce3a63..a86b714d4f2c 100644
--- a/drivers/power/supply/ab8500_chargalg.c
+++ b/drivers/power/supply/ab8500_chargalg.c
@@ -484,7 +484,7 @@ static void ab8500_chargalg_stop_safety_timer(struct ab8500_chargalg *di)
 /**
  * ab8500_chargalg_start_maintenance_timer() - Start charging maintenance timer
  * @di:		pointer to the ab8500_chargalg structure
- * @duration:	duration of ther maintenance timer in hours
+ * @duration:	duration of ther maintenance timer in minutes
  *
  * The maintenance timer is used to maintain the charge in the battery once
  * the battery is considered full. These timers are chosen to match the
@@ -493,9 +493,10 @@ static void ab8500_chargalg_stop_safety_timer(struct ab8500_chargalg *di)
 static void ab8500_chargalg_start_maintenance_timer(struct ab8500_chargalg *di,
 	int duration)
 {
+	/* Set a timer in minutes with a 30 second range */
 	hrtimer_set_expires_range(&di->maintenance_timer,
-		ktime_set(duration * ONE_HOUR_IN_SECONDS, 0),
-		ktime_set(FIVE_MINUTES_IN_SECONDS, 0));
+		ktime_set(duration * 60, 0),
+		ktime_set(30, 0));
 	di->events.maintenance_timer_expired = false;
 	hrtimer_start_expires(&di->maintenance_timer, HRTIMER_MODE_REL);
 }
@@ -1300,6 +1301,7 @@ static void ab8500_chargalg_external_power_changed(struct power_supply *psy)
 static void ab8500_chargalg_algorithm(struct ab8500_chargalg *di)
 {
 	struct power_supply_battery_info *bi = &di->bm->bi;
+	struct power_supply_maintenance_charge_table *mt;
 	int charger_status;
 	int ret;
 	int curr_step_lvl_ua;
@@ -1537,7 +1539,12 @@ static void ab8500_chargalg_algorithm(struct ab8500_chargalg *di)
 		handle_maxim_chg_curr(di);
 		if (di->charge_status == POWER_SUPPLY_STATUS_FULL &&
 			di->maintenance_chg) {
-			if (di->bm->no_maintenance)
+			/*
+			 * The battery is fully charged, check if we support
+			 * maintenance charging else go back to waiting for
+			 * the recharge voltage limit.
+			 */
+			if (power_supply_supports_maintenance_charging(bi))
 				ab8500_chargalg_state_to(di,
 					STATE_WAIT_FOR_RECHARGE_INIT);
 			else
@@ -1558,12 +1565,19 @@ static void ab8500_chargalg_algorithm(struct ab8500_chargalg *di)
 		break;
 
 	case STATE_MAINTENANCE_A_INIT:
+		mt = power_supply_get_maintenance_charging_setting(bi, 0);
+		if (!mt) {
+			/* No maintenance A state, go back to normal */
+			ab8500_chargalg_state_to(di, STATE_NORMAL_INIT);
+			power_supply_changed(di->chargalg_psy);
+			break;
+		}
 		ab8500_chargalg_stop_safety_timer(di);
 		ab8500_chargalg_start_maintenance_timer(di,
-			di->bm->bat_type->maint_a_chg_timer_h);
+			mt->charge_safety_timer_minutes);
 		ab8500_chargalg_start_charging(di,
-			di->bm->bat_type->maint_a_vol_lvl,
-			di->bm->bat_type->maint_a_cur_lvl);
+			mt->charge_voltage_max_uv,
+			mt->charge_current_max_ua);
 		ab8500_chargalg_state_to(di, STATE_MAINTENANCE_A);
 		power_supply_changed(di->chargalg_psy);
 		fallthrough;
@@ -1576,11 +1590,18 @@ static void ab8500_chargalg_algorithm(struct ab8500_chargalg *di)
 		break;
 
 	case STATE_MAINTENANCE_B_INIT:
+		mt = power_supply_get_maintenance_charging_setting(bi, 1);
+		if (!mt) {
+			/* No maintenance B state, go back to normal */
+			ab8500_chargalg_state_to(di, STATE_NORMAL_INIT);
+			power_supply_changed(di->chargalg_psy);
+			break;
+		}
 		ab8500_chargalg_start_maintenance_timer(di,
-			di->bm->bat_type->maint_b_chg_timer_h);
+			mt->charge_safety_timer_minutes);
 		ab8500_chargalg_start_charging(di,
-			di->bm->bat_type->maint_b_vol_lvl,
-			di->bm->bat_type->maint_b_cur_lvl);
+			mt->charge_voltage_max_uv,
+			mt->charge_current_max_ua);
 		ab8500_chargalg_state_to(di, STATE_MAINTENANCE_B);
 		power_supply_changed(di->chargalg_psy);
 		fallthrough;
diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c
index a870c3fe032c..b3820f087856 100644
--- a/drivers/power/supply/power_supply_core.c
+++ b/drivers/power/supply/power_supply_core.c
@@ -581,6 +581,7 @@ int power_supply_get_battery_info(struct power_supply *psy,
 	info->charge_term_current_ua         = -EINVAL;
 	info->constant_charge_current_max_ua = -EINVAL;
 	info->constant_charge_voltage_max_uv = -EINVAL;
+	info->maintenance_charge             = NULL;
 	info->temp_ambient_alert_min         = INT_MIN;
 	info->temp_ambient_alert_max         = INT_MAX;
 	info->temp_alert_min                 = INT_MIN;
@@ -858,6 +859,16 @@ int power_supply_ntc_resist2temp_simple(struct power_supply_battery_info *info,
 }
 EXPORT_SYMBOL_GPL(power_supply_ntc_resist2temp_simple);
 
+struct power_supply_maintenance_charge_table *
+power_supply_get_maintenance_charging_setting(struct power_supply_battery_info *info,
+					      int index)
+{
+	if (index >= info->maintenance_charge_size)
+		return NULL;
+	return &info->maintenance_charge[index];
+}
+EXPORT_SYMBOL_GPL(power_supply_get_maintenance_charging_setting);
+
 /**
  * power_supply_ocv2cap_simple() - find the battery capacity
  * @table: Pointer to battery OCV lookup table
diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
index a0da806185b5..c6f379715cb1 100644
--- a/include/linux/power_supply.h
+++ b/include/linux/power_supply.h
@@ -358,6 +358,52 @@ struct power_supply_ntc_resistance_temp_table {
 	int temp;
 };
 
+/**
+ * struct power_supply_maintenance_charge_table - setting for maintenace charging
+ * @charge_current_max_ua: maintenance charging current that is used to keep
+ *   the charge of the battery full as current is consumed after full charging.
+ *   The corresponding charge_voltage_max_uv is used as a safeguard: when we
+ *   reach this voltage the maintenance charging current is turned off. It is
+ *   turned back on if we fall below this voltage.
+ * @charge_voltage_max_uv: maintenance charging voltage that is usually a bit
+ *   lower than the constant_charge_voltage_max_uv. We can apply this settings
+ *   charge_current_max_ua until we get back up to this voltage.
+ * @safety_timer_minutes: maintenance charging safety timer, with an expiry
+ *   time in minutes. We will only use maintenance charging in this setting
+ *   for a certain amount of time, then we will first move to the next
+ *   maintenance charge current and voltage pair in respective array and wait
+ *   for the next safety timer timeout, or, if we reached the last maintencance
+ *   charging setting, disable charging until we reach
+ *   charge_restart_voltage_uv and restart ordinary CC/CV charging from there.
+ *   These timers should be chosen to align with the typical discharge curve
+ *   for the battery.
+ *
+ * When the main CC/CV charging is complete the battery can optionally be
+ * maintenance charged at the voltages from this table: a table of settings is
+ * traversed using a slightly lower current and voltage than what is used for
+ * CC/CV charging. The maintenance charging will for safety reasons not go on
+ * indefinately: we lower the current and voltage with successive maintenance
+ * settings, then disable charging completely after we reach the last one,
+ * and after that we do not restart charging until we reach
+ * charge_restart_voltage_uv (see struct power_supply_battery_info) and restart
+ * ordinary CC/CV charging from there.
+ *
+ * As an example, a Samsung EB425161LA Lithium-Ion battery is CC/CV charged
+ * at 900mA to 4340mV, then maintenance charged at 600mA and 4150mV for
+ * 60 hours, then maintenance charged at 600mA and 4100mV for 200 hours.
+ * After this the charge cycle is restarted waiting for
+ * charge_restart_voltage_uv.
+ *
+ * For most mobile electronics this type of maintenance charging is enough for
+ * the user to disconnect the device and make use of it before both maintenance
+ * charging cycles are complete.
+ */
+struct power_supply_maintenance_charge_table {
+	int charge_current_max_ua;
+	int charge_voltage_max_uv;
+	int charge_safety_timer_minutes;
+};
+
 #define POWER_SUPPLY_OCV_TEMP_MAX 20
 
 /**
@@ -403,6 +449,10 @@ struct power_supply_ntc_resistance_temp_table {
  * @constant_charge_voltage_max_uv: voltage in microvolts signifying the end of
  *   the CC (constant current) charging phase and the beginning of the CV
  *   (constant voltage) charging phase.
+ * @maintenance_charge: an array of maintenance charging settings to be used
+ *   after the main CC/CV charging phase is complete.
+ * @maintenance_charge_size: the number of maintenance charging settings in
+ *   maintenance_charge.
  * @factory_internal_resistance_uohm: the internal resistance of the battery
  *   at fabrication time, expressed in microohms. This resistance will vary
  *   depending on the lifetime and charge of the battery, so this is just a
@@ -564,6 +614,8 @@ struct power_supply_battery_info {
 	int overvoltage_limit_uv;
 	int constant_charge_current_max_ua;
 	int constant_charge_voltage_max_uv;
+	struct power_supply_maintenance_charge_table *maintenance_charge;
+	int maintenance_charge_size;
 	int factory_internal_resistance_uohm;
 	int ocv_temp[POWER_SUPPLY_OCV_TEMP_MAX];
 	int temp_ambient_alert_min;
@@ -621,12 +673,24 @@ power_supply_temp2resist_simple(struct power_supply_resistance_temp_table *table
 				int table_len, int temp);
 extern int power_supply_ntc_resist2temp_simple(struct power_supply_battery_info *info,
 					       int resistance_ohm);
+extern struct power_supply_maintenance_charge_table *
+power_supply_get_maintenance_charging_setting(struct power_supply_battery_info *info, int index);
 extern void power_supply_changed(struct power_supply *psy);
 extern int power_supply_am_i_supplied(struct power_supply *psy);
 extern int power_supply_set_input_current_limit_from_supplier(
 					 struct power_supply *psy);
 extern int power_supply_set_battery_charged(struct power_supply *psy);
 
+static inline bool
+power_supply_supports_maintenance_charging(struct power_supply_battery_info *info)
+{
+	struct power_supply_maintenance_charge_table *mt;
+
+	mt = power_supply_get_maintenance_charging_setting(info, 0);
+
+	return (mt != NULL);
+}
+
 #ifdef CONFIG_POWER_SUPPLY
 extern int power_supply_is_system_supplied(void);
 #else
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH 0/2] More AB8500 charging props
  2021-11-22 23:41 [PATCH 0/2] More AB8500 charging props Linus Walleij
  2021-11-22 23:41 ` [PATCH 1/2] power: supply: ab8500: Standardize NTC battery temp Linus Walleij
  2021-11-22 23:41 ` [PATCH 2/2] power: supply: ab8500: Standardize maintenance charging Linus Walleij
@ 2021-11-23  0:23 ` Guenter Roeck
  2 siblings, 0 replies; 8+ messages in thread
From: Guenter Roeck @ 2021-11-23  0:23 UTC (permalink / raw)
  To: Linus Walleij, Sebastian Reichel, Marcus Cooper, Jean Delvare
  Cc: linux-pm, linux-hwmon

On 11/22/21 3:41 PM, Linus Walleij wrote:
> These two patches begin to put new stuff into the
> drivers/power/supply/ab8500_bmdata.c for:
> 
> A) battery temperature look-up and interpolation, and
> B) maintenance charging
> 
> Some design choices can be discussed, so I included some
> HWMON etc maintainers.
> 
> NTC resistor:
> 
> For the battery NTC temperature we should note the existing NTC
> driver in drivers/hwmon/ntc_thermistor.c with bindings
> in Documentation/devicetree/bindings/hwmon/ntc-thermistor.yaml
> which is used for stand-alone NTC resistors.
> 
> It is probably possible to try to reuse the hwmon code but I
> wanted to see if we have buy-in from the hwmon maintainer first.
> 
Go ahead; anything that reduces code duplication is desirable.

Guenter

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 1/2] power: supply: ab8500: Standardize NTC battery temp
  2021-11-22 23:41 ` [PATCH 1/2] power: supply: ab8500: Standardize NTC battery temp Linus Walleij
@ 2021-12-03 20:53   ` Sebastian Reichel
  2021-12-06  0:33     ` Linus Walleij
  0 siblings, 1 reply; 8+ messages in thread
From: Sebastian Reichel @ 2021-12-03 20:53 UTC (permalink / raw)
  To: Linus Walleij
  Cc: Marcus Cooper, Jean Delvare, Guenter Roeck, linux-pm, linux-hwmon

[-- Attachment #1: Type: text/plain, Size: 14313 bytes --]

Hi,

On Tue, Nov 23, 2021 at 12:41:40AM +0100, Linus Walleij wrote:
> Several batteries monitor the temperature of the battery using
> an NTC resistor. Add an NTC resistor resistance to temperature
> look-up table to struct power_supply_battery_info and use this
> in the AB8500 battery temperature driver to look up the battery
> temperature.
> 
> Add a helper function in the power supply core:
> power_supply_ntc_resist2temp_simple(), which can use the
> NTC resistance table to look up and interpolate a temperature
> from the table.
> 
> Fix up some of the confusing wording on the temperature to
> internal resistance table, and clarify what each resistance
> table is for.
> 
> The AB8500 default NTC resistor is 47KOhm at 25 degrees
> Celsius and serves as a good example of how this usually works.
> 
> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
> ---

This LGTM, but let's wait a bit to synchronize with the work
happening in the AXP driver.

-- Sebastian

>  drivers/power/supply/ab8500-bm.h         | 16 --------
>  drivers/power/supply/ab8500_bmdata.c     | 41 ++++++++++---------
>  drivers/power/supply/ab8500_btemp.c      | 45 ++------------------
>  drivers/power/supply/power_supply_core.c | 52 ++++++++++++++++++++++++
>  include/linux/power_supply.h             | 34 ++++++++++++++++
>  5 files changed, 111 insertions(+), 77 deletions(-)
> 
> diff --git a/drivers/power/supply/ab8500-bm.h b/drivers/power/supply/ab8500-bm.h
> index 57e1a8e27e51..90397f2a731f 100644
> --- a/drivers/power/supply/ab8500-bm.h
> +++ b/drivers/power/supply/ab8500-bm.h
> @@ -272,18 +272,6 @@ enum ab8500_adc_therm {
>  	AB8500_ADC_THERM_BATTEMP,
>  };
>  
> -/**
> - * struct ab8500_res_to_temp - defines one point in a temp to res curve. To
> - * be used in battery packs that combines the identification resistor with a
> - * NTC resistor.
> - * @temp:			battery pack temperature in Celsius
> - * @resist:			NTC resistor net total resistance
> - */
> -struct ab8500_res_to_temp {
> -	int temp;
> -	int resist;
> -};
> -
>  /* Forward declaration */
>  struct ab8500_fg;
>  
> @@ -363,8 +351,6 @@ struct ab8500_maxim_parameters {
>   * @maint_b_chg_timer_h:	charge time in maintenance B state
>   * @low_high_cur_lvl:		charger current in temp low/high state in mA
>   * @low_high_vol_lvl:		charger voltage in temp low/high state in mV'
> - * @n_r_t_tbl_elements:		number of elements in r_to_t_tbl
> - * @r_to_t_tbl:			table containing resistance to temp points
>   */
>  struct ab8500_battery_type {
>  	int resis_high;
> @@ -377,8 +363,6 @@ struct ab8500_battery_type {
>  	int maint_b_chg_timer_h;
>  	int low_high_cur_lvl;
>  	int low_high_vol_lvl;
> -	int n_temp_tbl_elements;
> -	const struct ab8500_res_to_temp *r_to_t_tbl;
>  };
>  
>  /**
> diff --git a/drivers/power/supply/ab8500_bmdata.c b/drivers/power/supply/ab8500_bmdata.c
> index 62953f9cb85a..aba459393ee6 100644
> --- a/drivers/power/supply/ab8500_bmdata.c
> +++ b/drivers/power/supply/ab8500_bmdata.c
> @@ -44,25 +44,25 @@ static struct power_supply_battery_ocv_table ocv_cap_tbl[] = {
>  };
>  
>  /*
> - * Note that the res_to_temp table must be strictly sorted by falling
> + * Note that the ntc_res_to_temp_tbl table must be strictly sorted by falling
>   * resistance values to work.
>   */
> -static const struct ab8500_res_to_temp temp_tbl[] = {
> -	{-5, 214834},
> -	{ 0, 162943},
> -	{ 5, 124820},
> -	{10,  96520},
> -	{15,  75306},
> -	{20,  59254},
> -	{25,  47000},
> -	{30,  37566},
> -	{35,  30245},
> -	{40,  24520},
> -	{45,  20010},
> -	{50,  16432},
> -	{55,  13576},
> -	{60,  11280},
> -	{65,   9425},
> +static struct power_supply_ntc_resistance_temp_table ntc_res_to_temp_tbl[] = {
> +	{ .resistance_ohm = 214834, .temp = -5},
> +	{ .resistance_ohm = 162943, .temp = 0},
> +	{ .resistance_ohm = 124820, .temp = 5},
> +	{ .resistance_ohm = 96520, .temp = 10},
> +	{ .resistance_ohm = 75306, .temp = 15},
> +	{ .resistance_ohm = 59254, .temp = 20},
> +	{ .resistance_ohm = 47000, .temp = 25},
> +	{ .resistance_ohm = 37566, .temp = 30},
> +	{ .resistance_ohm = 30245, .temp = 35},
> +	{ .resistance_ohm = 24520, .temp = 40},
> +	{ .resistance_ohm = 20010, .temp = 45},
> +	{ .resistance_ohm = 16432, .temp = 50},
> +	{ .resistance_ohm = 13576, .temp = 55},
> +	{ .resistance_ohm = 11280, .temp = 60},
> +	{ .resistance_ohm = 9425, .temp = 65},
>  };
>  
>  /*
> @@ -92,8 +92,6 @@ static struct ab8500_battery_type bat_type_thermistor_unknown = {
>  	.maint_b_chg_timer_h = 200,
>  	.low_high_cur_lvl = 300,
>  	.low_high_vol_lvl = 4000,
> -	.n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
> -	.r_to_t_tbl = temp_tbl,
>  };
>  
>  static const struct ab8500_bm_capacity_levels cap_levels = {
> @@ -217,6 +215,11 @@ int ab8500_bm_of_probe(struct power_supply *psy,
>  		bi->resist_table_size = ARRAY_SIZE(temp_to_batres_tbl_thermistor);
>  	}
>  
> +	if (!bi->ntc_resist_table) {
> +		bi->ntc_resist_table = ntc_res_to_temp_tbl;
> +		bi->ntc_resist_table_size = ARRAY_SIZE(ntc_res_to_temp_tbl);
> +	}
> +
>  	if (!bi->ocv_table[0]) {
>  		/* Default capacity table at say 25 degrees Celsius */
>  		bi->ocv_temp[0] = 25;
> diff --git a/drivers/power/supply/ab8500_btemp.c b/drivers/power/supply/ab8500_btemp.c
> index 20253b8a7fe9..79528c18618d 100644
> --- a/drivers/power/supply/ab8500_btemp.c
> +++ b/drivers/power/supply/ab8500_btemp.c
> @@ -407,42 +407,6 @@ static int ab8500_btemp_get_batctrl_res(struct ab8500_btemp *di)
>  	return res;
>  }
>  
> -/**
> - * ab8500_btemp_res_to_temp() - resistance to temperature
> - * @di:		pointer to the ab8500_btemp structure
> - * @tbl:	pointer to the resiatance to temperature table
> - * @tbl_size:	size of the resistance to temperature table
> - * @res:	resistance to calculate the temperature from
> - *
> - * This function returns the battery temperature in degrees Celsius
> - * based on the NTC resistance.
> - */
> -static int ab8500_btemp_res_to_temp(struct ab8500_btemp *di,
> -	const struct ab8500_res_to_temp *tbl, int tbl_size, int res)
> -{
> -	int i;
> -	/*
> -	 * Calculate the formula for the straight line
> -	 * Simple interpolation if we are within
> -	 * the resistance table limits, extrapolate
> -	 * if resistance is outside the limits.
> -	 */
> -	if (res > tbl[0].resist)
> -		i = 0;
> -	else if (res <= tbl[tbl_size - 1].resist)
> -		i = tbl_size - 2;
> -	else {
> -		i = 0;
> -		while (!(res <= tbl[i].resist &&
> -			res > tbl[i + 1].resist))
> -			i++;
> -	}
> -
> -	return fixp_linear_interpolate(tbl[i].resist, tbl[i].temp,
> -				       tbl[i + 1].resist, tbl[i + 1].temp,
> -				       res);
> -}
> -
>  /**
>   * ab8500_btemp_measure_temp() - measure battery temperature
>   * @di:		pointer to the ab8500_btemp structure
> @@ -451,6 +415,7 @@ static int ab8500_btemp_res_to_temp(struct ab8500_btemp *di,
>   */
>  static int ab8500_btemp_measure_temp(struct ab8500_btemp *di)
>  {
> +	struct power_supply_battery_info *bi = &di->bm->bi;
>  	int temp, ret;
>  	static int prev;
>  	int rbat, rntc, vntc;
> @@ -469,9 +434,7 @@ static int ab8500_btemp_measure_temp(struct ab8500_btemp *di)
>  			return BTEMP_THERMAL_LOW_LIMIT;
>  		}
>  
> -		temp = ab8500_btemp_res_to_temp(di,
> -			di->bm->bat_type->r_to_t_tbl,
> -			di->bm->bat_type->n_temp_tbl_elements, rbat);
> +		temp = power_supply_ntc_resist2temp_simple(bi, rbat);
>  	} else {
>  		ret = iio_read_channel_processed(di->btemp_ball, &vntc);
>  		if (ret < 0) {
> @@ -486,9 +449,7 @@ static int ab8500_btemp_measure_temp(struct ab8500_btemp *di)
>  		 */
>  		rntc = 230000 * vntc / (VTVOUT_V - vntc);
>  
> -		temp = ab8500_btemp_res_to_temp(di,
> -			di->bm->bat_type->r_to_t_tbl,
> -			di->bm->bat_type->n_temp_tbl_elements, rntc);
> +		temp = power_supply_ntc_resist2temp_simple(bi, rntc);
>  		prev = temp;
>  	}
>  	dev_dbg(di->dev, "Battery temperature is %d\n", temp);
> diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c
> index 2723d7d0ced3..a870c3fe032c 100644
> --- a/drivers/power/supply/power_supply_core.c
> +++ b/drivers/power/supply/power_supply_core.c
> @@ -589,6 +589,7 @@ int power_supply_get_battery_info(struct power_supply *psy,
>  	info->temp_max                       = INT_MAX;
>  	info->factory_internal_resistance_uohm  = -EINVAL;
>  	info->resist_table = NULL;
> +	info->ntc_resist_table = NULL;
>  
>  	for (index = 0; index < POWER_SUPPLY_OCV_TEMP_MAX; index++) {
>  		info->ocv_table[index]       = NULL;
> @@ -806,6 +807,57 @@ int power_supply_temp2resist_simple(struct power_supply_resistance_temp_table *t
>  }
>  EXPORT_SYMBOL_GPL(power_supply_temp2resist_simple);
>  
> +/**
> + * power_supply_ntc_resist2temp_simple() - find the battery temperature
> + * @info: battery information
> + * @resistance_ohm: Current NTC resistance value in microohm
> + *
> + * This helper function is used to look up battery internal temperature
> + * according to current NTC resistance value from one NTC resistance table.
> + * The NTC resistance table must be ordered descending by resistance:
> + * largest resistance with lowest temperature first, lowest resistance with
> + * highest temperature last. The function will interpolate to find the
> + * corresponding temperature.
> + *
> + * Return: the battery temperature.
> + */
> +int power_supply_ntc_resist2temp_simple(struct power_supply_battery_info *info,
> +					int resistance_ohm)
> +{
> +	struct power_supply_ntc_resistance_temp_table *table;
> +	int i, high, low;
> +	int table_len;
> +
> +	table = info->ntc_resist_table;
> +	table_len = info->ntc_resist_table_size;
> +
> +	if (!table || !table_len) {
> +		pr_err("Empty battery NTC resistance table, assume 25 degrees\n");
> +		return 25;
> +	}
> +	if (!resistance_ohm)
> +		pr_info("Battery NTC resistance 0, this is unlikely\n");
> +
> +	/* Break loop at table_len - 1 because that is the highest index */
> +	for (i = 0; i < table_len - 1; i++)
> +		if (resistance_ohm > table[i].resistance_ohm)
> +			break;
> +
> +	/* The library function will deal with high == low */
> +	if ((i == 0) || (i == (table_len - 1)))
> +		high = i;
> +	else
> +		high = i - 1;
> +	low = i;
> +
> +	return fixp_linear_interpolate(table[low].resistance_ohm,
> +				       table[low].temp,
> +				       table[high].resistance_ohm,
> +				       table[high].temp,
> +				       resistance_ohm);
> +}
> +EXPORT_SYMBOL_GPL(power_supply_ntc_resist2temp_simple);
> +
>  /**
>   * power_supply_ocv2cap_simple() - find the battery capacity
>   * @table: Pointer to battery OCV lookup table
> diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
> index b5079109ac00..a0da806185b5 100644
> --- a/include/linux/power_supply.h
> +++ b/include/linux/power_supply.h
> @@ -335,11 +335,29 @@ struct power_supply_battery_ocv_table {
>  	int capacity;	/* percent */
>  };
>  
> +/**
> + * struct power_supply_resistance_temp_table - correlate temperature to resistance
> + * @temp: the internal temperature of the battery in degrees Celsius
> + * @resistance: the percentage of the factory internal resistance at this
> + *   temperature, usually nomimal factory resistance is 100 percent at 25
> + *   degrees Celsius, lower at higher temperature and higher at lower
> + *   temperature.
> + */
>  struct power_supply_resistance_temp_table {
>  	int temp;	/* celsius */
>  	int resistance;	/* internal resistance percent */
>  };
>  
> +/**
> + * struct power_supply_ntc_resistance_temp_table - correlate NTC to temp
> + * @resistance: the NTC resistance in ohm
> + * @temp: the corresponding temperature in degrees Celsius
> + */
> +struct power_supply_ntc_resistance_temp_table {
> +	int resistance_ohm;
> +	int temp;
> +};
> +
>  #define POWER_SUPPLY_OCV_TEMP_MAX 20
>  
>  /**
> @@ -426,6 +444,18 @@ struct power_supply_resistance_temp_table {
>   *   by temperature: highest temperature with lowest resistance first, lowest
>   *   temperature with highest resistance last.
>   * @resist_table_size: the number of items in the resist_table.
> + * @ntc_resist_table: this is a table that correlates a resistance of a negative
> + *   temperature coefficient (NTC) resistor to an internal temperature of a
> + *   battery. This can be achieved by a separate thermistor to
> + *   supply voltage on a third terminal on a battery which is the most
> + *   reliable. An external thermistor can also be used sometimes. Knowing the
> + *   temperature of the battery is usually necessary to perform a lookup in the
> + *   resist_table to determine the internal resistance of the battery, and
> + *   to find the right ocv_table to determine the capacity of the battery.
> + *   The NTC resistance table must be ordered descending by resistance:
> + *   largest resistance with lowest temperature first, lowest resistance with
> + *   highest temperature last.
> + * @ntc_resist_table_size: the number of items in the ntc_resist_table.
>   *
>   * This is the recommended struct to manage static battery parameters,
>   * populated by power_supply_get_battery_info(). Most platform drivers should
> @@ -546,6 +576,8 @@ struct power_supply_battery_info {
>  	int ocv_table_size[POWER_SUPPLY_OCV_TEMP_MAX];
>  	struct power_supply_resistance_temp_table *resist_table;
>  	int resist_table_size;
> +	struct power_supply_ntc_resistance_temp_table *ntc_resist_table;
> +	int ntc_resist_table_size;
>  };
>  
>  extern struct atomic_notifier_head power_supply_notifier;
> @@ -587,6 +619,8 @@ extern int power_supply_batinfo_ocv2cap(struct power_supply_battery_info *info,
>  extern int
>  power_supply_temp2resist_simple(struct power_supply_resistance_temp_table *table,
>  				int table_len, int temp);
> +extern int power_supply_ntc_resist2temp_simple(struct power_supply_battery_info *info,
> +					       int resistance_ohm);
>  extern void power_supply_changed(struct power_supply *psy);
>  extern int power_supply_am_i_supplied(struct power_supply *psy);
>  extern int power_supply_set_input_current_limit_from_supplier(
> -- 
> 2.31.1
> 

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 2/2] power: supply: ab8500: Standardize maintenance charging
  2021-11-22 23:41 ` [PATCH 2/2] power: supply: ab8500: Standardize maintenance charging Linus Walleij
@ 2021-12-03 21:02   ` Sebastian Reichel
  2021-12-06  0:37     ` Linus Walleij
  0 siblings, 1 reply; 8+ messages in thread
From: Sebastian Reichel @ 2021-12-03 21:02 UTC (permalink / raw)
  To: Linus Walleij
  Cc: Marcus Cooper, Jean Delvare, Guenter Roeck, linux-pm, linux-hwmon

[-- Attachment #1: Type: text/plain, Size: 16690 bytes --]

Hi,

On Tue, Nov 23, 2021 at 12:41:41AM +0100, Linus Walleij wrote:
> Maintenance charging is the phase of keeping up the charge
> after the battery has charged fully using CC/CV charging.
> 
> This can be done in many successive phases and is usually
> done with a slightly lower constant voltage than CV, and
> a slightly lower allowed current.
> 
> Add an array of maintenance charging points each with a
> current, voltage and safety timer, and add helper functions
> to use these. Migrate the AB8500 code over.
> 
> This is used in several Samsung products using the AB8500
> and these batteries and their complete parameters will
> be added later as full examples, but the default battery
> in the AB8500 code serves as a reasonable example so far.
> 
> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
> ---

I wonder if this will ever be used by any other driver. Having
multiple maintanence states seems to be very specific to the
ab8500 driver stack. But the code itself looks ok and considering
this does not expose any (potentially unfixable) userspace ABI
and improves the AB8500 mess I'm fine with it.

-- Sebastian

>  drivers/power/supply/ab8500-bm.h         | 14 ------
>  drivers/power/supply/ab8500_bmdata.c     | 27 +++++++---
>  drivers/power/supply/ab8500_chargalg.c   | 41 +++++++++++----
>  drivers/power/supply/power_supply_core.c | 11 ++++
>  include/linux/power_supply.h             | 64 ++++++++++++++++++++++++
>  5 files changed, 126 insertions(+), 31 deletions(-)
> 
> diff --git a/drivers/power/supply/ab8500-bm.h b/drivers/power/supply/ab8500-bm.h
> index 90397f2a731f..67acf4c72569 100644
> --- a/drivers/power/supply/ab8500-bm.h
> +++ b/drivers/power/supply/ab8500-bm.h
> @@ -343,24 +343,12 @@ struct ab8500_maxim_parameters {
>   * struct ab8500_battery_type - different batteries supported
>   * @resis_high:			battery upper resistance limit
>   * @resis_low:			battery lower resistance limit
> - * @maint_a_cur_lvl:		charger current in maintenance A state in mA
> - * @maint_a_vol_lvl:		charger voltage in maintenance A state in mV
> - * @maint_a_chg_timer_h:	charge time in maintenance A state
> - * @maint_b_cur_lvl:		charger current in maintenance B state in mA
> - * @maint_b_vol_lvl:		charger voltage in maintenance B state in mV
> - * @maint_b_chg_timer_h:	charge time in maintenance B state
>   * @low_high_cur_lvl:		charger current in temp low/high state in mA
>   * @low_high_vol_lvl:		charger voltage in temp low/high state in mV'
>   */
>  struct ab8500_battery_type {
>  	int resis_high;
>  	int resis_low;
> -	int maint_a_cur_lvl;
> -	int maint_a_vol_lvl;
> -	int maint_a_chg_timer_h;
> -	int maint_b_cur_lvl;
> -	int maint_b_vol_lvl;
> -	int maint_b_chg_timer_h;
>  	int low_high_cur_lvl;
>  	int low_high_vol_lvl;
>  };
> @@ -405,7 +393,6 @@ struct ab8500_bm_charger_parameters {
>   * @usb_safety_tmr_h	safety timer for usb charger
>   * @bkup_bat_v		voltage which we charge the backup battery with
>   * @bkup_bat_i		current which we charge the backup battery with
> - * @no_maintenance	indicates that maintenance charging is disabled
>   * @capacity_scaling    indicates whether capacity scaling is to be used
>   * @ab8500_adc_therm	placement of thermistor, batctrl or battemp adc
>   * @chg_unknown_bat	flag to enable charging of unknown batteries
> @@ -431,7 +418,6 @@ struct ab8500_bm_data {
>  	int usb_safety_tmr_h;
>  	int bkup_bat_v;
>  	int bkup_bat_i;
> -	bool no_maintenance;
>  	bool capacity_scaling;
>  	bool chg_unknown_bat;
>  	bool enable_overshoot;
> diff --git a/drivers/power/supply/ab8500_bmdata.c b/drivers/power/supply/ab8500_bmdata.c
> index aba459393ee6..fe3a316ce643 100644
> --- a/drivers/power/supply/ab8500_bmdata.c
> +++ b/drivers/power/supply/ab8500_bmdata.c
> @@ -80,16 +80,25 @@ static struct power_supply_resistance_temp_table temp_to_batres_tbl_thermistor[]
>  	{ .temp = -20, .resistance = 198 /* 595 mOhm */ },
>  };
>  
> +struct power_supply_maintenance_charge_table maint_charge_table[] = {
> +	{
> +		/* Maintenance charging phase A, 60 hours */
> +		.charge_current_max_ua = 400000,
> +		.charge_voltage_max_uv = 4050000,
> +		.charge_safety_timer_minutes = 60*60,
> +	},
> +	{
> +		/* Maintenance charging phase B, 200 hours */
> +		.charge_current_max_ua = 400000,
> +		.charge_voltage_max_uv = 4000000,
> +		.charge_safety_timer_minutes = 200*60,
> +	}
> +};
> +
>  /* Default battery type for reference designs is the unknown type */
>  static struct ab8500_battery_type bat_type_thermistor_unknown = {
>  	.resis_high = 0,
>  	.resis_low = 0,
> -	.maint_a_cur_lvl = 400,
> -	.maint_a_vol_lvl = 4050,
> -	.maint_a_chg_timer_h = 60,
> -	.maint_b_cur_lvl = 400,
> -	.maint_b_vol_lvl = 4000,
> -	.maint_b_chg_timer_h = 200,
>  	.low_high_cur_lvl = 300,
>  	.low_high_vol_lvl = 4000,
>  };
> @@ -146,7 +155,6 @@ struct ab8500_bm_data ab8500_bm_data = {
>  	.usb_safety_tmr_h       = 4,
>  	.bkup_bat_v             = BUP_VCH_SEL_2P6V,
>  	.bkup_bat_i             = BUP_ICH_SEL_150UA,
> -	.no_maintenance         = false,
>  	.capacity_scaling       = false,
>  	.adc_therm              = AB8500_ADC_THERM_BATCTRL,
>  	.chg_unknown_bat        = false,
> @@ -204,6 +212,11 @@ int ab8500_bm_of_probe(struct power_supply *psy,
>  		/* Charging stops when we drop below this current */
>  		bi->charge_term_current_ua = 200000;
>  
> +	if (!bi->maintenance_charge || !bi->maintenance_charge_size) {
> +		bi->maintenance_charge = maint_charge_table;
> +		bi->maintenance_charge_size = ARRAY_SIZE(maint_charge_table);
> +	}
> +
>  	/*
>  	 * Internal resistance and factory resistance are tightly coupled
>  	 * so both MUST be defined or we fall back to defaults.
> diff --git a/drivers/power/supply/ab8500_chargalg.c b/drivers/power/supply/ab8500_chargalg.c
> index 86d740ce3a63..a86b714d4f2c 100644
> --- a/drivers/power/supply/ab8500_chargalg.c
> +++ b/drivers/power/supply/ab8500_chargalg.c
> @@ -484,7 +484,7 @@ static void ab8500_chargalg_stop_safety_timer(struct ab8500_chargalg *di)
>  /**
>   * ab8500_chargalg_start_maintenance_timer() - Start charging maintenance timer
>   * @di:		pointer to the ab8500_chargalg structure
> - * @duration:	duration of ther maintenance timer in hours
> + * @duration:	duration of ther maintenance timer in minutes
>   *
>   * The maintenance timer is used to maintain the charge in the battery once
>   * the battery is considered full. These timers are chosen to match the
> @@ -493,9 +493,10 @@ static void ab8500_chargalg_stop_safety_timer(struct ab8500_chargalg *di)
>  static void ab8500_chargalg_start_maintenance_timer(struct ab8500_chargalg *di,
>  	int duration)
>  {
> +	/* Set a timer in minutes with a 30 second range */
>  	hrtimer_set_expires_range(&di->maintenance_timer,
> -		ktime_set(duration * ONE_HOUR_IN_SECONDS, 0),
> -		ktime_set(FIVE_MINUTES_IN_SECONDS, 0));
> +		ktime_set(duration * 60, 0),
> +		ktime_set(30, 0));
>  	di->events.maintenance_timer_expired = false;
>  	hrtimer_start_expires(&di->maintenance_timer, HRTIMER_MODE_REL);
>  }
> @@ -1300,6 +1301,7 @@ static void ab8500_chargalg_external_power_changed(struct power_supply *psy)
>  static void ab8500_chargalg_algorithm(struct ab8500_chargalg *di)
>  {
>  	struct power_supply_battery_info *bi = &di->bm->bi;
> +	struct power_supply_maintenance_charge_table *mt;
>  	int charger_status;
>  	int ret;
>  	int curr_step_lvl_ua;
> @@ -1537,7 +1539,12 @@ static void ab8500_chargalg_algorithm(struct ab8500_chargalg *di)
>  		handle_maxim_chg_curr(di);
>  		if (di->charge_status == POWER_SUPPLY_STATUS_FULL &&
>  			di->maintenance_chg) {
> -			if (di->bm->no_maintenance)
> +			/*
> +			 * The battery is fully charged, check if we support
> +			 * maintenance charging else go back to waiting for
> +			 * the recharge voltage limit.
> +			 */
> +			if (power_supply_supports_maintenance_charging(bi))
>  				ab8500_chargalg_state_to(di,
>  					STATE_WAIT_FOR_RECHARGE_INIT);
>  			else
> @@ -1558,12 +1565,19 @@ static void ab8500_chargalg_algorithm(struct ab8500_chargalg *di)
>  		break;
>  
>  	case STATE_MAINTENANCE_A_INIT:
> +		mt = power_supply_get_maintenance_charging_setting(bi, 0);
> +		if (!mt) {
> +			/* No maintenance A state, go back to normal */
> +			ab8500_chargalg_state_to(di, STATE_NORMAL_INIT);
> +			power_supply_changed(di->chargalg_psy);
> +			break;
> +		}
>  		ab8500_chargalg_stop_safety_timer(di);
>  		ab8500_chargalg_start_maintenance_timer(di,
> -			di->bm->bat_type->maint_a_chg_timer_h);
> +			mt->charge_safety_timer_minutes);
>  		ab8500_chargalg_start_charging(di,
> -			di->bm->bat_type->maint_a_vol_lvl,
> -			di->bm->bat_type->maint_a_cur_lvl);
> +			mt->charge_voltage_max_uv,
> +			mt->charge_current_max_ua);
>  		ab8500_chargalg_state_to(di, STATE_MAINTENANCE_A);
>  		power_supply_changed(di->chargalg_psy);
>  		fallthrough;
> @@ -1576,11 +1590,18 @@ static void ab8500_chargalg_algorithm(struct ab8500_chargalg *di)
>  		break;
>  
>  	case STATE_MAINTENANCE_B_INIT:
> +		mt = power_supply_get_maintenance_charging_setting(bi, 1);
> +		if (!mt) {
> +			/* No maintenance B state, go back to normal */
> +			ab8500_chargalg_state_to(di, STATE_NORMAL_INIT);
> +			power_supply_changed(di->chargalg_psy);
> +			break;
> +		}
>  		ab8500_chargalg_start_maintenance_timer(di,
> -			di->bm->bat_type->maint_b_chg_timer_h);
> +			mt->charge_safety_timer_minutes);
>  		ab8500_chargalg_start_charging(di,
> -			di->bm->bat_type->maint_b_vol_lvl,
> -			di->bm->bat_type->maint_b_cur_lvl);
> +			mt->charge_voltage_max_uv,
> +			mt->charge_current_max_ua);
>  		ab8500_chargalg_state_to(di, STATE_MAINTENANCE_B);
>  		power_supply_changed(di->chargalg_psy);
>  		fallthrough;
> diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c
> index a870c3fe032c..b3820f087856 100644
> --- a/drivers/power/supply/power_supply_core.c
> +++ b/drivers/power/supply/power_supply_core.c
> @@ -581,6 +581,7 @@ int power_supply_get_battery_info(struct power_supply *psy,
>  	info->charge_term_current_ua         = -EINVAL;
>  	info->constant_charge_current_max_ua = -EINVAL;
>  	info->constant_charge_voltage_max_uv = -EINVAL;
> +	info->maintenance_charge             = NULL;
>  	info->temp_ambient_alert_min         = INT_MIN;
>  	info->temp_ambient_alert_max         = INT_MAX;
>  	info->temp_alert_min                 = INT_MIN;
> @@ -858,6 +859,16 @@ int power_supply_ntc_resist2temp_simple(struct power_supply_battery_info *info,
>  }
>  EXPORT_SYMBOL_GPL(power_supply_ntc_resist2temp_simple);
>  
> +struct power_supply_maintenance_charge_table *
> +power_supply_get_maintenance_charging_setting(struct power_supply_battery_info *info,
> +					      int index)
> +{
> +	if (index >= info->maintenance_charge_size)
> +		return NULL;
> +	return &info->maintenance_charge[index];
> +}
> +EXPORT_SYMBOL_GPL(power_supply_get_maintenance_charging_setting);
> +
>  /**
>   * power_supply_ocv2cap_simple() - find the battery capacity
>   * @table: Pointer to battery OCV lookup table
> diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
> index a0da806185b5..c6f379715cb1 100644
> --- a/include/linux/power_supply.h
> +++ b/include/linux/power_supply.h
> @@ -358,6 +358,52 @@ struct power_supply_ntc_resistance_temp_table {
>  	int temp;
>  };
>  
> +/**
> + * struct power_supply_maintenance_charge_table - setting for maintenace charging
> + * @charge_current_max_ua: maintenance charging current that is used to keep
> + *   the charge of the battery full as current is consumed after full charging.
> + *   The corresponding charge_voltage_max_uv is used as a safeguard: when we
> + *   reach this voltage the maintenance charging current is turned off. It is
> + *   turned back on if we fall below this voltage.
> + * @charge_voltage_max_uv: maintenance charging voltage that is usually a bit
> + *   lower than the constant_charge_voltage_max_uv. We can apply this settings
> + *   charge_current_max_ua until we get back up to this voltage.
> + * @safety_timer_minutes: maintenance charging safety timer, with an expiry

charge_safety_timer_minutes

> + *   time in minutes. We will only use maintenance charging in this setting
> + *   for a certain amount of time, then we will first move to the next
> + *   maintenance charge current and voltage pair in respective array and wait
> + *   for the next safety timer timeout, or, if we reached the last maintencance
> + *   charging setting, disable charging until we reach
> + *   charge_restart_voltage_uv and restart ordinary CC/CV charging from there.
> + *   These timers should be chosen to align with the typical discharge curve
> + *   for the battery.
> + *
> + * When the main CC/CV charging is complete the battery can optionally be
> + * maintenance charged at the voltages from this table: a table of settings is
> + * traversed using a slightly lower current and voltage than what is used for
> + * CC/CV charging. The maintenance charging will for safety reasons not go on
> + * indefinately: we lower the current and voltage with successive maintenance
> + * settings, then disable charging completely after we reach the last one,
> + * and after that we do not restart charging until we reach
> + * charge_restart_voltage_uv (see struct power_supply_battery_info) and restart
> + * ordinary CC/CV charging from there.
> + *
> + * As an example, a Samsung EB425161LA Lithium-Ion battery is CC/CV charged
> + * at 900mA to 4340mV, then maintenance charged at 600mA and 4150mV for
> + * 60 hours, then maintenance charged at 600mA and 4100mV for 200 hours.
> + * After this the charge cycle is restarted waiting for
> + * charge_restart_voltage_uv.
> + *
> + * For most mobile electronics this type of maintenance charging is enough for
> + * the user to disconnect the device and make use of it before both maintenance
> + * charging cycles are complete.
> + */
> +struct power_supply_maintenance_charge_table {
> +	int charge_current_max_ua;
> +	int charge_voltage_max_uv;
> +	int charge_safety_timer_minutes;
> +};
> +
>  #define POWER_SUPPLY_OCV_TEMP_MAX 20
>  
>  /**
> @@ -403,6 +449,10 @@ struct power_supply_ntc_resistance_temp_table {
>   * @constant_charge_voltage_max_uv: voltage in microvolts signifying the end of
>   *   the CC (constant current) charging phase and the beginning of the CV
>   *   (constant voltage) charging phase.
> + * @maintenance_charge: an array of maintenance charging settings to be used
> + *   after the main CC/CV charging phase is complete.
> + * @maintenance_charge_size: the number of maintenance charging settings in
> + *   maintenance_charge.
>   * @factory_internal_resistance_uohm: the internal resistance of the battery
>   *   at fabrication time, expressed in microohms. This resistance will vary
>   *   depending on the lifetime and charge of the battery, so this is just a
> @@ -564,6 +614,8 @@ struct power_supply_battery_info {
>  	int overvoltage_limit_uv;
>  	int constant_charge_current_max_ua;
>  	int constant_charge_voltage_max_uv;
> +	struct power_supply_maintenance_charge_table *maintenance_charge;
> +	int maintenance_charge_size;
>  	int factory_internal_resistance_uohm;
>  	int ocv_temp[POWER_SUPPLY_OCV_TEMP_MAX];
>  	int temp_ambient_alert_min;
> @@ -621,12 +673,24 @@ power_supply_temp2resist_simple(struct power_supply_resistance_temp_table *table
>  				int table_len, int temp);
>  extern int power_supply_ntc_resist2temp_simple(struct power_supply_battery_info *info,
>  					       int resistance_ohm);
> +extern struct power_supply_maintenance_charge_table *
> +power_supply_get_maintenance_charging_setting(struct power_supply_battery_info *info, int index);
>  extern void power_supply_changed(struct power_supply *psy);
>  extern int power_supply_am_i_supplied(struct power_supply *psy);
>  extern int power_supply_set_input_current_limit_from_supplier(
>  					 struct power_supply *psy);
>  extern int power_supply_set_battery_charged(struct power_supply *psy);
>  
> +static inline bool
> +power_supply_supports_maintenance_charging(struct power_supply_battery_info *info)
> +{
> +	struct power_supply_maintenance_charge_table *mt;
> +
> +	mt = power_supply_get_maintenance_charging_setting(info, 0);
> +
> +	return (mt != NULL);
> +}
> +
>  #ifdef CONFIG_POWER_SUPPLY
>  extern int power_supply_is_system_supplied(void);
>  #else
> -- 
> 2.31.1
> 

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 1/2] power: supply: ab8500: Standardize NTC battery temp
  2021-12-03 20:53   ` Sebastian Reichel
@ 2021-12-06  0:33     ` Linus Walleij
  0 siblings, 0 replies; 8+ messages in thread
From: Linus Walleij @ 2021-12-06  0:33 UTC (permalink / raw)
  To: Sebastian Reichel
  Cc: Marcus Cooper, Jean Delvare, Guenter Roeck, linux-pm, linux-hwmon

On Fri, Dec 3, 2021 at 9:53 PM Sebastian Reichel
<sebastian.reichel@collabora.com> wrote:

> This LGTM, but let's wait a bit to synchronize with the work
> happening in the AXP driver.

My thinking as encouraged by Guenther is to try to reuse the hwmon
NTC driver for this so I just sent some patches making it possible
to use the hwmon NTC driver in a pretty stand-alone and compact
manner using software firmware nodes for configuration.

Yours,
Linus Walleij

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 2/2] power: supply: ab8500: Standardize maintenance charging
  2021-12-03 21:02   ` Sebastian Reichel
@ 2021-12-06  0:37     ` Linus Walleij
  0 siblings, 0 replies; 8+ messages in thread
From: Linus Walleij @ 2021-12-06  0:37 UTC (permalink / raw)
  To: Sebastian Reichel
  Cc: Marcus Cooper, Jean Delvare, Guenter Roeck, linux-pm, linux-hwmon

On Fri, Dec 3, 2021 at 10:02 PM Sebastian Reichel
<sebastian.reichel@collabora.com> wrote:

> I wonder if this will ever be used by any other driver. Having
> multiple maintanence states seems to be very specific to the
> ab8500 driver stack.

It's actually coming from Samsung and their way of dealing
with their Li-Ion batteries. They even have further charging
states... Like starting the charge with slightly higher current
etc.

> But the code itself looks ok and considering
> this does not expose any (potentially unfixable) userspace ABI
> and improves the AB8500 mess I'm fine with it.

I will rebase this patch before the NTC things and send
it separately so it can be applied. It will better to first merge
the pointer passing change I iterated though, so I'll wait a
bit so we can agree to merge that.
https://lore.kernel.org/linux-pm/20211206000651.4168035-1-linus.walleij@linaro.org/

Yours,
Linus Walleij

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2021-12-06  0:37 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-11-22 23:41 [PATCH 0/2] More AB8500 charging props Linus Walleij
2021-11-22 23:41 ` [PATCH 1/2] power: supply: ab8500: Standardize NTC battery temp Linus Walleij
2021-12-03 20:53   ` Sebastian Reichel
2021-12-06  0:33     ` Linus Walleij
2021-11-22 23:41 ` [PATCH 2/2] power: supply: ab8500: Standardize maintenance charging Linus Walleij
2021-12-03 21:02   ` Sebastian Reichel
2021-12-06  0:37     ` Linus Walleij
2021-11-23  0:23 ` [PATCH 0/2] More AB8500 charging props Guenter Roeck

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.