/** * Init state handler * * - check ac, charger, battery and temperature * - initialize charger * - new states: DISCHARGE, IDLE */ static enum charge_state state_init(struct charge_state_context *ctx) { /* Stop charger, unconditionally */ charge_request(0, 0); /* if battery was not detected initially, get battery info again */ if (ctx->battery == NULL) ctx->battery = battery_get_info(); /* Update static battery info */ update_battery_info(); /* Clear shutdown timer */ ctx->shutdown_warning_time.val = 0; /* If AC is not present, switch to discharging state */ if (!ctx->curr.ac) return PWR_STATE_DISCHARGE; /* Check general error conditions */ if (ctx->curr.error) return PWR_STATE_ERROR; /* Send battery event to host */ host_set_single_event(EC_HOST_EVENT_BATTERY); return PWR_STATE_IDLE0; }
/** * Idle state handler * * - both charger and battery are online * - detect charger and battery status change * - new states: CHARGE, INIT */ static enum charge_state state_idle(struct charge_state_context *ctx) { struct batt_params *batt = &ctx->curr.batt; /* If we are forcing idle mode, then just stay in IDLE. */ if (state_machine_force_idle) return PWR_STATE_UNCHANGE; if (!ctx->curr.ac) return PWR_STATE_REINIT; if (ctx->curr.error) return PWR_STATE_ERROR; /* Prevent charging in idle mode */ if (ctx->curr.charging_voltage || ctx->curr.charging_current) return PWR_STATE_REINIT; if (batt->state_of_charge >= BATTERY_LEVEL_FULL) return PWR_STATE_UNCHANGE; /* Configure init charger state and switch to charge state */ if (batt->flags & BATT_FLAG_WANT_CHARGE) { int want_current = charger_closest_current(batt->desired_current); CPRINTS("Charge start %dmV %dmA", batt->desired_voltage, want_current); if (charge_request(batt->desired_voltage, want_current)) return PWR_STATE_ERROR; update_charger_time(ctx, get_time()); if (ctx->curr.batt.state_of_charge < BATTERY_LEVEL_NEAR_FULL) return PWR_STATE_CHARGE; else return PWR_STATE_CHARGE_NEAR_FULL; } return PWR_STATE_UNCHANGE; }
/* Force charging off before the battery is full. */ static int set_chg_ctrl_mode(enum ec_charge_control_mode mode) { if (mode == CHARGE_CONTROL_NORMAL) { chg_ctl_mode = mode; manual_mode = 0; } else { /* * Changing mode is only meaningful if external power is * present. If it's not present we can't charge anyway. */ if (!curr.ac) return EC_ERROR_NOT_POWERED; chg_ctl_mode = mode; charge_request(0, 0); manual_mode = 1; } return EC_SUCCESS; }
/** * Error state handler * * - check charger and battery communication * - log error * - new state: INIT */ static enum charge_state state_error(struct charge_state_context *ctx) { static int logged_error; if (!ctx->curr.error) { logged_error = 0; return PWR_STATE_REINIT; } charge_request(0, 0); /* Debug output */ if (ctx->curr.error != logged_error) { CPRINTS("Charge error: flag[%08b -> %08b], ac %d, " " charger %s, battery %s", logged_error, ctx->curr.error, ctx->curr.ac, (ctx->curr.error & F_CHARGER_MASK) ? "(err)" : "ok", (ctx->curr.error & F_BATTERY_MASK) ? "(err)" : "ok"); logged_error = ctx->curr.error; } return PWR_STATE_UNCHANGE; }
/* Main loop */ void charger_task(void) { int sleep_usec; int need_static = 1; const struct charger_info * const info = charger_get_info(); /* Get the battery-specific values */ batt_info = battery_get_info(); prev_ac = prev_charge = -1; chg_ctl_mode = CHARGE_CONTROL_NORMAL; shutdown_warning_time.val = 0UL; battery_seems_to_be_dead = 0; /* * If system is not locked and we don't have a battery to live on, * then use max input current limit so that we can pull as much power * as needed. */ battery_get_params(&curr.batt); prev_bp = curr.batt.is_present; curr.desired_input_current = get_desired_input_current(prev_bp, info); while (1) { #ifdef CONFIG_SB_FIRMWARE_UPDATE if (sb_fw_update_in_progress()) { task_wait_event(CHARGE_MAX_SLEEP_USEC); continue; } #endif /* Let's see what's going on... */ curr.ts = get_time(); sleep_usec = 0; problems_exist = 0; curr.ac = extpower_is_present(); if (curr.ac != prev_ac) { if (curr.ac) { /* * Some chargers are unpowered when the AC is * off, so we'll reinitialize it when AC * comes back and set the input current limit. * Try again if it fails. */ int rv = charger_post_init(); if (rv != EC_SUCCESS) { problem(PR_POST_INIT, rv); } else { if (curr.desired_input_current != CHARGE_CURRENT_UNINITIALIZED) rv = charger_set_input_current( curr.desired_input_current); if (rv != EC_SUCCESS) problem(PR_SET_INPUT_CURR, rv); else prev_ac = curr.ac; } } else { /* Some things are only meaningful on AC */ chg_ctl_mode = CHARGE_CONTROL_NORMAL; battery_seems_to_be_dead = 0; prev_ac = curr.ac; } } charger_get_params(&curr.chg); battery_get_params(&curr.batt); if (prev_bp != curr.batt.is_present) { prev_bp = curr.batt.is_present; /* Update battery info due to change of battery */ batt_info = battery_get_info(); need_static = 1; curr.desired_input_current = get_desired_input_current(prev_bp, info); if (curr.desired_input_current != CHARGE_CURRENT_UNINITIALIZED) charger_set_input_current( curr.desired_input_current); hook_notify(HOOK_BATTERY_SOC_CHANGE); } /* * TODO(crosbug.com/p/27527). Sometimes the battery thinks its * temperature is 6280C, which seems a bit high. Let's ignore * anything above the boiling point of tungsten until this bug * is fixed. If the battery is really that warm, we probably * have more urgent problems. */ if (curr.batt.temperature > CELSIUS_TO_DECI_KELVIN(5660)) { CPRINTS("ignoring ridiculous batt.temp of %dC", DECI_KELVIN_TO_CELSIUS(curr.batt.temperature)); curr.batt.flags |= BATT_FLAG_BAD_TEMPERATURE; } /* If the battery thinks it's above 100%, don't believe it */ if (curr.batt.state_of_charge > 100) { CPRINTS("ignoring ridiculous batt.soc of %d%%", curr.batt.state_of_charge); curr.batt.flags |= BATT_FLAG_BAD_STATE_OF_CHARGE; } /* * Now decide what we want to do about it. We'll normally just * pass along whatever the battery wants to the charger. Note * that if battery_get_params() can't get valid values from the * battery it uses (0, 0), which is probably safer than blindly * applying power to a battery we can't talk to. */ curr.requested_voltage = curr.batt.desired_voltage; curr.requested_current = curr.batt.desired_current; /* If we *know* there's no battery, wait for one to appear. */ if (curr.batt.is_present == BP_NO) { ASSERT(curr.ac); /* How are we running? */ curr.state = ST_IDLE; curr.batt_is_charging = 0; battery_was_removed = 1; goto wait_for_it; } /* * If we had trouble talking to the battery or the charger, we * should probably do nothing for a bit, and if it doesn't get * better then flag it as an error. */ if (curr.chg.flags & CHG_FLAG_BAD_ANY) problem(PR_CHG_FLAGS, curr.chg.flags); if (curr.batt.flags & BATT_FLAG_BAD_ANY) problem(PR_BATT_FLAGS, curr.batt.flags); /* * If AC is present, check if input current is sufficient to * actually charge battery. */ curr.batt_is_charging = curr.ac && (curr.batt.current >= 0); /* Don't let the battery hurt itself. */ shutdown_on_critical_battery(); if (!curr.ac) { curr.state = ST_DISCHARGE; goto wait_for_it; } /* Okay, we're on AC and we should have a battery. */ /* Used for factory tests. */ if (chg_ctl_mode != CHARGE_CONTROL_NORMAL) { curr.state = ST_IDLE; goto wait_for_it; } /* If the battery is not responsive, try to wake it up. */ if (!(curr.batt.flags & BATT_FLAG_RESPONSIVE)) { if (battery_seems_to_be_dead || battery_is_cut_off()) { /* It's dead, do nothing */ curr.state = ST_IDLE; curr.requested_voltage = 0; curr.requested_current = 0; } else if (curr.state == ST_PRECHARGE && (get_time().val > precharge_start_time.val + PRECHARGE_TIMEOUT_US)) { /* We've tried long enough, give up */ CPRINTS("battery seems to be dead"); battery_seems_to_be_dead = 1; curr.state = ST_IDLE; curr.requested_voltage = 0; curr.requested_current = 0; } else { /* See if we can wake it up */ if (curr.state != ST_PRECHARGE) { CPRINTS("try to wake battery"); precharge_start_time = get_time(); need_static = 1; } curr.state = ST_PRECHARGE; curr.requested_voltage = batt_info->voltage_max; curr.requested_current = batt_info->precharge_current; } goto wait_for_it; } else { /* The battery is responding. Yay. Try to use it. */ #ifdef CONFIG_BATTERY_REQUESTS_NIL_WHEN_DEAD /* * TODO (crosbug.com/p/29467): remove this workaround * for dead battery that requests no voltage/current */ if (curr.requested_voltage == 0 && curr.requested_current == 0 && curr.batt.state_of_charge == 0) { /* Battery is dead, give precharge current */ curr.requested_voltage = batt_info->voltage_max; curr.requested_current = batt_info->precharge_current; } else #endif #ifdef CONFIG_BATTERY_REVIVE_DISCONNECT battery_seems_to_be_disconnected = 0; if (curr.requested_voltage == 0 && curr.requested_current == 0 && battery_get_disconnect_state() == BATTERY_DISCONNECTED) { /* * Battery is in disconnect state. Apply a * current to kick it out of this state. */ CPRINTS("found battery in disconnect state"); curr.requested_voltage = batt_info->voltage_max; curr.requested_current = batt_info->precharge_current; battery_seems_to_be_disconnected = 1; } else #endif if (curr.state == ST_PRECHARGE || battery_seems_to_be_dead || battery_was_removed) { CPRINTS("battery woke up"); /* Update the battery-specific values */ batt_info = battery_get_info(); need_static = 1; } battery_seems_to_be_dead = battery_was_removed = 0; curr.state = ST_CHARGE; } /* * TODO(crosbug.com/p/27643): Quit trying if charging too long * without getting full (CONFIG_CHARGER_TIMEOUT_HOURS). */ wait_for_it: #ifdef CONFIG_CHARGER_PROFILE_OVERRIDE sleep_usec = charger_profile_override(&curr); if (sleep_usec < 0) problem(PR_CUSTOM, sleep_usec); #endif /* Keep the AP informed */ if (need_static) need_static = update_static_battery_info(); /* Wait on the dynamic info until the static info is good. */ if (!need_static) update_dynamic_battery_info(); notify_host_of_low_battery(); /* And the EC console */ is_full = calc_is_full(); if ((!(curr.batt.flags & BATT_FLAG_BAD_STATE_OF_CHARGE) && curr.batt.state_of_charge != prev_charge) || (is_full != prev_full)) { show_charging_progress(); prev_charge = curr.batt.state_of_charge; hook_notify(HOOK_BATTERY_SOC_CHANGE); } prev_full = is_full; /* Turn charger off if it's not needed */ if (curr.state == ST_IDLE || curr.state == ST_DISCHARGE) { curr.requested_voltage = 0; curr.requested_current = 0; } /* Apply external limits */ if (curr.requested_current > user_current_limit) curr.requested_current = user_current_limit; /* Round to valid values */ curr.requested_voltage = charger_closest_voltage(curr.requested_voltage); curr.requested_current = charger_closest_current(curr.requested_current); /* Charger only accpets request when AC is on. */ if (curr.ac) { /* * Some batteries would wake up after cut-off if we keep * charging it. Thus, we only charge when AC is on and * battery is not cut off yet. */ if (battery_is_cut_off()) charge_request(0, 0); /* * As a safety feature, some chargers will stop * charging if we don't communicate with it frequently * enough. In manual mode, we'll just tell it what it * knows. */ else if (manual_mode) { charge_request(curr.chg.voltage, curr.chg.current); } else { charge_request(curr.requested_voltage, curr.requested_current); } } else { charge_request( charger_closest_voltage( curr.batt.voltage + info->voltage_step), -1); } /* How long to sleep? */ if (problems_exist) /* If there are errors, don't wait very long. */ sleep_usec = CHARGE_POLL_PERIOD_SHORT; else if (sleep_usec <= 0) { /* default values depend on the state */ if (curr.state == ST_IDLE || curr.state == ST_DISCHARGE) { /* If AP is off, we can sleep a long time */ if (chipset_in_state(CHIPSET_STATE_ANY_OFF | CHIPSET_STATE_SUSPEND)) sleep_usec = CHARGE_POLL_PERIOD_VERY_LONG; else /* Discharging, not too urgent */ sleep_usec = CHARGE_POLL_PERIOD_LONG; } else { /* Charging, so pay closer attention */ sleep_usec = CHARGE_POLL_PERIOD_CHARGE; } } /* Adjust for time spent in this loop */ sleep_usec -= (int)(get_time().val - curr.ts.val); if (sleep_usec < CHARGE_MIN_SLEEP_USEC) sleep_usec = CHARGE_MIN_SLEEP_USEC; else if (sleep_usec > CHARGE_MAX_SLEEP_USEC) sleep_usec = CHARGE_MAX_SLEEP_USEC; task_wait_event(sleep_usec); } }
static int charge_command_charge_state(struct host_cmd_handler_args *args) { const struct ec_params_charge_state *in = args->params; struct ec_response_charge_state *out = args->response; uint32_t val; int rv = EC_RES_SUCCESS; switch (in->cmd) { case CHARGE_STATE_CMD_GET_STATE: out->get_state.ac = curr.ac; out->get_state.chg_voltage = curr.chg.voltage; out->get_state.chg_current = curr.chg.current; out->get_state.chg_input_current = curr.chg.input_current; out->get_state.batt_state_of_charge = curr.batt.state_of_charge; args->response_size = sizeof(out->get_state); break; case CHARGE_STATE_CMD_GET_PARAM: val = 0; #ifdef CONFIG_CHARGER_PROFILE_OVERRIDE /* custom profile params */ if (in->get_param.param >= CS_PARAM_CUSTOM_PROFILE_MIN && in->get_param.param <= CS_PARAM_CUSTOM_PROFILE_MAX) { rv = charger_profile_override_get_param( in->get_param.param, &val); } else #endif /* standard params */ switch (in->get_param.param) { case CS_PARAM_CHG_VOLTAGE: val = curr.chg.voltage; break; case CS_PARAM_CHG_CURRENT: val = curr.chg.current; break; case CS_PARAM_CHG_INPUT_CURRENT: val = curr.chg.input_current; break; case CS_PARAM_CHG_STATUS: val = curr.chg.status; break; case CS_PARAM_CHG_OPTION: val = curr.chg.option; break; case CS_PARAM_LIMIT_POWER: #ifdef CONFIG_CHARGER_LIMIT_POWER_THRESH_BAT_PCT /* * LIMIT_POWER status is based on battery level * and external charger power. */ if ((curr.batt.is_present != BP_YES || curr.batt.state_of_charge < CONFIG_CHARGER_LIMIT_POWER_THRESH_BAT_PCT) && charge_manager_get_power_limit_uw() < CONFIG_CHARGER_LIMIT_POWER_THRESH_CHG_MW * 1000 && system_is_locked()) val = 1; else #endif val = 0; break; default: rv = EC_RES_INVALID_PARAM; } /* got something */ out->get_param.value = val; args->response_size = sizeof(out->get_param); break; case CHARGE_STATE_CMD_SET_PARAM: val = in->set_param.value; #ifdef CONFIG_CHARGER_PROFILE_OVERRIDE /* custom profile params */ if (in->set_param.param >= CS_PARAM_CUSTOM_PROFILE_MIN && in->set_param.param <= CS_PARAM_CUSTOM_PROFILE_MAX) { rv = charger_profile_override_set_param( in->set_param.param, val); } else #endif switch (in->set_param.param) { case CS_PARAM_CHG_VOLTAGE: val = charger_closest_voltage(val); if (charge_request(val, -1)) rv = EC_RES_ERROR; manual_mode = 1; break; case CS_PARAM_CHG_CURRENT: val = charger_closest_current(val); if (charge_request(-1, val)) rv = EC_RES_ERROR; manual_mode = 1; break; case CS_PARAM_CHG_INPUT_CURRENT: if (charger_set_input_current(val)) rv = EC_RES_ERROR; break; case CS_PARAM_CHG_STATUS: case CS_PARAM_LIMIT_POWER: /* Can't set this */ rv = EC_RES_ACCESS_DENIED; break; case CS_PARAM_CHG_OPTION: if (charger_set_option(val)) rv = EC_RES_ERROR; break; default: rv = EC_RES_INVALID_PARAM; } break; default: CPRINTS("EC_CMD_CHARGE_STATE: bad cmd 0x%x", in->cmd); rv = EC_RES_INVALID_PARAM; } return rv; }
/** * Charge state handler * * - detect battery status change * - new state: INIT */ static enum charge_state state_charge(struct charge_state_context *ctx) { struct charge_state_data *curr = &ctx->curr; struct batt_params *batt = &ctx->curr.batt; int debounce = 0; int want_current; int want_voltage; timestamp_t now; if (curr->error) return PWR_STATE_ERROR; /* * Some chargers will reset out from underneath us. If this happens, * reinitialize charging. */ if (curr->charging_voltage == 0 || curr->charging_current == 0) return PWR_STATE_REINIT; if (!curr->ac) return PWR_STATE_REINIT; /* Stop charging if charging is no longer allowed */ if (!(batt->flags & BATT_FLAG_WANT_CHARGE)) { if (charge_request(0, 0)) return PWR_STATE_ERROR; return PWR_STATE_IDLE; } now = get_time(); /* * Adjust desired voltage to one the charger can actually supply * or else we'll keep asking for a voltage the charger can't actually * supply. */ want_voltage = charger_closest_voltage(batt->desired_voltage); if (want_voltage != curr->charging_voltage) { CPRINTS("Charge voltage %dmV", want_voltage); if (charge_request(want_voltage, -1)) return PWR_STATE_ERROR; update_charger_time(ctx, now); } /* * Adjust desired current to one the charger can actually supply before * we do debouncing, or else we'll keep asking for a current the * charger can't actually supply. */ want_current = charger_closest_current(batt->desired_current); if (want_current == curr->charging_current) { /* Tick charger watchdog */ if (!is_charger_expired(ctx, now)) return PWR_STATE_UNCHANGE; } else if (want_current > curr->charging_current) { if (!timestamp_expired(ctx->voltage_debounce_time, &now)) return PWR_STATE_UNCHANGE; } else { debounce = 1; } if (want_current != curr->charging_current) { CPRINTS("Charge current %dmA @ %dmV", want_current, batt->desired_voltage); } if (charge_request(-1, want_current)) return PWR_STATE_ERROR; /* Update charger watchdog timer and debounce timer */ update_charger_time(ctx, now); if (debounce) ctx->voltage_debounce_time.val = now.val + DEBOUNCE_TIME; return PWR_STATE_UNCHANGE; }
/** * Common handler for charging states. * * This handler gets battery charging parameters, charger state, ac state, and * timestamp. It also fills memory map and issues power events on state change. */ static int state_common(struct charge_state_context *ctx) { int rv, d; struct charge_state_data *curr = &ctx->curr; struct charge_state_data *prev = &ctx->prev; struct batt_params *batt = &ctx->curr.batt; uint8_t *batt_flags = ctx->memmap_batt_flags; /* Copy previous state and init new state */ ctx->prev = ctx->curr; curr->ts = get_time(); curr->error = 0; /* Detect AC change */ curr->ac = charge_get_flags() & CHARGE_FLAG_EXTERNAL_POWER; if (curr->ac != prev->ac) { if (curr->ac) { /* AC on * Initialize charger to power on reset mode */ rv = charger_post_init(); if (rv) curr->error |= F_CHARGER_INIT; } } if (curr->ac) { *batt_flags |= EC_BATT_FLAG_AC_PRESENT; if (charger_get_voltage(&curr->charging_voltage)) { charge_request(0, 0); curr->error |= F_CHARGER_VOLTAGE; } if (charger_get_current(&curr->charging_current)) { charge_request(0, 0); curr->error |= F_CHARGER_CURRENT; } #ifdef CONFIG_CHARGER_EN_GPIO if (!charge_get_charger_en_gpio()) { curr->charging_voltage = 0; curr->charging_current = 0; } #endif } else { *batt_flags &= ~EC_BATT_FLAG_AC_PRESENT; /* AC disconnected should get us out of force idle mode. */ state_machine_force_idle = 0; } #if defined(CONFIG_BATTERY_PRESENT_CUSTOM) || \ defined(CONFIG_BATTERY_PRESENT_GPIO) if (battery_is_present() == BP_NO) { curr->error |= F_BATTERY_NOT_CONNECTED; return curr->error; } #endif /* Read params and see if battery is responsive */ battery_get_params(batt); if (!(batt->flags & BATT_FLAG_RESPONSIVE)) { /* Check low battery condition and retry */ if (curr->ac && ctx->battery_responsive && !(curr->error & F_CHARGER_MASK)) { ctx->battery_responsive = 0; /* * Try to revive ultra low voltage pack. Charge * battery pack with minimum current and maximum * voltage for 30 seconds. */ charge_request(ctx->battery->voltage_max, ctx->battery->precharge_current); for (d = 0; d < PRECHARGE_TIMEOUT; d++) { sleep(1); battery_get_params(batt); if (batt->flags & BATT_FLAG_RESPONSIVE) { ctx->battery_responsive = 1; break; } } } /* Set error if battery is still unresponsive */ if (!(batt->flags & BATT_FLAG_RESPONSIVE)) { curr->error |= F_BATTERY_UNRESPONSIVE; return curr->error; } } else { ctx->battery_responsive = 1; } /* Translate flags */ if (batt->flags & BATT_FLAG_BAD_ANY) curr->error |= F_BATTERY_GET_PARAMS; if (batt->flags & BATT_FLAG_BAD_VOLTAGE) curr->error |= F_BATTERY_VOLTAGE; if (batt->flags & BATT_FLAG_BAD_STATE_OF_CHARGE) curr->error |= F_BATTERY_STATE_OF_CHARGE; *ctx->memmap_batt_volt = batt->voltage; /* Memory mapped value: discharge rate */ *ctx->memmap_batt_rate = batt->current < 0 ? -batt->current : batt->current; /* Fake state of charge if necessary */ if (fake_state_of_charge >= 0) { batt->state_of_charge = fake_state_of_charge; curr->error &= ~F_BATTERY_STATE_OF_CHARGE; } if (batt->state_of_charge != prev->batt.state_of_charge) { rv = battery_full_charge_capacity(&d); if (!rv && d != *(int *)host_get_memmap(EC_MEMMAP_BATT_LFCC)) { *(int *)host_get_memmap(EC_MEMMAP_BATT_LFCC) = d; /* Notify host to re-read battery information */ host_set_single_event(EC_HOST_EVENT_BATTERY); } } /* Prevent deep discharging */ if (!curr->ac) { if ((batt->state_of_charge < BATTERY_LEVEL_SHUTDOWN && !(curr->error & F_BATTERY_STATE_OF_CHARGE)) || (batt->voltage <= ctx->battery->voltage_min && !(curr->error & F_BATTERY_VOLTAGE))) low_battery_shutdown(ctx); } /* Check battery presence */ if (curr->error & F_BATTERY_MASK) { *ctx->memmap_batt_flags &= ~EC_BATT_FLAG_BATT_PRESENT; return curr->error; } *ctx->memmap_batt_flags |= EC_BATT_FLAG_BATT_PRESENT; /* Battery charge level low */ if (batt->state_of_charge <= BATTERY_LEVEL_LOW && prev->batt.state_of_charge > BATTERY_LEVEL_LOW) host_set_single_event(EC_HOST_EVENT_BATTERY_LOW); /* Battery charge level critical */ if (batt->state_of_charge <= BATTERY_LEVEL_CRITICAL) { *ctx->memmap_batt_flags |= EC_BATT_FLAG_LEVEL_CRITICAL; /* Send battery critical host event */ if (prev->batt.state_of_charge > BATTERY_LEVEL_CRITICAL) host_set_single_event(EC_HOST_EVENT_BATTERY_CRITICAL); } else { *ctx->memmap_batt_flags &= ~EC_BATT_FLAG_LEVEL_CRITICAL; } #ifdef CONFIG_BATTERY_OVERRIDE_PARAMS /* Apply battery pack vendor charging method */ battery_override_params(batt); #endif #ifdef CONFIG_CHARGER_CURRENT_LIMIT if (batt->desired_current > CONFIG_CHARGER_CURRENT_LIMIT) batt->desired_current = CONFIG_CHARGER_CURRENT_LIMIT; #endif if (batt->desired_current > user_current_limit) batt->desired_current = user_current_limit; if (fake_state_of_charge >= 0) *ctx->memmap_batt_cap = fake_state_of_charge * *(int *)host_get_memmap(EC_MEMMAP_BATT_LFCC) / 100; else if (battery_remaining_capacity(&d)) ctx->curr.error |= F_BATTERY_CAPACITY; else *ctx->memmap_batt_cap = d; return ctx->curr.error; }