summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLi Feng <li1.feng@intel.com>2022-02-08 18:18:50 -0800
committerCommit Bot <commit-bot@chromium.org>2022-02-25 07:36:02 +0000
commit96e76c065c5f6c11fe65398fffa61ff7917b25c4 (patch)
treec86ea263dd51a3a5b46ed309d07d75f1676771e3
parent495172f0d7c8055218af9d4ffc21bda038942726 (diff)
downloadchrome-ec-96e76c065c5f6c11fe65398fffa61ff7917b25c4.tar.gz
zephyr: subsys/ap_pwrseq: handle power signals
Input power signals change results power state transition. Signals are from either GPIO interrupts or ESPI via virtual wires. Add power signals handling in power state machine. 1. Config power signals GPIO interrupts 2. Add power signals list 3. Check power signals for power state transition 4. Add S5 inactive timer BUG=b:203446068 BRANCH=none TEST=On Brya platform, tested boot up to S0; AP "shutdown -h now", power state enters S5 then G3 after 10 seconds. Signed-off-by: Li Feng <li1.feng@intel.com> Change-Id: I34535fa8f8d1e5ba20fca578a638bf1bd7da250f Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3449792 Reviewed-by: Andrew McRae <amcrae@google.com>
-rw-r--r--zephyr/dts/bindings/power/intel,ap-pwrseq-signal-list.yaml63
-rw-r--r--zephyr/dts/bindings/power/intel,ap-pwrseq.yaml7
-rw-r--r--zephyr/subsys/ap_pwrseq/CMakeLists.txt1
-rw-r--r--zephyr/subsys/ap_pwrseq/include/x86_common_pwrseq.h15
-rw-r--r--zephyr/subsys/ap_pwrseq/include/x86_non_dsx_common_pwrseq_sm_handler.h20
-rw-r--r--zephyr/subsys/ap_pwrseq/include/x86_power_signals.h88
-rw-r--r--zephyr/subsys/ap_pwrseq/x86_non_dsx_adlp_pwrseq_sm.c2
-rw-r--r--zephyr/subsys/ap_pwrseq/x86_non_dsx_common_pwrseq_console.c40
-rw-r--r--zephyr/subsys/ap_pwrseq/x86_non_dsx_common_pwrseq_sm_handler.c309
-rw-r--r--zephyr/subsys/ap_pwrseq/x86_non_dsx_espi.c8
-rw-r--r--zephyr/subsys/ap_pwrseq/x86_power_signals.c79
11 files changed, 617 insertions, 15 deletions
diff --git a/zephyr/dts/bindings/power/intel,ap-pwrseq-signal-list.yaml b/zephyr/dts/bindings/power/intel,ap-pwrseq-signal-list.yaml
new file mode 100644
index 0000000000..f09bcea1a6
--- /dev/null
+++ b/zephyr/dts/bindings/power/intel,ap-pwrseq-signal-list.yaml
@@ -0,0 +1,63 @@
+# Copyright 2022 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.
+
+description: Power sequence signals information list.
+ The signals could be GPIO signals, eSPI signals through virtual wire.
+
+compatible: "intel,ap-pwrseq-signal-list"
+
+child-binding:
+ description: Power sequence signals list child node
+ properties:
+ dbg-label:
+ type: string
+ required: true
+ description: Label of power signals used in debug messages
+
+ pwrseq-int-gpios:
+ type: phandle-array
+ required: false
+ description: Associated GPIO interrupts pin
+
+ pwrseq-int-flags:
+ type: int
+ required: false
+ description: GPIO interrupt configuration flags.
+
+ enable-int-on-boot:
+ type: boolean
+ required: false
+ description: true - Enable GPIO interrupt at boot up;
+ false - Do not enable GPIO interrupt at boot up.
+
+ pwrseq-vw-enum:
+ type: string
+ required: false
+ description: Enumeration value to eSPI signals that can be sent
+ or received through virtual wire channel
+ enum:
+ - ESPI_VWIRE_SIGNAL_SLP_S3
+ - ESPI_VWIRE_SIGNAL_SLP_S4
+ - ESPI_VWIRE_SIGNAL_SLP_S5
+
+ pwrseq-signal-enum:
+ type: string
+ required: true
+ description: Enumeration values for power signals
+
+ pwrseq-adc-enum:
+ type: string
+ required: false
+
+ flags:
+ type: int
+ required: true
+ description: bit 0 represents active in high or low state
+ bit[0] = 1 active high
+ bit[0] = 0 active low
+
+ pwrseq-board-value:
+ type: string
+ required: false
+ description: a value defined at board level.
diff --git a/zephyr/dts/bindings/power/intel,ap-pwrseq.yaml b/zephyr/dts/bindings/power/intel,ap-pwrseq.yaml
index ebc875814d..e43fe35ff1 100644
--- a/zephyr/dts/bindings/power/intel,ap-pwrseq.yaml
+++ b/zephyr/dts/bindings/power/intel,ap-pwrseq.yaml
@@ -79,6 +79,13 @@ properties:
description: |
Timeout in ms for monitoring power signals.
+ s5-inactivity-timeout:
+ type: int
+ required: false
+ default: 10
+ description: |
+ Timeout in seconds for dropping back from S5 to G3.
+
pwrseq-espi-max-freq:
type: int
required: false
diff --git a/zephyr/subsys/ap_pwrseq/CMakeLists.txt b/zephyr/subsys/ap_pwrseq/CMakeLists.txt
index e4f5c6c3d3..0ecf8aad22 100644
--- a/zephyr/subsys/ap_pwrseq/CMakeLists.txt
+++ b/zephyr/subsys/ap_pwrseq/CMakeLists.txt
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: Apache-2.0
zephyr_include_directories(include)
+zephyr_sources(x86_power_signals.c)
zephyr_sources_ifdef(CONFIG_X86_NON_DSX_PWRSEQ
x86_non_dsx_common_pwrseq_sm_handler.c)
zephyr_sources_ifdef(CONFIG_X86_NON_DSX_PWRSEQ x86_non_dsx_espi.c)
diff --git a/zephyr/subsys/ap_pwrseq/include/x86_common_pwrseq.h b/zephyr/subsys/ap_pwrseq/include/x86_common_pwrseq.h
index d46a2183fa..defea12933 100644
--- a/zephyr/subsys/ap_pwrseq/include/x86_common_pwrseq.h
+++ b/zephyr/subsys/ap_pwrseq/include/x86_common_pwrseq.h
@@ -9,6 +9,7 @@
#include <drivers/espi.h>
#include <drivers/gpio.h>
#include <logging/log.h>
+#include <x86_power_signals.h>
/**
* @brief System power states for Non Deep Sleep Well
@@ -76,5 +77,17 @@ enum pwrseq_chipset_shutdown_reason {
struct pwrseq_context {
/* On power-on start boot up sequence */
enum power_states_ndsx power_state;
+
+ /*
+ * Current input power signal states. Each bit represents an input
+ * power signal that is defined by enum power_signal in same order.
+ * 1 - signal state is asserted.
+ * 0 - signal state is de-asserted.
+ */
+ uint32_t in_signals;
+ /* Input signal state we're waiting for */
+ uint32_t in_want;
+ /* Signal values which print debug output */
+ uint32_t in_debug;
};
-#endif /* __X86_COMMON_H__ */
+#endif /* __X86_COMMON_PWRSEQ_H__ */
diff --git a/zephyr/subsys/ap_pwrseq/include/x86_non_dsx_common_pwrseq_sm_handler.h b/zephyr/subsys/ap_pwrseq/include/x86_non_dsx_common_pwrseq_sm_handler.h
index 51c1c17a6c..7e627c9b21 100644
--- a/zephyr/subsys/ap_pwrseq/include/x86_non_dsx_common_pwrseq_sm_handler.h
+++ b/zephyr/subsys/ap_pwrseq/include/x86_non_dsx_common_pwrseq_sm_handler.h
@@ -22,6 +22,8 @@ struct common_pwrseq_config {
int pch_dsw_pwrok_delay_ms;
int pch_pm_pwrbtn_delay_ms;
int pch_rsmrst_delay_ms;
+ int wait_signal_timeout_ms;
+ int s5_timeout_s;
#if PWRSEQ_GPIO_PRESENT(en_pp5000_s5_gpios)
const struct gpio_dt_spec enable_pp5000_a;
#endif
@@ -41,6 +43,19 @@ struct common_pwrseq_config {
const struct gpio_dt_spec slp_sus_l;
};
+/* The wait time is ~150 msec, allow for safety margin. */
+#define IN_PCH_SLP_SUS_WAIT_TIME_MS 250
+
+/*
+ * Each board must provide its signal list and a corresponding enum
+ * power_signal.
+ */
+extern const struct power_signal_config power_signal_list[];
+extern const struct gpio_power_signal_config power_signal_gpio_list[];
+extern struct gpio_callback intr_callbacks[];
+int board_power_signal_is_asserted(enum power_signal signal);
+int power_signal_is_asserted(enum power_signal signal);
+void power_update_signals(void);
enum power_states_ndsx chipset_pwr_sm_run(
enum power_states_ndsx curr_state,
const struct common_pwrseq_config *com_cfg);
@@ -51,8 +66,11 @@ void chipset_reset(enum pwrseq_chipset_shutdown_reason reason);
void common_rsmrst_pass_thru_handler(void);
void init_chipset_pwr_seq_state(void);
enum power_states_ndsx pwr_sm_get_state(void);
+uint32_t pwrseq_get_input_signals(void);
+void pwrseq_set_debug_signals(uint32_t signals);
+uint32_t pwrseq_get_debug_signals(void);
void apshutdown(void);
extern const char pwrsm_dbg[][25];
-#endif /* __X86_NON_DSX_COMMON_H__ */
+#endif /* __X86_NON_DSX_COMMON_PWRSEQ_SM_HANDLER_H__ */
diff --git a/zephyr/subsys/ap_pwrseq/include/x86_power_signals.h b/zephyr/subsys/ap_pwrseq/include/x86_power_signals.h
new file mode 100644
index 0000000000..6eb1ede3fb
--- /dev/null
+++ b/zephyr/subsys/ap_pwrseq/include/x86_power_signals.h
@@ -0,0 +1,88 @@
+/* Copyright 2022 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.
+ */
+
+/* Define power signals from device tree */
+
+#ifndef __X86_POWER_SIGNALS_H__
+#define __X86_POWER_SIGNALS_H__
+
+#include <drivers/espi.h>
+#include <drivers/gpio.h>
+
+#if DT_HAS_COMPAT_STATUS_OKAY(intel_ap_pwrseq_signal_list)
+BUILD_ASSERT(DT_NUM_INST_STATUS_OKAY(intel_ap_pwrseq_signal_list) == 1,
+ "Only one node for intel_ap_pwrseq_signal_list is allowed");
+#endif
+
+#define POWER_SIGNALS_LIST_NODE \
+ DT_COMPAT_GET_ANY_STATUS_OKAY(intel_ap_pwrseq_signal_list)
+
+#define POWER_SIGNAL_ENUM(id) DT_STRING_UPPER_TOKEN(id, pwrseq_signal_enum)
+#define POWER_SIGNAL_ENUM_COMMA(id) POWER_SIGNAL_ENUM(id),
+
+enum power_signal {
+ DT_FOREACH_CHILD(POWER_SIGNALS_LIST_NODE, POWER_SIGNAL_ENUM_COMMA)
+ POWER_SIGNAL_COUNT
+};
+
+enum power_source {
+ SOURCE_GPIO,
+ SOURCE_VW,
+ SOURCE_ADC,
+ SOURCE_BOARD,
+};
+
+#define GPIO_PWR_ENUM(id, enum_name) DT_CAT(GPIO, enum_name)
+#define GPIO_PWR_ENUM_COMMA(id, enum_name) GPIO_PWR_ENUM(id, enum_name),
+#define GEN_GPIO_ENUM(id) \
+ COND_CODE_1(DT_NODE_HAS_PROP(id, pwrseq_int_gpios), \
+ (GPIO_PWR_ENUM_COMMA(id, POWER_SIGNAL_ENUM(id))), ())
+
+enum gpio_power_signal {
+ DT_FOREACH_CHILD(POWER_SIGNALS_LIST_NODE, GEN_GPIO_ENUM)
+ POWER_SIGNAL_GPIO_COUNT
+};
+
+/* GPIO power signal configuration */
+struct gpio_power_signal_config {
+ enum power_signal power_signal;
+ const struct gpio_dt_spec spec;
+ gpio_flags_t intr_flags; /* GPIO interrupt flags */
+ bool enable_on_boot; /* Enable interrupt at boot up */
+};
+
+/* Power signal common configuration */
+struct power_signal_config {
+ const char *debug_name;
+ enum power_source source;
+ uint8_t source_id;
+ uint8_t flags;
+};
+
+/* Power signal flags */
+#define POWER_SIGNAL_ACTIVE_STATE BIT(0)
+/* Convert enum power_signal to a mask for signal functions */
+#define POWER_SIGNAL_MASK(signal) (1 << (signal))
+
+#if defined(CONFIG_AP_X86_INTEL_ADL)
+
+/* 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_S5_DEASSERTED POWER_SIGNAL_MASK(X86_SLP_S5_DEASSERTED)
+#define IN_PCH_SLP_SUS_DEASSERTED POWER_SIGNAL_MASK(X86_SLP_SUS_DEASSERTED)
+#define IN_ALL_PM_SLP_DEASSERTED (IN_PCH_SLP_S3 | \
+ IN_PCH_SLP_S4 | \
+ IN_PCH_SLP_SUS)
+#define IN_PGOOD_ALL_CORE POWER_SIGNAL_MASK(X86_DSW_PWROK)
+#define IN_ALL_S0 (IN_PGOOD_ALL_CORE | IN_ALL_PM_SLP_DEASSERTED)
+#define CHIPSET_G3S5_POWERUP_SIGNAL IN_PCH_SLP_SUS_DEASSERTED
+
+#else
+#warning("Input power signals state flags not defined");
+#endif
+
+#endif /* __X86_POWER_SIGNALS_H__ */
diff --git a/zephyr/subsys/ap_pwrseq/x86_non_dsx_adlp_pwrseq_sm.c b/zephyr/subsys/ap_pwrseq/x86_non_dsx_adlp_pwrseq_sm.c
index c579a86f32..663df2cfdb 100644
--- a/zephyr/subsys/ap_pwrseq/x86_non_dsx_adlp_pwrseq_sm.c
+++ b/zephyr/subsys/ap_pwrseq/x86_non_dsx_adlp_pwrseq_sm.c
@@ -274,7 +274,7 @@ void init_chipset_pwr_seq_state(void)
enum power_states_ndsx chipset_pwr_sm_run(enum power_states_ndsx curr_state,
const struct common_pwrseq_config *com_cfg)
{
-/* Add chipset specific state handling if any */
+ /* Add chipset specific state handling if any */
switch (curr_state) {
case SYS_POWER_STATE_G3S5:
g3s5_action_handler(com_cfg);
diff --git a/zephyr/subsys/ap_pwrseq/x86_non_dsx_common_pwrseq_console.c b/zephyr/subsys/ap_pwrseq/x86_non_dsx_common_pwrseq_console.c
index 7d9ac38541..7256cf77d4 100644
--- a/zephyr/subsys/ap_pwrseq/x86_non_dsx_common_pwrseq_console.c
+++ b/zephyr/subsys/ap_pwrseq/x86_non_dsx_common_pwrseq_console.c
@@ -4,6 +4,7 @@
*/
#include <shell/shell.h>
+#include <stdlib.h>
#include <x86_non_dsx_common_pwrseq_sm_handler.h>
LOG_MODULE_DECLARE(ap_pwrseq, 4);
@@ -22,6 +23,45 @@ static int powerinfo_handler(const struct shell *shell, size_t argc,
SHELL_CMD_REGISTER(powerinfo, NULL, NULL, powerinfo_handler);
+static int powerindebug_handler(const struct shell *shell, size_t argc,
+ char **argv)
+{
+ int i;
+ char *e;
+
+ /* If one arg, set the mask */
+ if (argc == 2) {
+ int m = strtol(argv[1], &e, 0);
+
+ if (*e)
+ return -EINVAL;
+
+ pwrseq_set_debug_signals(m);
+ }
+
+ /* Print the mask */
+ shell_fprintf(shell, SHELL_INFO, "power in: 0x%04x\n",
+ pwrseq_get_input_signals());
+ shell_fprintf(shell, SHELL_INFO, "debug mask: 0x%04x\n",
+ pwrseq_get_debug_signals());
+
+ /* Print the decode */
+ shell_fprintf(shell, SHELL_INFO, "bit meanings:\n");
+ for (i = 0; i < POWER_SIGNAL_COUNT; i++) {
+ int mask = 1 << i;
+
+ shell_fprintf(shell, SHELL_INFO, " 0x%04x %d %s\n",
+ mask, pwrseq_get_input_signals() & mask ? 1 : 0,
+ power_signal_list[i].debug_name);
+ }
+
+ return 0;
+};
+
+SHELL_CMD_REGISTER(powerindebug, NULL,
+ "[mask] Get/set power input debug mask", powerindebug_handler);
+
+
static int apshutdown_handler(const struct shell *shell, size_t argc,
char **argv)
{
diff --git a/zephyr/subsys/ap_pwrseq/x86_non_dsx_common_pwrseq_sm_handler.c b/zephyr/subsys/ap_pwrseq/x86_non_dsx_common_pwrseq_sm_handler.c
index c87dbc111d..1667d9eeff 100644
--- a/zephyr/subsys/ap_pwrseq/x86_non_dsx_common_pwrseq_sm_handler.c
+++ b/zephyr/subsys/ap_pwrseq/x86_non_dsx_common_pwrseq_sm_handler.c
@@ -9,6 +9,8 @@ static K_KERNEL_STACK_DEFINE(pwrseq_thread_stack,
CONFIG_X86_NON_DSW_PWRSEQ_STACK_SIZE);
static struct k_thread pwrseq_thread_data;
static struct pwrseq_context pwrseq_ctx;
+/* S5 inactive timer*/
+K_TIMER_DEFINE(s5_inactive_timer, NULL, NULL);
LOG_MODULE_REGISTER(ap_pwrseq, 4);
@@ -16,6 +18,8 @@ static const struct common_pwrseq_config com_cfg = {
.pch_dsw_pwrok_delay_ms = DT_INST_PROP(0, dsw_pwrok_delay),
.pch_pm_pwrbtn_delay_ms = DT_INST_PROP(0, pm_pwrbtn_delay),
.pch_rsmrst_delay_ms = DT_INST_PROP(0, rsmrst_delay),
+ .wait_signal_timeout_ms = DT_INST_PROP(0, wait_signal_timeout),
+ .s5_timeout_s = DT_INST_PROP(0, s5_inactivity_timeout),
#if PWRSEQ_GPIO_PRESENT(en_pp5000_s5_gpios)
.enable_pp5000_a = GPIO_DT_SPEC_GET(DT_DRV_INST(0),
en_pp5000_s5_gpios),
@@ -63,9 +67,130 @@ const char pwrsm_dbg[][25] = {
};
#endif
-void power_signal_interrupt(void)
+int power_signal_disable_interrupt(enum power_signal signal)
{
- /* TODO: Add handling */
+ int index = power_signal_list[signal].source_id;
+
+ return gpio_pin_interrupt_configure_dt(
+ &power_signal_gpio_list[index].spec,
+ GPIO_INT_DISABLE);
+}
+
+int power_signal_enable_interrupt(enum power_signal signal)
+{
+ int index = power_signal_list[signal].source_id;
+
+ return gpio_pin_interrupt_configure_dt(
+ &power_signal_gpio_list[index].spec,
+ power_signal_gpio_list[index].intr_flags);
+}
+
+int power_wait_mask_signals_timeout(uint32_t want, uint32_t mask, int timeout)
+{
+ int time_left = timeout;
+
+ pwrseq_ctx.in_want = want;
+ if (!mask)
+ return 0;
+
+ while (time_left--) {
+ if ((pwrseq_ctx.in_signals & mask) != pwrseq_ctx.in_want)
+ k_msleep(1);
+ else
+ return 0;
+ }
+
+ power_update_signals();
+ return -ETIMEDOUT;
+}
+
+int power_wait_signals_timeout(uint32_t want, int timeout)
+{
+ return power_wait_mask_signals_timeout(want, want, timeout);
+}
+
+int power_wait_signals(uint32_t want)
+{
+ int ret = power_wait_signals_timeout(want,
+ com_cfg.wait_signal_timeout_ms);
+
+ if (ret == -ETIMEDOUT)
+ LOG_INF("power timeout on input; wanted 0x%04x, got 0x%04x",
+ want, pwrseq_ctx.in_signals & want);
+ return ret;
+}
+
+__attribute__((weak)) int board_power_signal_is_asserted(
+ enum power_signal signal)
+{
+ return 0;
+}
+
+/* TODO: b/220634934 use GPIO logical level */
+int power_signal_is_asserted(enum power_signal signal)
+{
+ int flags = power_signal_list[signal].flags;
+ int id = power_signal_list[signal].source_id;
+
+ if (power_signal_list[signal].source == SOURCE_GPIO)
+ /* GPIO generated signal */
+ return gpio_pin_get_dt(
+ &power_signal_gpio_list[id].spec) ==
+ !!(flags & POWER_SIGNAL_ACTIVE_STATE);
+ else if (power_signal_list[signal].source == SOURCE_VW)
+ /* ESPI generated signal */
+ return vw_get_level(id) ==
+ !!(flags & POWER_SIGNAL_ACTIVE_STATE);
+ else /* TODO: handle SOURCE_ADC */
+ return board_power_signal_is_asserted(signal);
+}
+
+/**
+ * Update input signals mask
+ */
+void power_update_signals(void)
+{
+ uint32_t inew = 0;
+ int i;
+
+ for (i = 0; i < POWER_SIGNAL_COUNT; i++) {
+ if (power_signal_is_asserted(i))
+ inew |= BIT(i);
+ }
+
+ if ((pwrseq_ctx.in_signals & pwrseq_ctx.in_debug) !=
+ (inew & pwrseq_ctx.in_debug))
+ LOG_INF("power update 0x%04x->0x%04x",
+ pwrseq_ctx.in_signals, inew);
+ pwrseq_ctx.in_signals = inew;
+}
+
+uint32_t power_get_signals(void)
+{
+ return pwrseq_ctx.in_signals;
+}
+
+bool power_has_signals(uint32_t want)
+{
+ if ((pwrseq_ctx.in_signals & want) == want)
+ return true;
+
+ return false;
+}
+
+void power_signal_interrupt(const struct device *gpiodev,
+ struct gpio_callback *cb,
+ uint32_t pin)
+{
+ int index = cb - &intr_callbacks[0];
+
+ if (power_signal_gpio_list[index].power_signal
+ >= POWER_SIGNAL_COUNT)
+ return;
+
+ /* TODO: Monitor interrupt storm */
+
+ power_update_signals();
}
static int check_power_rails_enabled(void)
@@ -87,6 +212,7 @@ static int check_power_rails_enabled(void)
static void pwrseq_gpio_init(void)
{
int ret = 0;
+ int i;
/* Configure the GPIO */
#if PWRSEQ_GPIO_PRESENT(en_pp5000_s5_gpios)
@@ -117,6 +243,37 @@ static void pwrseq_gpio_init(void)
LOG_INF("Configuring GPIO complete");
else
LOG_ERR("GPIO configure failed\n");
+
+ /* Initialize GPIO interrupts */
+ for (i = 0; i < POWER_SIGNAL_GPIO_COUNT; i++) {
+ const struct gpio_power_signal_config *int_config = NULL;
+
+ int_config = &power_signal_gpio_list[i];
+
+ /* Configure interrupt */
+ gpio_init_callback(&intr_callbacks[i],
+ power_signal_interrupt,
+ BIT(int_config->spec.pin));
+ ret = gpio_add_callback(int_config->spec.port,
+ &intr_callbacks[i]);
+
+ if (!ret) {
+ if (int_config->enable_on_boot)
+ gpio_pin_interrupt_configure_dt(
+ &int_config->spec,
+ int_config->intr_flags);
+ } else {
+ LOG_ERR("Fail to config interrupt %s, ret=%d",
+ power_signal_list[i].debug_name, ret);
+ }
+ }
+
+ /*
+ * Update input state again since there is a small window
+ * before GPIO is enabled.
+ */
+ power_update_signals();
+
}
enum power_states_ndsx pwr_sm_get_state(void)
@@ -132,6 +289,21 @@ void pwr_sm_set_state(enum power_states_ndsx new_state)
pwrseq_ctx.power_state = new_state;
}
+uint32_t pwrseq_get_input_signals(void)
+{
+ return pwrseq_ctx.in_signals;
+}
+
+void pwrseq_set_debug_signals(uint32_t signals)
+{
+ pwrseq_ctx.in_debug = signals;
+}
+
+uint32_t pwrseq_get_debug_signals(void)
+{
+ return pwrseq_ctx.in_debug;
+}
+
void apshutdown(void)
{
if (pwr_sm_get_state() != SYS_POWER_STATE_G3) {
@@ -150,7 +322,14 @@ int check_rsmrst_ok(void)
int check_pch_out_of_suspend(void)
{
- return gpio_pin_get_dt(&com_cfg.slp_sus_l);
+ int ret;
+
+ ret = power_wait_signals_timeout(
+ IN_PCH_SLP_SUS_DEASSERTED, IN_PCH_SLP_SUS_WAIT_TIME_MS);
+
+ if (ret == 0)
+ return 1;
+ return 0; /* timeout */
}
/* Handling RSMRST signal is mostly common across x86 chipsets */
@@ -170,7 +349,6 @@ __attribute__((weak)) void rsmrst_pass_thru_handler(void)
/* TODO:
* Add power down sequence
- * Add power signal monitoring
* Add logic to suspend and resume the thread
*/
static int common_pwr_sm_run(int state)
@@ -181,19 +359,46 @@ static int common_pwr_sm_run(int state)
break;
case SYS_POWER_STATE_G3S5:
- /* TODO: Check if we are good to move to S5*/
+ if (power_wait_signals(IN_PGOOD_ALL_CORE))
+ break;
+ /*
+ * Now wait for SLP_SUS_L to go high based on tPCH32. If this
+ * signal doesn't go high within 250 msec then go back to G3.
+ */
if (check_pch_out_of_suspend())
return SYS_POWER_STATE_S5;
- break;
+ return SYS_POWER_STATE_S5G3;
case SYS_POWER_STATE_S5:
- /* If A-rails are stable move to higher state */
+ /* In S5 make sure no more signal lost */
+ /* If A-rails are stable then move to higher state */
if (check_power_rails_enabled() && check_rsmrst_ok()) {
/* rsmrst is intact */
rsmrst_pass_thru_handler();
- return SYS_POWER_STATE_S5S4;
+ if (!power_has_signals(IN_PCH_SLP_SUS_DEASSERTED)) {
+ k_timer_stop(&s5_inactive_timer);
+ return SYS_POWER_STATE_S5G3;
+ }
+ if (power_has_signals(IN_PCH_SLP_S5_DEASSERTED)) {
+ k_timer_stop(&s5_inactive_timer);
+ return SYS_POWER_STATE_S5S4;
+ }
}
- return SYS_POWER_STATE_S5G3;
+ /* S5 inactivity timeout, go to S5G3 */
+ if (com_cfg.s5_timeout_s == 0)
+ return SYS_POWER_STATE_S5G3;
+ else if (com_cfg.s5_timeout_s > 0) {
+ if (k_timer_status_get(&s5_inactive_timer) > 0)
+ /* Timer is expired */
+ return SYS_POWER_STATE_S5G3;
+ else if (k_timer_remaining_get(
+ &s5_inactive_timer) == 0)
+ /* Timer is not started or stopped */
+ k_timer_start(&s5_inactive_timer,
+ K_SECONDS(com_cfg.s5_timeout_s),
+ K_NO_WAIT);
+ }
+ break;
case SYS_POWER_STATE_S5G3:
chipset_force_shutdown(PWRSEQ_CHIPSET_SHUTDOWN_G3, &com_cfg);
@@ -209,28 +414,88 @@ static int common_pwr_sm_run(int state)
return SYS_POWER_STATE_S5;
case SYS_POWER_STATE_S4:
+ if (!power_has_signals(IN_PCH_SLP_S5_DEASSERTED))
+ return SYS_POWER_STATE_S4S5;
+ else if (power_has_signals(IN_PCH_SLP_S4_DEASSERTED))
+ return SYS_POWER_STATE_S4S3;
+
+ break;
+
+ case SYS_POWER_STATE_S4S3:
+ if (!power_has_signals(IN_PGOOD_ALL_CORE)) {
+ /* Required rail went away */
+ chipset_force_shutdown(
+ PWRSEQ_CHIPSET_SHUTDOWN_POWERFAIL, &com_cfg);
+ return SYS_POWER_STATE_G3;
+ }
+
+ /* Call hooks now that rails are up */
+ /* TODO: hook_notify(HOOK_CHIPSET_STARTUP); */
+ /* TODO: S0ix
+ * Clearing the S0ix flag on the path to S0
+ * to handle any reset conditions.
+ */
+
return SYS_POWER_STATE_S3;
case SYS_POWER_STATE_S3:
/* AP is out of suspend to RAM */
- if (gpio_pin_get_dt(&com_cfg.slp_s3_l))
+ if (!power_has_signals(IN_PGOOD_ALL_CORE)) {
+ /* Required rail went away, go straight to S5 */
+ chipset_force_shutdown(
+ PWRSEQ_CHIPSET_SHUTDOWN_POWERFAIL, &com_cfg);
+ return SYS_POWER_STATE_G3;
+ } else if (power_has_signals(IN_PCH_SLP_S3_DEASSERTED))
return SYS_POWER_STATE_S3S0;
+ else if (!power_has_signals(IN_PCH_SLP_S4_DEASSERTED))
+ return SYS_POWER_STATE_S3S4;
+
break;
case SYS_POWER_STATE_S3S0:
+ if (!power_has_signals(IN_PGOOD_ALL_CORE)) {
+ chipset_force_shutdown(
+ PWRSEQ_CHIPSET_SHUTDOWN_POWERFAIL, &com_cfg);
+ return SYS_POWER_STATE_G3;
+ }
+
/* All the power rails must be stable */
if (gpio_pin_get_dt(&com_cfg.all_sys_pwrgd))
return SYS_POWER_STATE_S0;
break;
case SYS_POWER_STATE_S0:
- /* Stay in S0 */
+ if (!power_has_signals(IN_PGOOD_ALL_CORE)) {
+ chipset_force_shutdown(
+ PWRSEQ_CHIPSET_SHUTDOWN_POWERFAIL, &com_cfg);
+ return SYS_POWER_STATE_G3;
+ } else if (!power_has_signals(IN_PCH_SLP_S3_DEASSERTED))
+ return SYS_POWER_STATE_S0S3;
+ /* TODO: S0ix */
+
break;
case SYS_POWER_STATE_S4S5:
+ /* TODO */
+ /* Call hooks before we remove power rails */
+ /* hook_notify(HOOK_CHIPSET_SHUTDOWN); */
+ /* Disable wireless */
+ /* wireless_set_state(WIRELESS_OFF); */
+ /* Call hooks after we remove power rails */
+ /* hook_notify(HOOK_CHIPSET_SHUTDOWN_COMPLETE); */
+ /* 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.
+ */
+ return SYS_POWER_STATE_S5;
+
case SYS_POWER_STATE_S3S4:
+ return SYS_POWER_STATE_S4;
+
case SYS_POWER_STATE_S0S3:
- break;
+ /* TODO: Call hooks before we remove power rails */
+ /* hook_notify(HOOK_CHIPSET_SUSPEND); */
+ return SYS_POWER_STATE_S3;
default:
break;
@@ -243,9 +508,29 @@ static void pwrseq_loop_thread(void *p1, void *p2, void *p3)
{
int32_t t_wait_ms = 10;
enum power_states_ndsx curr_state, new_state;
+ uint32_t this_in_signals;
+ static uint32_t last_in_signals;
+ static enum power_states_ndsx last_state;
while (1) {
curr_state = pwr_sm_get_state();
+
+ /*
+ * In order to prevent repeated console spam, only print the
+ * current power state if something has actually changed. It's
+ * possible that one of the power signals goes away briefly and
+ * comes back by the time we update our pwrseq_ctx.in_signals.
+ */
+ this_in_signals = pwrseq_ctx.in_signals;
+ if (this_in_signals != last_in_signals ||
+ curr_state != last_state) {
+ LOG_INF("power state %d = %s, in 0x%04x",
+ curr_state, pwrsm_dbg[curr_state],
+ this_in_signals);
+ last_in_signals = this_in_signals;
+ last_state = curr_state;
+ }
+
/* Run chipset specific state machine */
new_state = chipset_pwr_sm_run(curr_state, &com_cfg);
diff --git a/zephyr/subsys/ap_pwrseq/x86_non_dsx_espi.c b/zephyr/subsys/ap_pwrseq/x86_non_dsx_espi.c
index 9625adca57..f0a64370e7 100644
--- a/zephyr/subsys/ap_pwrseq/x86_non_dsx_espi.c
+++ b/zephyr/subsys/ap_pwrseq/x86_non_dsx_espi.c
@@ -23,8 +23,16 @@ static void espi_bus_vw_handler(const struct device *dev,
vw_get_level(event.evt_details));
switch (event.evt_details) {
+#ifdef CONFIG_PLATFORM_EC_ESPI_VW_SLP_S3
case ESPI_VWIRE_SIGNAL_SLP_S3:
+#endif
+#ifdef CONFIG_PLATFORM_EC_ESPI_VW_SLP_S4
case ESPI_VWIRE_SIGNAL_SLP_S4:
+#endif
+#ifdef CONFIG_PLATFORM_EC_ESPI_VW_SLP_S5
+ case ESPI_VWIRE_SIGNAL_SLP_S5:
+#endif
+ power_update_signals();
break;
default:
break;
diff --git a/zephyr/subsys/ap_pwrseq/x86_power_signals.c b/zephyr/subsys/ap_pwrseq/x86_power_signals.c
new file mode 100644
index 0000000000..ceac962683
--- /dev/null
+++ b/zephyr/subsys/ap_pwrseq/x86_power_signals.c
@@ -0,0 +1,79 @@
+/* Copyright 2022 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.
+ */
+
+/* Compose power signals list from device tree */
+#include <shell/shell.h>
+#include <stdlib.h>
+#include <x86_power_signals.h>
+
+#define GEN_ENABLE_ON_BOOT_DATA(id) \
+ COND_CODE_1(DT_PROP(id, enable_int_on_boot), (1), (0))
+
+#define POWER_SIGNAL_CONFIG_ENTRY(id, src, src_id) \
+[POWER_SIGNAL_ENUM(id)] = \
+{ \
+ .source = src, \
+ .debug_name = DT_PROP(id, dbg_label), \
+ .source_id = src_id, \
+ .flags = DT_PROP(id, flags), \
+},
+
+#define GEN_BOARD_ENTRY(id) \
+ POWER_SIGNAL_CONFIG_ENTRY(id, SOURCE_BOARD, \
+ DT_STRING_UPPER_TOKEN(id, pwrseq_board_value))
+
+#define POWER_SIGNAL_BOARD_ENTRY(id) \
+ COND_CODE_1(DT_NODE_HAS_PROP(id, pwrseq_board_value), \
+ (GEN_BOARD_ENTRY(id)), ())
+
+#define GEN_ADC_ENTRY(id) \
+ POWER_SIGNAL_CONFIG_ENTRY(id, SOURCE_ADC, \
+ DT_STRING_UPPER_TOKEN(id, pwrseq_adc_enum))
+
+#define POWER_SIGNAL_ADC_ENTRY(id) \
+ COND_CODE_1(DT_NODE_HAS_PROP(id, pwrseq_adc_enum), \
+ (GEN_ADC_ENTRY(id)), ())
+
+#define GEN_VW_ENTRY(id) \
+ POWER_SIGNAL_CONFIG_ENTRY(id, SOURCE_VW, \
+ DT_STRING_UPPER_TOKEN(id, pwrseq_vw_enum))
+
+#define POWER_SIGNAL_VW_ENTRY(id) \
+ COND_CODE_1(DT_NODE_HAS_PROP(id, pwrseq_vw_enum), \
+ (GEN_VW_ENTRY(id)), ())
+
+#define GEN_GPIO_ENTRY(id) \
+ POWER_SIGNAL_CONFIG_ENTRY(id, SOURCE_GPIO, \
+ GPIO_PWR_ENUM(id, POWER_SIGNAL_ENUM(id)))
+
+#define POWER_SIGNAL_GPIO_ENTRY(id) \
+ COND_CODE_1(DT_NODE_HAS_PROP(id, pwrseq_int_gpios), \
+ (GEN_GPIO_ENTRY(id)), ())
+
+const struct power_signal_config power_signal_list[] = {
+ DT_FOREACH_CHILD(POWER_SIGNALS_LIST_NODE, POWER_SIGNAL_GPIO_ENTRY)
+ DT_FOREACH_CHILD(POWER_SIGNALS_LIST_NODE, POWER_SIGNAL_VW_ENTRY)
+ DT_FOREACH_CHILD(POWER_SIGNALS_LIST_NODE, POWER_SIGNAL_ADC_ENTRY)
+ DT_FOREACH_CHILD(POWER_SIGNALS_LIST_NODE, POWER_SIGNAL_BOARD_ENTRY)
+};
+
+#define GEN_GPIO_CONFIG_ENTRY(id) \
+[GPIO_PWR_ENUM(id, POWER_SIGNAL_ENUM(id))] = \
+{ \
+ .spec = GPIO_DT_SPEC_GET(id, pwrseq_int_gpios), \
+ .intr_flags = DT_PROP(id, pwrseq_int_flags), \
+ .enable_on_boot = GEN_ENABLE_ON_BOOT_DATA(id), \
+ .power_signal = POWER_SIGNAL_ENUM(id), \
+},
+
+#define POWER_SIGNAL_GPIO_CONFIG(id) \
+ COND_CODE_1(DT_NODE_HAS_PROP(id, pwrseq_int_gpios), \
+ (GEN_GPIO_CONFIG_ENTRY(id)), ())
+
+const struct gpio_power_signal_config power_signal_gpio_list[] = {
+ DT_FOREACH_CHILD(POWER_SIGNALS_LIST_NODE, POWER_SIGNAL_GPIO_CONFIG)
+};
+
+struct gpio_callback intr_callbacks[POWER_SIGNAL_GPIO_COUNT];