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