diff options
author | Kevin K Wong <kevin.k.wong@intel.com> | 2016-03-02 17:07:13 -0800 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2016-03-31 21:35:56 -0700 |
commit | 55cd6e4c75895c5727ea6fd87b047a39a8730490 (patch) | |
tree | ba3f8625bf2118e496a48e33b2003be47ceff72e | |
parent | 6711629163f23a98099d8c6d37f6cdb1ef56ca52 (diff) | |
download | chrome-ec-55cd6e4c75895c5727ea6fd87b047a39a8730490.tar.gz |
apollolake: initial chipset code
used chipset skylake as the initial code base for apollolake
BUG=none
BRANCH=none
TEST=make buildall
Change-Id: If82f9bcd53ff44714f4b277637ff9f3c115ccc4d
Signed-off-by: Kevin K Wong <kevin.k.wong@intel.com>
Reviewed-on: https://chromium-review.googlesource.com/331651
Reviewed-by: Li1 Feng <li1.feng@intel.com>
Reviewed-by: Shawn N <shawnn@chromium.org>
-rw-r--r-- | include/config.h | 2 | ||||
-rw-r--r-- | power/apollolake.c | 459 | ||||
-rw-r--r-- | power/build.mk | 1 |
3 files changed, 462 insertions, 0 deletions
diff --git a/include/config.h b/include/config.h index 165b20981d..a126055be8 100644 --- a/include/config.h +++ b/include/config.h @@ -467,6 +467,7 @@ /* Chipset config */ /* AP chipset support; pick at most one */ +#undef CONFIG_CHIPSET_APOLLOLAKE/* Intel Apollolake (x86) */ #undef CONFIG_CHIPSET_BAYTRAIL /* Intel Bay Trail (x86) */ #undef CONFIG_CHIPSET_BRASWELL /* Intel Braswell (x86) */ #undef CONFIG_CHIPSET_ECDRIVEN /* Dummy power module */ @@ -2111,6 +2112,7 @@ */ #ifndef HAS_TASK_CHIPSET +#undef CONFIG_CHIPSET_APOLLOLAKE #undef CONFIG_CHIPSET_BAYTRAIL #undef CONFIG_CHIPSET_BRASWELL #undef CONFIG_CHIPSET_GAIA diff --git a/power/apollolake.c b/power/apollolake.c new file mode 100644 index 0000000000..a4f7bf27e4 --- /dev/null +++ b/power/apollolake.c @@ -0,0 +1,459 @@ +/* Copyright 2016 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. + */ + +/* Apollolake chipset power control module for Chrome EC */ + +#include "charge_state.h" +#include "chipset.h" +#include "common.h" +#include "console.h" +#include "hooks.h" +#include "host_command.h" +#include "power.h" +#include "power_button.h" +#include "system.h" +#include "task.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_RSMRST_N POWER_SIGNAL_MASK(X86_RSMRST_N) +#define IN_ALL_SYS_PG POWER_SIGNAL_MASK(X86_ALL_SYS_PG) +#define IN_SLP_S0_N POWER_SIGNAL_MASK(X86_SLP_S0_N) +#define IN_SLP_S3_N POWER_SIGNAL_MASK(X86_SLP_S3_N) +#define IN_SLP_S4_N POWER_SIGNAL_MASK(X86_SLP_S4_N) +#define IN_SUSPWRDNACK POWER_SIGNAL_MASK(X86_SUSPWRDNACK) +#define IN_SUS_STAT_N POWER_SIGNAL_MASK(X86_SUS_STAT_N) + +#ifdef CONFIG_POWER_S0IX +#define IN_ALL_PM_SLP_DEASSERTED (IN_SLP_S0_N | \ + IN_SLP_S3_N | \ + IN_SLP_S4_N) +#else +#define IN_ALL_PM_SLP_DEASSERTED (IN_SLP_S3_N | \ + IN_SLP_S4_N) +#endif + +#define IN_PGOOD_ALL_CORE (IN_RSMRST_N) + +#define IN_ALL_S0 (IN_PGOOD_ALL_CORE | IN_ALL_PM_SLP_DEASSERTED) + +#define CHARGER_INITIALIZED_DELAY_MS 100 +#define CHARGER_INITIALIZED_TRIES 40 + +static int throttle_cpu; /* Throttle CPU? */ +static int forcing_coldreset; /* Forced coldreset in progress? */ +static int power_s5_up; /* Chipset is sequencing up or down */ + +void chipset_force_shutdown(void) +{ + if (!forcing_coldreset) + CPRINTS("%s()", __func__); + + /* + * Disable V5A which de-assert PMIC_EN and causes PMIC to shutdown. + */ + gpio_set_level(GPIO_V5A_EN, 0); +} + +void chipset_reset(int cold_reset) +{ + CPRINTS("%s(%d)", __func__, cold_reset); + if (cold_reset) { + /* + * Perform chipset_force_shutdown and mark forcing_coldreset. + * Once in S5G3 state, check forcing_coldreset to power up. + */ + forcing_coldreset = 1; + + chipset_force_shutdown(); + } else { + /* + * Send a pulse to SOC PMU_RSTBTN_N to trigger a warm reset. + */ + gpio_set_level(GPIO_PCH_RCIN_L, 0); + usleep(32 * MSEC); + gpio_set_level(GPIO_PCH_RCIN_L, 1); + } +} + +void chipset_throttle_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_shutdown(); + } + } + + return POWER_G3; +} + +static void handle_pass_through(enum power_state state, + enum gpio_signal pin_in, + enum gpio_signal pin_out, + int pass_through_delay) +{ + /* + * Pass through asynchronously, as SOC may not react + * immediately to power changes. + */ + int in_level = gpio_get_level(pin_in); + int out_level = gpio_get_level(pin_out); + + /* Nothing to do. */ + if (in_level == out_level) + return; + /* + * Wait at least 10ms between power signals going high + * and pass through to SOC. + */ + if (in_level) + msleep(MAX(10, pass_through_delay)); + + gpio_set_level(pin_out, in_level); + + CPRINTS("Pass through %s: %d", gpio_get_name(pin_in), in_level); +} + +#ifdef CONFIG_BOARD_HAS_RTC_RESET +static enum power_state power_wait_s5_rtc_reset(void) +{ + static int s5_exit_tries; + + /* Wait for S5 exit and then attempt RTC reset */ + while ((power_get_signals() & IN_PCH_SLP_S4_DEASSERTED) == 0) { + /* Handle RSMRST passthru event while waiting */ + handle_rsmrst(POWER_S5); + if (task_wait_event(SECOND*4) == TASK_EVENT_TIMER) { + CPRINTS("timeout waiting for S5 exit"); + chipset_force_g3(); + + /* Assert RTCRST# and retry 5 times */ + board_rtc_reset(); + + if (++s5_exit_tries > 4) { + s5_exit_tries = 0; + return POWER_G3; /* Stay off */ + } + + udelay(10 * MSEC); + return POWER_G3S5; /* Power up again */ + } + } + + s5_exit_tries = 0; + return POWER_S5S3; /* Power up to next state */ +} +#endif + +static enum power_state _power_handle_state(enum power_state state) +{ + int tries = 0; + + switch (state) { + case POWER_G3: + break; + + case POWER_S5: +#ifdef CONFIG_BOARD_HAS_RTC_RESET + /* Wait for S5 exit and attempt RTC reset it supported */ + if (power_s5_up) + return power_wait_s5_rtc_reset(); +#endif + + if (!power_has_signals(IN_PGOOD_ALL_CORE)) { + /* Required rail went away */ + chipset_force_shutdown(); + return POWER_S5G3; + } else if (gpio_get_level(GPIO_PCH_SLP_S4_L) == 1) { + /* Power up to next state */ + return POWER_S5S3; + } + 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; +#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: + /* Platform is powering up, clear forcing_coldreset */ + forcing_coldreset = 0; + + /* Call hooks to initialize PMIC */ + hook_notify(HOOK_CHIPSET_PRE_INIT); + + /* + * Allow up to 1s for charger to be initialized, in case + * we're trying to boot the AP with no battery. + */ + while (charge_prevent_power_on(0) && + tries++ < CHARGER_INITIALIZED_TRIES) { + msleep(CHARGER_INITIALIZED_DELAY_MS); + } + + /* Return to G3 if battery level is too low */ + if (charge_want_shutdown() || + tries > CHARGER_INITIALIZED_TRIES) { + CPRINTS("power-up inhibited"); + chipset_force_shutdown(); + return POWER_G3; + } + + /* Enable V5A */ + gpio_set_level(GPIO_V5A_EN, 1); + + if (power_wait_signals(IN_PGOOD_ALL_CORE)) { + chipset_force_shutdown(); + return POWER_G3; + } + + power_s5_up = 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; + } + + /* 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_ENABLE_BACKLIGHT, 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); + + gpio_set_level(GPIO_ENABLE_BACKLIGHT, 0); + + /* 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); + + return POWER_S3; + +#ifdef CONFIG_POWER_S0IX + case POWER_S0S0ix: + /* call hooks before standby */ + hook_notify(HOOK_CHIPSET_SUSPEND); + + lpc_enable_wake_mask_for_lid_open(); + + /* + * 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: + lpc_disable_wake_mask_for_lid_open(); + + /* 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); + + /* Disable wireless */ + wireless_set_state(WIRELESS_OFF); + + /* Always enter into S5 state. The S5 state is required to + * correctly handle global resets which have a bit of delay + * while the SLP_Sx_L signals are asserted then deasserted. */ + power_s5_up = 0; + return POWER_S5; + + case POWER_S5G3: + chipset_force_shutdown(); + + /* Power up the platform again for forced cold reset */ + if (forcing_coldreset) { + forcing_coldreset = 0; + return POWER_G3S5; + } + + return POWER_G3; + + default: + break; + } + + return state; +} + +enum power_state power_handle_state(enum power_state state) +{ + enum power_state new_state; + + /* Process RSMRST_L state changes. */ + handle_pass_through(state, GPIO_RSMRST_L_PGOOD, GPIO_PCH_RSMRST_L, 0); + + /* Process ALL_SYS_PGOOD state changes. */ + handle_pass_through(state, GPIO_ALL_SYS_PGOOD, GPIO_PCH_SYS_PWROK, 0); + + new_state = _power_handle_state(state); + + return new_state; +} + +#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 diff --git a/power/build.mk b/power/build.mk index d45e4ae5a7..cfb142833b 100644 --- a/power/build.mk +++ b/power/build.mk @@ -6,6 +6,7 @@ # Power management for application processor and peripherals # +power-$(CONFIG_CHIPSET_APOLLOLAKE)+=apollolake.o power-$(CONFIG_CHIPSET_BAYTRAIL)+=baytrail.o power-$(CONFIG_CHIPSET_BRASWELL)+=braswell.o power-$(CONFIG_CHIPSET_ECDRIVEN)+=ec_driven.o |