summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin K Wong <kevin.k.wong@intel.com>2016-03-02 17:07:13 -0800
committerchrome-bot <chrome-bot@chromium.org>2016-03-31 21:35:56 -0700
commit55cd6e4c75895c5727ea6fd87b047a39a8730490 (patch)
treeba3f8625bf2118e496a48e33b2003be47ceff72e
parent6711629163f23a98099d8c6d37f6cdb1ef56ca52 (diff)
downloadchrome-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.h2
-rw-r--r--power/apollolake.c459
-rw-r--r--power/build.mk1
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