コード例 #1
0
int charger_set_current(int current)
{
	const struct charger_info *info = charger_get_info();

	if (current > 0 && current < info->current_min)
		current = info->current_min;
	if (current > info->current_max)
		current = info->current_max;

	if (mock_current != current)
		ccprintf("Charger set current: %d\n", current);
	mock_current = current;
	return EC_SUCCESS;
}
コード例 #2
0
ファイル: charge_state_v1.c プロジェクト: longsleep/ec
static void charge_init(void)
{
	struct charge_state_context *ctx = &task_ctx;

	ctx->prev.state = PWR_STATE_INIT;
	ctx->curr.state = PWR_STATE_INIT;
	ctx->trickle_charging_time.val = 0;
	ctx->battery = battery_get_info();
	ctx->charger = charger_get_info();
	/* Assume the battery is responsive until proven otherwise */
	ctx->battery_responsive = 1;

	/* Set up LPC direct memmap */
	ctx->memmap_batt_volt =
		(uint32_t *)host_get_memmap(EC_MEMMAP_BATT_VOLT);
	ctx->memmap_batt_rate =
		(uint32_t *)host_get_memmap(EC_MEMMAP_BATT_RATE);
	ctx->memmap_batt_cap =
		(uint32_t *)host_get_memmap(EC_MEMMAP_BATT_CAP);
	ctx->memmap_batt_flags = host_get_memmap(EC_MEMMAP_BATT_FLAG);
}
コード例 #3
0
ファイル: charge_state_v2.c プロジェクト: coreboot/chrome-ec
/* 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);
	}
}
コード例 #4
0
ファイル: charge_state_v2.c プロジェクト: coreboot/chrome-ec
/*
 * Ask the charger for some voltage and current. If either value is 0,
 * charging is disabled; otherwise it's enabled. Negative values are ignored.
 */
static int charge_request(int voltage, int current)
{
	int r1 = EC_SUCCESS, r2 = EC_SUCCESS, r3 = EC_SUCCESS;
	static int __bss_slow prev_volt, prev_curr;

	if (!voltage || !current) {
#ifdef CONFIG_CHARGER_NARROW_VDC
		current = 0;
		/* With NVDC charger, keep VSYS voltage higher than battery */
		voltage = charger_closest_voltage(
			curr.batt.voltage + charger_get_info()->voltage_step);
		/* If the battery is full, request the max voltage. */
		if (is_full)
			voltage = battery_get_info()->voltage_max;
		/* And handle dead battery case */
		voltage = MAX(voltage, battery_get_info()->voltage_min);
#else
		voltage = current = 0;
#endif
	}

	if (curr.ac) {
		if (prev_volt != voltage || prev_curr != current)
			CPRINTS("%s(%dmV, %dmA)", __func__, voltage, current);
	}

	/*
	 * Set current before voltage so that if we are just starting
	 * to charge, we allow some time (i2c delay) for charging circuit to
	 * start at a voltage just above battery voltage before jumping
	 * up. This helps avoid large current spikes when connecting
	 * battery.
	 */
	if (current >= 0)
		r2 = charger_set_current(current);
	if (r2 != EC_SUCCESS)
		problem(PR_SET_CURRENT, r2);

	if (voltage >= 0)
		r1 = charger_set_voltage(voltage);
	if (r1 != EC_SUCCESS)
		problem(PR_SET_VOLTAGE, r1);

	/*
	 * Set the charge inhibit bit when possible as it appears to save
	 * power in some cases (e.g. Nyan with BQ24735).
	 */
#if defined(CONFIG_CHARGER_BD99955) || defined(CONFIG_CHARGER_BD99956)
	/* Charger auto exits from battery learn mode if charge inhibited */
	if (current > 0 || chg_ctl_mode == CHARGE_CONTROL_DISCHARGE)
#else
	if (voltage > 0 || current > 0)
#endif
		r3 = charger_set_mode(0);
	else
		r3 = charger_set_mode(CHARGE_FLAG_INHIBIT_CHARGE);
	if (r3 != EC_SUCCESS)
		problem(PR_SET_MODE, r3);

	/*
	 * Only update if the request worked, so we'll keep trying on failures.
	 */
	if (!r1 && !r2) {
		prev_volt = voltage;
		prev_curr = current;
	}

	return r1 ? r1 : r2;
}
コード例 #5
0
ファイル: battery.c プロジェクト: coreboot/chrome-ec
/*
 * This can override the smart battery's charging profile. To make a change,
 * modify one or more of requested_voltage, requested_current, or state.
 * Leave everything else unchanged.
 *
 * Return the next poll period in usec, or zero to use the default (which is
 * state dependent).
 */
int charger_profile_override(struct charge_state_data *curr)
{
	/* temp in 0.1 deg C */
	int temp_c;
	const struct charger_info *info;

	/* keep track of last temperature range for hysteresis */
	static enum {
		TEMP_LOW,
		TEMP_NORMAL,
		TEMP_HIGH
	} temp_range = TEMP_NORMAL, prev_temp_range = TEMP_NORMAL;

	/* charging voltage to use at high temp */
	static int high_temp_charging_voltage;

	/* custom profile phase at normal temp */
	static int normal_temp_phase;

	/* battery voltage and current and previous voltage and current */
	int batt_voltage, batt_current;
	static int prev_batt_voltage, prev_batt_current;

	/*
	 * Determine temperature range:
	 * Low: Battery is <15C
	 * Normal: Battery is 15-45C
	 * High: Battery is >45C
	 *
	 * Add 0.2 degrees of hysteresis.
	 * If temp reading was bad use last range.
	 */
	if (!(curr->batt.flags & BATT_FLAG_BAD_TEMPERATURE)) {
		temp_c = curr->batt.temperature - 2731;
		if (temp_c < 149)
			temp_range = TEMP_LOW;
		else if (temp_c > 151 && temp_c < 449)
			temp_range = TEMP_NORMAL;
		else if (temp_c > 451)
			temp_range = TEMP_HIGH;
	}

	/*
	 * Treat voltage and current as a pair, if either is bad fall back to
	 * previous reading.
	 */
	if (curr->batt.flags &
	    (BATT_FLAG_BAD_VOLTAGE | BATT_FLAG_BAD_CURRENT)) {
		batt_voltage = prev_batt_voltage;
		batt_current = prev_batt_current;
	} else {
		batt_voltage = prev_batt_voltage = curr->batt.voltage;
		batt_current = prev_batt_current = curr->batt.current;
	}

	/*
	 * If we are not charging or we aren't using fast charging profiles,
	 * then do not override desired current and voltage and reset some
	 * fast charging profile static variables.
	 */
	if (curr->state != ST_CHARGE || !fast_charging_allowed) {
		prev_temp_range = TEMP_NORMAL;
		normal_temp_phase = 0;
		return 0;
	}

	/*
	 * Okay, impose our custom will:
	 * Normal temp:
	 * Phase 0: CC at 9515mA @ 8.3V
	 *          CV at 8.3V until current drops to 4759mA
	 * Phase 1: CC at 4759mA @ 8.7V
	 *          CV at 8.7V
	 *
	 * Low temp:
	 * CC at 2854mA @ 8.7V
	 * CV at 8.7V
	 *
	 * High temp:
	 * If battery voltage < 8.3V then:
	 *	CC at 6660mA @ 8.3V
	 *	CV at 8.3V (when battery is hot we don't go to fully charged)
	 * else:
	 *	CV at just above battery voltage which will essentially
	 *         terminate the charge and allow battery to cool.
	 * Note that if we ever request a voltage below the present battery
	 * voltage, then we will stop the BQ switching, which will power off
	 * the INA and we won't be able to charge again until AC is
	 * disconnected. see crbug.com/p/35491.
	 */
	switch (temp_range) {
	case TEMP_LOW:
		curr->requested_current = 2854;
		curr->requested_voltage = 8700;
		break;
	case TEMP_NORMAL:
		if (normal_temp_phase == 0) {
			curr->requested_current = 9515;
			curr->requested_voltage = 8300;
			if (batt_current <= 4759 && batt_voltage >= 8200)
				normal_temp_phase = 1;
		}
		if (normal_temp_phase == 1) {
			curr->requested_current = 4759;
			curr->requested_voltage = 8700;
		}
		break;
	case TEMP_HIGH:
		/*
		 * First time TEMP_HIGH is used, get the closest voltage
		 * just above the battery voltage. If it is above 8.3V, we
		 * will use that as the target, otherwise we will use 8.3V.
		 */
		if (prev_temp_range != TEMP_HIGH) {
			info = charger_get_info();
			high_temp_charging_voltage = MAX(8300,
				charger_closest_voltage(batt_voltage +
							info->voltage_step));
		}
		curr->requested_current = 6660;
		curr->requested_voltage = high_temp_charging_voltage;
		break;
	}
	prev_temp_range = temp_range;

	return 0;
}