Пример #1
0
/**
 * 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;
}
Пример #2
0
/**
 * 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;
}
Пример #3
0
/* 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;
}
Пример #4
0
/**
 * 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;
}
Пример #5
0
/* 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);
	}
}
Пример #6
0
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;
}
Пример #7
0
/**
 * 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;
}
Пример #8
0
/**
 * 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;
}