diff options
Diffstat (limited to 'zephyr/subsys/ap_pwrseq')
25 files changed, 815 insertions, 403 deletions
diff --git a/zephyr/subsys/ap_pwrseq/CMakeLists.txt b/zephyr/subsys/ap_pwrseq/CMakeLists.txt index 30edfae4dd..0a77bc821d 100644 --- a/zephyr/subsys/ap_pwrseq/CMakeLists.txt +++ b/zephyr/subsys/ap_pwrseq/CMakeLists.txt @@ -13,9 +13,10 @@ zephyr_library_sources_ifdef(CONFIG_AP_PWRSEQ signal_adc.c ) zephyr_library_sources_ifdef(CONFIG_X86_NON_DSX_PWRSEQ - x86_non_dsx_common_pwrseq_sm_handler.c) -zephyr_library_sources_ifdef(CONFIG_X86_NON_DSX_PWRSEQ + x86_non_dsx_common_pwrseq_sm_handler.c x86_non_dsx_chipset_power_state.c) +zephyr_library_sources_ifdef(CONFIG_AP_PWRSEQ_S0IX_ERROR_RECOVERY + x86_non_dsx_common_pwrseq_host_sleep.c) zephyr_library_sources_ifdef(CONFIG_X86_NON_DSX_PWRSEQ_CONSOLE x86_non_dsx_common_pwrseq_console.c) zephyr_library_sources_ifdef(CONFIG_X86_NON_DSX_PWRSEQ_HOST_CMD diff --git a/zephyr/subsys/ap_pwrseq/Kconfig b/zephyr/subsys/ap_pwrseq/Kconfig index 6f39906bf2..677bca7c6d 100644 --- a/zephyr/subsys/ap_pwrseq/Kconfig +++ b/zephyr/subsys/ap_pwrseq/Kconfig @@ -1,16 +1,19 @@ -# Copyright 2022 The Chromium OS Authors. All rights reserved. +# Copyright 2022 The ChromiumOS Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. menuconfig AP_PWRSEQ bool "AP Power sequencing support" select HAS_TASK_POWERBTN + select GPIO_GET_CONFIG help Enables AP power sequencing support with embedded controller. This includes normal shutdown, critical shutdown and reset handling. Enabling this automatically enables HAS_TASK_POWERBTN since this task is required to handle power button pressed/released by user. + Enabling this also enables retrieving the GPIO config feature + so that the value of output GPIOs can be determined. if AP_PWRSEQ @@ -104,4 +107,14 @@ config AP_PWRSEQ_S0IX required, AP_PWRSEQ_HOST_SLEEP for host sleep event handling is enabled. +config AP_PWRSEQ_S0IX_ERROR_RECOVERY + bool "Detect failure to enter or exit Sleep state" + depends on AP_PWRSEQ_HOST_SLEEP + help + Enables detection of the AP failing to go to sleep, perhaps due to a + bug in the internal SoC periodic housekeeping code. + + Failure information is reported via the EC_CMD_HOST_SLEEP_EVENT host + command. + endif diff --git a/zephyr/subsys/ap_pwrseq/ap_events.c b/zephyr/subsys/ap_pwrseq/ap_events.c index d5b78c2c33..0d99c0fe36 100644 --- a/zephyr/subsys/ap_pwrseq/ap_events.c +++ b/zephyr/subsys/ap_pwrseq/ap_events.c @@ -1,4 +1,4 @@ -/* Copyright 2022 The Chromium OS Authors. All rights reserved. +/* Copyright 2022 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ @@ -71,7 +71,8 @@ void ap_power_ev_send_callbacks(enum ap_power_events event) return; } data.event = event; - SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&callbacks, cb, tmp, node) { + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&callbacks, cb, tmp, node) + { if (cb->events & event) { cb->handler(cb, data); } diff --git a/zephyr/subsys/ap_pwrseq/ap_power_interface.c b/zephyr/subsys/ap_pwrseq/ap_power_interface.c index d6dc352033..1461ed139b 100644 --- a/zephyr/subsys/ap_pwrseq/ap_power_interface.c +++ b/zephyr/subsys/ap_pwrseq/ap_power_interface.c @@ -1,4 +1,4 @@ -/* Copyright 2022 The Chromium OS Authors. All rights reserved. +/* Copyright 2022 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ @@ -6,12 +6,17 @@ #include <ap_power/ap_power_interface.h> #include <x86_non_dsx_common_pwrseq_sm_handler.h> -bool ap_power_in_state( - enum ap_power_state_mask state_mask) +LOG_MODULE_DECLARE(ap_pwrseq, CONFIG_AP_PWRSEQ_LOG_LEVEL); + +bool ap_power_in_state(enum ap_power_state_mask state_mask) { int need_mask = 0; switch (pwr_sm_get_state()) { + case SYS_POWER_STATE_UNINIT: + LOG_WRN("%s: init not yet complete; AP state is unknown", + __func__); + return false; case SYS_POWER_STATE_G3: need_mask = AP_POWER_STATE_HARD_OFF; break; @@ -21,16 +26,14 @@ bool ap_power_in_state( * In between hard and soft off states. Match only if caller * will accept both. */ - need_mask = AP_POWER_STATE_HARD_OFF | - AP_POWER_STATE_SOFT_OFF; + need_mask = AP_POWER_STATE_HARD_OFF | AP_POWER_STATE_SOFT_OFF; break; case SYS_POWER_STATE_S5: need_mask = AP_POWER_STATE_SOFT_OFF; break; case SYS_POWER_STATE_S5S4: case SYS_POWER_STATE_S4S5: - need_mask = AP_POWER_STATE_SOFT_OFF | - AP_POWER_STATE_SUSPEND; + need_mask = AP_POWER_STATE_SOFT_OFF | AP_POWER_STATE_SUSPEND; break; case SYS_POWER_STATE_S4: case SYS_POWER_STATE_S4S3: @@ -40,8 +43,7 @@ bool ap_power_in_state( break; case SYS_POWER_STATE_S3S0: case SYS_POWER_STATE_S0S3: - need_mask = AP_POWER_STATE_SUSPEND | - AP_POWER_STATE_ON; + need_mask = AP_POWER_STATE_SUSPEND | AP_POWER_STATE_ON; break; case SYS_POWER_STATE_S0: need_mask = AP_POWER_STATE_ON; @@ -60,10 +62,13 @@ bool ap_power_in_state( return (state_mask & need_mask) == need_mask; } -bool ap_power_in_or_transitioning_to_state( - enum ap_power_state_mask state_mask) +bool ap_power_in_or_transitioning_to_state(enum ap_power_state_mask state_mask) { switch (pwr_sm_get_state()) { + case SYS_POWER_STATE_UNINIT: + LOG_WRN("%s: init not yet complete; AP state is unknown", + __func__); + return 0; case SYS_POWER_STATE_G3: case SYS_POWER_STATE_S5G3: return state_mask & AP_POWER_STATE_HARD_OFF; @@ -107,7 +112,7 @@ void ap_power_exit_hardoff(void) power_state != SYS_POWER_STATE_S5G3 && power_state != SYS_POWER_STATE_S5) return; - request_exit_hardoff(true); + request_start_from_g3(); } void ap_power_init_reset_log(void) diff --git a/zephyr/subsys/ap_pwrseq/include/ap_power_host_sleep.h b/zephyr/subsys/ap_pwrseq/include/ap_power_host_sleep.h index 9bee8af826..7251f96b76 100644 --- a/zephyr/subsys/ap_pwrseq/include/ap_power_host_sleep.h +++ b/zephyr/subsys/ap_pwrseq/include/ap_power_host_sleep.h @@ -1,4 +1,4 @@ -/* Copyright 2022 The Chromium OS Authors. All rights reserved. +/* Copyright 2022 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ @@ -22,8 +22,8 @@ void ap_power_set_active_wake_mask(void); * * @return 0 for success; -EINVAL if power state is not S3/S5/S0ix */ -int ap_power_get_lazy_wake_mask( - enum power_states_ndsx state, host_event_t *mask); +int ap_power_get_lazy_wake_mask(enum power_states_ndsx state, + host_event_t *mask); #if CONFIG_AP_PWRSEQ_S0IX /* For S0ix path, flag to notify sleep change */ @@ -56,4 +56,17 @@ enum ap_power_sleep_type ap_power_sleep_get_notify(void); void ap_power_sleep_notify_transition(enum ap_power_sleep_type check_state); #endif /* CONFIG_AP_PWRSEQ_S0IX */ +/* + * Get sleep timeout from host command context + */ +uint16_t host_get_sleep_timeout(void); + +/* + * Set sleep transitions for host command response + * + * @param val sleep transitions + * + */ +void host_set_sleep_transitions(uint32_t val); + #endif /* __AP_PWRSEQ_HOST_SLEEP_H */ diff --git a/zephyr/subsys/ap_pwrseq/include/ap_power_override_functions.h b/zephyr/subsys/ap_pwrseq/include/ap_power_override_functions.h index 229bfb7e60..0d9195e5f2 100644 --- a/zephyr/subsys/ap_pwrseq/include/ap_power_override_functions.h +++ b/zephyr/subsys/ap_pwrseq/include/ap_power_override_functions.h @@ -1,4 +1,4 @@ -/* Copyright 2022 The Chromium OS Authors. All rights reserved. +/* Copyright 2022 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ @@ -75,7 +75,7 @@ bool board_ap_power_check_power_rails_enabled(void); /** * @brief macro to access configuration properties from DTS */ -#define AP_PWRSEQ_DT_VALUE(p) \ - DT_PROP(DT_COMPAT_GET_ANY_STATUS_OKAY(intel_ap_pwrseq), p) \ +#define AP_PWRSEQ_DT_VALUE(p) \ + DT_PROP(DT_COMPAT_GET_ANY_STATUS_OKAY(intel_ap_pwrseq), p) #endif /* __AP_PWRSEQ_AP_POWER_BOARD_FUNCTIONS_H__ */ diff --git a/zephyr/subsys/ap_pwrseq/include/power_signals.h b/zephyr/subsys/ap_pwrseq/include/power_signals.h index 8755f1005a..5d3e97a52e 100644 --- a/zephyr/subsys/ap_pwrseq/include/power_signals.h +++ b/zephyr/subsys/ap_pwrseq/include/power_signals.h @@ -1,4 +1,4 @@ -/* Copyright 2022 The Chromium OS Authors. All rights reserved. +/* Copyright 2022 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ @@ -48,10 +48,10 @@ * included if that signal source is configured in the * devicetree. */ -#define HAS_GPIO_SIGNALS DT_HAS_COMPAT_STATUS_OKAY(intel_ap_pwrseq_gpio) -#define HAS_VW_SIGNALS DT_HAS_COMPAT_STATUS_OKAY(intel_ap_pwrseq_vw) -#define HAS_EXT_SIGNALS DT_HAS_COMPAT_STATUS_OKAY(intel_ap_pwrseq_external) -#define HAS_ADC_SIGNALS DT_HAS_COMPAT_STATUS_OKAY(intel_ap_pwrseq_adc) +#define HAS_GPIO_SIGNALS DT_HAS_COMPAT_STATUS_OKAY(intel_ap_pwrseq_gpio) +#define HAS_VW_SIGNALS DT_HAS_COMPAT_STATUS_OKAY(intel_ap_pwrseq_vw) +#define HAS_EXT_SIGNALS DT_HAS_COMPAT_STATUS_OKAY(intel_ap_pwrseq_external) +#define HAS_ADC_SIGNALS DT_HAS_COMPAT_STATUS_OKAY(intel_ap_pwrseq_adc) /** * @brief Definitions for AP power sequence signals. @@ -62,11 +62,9 @@ /** * @brief Generate the enum for this power signal. */ -#define PWR_SIGNAL_ENUM(id) \ - DT_STRING_UPPER_TOKEN(id, enum_name) +#define PWR_SIGNAL_ENUM(id) DT_STRING_UPPER_TOKEN(id, enum_name) -#define PWR_SIGNAL_ENUM_COMMA(id) \ - PWR_SIGNAL_ENUM(id), +#define PWR_SIGNAL_ENUM_COMMA(id) PWR_SIGNAL_ENUM(id), /** * @brief Enum of all power signals. * @@ -78,11 +76,14 @@ * must be the same as in power_signals.c */ enum power_signal { -DT_FOREACH_STATUS_OKAY(intel_ap_pwrseq_gpio, PWR_SIGNAL_ENUM_COMMA) -DT_FOREACH_STATUS_OKAY(intel_ap_pwrseq_vw, PWR_SIGNAL_ENUM_COMMA) -DT_FOREACH_STATUS_OKAY(intel_ap_pwrseq_external, PWR_SIGNAL_ENUM_COMMA) -DT_FOREACH_STATUS_OKAY(intel_ap_pwrseq_adc, PWR_SIGNAL_ENUM_COMMA) - POWER_SIGNAL_COUNT, + DT_FOREACH_STATUS_OKAY(intel_ap_pwrseq_gpio, PWR_SIGNAL_ENUM_COMMA) + DT_FOREACH_STATUS_OKAY(intel_ap_pwrseq_vw, + PWR_SIGNAL_ENUM_COMMA) + DT_FOREACH_STATUS_OKAY(intel_ap_pwrseq_external, + PWR_SIGNAL_ENUM_COMMA) + DT_FOREACH_STATUS_OKAY(intel_ap_pwrseq_adc, + PWR_SIGNAL_ENUM_COMMA) + POWER_SIGNAL_COUNT, }; #undef PWR_SIGNAL_ENUM_COMMA @@ -301,8 +302,7 @@ static inline bool power_signals_off(power_signal_mask_t want) * @return negative If the signals did not match before the timeout. */ int power_wait_mask_signals_timeout(power_signal_mask_t want, - power_signal_mask_t mask, - int timeout); + power_signal_mask_t mask, int timeout); /** * @brief Wait until the selected power signals match, with timeout diff --git a/zephyr/subsys/ap_pwrseq/include/signal_adc.h b/zephyr/subsys/ap_pwrseq/include/signal_adc.h index e43e73e1a7..81c6a1edd4 100644 --- a/zephyr/subsys/ap_pwrseq/include/signal_adc.h +++ b/zephyr/subsys/ap_pwrseq/include/signal_adc.h @@ -1,4 +1,4 @@ -/* Copyright 2022 The Chromium OS Authors. All rights reserved. +/* Copyright 2022 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ @@ -6,7 +6,7 @@ #ifndef __AP_PWRSEQ_SIGNAL_ADC_H__ #define __AP_PWRSEQ_SIGNAL_ADC_H__ -#define PWR_SIG_TAG_ADC PWR_ADC_ +#define PWR_SIG_TAG_ADC PWR_ADC_ /* * Generate enums for the analogue converters. @@ -21,13 +21,13 @@ enum pwr_sig_adc { #if HAS_ADC_SIGNALS -DT_FOREACH_STATUS_OKAY(intel_ap_pwrseq_adc, PWR_ADC_ENUM) + DT_FOREACH_STATUS_OKAY(intel_ap_pwrseq_adc, PWR_ADC_ENUM) #endif - PWR_SIG_ADC_COUNT + PWR_SIG_ADC_COUNT }; -#undef PWR_ADC_ENUM -#undef TAG_ADC +#undef PWR_ADC_ENUM +#undef TAG_ADC /** * @brief Get the value of the ADC power signal. diff --git a/zephyr/subsys/ap_pwrseq/include/signal_gpio.h b/zephyr/subsys/ap_pwrseq/include/signal_gpio.h index e797f0c21f..7cdd4ec316 100644 --- a/zephyr/subsys/ap_pwrseq/include/signal_gpio.h +++ b/zephyr/subsys/ap_pwrseq/include/signal_gpio.h @@ -1,4 +1,4 @@ -/* Copyright 2022 The Chromium OS Authors. All rights reserved. +/* Copyright 2022 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ @@ -6,7 +6,7 @@ #ifndef __AP_PWRSEQ_SIGNAL_GPIO_H__ #define __AP_PWRSEQ_SIGNAL_GPIO_H__ -#define PWR_SIG_TAG_GPIO PWR_GPIO_ +#define PWR_SIG_TAG_GPIO PWR_GPIO_ /* * Generate enums for the GPIOs. @@ -21,13 +21,13 @@ enum pwr_sig_gpio { #if HAS_GPIO_SIGNALS -DT_FOREACH_STATUS_OKAY(intel_ap_pwrseq_gpio, PWR_GPIO_ENUM) + DT_FOREACH_STATUS_OKAY(intel_ap_pwrseq_gpio, PWR_GPIO_ENUM) #endif - PWR_SIG_GPIO_COUNT + PWR_SIG_GPIO_COUNT }; -#undef PWR_GPIO_ENUM -#undef TAG_GPIO +#undef PWR_GPIO_ENUM +#undef TAG_GPIO /** * @brief Get the value of the GPIO power signal. diff --git a/zephyr/subsys/ap_pwrseq/include/signal_vw.h b/zephyr/subsys/ap_pwrseq/include/signal_vw.h index d005daaa40..55ecc73e99 100644 --- a/zephyr/subsys/ap_pwrseq/include/signal_vw.h +++ b/zephyr/subsys/ap_pwrseq/include/signal_vw.h @@ -1,4 +1,4 @@ -/* Copyright 2022 The Chromium OS Authors. All rights reserved. +/* Copyright 2022 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ @@ -6,7 +6,7 @@ #ifndef __AP_PWRSEQ_SIGNAL_VW_H__ #define __AP_PWRSEQ_SIGNAL_VW_H__ -#define PWR_SIG_TAG_VW PWR_VW_ +#define PWR_SIG_TAG_VW PWR_VW_ /* * Generate enums for the virtual wire signals. @@ -21,13 +21,13 @@ enum pwr_sig_vw { #if HAS_VW_SIGNALS -DT_FOREACH_STATUS_OKAY(intel_ap_pwrseq_vw, PWR_VW_ENUM) + DT_FOREACH_STATUS_OKAY(intel_ap_pwrseq_vw, PWR_VW_ENUM) #endif - PWR_SIG_VW_COUNT + PWR_SIG_VW_COUNT }; -#undef PWR_VW_ENUM -#undef TAG_VW +#undef PWR_VW_ENUM +#undef TAG_VW /** * @brief Get the value of the virtual wire signal. diff --git a/zephyr/subsys/ap_pwrseq/include/x86_common_pwrseq.h b/zephyr/subsys/ap_pwrseq/include/x86_common_pwrseq.h index 526b0b6ca6..dcb2b3b968 100644 --- a/zephyr/subsys/ap_pwrseq/include/x86_common_pwrseq.h +++ b/zephyr/subsys/ap_pwrseq/include/x86_common_pwrseq.h @@ -1,4 +1,4 @@ -/* Copyright 2022 The Chromium OS Authors. All rights reserved. +/* Copyright 2022 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ @@ -16,11 +16,6 @@ struct pwrseq_context { /* On power-on start boot up sequence */ enum power_states_ndsx power_state; - /* Indicate should exit G3 power state or not */ - bool want_g3_exit; - /* Indicate to exit G3 state or not with delay in ms*/ - uint32_t reboot_ap_at_g3_delay_ms; - }; #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 f874879f04..2320e61965 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 @@ -1,4 +1,4 @@ -/* Copyright 2022 The Chromium OS Authors. All rights reserved. +/* Copyright 2022 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ @@ -15,7 +15,7 @@ #include <ap_power_host_sleep.h> #include <x86_common_pwrseq.h> -#define DT_DRV_COMPAT intel_ap_pwrseq +#define DT_DRV_COMPAT intel_ap_pwrseq /* The wait time is ~150 msec, allow for safety margin. */ #define IN_PCH_SLP_SUS_WAIT_TIME_MS 250 @@ -23,11 +23,11 @@ enum power_states_ndsx chipset_pwr_sm_run(enum power_states_ndsx curr_state); void init_chipset_pwr_seq_state(void); enum power_states_ndsx chipset_pwr_seq_get_state(void); -void request_exit_hardoff(bool should_exit); +void request_start_from_g3(void); enum power_states_ndsx pwr_sm_get_state(void); -const char * const pwr_sm_get_state_name(enum power_states_ndsx state); +const char *const pwr_sm_get_state_name(enum power_states_ndsx state); void apshutdown(void); void ap_pwrseq_handle_chipset_reset(void); -void set_reboot_ap_at_g3_delay_seconds(uint32_t d_time); +void set_start_from_g3_delay_seconds(uint32_t d_time); #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 index 4e1277dce7..7c7e25d951 100644 --- a/zephyr/subsys/ap_pwrseq/include/x86_power_signals.h +++ b/zephyr/subsys/ap_pwrseq/include/x86_power_signals.h @@ -1,4 +1,4 @@ -/* Copyright 2022 The Chromium OS Authors. All rights reserved. +/* Copyright 2022 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ @@ -8,11 +8,15 @@ #ifndef __X86_POWER_SIGNALS_H__ #define __X86_POWER_SIGNALS_H__ -#define IN_PCH_SLP_S0 POWER_SIGNAL_MASK(PWR_SLP_S0) -#define IN_PCH_SLP_S3 POWER_SIGNAL_MASK(PWR_SLP_S3) -#define IN_PCH_SLP_S4 POWER_SIGNAL_MASK(PWR_SLP_S4) -#define IN_PCH_SLP_S5 POWER_SIGNAL_MASK(PWR_SLP_S5) +#define IN_PCH_SLP_S0 POWER_SIGNAL_MASK(PWR_SLP_S0) +#define IN_PCH_SLP_S3 POWER_SIGNAL_MASK(PWR_SLP_S3) +#define IN_PCH_SLP_S4 POWER_SIGNAL_MASK(PWR_SLP_S4) +#define IN_PCH_SLP_S5 POWER_SIGNAL_MASK(PWR_SLP_S5) +/* + * Define the chipset specific power signal masks and values + * matching the AP state. + */ #if defined(CONFIG_AP_X86_INTEL_ADL) /* Input state flags */ @@ -21,18 +25,31 @@ #define PWRSEQ_G3S5_UP_SIGNAL IN_PCH_SLP_SUS #define PWRSEQ_G3S5_UP_VALUE 0 -#define MASK_ALL_POWER_GOOD \ - (POWER_SIGNAL_MASK(PWR_RSMRST) | \ - POWER_SIGNAL_MASK(PWR_ALL_SYS_PWRGD) | \ - POWER_SIGNAL_MASK(PWR_DSW_PWROK) | \ - POWER_SIGNAL_MASK(PWR_PG_PP1P05)) -#define MASK_S0 \ - (MASK_ALL_POWER_GOOD | \ - POWER_SIGNAL_MASK(PWR_SLP_S0) | \ - POWER_SIGNAL_MASK(PWR_SLP_S3) | \ - POWER_SIGNAL_MASK(PWR_SLP_SUS) | \ - POWER_SIGNAL_MASK(PWR_SLP_S4) | \ +#define MASK_ALL_POWER_GOOD \ + (POWER_SIGNAL_MASK(PWR_RSMRST) | \ + POWER_SIGNAL_MASK(PWR_ALL_SYS_PWRGD) | \ + POWER_SIGNAL_MASK(PWR_DSW_PWROK) | POWER_SIGNAL_MASK(PWR_PG_PP1P05)) + +#define MASK_VW_POWER \ + (POWER_SIGNAL_MASK(PWR_RSMRST) | POWER_SIGNAL_MASK(PWR_DSW_PWROK) | \ + POWER_SIGNAL_MASK(PWR_SLP_SUS)) +#define VALUE_VW_POWER \ + (POWER_SIGNAL_MASK(PWR_RSMRST) | POWER_SIGNAL_MASK(PWR_DSW_PWROK)) + +#define MASK_S0 \ + (MASK_ALL_POWER_GOOD | POWER_SIGNAL_MASK(PWR_SLP_S0) | \ + POWER_SIGNAL_MASK(PWR_SLP_S3) | POWER_SIGNAL_MASK(PWR_SLP_SUS) | \ + POWER_SIGNAL_MASK(PWR_SLP_S4) | POWER_SIGNAL_MASK(PWR_SLP_S5)) +#define VALUE_S0 MASK_ALL_POWER_GOOD + +#define MASK_S3 MASK_S0 +#define VALUE_S3 (MASK_ALL_POWER_GOOD | POWER_SIGNAL_MASK(PWR_SLP_S3)) + +#define MASK_S5 \ + (POWER_SIGNAL_MASK(PWR_RSMRST) | POWER_SIGNAL_MASK(PWR_DSW_PWROK) | \ + POWER_SIGNAL_MASK(PWR_SLP_S3) | POWER_SIGNAL_MASK(PWR_SLP_S4) | \ POWER_SIGNAL_MASK(PWR_SLP_S5)) +#define VALUE_S5 MASK_S5 #elif defined(CONFIG_AP_X86_INTEL_MTL) @@ -41,21 +58,27 @@ #define PWRSEQ_G3S5_UP_VALUE IN_PGOOD_ALL_CORE #define MASK_ALL_POWER_GOOD \ - (POWER_SIGNAL_MASK(PWR_RSMRST) | \ - POWER_SIGNAL_MASK(PWR_ALL_SYS_PWRGD)) -#define MASK_S0 \ - (MASK_ALL_POWER_GOOD | \ - POWER_SIGNAL_MASK(PWR_SLP_S0) | \ - POWER_SIGNAL_MASK(PWR_SLP_S3) | \ - POWER_SIGNAL_MASK(PWR_SLP_S4) | \ + (POWER_SIGNAL_MASK(PWR_RSMRST) | POWER_SIGNAL_MASK(PWR_ALL_SYS_PWRGD)) + +#define MASK_VW_POWER POWER_SIGNAL_MASK(PWR_RSMRST) +#define VALUE_VW_POWER POWER_SIGNAL_MASK(PWR_RSMRST) + +#define MASK_S0 \ + (MASK_ALL_POWER_GOOD | POWER_SIGNAL_MASK(PWR_SLP_S0) | \ + POWER_SIGNAL_MASK(PWR_SLP_S3) | POWER_SIGNAL_MASK(PWR_SLP_S4) | \ POWER_SIGNAL_MASK(PWR_SLP_S5)) +#define VALUE_S0 MASK_ALL_POWER_GOOD + +#define MASK_S3 MASK_S0 +#define VALUE_S3 (MASK_ALL_POWER_GOOD | POWER_SIGNAL_MASK(PWR_SLP_S3)) + +#define MASK_S5 \ + (POWER_SIGNAL_MASK(PWR_RSMRST) | POWER_SIGNAL_MASK(PWR_SLP_S3) | \ + POWER_SIGNAL_MASK(PWR_SLP_S4) | POWER_SIGNAL_MASK(PWR_SLP_S5)) +#define VALUE_S5 MASK_S5 #else #warning("Input power signals state flags not defined"); #endif -#define MASK_S5 \ - (MASK_ALL_POWER_GOOD | \ - POWER_SIGNAL_MASK(PWR_SLP_S5)) - #endif /* __X86_POWER_SIGNALS_H__ */ diff --git a/zephyr/subsys/ap_pwrseq/power_host_sleep.c b/zephyr/subsys/ap_pwrseq/power_host_sleep.c index ff512fa941..30025d21ea 100644 --- a/zephyr/subsys/ap_pwrseq/power_host_sleep.c +++ b/zephyr/subsys/ap_pwrseq/power_host_sleep.c @@ -1,9 +1,10 @@ -/* Copyright 2022 The Chromium OS Authors. All rights reserved. +/* Copyright 2022 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include <ap_power/ap_power_interface.h> +#include <ap_power/ap_pwrseq.h> #include <x86_non_dsx_common_pwrseq_sm_handler.h> LOG_MODULE_DECLARE(ap_pwrseq, CONFIG_AP_PWRSEQ_LOG_LEVEL); @@ -11,8 +12,9 @@ LOG_MODULE_DECLARE(ap_pwrseq, CONFIG_AP_PWRSEQ_LOG_LEVEL); #if CONFIG_PLATFORM_EC_HOST_INTERFACE_ESPI /* If host doesn't program S0ix lazy wake mask, use default S0ix mask */ -#define DEFAULT_WAKE_MASK_S0IX (EC_HOST_EVENT_MASK(EC_HOST_EVENT_LID_OPEN) | \ - EC_HOST_EVENT_MASK(EC_HOST_EVENT_MODE_CHANGE)) +#define DEFAULT_WAKE_MASK_S0IX \ + (EC_HOST_EVENT_MASK(EC_HOST_EVENT_LID_OPEN) | \ + EC_HOST_EVENT_MASK(EC_HOST_EVENT_MODE_CHANGE)) /* * Set the wake mask according to the current power state: @@ -33,7 +35,7 @@ void power_update_wake_mask(void) if (state == SYS_POWER_STATE_S0) wake_mask = 0; else if (lpc_is_active_wm_set_by_host() || - ap_power_get_lazy_wake_mask(state, &wake_mask)) + ap_power_get_lazy_wake_mask(state, &wake_mask)) return; #if CONFIG_AP_PWRSEQ_S0IX if ((state == SYS_POWER_STATE_S0ix) && (wake_mask == 0)) @@ -48,8 +50,8 @@ static void power_update_wake_mask_deferred(struct k_work *work) power_update_wake_mask(); } -static K_WORK_DELAYABLE_DEFINE( - power_update_wake_mask_deferred_data, power_update_wake_mask_deferred); +static K_WORK_DELAYABLE_DEFINE(power_update_wake_mask_deferred_data, + power_update_wake_mask_deferred); void ap_power_set_active_wake_mask(void) { @@ -74,14 +76,16 @@ void ap_power_set_active_wake_mask(void) * has changed again and the work is not processed, we should * reschedule it. */ - rv = k_work_reschedule( - &power_update_wake_mask_deferred_data, K_MSEC(5)); + rv = k_work_reschedule(&power_update_wake_mask_deferred_data, + K_MSEC(5)); } __ASSERT(rv >= 0, "Set wake mask work queue error"); } #else /* CONFIG_PLATFORM_EC_HOST_INTERFACE_ESPI */ -static void ap_power_set_active_wake_mask(void) { } +static void ap_power_set_active_wake_mask(void) +{ +} #endif /* CONFIG_PLATFORM_EC_HOST_INTERFACE_ESPI */ #if CONFIG_AP_PWRSEQ_S0IX @@ -180,11 +184,14 @@ void ap_power_sleep_notify_transition(enum ap_power_sleep_type check_state) #if CONFIG_AP_PWRSEQ_HOST_SLEEP #define HOST_SLEEP_EVENT_DEFAULT_RESET 0 +static struct host_sleep_event_context *g_ctx; + void ap_power_reset_host_sleep_state(void) { power_set_host_sleep_state(HOST_SLEEP_EVENT_DEFAULT_RESET); - ap_power_chipset_handle_host_sleep_event( - HOST_SLEEP_EVENT_DEFAULT_RESET, NULL); + ap_power_ev_send_callbacks(AP_POWER_S0IX_RESET_TRACKING); + ap_power_chipset_handle_host_sleep_event(HOST_SLEEP_EVENT_DEFAULT_RESET, + NULL); } /* TODO: hook to reset event */ @@ -195,19 +202,21 @@ void ap_power_handle_chipset_reset(void) } void ap_power_chipset_handle_host_sleep_event( - enum host_sleep_event state, - struct host_sleep_event_context *ctx) + enum host_sleep_event state, struct host_sleep_event_context *ctx) { LOG_DBG("host sleep event = %d!", state); + + g_ctx = ctx; + #if CONFIG_AP_PWRSEQ_S0IX if (state == HOST_SLEEP_EVENT_S0IX_SUSPEND) { - /* * Indicate to power state machine that a new host event for * s0ix/s3 suspend has been received and so chipset suspend * notification needs to be sent to listeners. */ ap_power_sleep_set_notify(AP_POWER_SLEEP_SUSPEND); + ap_power_ev_send_callbacks(AP_POWER_S0IX_SUSPEND_START); power_signal_enable(PWR_SLP_S0); } else if (state == HOST_SLEEP_EVENT_S0IX_RESUME) { @@ -218,6 +227,7 @@ void ap_power_chipset_handle_host_sleep_event( ap_power_sleep_set_notify(AP_POWER_SLEEP_RESUME); power_s0ix_resume_restore_masks(); power_signal_disable(PWR_SLP_S0); + ap_power_ev_send_callbacks(AP_POWER_S0IX_RESUME_COMPLETE); /* * If the sleep signal timed out and never transitioned, then @@ -231,6 +241,17 @@ void ap_power_chipset_handle_host_sleep_event( power_signal_disable(PWR_SLP_S0); } #endif /* CONFIG_AP_PWRSEQ_S0IX */ + ap_pwrseq_wake(); +} + +uint16_t host_get_sleep_timeout(void) +{ + return g_ctx->sleep_timeout_ms; +} + +void host_set_sleep_transitions(uint32_t val) +{ + g_ctx->sleep_transitions = val; } #endif /* CONFIG_AP_PWRSEQ_HOST_SLEEP */ diff --git a/zephyr/subsys/ap_pwrseq/power_signals.c b/zephyr/subsys/ap_pwrseq/power_signals.c index 135a0d9ac1..a02eef6e6b 100644 --- a/zephyr/subsys/ap_pwrseq/power_signals.c +++ b/zephyr/subsys/ap_pwrseq/power_signals.c @@ -1,4 +1,4 @@ -/* Copyright 2022 The Chromium OS Authors. All rights reserved. +/* Copyright 2022 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ @@ -8,6 +8,7 @@ #include <zephyr/logging/log.h> #include <zephyr/sys/atomic.h> +#include <ap_power/ap_pwrseq.h> #include <power_signals.h> #include "signal_gpio.h" @@ -18,7 +19,7 @@ LOG_MODULE_DECLARE(ap_pwrseq, CONFIG_AP_PWRSEQ_LOG_LEVEL); #if DT_HAS_COMPAT_STATUS_OKAY(intel_ap_pwrseq) BUILD_ASSERT(DT_NUM_INST_STATUS_OKAY(intel_ap_pwrseq) == 1, - "Only one node for intel_ap_pwrseq is allowed"); + "Only one node for intel_ap_pwrseq is allowed"); #endif BUILD_ASSERT(POWER_SIGNAL_COUNT <= 32, "Too many power signals"); @@ -41,49 +42,46 @@ struct ps_config { #define TAG_PWR_ENUM(tag, name) DT_CAT(tag, name) -#define PWR_ENUM(id, tag) \ - TAG_PWR_ENUM(tag, PWR_SIGNAL_ENUM(id)) +#define PWR_ENUM(id, tag) TAG_PWR_ENUM(tag, PWR_SIGNAL_ENUM(id)) -#define DBGNAME(id) \ - "(" DT_PROP(id, enum_name) ") " \ - DT_PROP(id, dbg_label) +#define DBGNAME(id) "(" DT_PROP(id, enum_name) ") " DT_PROP(id, dbg_label) -#define GEN_PS_ENTRY(id, src, tag) \ -{ \ - .debug_name = DBGNAME(id), \ - .source = src, \ - .src_enum = PWR_ENUM(id, tag), \ -}, - -#define GEN_PS_ENTRY_NO_ENUM(id, src) \ -{ \ - .debug_name = DBGNAME(id), \ - .source = src, \ -}, +#define GEN_PS_ENTRY(id, src, tag) \ + { \ + .debug_name = DBGNAME(id), \ + .source = src, \ + .src_enum = PWR_ENUM(id, tag), \ + }, +#define GEN_PS_ENTRY_NO_ENUM(id, src) \ + { \ + .debug_name = DBGNAME(id), \ + .source = src, \ + }, /* * Generate the power signal configuration array. */ static const struct ps_config sig_config[] = { -DT_FOREACH_STATUS_OKAY_VARGS(intel_ap_pwrseq_gpio, GEN_PS_ENTRY, - PWR_SIG_SRC_GPIO, PWR_SIG_TAG_GPIO) -DT_FOREACH_STATUS_OKAY_VARGS(intel_ap_pwrseq_vw, GEN_PS_ENTRY, - PWR_SIG_SRC_VW, PWR_SIG_TAG_VW) -DT_FOREACH_STATUS_OKAY_VARGS(intel_ap_pwrseq_external, GEN_PS_ENTRY_NO_ENUM, - PWR_SIG_SRC_EXT) -DT_FOREACH_STATUS_OKAY_VARGS(intel_ap_pwrseq_adc, GEN_PS_ENTRY, - PWR_SIG_SRC_ADC, PWR_SIG_TAG_ADC) + DT_FOREACH_STATUS_OKAY_VARGS(intel_ap_pwrseq_gpio, GEN_PS_ENTRY, + PWR_SIG_SRC_GPIO, PWR_SIG_TAG_GPIO) + DT_FOREACH_STATUS_OKAY_VARGS(intel_ap_pwrseq_vw, GEN_PS_ENTRY, + PWR_SIG_SRC_VW, PWR_SIG_TAG_VW) + DT_FOREACH_STATUS_OKAY_VARGS(intel_ap_pwrseq_external, + GEN_PS_ENTRY_NO_ENUM, + PWR_SIG_SRC_EXT) + DT_FOREACH_STATUS_OKAY_VARGS( + intel_ap_pwrseq_adc, GEN_PS_ENTRY, + PWR_SIG_SRC_ADC, PWR_SIG_TAG_ADC) }; -#define PWR_SIGNAL_POLLED(id) PWR_SIGNAL_ENUM(id), +#define PWR_SIGNAL_POLLED(id) PWR_SIGNAL_ENUM(id), /* * List of power signals that need to be polled. */ -static const uint8_t polled_signals[] = { -DT_FOREACH_STATUS_OKAY(intel_ap_pwrseq_external, PWR_SIGNAL_POLLED) -}; +static const uint8_t polled_signals[] = { DT_FOREACH_STATUS_OKAY( + intel_ap_pwrseq_external, PWR_SIGNAL_POLLED) }; /* * Bitmasks of power signals. A previous copy is held so that @@ -112,7 +110,7 @@ static inline void check_debug(enum power_signal signal) */ if ((CONFIG_AP_PWRSEQ_LOG_LEVEL >= LOG_LEVEL_INF) && (debug_signals & POWER_SIGNAL_MASK(signal))) { - bool value = atomic_test_bit(&power_signals, signal); + bool value = atomic_test_bit(&power_signals, signal); if (value != atomic_test_bit(&prev_power_signals, signal)) { LOG_INF("%s -> %d", power_signal_name(signal), value); @@ -137,11 +135,11 @@ void power_signal_interrupt(enum power_signal signal, int value) { atomic_set_bit_to(&power_signals, signal, value); check_debug(signal); + ap_pwrseq_wake(); } int power_wait_mask_signals_timeout(power_signal_mask_t mask, - power_signal_mask_t want, - int timeout) + power_signal_mask_t want, int timeout) { if (mask == 0) { return 0; @@ -166,7 +164,7 @@ int power_signal_get(enum power_signal signal) cp = &sig_config[signal]; switch (cp->source) { default: - return -EINVAL; /* should never happen */ + return -EINVAL; /* should never happen */ #if HAS_GPIO_SIGNALS case PWR_SIG_SRC_GPIO: diff --git a/zephyr/subsys/ap_pwrseq/signal_adc.c b/zephyr/subsys/ap_pwrseq/signal_adc.c index 4b8f0e0366..c23cd0d30a 100644 --- a/zephyr/subsys/ap_pwrseq/signal_adc.c +++ b/zephyr/subsys/ap_pwrseq/signal_adc.c @@ -1,4 +1,4 @@ -/* Copyright 2022 The Chromium OS Authors. All rights reserved. +/* Copyright 2022 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ @@ -11,7 +11,7 @@ #include <power_signals.h> #include <signal_adc.h> -#define MY_COMPAT intel_ap_pwrseq_adc +#define MY_COMPAT intel_ap_pwrseq_adc #if HAS_ADC_SIGNALS @@ -26,74 +26,59 @@ struct adc_config { enum power_signal signal; }; -#define ADC_HIGH_DEV(id) DEVICE_DT_GET(DT_IO_CHANNELS_CTLR(id)) +#define ADC_HIGH_DEV(id) DEVICE_DT_GET(DT_IO_CHANNELS_CTLR(id)) -#define ADC_HIGH_CHAN(id) DT_IO_CHANNELS_INPUT(id) +#define ADC_HIGH_CHAN(id) DT_IO_CHANNELS_INPUT(id) -#define ADC_THRESH(id) DT_PROP(id, threshold_mv) +#define ADC_THRESH(id) DT_PROP(id, threshold_mv) -#define INIT_ADC_CONFIG(id) \ -{ \ - .dev_trig_high = DEVICE_DT_GET(DT_PHANDLE(id, trigger_high)), \ - .dev_trig_low = DEVICE_DT_GET(DT_PHANDLE(id, trigger_low)), \ - .adc_dev = ADC_HIGH_DEV(DT_PHANDLE(id, trigger_high)), \ - .adc_ch = ADC_HIGH_CHAN(DT_PHANDLE(id, trigger_high)), \ - .threshold = ADC_THRESH(DT_PHANDLE(id, trigger_high)), \ - .signal = PWR_SIGNAL_ENUM(id), \ -}, +#define INIT_ADC_CONFIG(id) \ + { \ + .dev_trig_high = DEVICE_DT_GET(DT_PHANDLE(id, trigger_high)), \ + .dev_trig_low = DEVICE_DT_GET(DT_PHANDLE(id, trigger_low)), \ + .adc_dev = ADC_HIGH_DEV(DT_PHANDLE(id, trigger_high)), \ + .adc_ch = ADC_HIGH_CHAN(DT_PHANDLE(id, trigger_high)), \ + .threshold = ADC_THRESH(DT_PHANDLE(id, trigger_high)), \ + .signal = PWR_SIGNAL_ENUM(id), \ + }, -static const struct adc_config config[] = { -DT_FOREACH_STATUS_OKAY(MY_COMPAT, INIT_ADC_CONFIG) -}; +static const struct adc_config config[] = { DT_FOREACH_STATUS_OKAY( + MY_COMPAT, INIT_ADC_CONFIG) }; /* * Bit allocations for atomic state */ -enum { - ADC_BIT_VALUE = 0, - ADC_BIT_LOW_ENABLED = 1, - ADC_BIT_HIGH_ENABLED = 2 -}; +enum { ADC_BIT_VALUE = 0, ADC_BIT_LOW_ENABLED = 1, ADC_BIT_HIGH_ENABLED = 2 }; atomic_t adc_state[ARRAY_SIZE(config)]; -static void set_trigger(const struct device *dev, - atomic_t *state, - int bit, +static void set_trigger(const struct device *dev, atomic_t *state, int bit, bool enable) { /* * Only enable or disable if the trigger is not * already enabled or disabled. */ - if (enable - ? !atomic_test_and_set_bit(state, bit) - : atomic_test_and_clear_bit(state, bit)) { + if (enable ? !atomic_test_and_set_bit(state, bit) : + atomic_test_and_clear_bit(state, bit)) { struct sensor_value val; val.val1 = enable; - sensor_attr_set(dev, - SENSOR_CHAN_VOLTAGE, - SENSOR_ATTR_ALERT, + sensor_attr_set(dev, SENSOR_CHAN_VOLTAGE, SENSOR_ATTR_ALERT, &val); } } static void set_low_trigger(enum pwr_sig_adc adc, bool enable) { - set_trigger(config[adc].dev_trig_low, - &adc_state[adc], - ADC_BIT_LOW_ENABLED, - enable); - + set_trigger(config[adc].dev_trig_low, &adc_state[adc], + ADC_BIT_LOW_ENABLED, enable); } static void set_high_trigger(enum pwr_sig_adc adc, bool enable) { - set_trigger(config[adc].dev_trig_high, - &adc_state[adc], - ADC_BIT_HIGH_ENABLED, - enable); + set_trigger(config[adc].dev_trig_high, &adc_state[adc], + ADC_BIT_HIGH_ENABLED, enable); } static void trigger_high(enum pwr_sig_adc adc) @@ -156,34 +141,30 @@ int power_signal_adc_disable(enum pwr_sig_adc adc) #define PWR_ADC_ENUM(id) TAG_ADC(PWR_SIG_TAG_ADC, PWR_SIGNAL_ENUM(id)) -#define ADC_CB(id, lev) cb_##lev##_##id +#define ADC_CB(id, lev) cb_##lev##_##id -#define ADC_CB_DEFINE(id, lev) \ -static void ADC_CB(id, lev)(const struct device *dev, \ - const struct sensor_trigger *trigger) \ -{ \ - trigger_##lev(PWR_ADC_ENUM(id)); \ -} +#define ADC_CB_DEFINE(id, lev) \ + static void ADC_CB(id, lev)(const struct device *dev, \ + const struct sensor_trigger *trigger) \ + { \ + trigger_##lev(PWR_ADC_ENUM(id)); \ + } DT_FOREACH_STATUS_OKAY_VARGS(MY_COMPAT, ADC_CB_DEFINE, high) DT_FOREACH_STATUS_OKAY_VARGS(MY_COMPAT, ADC_CB_DEFINE, low) -#define ADC_CB_COMMA(id, lev) ADC_CB(id, lev), +#define ADC_CB_COMMA(id, lev) ADC_CB(id, lev), void power_signal_adc_init(void) { - struct sensor_trigger trig = { - .type = SENSOR_TRIG_THRESHOLD, - .chan = SENSOR_CHAN_VOLTAGE - }; - sensor_trigger_handler_t low_cb[] = { - DT_FOREACH_STATUS_OKAY_VARGS(MY_COMPAT, ADC_CB_COMMA, low) - }; - sensor_trigger_handler_t high_cb[] = { - DT_FOREACH_STATUS_OKAY_VARGS(MY_COMPAT, ADC_CB_COMMA, high) - }; + struct sensor_trigger trig = { .type = SENSOR_TRIG_THRESHOLD, + .chan = SENSOR_CHAN_VOLTAGE }; + sensor_trigger_handler_t low_cb[] = { DT_FOREACH_STATUS_OKAY_VARGS( + MY_COMPAT, ADC_CB_COMMA, low) }; + sensor_trigger_handler_t high_cb[] = { DT_FOREACH_STATUS_OKAY_VARGS( + MY_COMPAT, ADC_CB_COMMA, high) }; int i, rv; - int32_t val; + int32_t val = 0; for (i = 0; i < ARRAY_SIZE(low_cb); i++) { /* @@ -202,11 +183,10 @@ void power_signal_adc_init(void) rv = adc_read(dev, &seq); if (rv) { - LOG_ERR("ADC %s:%d initial read failed", - dev->name, config[i].adc_ch); + LOG_ERR("ADC %s:%d initial read failed", dev->name, + config[i].adc_ch); } else { - adc_raw_to_millivolts(adc_ref_internal(dev), - ADC_GAIN_1, + adc_raw_to_millivolts(adc_ref_internal(dev), ADC_GAIN_1, CONFIG_PLATFORM_EC_ADC_RESOLUTION, &val); if (val >= config[i].threshold) { diff --git a/zephyr/subsys/ap_pwrseq/signal_gpio.c b/zephyr/subsys/ap_pwrseq/signal_gpio.c index 9f8c3adb48..1dbd430bef 100644 --- a/zephyr/subsys/ap_pwrseq/signal_gpio.c +++ b/zephyr/subsys/ap_pwrseq/signal_gpio.c @@ -1,4 +1,4 @@ -/* Copyright 2022 The Chromium OS Authors. All rights reserved. +/* Copyright 2022 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ @@ -8,16 +8,14 @@ #include <zephyr/drivers/gpio.h> #include "system.h" -#define MY_COMPAT intel_ap_pwrseq_gpio +#define MY_COMPAT intel_ap_pwrseq_gpio #if HAS_GPIO_SIGNALS -#define INIT_GPIO_SPEC(id) \ - GPIO_DT_SPEC_GET(id, gpios), +#define INIT_GPIO_SPEC(id) GPIO_DT_SPEC_GET(id, gpios), -const static struct gpio_dt_spec spec[] = { -DT_FOREACH_STATUS_OKAY(MY_COMPAT, INIT_GPIO_SPEC) -}; +const static struct gpio_dt_spec spec[] = { DT_FOREACH_STATUS_OKAY( + MY_COMPAT, INIT_GPIO_SPEC) }; /* * Configuration for GPIO inputs. @@ -29,17 +27,16 @@ struct ps_gpio_int { unsigned no_enable : 1; }; -#define INIT_GPIO_CONFIG(id) \ - { \ - .flags = DT_PROP_OR(id, interrupt_flags, 0), \ - .signal = PWR_SIGNAL_ENUM(id), \ - .no_enable = DT_PROP(id, no_enable), \ - .output = DT_PROP(id, output), \ - }, +#define INIT_GPIO_CONFIG(id) \ + { \ + .flags = DT_PROP_OR(id, interrupt_flags, 0), \ + .signal = PWR_SIGNAL_ENUM(id), \ + .no_enable = DT_PROP(id, no_enable), \ + .output = DT_PROP(id, output), \ + }, -const static struct ps_gpio_int gpio_config[] = { -DT_FOREACH_STATUS_OKAY(MY_COMPAT, INIT_GPIO_CONFIG) -}; +const static struct ps_gpio_int gpio_config[] = { DT_FOREACH_STATUS_OKAY( + MY_COMPAT, INIT_GPIO_CONFIG) }; static struct gpio_callback int_cb[ARRAY_SIZE(gpio_config)]; @@ -103,6 +100,34 @@ int power_signal_gpio_get(enum pwr_sig_gpio index) if (index < 0 || index >= ARRAY_SIZE(gpio_config)) { return -EINVAL; } + /* + * Getting the current value of an output is + * done by retrieving the config and checking what the + * output state has been set to, not by reading the + * physical level of the pin (open drain outputs + * may have a low voltage). + */ + if (IS_ENABLED(CONFIG_GPIO_GET_CONFIG) && gpio_config[index].output) { + int rv; + gpio_flags_t flags; + + rv = gpio_pin_get_config_dt(&spec[index], &flags); + if (rv == 0) { + int pin = (flags & GPIO_OUTPUT_INIT_HIGH) ? 1 : 0; + /* If active low signal, invert it */ + if (spec[index].dt_flags & GPIO_ACTIVE_LOW) { + pin = !pin; + } + return pin; + } + /* + * -ENOSYS is returned when this API call is not supported, + * so drop into the default method of returning the pin value. + */ + if (rv != -ENOSYS) { + return rv; + } + } return gpio_pin_get_dt(&spec[index]); } @@ -123,7 +148,8 @@ void power_signal_gpio_init(void) * to the deasserted state. */ gpio_flags_t out_flags = system_jumped_to_this_image() ? - GPIO_OUTPUT : GPIO_OUTPUT_INACTIVE; + GPIO_OUTPUT : + GPIO_OUTPUT_INACTIVE; for (int i = 0; i < ARRAY_SIZE(gpio_config); i++) { if (gpio_config[i].output) { @@ -133,8 +159,8 @@ void power_signal_gpio_init(void) /* If interrupt, initialise it */ if (gpio_config[i].flags) { gpio_init_callback(&int_cb[i], - power_signal_gpio_interrupt, - BIT(spec[i].pin)); + power_signal_gpio_interrupt, + BIT(spec[i].pin)); gpio_add_callback(spec[i].port, &int_cb[i]); /* * If the interrupt is to be enabled at diff --git a/zephyr/subsys/ap_pwrseq/signal_vw.c b/zephyr/subsys/ap_pwrseq/signal_vw.c index de2756c137..0e9e4affff 100644 --- a/zephyr/subsys/ap_pwrseq/signal_vw.c +++ b/zephyr/subsys/ap_pwrseq/signal_vw.c @@ -1,4 +1,4 @@ -/* Copyright 2022 The Chromium OS Authors. All rights reserved. +/* Copyright 2022 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ @@ -9,40 +9,47 @@ #include "signal_vw.h" -#define MY_COMPAT intel_ap_pwrseq_vw +#define MY_COMPAT intel_ap_pwrseq_vw #if HAS_VW_SIGNALS +/* + * A callback must be registered on the ESPI device (for the + * bus events that are required to be handled) that calls + * power_signal_espi_cb(). + * + * This registration is done in a common ESPI initialisation module so + * that there is no possibility of missing events. + */ + LOG_MODULE_DECLARE(ap_pwrseq, CONFIG_AP_PWRSEQ_LOG_LEVEL); -#define INIT_ESPI_SIGNAL(id) \ -{ \ - .espi_signal = DT_STRING_UPPER_TOKEN(id, virtual_wire), \ - .signal = PWR_SIGNAL_ENUM(id), \ - .invert = DT_PROP(id, vw_invert), \ -}, +#define INIT_ESPI_SIGNAL(id) \ + { \ + .espi_signal = DT_STRING_UPPER_TOKEN(id, virtual_wire), \ + .signal = PWR_SIGNAL_ENUM(id), \ + .invert = DT_PROP(id, vw_invert), \ + }, /* * Struct containing the eSPI virtual wire config. */ struct vw_config { - uint8_t espi_signal; /* associated VW signal */ - uint8_t signal; /* power signal */ - bool invert; /* Invert the signal value */ + uint8_t espi_signal; /* associated VW signal */ + uint8_t signal; /* power signal */ + bool invert; /* Invert the signal value */ }; -const static struct vw_config vw_config[] = { -DT_FOREACH_STATUS_OKAY(MY_COMPAT, INIT_ESPI_SIGNAL) -}; +const static struct vw_config vw_config[] = { DT_FOREACH_STATUS_OKAY( + MY_COMPAT, INIT_ESPI_SIGNAL) }; /* * Current signal value. */ static atomic_t signal_data; /* - * Mask of valid signals. If the bus is reset, this is cleared, - * and when a signal is updated the associated bit is set to indicate - * the signal is valid. + * Mask of valid signals. A signal is considered valid once an + * initial value has been received for it. */ static atomic_t signal_valid; @@ -50,36 +57,63 @@ static atomic_t signal_valid; BUILD_ASSERT(ARRAY_SIZE(vw_config) <= (sizeof(atomic_t) * 8)); -static void espi_handler(const struct device *dev, - struct espi_callback *cb, - struct espi_event event) +/* + * Set the value of the VW signal, and optionally + * call the power signal interrupt handling. + */ +static void vw_set(int index, int data, bool notify) +{ + bool value = vw_config[index].invert ? !data : !!data; + + atomic_set_bit_to(&signal_data, index, value); + atomic_set_bit(&signal_valid, index); + if (notify) { + power_signal_interrupt(vw_config[index].signal, value); + } +} + +/* + * Update all the VW signals. + */ +static void vw_update_all(bool notify) +{ + for (int i = 0; i < ARRAY_SIZE(vw_config); i++) { + uint8_t vw_value; + + if (espi_receive_vwire(espi_dev, vw_config[i].espi_signal, + &vw_value) == 0) { + vw_set(i, vw_value, notify); + } + } +} + +void power_signal_espi_cb(const struct device *dev, struct espi_callback *cb, + struct espi_event event) { - LOG_DBG("ESPI event type 0x%x %d:%d", event.evt_type, - event.evt_details, event.evt_data); + LOG_DBG("ESPI event type 0x%x %d:%d", event.evt_type, event.evt_details, + event.evt_data); switch (event.evt_type) { default: - __ASSERT(0, "ESPI unknown event type: %d", - event.evt_type); + __ASSERT(0, "ESPI unknown event type: %d", event.evt_type); break; - case ESPI_BUS_RESET: - /* - * Clear the signal valid mask. - */ - atomic_clear(&signal_valid); + case ESPI_BUS_EVENT_CHANNEL_READY: + /* Virtual wire channel status change */ + if (event.evt_details == ESPI_CHANNEL_VWIRE) { + if (event.evt_data) { + /* If now ready, update all the signals */ + vw_update_all(true); + } else { + /* If not ready, invalidate the signals */ + atomic_clear(&signal_valid); + } + } break; case ESPI_BUS_EVENT_VWIRE_RECEIVED: for (int i = 0; i < ARRAY_SIZE(vw_config); i++) { if (event.evt_details == vw_config[i].espi_signal) { - bool value = vw_config[i].invert - ? !event.evt_data - : !!event.evt_data; - - atomic_set_bit_to(&signal_data, i, value); - atomic_set_bit(&signal_valid, i); - power_signal_interrupt(vw_config[i].signal, - value); + vw_set(i, event.evt_data, true); } } break; @@ -97,34 +131,12 @@ int power_signal_vw_get(enum pwr_sig_vw vw) void power_signal_vw_init(void) { - static struct espi_callback espi_cb; - - /* Assumes ESPI device is already configured. */ - - /* Configure handler for eSPI events */ - espi_init_callback(&espi_cb, espi_handler, - ESPI_BUS_RESET | - ESPI_BUS_EVENT_VWIRE_RECEIVED); - espi_add_callback(espi_dev, &espi_cb); /* * Check whether the bus is ready, and if so, * initialise the current values of the signals. */ if (espi_get_channel_status(espi_dev, ESPI_CHANNEL_VWIRE)) { - for (int i = 0; i < ARRAY_SIZE(vw_config); i++) { - uint8_t vw_value; - - if (espi_receive_vwire(espi_dev, - vw_config[i].espi_signal, - &vw_value) == 0) { - atomic_set_bit_to(&signal_data, i, - vw_config[i].invert - ? !vw_value - : !!vw_value); - atomic_set_bit(&signal_valid, i); - - } - } + vw_update_all(false); } } 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 375e93c74f..9ef482b712 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 @@ -1,4 +1,4 @@ -/* Copyright 2022 The Chromium OS Authors. All rights reserved. +/* Copyright 2022 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ @@ -20,8 +20,7 @@ static int check_pch_out_of_suspend(void) /* * Wait for SLP_SUS deasserted. */ - ret = power_wait_mask_signals_timeout(IN_PCH_SLP_SUS, - 0, + ret = power_wait_mask_signals_timeout(IN_PCH_SLP_SUS, 0, IN_PCH_SLP_SUS_WAIT_TIME_MS); if (ret == 0) { LOG_DBG("SLP_SUS now %d", power_signal_get(PWR_SLP_SUS)); @@ -38,14 +37,21 @@ int all_sys_pwrgd_handler(void) { int retry = 0; + /* SLP_S3 is off */ + if (power_signal_get(PWR_SLP_S3) == 1) { + ap_off(); + return 1; + } + /* TODO: Add condition for no power sequencer */ - k_msleep(AP_PWRSEQ_DT_VALUE(all_sys_pwrgd_timeout)); + power_wait_signals_timeout(POWER_SIGNAL_MASK(PWR_ALL_SYS_PWRGD), + AP_PWRSEQ_DT_VALUE(all_sys_pwrgd_timeout)); if (power_signal_get(PWR_DSW_PWROK) == 0) { - /* Todo: Remove workaround for the retry - * without this change the system hits G3 as it detects - * ALL_SYS_PWRGD as 0 and then 1 as a glitch - */ + /* Todo: Remove workaround for the retry + * without this change the system hits G3 as it detects + * ALL_SYS_PWRGD as 0 and then 1 as a glitch + */ while (power_signal_get(PWR_ALL_SYS_PWRGD) == 0) { if (++retry > 2) { LOG_ERR("PG_EC_ALL_SYS_PWRGD not ok"); @@ -58,7 +64,7 @@ int all_sys_pwrgd_handler(void) /* PG_EC_ALL_SYS_PWRGD is asserted, enable VCCST_PWRGD_OD. */ - if (power_signal_get(PWR_VCCST_PWRGD) == 0) { + if (!power_signals_on(POWER_SIGNAL_MASK(PWR_VCCST_PWRGD))) { k_msleep(AP_PWRSEQ_DT_VALUE(vccst_pwrgd_delay)); power_signal_set(PWR_VCCST_PWRGD, 1); } diff --git a/zephyr/subsys/ap_pwrseq/x86_non_dsx_chipset_power_state.c b/zephyr/subsys/ap_pwrseq/x86_non_dsx_chipset_power_state.c index 97268e8fe6..e4ce364cb1 100644 --- a/zephyr/subsys/ap_pwrseq/x86_non_dsx_chipset_power_state.c +++ b/zephyr/subsys/ap_pwrseq/x86_non_dsx_chipset_power_state.c @@ -1,4 +1,4 @@ -/* Copyright 2022 The Chromium OS Authors. All rights reserved. +/* Copyright 2022 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ @@ -14,39 +14,41 @@ LOG_MODULE_DECLARE(ap_pwrseq, CONFIG_AP_PWRSEQ_LOG_LEVEL); */ enum power_states_ndsx chipset_pwr_seq_get_state(void) { + power_signal_mask_t sig = power_get_signals(); + /* - * Chip is shut down. + * Chip is shut down, G3 state. */ - if ((power_get_signals() & MASK_ALL_POWER_GOOD) == 0) { - LOG_DBG("Power rails off, G3 state"); + if ((sig & MASK_ALL_POWER_GOOD) == 0) { + LOG_DBG("All power rails off, G3 state"); return SYS_POWER_STATE_G3; } /* - * If not all the power rails are available, - * then force shutdown to G3 to get to known state. + * Not enough power rails up to read VW signals. + * Force a shutdown. */ - if ((power_get_signals() & MASK_ALL_POWER_GOOD) - != MASK_ALL_POWER_GOOD) { + if ((sig & MASK_VW_POWER) != VALUE_VW_POWER) { + LOG_ERR("Not enough power signals on (%#x), forcing shutdown", + sig); ap_power_force_shutdown(AP_POWER_SHUTDOWN_G3); - LOG_INF("Not all power rails up, forcing shutdown"); return SYS_POWER_STATE_G3; } /* - * All the power rails are good, so + * Enough power signals are up, so * wait for virtual wire signals to become available. * Not sure how long to wait? 5 seconds total. */ for (int delay = 0; delay < 500; k_msleep(10), delay++) { -#if defined(CONFIG_PLATFORM_EC_ESPI_VW_SLP_S3) +#if defined(CONFIG_PLATFORM_EC_HOST_INTERFACE_ESPI_VW_SLP_S3) if (power_signal_get(PWR_SLP_S3) < 0) continue; #endif -#if defined(CONFIG_PLATFORM_EC_ESPI_VW_SLP_S4) +#if defined(CONFIG_PLATFORM_EC_HOST_INTERFACE_ESPI_VW_SLP_S4) if (power_signal_get(PWR_SLP_S4) < 0) continue; #endif -#if defined(CONFIG_PLATFORM_EC_ESPI_VW_SLP_S5) +#if defined(CONFIG_PLATFORM_EC_HOST_INTERFACE_ESPI_VW_SLP_S5) if (power_signal_get(PWR_SLP_S5) < 0) continue; #endif @@ -56,32 +58,34 @@ enum power_states_ndsx chipset_pwr_seq_get_state(void) LOG_DBG("All VW signals valid after %d ms", delay * 10); break; } + /* Re-read the power signals */ + sig = power_get_signals(); + /* * S0, all power OK, no suspend or sleep on. */ - if ((power_get_signals() & MASK_S0) == MASK_ALL_POWER_GOOD) { + if ((sig & MASK_S0) == VALUE_S0) { LOG_DBG("CPU in S0 state"); return SYS_POWER_STATE_S0; } /* * S3, all power OK, PWR_SLP_S3 on. */ - if ((power_get_signals() & MASK_S0) == - (MASK_ALL_POWER_GOOD | POWER_SIGNAL_MASK(PWR_SLP_S3))) { + if ((sig & MASK_S3) == VALUE_S3) { LOG_DBG("CPU in S3 state"); return SYS_POWER_STATE_S3; } /* - * S5, all power OK, PWR_SLP_S5 on. + * S5, some power signals on, PWR_SLP_S5 on. */ - if ((power_get_signals() & MASK_S5) == MASK_S5) { + if ((sig & MASK_S5) == VALUE_S5) { LOG_DBG("CPU in S5 state"); return SYS_POWER_STATE_S5; } /* * Unable to determine state, force to G3. */ + LOG_INF("Unable to determine CPU state (%#x), forcing shutdown", sig); ap_power_force_shutdown(AP_POWER_SHUTDOWN_G3); - LOG_INF("Unable to determine CPU state, forcing shutdown"); return SYS_POWER_STATE_G3; } 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 e671e46113..dbceeacc85 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 @@ -1,4 +1,4 @@ -/* Copyright 2022 The Chromium OS Authors. All rights reserved. +/* Copyright 2022 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ @@ -11,20 +11,19 @@ LOG_MODULE_DECLARE(ap_pwrseq, CONFIG_AP_PWRSEQ_LOG_LEVEL); /* Console commands */ static int powerinfo_handler(const struct shell *shell, size_t argc, - char **argv) + char **argv) { enum power_states_ndsx state = pwr_sm_get_state(); shell_fprintf(shell, SHELL_INFO, "power state %d = %s, in 0x%04x\n", - state, pwr_sm_get_state_name(state), - power_get_signals()); + state, pwr_sm_get_state_name(state), power_get_signals()); return 0; } SHELL_CMD_REGISTER(powerinfo, NULL, NULL, powerinfo_handler); static int powerindebug_handler(const struct shell *shell, size_t argc, - char **argv) + char **argv) { int i; char *e; @@ -42,29 +41,29 @@ static int powerindebug_handler(const struct shell *shell, size_t argc, /* Print the mask */ current = power_get_signals(); - shell_fprintf(shell, SHELL_INFO, "power in: 0x%04x\n", current); - shell_fprintf(shell, SHELL_INFO, "debug mask: 0x%04x\n", - power_get_debug()); + shell_fprintf(shell, SHELL_INFO, "power in: 0x%05x\n", current); + shell_fprintf(shell, SHELL_INFO, "debug mask: 0x%05x\n", + power_get_debug()); /* Print the decode */ shell_fprintf(shell, SHELL_INFO, "bit meanings:\n"); for (i = 0; i < POWER_SIGNAL_COUNT; i++) { power_signal_mask_t mask = POWER_SIGNAL_MASK(i); + bool valid = (power_signal_get(i) >= 0); - shell_fprintf(shell, SHELL_INFO, " 0x%04x %d %s\n", - mask, (current & mask) ? 1 : 0, - power_signal_name(i)); + shell_fprintf(shell, SHELL_INFO, " 0x%05x %d%s %s\n", mask, + (current & mask) ? 1 : 0, valid ? " " : "!", + power_signal_name(i)); } return 0; }; -SHELL_CMD_REGISTER(powerindebug, NULL, - "[mask] Get/set power input debug mask", powerindebug_handler); - +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) + char **argv) { ap_power_force_shutdown(AP_POWER_SHUTDOWN_CONSOLE_CMD); return 0; @@ -72,8 +71,7 @@ static int apshutdown_handler(const struct shell *shell, size_t argc, SHELL_CMD_REGISTER(apshutdown, NULL, NULL, apshutdown_handler); -static int apreset_handler(const struct shell *shell, size_t argc, - char **argv) +static int apreset_handler(const struct shell *shell, size_t argc, char **argv) { ap_power_reset(AP_POWER_SHUTDOWN_CONSOLE_CMD); return 0; diff --git a/zephyr/subsys/ap_pwrseq/x86_non_dsx_common_pwrseq_host_command.c b/zephyr/subsys/ap_pwrseq/x86_non_dsx_common_pwrseq_host_command.c index 7bacbcd8cd..ee6e2cf41e 100644 --- a/zephyr/subsys/ap_pwrseq/x86_non_dsx_common_pwrseq_host_command.c +++ b/zephyr/subsys/ap_pwrseq/x86_non_dsx_common_pwrseq_host_command.c @@ -1,4 +1,4 @@ -/* Copyright 2022 The Chromium OS Authors. All rights reserved. +/* Copyright 2022 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ @@ -12,14 +12,14 @@ host_command_reboot_ap_on_g3(struct host_cmd_handler_args *args) const struct ec_params_reboot_ap_on_g3_v1 *cmd = args->params; /* Store request for processing at g3 */ - request_exit_hardoff(true); + request_start_from_g3(); switch (args->version) { case 0: break; case 1: /* Store user specified delay to wait in G3 state */ - set_reboot_ap_at_g3_delay_seconds(cmd->reboot_ap_at_g3_delay); + set_start_from_g3_delay_seconds(cmd->reboot_ap_at_g3_delay); break; default: return EC_RES_INVALID_PARAM; @@ -27,8 +27,66 @@ host_command_reboot_ap_on_g3(struct host_cmd_handler_args *args) return EC_RES_SUCCESS; } -DECLARE_HOST_COMMAND(EC_CMD_REBOOT_AP_ON_G3, - host_command_reboot_ap_on_g3, +DECLARE_HOST_COMMAND(EC_CMD_REBOOT_AP_ON_G3, host_command_reboot_ap_on_g3, EC_VER_MASK(0) | EC_VER_MASK(1)); +#if CONFIG_AP_PWRSEQ_HOST_SLEEP +/* Track last reported sleep event */ +static enum host_sleep_event host_sleep_state; + +static enum ec_status +host_command_host_sleep_event(struct host_cmd_handler_args *args) +{ + const struct ec_params_host_sleep_event_v1 *p = args->params; + struct ec_response_host_sleep_event_v1 *r = args->response; + struct host_sleep_event_context ctx; + enum host_sleep_event state = p->sleep_event; + + host_sleep_state = state; + ctx.sleep_transitions = 0; + switch (state) { + case HOST_SLEEP_EVENT_S0IX_SUSPEND: + case HOST_SLEEP_EVENT_S3_SUSPEND: + case HOST_SLEEP_EVENT_S3_WAKEABLE_SUSPEND: + ctx.sleep_timeout_ms = EC_HOST_SLEEP_TIMEOUT_DEFAULT; + + /* The original version contained only state. */ + if (args->version >= 1) + ctx.sleep_timeout_ms = + p->suspend_params.sleep_timeout_ms; + + break; + + default: + break; + } + + ap_power_chipset_handle_host_sleep_event(host_sleep_state, &ctx); + switch (state) { + case HOST_SLEEP_EVENT_S0IX_RESUME: + case HOST_SLEEP_EVENT_S3_RESUME: + if (args->version >= 1) { + r->resume_response.sleep_transitions = + ctx.sleep_transitions; + + args->response_size = sizeof(*r); + } + + break; + + default: + break; + } + + return EC_RES_SUCCESS; +} +DECLARE_HOST_COMMAND(EC_CMD_HOST_SLEEP_EVENT, host_command_host_sleep_event, + EC_VER_MASK(0) | EC_VER_MASK(1)); + +void power_set_host_sleep_state(enum host_sleep_event state) +{ + host_sleep_state = state; +} +#endif /* CONFIG_AP_PWRSEQ_HOST_SLEEP */ + /* End of host commands */ diff --git a/zephyr/subsys/ap_pwrseq/x86_non_dsx_common_pwrseq_host_sleep.c b/zephyr/subsys/ap_pwrseq/x86_non_dsx_common_pwrseq_host_sleep.c new file mode 100644 index 0000000000..015cecb502 --- /dev/null +++ b/zephyr/subsys/ap_pwrseq/x86_non_dsx_common_pwrseq_host_sleep.c @@ -0,0 +1,179 @@ +/* Copyright 2022 The ChromiumOS Authors + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include <ap_power_host_sleep.h> +#include <x86_non_dsx_common_pwrseq_sm_handler.h> + +static uint16_t sleep_signal_timeout; +static uint16_t host_sleep_timeout_default = CONFIG_SLEEP_TIMEOUT_MS; +static uint32_t sleep_signal_transitions; +static enum sleep_hang_type timeout_hang_type; + +static void sleep_transition_timeout(struct k_work *work); + +static K_WORK_DELAYABLE_DEFINE(sleep_transition_timeout_data, + sleep_transition_timeout); + +/** + * Type of sleep hang detected + */ +enum sleep_hang_type { + SLEEP_HANG_NONE, + SLEEP_HANG_S0IX_SUSPEND, + SLEEP_HANG_S0IX_RESUME +}; + +void power_chipset_handle_sleep_hang(enum sleep_hang_type hang_type) +{ + /* + * Wake up the AP so they don't just chill in a non-suspended state and + * burn power. Overload a vaguely related event bit since event bits are + * at a premium. If the system never entered S0ix, then manually set the + * wake mask to pretend it did, so that the hang detect event wakes the + * system. + */ + if (pwr_sm_get_state() == SYS_POWER_STATE_S0) { + host_event_t sleep_wake_mask; + + ap_power_get_lazy_wake_mask(SYS_POWER_STATE_S0ix, + &sleep_wake_mask); + lpc_set_host_event_mask(LPC_HOST_EVENT_WAKE, sleep_wake_mask); + } + + ccprintf("Warning: Detected sleep hang! Waking host up!"); + host_set_single_event(EC_HOST_EVENT_HANG_DETECT); +} + +static void sleep_transition_timeout(struct k_work *work) +{ + /* Mark the timeout. */ + sleep_signal_transitions |= EC_HOST_RESUME_SLEEP_TIMEOUT; + k_work_cancel_delayable(&sleep_transition_timeout_data); + + if (timeout_hang_type != SLEEP_HANG_NONE) { + power_chipset_handle_sleep_hang(timeout_hang_type); + } +} + +static void sleep_increment_transition(void) +{ + if ((sleep_signal_transitions & EC_HOST_RESUME_SLEEP_TRANSITIONS_MASK) < + EC_HOST_RESUME_SLEEP_TRANSITIONS_MASK) + sleep_signal_transitions += 1; +} + +void sleep_suspend_transition(void) +{ + sleep_increment_transition(); + k_work_cancel_delayable(&sleep_transition_timeout_data); +} + +void sleep_resume_transition(void) +{ + sleep_increment_transition(); + + /* + * Start the timer again to ensure the AP doesn't get itself stuck in + * a state where it's no longer in a sleep state (S0ix/S3), but from + * the Linux perspective is still suspended. Perhaps a bug in the SoC- + * internal periodic housekeeping code might result in a situation + * like this. + */ + if (sleep_signal_timeout) { + timeout_hang_type = SLEEP_HANG_S0IX_RESUME; + k_work_schedule(&sleep_transition_timeout_data, + K_MSEC(sleep_signal_timeout)); + } +} + +void sleep_start_suspend(void) +{ + uint16_t timeout = host_get_sleep_timeout(); + + sleep_signal_transitions = 0; + + /* Use 0xFFFF to disable the timeout */ + if (timeout == EC_HOST_SLEEP_TIMEOUT_INFINITE) { + sleep_signal_timeout = 0; + return; + } + + /* Use zero internally to indicate host doesn't set timeout value; + * we will use default timeout. + */ + if (timeout == EC_HOST_SLEEP_TIMEOUT_DEFAULT) { + timeout = host_sleep_timeout_default; + } + + sleep_signal_timeout = timeout; + timeout_hang_type = SLEEP_HANG_S0IX_SUSPEND; + k_work_schedule(&sleep_transition_timeout_data, K_MSEC(timeout)); +} + +void sleep_complete_resume(void) +{ + /* + * Ensure we don't schedule another sleep_transition_timeout + * if the the HOST_SLEEP_EVENT_S0IX_RESUME message arrives before + * the CHIPSET task transitions to the POWER_S0ixS0 state. + */ + sleep_signal_timeout = 0; + k_work_cancel_delayable(&sleep_transition_timeout_data); + host_set_sleep_transitions(sleep_signal_transitions); +} + +void sleep_reset_tracking(void) +{ + sleep_signal_transitions = 0; + sleep_signal_timeout = 0; + timeout_hang_type = SLEEP_HANG_NONE; +} + +/* + * s0ix event handler. + */ +static void ap_power_sleep_event_handler(struct ap_power_ev_callback *cb, + struct ap_power_ev_data data) +{ + switch (data.event) { + case AP_POWER_S0IX_SUSPEND_START: + sleep_start_suspend(); + break; + case AP_POWER_S0IX_SUSPEND: + sleep_suspend_transition(); + break; + case AP_POWER_S0IX_RESUME: + sleep_resume_transition(); + break; + case AP_POWER_S0IX_RESUME_COMPLETE: + sleep_complete_resume(); + break; + case AP_POWER_S0IX_RESET_TRACKING: + sleep_reset_tracking(); + break; + default: + break; + } +} + +/* + * Registers callback for s0ix events. + */ +static int ap_power_sleep_s0ix_event(const struct device *unused) +{ + static struct ap_power_ev_callback cb; + + /* + * Register for all events. + */ + ap_power_ev_init_callback( + &cb, ap_power_sleep_event_handler, + AP_POWER_S0IX_SUSPEND_START | AP_POWER_S0IX_SUSPEND | + AP_POWER_S0IX_RESUME | AP_POWER_S0IX_RESUME_COMPLETE | + AP_POWER_S0IX_RESET_TRACKING); + ap_power_ev_add_callback(&cb); + return 0; +} +SYS_INIT(ap_power_sleep_s0ix_event, APPLICATION, 1); 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 2f38c36684..48cab7f6e7 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 @@ -1,25 +1,42 @@ -/* Copyright 2022 The Chromium OS Authors. All rights reserved. +/* Copyright 2022 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ +#include <atomic.h> #include <zephyr/init.h> #include <x86_non_dsx_common_pwrseq_sm_handler.h> -static K_KERNEL_STACK_DEFINE(pwrseq_thread_stack, - CONFIG_AP_PWRSEQ_STACK_SIZE); +static K_KERNEL_STACK_DEFINE(pwrseq_thread_stack, CONFIG_AP_PWRSEQ_STACK_SIZE); static struct k_thread pwrseq_thread_data; -static struct pwrseq_context pwrseq_ctx; +static struct pwrseq_context pwrseq_ctx = { + .power_state = SYS_POWER_STATE_UNINIT, +}; +static struct k_sem pwrseq_sem; + +static void s5_inactive_timer_handler(struct k_timer *timer); /* S5 inactive timer*/ -K_TIMER_DEFINE(s5_inactive_timer, NULL, NULL); +K_TIMER_DEFINE(s5_inactive_timer, s5_inactive_timer_handler, NULL); +/* + * Flags, may be set/cleared from other threads. + */ +enum { + S5_INACTIVE_TIMER_RUNNING, + START_FROM_G3, + FLAGS_MAX, +}; +static ATOMIC_DEFINE(flags, FLAGS_MAX); +/* Delay in ms when starting from G3 */ +static uint32_t start_from_g3_delay_ms; LOG_MODULE_REGISTER(ap_pwrseq, CONFIG_AP_PWRSEQ_LOG_LEVEL); /** * @brief power_state names for debug */ -static const char * const pwrsm_dbg[] = { +static const char *const pwrsm_dbg[] = { + [SYS_POWER_STATE_UNINIT] = "Unknown", [SYS_POWER_STATE_G3] = "G3", [SYS_POWER_STATE_S5] = "S5", [SYS_POWER_STATE_S4] = "S4", @@ -48,17 +65,17 @@ static const char * const pwrsm_dbg[] = { */ static inline bool signals_valid(power_signal_mask_t signals) { -#if defined(CONFIG_PLATFORM_EC_ESPI_VW_SLP_S3) +#if defined(CONFIG_PLATFORM_EC_HOST_INTERFACE_ESPI_VW_SLP_S3) if ((signals & POWER_SIGNAL_MASK(PWR_SLP_S3)) && power_signal_get(PWR_SLP_S3) < 0) return false; #endif -#if defined(CONFIG_PLATFORM_EC_ESPI_VW_SLP_S4) +#if defined(CONFIG_PLATFORM_EC_HOST_INTERFACE_ESPI_VW_SLP_S4) if ((signals & POWER_SIGNAL_MASK(PWR_SLP_S4)) && power_signal_get(PWR_SLP_S4) < 0) return false; #endif -#if defined(CONFIG_PLATFORM_EC_ESPI_VW_SLP_S5) +#if defined(CONFIG_PLATFORM_EC_HOST_INTERFACE_ESPI_VW_SLP_S5) if ((signals & POWER_SIGNAL_MASK(PWR_SLP_S5)) && power_signal_get(PWR_SLP_S5) < 0) return false; @@ -81,7 +98,7 @@ enum power_states_ndsx pwr_sm_get_state(void) return pwrseq_ctx.power_state; } -const char * const pwr_sm_get_state_name(enum power_states_ndsx state) +const char *const pwr_sm_get_state_name(enum power_states_ndsx state) { return pwrsm_dbg[state]; } @@ -95,14 +112,34 @@ void pwr_sm_set_state(enum power_states_ndsx new_state) pwrseq_ctx.power_state = new_state; } -void request_exit_hardoff(bool should_exit) +void ap_pwrseq_wake(void) { - pwrseq_ctx.want_g3_exit = should_exit; + k_sem_give(&pwrseq_sem); } -static bool chipset_is_exit_hardoff(void) +/* + * Set a flag to enable starting the AP once it is in G3. + * This is called from ap_power_exit_hardoff() which checks + * to ensure that the AP is in S5 or G3 state before calling + * this function. + * It can also be called via a hostcmd, which allows the flag + * to be set in any AP state. + */ +void request_start_from_g3(void) { - return pwrseq_ctx.want_g3_exit; + LOG_INF("Request start from G3"); + atomic_set_bit(flags, START_FROM_G3); + /* + * If in S5, restart the timer to give the CPU more time + * to respond to a power button press (which is presumably + * why we are being called). This avoids having the S5 + * inactivity timer expiring before the AP can process + * the power button press and start up. + */ + if (pwr_sm_get_state() == SYS_POWER_STATE_S5) { + atomic_clear_bit(flags, S5_INACTIVE_TIMER_RUNNING); + } + ap_pwrseq_wake(); } void ap_power_force_shutdown(enum ap_power_shutdown_reason reason) @@ -110,6 +147,11 @@ void ap_power_force_shutdown(enum ap_power_shutdown_reason reason) board_ap_power_force_shutdown(); } +static void s5_inactive_timer_handler(struct k_timer *timer) +{ + ap_pwrseq_wake(); +} + static void shutdown_and_notify(enum ap_power_shutdown_reason reason) { ap_power_force_shutdown(reason); @@ -117,9 +159,9 @@ static void shutdown_and_notify(enum ap_power_shutdown_reason reason) ap_power_ev_send_callbacks(AP_POWER_SHUTDOWN_COMPLETE); } -void set_reboot_ap_at_g3_delay_seconds(uint32_t d_time) +void set_start_from_g3_delay_seconds(uint32_t d_time) { - pwrseq_ctx.reboot_ap_at_g3_delay_ms = d_time * MSEC; + start_from_g3_delay_ms = d_time * MSEC; } void apshutdown(void) @@ -190,15 +232,16 @@ static int common_pwr_sm_run(int state) { switch (state) { case SYS_POWER_STATE_G3: - if (chipset_is_exit_hardoff()) { - request_exit_hardoff(false); - /* - * G3->S0 transition should happen only after the - * user specified delay. Hence, wait until the - * user specified delay times out. - */ - k_msleep(pwrseq_ctx.reboot_ap_at_g3_delay_ms); - pwrseq_ctx.reboot_ap_at_g3_delay_ms = 0; + /* + * If the START_FROM_G3 flag is set, begin starting + * the AP. There may be a delay set, so only start + * after that delay. + */ + if (atomic_test_and_clear_bit(flags, START_FROM_G3)) { + LOG_INF("Starting from G3, delay %d ms", + start_from_g3_delay_ms); + k_msleep(start_from_g3_delay_ms); + start_from_g3_delay_ms = 0; return SYS_POWER_STATE_G3S5; } @@ -207,7 +250,7 @@ static int common_pwr_sm_run(int state) case SYS_POWER_STATE_G3S5: if ((power_get_signals() & PWRSEQ_G3S5_UP_SIGNAL) == - PWRSEQ_G3S5_UP_VALUE) + PWRSEQ_G3S5_UP_VALUE) return SYS_POWER_STATE_S5; else return SYS_POWER_STATE_S5G3; @@ -215,29 +258,56 @@ static int common_pwr_sm_run(int state) case SYS_POWER_STATE_S5: /* In S5 make sure no more signal lost */ /* If A-rails are stable then move to higher state */ - if (board_ap_power_check_power_rails_enabled() - && rsmrst_power_is_good()) { + if (board_ap_power_check_power_rails_enabled() && + rsmrst_power_is_good()) { /* rsmrst is intact */ rsmrst_pass_thru_handler(); if (signals_valid_and_off(IN_PCH_SLP_S5)) { k_timer_stop(&s5_inactive_timer); + /* Clear the timer running flag */ + atomic_clear_bit(flags, + S5_INACTIVE_TIMER_RUNNING); + /* Clear any request to exit hard-off */ + atomic_clear_bit(flags, START_FROM_G3); + LOG_INF("Clearing request to exit G3"); return SYS_POWER_STATE_S5S4; } } - /* S5 inactivity timeout, go to S5G3 */ + /* + * S5 state has an inactivity timer, so moving + * to S5G3 (where the power rails are turned off) is + * delayed for some time, usually ~10 seconds or so. + * The purpose of this delay is: + * - to handle AP initiated cold boot, where the AP + * will go to S5 for a short time and then restart. + * - give time for the power button to be pressed, + * which may set the START_FROM_G3 flag. + */ if (AP_PWRSEQ_DT_VALUE(s5_inactivity_timeout) == 0) return SYS_POWER_STATE_S5G3; else if (AP_PWRSEQ_DT_VALUE(s5_inactivity_timeout) > 0) { - if (k_timer_status_get(&s5_inactive_timer) > 0) + /* + * Test and set timer running flag. + * If it was 0, then the timer wasn't running + * and it is started (and the flag is set), + * otherwise it is already set, so no change. + */ + if (!atomic_test_and_set_bit( + flags, S5_INACTIVE_TIMER_RUNNING)) { + /* + * Timer is not started, or needs + * restarting. + */ + k_timer_start(&s5_inactive_timer, + K_SECONDS(AP_PWRSEQ_DT_VALUE( + s5_inactivity_timeout)), + K_NO_WAIT); + } else if (k_timer_status_get(&s5_inactive_timer) > 0) { /* Timer is expired */ + atomic_clear_bit(flags, + S5_INACTIVE_TIMER_RUNNING); 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(AP_PWRSEQ_DT_VALUE( - s5_inactivity_timeout)), - K_NO_WAIT); + } } break; @@ -318,7 +388,7 @@ static int common_pwr_sm_run(int state) case SYS_POWER_STATE_S0ix: /* System in S0 only if SLP_S0 and SLP_S3 are de-asserted */ if (power_signals_off(IN_PCH_SLP_S0) && - signals_valid_and_off(IN_PCH_SLP_S3)) { + signals_valid_and_off(IN_PCH_SLP_S3)) { /* TODO: Make sure ap reset handling is done * before leaving S0ix. */ @@ -334,6 +404,7 @@ static int common_pwr_sm_run(int state) * HC already set sleep suspend state. */ ap_power_sleep_notify_transition(AP_POWER_SLEEP_SUSPEND); + ap_power_ev_send_callbacks(AP_POWER_S0IX_SUSPEND); /* * Enable idle task deep sleep. Allow the low power idle task @@ -354,9 +425,11 @@ static int common_pwr_sm_run(int state) */ disable_sleep(SLEEP_MASK_AP_RUN); + ap_power_ev_send_callbacks(AP_POWER_S0IX_RESUME #if CONFIG_PLATFORM_EC_CHIPSET_RESUME_INIT_HOOK - ap_power_ev_send_callbacks(AP_POWER_RESUME_INIT); + | AP_POWER_RESUME_INIT #endif + ); return SYS_POWER_STATE_S0; @@ -370,19 +443,19 @@ static int common_pwr_sm_run(int state) return SYS_POWER_STATE_S0S3; #if CONFIG_AP_PWRSEQ_S0IX - /* - * SLP_S0 may assert in system idle scenario without a kernel - * freeze call. This may cause interrupt storm since there is - * no freeze/unfreeze of threads/process in the idle scenario. - * Ignore the SLP_S0 assertions in idle scenario by checking - * the host sleep state. - */ + /* + * SLP_S0 may assert in system idle scenario without a + * kernel freeze call. This may cause interrupt storm + * since there is no freeze/unfreeze of threads/process + * in the idle scenario. Ignore the SLP_S0 assertions in + * idle scenario by checking the host sleep state. + */ } else if (ap_power_sleep_get_notify() == - AP_POWER_SLEEP_SUSPEND && - power_signals_on(IN_PCH_SLP_S0)) { + AP_POWER_SLEEP_SUSPEND && + power_signals_on(IN_PCH_SLP_S0)) { return SYS_POWER_STATE_S0S0ix; } else if (ap_power_sleep_get_notify() == - AP_POWER_SLEEP_RESUME) { + AP_POWER_SLEEP_RESUME) { ap_power_sleep_notify_transition(AP_POWER_SLEEP_RESUME); #endif /* CONFIG_AP_PWRSEQ_S0IX */ } @@ -464,12 +537,16 @@ static void pwr_seq_set_initial_state(void) 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; power_signal_mask_t this_in_signals; power_signal_mask_t last_in_signals = 0; enum power_states_ndsx last_state = -1; + /* + * Let clients know that the AP power state is now + * initialized and ready. + */ + ap_power_ev_send_callbacks(AP_POWER_INITIALIZED); while (1) { curr_state = pwr_sm_get_state(); @@ -482,9 +559,8 @@ static void pwrseq_loop_thread(void *p1, void *p2, void *p3) this_in_signals = power_get_signals(); if (this_in_signals != last_in_signals || - curr_state != last_state) { - LOG_INF("power state %d = %s, in 0x%04x", - curr_state, + curr_state != last_state) { + LOG_INF("power state %d = %s, in 0x%04x", curr_state, pwr_sm_get_state_name(curr_state), this_in_signals); last_in_signals = this_in_signals; @@ -506,22 +582,24 @@ static void pwrseq_loop_thread(void *p1, void *p2, void *p3) if (curr_state != new_state) { pwr_sm_set_state(new_state); ap_power_set_active_wake_mask(); + } else { + /* + * No state transition, we can go to sleep and wait + * for any event to wake us up. + */ + k_sem_take(&pwrseq_sem, K_FOREVER); } - - k_msleep(t_wait_ms); } } static inline void create_pwrseq_thread(void) { - k_thread_create(&pwrseq_thread_data, - pwrseq_thread_stack, + k_thread_create(&pwrseq_thread_data, pwrseq_thread_stack, K_KERNEL_STACK_SIZEOF(pwrseq_thread_stack), - (k_thread_entry_t)pwrseq_loop_thread, - NULL, NULL, NULL, + (k_thread_entry_t)pwrseq_loop_thread, NULL, NULL, NULL, CONFIG_AP_PWRSEQ_THREAD_PRIORITY, 0, - IS_ENABLED(CONFIG_AP_PWRSEQ_AUTOSTART) ? K_NO_WAIT - : K_FOREVER); + IS_ENABLED(CONFIG_AP_PWRSEQ_AUTOSTART) ? K_NO_WAIT : + K_FOREVER); k_thread_name_set(&pwrseq_thread_data, "pwrseq_task"); } @@ -535,7 +613,7 @@ void ap_pwrseq_task_start(void) static void init_pwr_seq_state(void) { - request_exit_hardoff(false); + atomic_clear_bit(flags, START_FROM_G3); /* * The state of the CPU needs to be determined now * so that init routines can check the state of @@ -549,6 +627,7 @@ static int pwrseq_init(const struct device *dev) { LOG_INF("Pwrseq Init"); + k_sem_init(&pwrseq_sem, 0, 1); /* Initialize signal handlers */ power_signal_init(); LOG_DBG("Init pwr seq state"); @@ -559,7 +638,7 @@ static int pwrseq_init(const struct device *dev) } /* - * The initialisation must occur after system I/O initialisation that + * The initialization must occur after system I/O initialization that * the signals depend upon, such as GPIO, ADC etc. */ SYS_INIT(pwrseq_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY); diff --git a/zephyr/subsys/ap_pwrseq/x86_non_dsx_mtl_pwrseq_sm.c b/zephyr/subsys/ap_pwrseq/x86_non_dsx_mtl_pwrseq_sm.c index 5183824117..80fa06e454 100644 --- a/zephyr/subsys/ap_pwrseq/x86_non_dsx_mtl_pwrseq_sm.c +++ b/zephyr/subsys/ap_pwrseq/x86_non_dsx_mtl_pwrseq_sm.c @@ -1,4 +1,4 @@ -/* Copyright 2022 The Chromium OS Authors. All rights reserved. +/* Copyright 2022 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ @@ -33,8 +33,8 @@ static void generate_pwrok_handler(void) power_signal_set(PWR_EC_PCH_SYS_PWROK, all_sys_pwrgd_in); /* PCH_PWROK is set to combined result of ALL_SYS_PWRGD and SLP_S3 */ - power_signal_set(PWR_PCH_PWROK, all_sys_pwrgd_in && - !power_signal_get(PWR_SLP_S3)); + power_signal_set(PWR_PCH_PWROK, + all_sys_pwrgd_in && !power_signal_get(PWR_SLP_S3)); } /* Chipset specific power state machine handler */ |