summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLouis Yung-Chieh Lo <yjlou@chromium.org>2014-01-09 11:22:50 -0800
committerchrome-internal-fetch <chrome-internal-fetch@google.com>2014-01-17 18:17:09 +0000
commit2c0292958f2d7156be1e186e7a02d53c2699e38e (patch)
treecea3bf49ce1a5ebb3b4498599d5227b902e08008
parent21dd8328719aa3c150899df1ab176cbab5bcc9ee (diff)
downloadchrome-ec-2c0292958f2d7156be1e186e7a02d53c2699e38e.tar.gz
tegra: refactor the power state machine.
Integrate with power/common.c -- a real state machine now. Also remove duplicate and unused functions/consts. BUG=chrome-os-partner:24831 BRANCH=nyan TEST=on nyan rev 3.12. re-plug AC: PASS, power on 2 reboot: PASS, power on 2 power off (S5), power on: PASS, power off 4, power on 5 power off (G3), power on: PASS, power off 4, power on 5 lid close / power off (S5)/ lid open: PASS, power on 3 lid close / power off (G3)/ lid open: PASS, power on 3 press power button and release: nothing happens after 15s. button off (S5)/ on: PASS, power off 3, power on 4 button off (G3)/ on: PASS, power off 3, power on 4 power off (S5)/ button on: PASS, power off 4, power on 4 power off (G3)/ button on: PASS, power off 4, power on 4 button off (S5)/ power on: PASS, power off 3, power on 5 button off (G3)/ power on: PASS, power off 3, power on 4 button off (S5)/ lid open: PASS, power off 3, power on 3 button off (G3)/ lid open: PASS, power off 3, power on 3 is off, long press button (60s): power on 4, too long, shutdown, stay off is on, long press button (60s): power off 3, stay off apreset cold: entered to S5, power off 3, power on 5 apreset warm: power state is not changed, but reboots to BIOS. Change-Id: Ie12fa4f79b6156f71f89155b2b01880914809c75 Signed-off-by: Louis Yung-Chieh Lo <yjlou@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/182348 Reviewed-by: Randall Spangler <rspangler@chromium.org>
-rw-r--r--board/nyan/board.c8
-rw-r--r--board/nyan/board.h7
-rw-r--r--include/power.h2
-rw-r--r--power/build.mk2
-rw-r--r--power/tegra.c321
5 files changed, 122 insertions, 218 deletions
diff --git a/board/nyan/board.c b/board/nyan/board.c
index 0daa122fe9..7813af3a2b 100644
--- a/board/nyan/board.c
+++ b/board/nyan/board.c
@@ -13,6 +13,7 @@
#include "keyboard_raw.h"
#include "lid_switch.h"
#include "pmu_tpschrome.h"
+#include "power.h"
#include "power_button.h"
#include "power.h"
#include "pwm.h"
@@ -100,6 +101,13 @@ const struct gpio_alt_func gpio_alt_funcs[] = {
};
const int gpio_alt_funcs_count = ARRAY_SIZE(gpio_alt_funcs);
+/* power signal list. Must match order of enum power_signal. */
+const struct power_signal_info power_signal_list[] = {
+ {GPIO_SOC1V8_XPSHOLD, 1, "XPSHOLD"},
+ {GPIO_SUSPEND_L, 0, "SUSPEND#_ASSERTED"},
+};
+BUILD_ASSERT(ARRAY_SIZE(power_signal_list) == POWER_SIGNAL_COUNT);
+
/* I2C ports */
const struct i2c_port_t i2c_ports[] = {
{"master", I2C_PORT_MASTER, 100},
diff --git a/board/nyan/board.h b/board/nyan/board.h
index ebbe266ea9..60df3ee303 100644
--- a/board/nyan/board.h
+++ b/board/nyan/board.h
@@ -95,6 +95,13 @@ enum gpio_signal {
GPIO_COUNT
};
+enum power_signal {
+ TEGRA_XPSHOLD = 0,
+ TEGRA_SUSPEND_ASSERTED,
+
+ /* Number of power signals */
+ POWER_SIGNAL_COUNT
+};
enum pwm_channel {
PWM_CH_POWER_LED = 0,
diff --git a/include/power.h b/include/power.h
index e4f4ac1353..80b977a81b 100644
--- a/include/power.h
+++ b/include/power.h
@@ -90,7 +90,7 @@ enum power_state power_handle_state(enum power_state state);
/**
* Interrupt handler for chipset GPIOs.
*/
-#ifdef CONFIG_CHIPSET_X86
+#if defined(CONFIG_CHIPSET_X86) || defined(CONFIG_CHIPSET_TEGRA)
void power_signal_interrupt(enum gpio_signal signal);
#else
#define power_signal_interrupt NULL
diff --git a/power/build.mk b/power/build.mk
index 2042cdee34..7afcc38233 100644
--- a/power/build.mk
+++ b/power/build.mk
@@ -10,5 +10,5 @@ power-$(CONFIG_CHIPSET_BAYTRAIL)+=baytrail.o
power-$(CONFIG_CHIPSET_GAIA)+=gaia.o
power-$(CONFIG_CHIPSET_HASWELL)+=haswell.o
power-$(CONFIG_CHIPSET_IVYBRIDGE)+=ivybridge.o
-power-$(CONFIG_CHIPSET_TEGRA)+=tegra.o
+power-$(CONFIG_CHIPSET_TEGRA)+=tegra.o common.o
power-$(CONFIG_CHIPSET_X86)+=common.o
diff --git a/power/tegra.c b/power/tegra.c
index 42f57b5023..79cd1e5899 100644
--- a/power/tegra.c
+++ b/power/tegra.c
@@ -18,19 +18,22 @@
* When powered on:
* - The PMIC PWRON signal is released <= 1 second after the power button is
* released
- * - Holding pwron for 9s powers off the AP
- * - Pressing and releasing pwron within that 9s is ignored
+ * - Holding pwron for 10.2s powers off the AP
+ * - Pressing and releasing pwron within that 10.2s is ignored
* - If XPSHOLD is dropped by the AP, then we power the AP off
+ * - If SUSPEND_L goes low, enter suspend mode.
+ *
*/
-#include "clock.h"
#include "chipset.h" /* This module implements chipset functions too */
+#include "clock.h"
#include "common.h"
#include "console.h"
#include "gpio.h"
#include "hooks.h"
#include "lid_switch.h"
#include "keyboard_scan.h"
+#include "power.h"
#include "power_button.h"
#include "power_led.h"
#include "pmu_tpschrome.h"
@@ -43,6 +46,10 @@
#define CPUTS(outstr) cputs(CC_CHIPSET, outstr)
#define CPRINTF(format, args...) cprintf(CC_CHIPSET, format, ## args)
+/* masks for power signals */
+#define IN_XPSHOLD POWER_SIGNAL_MASK(TEGRA_XPSHOLD)
+#define IN_SUSPEND POWER_SIGNAL_MASK(TEGRA_SUSPEND_ASSERTED)
+
/* Long power key press to force shutdown */
#define DELAY_FORCE_SHUTDOWN (10200 * MSEC) /* 10.2 seconds */
@@ -70,12 +77,6 @@
#define DELAY_SHUTDOWN_ON_POWER_HOLD (10200 * MSEC) /* 10.2 seconds */
/*
- * nyan's GPIO_SOC1V8_XPSHOLD will go low for 36~40ms after PMIC_PWRON_L is low.
- * XPSHOLD_DEBOUNCE is waiting slightly longer.
- */
-#define XPSHOLD_TIMEOUT (50 * MSEC) /* 50 ms */
-
-/*
* The hold time for pulling down the PMIC_WARM_RESET_L pin so that
* the AP can entery the recovery mode (flash SPI flash from USB).
*/
@@ -87,14 +88,7 @@
* time of approx. 0.5msec until V2_5 regulator starts up. */
#define PMIC_RTC_STARTUP (225 * MSEC)
-/* Application processor power state */
-static int ap_on;
-static int ap_suspended;
-
-/* simulated event state */
-static int force_signal = -1;
-static int force_value;
-
+/* TODO(crosbug.com/p/25047): move to HOOK_POWER_BUTTON_CHANGE */
/* 1 if the power button was pressed last time we checked */
static char power_button_was_pressed;
@@ -118,40 +112,6 @@ enum power_request_t {
static enum power_request_t power_request;
/**
- * Wait for GPIO "signal" to reach level "value".
- * Returns EC_ERROR_TIMEOUT if timeout before reaching the desired state.
- *
- * @param signal Signal to watch
- * @param value Value to watch for
- * @param timeout Timeout in microseconds from now, or -1 to wait forever
- * @return 0 if signal did change to required value, EC_ERROR_TIMEOUT if we
- * timed out first.
- */
-static int wait_in_signal(enum gpio_signal signal, int value, int timeout)
-{
- timestamp_t deadline;
- timestamp_t now = get_time();
-
- deadline.val = now.val + timeout;
-
- while (((force_signal != signal) || (force_value != value)) &&
- gpio_get_level(signal) != value) {
- now = get_time();
- if (timeout < 0) {
- task_wait_event(-1);
- } else if (timestamp_expired(deadline, &now) ||
- (task_wait_event(deadline.val - now.val) ==
- TASK_EVENT_TIMER)) {
- CPRINTF("[%T power timeout waiting for GPIO %d/%s]\n",
- signal, gpio_get_name(signal));
- return EC_ERROR_TIMEOUT;
- }
- }
-
- return EC_SUCCESS;
-}
-
-/**
* Set the AP RESET signal.
*
* This fucntion is for backward-compatible.
@@ -246,62 +206,12 @@ static int check_for_power_off_event(void)
power_button_was_pressed = pressed;
/* XPSHOLD released by AP : shutdown immediately */
- if (gpio_get_level(GPIO_SOC1V8_XPSHOLD) == 0)
+ if (!power_has_signals(IN_XPSHOLD))
return 3;
return 0;
}
-/**
- * Deferred handling for suspend events
- *
- * The suspend event needs to be able to call the suspend and resume hooks.
- * This cannot be done from interrupt level, since the handlers from those
- * hooks may need to use mutexes or other functionality not present at
- * interrupt level. Use a deferred function instead.
- *
- * Deferred functions are called from the hook task and not the chipset task,
- * so that's a slight deviation from the spec in hooks.h, but a minor one.
- */
-static void tegra_suspend_deferred(void)
-{
- int new_ap_suspended;
-
- if (!ap_on) /* power on/off : not a real suspend / resume */
- return;
-
- new_ap_suspended = !gpio_get_level(GPIO_SUSPEND_L);
-
- /* We never want to call two suspend or two resumes in a row */
- if (ap_suspended == new_ap_suspended)
- return;
-
- ap_suspended = new_ap_suspended;
-
- if (ap_suspended) {
- 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);
- } else {
- powerled_set_state(POWERLED_STATE_ON);
- hook_notify(HOOK_CHIPSET_RESUME);
- }
-}
-DECLARE_DEFERRED(tegra_suspend_deferred);
-
-void power_signal_interrupt(enum gpio_signal signal)
-{
- if (signal == GPIO_SUSPEND_L) {
- /* Handle suspend events in the hook task */
- hook_call_deferred(tegra_suspend_deferred, 0);
- } else {
- /* All other events are handled in the chipset task */
- task_wake(TASK_ID_CHIPSET);
- }
-}
static void tegra_lid_event(void)
{
@@ -314,12 +224,8 @@ static void tegra_lid_event(void)
}
DECLARE_HOOK(HOOK_LID_CHANGE, tegra_lid_event, HOOK_PRIO_DEFAULT);
-static int tegra_power_init(void)
+enum power_state power_chipset_init(void)
{
- /* Enable interrupts for our GPIOs */
- gpio_enable_interrupt(GPIO_SOC1V8_XPSHOLD);
- gpio_enable_interrupt(GPIO_SUSPEND_L);
-
/*
* Force the AP shutdown unless we are doing SYSJUMP. Otherwise,
* the AP could stay in strange state.
@@ -342,38 +248,12 @@ static int tegra_power_init(void)
auto_power_on = 1;
}
- return EC_SUCCESS;
+ return POWER_G3;
}
/*****************************************************************************/
/* Chipset interface */
-int chipset_in_state(int state_mask)
-{
- /* If AP is off, match any off state for now */
- if ((state_mask & CHIPSET_STATE_ANY_OFF) && !ap_on)
- return 1;
-
- /* If AP is on, match on state */
- if ((state_mask & CHIPSET_STATE_ON) && ap_on && !ap_suspended)
- return 1;
-
- /* if AP is suspended, match on state */
- if ((state_mask & CHIPSET_STATE_SUSPEND) && ap_on && ap_suspended)
- return 1;
-
- /* In any other case, we don't have a match */
- return 0;
-}
-
-void chipset_exit_hard_off(void)
-{
- /*
- * TODO(crosbug.com/p/23822): Implement, if/when we take the AP down to
- * a hard-off state.
- */
-}
-
void chipset_force_shutdown(void)
{
/* Release the power button, if it was asserted */
@@ -401,7 +281,7 @@ void chipset_force_shutdown(void)
static int check_for_power_on_event(void)
{
/* check if system is already ON */
- if (gpio_get_level(GPIO_SOC1V8_XPSHOLD)) {
+ if (power_get_signals() & IN_XPSHOLD) {
CPRINTF("[%T system is on, thus clear auto_power_on]\n");
auto_power_on = 0; /* no need to arrange another power on */
return 1;
@@ -433,10 +313,8 @@ static int check_for_power_on_event(void)
/**
* Power on the AP
- *
- * @return 0 if ok, -1 on error (PP1800_LDO2 failed to come on)
*/
-static int power_on(void)
+static void power_on(void)
{
uint64_t t;
@@ -461,18 +339,14 @@ static int power_on(void)
usleep(PMIC_PWRON_DEBOUNCE_TIME);
/* Initialize non-AP components if the AP is off. */
- if (!ap_on)
+ if (chipset_in_state(CHIPSET_STATE_ANY_OFF))
hook_notify(HOOK_CHIPSET_PRE_INIT);
- ap_on = 1;
disable_sleep(SLEEP_MASK_AP_RUN);
powerled_set_state(POWERLED_STATE_ON);
/* Call hooks now that AP is running */
hook_notify(HOOK_CHIPSET_STARTUP);
-
- CPRINTF("[%T AP running ...]\n");
- return 0;
}
/**
@@ -502,28 +376,11 @@ static int wait_for_power_button_release(unsigned int timeout_us)
}
CPRINTF("[%T power button released]\n");
+ power_button_was_pressed = 0;
return EC_SUCCESS;
}
/**
- * Wait for the XPSHOLD signal from the AP to be asserted.
- *
- * @return 0 if ok, -1 if XPSHOLD doesn't show up in time.
- */
-static int wait_for_xpshold(void)
-{
- wait_in_signal(GPIO_SOC1V8_XPSHOLD, 1, XPSHOLD_TIMEOUT);
-
- if (gpio_get_level(GPIO_SOC1V8_XPSHOLD) == 0) {
- CPRINTF("[%T XPSHOLD not seen in time]\n");
- return -1;
- }
-
- CPRINTF("[%T XPSHOLD seen]\n");
- return 0;
-}
-
-/**
* Power off the AP
*/
static void power_off(void)
@@ -532,8 +389,7 @@ static void power_off(void)
hook_notify(HOOK_CHIPSET_SHUTDOWN);
/* switch off all rails */
chipset_force_shutdown();
- ap_on = 0;
- ap_suspended = 0;
+
lid_opened = 0;
enable_sleep(SLEEP_MASK_AP_RUN);
powerled_set_state(POWERLED_STATE_OFF);
@@ -557,29 +413,27 @@ void chipset_reset(int is_cold)
}
}
-/*
- * Calculates the delay in microseconds to the next time we have to check
- * for a power event,
- *
- *@return delay to next check, or -1 if no future check is needed
- */
-static int next_pwr_event(void)
-{
- if (!power_off_deadline.val)
- return -1;
-
- return power_off_deadline.val - get_time().val;
-}
-
-/*****************************************************************************/
-static int wait_for_power_on(void)
+enum power_state power_handle_state(enum power_state state)
{
int value;
- while (1) {
- value = check_for_power_on_event();
- if (!value) {
- task_wait_event(-1);
- continue;
+ 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();
}
#ifdef HAS_TASK_CHARGER
@@ -590,46 +444,81 @@ static int wait_for_power_on(void)
*/
if (value != 1 && charge_keep_power_off()) {
CPRINTF("[%T power on ignored due to low battery]\n");
- continue;
+ return state;
}
#endif
- CPRINTF("[%T power on %d]\n", value);
- return value;
- }
-}
-
-void chipset_task(void)
-{
- int value;
-
- tegra_power_init();
- ap_on = 0;
+ if (value) {
+ CPRINTF("[%T power on %d]\n", value);
+ return POWER_S5S3;
+ }
+ return state;
+
+ case POWER_S5S3:
+ power_on();
+ if (power_wait_signals(IN_XPSHOLD) == EC_SUCCESS) {
+ CPRINTF("[%T XPSHOLD seen]\n");
+ if (wait_for_power_button_release(
+ DELAY_SHUTDOWN_ON_POWER_HOLD) ==
+ EC_SUCCESS) {
+ set_pmic_pwron(0);
+ CPRINTF("[%T AP running ...]\n");
+ return POWER_S3;
+ } else {
+ CPRINTF("[%T long-press button, shutdown]\n");
+ power_off();
+ /*
+ * Since the AP may be up already, return S0S3
+ * state to go through the suspend hook.
+ */
+ return POWER_S0S3;
+ }
+ } else {
+ CPRINTF("[%T XPSHOLD not seen in time]\n");
+ }
+ set_pmic_pwron(0);
+ return POWER_S5;
- while (1) {
- /* Wait until we need to power on, then power on */
- wait_for_power_on();
+ case POWER_S3:
+ if (!(power_get_signals() & IN_XPSHOLD))
+ return POWER_S3S5;
+ else if (!(power_get_signals() & IN_SUSPEND))
+ return POWER_S3S0;
+ return state;
- if (!power_on()) {
- int continue_power = 0;
+ 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) {
+ CPRINTF("[%T power off %d]\n", 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;
- if (!wait_for_xpshold()) {
- /* AP looks good */
- if (!wait_for_power_button_release(
- DELAY_SHUTDOWN_ON_POWER_HOLD))
- continue_power = 1;
- }
- set_pmic_pwron(0);
- if (continue_power) {
- power_button_was_pressed = 0;
- while (!(value = check_for_power_off_event()))
- task_wait_event(next_pwr_event());
- CPRINTF("[%T power ending loop %d]\n", value);
- }
- }
- power_off();
+ case POWER_S3S5:
wait_for_power_button_release(-1);
+ return POWER_S5;
+
+ case POWER_S5G3:
+ return POWER_G3;
}
+
+ return state;
}
static void powerbtn_tegra_changed(void)