summaryrefslogtreecommitdiff
path: root/power
diff options
context:
space:
mode:
authorAndrew McRae <amcrae@google.com>2022-10-27 16:51:29 +1100
committerChromeos LUCI <chromeos-scoped@luci-project-accounts.iam.gserviceaccount.com>2022-11-10 00:16:34 +0000
commit7c7c12e53d64c6c9f88020e7257247d9c9c828df (patch)
tree1b6cd2e4c3dd4359e20b2d9e14c7d4674f43b239 /power
parenteecd4959ca079103f76fca139626c071679a210b (diff)
downloadchrome-ec-7c7c12e53d64c6c9f88020e7257247d9c9c828df.tar.gz
Reland "hibernate: Add hibernate support to ap power code"
This is a reland of commit 16c8fce71986d50d9e10ea089d9d9352818129ad Reworked Kconfig handling. Original change's description: > hibernate: Add hibernate support to ap power code > > Add hibernate support to the AP power sequence code. > > The smart discharge system isn't supported yet. > > The system will hibernate after a delay when the AP is in G3 > and there is no external power connected. > > v2: add tests BUG=b:246643307 TEST=build and run on nivviks BRANCH=none Change-Id: I7f4e5633d05ca545e63ad9784a410284ef83e2b7 Signed-off-by: Andrew McRae <amcrae@google.com> Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/4019012 Reviewed-by: Keith Short <keithshort@chromium.org> Code-Coverage: Zoss <zoss-cl-coverage@prod.google.com>
Diffstat (limited to 'power')
-rw-r--r--power/hibernate.c181
1 files changed, 181 insertions, 0 deletions
diff --git a/power/hibernate.c b/power/hibernate.c
new file mode 100644
index 0000000000..322adca3ea
--- /dev/null
+++ b/power/hibernate.c
@@ -0,0 +1,181 @@
+/* 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 <zephyr/init.h>
+#include <zephyr/logging/log.h>
+
+#include <ap_power/ap_power.h>
+#include <ap_power/ap_power_interface.h>
+
+#include "console.h"
+#include "extpower.h"
+#include "hooks.h"
+#include "host_command.h"
+#include "system.h"
+#include "util.h"
+
+LOG_MODULE_DECLARE(ap_pwrseq, CONFIG_AP_PWRSEQ_LOG_LEVEL);
+
+/*
+ * Hibernate processing.
+ *
+ * When enabled, the system will be put into an extreme low
+ * power state after the AP is in G3 for a configurable period of time,
+ * and there is no external power connected (i.e on battery).
+ *
+ * The delay has a configurable default, and may be set dynamically
+ * via a host command, or an EC console command. A typical delay
+ * may be 1 hour (3600 seconds).
+ *
+ * AP events such as AP_POWER_HARD_OFF are listened for, and
+ * a timer is used to detect when the AP has been off for the
+ * selected delay time. If the AP is started again, the timer is canceled.
+ * Once the timer expires, the system_hibernate() function is called,
+ * and this will suspend the EC until a wake signal is received.
+ */
+static uint32_t hibernate_delay = CONFIG_HIBERNATE_DELAY_SEC;
+
+/*
+ * Return true if conditions are right for hibernation.
+ */
+static inline bool ready_to_hibernate(void)
+{
+ return ap_power_in_or_transitioning_to_state(AP_POWER_STATE_HARD_OFF) &&
+ !extpower_is_present();
+}
+
+/*
+ * The AP has been off for the delay period, so hibernate the system,
+ * if ready. Called from system work queue.
+ */
+static void hibernate_handler(struct k_work *unused)
+{
+ if (ready_to_hibernate()) {
+ LOG_INF("System hibernating due to %d seconds AP off",
+ hibernate_delay);
+ system_hibernate(0, 0);
+ }
+}
+
+K_WORK_DEFINE(hibernate_work, hibernate_handler);
+
+/*
+ * Hibernate timer handler.
+ * Called when timer has expired.
+ * Schedule hibernate_handler to run via system work queue.
+ */
+static void timer_handler(struct k_timer *timer)
+{
+ k_work_submit(&hibernate_work);
+}
+
+K_TIMER_DEFINE(hibernate_timer, timer_handler, NULL);
+
+/*
+ * A change has been detected in either the AP state or the
+ * external power supply.
+ */
+static void change_detected(void)
+{
+ if (ready_to_hibernate()) {
+ /*
+ * AP is off, and there is no external power.
+ * Start the timer if it is not already running.
+ */
+ if (k_timer_remaining_get(&hibernate_timer) == 0) {
+ k_timer_start(&hibernate_timer,
+ K_SECONDS(hibernate_delay), K_NO_WAIT);
+ }
+
+ } else {
+ /*
+ * AP is either on, or external power is on.
+ * Either way, no hibernation is done.
+ * Make sure the timer is not running.
+ */
+ k_timer_stop(&hibernate_timer);
+ }
+}
+
+static void ap_change(struct ap_power_ev_callback *callback,
+ struct ap_power_ev_data data)
+{
+ change_detected();
+}
+
+/*
+ * Hook to listen for external power supply changes.
+ */
+DECLARE_HOOK(HOOK_AC_CHANGE, change_detected, HOOK_PRIO_DEFAULT);
+
+/*
+ * EC Console command to get/set the hibernation delay
+ */
+static int command_hibernation_delay(int argc, const char **argv)
+{
+ char *e;
+ uint32_t remaining;
+
+ if (argc >= 2) {
+ uint32_t s = strtoi(argv[1], &e, 0);
+
+ if (*e)
+ return EC_ERROR_PARAM1;
+
+ hibernate_delay = s;
+ }
+
+ /* Print the current setting */
+ ccprintf("Hibernation delay: %d s\n", hibernate_delay);
+ remaining = k_timer_remaining_get(&hibernate_timer);
+ if (remaining == 0) {
+ ccprintf("Timer not running\n");
+ } else {
+ ccprintf("Time remaining: %d s\n", remaining / 1000);
+ }
+ return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(hibdelay, command_hibernation_delay, "[sec]",
+ "Set the delay before going into hibernation");
+/*
+ * Host command to set the hibernation delay
+ */
+static enum ec_status
+host_command_hibernation_delay(struct host_cmd_handler_args *args)
+{
+ const struct ec_params_hibernation_delay *p = args->params;
+ struct ec_response_hibernation_delay *r = args->response;
+
+ /* Only change the hibernation delay if seconds is non-zero. */
+ if (p->seconds)
+ hibernate_delay = p->seconds;
+
+ r->hibernate_delay = hibernate_delay;
+ /*
+ * It makes no sense to try and set these values since
+ * they are only valid when the AP is in G3 (so this
+ * host command will never be called at that point).
+ */
+ r->time_g3 = 0;
+ r->time_remaining = 0;
+
+ args->response_size = sizeof(struct ec_response_hibernation_delay);
+ return EC_RES_SUCCESS;
+}
+DECLARE_HOST_COMMAND(EC_CMD_HIBERNATION_DELAY, host_command_hibernation_delay,
+ EC_VER_MASK(0));
+
+static int hibernate_init(const struct device *unused)
+{
+ static struct ap_power_ev_callback cb;
+
+ ap_power_ev_init_callback(&cb, ap_change,
+ AP_POWER_INITIALIZED | AP_POWER_HARD_OFF |
+ AP_POWER_STARTUP);
+ ap_power_ev_add_callback(&cb);
+ return 0;
+}
+
+SYS_INIT(hibernate_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY);