summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShawn Nematbakhsh <shawnn@chromium.org>2015-05-05 14:14:43 -0700
committerChromeOS Commit Bot <chromeos-commit-bot@chromium.org>2015-05-12 02:50:09 +0000
commita394302e4aef0349c30cbccf6b4623bcb3dda5bf (patch)
treedbe5031332d6dc5c423cefcf3efabecb46bd42f4
parent1f09bd7c469fdede4f72425911b0a9d15a439c00 (diff)
downloadchrome-ec-a394302e4aef0349c30cbccf6b4623bcb3dda5bf.tar.gz
power: skylake: Add support for skylake power sequencing
Add power sequencing for Skylake, following the IMVP8 / ROP PMIC design for SKL-U / SKL-Y. BUG=chrome-os-partner:39510 TEST=Compile only BRANCH=None Signed-off-by: Shawn Nematbakhsh <shawnn@chromium.org> Change-Id: Ibf6a0e4415544b6b4b2cf28c167106ce4bfdc54e Reviewed-on: https://chromium-review.googlesource.com/269460 Reviewed-by: Alec Berg <alecaberg@chromium.org>
-rw-r--r--include/config.h3
-rw-r--r--power/build.mk5
-rw-r--r--power/skylake.c245
3 files changed, 250 insertions, 3 deletions
diff --git a/include/config.h b/include/config.h
index 47370d8aac..27137fdf00 100644
--- a/include/config.h
+++ b/include/config.h
@@ -317,12 +317,13 @@
/* AP chipset support; pick at most one */
#undef CONFIG_CHIPSET_BAYTRAIL /* Intel Bay Trail (x86) */
+#undef CONFIG_CHIPSET_BRASWELL /* Intel Braswell (x86) */
#undef CONFIG_CHIPSET_GAIA /* Gaia and Ares (ARM) */
#undef CONFIG_CHIPSET_HASWELL /* Intel Haswell (x86) */
#undef CONFIG_CHIPSET_IVYBRIDGE /* Intel Ivy Bridge (x86) */
#undef CONFIG_CHIPSET_ROCKCHIP /* Rockchip rk32xx */
+#undef CONFIG_CHIPSET_SKYLAKE /* Intel Skylake (x86) */
#undef CONFIG_CHIPSET_TEGRA /* nVidia Tegra 5 */
-#undef CONFIG_CHIPSET_BRASWELL /* Intel Braswell (x86) */
/* Support chipset throttling */
#undef CONFIG_CHIPSET_CAN_THROTTLE
diff --git a/power/build.mk b/power/build.mk
index 212ff2cf7e..52ac0ab3e7 100644
--- a/power/build.mk
+++ b/power/build.mk
@@ -7,12 +7,13 @@
#
power-$(CONFIG_CHIPSET_BAYTRAIL)+=baytrail.o
+power-$(CONFIG_CHIPSET_BRASWELL)+=braswell.o
power-$(CONFIG_CHIPSET_ECDRIVEN)+=ec_driven.o
power-$(CONFIG_CHIPSET_GAIA)+=gaia.o
power-$(CONFIG_CHIPSET_HASWELL)+=haswell.o
power-$(CONFIG_CHIPSET_IVYBRIDGE)+=ivybridge.o
+power-$(CONFIG_CHIPSET_MEDIATEK)+=mediatek.o
power-$(CONFIG_CHIPSET_ROCKCHIP)+=rockchip.o
+power-$(CONFIG_CHIPSET_SKYLAKE)+=skylake.o
power-$(CONFIG_CHIPSET_TEGRA)+=tegra.o
-power-$(CONFIG_CHIPSET_BRASWELL)+=braswell.o
-power-$(CONFIG_CHIPSET_MEDIATEK)+=mediatek.o
power-$(CONFIG_POWER_COMMON)+=common.o
diff --git a/power/skylake.c b/power/skylake.c
new file mode 100644
index 0000000000..1dce1fde4f
--- /dev/null
+++ b/power/skylake.c
@@ -0,0 +1,245 @@
+/* Copyright 2015 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Skylake IMVP8 / ROP PMIC chipset power control module for Chrome EC */
+
+#include "chipset.h"
+#include "common.h"
+#include "console.h"
+#include "hooks.h"
+#include "host_command.h"
+#include "power.h"
+#include "system.h"
+#include "util.h"
+#include "wireless.h"
+
+/* Console output macros */
+#define CPUTS(outstr) cputs(CC_CHIPSET, outstr)
+#define CPRINTS(format, args...) cprints(CC_CHIPSET, format, ## args)
+
+/* Input state flags */
+#define IN_PCH_SLP_S0_DEASSERTED POWER_SIGNAL_MASK(X86_SLP_S0_DEASSERTED)
+#define IN_PCH_SLP_S3_DEASSERTED POWER_SIGNAL_MASK(X86_SLP_S3_DEASSERTED)
+#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)
+
+#define IN_ALL_PM_SLP_DEASSERTED (IN_PCH_SLP_S3_DEASSERTED | \
+ IN_PCH_SLP_S4_DEASSERTED | \
+ IN_PCH_SLP_SUS_DEASSERTED)
+
+/*
+ * DPWROK is NC / stuffing option on initial boards.
+ * TODO(shawnn): Figure out proper control signals.
+ */
+#define IN_PGOOD_ALL_CORE 0
+
+#define IN_ALL_S0 (IN_PGOOD_ALL_CORE | IN_ALL_PM_SLP_DEASSERTED)
+
+static int throttle_cpu; /* Throttle CPU? */
+
+void chipset_force_shutdown(void)
+{
+ CPRINTS("%s()", __func__);
+
+ /*
+ * Force off. This condition will reset once the state machine
+ * transitions to G3.
+ */
+ gpio_set_level(GPIO_PCH_RSMRST_L, 0);
+}
+
+void chipset_force_g3(void)
+{
+ CPRINTS("Forcing G3");
+
+ gpio_set_level(GPIO_PCH_RSMRST_L, 0);
+ gpio_set_level(GPIO_PP1800_DX_SENSOR_EN, 0);
+ gpio_set_level(GPIO_PP1800_DX_AUDIO_EN, 0);
+ gpio_set_level(GPIO_PP3300_WLAN_EN, 0);
+}
+
+void chipset_reset(int cold_reset)
+{
+ CPRINTS("%s(%d)", __func__, cold_reset);
+
+ if (cold_reset) {
+ if (gpio_get_level(GPIO_SYS_RESET_L) == 0)
+ return;
+ gpio_set_level(GPIO_SYS_RESET_L, 0);
+ udelay(100);
+ gpio_set_level(GPIO_SYS_RESET_L, 1);
+ } else {
+ /*
+ * Send a RCIN_PCH_RCIN_L
+ * assert INIT# to the CPU without dropping power or asserting
+ * PLTRST# to reset the rest of the system.
+ */
+
+ /* Pulse must be at least 16 PCI clocks long = 500 ns */
+ gpio_set_level(GPIO_PCH_RCIN_L, 0);
+ udelay(10);
+ gpio_set_level(GPIO_PCH_RCIN_L, 1);
+ }
+}
+
+void chipset_thottle_cpu(int throttle)
+{
+ if (chipset_in_state(CHIPSET_STATE_ON))
+ gpio_set_level(GPIO_CPU_PROCHOT, throttle);
+}
+
+enum power_state power_chipset_init(void)
+{
+ /*
+ * If we're switching between images without rebooting, see if the x86
+ * is already powered on; if so, leave it there instead of cycling
+ * through G3.
+ */
+ if (system_jumped_to_this_image()) {
+ if ((power_get_signals() & IN_ALL_S0) == IN_ALL_S0) {
+ /* Disable idle task deep sleep when in S0. */
+ disable_sleep(SLEEP_MASK_AP_RUN);
+ CPRINTS("already in S0");
+ return POWER_S0;
+ } else {
+ /* Force all signals to their G3 states */
+ chipset_force_g3();
+ }
+ }
+
+ return POWER_G3;
+}
+
+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_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:
+ if (gpio_get_level(GPIO_PCH_SLP_SUS_L) == 0) {
+ chipset_force_shutdown();
+ return POWER_G3;
+ }
+
+ /* Deassert RSMRST# */
+ gpio_set_level(GPIO_PCH_RSMRST_L, 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;
+ }
+
+ /* Enable TP so that it can wake the system */
+ gpio_set_level(GPIO_ENABLE_TOUCHPAD, 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_PP1800_DX_SENSOR_EN, 1);
+ gpio_set_level(GPIO_PP1800_DX_AUDIO_EN, 1);
+ gpio_set_level(GPIO_PP3300_WLAN_EN, 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);
+
+ /* 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);
+
+ gpio_set_level(GPIO_PP1800_DX_SENSOR_EN, 0);
+ gpio_set_level(GPIO_PP1800_DX_AUDIO_EN, 0);
+ gpio_set_level(GPIO_PP3300_WLAN_EN, 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);
+
+ gpio_set_level(GPIO_ENABLE_TOUCHPAD, 0);
+
+ return power_get_pause_in_s5() ? POWER_S5 : POWER_S5G3;
+
+ case POWER_S5G3:
+ gpio_set_level(GPIO_PCH_RSMRST_L, 0);
+ return POWER_G3;
+
+ default:
+ break;
+ }
+
+ return state;
+}