summaryrefslogtreecommitdiff
path: root/power/skylake.c
diff options
context:
space:
mode:
authorKyoung Kim <kyoung.il.kim@intel.com>2015-10-01 19:22:01 -0700
committerchrome-bot <chrome-bot@chromium.org>2015-11-19 20:01:58 -0800
commitebf92ecc839a6361605eb4c3ac7cb44fa4eb603a (patch)
treea7ef9a393bc55799e9b61b9b4a9d5b613fedc216 /power/skylake.c
parent8704de934edc294c1efb3115cb8192bbc7f0dc65 (diff)
downloadchrome-ec-ebf92ecc839a6361605eb4c3ac7cb44fa4eb603a.tar.gz
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 <kyoung.il.kim@intel.com> Reviewed-on: https://chromium-review.googlesource.com/307947 Commit-Ready: Kyoung Il Kim <kyoung.il.kim@intel.com> Tested-by: Kyoung Il Kim <kyoung.il.kim@intel.com> Reviewed-by: Kyoung Il Kim <kyoung.il.kim@intel.com> Reviewed-by: Shawn N <shawnn@chromium.org>
Diffstat (limited to 'power/skylake.c')
-rw-r--r--power/skylake.c106
1 files changed, 106 insertions, 0 deletions
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