From a394302e4aef0349c30cbccf6b4623bcb3dda5bf Mon Sep 17 00:00:00 2001 From: Shawn Nematbakhsh Date: Tue, 5 May 2015 14:14:43 -0700 Subject: 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 Change-Id: Ibf6a0e4415544b6b4b2cf28c167106ce4bfdc54e Reviewed-on: https://chromium-review.googlesource.com/269460 Reviewed-by: Alec Berg --- include/config.h | 3 +- power/build.mk | 5 +- power/skylake.c | 245 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 250 insertions(+), 3 deletions(-) create mode 100644 power/skylake.c 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; +} -- cgit v1.2.1