/**
 * Handle the hang detect timer expiring.
 */
static void hang_detect_deferred(void)
{
	/* If we're no longer active, nothing to do */
	if (!active)
		return;

	/* If we're rebooting the AP, stop hang detection */
	if (timeout_will_reboot) {
		CPRINTS("hang detect triggering warm reboot");
		host_set_single_event(EC_HOST_EVENT_HANG_REBOOT);
		chipset_reset(0);
		active = 0;
		return;
	}

	/* Otherwise, we're starting with the host event */
	CPRINTS("hang detect sending host event");
	host_set_single_event(EC_HOST_EVENT_HANG_DETECT);

	/* If we're also rebooting, defer for the remaining delay */
	if (hdparams.warm_reboot_timeout_msec) {
		CPRINTS("hang detect continuing (for reboot)");
		timeout_will_reboot = 1;
		hook_call_deferred(hang_detect_deferred,
				   (hdparams.warm_reboot_timeout_msec -
				    hdparams.host_event_timeout_msec) * MSEC);
	} else {
		/* Not rebooting, so go back to idle */
		active = 0;
	}
}
/**
 * Politely ask the CPU to enable/disable its own throttling.
 *
 * @param throttle	Enable (!=0) or disable(0) throttling
 */
test_mockable void host_throttle_cpu(int throttle)
{
	if (throttle)
		host_set_single_event(EC_HOST_EVENT_THROTTLE_START);
	else
		host_set_single_event(EC_HOST_EVENT_THROTTLE_STOP);
}
Exemple #3
0
/*
 * Send host events as the battery charge drops below certain thresholds.
 * We handle forced shutdown and other actions elsewhere; this is just for the
 * host events. We send these even if the AP is off, since the AP will read and
 * discard any events it doesn't care about the next time it wakes up.
 */
static void notify_host_of_low_battery(void)
{
	/* We can't tell what the current charge is. Assume it's okay. */
	if (curr.batt.flags & BATT_FLAG_BAD_STATE_OF_CHARGE)
		return;

	if (curr.batt.state_of_charge <= BATTERY_LEVEL_LOW &&
	    prev_charge > BATTERY_LEVEL_LOW)
		host_set_single_event(EC_HOST_EVENT_BATTERY_LOW);

	if (curr.batt.state_of_charge <= BATTERY_LEVEL_CRITICAL &&
	    prev_charge > BATTERY_LEVEL_CRITICAL)
		host_set_single_event(EC_HOST_EVENT_BATTERY_CRITICAL);
}
Exemple #4
0
/**
 * Handle debounced power button changing state.
 */
static void power_button_change_deferred(void)
{
	const int new_pressed = raw_power_button_pressed();

	/* Re-enable keyboard scanning if power button is no longer pressed */
	if (!new_pressed)
		keyboard_scan_enable(1, KB_SCAN_DISABLE_POWER_BUTTON);

	/* If power button hasn't changed state, nothing to do */
	if (new_pressed == debounced_power_pressed) {
		power_button_is_stable = 1;
		return;
	}

	debounced_power_pressed = new_pressed;
	power_button_is_stable = 1;

	CPRINTS("power button %s", new_pressed ? "pressed" : "released");

	/* Call hooks */
	hook_notify(HOOK_POWER_BUTTON_CHANGE);

	/* Notify host if power button has been pressed */
	if (new_pressed)
		host_set_single_event(EC_HOST_EVENT_POWER_BUTTON);
}
Exemple #5
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;
}
Exemple #6
0
/**
 * Deferred function to handle external power change
 */
static void extpower_deferred(void)
{
	int extpower_presence = gpio_get_level(GPIO_AC_PRESENT);

	if (extpower_presence == debounced_extpower_presence)
		return;

	debounced_extpower_presence = extpower_presence;
	hook_notify(HOOK_AC_CHANGE);

	/* Forward notification to host */
	if (extpower_presence)
		host_set_single_event(EC_HOST_EVENT_AC_CONNECTED);
	else
		host_set_single_event(EC_HOST_EVENT_AC_DISCONNECTED);
}
Exemple #7
0
/* Send host event up to AP */
void pd_send_host_event(int mask)
{
	/* mask must be set */
	if (!mask)
		return;

	atomic_or(&(host_event_status.status), mask);
	/* interrupt the AP */
	host_set_single_event(EC_HOST_EVENT_PD_MCU);
}
static void host_command_init(void)
{
	/* Initialize memory map ID area */
	host_get_memmap(EC_MEMMAP_ID)[0] = 'E';
	host_get_memmap(EC_MEMMAP_ID)[1] = 'C';
	*host_get_memmap(EC_MEMMAP_ID_VERSION) = 1;
	*host_get_memmap(EC_MEMMAP_EVENTS_VERSION) = 1;

	host_set_single_event(EC_HOST_EVENT_INTERFACE_READY);
	CPRINTS("hostcmd init 0x%x", host_get_events());
}
Exemple #9
0
/*
 * Send host event to the AP if the battery is temperature or charge level
 * is critical. Force-shutdown if the problem isn't corrected after timeout.
 */
static void shutdown_on_critical_battery(void)
{
	int batt_temp_c;
	int battery_critical = 0;

	/*
	 * TODO(crosbug.com/p/27642): The thermal loop should watch the battery
	 * temp, so it can turn fans on.
	 */
	batt_temp_c = DECI_KELVIN_TO_CELSIUS(curr.batt.temperature);
	if (battery_too_hot(batt_temp_c)) {
		CPRINTS("Batt temp out of range: %dC", batt_temp_c);
		battery_critical = 1;
	}

	if (battery_too_low() && !curr.batt_is_charging) {
		CPRINTS("Low battery: %d%%, %dmV",
			curr.batt.state_of_charge, curr.batt.voltage);
		battery_critical = 1;
	}

	if (!battery_critical) {
		/* Reset shutdown warning time */
		shutdown_warning_time.val = 0;
		return;
	}

	if (!shutdown_warning_time.val) {
		CPRINTS("charge warn shutdown due to critical battery");
		shutdown_warning_time = get_time();
		if (!chipset_in_state(CHIPSET_STATE_ANY_OFF))
			host_set_single_event(EC_HOST_EVENT_BATTERY_SHUTDOWN);
	} else if (get_time().val > shutdown_warning_time.val +
		   CRITICAL_BATTERY_SHUTDOWN_TIMEOUT_US) {
		if (chipset_in_state(CHIPSET_STATE_ANY_OFF)) {
#ifdef CONFIG_HIBERNATE
			/* Timeout waiting for charger to provide more power */
			CPRINTS(
			  "charge force EC hibernate due to critical battery");
			system_hibernate(0, 0);
#elif defined(CONFIG_BATTERY_CRITICAL_SHUTDOWN_CUT_OFF)
			CPRINTS(
			  "charge force battery cut-off due to critical level");
			board_cut_off_battery();
#endif
		} else {
			/* Timeout waiting for AP to shut down, so kill it */
			CPRINTS(
			  "charge force shutdown due to critical battery");
			chipset_force_shutdown();
		}
	}
}
Exemple #10
0
static void panic_init(void)
{
#ifdef CONFIG_HOSTCMD_EVENTS
	struct panic_data *addr = panic_get_data();

	/* Notify host of new panic event */
	if (addr && !(addr->flags & PANIC_DATA_FLAG_OLD_HOSTEVENT)) {
		host_set_single_event(EC_HOST_EVENT_PANIC);
		addr->flags |= PANIC_DATA_FLAG_OLD_HOSTEVENT;
	}
#endif
}
Exemple #11
0
static void board_startup_key_combo(void)
{
	int vold = !gpio_get_level(GPIO_BTN_VOLD_L);
	int volu = !gpio_get_level(GPIO_BTN_VOLU_L);
	int pwr = power_button_signal_asserted();

	/*
	 * Determine recovery mode is requested by the power and
	 * voldown buttons being pressed (while device was off).
	 */
	if (pwr && vold && !volu) {
		host_set_single_event(EC_HOST_EVENT_KEYBOARD_RECOVERY);
		CPRINTS("> RECOVERY mode");
	}

	/*
	 * Determine fastboot mode is requested by the power and
	 * voldown buttons being pressed (while device was off).
	 */
	if (pwr && volu && !vold) {
		host_set_single_event(EC_HOST_EVENT_KEYBOARD_FASTBOOT);
		CPRINTS("> FASTBOOT mode");
	}
}
Exemple #12
0
/**
 * Prevent battery from going into deep discharge state
 */
static void low_battery_shutdown(struct charge_state_context *ctx)
{
	if (chipset_in_state(CHIPSET_STATE_ANY_OFF)) {
		/* AP is off, so shut down the EC now */
		CPRINTS("charge force EC hibernate due to low battery");
		system_hibernate(0, 0);
	} else if (!ctx->shutdown_warning_time.val) {
		/* Warn AP battery level is so low we'll shut down */
		CPRINTS("charge warn shutdown due to low battery");
		ctx->shutdown_warning_time = get_time();
		host_set_single_event(EC_HOST_EVENT_BATTERY_SHUTDOWN);
	} else if (get_time().val > ctx->shutdown_warning_time.val +
		   LOW_BATTERY_SHUTDOWN_TIMEOUT_US) {
		/* Timeout waiting for AP to shut down, so kill it */
		CPRINTS("charge force shutdown due to low battery");
		chipset_force_shutdown();
	}
}
Exemple #13
0
/**
 * Discharge state handler
 *
 *	- detect ac status
 *	- new state: INIT
 */
static enum charge_state state_discharge(struct charge_state_context *ctx)
{
	struct batt_params *batt = &ctx->curr.batt;
	int8_t bat_temp_c = DECI_KELVIN_TO_CELSIUS(batt->temperature);
	if (ctx->curr.ac)
		return PWR_STATE_REINIT;

	if (ctx->curr.error)
		return PWR_STATE_ERROR;

	/* Handle overtemp in discharging state by powering off host */
	if ((bat_temp_c >= ctx->battery->discharging_max_c ||
	     bat_temp_c < ctx->battery->discharging_min_c) &&
	    chipset_in_state(CHIPSET_STATE_ON)) {
		CPRINTS("charge force shutdown due to battery temp");
		chipset_force_shutdown();
		host_set_single_event(EC_HOST_EVENT_BATTERY_SHUTDOWN);
	}
	return PWR_STATE_UNCHANGE;
}
static void thermal_control(void)
{
	int i, j, t, rv, f;
	int count_over[EC_TEMP_THRESH_COUNT];
	int count_under[EC_TEMP_THRESH_COUNT];
	int num_valid_limits[EC_TEMP_THRESH_COUNT];
	int num_sensors_read;
	int fmax;
	int dptf_tripped;
	int temp_fan_configured;

	/* Get ready to count things */
	memset(count_over, 0, sizeof(count_over));
	memset(count_under, 0, sizeof(count_under));
	memset(num_valid_limits, 0, sizeof(num_valid_limits));
	num_sensors_read = 0;
	fmax = 0;
	dptf_tripped = 0;
	temp_fan_configured = 0;

	/* go through all the sensors */
	for (i = 0; i < TEMP_SENSOR_COUNT; ++i) {

		/* read one */
		rv = temp_sensor_read(i, &t);
		if (rv != EC_SUCCESS)
			continue;
		else
			num_sensors_read++;

		/* check all the limits */
		for (j = 0; j < EC_TEMP_THRESH_COUNT; j++) {
			int limit = thermal_params[i].temp_host[j];
			if (limit) {
				num_valid_limits[j]++;
				if (t > limit)
					count_over[j]++;
				else if (t < limit)
					count_under[j]++;
			}
		}

		/* figure out the max fan needed, too */
		if (thermal_params[i].temp_fan_off &&
		    thermal_params[i].temp_fan_max) {
			f = thermal_fan_percent(thermal_params[i].temp_fan_off,
						thermal_params[i].temp_fan_max,
						t);
			if (f > fmax)
				fmax = f;

			temp_fan_configured = 1;
		}

		/* and check the dptf thresholds */
		dptf_tripped |= dpft_check_temp_threshold(i, t);
	}

	if (!num_sensors_read) {
		/*
		 * Trigger a SMI event if we can't read any sensors.
		 *
		 * In theory we could do something more elaborate like forcing
		 * the system to shut down if no sensors are available after
		 * several retries.  This is a very unlikely scenario -
		 * particularly on LM4-based boards, since the LM4 has its own
		 * internal temp sensor.  It's most likely to occur during
		 * bringup of a new board, where we haven't debugged the I2C
		 * bus to the sensors; forcing a shutdown in that case would
		 * merely hamper board bringup.
		 */
		smi_sensor_failure_warning();
		return;
	}

	/* See what the aggregated limits are. Any temp over the limit
	 * means it's hot, but all temps have to be under the limit to
	 * be cool again.
	 */
	for (j = 0; j < EC_TEMP_THRESH_COUNT; j++) {
		if (count_over[j])
			cond_set_true(&cond_hot[j]);
		else if (count_under[j] == num_valid_limits[j])
			cond_set_false(&cond_hot[j]);
	}


	/* What do we do about it? (note hard-coded logic). */

	if (cond_went_true(&cond_hot[EC_TEMP_THRESH_HALT])) {
		CPRINTS("thermal SHUTDOWN");
		chipset_force_shutdown();
	} else if (cond_went_false(&cond_hot[EC_TEMP_THRESH_HALT])) {
		/* We don't reboot automatically - the user has to push
		 * the power button. It's likely that we can't even
		 * detect this sensor transition until then, but we
		 * do have to check in order to clear the cond_t.
		 */
		CPRINTS("thermal no longer shutdown");
	}

	if (cond_went_true(&cond_hot[EC_TEMP_THRESH_HIGH])) {
		CPRINTS("thermal HIGH");
		throttle_ap(THROTTLE_ON, THROTTLE_HARD, THROTTLE_SRC_THERMAL);
	} else if (cond_went_false(&cond_hot[EC_TEMP_THRESH_HIGH])) {
		CPRINTS("thermal no longer high");
		throttle_ap(THROTTLE_OFF, THROTTLE_HARD, THROTTLE_SRC_THERMAL);
	}

	if (cond_went_true(&cond_hot[EC_TEMP_THRESH_WARN])) {
		CPRINTS("thermal WARN");
		throttle_ap(THROTTLE_ON, THROTTLE_SOFT, THROTTLE_SRC_THERMAL);
	} else if (cond_went_false(&cond_hot[EC_TEMP_THRESH_WARN])) {
		CPRINTS("thermal no longer warn");
		throttle_ap(THROTTLE_OFF, THROTTLE_SOFT, THROTTLE_SRC_THERMAL);
	}

	if (temp_fan_configured) {
#ifdef CONFIG_FANS
	/* TODO(crosbug.com/p/23797): For now, we just treat all fans the
	 * same. It would be better if we could assign different thermal
	 * profiles to each fan - in case one fan cools the CPU while another
	 * cools the radios or battery.
	 */
		for (i = 0; i < CONFIG_FANS; i++)
			fan_set_percent_needed(i, fmax);
#endif
	}

	/* Don't forget to signal any DPTF thresholds */
	if (dptf_tripped)
		host_set_single_event(EC_HOST_EVENT_THERMAL_THRESHOLD);
}
test_mockable_static void smi_sensor_failure_warning(void)
{
	CPRINTS("can't read any temp sensors!");
	host_set_single_event(EC_HOST_EVENT_THERMAL);
}
Exemple #16
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;
}
Exemple #17
0
static void pd_exchange_status(void)
{
	struct ec_params_pd_status ec_status;
	struct ec_response_pd_status pd_status;
	int rv = 0;
#ifdef CONFIG_HOSTCMD_PD_PANIC
	static int pd_in_rw;
#endif

	/* Send PD charge state and battery state of charge */
	ec_status.charge_state = charge_state;
	if (charge_get_flags() & CHARGE_FLAG_BATT_RESPONSIVE)
		ec_status.batt_soc = charge_get_percent();
	else
		ec_status.batt_soc = -1;

	rv = pd_host_command(EC_CMD_PD_EXCHANGE_STATUS, 1, &ec_status,
			     sizeof(struct ec_params_pd_status), &pd_status,
			     sizeof(struct ec_response_pd_status));

	/* If PD doesn't support new command version, try old version */
	if (rv == -EC_RES_INVALID_VERSION)
		rv = pd_host_command(EC_CMD_PD_EXCHANGE_STATUS, 0, &ec_status,
			     sizeof(struct ec_params_pd_status), &pd_status,
			     sizeof(struct ec_response_pd_status));

	if (rv < 0) {
		CPRINTS("Host command to PD MCU failed");
		return;
	}

#ifdef CONFIG_HOSTCMD_PD_PANIC
	/*
	 * Check if PD MCU is in RW. If PD MCU was in RW and is now in RO
	 * AND it did not sysjump to RO, then it must have crashed, and
	 * therefore we should panic as well.
	 */
	if (pd_status.status & PD_STATUS_IN_RW) {
		pd_in_rw = 1;
	} else if (pd_in_rw &&
		   !(pd_status.status & PD_STATUS_JUMPED_TO_IMAGE)) {
		panic_printf("PD crash");
		software_panic(PANIC_SW_PD_CRASH, 0);
	}
#endif

#ifdef HAS_TASK_LIGHTBAR
	/*
	 * If charge port has changed, and it was initialized, then show
	 * battery status on lightbar.
	 */
	if (pd_status.active_charge_port != charge_port) {
		if (charge_port != CHARGE_PORT_UNINITIALIZED) {
			charge_port = pd_status.active_charge_port;
			lightbar_sequence(LIGHTBAR_TAP);
		} else {
			charge_port = pd_status.active_charge_port;
		}
	}
#else
	/* Store the active charge port */
	charge_port = pd_status.active_charge_port;
#endif

	/* Set input current limit */
	rv = charge_set_input_current_limit(MAX(pd_status.curr_lim_ma,
					CONFIG_CHARGER_INPUT_CURRENT));
	if (rv < 0)
		CPRINTS("Failed to set input current limit from PD MCU");

	/* If PD is signalling host event, then pass it up to AP */
	if (pd_status.status & PD_STATUS_HOST_EVENT)
		host_set_single_event(EC_HOST_EVENT_PD_MCU);
}
Exemple #18
0
static void update_dynamic_battery_info(void)
{
	/* The memmap address is constant. We should fix these calls somehow. */
	int *memmap_volt = (int *)host_get_memmap(EC_MEMMAP_BATT_VOLT);
	int *memmap_rate = (int *)host_get_memmap(EC_MEMMAP_BATT_RATE);
	int *memmap_cap = (int *)host_get_memmap(EC_MEMMAP_BATT_CAP);
	int *memmap_lfcc = (int *)host_get_memmap(EC_MEMMAP_BATT_LFCC);
	uint8_t *memmap_flags = host_get_memmap(EC_MEMMAP_BATT_FLAG);
	uint8_t tmp;
	int send_batt_status_event = 0;
	int send_batt_info_event = 0;
	static int __bss_slow batt_present;

	tmp = 0;
	if (curr.ac)
		tmp |= EC_BATT_FLAG_AC_PRESENT;

	if (curr.batt.is_present == BP_YES) {
		tmp |= EC_BATT_FLAG_BATT_PRESENT;
		batt_present = 1;
		/* Tell the AP to read battery info if it is newly present. */
		if (!(*memmap_flags & EC_BATT_FLAG_BATT_PRESENT))
			send_batt_info_event++;
	} else {
		/*
		 * Require two consecutive updates with BP_NOT_SURE
		 * before reporting it gone to the host.
		 */
		if (batt_present)
			tmp |= EC_BATT_FLAG_BATT_PRESENT;
		else if (*memmap_flags & EC_BATT_FLAG_BATT_PRESENT)
			send_batt_info_event++;
		batt_present = 0;
	}

	if (!(curr.batt.flags & BATT_FLAG_BAD_VOLTAGE))
		*memmap_volt = curr.batt.voltage;

	if (!(curr.batt.flags & BATT_FLAG_BAD_CURRENT))
		*memmap_rate = ABS(curr.batt.current);

	if (!(curr.batt.flags & BATT_FLAG_BAD_REMAINING_CAPACITY)) {
		/*
		 * If we're running off the battery, it must have some charge.
		 * Don't report zero charge, as that has special meaning
		 * to Chrome OS powerd.
		 */
		if (curr.batt.remaining_capacity == 0 && !curr.batt_is_charging)
			*memmap_cap = 1;
		else
			*memmap_cap = curr.batt.remaining_capacity;
	}

	if (!(curr.batt.flags & BATT_FLAG_BAD_FULL_CAPACITY) &&
	    (curr.batt.full_capacity <= (*memmap_lfcc - LFCC_EVENT_THRESH) ||
	     curr.batt.full_capacity >= (*memmap_lfcc + LFCC_EVENT_THRESH))) {
		*memmap_lfcc = curr.batt.full_capacity;
		/* Poke the AP if the full_capacity changes. */
		send_batt_info_event++;
	}

	if (curr.batt.is_present == BP_YES &&
	    !(curr.batt.flags & BATT_FLAG_BAD_STATE_OF_CHARGE) &&
	    curr.batt.state_of_charge <= BATTERY_LEVEL_CRITICAL)
		tmp |= EC_BATT_FLAG_LEVEL_CRITICAL;

	tmp |= curr.batt_is_charging ? EC_BATT_FLAG_CHARGING :
				       EC_BATT_FLAG_DISCHARGING;

	/* Tell the AP to re-read battery status if charge state changes */
	if (*memmap_flags != tmp)
		send_batt_status_event++;

	/* Update flags before sending host events. */
	*memmap_flags = tmp;

	if (send_batt_info_event)
		host_set_single_event(EC_HOST_EVENT_BATTERY);
	if (send_batt_status_event)
		host_set_single_event(EC_HOST_EVENT_BATTERY_STATUS);
}