in supply/ab8500_fg.c [1503:1664]
static void ab8500_fg_algorithm_discharging(struct ab8500_fg *di)
{
int sleep_time;
/* If we change to charge mode we should start with init */
if (di->charge_state != AB8500_FG_CHARGE_INIT)
ab8500_fg_charge_state_to(di, AB8500_FG_CHARGE_INIT);
switch (di->discharge_state) {
case AB8500_FG_DISCHARGE_INIT:
/* We use the FG IRQ to work on */
di->init_cnt = 0;
di->fg_samples = SEC_TO_SAMPLE(di->bm->fg_params->init_timer);
ab8500_fg_coulomb_counter(di, true);
ab8500_fg_discharge_state_to(di,
AB8500_FG_DISCHARGE_INITMEASURING);
fallthrough;
case AB8500_FG_DISCHARGE_INITMEASURING:
/*
* Discard a number of samples during startup.
* After that, use compensated voltage for a few
* samples to get an initial capacity.
* Then go to READOUT
*/
sleep_time = di->bm->fg_params->init_timer;
/* Discard the first [x] seconds */
if (di->init_cnt > di->bm->fg_params->init_discard_time) {
ab8500_fg_calc_cap_discharge_voltage(di, true);
ab8500_fg_check_capacity_limits(di, true);
}
di->init_cnt += sleep_time;
if (di->init_cnt > di->bm->fg_params->init_total_time)
ab8500_fg_discharge_state_to(di,
AB8500_FG_DISCHARGE_READOUT_INIT);
break;
case AB8500_FG_DISCHARGE_INIT_RECOVERY:
di->recovery_cnt = 0;
di->recovery_needed = true;
ab8500_fg_discharge_state_to(di,
AB8500_FG_DISCHARGE_RECOVERY);
fallthrough;
case AB8500_FG_DISCHARGE_RECOVERY:
sleep_time = di->bm->fg_params->recovery_sleep_timer;
/*
* We should check the power consumption
* If low, go to READOUT (after x min) or
* RECOVERY_SLEEP if time left.
* If high, go to READOUT
*/
di->inst_curr_ua = ab8500_fg_inst_curr_blocking(di);
if (ab8500_fg_is_low_curr(di, di->inst_curr_ua)) {
if (di->recovery_cnt >
di->bm->fg_params->recovery_total_time) {
di->fg_samples = SEC_TO_SAMPLE(
di->bm->fg_params->accu_high_curr);
ab8500_fg_coulomb_counter(di, true);
ab8500_fg_discharge_state_to(di,
AB8500_FG_DISCHARGE_READOUT);
di->recovery_needed = false;
} else {
queue_delayed_work(di->fg_wq,
&di->fg_periodic_work,
sleep_time * HZ);
}
di->recovery_cnt += sleep_time;
} else {
di->fg_samples = SEC_TO_SAMPLE(
di->bm->fg_params->accu_high_curr);
ab8500_fg_coulomb_counter(di, true);
ab8500_fg_discharge_state_to(di,
AB8500_FG_DISCHARGE_READOUT);
}
break;
case AB8500_FG_DISCHARGE_READOUT_INIT:
di->fg_samples = SEC_TO_SAMPLE(
di->bm->fg_params->accu_high_curr);
ab8500_fg_coulomb_counter(di, true);
ab8500_fg_discharge_state_to(di,
AB8500_FG_DISCHARGE_READOUT);
break;
case AB8500_FG_DISCHARGE_READOUT:
di->inst_curr_ua = ab8500_fg_inst_curr_blocking(di);
if (ab8500_fg_is_low_curr(di, di->inst_curr_ua)) {
/* Detect mode change */
if (di->high_curr_mode) {
di->high_curr_mode = false;
di->high_curr_cnt = 0;
}
if (di->recovery_needed) {
ab8500_fg_discharge_state_to(di,
AB8500_FG_DISCHARGE_INIT_RECOVERY);
queue_delayed_work(di->fg_wq,
&di->fg_periodic_work, 0);
break;
}
ab8500_fg_calc_cap_discharge_voltage(di, true);
} else {
mutex_lock(&di->cc_lock);
if (!di->flags.conv_done) {
/* Wasn't the CC IRQ that got us here */
mutex_unlock(&di->cc_lock);
dev_dbg(di->dev, "%s CC conv not done\n",
__func__);
break;
}
di->flags.conv_done = false;
mutex_unlock(&di->cc_lock);
/* Detect mode change */
if (!di->high_curr_mode) {
di->high_curr_mode = true;
di->high_curr_cnt = 0;
}
di->high_curr_cnt +=
di->bm->fg_params->accu_high_curr;
if (di->high_curr_cnt >
di->bm->fg_params->high_curr_time)
di->recovery_needed = true;
ab8500_fg_calc_cap_discharge_fg(di);
}
ab8500_fg_check_capacity_limits(di, false);
break;
case AB8500_FG_DISCHARGE_WAKEUP:
ab8500_fg_calc_cap_discharge_voltage(di, true);
di->fg_samples = SEC_TO_SAMPLE(
di->bm->fg_params->accu_high_curr);
ab8500_fg_coulomb_counter(di, true);
ab8500_fg_discharge_state_to(di,
AB8500_FG_DISCHARGE_READOUT);
ab8500_fg_check_capacity_limits(di, false);
break;
default:
break;
}
}