static int test_low_battery(void)
{
	test_setup(1);

	ccprintf("[CHARGING TEST] Low battery with AC\n");

	sb_write(SB_RELATIVE_STATE_OF_CHARGE, 2);
	wait_charging_state();
	mock_chipset_state = CHIPSET_STATE_SOFT_OFF;
	hook_notify(HOOK_CHIPSET_SHUTDOWN);
	TEST_ASSERT(!is_hibernated);

	ccprintf("[CHARGING TEST] Low battery shutdown S0->S5\n");
	mock_chipset_state = CHIPSET_STATE_ON;
	hook_notify(HOOK_CHIPSET_PRE_INIT);
	hook_notify(HOOK_CHIPSET_STARTUP);
	gpio_set_level(GPIO_AC_PRESENT, 0);
	is_hibernated = 0;
	sb_write(SB_CURRENT, -1000);
	sb_write(SB_RELATIVE_STATE_OF_CHARGE, 2);
	wait_charging_state();
	mock_chipset_state = CHIPSET_STATE_SOFT_OFF;
	hook_notify(HOOK_CHIPSET_SHUTDOWN);
	wait_charging_state();
	TEST_ASSERT(is_hibernated);

	ccprintf("[CHARGING TEST] Low battery shutdown S5\n");
	is_hibernated = 0;
	sb_write(SB_RELATIVE_STATE_OF_CHARGE, 10);
	wait_charging_state();
	sb_write(SB_RELATIVE_STATE_OF_CHARGE, 2);
	wait_charging_state();
	TEST_ASSERT(is_hibernated);

	ccprintf("[CHARGING TEST] Low battery AP shutdown\n");
	is_shutdown = 0;
	mock_chipset_state = CHIPSET_STATE_ON;
	sb_write(SB_RELATIVE_STATE_OF_CHARGE, 10);
	gpio_set_level(GPIO_AC_PRESENT, 1);
	sb_write(SB_CURRENT, 1000);
	wait_charging_state();
	gpio_set_level(GPIO_AC_PRESENT, 0);
	sb_write(SB_CURRENT, -1000);
	sb_write(SB_RELATIVE_STATE_OF_CHARGE, 2);
	wait_charging_state();
	usleep(32 * SECOND);
	wait_charging_state();
	TEST_ASSERT(is_shutdown);

	return EC_SUCCESS;
}
Пример #2
0
/**
 * Power off the AP
 */
static void power_off(void)
{
	unsigned int power_off_timeout = 100; /* ms */

	/* Call hooks before we drop power rails */
	hook_notify(HOOK_CHIPSET_SHUTDOWN);
	/* switch off all rails */
	chipset_turn_off_power_rails();
	/* Change SUSPEND_L and EC_INT pin to high-Z to reduce power draw. */
	gpio_set_flags(GPIO_SUSPEND_L, GPIO_INPUT);
	gpio_set_flags(GPIO_EC_INT_L, GPIO_INPUT);

	/* Wait till we actually turn off to not mess up the state machine. */
	while (power_get_signals() & IN_POWER_GOOD) {
		msleep(1);
		power_off_timeout--;
		ASSERT(power_off_timeout);
	}

	lid_opened = 0;
	enable_sleep(SLEEP_MASK_AP_RUN);
	powerled_set_state(POWERLED_STATE_OFF);

	CPRINTS("power shutdown complete");
}
Пример #3
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);
}
Пример #4
0
static int lid_test(void)
{
	lid_open = 0;
	hook_notify(HOOK_LID_CHANGE);
	mock_key(1, 1, 1);
	TEST_ASSERT(expect_no_keychange() == EC_SUCCESS);
	mock_key(1, 1, 0);
	TEST_ASSERT(expect_no_keychange() == EC_SUCCESS);

	lid_open = 1;
	hook_notify(HOOK_LID_CHANGE);
	mock_key(1, 1, 1);
	TEST_ASSERT(expect_keychange() == EC_SUCCESS);
	mock_key(1, 1, 0);
	TEST_ASSERT(expect_keychange() == EC_SUCCESS);

	return EC_SUCCESS;
}
Пример #5
0
/**
 * Jump to what we hope is the init address of an image.
 *
 * This function does not return.
 *
 * @param init_addr	Init address of target image
 */
static void jump_to_image(uintptr_t init_addr)
{
	void (*resetvec)(void) = (void(*)(void))init_addr;

	/*
	 * Jumping to any image asserts the signal to the Silego chip that that
	 * EC is not in read-only firmware.  (This is not technically true if
	 * jumping from RO -> RO, but that's not a meaningful use case...).
	 *
	 * Pulse the signal long enough to set the latch in the Silego, then
	 * drop it again so we don't leak power through the pulldown in the
	 * Silego.
	 */
	gpio_set_level(GPIO_ENTERING_RW, 1);
	usleep(MSEC);
	gpio_set_level(GPIO_ENTERING_RW, 0);

#ifdef CONFIG_USB_POWER_DELIVERY
	/*
	 * Notify USB PD module that we are about to sysjump and give it time
	 * to do what it needs.
	 */
	pd_prepare_sysjump();
	usleep(5*MSEC);
#endif

	/* Flush UART output unless the UART hasn't been initialized yet */
	if (uart_init_done())
		uart_flush_output();

	/* Disable interrupts before jump */
	interrupt_disable();

#ifdef CONFIG_DMA
	/* Disable all DMA channels to avoid memory corruption */
	dma_disable_all();
#endif /* CONFIG_DMA */

	/* Fill in preserved data between jumps */
	jdata->reserved0 = 0;
	jdata->magic = JUMP_DATA_MAGIC;
	jdata->version = JUMP_DATA_VERSION;
	jdata->reset_flags = reset_flags;
	jdata->jump_tag_total = 0;  /* Reset tags */
	jdata->struct_size = sizeof(struct jump_data);

	/* Call other hooks; these may add tags */
	hook_notify(HOOK_SYSJUMP);

	/* Jump to the reset vector */
	resetvec();
}
Пример #6
0
static void run_test_step3(void)
{
	lid_open = 1;
	hook_notify(HOOK_LID_CHANGE);
	test_reset();

	RUN_TEST(test_check_boot_down);

	if (test_get_error_count())
		test_reboot_to_next_step(TEST_STATE_FAILED);
	else
		test_reboot_to_next_step(TEST_STATE_PASSED);
}
Пример #7
0
/**
 * Jump to what we hope is the init address of an image.
 *
 * This function does not return.
 *
 * @param init_addr	Init address of target image
 */
static void jump_to_image(uintptr_t init_addr)
{
	void (*resetvec)(void) = (void(*)(void))init_addr;

	/*
	 * Jumping to any image asserts the signal to the Silego chip that that
	 * EC is not in read-only firmware.  (This is not technically true if
	 * jumping from RO -> RO, but that's not a meaningful use case...).
	 *
	 * Pulse the signal long enough to set the latch in the Silego, then
	 * drop it again so we don't leak power through the pulldown in the
	 * Silego.
	 */
	gpio_set_level(GPIO_ENTERING_RW, 1);
	usleep(MSEC);
	gpio_set_level(GPIO_ENTERING_RW, 0);

#ifdef CONFIG_USB_POWER_DELIVERY
	/*
	 * On sysjump, we are most definitely going to drop pings (if any)
	 * and lose all of our PD state. Instead of trying to remember all
	 * the states and deal with on-going transmission, let's send soft
	 * reset here so that the communication starts over without dropping
	 * power.
	 */
	pd_soft_reset();
	usleep(5*MSEC);
#endif

	/* Flush UART output unless the UART hasn't been initialized yet */
	if (uart_init_done())
		uart_flush_output();

	/* Disable interrupts before jump */
	interrupt_disable();

	/* Fill in preserved data between jumps */
	jdata->reserved0 = 0;
	jdata->magic = JUMP_DATA_MAGIC;
	jdata->version = JUMP_DATA_VERSION;
	jdata->reset_flags = reset_flags;
	jdata->jump_tag_total = 0;  /* Reset tags */
	jdata->struct_size = sizeof(struct jump_data);

	/* Call other hooks; these may add tags */
	hook_notify(HOOK_SYSJUMP);

	/* Jump to the reset vector */
	resetvec();
}
Пример #8
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);
}
Пример #9
0
/**
 * Power off the AP
 */
static void power_off(void)
{
	/* Call hooks before we drop power rails */
	hook_notify(HOOK_CHIPSET_SHUTDOWN);
	/* switch off all rails */
	chipset_turn_off_power_rails();
	/* Change SUSPEND_L and EC_INT pin to high-Z to reduce power draw. */
	gpio_set_flags(GPIO_SUSPEND_L, GPIO_INPUT);
	gpio_set_flags(GPIO_EC_INT, GPIO_INPUT);

	lid_opened = 0;
	enable_sleep(SLEEP_MASK_AP_RUN);
	powerled_set_state(POWERLED_STATE_OFF);

	CPRINTS("power shutdown complete");
}
Пример #10
0
/**
 * Jump to what we hope is the init address of an image.
 *
 * This function does not return.
 *
 * @param init_addr	Init address of target image
 */
static void jump_to_image(uintptr_t init_addr)
{
	void (*resetvec)(void) = (void(*)(void))init_addr;

	/*
	 * Jumping to any image asserts the signal to the Silego chip that that
	 * EC is not in read-only firmware.  (This is not technically true if
	 * jumping from RO -> RO, but that's not a meaningful use case...).
	 *
	 * Pulse the signal long enough to set the latch in the Silego, then
	 * drop it again so we don't leak power through the pulldown in the
	 * Silego.
	 */
	gpio_set_level(GPIO_ENTERING_RW, 1);
	usleep(MSEC);
	gpio_set_level(GPIO_ENTERING_RW, 0);

#if defined(CONFIG_I2C) && !defined(CONFIG_I2C_SLAVE_ONLY)
	/* Prepare I2C module for sysjump */
	i2c_prepare_sysjump();
#endif

	/* Flush UART output */
	cflush();

	/* Disable interrupts before jump */
	interrupt_disable();

#ifdef CONFIG_DMA
	/* Disable all DMA channels to avoid memory corruption */
	dma_disable_all();
#endif /* CONFIG_DMA */

	/* Fill in preserved data between jumps */
	jdata->reserved0 = 0;
	jdata->magic = JUMP_DATA_MAGIC;
	jdata->version = JUMP_DATA_VERSION;
	jdata->reset_flags = reset_flags;
	jdata->jump_tag_total = 0;  /* Reset tags */
	jdata->struct_size = sizeof(struct jump_data);

	/* Call other hooks; these may add tags */
	hook_notify(HOOK_SYSJUMP);

	/* Jump to the reset vector */
	resetvec();
}
Пример #11
0
static void run_test_step1(void)
{
	lid_open = 1;
	hook_notify(HOOK_LID_CHANGE);
	test_reset();

	RUN_TEST(deghost_test);
	RUN_TEST(debounce_test);
	RUN_TEST(simulate_key_test);
#ifdef EMU_BUILD
	RUN_TEST(runtime_key_test);
#endif
#ifdef CONFIG_LID_SWITCH
	RUN_TEST(lid_test);
#endif

	if (test_get_error_count())
		test_reboot_to_next_step(TEST_STATE_FAILED);
	else
		test_reboot_to_next_step(TEST_STATE_STEP_2);
}
Пример #12
0
/**
 * Jump to what we hope is the init address of an image.
 *
 * This function does not return.
 *
 * @param init_addr	Init address of target image
 */
static void jump_to_image(uintptr_t init_addr)
{
	void (*resetvec)(void) = (void(*)(void))init_addr;

	/*
	 * Jumping to any image asserts the signal to the Silego chip that that
	 * EC is not in read-only firmware.  (This is not technically true if
	 * jumping from RO -> RO, but that's not a meaningful use case...).
	 *
	 * Pulse the signal long enough to set the latch in the Silego, then
	 * drop it again so we don't leak power through the pulldown in the
	 * Silego.
	 */
	gpio_set_level(GPIO_ENTERING_RW, 1);
	usleep(MSEC);
	gpio_set_level(GPIO_ENTERING_RW, 0);

	/* Flush UART output unless the UART hasn't been initialized yet */
	if (uart_init_done())
		uart_flush_output();

	/* Disable interrupts before jump */
	interrupt_disable();

	/* Fill in preserved data between jumps */
	jdata->reserved0 = 0;
	jdata->magic = JUMP_DATA_MAGIC;
	jdata->version = JUMP_DATA_VERSION;
	jdata->reset_flags = reset_flags;
	jdata->jump_tag_total = 0;  /* Reset tags */
	jdata->struct_size = sizeof(struct jump_data);

	/* Call other hooks; these may add tags */
	hook_notify(HOOK_SYSJUMP);

	/* Jump to the reset vector */
	resetvec();
}
Пример #13
0
static void lpc_chipset_reset(void)
{
	hook_notify(HOOK_CHIPSET_RESET);
}
Пример #14
0
/**
 * Battery charging task
 */
void charger_task(void)
{
	struct charge_state_context *ctx = &task_ctx;
	timestamp_t ts;
	int sleep_usec = CHARGE_POLL_PERIOD_SHORT, diff_usec, sleep_next;
	enum charge_state new_state;
	uint8_t batt_flags;

	while (1) {
#ifdef CONFIG_SB_FIRMWARE_UPDATE
		if (sb_fw_update_in_progress()) {
			task_wait_event(CHARGE_MAX_SLEEP_USEC);
			continue;
		}
#endif
		state_common(ctx);

#ifdef CONFIG_CHARGER_TIMEOUT_HOURS
		if (ctx->curr.state == PWR_STATE_CHARGE &&
		    ctx->charge_state_updated_time.val +
		    CONFIG_CHARGER_TIMEOUT_HOURS * HOUR < ctx->curr.ts.val) {
			CPRINTS("Charge timed out after %d hours",
				CONFIG_CHARGER_TIMEOUT_HOURS);
			charge_force_idle(1);
		}
#endif /* CONFIG_CHARGER_TIMEOUT_HOURS */

		switch (ctx->prev.state) {
		case PWR_STATE_INIT:
		case PWR_STATE_REINIT:
			new_state = state_init(ctx);
			break;
		case PWR_STATE_IDLE0:
			new_state = state_idle(ctx);
			/* If still idling, move from IDLE0 to IDLE */
			if (new_state == PWR_STATE_UNCHANGE)
				new_state = PWR_STATE_IDLE;
			break;
		case PWR_STATE_IDLE:
			new_state = state_idle(ctx);
			break;
		case PWR_STATE_DISCHARGE:
			new_state = state_discharge(ctx);
			break;
		case PWR_STATE_CHARGE:
			new_state = state_charge(ctx);
			if (new_state == PWR_STATE_UNCHANGE &&
			    (ctx->curr.batt.state_of_charge >=
			     BATTERY_LEVEL_NEAR_FULL)) {
				/* Almost done charging */
				new_state = PWR_STATE_CHARGE_NEAR_FULL;
			}
			break;

		case PWR_STATE_CHARGE_NEAR_FULL:
			new_state = state_charge(ctx);
			if (new_state == PWR_STATE_UNCHANGE &&
			    (ctx->curr.batt.state_of_charge <
			     BATTERY_LEVEL_NEAR_FULL)) {
				/* Battery below almost-full threshold. */
				new_state = PWR_STATE_CHARGE;
			}
			break;
		case PWR_STATE_ERROR:
			new_state = state_error(ctx);
			break;
		default:
			CPRINTS("Charge state %d undefined",
				ctx->curr.state);
			ctx->curr.state = PWR_STATE_ERROR;
			new_state = PWR_STATE_ERROR;
		}

		if (state_machine_force_idle &&
		    ctx->prev.state != PWR_STATE_IDLE0 &&
		    ctx->prev.state != PWR_STATE_IDLE &&
		    ctx->prev.state != PWR_STATE_INIT &&
		    ctx->prev.state != PWR_STATE_REINIT)
			new_state = PWR_STATE_REINIT;

		if (new_state) {
			ctx->curr.state = new_state;
			CPRINTS("Charge state %s -> %s after %.6ld sec",
				state_name[ctx->prev.state],
				state_name[new_state],
				ctx->curr.ts.val -
				ctx->charge_state_updated_time.val);
			ctx->charge_state_updated_time = ctx->curr.ts;
			hook_notify(HOOK_CHARGE_STATE_CHANGE);
		}

		switch (new_state) {
		case PWR_STATE_IDLE0:
			/*
			 * First time transitioning from init -> idle.  Don't
			 * set the flags or LED yet because we may transition
			 * to charging on the next call and we don't want to
			 * blink the LED green.
			 */
			sleep_usec = CHARGE_POLL_PERIOD_SHORT;
			break;
		case PWR_STATE_CHARGE_NEAR_FULL:
			/*
			 * Battery is almost charged.  The last few percent
			 * take a loooong time, so fall through and look like
			 * we're charged.  This mirrors similar hacks at the
			 * ACPI/kernel/UI level.
			 */
		case PWR_STATE_IDLE:
			batt_flags = *ctx->memmap_batt_flags;
			batt_flags &= ~EC_BATT_FLAG_CHARGING;
			batt_flags &= ~EC_BATT_FLAG_DISCHARGING;
			*ctx->memmap_batt_flags = batt_flags;

			/* Charge done */
			sleep_usec = (new_state == PWR_STATE_IDLE
				      ? CHARGE_POLL_PERIOD_LONG
				      : CHARGE_POLL_PERIOD_CHARGE);
			break;
		case PWR_STATE_DISCHARGE:
			batt_flags = *ctx->memmap_batt_flags;
			batt_flags &= ~EC_BATT_FLAG_CHARGING;
			batt_flags |= EC_BATT_FLAG_DISCHARGING;
			*ctx->memmap_batt_flags = batt_flags;
			sleep_usec = CHARGE_POLL_PERIOD_LONG;
			break;
		case PWR_STATE_CHARGE:
			batt_flags = *ctx->memmap_batt_flags;
			batt_flags |= EC_BATT_FLAG_CHARGING;
			batt_flags &= ~EC_BATT_FLAG_DISCHARGING;
			*ctx->memmap_batt_flags = batt_flags;

			/* Charging */
			sleep_usec = CHARGE_POLL_PERIOD_CHARGE;
			break;
		case PWR_STATE_ERROR:
			/* Error */
			sleep_usec = CHARGE_POLL_PERIOD_CHARGE;
			break;
		case PWR_STATE_UNCHANGE:
			/* Don't change sleep duration */
			break;
		default:
			/* Other state; poll quickly and hope it goes away */
			sleep_usec = CHARGE_POLL_PERIOD_SHORT;
		}

#ifdef CONFIG_EXTPOWER_FALCO
		watch_adapter_closely(ctx);
		sleep_usec = EXTPOWER_FALCO_POLL_PERIOD;
#endif

		/* Show charging progress in console */
		charging_progress(ctx);

		ts = get_time();
		diff_usec = (int)(ts.val - ctx->curr.ts.val);
		sleep_next = sleep_usec - diff_usec;

		if (ctx->curr.state == PWR_STATE_DISCHARGE &&
		    chipset_in_state(CHIPSET_STATE_ANY_OFF |
				     CHIPSET_STATE_SUSPEND)) {
			/*
			 * Discharging and system is off or suspended, so no
			 * need to poll frequently.  charge_hook() will wake us
			 * up if anything important changes.
			 */
			sleep_next = CHARGE_POLL_PERIOD_VERY_LONG - diff_usec;
		} else if (sleep_next < CHARGE_MIN_SLEEP_USEC) {
			sleep_next = CHARGE_MIN_SLEEP_USEC;
		} else if (sleep_next > CHARGE_MAX_SLEEP_USEC) {
			sleep_next = CHARGE_MAX_SLEEP_USEC;
		}

		task_wait_event(sleep_next);
	}
}
Пример #15
0
/* Test utilities */
static int test_lid_angle(void)
{
	uint8_t *lpc_status = host_get_memmap(EC_MEMMAP_ACC_STATUS);
	uint8_t sample;

	struct motion_sensor_t *base = &motion_sensors[0];
	struct motion_sensor_t *lid = &motion_sensors[1];

	/* Go to S3 state */
	hook_notify(HOOK_CHIPSET_STARTUP);

	/* Go to S0 state */
	hook_notify(HOOK_CHIPSET_RESUME);

	/*
	 * Set the base accelerometer as if it were sitting flat on a desk
	 * and set the lid to closed.
	 */
	base->xyz[X] = 0;
	base->xyz[Y] = 0;
	base->xyz[Z] = 1000;
	lid->xyz[X] = 0;
	lid->xyz[Y] = 0;
	lid->xyz[Z] = 1000;
	sample = *lpc_status & EC_MEMMAP_ACC_STATUS_SAMPLE_ID_MASK;
	task_wake(TASK_ID_MOTIONSENSE);
	while ((*lpc_status & EC_MEMMAP_ACC_STATUS_SAMPLE_ID_MASK) == sample)
		msleep(5);
	TEST_ASSERT(motion_lid_get_angle() == 0);

	/* Set lid open to 90 degrees. */
	lid->xyz[X] = -1000;
	lid->xyz[Y] = 0;
	lid->xyz[Z] = 0;
	sample = *lpc_status & EC_MEMMAP_ACC_STATUS_SAMPLE_ID_MASK;
	task_wake(TASK_ID_MOTIONSENSE);
	while ((*lpc_status & EC_MEMMAP_ACC_STATUS_SAMPLE_ID_MASK) == sample)
		msleep(5);
	TEST_ASSERT(motion_lid_get_angle() == 90);

	/* Set lid open to 225. */
	lid->xyz[X] = 500;
	lid->xyz[Y] = 0;
	lid->xyz[Z] = -500;
	sample = *lpc_status & EC_MEMMAP_ACC_STATUS_SAMPLE_ID_MASK;
	task_wake(TASK_ID_MOTIONSENSE);
	while ((*lpc_status & EC_MEMMAP_ACC_STATUS_SAMPLE_ID_MASK) == sample)
		msleep(5);
	TEST_ASSERT(motion_lid_get_angle() == 225);

	/*
	 * Align base with hinge and make sure it returns unreliable for angle.
	 * In this test it doesn't matter what the lid acceleration vector is.
	 */
	base->xyz[X] = 0;
	base->xyz[Y] = 1000;
	base->xyz[Z] = 0;
	sample = *lpc_status & EC_MEMMAP_ACC_STATUS_SAMPLE_ID_MASK;
	task_wake(TASK_ID_MOTIONSENSE);
	while ((*lpc_status & EC_MEMMAP_ACC_STATUS_SAMPLE_ID_MASK) == sample)
		msleep(5);
	TEST_ASSERT(motion_lid_get_angle() == LID_ANGLE_UNRELIABLE);

	/*
	 * Use all three axes and set lid to negative base and make sure
	 * angle is 180.
	 */
	base->xyz[X] = 500;
	base->xyz[Y] = 400;
	base->xyz[Z] = 300;
	lid->xyz[X] = -500;
	lid->xyz[Y] = -400;
	lid->xyz[Z] = -300;
	sample = *lpc_status & EC_MEMMAP_ACC_STATUS_SAMPLE_ID_MASK;
	task_wake(TASK_ID_MOTIONSENSE);
	while ((*lpc_status & EC_MEMMAP_ACC_STATUS_SAMPLE_ID_MASK) == sample)
		msleep(5);
	TEST_ASSERT(motion_lid_get_angle() == 180);

	return EC_SUCCESS;
}
Пример #16
0
enum power_state power_handle_state(enum power_state state)
{
	int value;
	static int boot_from_g3;

	switch (state) {
	case POWER_G3:
		boot_from_g3 = check_for_power_on_event();
		if (boot_from_g3)
			return POWER_G3S5;
		break;

	case POWER_G3S5:
		return POWER_S5;

	case POWER_S5:
		if (boot_from_g3) {
			value = boot_from_g3;
			boot_from_g3 = 0;
		} else {
			value = check_for_power_on_event();
		}

		if (value) {
			CPRINTS("power on %d", value);
			return POWER_S5S3;
		}
		return state;

	case POWER_S5S3:
		hook_notify(HOOK_CHIPSET_PRE_INIT);

		power_on();

		disable_sleep(SLEEP_MASK_AP_RUN);
		powerled_set_state(POWERLED_STATE_ON);

		if (power_wait_signals(IN_POWER_GOOD) == EC_SUCCESS) {
			CPRINTS("POWER_GOOD seen");
			if (power_button_wait_for_release(
					DELAY_SHUTDOWN_ON_POWER_HOLD) ==
					EC_SUCCESS) {
				power_button_was_pressed = 0;
				set_pmic_pwron(0);

				/* setup misc gpio for S3/S0 functionality */
				gpio_set_flags(GPIO_SUSPEND_L, GPIO_INPUT
					| GPIO_INT_BOTH | GPIO_PULL_DOWN);
				gpio_set_flags(GPIO_EC_INT_L, GPIO_OUTPUT
						| GPIO_OUT_HIGH);

				/* Call hooks now that AP is running */
				hook_notify(HOOK_CHIPSET_STARTUP);

				return POWER_S3;
			} else {
				CPRINTS("long-press button, shutdown");
				power_off();
				/*
				 * Since the AP may be up already, return S0S3
				 * state to go through the suspend hook.
				 */
				return POWER_S0S3;
			}
		} else {
			CPRINTS("POWER_GOOD not seen in time");
		}

		chipset_turn_off_power_rails();
		return POWER_S5;

	case POWER_S3:
		if (!(power_get_signals() & IN_POWER_GOOD))
			return POWER_S3S5;
		else if (!(power_get_signals() & IN_SUSPEND))
			return POWER_S3S0;
		return state;

	case POWER_S3S0:
		powerled_set_state(POWERLED_STATE_ON);
		hook_notify(HOOK_CHIPSET_RESUME);
		return POWER_S0;

	case POWER_S0:
		value = check_for_power_off_event();
		if (value) {
			CPRINTS("power off %d", value);
			power_off();
			return POWER_S0S3;
		} else if (power_get_signals() & IN_SUSPEND)
			return POWER_S0S3;
		return state;

	case POWER_S0S3:
		if (lid_is_open())
			powerled_set_state(POWERLED_STATE_SUSPEND);
		else
			powerled_set_state(POWERLED_STATE_OFF);
		/* Call hooks here since we don't know it prior to AP suspend */
		hook_notify(HOOK_CHIPSET_SUSPEND);
		return POWER_S3;

	case POWER_S3S5:
		power_button_wait_for_release(-1);
		power_button_was_pressed = 0;
		return POWER_S5;

	case POWER_S5G3:
		return POWER_G3;
	}

	return state;
}
Пример #17
0
enum power_state power_handle_state(enum power_state state)
{
	/*
	 * Pass through RSMRST asynchronously, as PCH may not react
	 * immediately to power changes.
	 */
	int rsmrst_in = gpio_get_level(GPIO_RSMRST_L_PGOOD);
	int rsmrst_out = gpio_get_level(GPIO_PCH_RSMRST_L);
#ifdef GLADOS_BOARD_V2
	int tries = 0;
#endif

	if (rsmrst_in != rsmrst_out) {
		/*
		 * Wait at least 10ms between power signals going high
		 * and deasserting RSMRST to PCH.
		 */
		if (rsmrst_in)
			msleep(10);
		gpio_set_level(GPIO_PCH_RSMRST_L, rsmrst_in);
		CPRINTS("RSMRST: %d", rsmrst_in);
	}

	switch (state) {
	case POWER_G3:
		if (forcing_shutdown) {
			power_button_pch_release();
			forcing_shutdown = 0;
		}
		break;

	case POWER_S5:
		if (gpio_get_level(GPIO_PCH_SLP_S4_L) == 1)
			return POWER_S5S3; /* Power up to next state */
		break;

	case POWER_S3:
		if (!power_has_signals(IN_PGOOD_ALL_CORE)) {
			/* Required rail went away */
			chipset_force_shutdown();
			return POWER_S3S5;
		} else if (gpio_get_level(GPIO_PCH_SLP_S3_L) == 1) {
			/* Power up to next state */
			return POWER_S3S0;
		} else if (gpio_get_level(GPIO_PCH_SLP_S4_L) == 0) {
			/* Power down to next state */
			return POWER_S3S5;
		}
		break;

	case POWER_S0:
		if (!power_has_signals(IN_PGOOD_ALL_CORE)) {
			chipset_force_shutdown();
			return POWER_S0S3;
		} else if (gpio_get_level(GPIO_PCH_SLP_S3_L) == 0) {
			/* Power down to next state */
			return POWER_S0S3;
		}
		break;

	case POWER_G3S5:
		/* Call hooks to initialize PMIC */
		hook_notify(HOOK_CHIPSET_PRE_INIT);

		if (power_wait_signals(IN_PCH_SLP_SUS_DEASSERTED)) {
			chipset_force_shutdown();
			return POWER_G3;
		}

#ifdef GLADOS_BOARD_V2
		/*
		 * Allow up to 1s for charger to be initialized, in case
		 * we're trying to boot the AP with no battery.
		 */
		while (charge_prevent_power_on() &&
		       tries++ < CHARGER_INITIALIZED_TRIES) {
			msleep(CHARGER_INITIALIZED_DELAY_MS);
		}

		/* Return to G3 if battery level is too low */
		if (charge_want_shutdown() ||
		    tries == CHARGER_INITIALIZED_TRIES) {
			CPRINTS("power-up inhibited");
			chipset_force_shutdown();
			return POWER_G3;
		}

		/* Allow AP to power on */
		gpio_set_level(GPIO_PMIC_SLP_SUS_L, 1);
		gpio_set_level(GPIO_PCH_BATLOW_L, 1);
#endif

		return POWER_S5;

	case POWER_S5S3:
		if (!power_has_signals(IN_PGOOD_ALL_CORE)) {
			/* Required rail went away */
			chipset_force_shutdown();
			return POWER_S5G3;
		}

		/* Enable TP + USB so that they can wake the system */
		gpio_set_level(GPIO_ENABLE_TOUCHPAD, 1);
		gpio_set_level(GPIO_USB1_ENABLE, 1);
		gpio_set_level(GPIO_USB2_ENABLE, 1);

		/* Call hooks now that rails are up */
		hook_notify(HOOK_CHIPSET_STARTUP);
		return POWER_S3;

	case POWER_S3S0:
		if (!power_has_signals(IN_PGOOD_ALL_CORE)) {
			/* Required rail went away */
			chipset_force_shutdown();
			return POWER_S3S5;
		}

		gpio_set_level(GPIO_ENABLE_BACKLIGHT, 1);

		/* Enable wireless */
		wireless_set_state(WIRELESS_ON);

		/* Call hooks now that rails are up */
		hook_notify(HOOK_CHIPSET_RESUME);

		/*
		 * Disable idle task deep sleep. This means that the low
		 * power idle task will not go into deep sleep while in S0.
		 */
		disable_sleep(SLEEP_MASK_AP_RUN);

		/*
		 * Throttle CPU if necessary.  This should only be asserted
		 * when +VCCP is powered (it is by now).
		 */
		gpio_set_level(GPIO_CPU_PROCHOT, throttle_cpu);

		return POWER_S0;

	case POWER_S0S3:
		/* Call hooks before we remove power rails */
		hook_notify(HOOK_CHIPSET_SUSPEND);

		gpio_set_level(GPIO_ENABLE_BACKLIGHT, 0);

		/* Suspend wireless */
		wireless_set_state(WIRELESS_SUSPEND);

		/*
		 * Enable idle task deep sleep. Allow the low power idle task
		 * to go into deep sleep in S3 or lower.
		 */
		enable_sleep(SLEEP_MASK_AP_RUN);

		return POWER_S3;

	case POWER_S3S5:
		/* Call hooks before we remove power rails */
		hook_notify(HOOK_CHIPSET_SHUTDOWN);

		/* Disable wireless */
		wireless_set_state(WIRELESS_OFF);

		gpio_set_level(GPIO_ENABLE_TOUCHPAD, 0);
		gpio_set_level(GPIO_USB1_ENABLE, 0);
		gpio_set_level(GPIO_USB2_ENABLE, 0);

		return POWER_S5G3;

	case POWER_S5G3:
#ifdef CONFIG_G3_SLEEP
		gpio_set_level(GPIO_G3_SLEEP_EN, 1);
#endif
		chipset_force_g3();
		return POWER_G3;

	default:
		break;
	}

	return state;
}
Пример #18
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);
	}
}
Пример #19
0
/**
 * Set which oscillator is used for the clock
 *
 * @param osc		Oscillator to use
 */
static void clock_set_osc(enum clock_osc osc)
{
	uint32_t tmp_acr;

	if (osc == current_osc)
		return;

	if (current_osc != OSC_INIT)
		hook_notify(HOOK_PRE_FREQ_CHANGE);

	switch (osc) {
	case OSC_HSI:
		/* Ensure that HSI is ON */
		if (!(STM32_RCC_CR & STM32_RCC_CR_HSIRDY)) {
			/* Enable HSI */
			STM32_RCC_CR |= STM32_RCC_CR_HSION;
			/* Wait for HSI to be ready */
			while (!(STM32_RCC_CR & STM32_RCC_CR_HSIRDY))
				;
		}

		/* Disable LPSDSR */
		STM32_PWR_CR &= ~STM32_PWR_CR_LPSDSR;

		/*
		 * Set the recommended flash settings for 16MHz clock.
		 *
		 * The 3 bits must be programmed strictly sequentially.
		 * Also, follow the RM to check 64-bit access and latency bit
		 * after writing those bits to the FLASH_ACR register.
		 */
		tmp_acr = STM32_FLASH_ACR;
		/* Enable 64-bit access */
		tmp_acr |= STM32_FLASH_ACR_ACC64;
		STM32_FLASH_ACR = tmp_acr;
		/* Check ACC64 bit == 1 */
		while (!(STM32_FLASH_ACR & STM32_FLASH_ACR_ACC64))
			;

		/* Enable Prefetch Buffer */
		tmp_acr |= STM32_FLASH_ACR_PRFTEN;
		STM32_FLASH_ACR = tmp_acr;

		/* Flash 1 wait state */
		tmp_acr |= STM32_FLASH_ACR_LATENCY;
		STM32_FLASH_ACR = tmp_acr;
		/* Check LATENCY bit == 1 */
		while (!(STM32_FLASH_ACR & STM32_FLASH_ACR_LATENCY))
			;

		/* Switch to HSI */
		STM32_RCC_CFGR = STM32_RCC_CFGR_SW_HSI;
		/* RM says to check SWS bits to make sure HSI is the sysclock */
		while ((STM32_RCC_CFGR & STM32_RCC_CFGR_SWS_MASK) !=
			STM32_RCC_CFGR_SWS_HSI)
			;

		/* Disable MSI */
		STM32_RCC_CR &= ~STM32_RCC_CR_MSION;

		freq = HSI_CLOCK;
		break;

	case OSC_MSI:
		/* Switch to MSI @ 1MHz */
		STM32_RCC_ICSCR =
			(STM32_RCC_ICSCR & ~STM32_RCC_ICSCR_MSIRANGE_MASK) |
			STM32_RCC_ICSCR_MSIRANGE_1MHZ;
		/* Ensure that MSI is ON */
		if (!(STM32_RCC_CR & STM32_RCC_CR_MSIRDY)) {
			/* Enable MSI */
			STM32_RCC_CR |= STM32_RCC_CR_MSION;
			/* Wait for MSI to be ready */
			while (!(STM32_RCC_CR & STM32_RCC_CR_MSIRDY))
				;
		}

		/* Switch to MSI */
		STM32_RCC_CFGR = STM32_RCC_CFGR_SW_MSI;
		/* RM says to check SWS bits to make sure MSI is the sysclock */
		while ((STM32_RCC_CFGR & STM32_RCC_CFGR_SWS_MASK) !=
			STM32_RCC_CFGR_SWS_MSI)
			;

		/*
		 * Set the recommended flash settings for <= 2MHz clock.
		 *
		 * The 3 bits must be programmed strictly sequentially.
		 * Also, follow the RM to check 64-bit access and latency bit
		 * after writing those bits to the FLASH_ACR register.
		 */
		tmp_acr = STM32_FLASH_ACR;
		/* Flash 0 wait state */
		tmp_acr &= ~STM32_FLASH_ACR_LATENCY;
		STM32_FLASH_ACR = tmp_acr;
		/* Check LATENCY bit == 0 */
		while (STM32_FLASH_ACR & STM32_FLASH_ACR_LATENCY)
			;

		/* Disable prefetch Buffer */
		tmp_acr &= ~STM32_FLASH_ACR_PRFTEN;
		STM32_FLASH_ACR = tmp_acr;

		/* Disable 64-bit access */
		tmp_acr &= ~STM32_FLASH_ACR_ACC64;
		STM32_FLASH_ACR = tmp_acr;
		/* Check ACC64 bit == 0 */
		while (STM32_FLASH_ACR & STM32_FLASH_ACR_ACC64)
			;

		/* Disable HSI */
		STM32_RCC_CR &= ~STM32_RCC_CR_HSION;

		/* Enable LPSDSR */
		STM32_PWR_CR |= STM32_PWR_CR_LPSDSR;

		freq = MSI_1MHZ_CLOCK;
		break;

	default:
		break;
	}

	/* Notify modules of frequency change unless we're initializing */
	if (current_osc != OSC_INIT) {
		current_osc = osc;
		hook_notify(HOOK_FREQ_CHANGE);
	} else {
		current_osc = osc;
	}
}
Пример #20
0
static int test_low_battery(void)
{
	test_setup(1);

	ccprintf("[CHARGING TEST] Low battery with AC and positive current\n");
	sb_write(SB_RELATIVE_STATE_OF_CHARGE, 2);
	sb_write(SB_CURRENT, 1000);
	wait_charging_state();
	mock_chipset_state = CHIPSET_STATE_SOFT_OFF;
	hook_notify(HOOK_CHIPSET_SHUTDOWN);
	TEST_ASSERT(!is_hibernated);

	ccprintf("[CHARGING TEST] Low battery with AC and negative current\n");
	sb_write(SB_CURRENT, -1000);
	wait_charging_state();
	sleep(CONFIG_BATTERY_CRITICAL_SHUTDOWN_TIMEOUT);
	TEST_ASSERT(is_hibernated);

	ccprintf("[CHARGING TEST] Low battery shutdown S0->S5\n");
	mock_chipset_state = CHIPSET_STATE_ON;
	hook_notify(HOOK_CHIPSET_PRE_INIT);
	hook_notify(HOOK_CHIPSET_STARTUP);
	gpio_set_level(GPIO_AC_PRESENT, 0);
	is_hibernated = 0;
	sb_write(SB_CURRENT, -1000);
	sb_write(SB_RELATIVE_STATE_OF_CHARGE, 2);
	wait_charging_state();
	mock_chipset_state = CHIPSET_STATE_SOFT_OFF;
	hook_notify(HOOK_CHIPSET_SHUTDOWN);
	wait_charging_state();
	/* after a while, the EC should hibernate */
	sleep(CONFIG_BATTERY_CRITICAL_SHUTDOWN_TIMEOUT);
	TEST_ASSERT(is_hibernated);

	ccprintf("[CHARGING TEST] Low battery shutdown S5\n");
	is_hibernated = 0;
	sb_write(SB_RELATIVE_STATE_OF_CHARGE, 10);
	wait_charging_state();
	sb_write(SB_RELATIVE_STATE_OF_CHARGE, 2);
	wait_charging_state();
	/* after a while, the EC should hibernate */
	sleep(CONFIG_BATTERY_CRITICAL_SHUTDOWN_TIMEOUT);
	TEST_ASSERT(is_hibernated);

	ccprintf("[CHARGING TEST] Low battery AP shutdown\n");
	is_shutdown = 0;
	mock_chipset_state = CHIPSET_STATE_ON;
	sb_write(SB_RELATIVE_STATE_OF_CHARGE, 10);
	gpio_set_level(GPIO_AC_PRESENT, 1);
	sb_write(SB_CURRENT, 1000);
	wait_charging_state();
	gpio_set_level(GPIO_AC_PRESENT, 0);
	sb_write(SB_CURRENT, -1000);
	sb_write(SB_RELATIVE_STATE_OF_CHARGE, 2);
	wait_charging_state();
	usleep(32 * SECOND);
	wait_charging_state();
	TEST_ASSERT(is_shutdown);

	return EC_SUCCESS;
}
Пример #21
0
enum power_state power_handle_state(enum power_state state)
{
	switch (state) {
	case POWER_G3:
		break;

	case POWER_S5:
		if (gpio_get_level(GPIO_PCH_SLP_S5_L) == 1)
			return POWER_S5S3; /* Power up to next state */

		break;

	case POWER_S3:
		/* Check for state transitions */
		if (!power_has_signals(IN_PGOOD_S3)) {
			/* Required rail went away */
			chipset_force_shutdown();
			return POWER_S3S5;
		} else if (gpio_get_level(GPIO_PCH_SLP_S3_L) == 1) {
			/* Power up to next state */
			return POWER_S3S0;
		} else if (gpio_get_level(GPIO_PCH_SLP_S5_L) == 0) {
			/* Power down to next state */
			return POWER_S3S5;
		}
		break;

	case POWER_S0:
		if (!power_has_signals(IN_PGOOD_S0)) {
			/* Required rail went away */
			chipset_force_shutdown();
			return POWER_S0S3;
		} else if (gpio_get_level(GPIO_PCH_SLP_S3_L) == 0) {
			/* Power down to next state */
			return POWER_S0S3;
		}
		break;

	case POWER_G3S5:
		/* Enable 3.3V DSW */
		gpio_set_level(GPIO_PP3300_DSW_EN, 1);

		/*
		 * Wait 10ms after +3VALW good, since that powers VccDSW and
		 * VccSUS.
		 */
		msleep(10);

		/* Enable PP5000 (5V) rail as 1.05V and 1.2V rails need 5V
		 * rail to regulate properly. */
		gpio_set_level(GPIO_PP5000_EN, 1);

		/* Wait for PP1050/PP1200 PGOOD to go LOW to
		 * indicate that PP5000 is stable */
		while ((power_get_signals() & IN_PGOOD_PP5000) != 0) {
			if (task_wait_event(SECOND) == TASK_EVENT_TIMER) {
				CPRINTS("timeout waiting for PP5000");
				gpio_set_level(GPIO_PP5000_EN, 0);
				chipset_force_shutdown();
				return POWER_G3;
			}
		}

		/* Turn on 3.3V DSW gated rail for core regulator */
		gpio_set_level(GPIO_PP3300_DSW_GATED_EN, 1);

		/* Assert DPWROK */
		gpio_set_level(GPIO_PCH_DPWROK, 1);

		/* Enable PP1050 rail. */
		gpio_set_level(GPIO_PP1050_EN, 1);

		/* Wait for 1.05V to come up and CPU to notice */
		if (power_wait_signals(IN_PGOOD_PP1050 |
				     IN_PCH_SLP_SUS_DEASSERTED)) {
			gpio_set_level(GPIO_PP1050_EN, 0);
			gpio_set_level(GPIO_PP3300_DSW_GATED_EN, 0);
			gpio_set_level(GPIO_PP5000_EN, 0);
			chipset_force_shutdown();
			return POWER_G3;
		}

		/* Wait 5ms for SUSCLK to stabilize */
		msleep(5);

		/* Call hook to indicate out of G3 state */
		hook_notify(HOOK_CHIPSET_PRE_INIT);
		return POWER_S5;

	case POWER_S5S3:
		/* Turn on power to RAM */
		gpio_set_level(GPIO_PP1800_EN, 1);
		gpio_set_level(GPIO_PP1200_EN, 1);
		if (power_wait_signals(IN_PGOOD_S3)) {
			gpio_set_level(GPIO_PP1800_EN, 0);
			gpio_set_level(GPIO_PP1200_EN, 0);
			chipset_force_shutdown();
			return POWER_S5;
		}

		/*
		 * Take lightbar out of reset, now that +5VALW is
		 * available and we won't leak +3VALW through the reset
		 * line.
		 */
		gpio_set_level(GPIO_LIGHTBAR_RESET_L, 1);

		/*
		 * Enable touchpad power so it can wake the system from
		 * suspend.
		 */
		gpio_set_level(GPIO_ENABLE_TOUCHPAD, 1);

		/* Turn on USB power rail. */
		gpio_set_level(GPIO_PP5000_USB_EN, 1);

		/* Call hooks now that rails are up */
		hook_notify(HOOK_CHIPSET_STARTUP);
		return POWER_S3;

	case POWER_S3S0:
		/* Wait 20ms before allowing VCCST_PGOOD to rise. */
		msleep(20);

		/* Enable wireless. */
		wireless_set_state(WIRELESS_ON);

		/* Make sure the touchscreen is on, too. */
		gpio_set_level(GPIO_TOUCHSCREEN_RESET_L, 1);

		/* Wait for non-core power rails good */
		if (power_wait_signals(IN_PGOOD_S0)) {
			chipset_force_shutdown();
			wireless_set_state(WIRELESS_OFF);
			return POWER_S3;
		}

		/* Call hooks now that rails are up */
		hook_notify(HOOK_CHIPSET_RESUME);

		/*
		 * Disable idle task deep sleep. This means that the low
		 * power idle task will not go into deep sleep while in S0.
		 */
		disable_sleep(SLEEP_MASK_AP_RUN);

		/* Wait 99ms after all voltages good */
		msleep(99);

		/*
		 * Throttle CPU if necessary.  This should only be asserted
		 * when +VCCP is powered (it is by now).
		 */
		gpio_set_level(GPIO_CPU_PROCHOT, throttle_cpu);

		/* Set PCH_PWROK */
		gpio_set_level(GPIO_PCH_PWROK, 1);
		gpio_set_level(GPIO_SYS_PWROK, 1);
		return POWER_S0;

	case POWER_S0S3:
		/* Call hooks before we remove power rails */
		hook_notify(HOOK_CHIPSET_SUSPEND);

		/* Clear PCH_PWROK */
		gpio_set_level(GPIO_SYS_PWROK, 0);
		gpio_set_level(GPIO_PCH_PWROK, 0);

		/* Wait 40ns */
		udelay(1);

		/* Suspend wireless */
		wireless_set_state(WIRELESS_SUSPEND);

		/*
		 * Enable idle task deep sleep. Allow the low power idle task
		 * to go into deep sleep in S3 or lower.
		 */
		enable_sleep(SLEEP_MASK_AP_RUN);

		/*
		 * Deassert prochot since CPU is off and we're about to drop
		 * +VCCP.
		 */
		gpio_set_level(GPIO_CPU_PROCHOT, 0);

		return POWER_S3;

	case POWER_S3S5:
		/* Call hooks before we remove power rails */
		hook_notify(HOOK_CHIPSET_SHUTDOWN);

		/* Disable wireless */
		wireless_set_state(WIRELESS_OFF);

		/* Disable peripheral power */
		gpio_set_level(GPIO_ENABLE_TOUCHPAD, 0);
		gpio_set_level(GPIO_PP5000_USB_EN, 0);

		/* Turn off power to RAM */
		gpio_set_level(GPIO_PP1800_EN, 0);
		gpio_set_level(GPIO_PP1200_EN, 0);

		/*
		 * Put touchscreen and lightbar in reset, so we won't
		 * leak +3VALW through the reset line to chips powered
		 * by +5VALW.
		 *
		 * (Note that we're no longer powering down +5VALW due
		 * to crosbug.com/p/16600, but to minimize side effects
		 * of that change we'll still reset these components in
		 * S5.)
		 */
		gpio_set_level(GPIO_TOUCHSCREEN_RESET_L, 0);
		gpio_set_level(GPIO_LIGHTBAR_RESET_L, 0);

		return pause_in_s5 ? POWER_S5 : POWER_S5G3;

	case POWER_S5G3:
		/* Deassert DPWROK */
		gpio_set_level(GPIO_PCH_DPWROK, 0);

		/* Turn off power rails enabled in S5 */
		gpio_set_level(GPIO_PP1050_EN, 0);
		gpio_set_level(GPIO_PP3300_DSW_GATED_EN, 0);
		gpio_set_level(GPIO_PP5000_EN, 0);
		/* Disable 3.3V DSW */
		gpio_set_level(GPIO_PP3300_DSW_EN, 0);
		return POWER_G3;
	}

	return state;
}
Пример #22
0
static enum power_state _power_handle_state(enum power_state state)
{
	int tries = 0;

	switch (state) {
	case POWER_G3:
		break;

	case POWER_S5:
		if (forcing_shutdown) {
			power_button_pch_release();
			forcing_shutdown = 0;
		}

#ifdef CONFIG_BOARD_HAS_RTC_RESET
		/* Wait for S5 exit and attempt RTC reset it supported */
		if (power_s5_up)
			return power_wait_s5_rtc_reset();
#endif
		if (gpio_get_level(GPIO_PCH_SLP_S4_L) == 1)
			return POWER_S5S3; /* Power up to next state */
		break;

	case POWER_S3:
		if (!power_has_signals(IN_PGOOD_ALL_CORE)) {
			/* Required rail went away */
			chipset_force_shutdown();
			return POWER_S3S5;
		} else if (gpio_get_level(GPIO_PCH_SLP_S3_L) == 1) {
			/* Power up to next state */
			return POWER_S3S0;
		} else if (gpio_get_level(GPIO_PCH_SLP_S4_L) == 0) {
			/* Power down to next state */
			return POWER_S3S5;
		}
		break;

	case POWER_S0:
		if (!power_has_signals(IN_PGOOD_ALL_CORE)) {
			chipset_force_shutdown();
			return POWER_S0S3;
#ifdef CONFIG_POWER_S0IX
		} else if ((gpio_get_level(GPIO_PCH_SLP_S0_L) == 0) &&
			   (gpio_get_level(GPIO_PCH_SLP_S3_L) == 1)) {
			return POWER_S0S0ix;
#endif
		} else if (gpio_get_level(GPIO_PCH_SLP_S3_L) == 0) {
			/* Power down to next state */
			return POWER_S0S3;
		}

		break;

#ifdef CONFIG_POWER_S0IX
	case POWER_S0ix:
		/*
		 * TODO: add code for unexpected power loss
		 */
		if ((gpio_get_level(GPIO_PCH_SLP_S0_L) == 1) &&
		   (gpio_get_level(GPIO_PCH_SLP_S3_L) == 1)) {
			return POWER_S0ixS0;
		}

		break;
#endif

	case POWER_G3S5:
		/* Call hooks to initialize PMIC */
		hook_notify(HOOK_CHIPSET_PRE_INIT);

		/*
		 * Allow up to 1s for charger to be initialized, in case
		 * we're trying to boot the AP with no battery.
		 */
		while (charge_prevent_power_on(0) &&
		       tries++ < CHARGER_INITIALIZED_TRIES) {
			msleep(CHARGER_INITIALIZED_DELAY_MS);
		}

		/* Return to G3 if battery level is too low */
		if (charge_want_shutdown() ||
		    tries > CHARGER_INITIALIZED_TRIES) {
			CPRINTS("power-up inhibited");
			chipset_force_shutdown();
			return POWER_G3;
		}

		if (power_wait_signals(IN_PCH_SLP_SUS_DEASSERTED)) {
			chipset_force_shutdown();
			return POWER_G3;
		}

		power_s5_up = 1;
		return POWER_S5;

	case POWER_S5S3:
		if (!power_has_signals(IN_PGOOD_ALL_CORE)) {
			/* Required rail went away */
			chipset_force_shutdown();
			return POWER_S5G3;
		}

		/* Call hooks now that rails are up */
		hook_notify(HOOK_CHIPSET_STARTUP);
		return POWER_S3;

	case POWER_S3S0:
		if (!power_has_signals(IN_PGOOD_ALL_CORE)) {
			/* Required rail went away */
			chipset_force_shutdown();
			return POWER_S3S5;
		}

		gpio_set_level(GPIO_ENABLE_BACKLIGHT, 1);

		/* Enable wireless */
		wireless_set_state(WIRELESS_ON);

		/* Call hooks now that rails are up */
		hook_notify(HOOK_CHIPSET_RESUME);

		/*
		 * Disable idle task deep sleep. This means that the low
		 * power idle task will not go into deep sleep while in S0.
		 */
		disable_sleep(SLEEP_MASK_AP_RUN);

		/*
		 * Throttle CPU if necessary.  This should only be asserted
		 * when +VCCP is powered (it is by now).
		 */
		gpio_set_level(GPIO_CPU_PROCHOT, throttle_cpu);

		return POWER_S0;

	case POWER_S0S3:
		/* Call hooks before we remove power rails */
		hook_notify(HOOK_CHIPSET_SUSPEND);

		gpio_set_level(GPIO_ENABLE_BACKLIGHT, 0);

		/* Suspend wireless */
		wireless_set_state(WIRELESS_SUSPEND);

		/*
		 * Enable idle task deep sleep. Allow the low power idle task
		 * to go into deep sleep in S3 or lower.
		 */
		enable_sleep(SLEEP_MASK_AP_RUN);

		return POWER_S3;

#ifdef CONFIG_POWER_S0IX
	case POWER_S0S0ix:
		/* call hooks before standby */
		hook_notify(HOOK_CHIPSET_SUSPEND);

		lpc_enable_wake_mask_for_lid_open();

		/*
		 * Enable idle task deep sleep. Allow the low power idle task
		 * to go into deep sleep in S0ix.
		 */
		enable_sleep(SLEEP_MASK_AP_RUN);

		return POWER_S0ix;


	case POWER_S0ixS0:
		lpc_disable_wake_mask_for_lid_open();

		/* Call hooks now that rails are up */
		hook_notify(HOOK_CHIPSET_RESUME);

		/*
		 * Disable idle task deep sleep. This means that the low
		 * power idle task will not go into deep sleep while in S0.
		 */
		disable_sleep(SLEEP_MASK_AP_RUN);

		return POWER_S0;
#endif

	case POWER_S3S5:
		/* Call hooks before we remove power rails */
		hook_notify(HOOK_CHIPSET_SHUTDOWN);

		/* Disable wireless */
		wireless_set_state(WIRELESS_OFF);

		/* Always enter into S5 state. The S5 state is required to
		 * correctly handle global resets which have a bit of delay
		 * while the SLP_Sx_L signals are asserted then deasserted. */
		power_s5_up = 0;
		return POWER_S5;

	case POWER_S5G3:
		chipset_force_g3();
		return POWER_G3;

	default:
		break;
	}

	return state;
}
Пример #23
0
/**
 * Set system clock oscillator
 *
 * @param osc		Oscillator to use
 * @param pll_osc	Source oscillator for PLL. Ignored if osc is not PLL.
 */
static void clock_set_osc(enum clock_osc osc, enum clock_osc pll_osc)
{
	uint32_t val;

	if (osc == current_osc)
		return;

	if (current_osc != OSC_INIT)
		hook_notify(HOOK_PRE_FREQ_CHANGE);

	switch (osc) {
	case OSC_HSI:
		/* Ensure that HSI is ON */
		clock_enable_osc(osc);

		/* Disable LPSDSR */
		STM32_PWR_CR &= ~STM32_PWR_CR_LPSDSR;

		/* Switch to HSI */
		clock_switch_osc(osc);

		/* Disable MSI */
		STM32_RCC_CR &= ~STM32_RCC_CR_MSION;

		freq = STM32_HSI_CLOCK;
		break;

	case OSC_MSI:
		/* Switch to MSI @ 1MHz */
		STM32_RCC_ICSCR =
			(STM32_RCC_ICSCR & ~STM32_RCC_ICSCR_MSIRANGE_MASK) |
			STM32_RCC_ICSCR_MSIRANGE_1MHZ;
		/* Ensure that MSI is ON */
		clock_enable_osc(osc);

		/* Switch to MSI */
		clock_switch_osc(osc);

		/* Disable HSI */
		STM32_RCC_CR &= ~STM32_RCC_CR_HSION;

		/* Enable LPSDSR */
		STM32_PWR_CR |= STM32_PWR_CR_LPSDSR;

		freq = STM32_MSI_CLOCK;
		break;

#ifdef STM32_HSE_CLOCK
	case OSC_HSE:
		/* Ensure that HSE is stable */
		clock_enable_osc(osc);

		/* Switch to HSE */
		clock_switch_osc(osc);

		/* Disable other clock sources */
		STM32_RCC_CR &= ~(STM32_RCC_CR_MSION | STM32_RCC_CR_HSION |
				STM32_RCC_CR_PLLON);

		freq = STM32_HSE_CLOCK;

		break;
#endif
	case OSC_PLL:
		/* Ensure that source clock is stable */
		clock_enable_osc(pll_osc);

		/* Configure PLLCFGR */
		freq = stm32_configure_pll(pll_osc, STM32_PLLM,
					   STM32_PLLN, STM32_PLLR);
		ASSERT(freq > 0);

		/* Adjust flash latency as instructed in TRM */
		val = STM32_FLASH_ACR;
		val &= ~STM32_FLASH_ACR_LATENCY_MASK;
		/* Flash 4 wait state. TODO: Should depend on freq. */
		val |= 4 << STM32_FLASH_ACR_LATENCY_SHIFT;
		STM32_FLASH_ACR = val;
		while (STM32_FLASH_ACR != val)
			;

		/* Switch to PLL */
		clock_switch_osc(osc);

		/* TODO: Disable other sources */
		break;
	default:
		break;
	}

	/* Notify modules of frequency change unless we're initializing */
	if (current_osc != OSC_INIT) {
		current_osc = osc;
		hook_notify(HOOK_FREQ_CHANGE);
	} else {
		current_osc = osc;
	}
}