aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorLinus Walleij2022-02-26 00:27:55 +0100
committerSebastian Reichel2022-02-28 11:34:31 +0100
commitd72ce7d324786257410bec6b36e3756647dd76fd (patch)
tree4e78091df0147a5be8385d9686d7c5a7c7338bc3 /drivers
parentd69fc86aca7eda7bd7405c16f35f260c154e14e3 (diff)
power: supply: ab8500: Standardize maintenance charging
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. Reviewed-by: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com> Signed-off-by: Linus Walleij <linus.walleij@linaro.org> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/power/supply/ab8500-bm.h14
-rw-r--r--drivers/power/supply/ab8500_bmdata.c27
-rw-r--r--drivers/power/supply/ab8500_chargalg.c41
-rw-r--r--drivers/power/supply/power_supply_core.c11
4 files changed, 62 insertions, 31 deletions
diff --git a/drivers/power/supply/ab8500-bm.h b/drivers/power/supply/ab8500-bm.h
index 6efd5174dbce..4d74d21cf1eb 100644
--- a/drivers/power/supply/ab8500-bm.h
+++ b/drivers/power/supply/ab8500-bm.h
@@ -331,24 +331,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;
};
@@ -393,7 +381,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
* @chg_unknown_bat flag to enable charging of unknown batteries
* @enable_overshoot flag to enable VBAT overshoot control
@@ -417,7 +404,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 d8fc72be0f0e..66a454942c7c 100644
--- a/drivers/power/supply/ab8500_bmdata.c
+++ b/drivers/power/supply/ab8500_bmdata.c
@@ -58,16 +58,25 @@ static struct power_supply_resistance_temp_table temp_to_batres_tbl_thermistor[]
{ .temp = -20, .resistance = 198 /* 595 mOhm */ },
};
+static struct power_supply_maintenance_charge_table ab8500_maint_charg_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,
};
@@ -124,7 +133,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,
.chg_unknown_bat = false,
.enable_overshoot = false,
@@ -179,6 +187,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 = ab8500_maint_charg_table;
+ bi->maintenance_charge_size = ARRAY_SIZE(ab8500_maint_charg_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 b5a3096e78a1..6054996b6260 100644
--- a/drivers/power/supply/ab8500_chargalg.c
+++ b/drivers/power/supply/ab8500_chargalg.c
@@ -430,7 +430,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
@@ -439,9 +439,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);
}
@@ -1223,6 +1224,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;
@@ -1433,7 +1435,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
@@ -1454,12 +1461,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;
@@ -1472,11 +1486,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 8dbd1197cc62..accbbd36bfe7 100644
--- a/drivers/power/supply/power_supply_core.c
+++ b/drivers/power/supply/power_supply_core.c
@@ -595,6 +595,7 @@ int power_supply_get_battery_info(struct power_supply *psy,
info->precharge_voltage_max_uv = -EINVAL;
info->charge_restart_voltage_uv = -EINVAL;
info->overvoltage_limit_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;
@@ -844,6 +845,16 @@ int power_supply_temp2resist_simple(struct power_supply_resistance_temp_table *t
}
EXPORT_SYMBOL_GPL(power_supply_temp2resist_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