From ebf92ecc839a6361605eb4c3ac7cb44fa4eb603a Mon Sep 17 00:00:00 2001 From: Kyoung Kim Date: Thu, 1 Oct 2015 19:22:01 -0700 Subject: [PATCH] Kunimitsu: Add S0ix on SLP_S0 assertion On assertion of SLP_S0, EC goes to S0ix while system is in Lucid sleep and EC is eligable to enter heavy sleep idle task. Wakeup from S0ix by lid open, any key press, power button or track pad will be done by PCH block by asserting SLP_S0. At S0ix, 1 msec pulse will be generated every 8sec and this signal should be ignored since this is NOT S0ix entry/exit related and defered interrupt for SLP_S0 were added. BRANCH=master BUG=none TEST=in OS shell, run following commands. Following command is valid with coreboot with S0ix patches. "echo freeze > /sys/power/state" then, Measure EC power consumption and compare it with one in S0. And on EC console, there should be NO periodic message, "power state 4 = S0ix, in 0x001d" every 8 sec. Change-Id: Ia9cf5256b1ad7234815d4b6dbe2b45788aaf49dd Signed-off-by: Kyoung Kim Reviewed-on: https://chromium-review.googlesource.com/307947 Commit-Ready: Kyoung Il Kim Tested-by: Kyoung Il Kim Reviewed-by: Kyoung Il Kim Reviewed-by: Shawn N --- board/kunimitsu/board.h | 3 +- board/kunimitsu/gpio.inc | 2 +- include/chipset.h | 1 + include/config.h | 2 + include/power.h | 18 ++++++- power/common.c | 35 ++++++++++++- power/skylake.c | 106 +++++++++++++++++++++++++++++++++++++++ 7 files changed, 162 insertions(+), 5 deletions(-) diff --git a/board/kunimitsu/board.h b/board/kunimitsu/board.h index 4a4c8bcb66..b32b851d2f 100644 --- a/board/kunimitsu/board.h +++ b/board/kunimitsu/board.h @@ -68,6 +68,7 @@ /* We're space constrained on Kunimitsu, so reduce the UART TX buffer size. */ #undef CONFIG_UART_TX_BUF_SIZE #define CONFIG_UART_TX_BUF_SIZE 512 +#define CONFIG_POWER_S0IX #define CONFIG_USB_CHARGER #define CONFIG_USB_MUX_PI3USB30532 #define CONFIG_USB_POWER_DELIVERY @@ -124,7 +125,7 @@ #define I2C_PORT_USB_CHARGER_2 MEC1322_I2C0_0 #undef DEFERRABLE_MAX_COUNT -#define DEFERRABLE_MAX_COUNT 13 +#define DEFERRABLE_MAX_COUNT 14 #define CONFIG_ALS #define CONFIG_ALS_OPT3001 diff --git a/board/kunimitsu/gpio.inc b/board/kunimitsu/gpio.inc index d6c9940242..c8fd2ff4a5 100644 --- a/board/kunimitsu/gpio.inc +++ b/board/kunimitsu/gpio.inc @@ -14,7 +14,7 @@ GPIO_INT(POWER_BUTTON_L, PIN(35), GPIO_INT_BOTH, power_button_interrupt) GPIO_INT(RSMRST_L_PGOOD, PIN(63), GPIO_INT_BOTH, power_signal_interrupt) GPIO_INT(PCH_SLP_S4_L, PIN(200), GPIO_INT_BOTH | GPIO_PULL_UP, power_signal_interrupt) GPIO_INT(PCH_SLP_S3_L, PIN(206), GPIO_INT_BOTH | GPIO_PULL_UP, power_signal_interrupt) -GPIO_INT(PCH_SLP_S0_L, PIN(141), GPIO_INT_BOTH, power_signal_interrupt) +GPIO_INT(PCH_SLP_S0_L, PIN(141), GPIO_INT_BOTH, power_signal_interrupt_S0) GPIO_INT(PCH_SLP_SUS_L, PIN(12), GPIO_INT_BOTH | GPIO_PULL_UP, power_signal_interrupt) GPIO_INT(VOLUME_UP_L, PIN(31), GPIO_INT_BOTH | GPIO_PULL_UP, button_interrupt) GPIO_INT(VOLUME_DOWN_L, PIN(47), GPIO_INT_BOTH | GPIO_PULL_UP, button_interrupt) diff --git a/include/chipset.h b/include/chipset.h index 0237767fbf..4b91a6e57f 100644 --- a/include/chipset.h +++ b/include/chipset.h @@ -30,6 +30,7 @@ enum chipset_state_mask { CHIPSET_STATE_SOFT_OFF = 0x02, /* Soft off (S5) */ CHIPSET_STATE_SUSPEND = 0x04, /* Suspend (S3) */ CHIPSET_STATE_ON = 0x08, /* On (S0) */ + CHIPSET_STATE_STANDBY = 0x10, /* Standby (S0ix) */ /* Common combinations */ CHIPSET_STATE_ANY_OFF = (CHIPSET_STATE_HARD_OFF | CHIPSET_STATE_SOFT_OFF), /* Any off state */ diff --git a/include/config.h b/include/config.h index 918ba2130f..4c8c4a492c 100644 --- a/include/config.h +++ b/include/config.h @@ -1441,6 +1441,8 @@ /* Use part of the EC's data EEPROM to hold persistent storage for the AP. */ #undef CONFIG_PSTORE +/* Support S0ix */ +#undef CONFIG_POWER_S0IX /*****************************************************************************/ /* Support PWM control */ #undef CONFIG_PWM diff --git a/include/power.h b/include/power.h index 6a108c8690..5eff1f854a 100644 --- a/include/power.h +++ b/include/power.h @@ -20,7 +20,9 @@ enum power_state { POWER_S5, /* System is soft-off */ POWER_S3, /* Suspend; RAM on, processor is asleep */ POWER_S0, /* System is on */ - +#ifdef CONFIG_POWER_S0IX + POWER_S0ix, +#endif /* Transitions */ POWER_G3S5, /* G3 -> S5 (at system init time) */ POWER_S5S3, /* S5 -> S3 */ @@ -28,6 +30,10 @@ enum power_state { POWER_S0S3, /* S0 -> S3 */ POWER_S3S5, /* S3 -> S5 */ POWER_S5G3, /* S5 -> G3 */ +#ifdef CONFIG_POWER_S0IX + POWER_S0ixS0, /* S0ix -> S0 */ + POWER_S0S0ix, /* S0 -> S0ix */ +#endif }; /* Information on an power signal */ @@ -99,10 +105,20 @@ enum power_state power_handle_state(enum power_state state); */ #ifdef HAS_TASK_CHIPSET void power_signal_interrupt(enum gpio_signal signal); +#ifdef CONFIG_POWER_S0IX +void power_signal_interrupt_S0(enum gpio_signal signal); +#endif #else static inline void power_signal_interrupt(enum gpio_signal signal) { } +#ifdef CONFIG_POWER_S0IX +static inline void power_signal_interrupt_S0(enum gpio_signal signal) { } +#endif #endif /* !HAS_TASK_CHIPSET */ +#ifdef CONFIG_POWER_S0IX +int chipset_get_ps_debounced_level(enum gpio_signal signal); +#endif + /** * pause_in_s5 getter method. * diff --git a/power/common.c b/power/common.c index c75865ea53..380b3410fe 100644 --- a/power/common.c +++ b/power/common.c @@ -38,12 +38,19 @@ static const char * const state_names[] = { "S5", "S3", "S0", +#ifdef CONFIG_POWER_S0IX + "S0ix", +#endif "G3->S5", "S5->S3", "S3->S0", "S0->S3", "S3->S5", "S5->G3", +#ifdef CONFIG_POWER_S0IX + "S0ix->S0", + "S0->S0ix", +#endif }; static uint32_t in_signals; /* Current input signal states (IN_PGOOD_*) */ @@ -64,6 +71,15 @@ static uint32_t hibernate_delay = CONFIG_HIBERNATE_DELAY_SEC; static int pause_in_s5; #endif +static int power_signal_get_level(enum gpio_signal signal) +{ +#ifdef CONFIG_POWER_S0IX + return chipset_get_ps_debounced_level(signal); +#else + return gpio_get_level(signal); +#endif +} + /** * Update input signals mask */ @@ -74,7 +90,7 @@ static void power_update_signals(void) int i; for (i = 0; i < POWER_SIGNAL_COUNT; i++, s++) { - if (gpio_get_level(s->gpio) == s->level) + if (power_signal_get_level(s->gpio) == s->level) inew |= 1 << i; } @@ -229,7 +245,13 @@ static enum power_state power_common_state(enum power_state state) power_wait_signals(0); task_wait_event(-1); break; - +#ifdef CONFIG_POWER_S0IX + case POWER_S0ix: + /* Wait for a message */ + power_wait_signals(0); + task_wait_event(-1); + break; +#endif default: /* No common functionality for transition states */ break; @@ -279,6 +301,15 @@ int chipset_in_state(int state_mask) case POWER_S0: need_mask = CHIPSET_STATE_ON; break; +#ifdef CONFIG_POWER_S0IX + case POWER_S0ixS0: + case POWER_S0S0ix: + need_mask = CHIPSET_STATE_ON | CHIPSET_STATE_STANDBY; + break; + case POWER_S0ix: + need_mask = CHIPSET_STATE_STANDBY; + break; +#endif } /* Return non-zero if all needed bits are present */ diff --git a/power/skylake.c b/power/skylake.c index 1aa25d6bb6..f241ad25dd 100644 --- a/power/skylake.c +++ b/power/skylake.c @@ -14,6 +14,7 @@ #include "power.h" #include "power_button.h" #include "system.h" +#include "task.h" #include "util.h" #include "wireless.h" @@ -27,9 +28,16 @@ #define IN_PCH_SLP_S4_DEASSERTED POWER_SIGNAL_MASK(X86_SLP_S4_DEASSERTED) #define IN_PCH_SLP_SUS_DEASSERTED POWER_SIGNAL_MASK(X86_SLP_SUS_DEASSERTED) +#ifdef CONFIG_POWER_S0IX +#define IN_ALL_PM_SLP_DEASSERTED (IN_PCH_SLP_S0_DEASSERTED | \ + IN_PCH_SLP_S3_DEASSERTED | \ + IN_PCH_SLP_S4_DEASSERTED | \ + IN_PCH_SLP_SUS_DEASSERTED) +#else #define IN_ALL_PM_SLP_DEASSERTED (IN_PCH_SLP_S3_DEASSERTED | \ IN_PCH_SLP_S4_DEASSERTED | \ IN_PCH_SLP_SUS_DEASSERTED) +#endif /* * DPWROK is NC / stuffing option on initial boards. @@ -164,12 +172,31 @@ static enum power_state _power_handle_state(enum power_state state) 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); @@ -257,6 +284,33 @@ static enum power_state _power_handle_state(enum power_state state) return POWER_S3; +#ifdef CONFIG_POWER_S0IX + case POWER_S0S0ix: + /* call hooks before standby */ + hook_notify(HOOK_CHIPSET_SUSPEND); + + /* + * 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: + /* 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); @@ -347,3 +401,55 @@ void enter_pseudo_g3(void) ; } #endif + +#ifdef CONFIG_POWER_S0IX +static struct { + int required; /* indicates de-bounce required. */ + int done; /* debounced */ +} slp_s0_debounce = { + .required = 0, + .done = 1, +}; + +int chipset_get_ps_debounced_level(enum gpio_signal signal) +{ + /* + * If power state is updated in power_update_signal() by any interrupts + * other than SLP_S0 during the 1 msec pulse(invalid SLP_S0 signal), + * reading SLP_S0 should be corrected with slp_s0_debounce.done flag. + */ + int level = gpio_get_level(signal); + return (signal == GPIO_PCH_SLP_S0_L) ? + (level & slp_s0_debounce.done) : level; +} + +static void slp_s0_assertion_deferred(void) +{ + int s0_level = gpio_get_level(GPIO_PCH_SLP_S0_L); + /* + (s0_level != 0) || + ((s0_level == 0) && (slp_s0_debounce.required == 0)) + */ + if (s0_level == slp_s0_debounce.required) { + if (s0_level) + slp_s0_debounce.done = 1; /* debounced! */ + + power_signal_interrupt(GPIO_PCH_SLP_S0_L); + } + + slp_s0_debounce.required = 0; +} +DECLARE_DEFERRED(slp_s0_assertion_deferred); + +void power_signal_interrupt_S0(enum gpio_signal signal) +{ + if (gpio_get_level(GPIO_PCH_SLP_S0_L)) { + slp_s0_debounce.required = 1; + hook_call_deferred(slp_s0_assertion_deferred, 3 * MSEC); + } + else if (slp_s0_debounce.required == 0) { + slp_s0_debounce.done = 0; + slp_s0_assertion_deferred(); + } +} +#endif