summaryrefslogtreecommitdiff
path: root/zephyr/mock/power.c
diff options
context:
space:
mode:
Diffstat (limited to 'zephyr/mock/power.c')
-rw-r--r--zephyr/mock/power.c264
1 files changed, 264 insertions, 0 deletions
diff --git a/zephyr/mock/power.c b/zephyr/mock/power.c
new file mode 100644
index 0000000000..fc82cc7250
--- /dev/null
+++ b/zephyr/mock/power.c
@@ -0,0 +1,264 @@
+/* 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/logging/log.h>
+#include <zephyr/ztest.h>
+
+#include "hooks.h"
+#include "lid_switch.h"
+#include "power.h"
+#include "task.h"
+#include "util.h"
+
+#include "mock/power.h"
+
+LOG_MODULE_REGISTER(mock_power);
+
+/* Mocks for ec/power/common.c and board specific implementations */
+DEFINE_FAKE_VALUE_FUNC(enum power_state, power_handle_state, enum power_state);
+DEFINE_FAKE_VOID_FUNC(chipset_force_shutdown, enum chipset_shutdown_reason);
+DEFINE_FAKE_VOID_FUNC(chipset_power_on);
+DEFINE_FAKE_VALUE_FUNC(int, command_power, int, const char **);
+
+#define MOCK_POWER_LIST(FAKE) \
+ { \
+ FAKE(power_handle_state); \
+ FAKE(chipset_force_shutdown); \
+ FAKE(chipset_power_on); \
+ FAKE(command_power); \
+ }
+
+/**
+ * @brief Reset all the fakes before each test.
+ */
+static void mock_power_rule_before(const struct ztest_unit_test *test,
+ void *data)
+{
+ ARG_UNUSED(test);
+ ARG_UNUSED(data);
+
+ MOCK_POWER_LIST(RESET_FAKE);
+
+ FFF_RESET_HISTORY();
+
+ power_handle_state_fake.custom_fake = power_handle_state_custom_fake;
+ chipset_force_shutdown_fake.custom_fake =
+ chipset_force_shutdown_custom_fake;
+ chipset_power_on_fake.custom_fake = chipset_power_on_custom_fake;
+ command_power_fake.custom_fake = command_power_custom_fake;
+}
+
+ZTEST_RULE(mock_power_rule, mock_power_rule_before, NULL);
+
+enum power_request_t {
+ POWER_REQ_NONE,
+ POWER_REQ_OFF,
+ POWER_REQ_ON,
+ POWER_REQ_COUNT,
+};
+
+static const char *power_req_name[POWER_REQ_COUNT] = {
+ "none",
+ "OFF",
+ "ON",
+};
+
+static enum power_request_t current_power_request = POWER_REQ_NONE;
+static enum power_request_t pending_power_request = POWER_REQ_NONE;
+
+void handle_power_request(enum power_request_t req)
+{
+ if (current_power_request == POWER_REQ_NONE) {
+ current_power_request = req;
+ } else if (current_power_request != req) {
+ LOG_INF("MOCK: Handling %s, pend %s request",
+ power_req_name[current_power_request],
+ power_req_name[req]);
+ pending_power_request = req;
+ }
+}
+
+void power_request_complete(void)
+{
+ current_power_request = pending_power_request;
+ pending_power_request = POWER_REQ_NONE;
+}
+
+void chipset_force_shutdown_custom_fake(enum chipset_shutdown_reason reason)
+{
+ LOG_INF("MOCK %s(%d)", __func__, reason);
+ handle_power_request(POWER_REQ_OFF);
+ task_wake(TASK_ID_CHIPSET);
+}
+
+void chipset_power_on_custom_fake(void)
+{
+ if (chipset_in_state(CHIPSET_STATE_ANY_OFF)) {
+ handle_power_request(POWER_REQ_ON);
+ task_wake(TASK_ID_CHIPSET);
+ }
+}
+
+/* Power states that we can report */
+enum power_state_t {
+ PSTATE_UNKNOWN,
+ PSTATE_OFF,
+ PSTATE_ON,
+ PSTATE_COUNT,
+};
+
+static const char *const state_name[] = {
+ "unknown",
+ "OFF",
+ "ON",
+};
+
+int command_power_custom_fake(int argc, const char **argv)
+{
+ int v, req;
+
+ if (argc < 2) {
+ enum power_state_t state;
+
+ state = PSTATE_UNKNOWN;
+ if (chipset_in_state(CHIPSET_STATE_ANY_OFF))
+ state = PSTATE_OFF;
+ if (chipset_in_state(CHIPSET_STATE_ON))
+ state = PSTATE_ON;
+ ccprintf("%s\n", state_name[state]);
+
+ return EC_SUCCESS;
+ }
+
+ if (!parse_bool(argv[1], &v)) {
+ return EC_ERROR_PARAM1;
+ }
+ req = v ? POWER_REQ_ON : POWER_REQ_OFF;
+ handle_power_request(req);
+ LOG_INF("MOCK: Requesting power %s\n", power_req_name[req]);
+ task_wake(TASK_ID_CHIPSET);
+
+ return EC_SUCCESS;
+}
+
+static void mock_lid_event(void)
+{
+ /* Power task only cares about lid-open events */
+ if (!lid_is_open()) {
+ return;
+ }
+
+ LOG_INF("MOCK: lid opened %s\n", power_req_name[POWER_REQ_ON]);
+ handle_power_request(POWER_REQ_ON);
+ task_wake(TASK_ID_CHIPSET);
+}
+DECLARE_HOOK(HOOK_LID_CHANGE, mock_lid_event, HOOK_PRIO_DEFAULT);
+
+enum power_state power_handle_state_custom_fake(enum power_state state)
+{
+ enum power_state new_state = state;
+ enum power_request_t request = current_power_request;
+
+ switch (state) {
+ /* Steady states */
+ case POWER_G3:
+ if (current_power_request == POWER_REQ_ON) {
+ new_state = POWER_G3S5;
+ } else if (current_power_request == POWER_REQ_OFF) {
+ new_state = state;
+ power_request_complete();
+ }
+ break;
+ case POWER_S5: /* System is soft-off */
+ if (current_power_request == POWER_REQ_ON) {
+ new_state = POWER_S5S3;
+ } else if (current_power_request == POWER_REQ_OFF) {
+ /* S5 timeout should transition to G3 */
+ }
+ break;
+ case POWER_S3: /* Suspend; RAM on, processor is asleep */
+ if (current_power_request == POWER_REQ_ON) {
+ new_state = POWER_S3S0;
+ } else if (current_power_request == POWER_REQ_OFF) {
+ new_state = POWER_S3S5;
+ }
+ break;
+ case POWER_S0: /* System is on */
+ if (current_power_request == POWER_REQ_ON) {
+ new_state = state;
+ power_request_complete();
+
+ sleep_notify_transition(SLEEP_NOTIFY_RESUME,
+ HOOK_CHIPSET_RESUME);
+ } else if (current_power_request == POWER_REQ_OFF) {
+ new_state = POWER_S0S3;
+ }
+ break;
+ case POWER_S4: /* System is suspended to disk */
+#ifdef CONFIG_POWER_S0IX
+ case POWER_S0ix:
+#endif
+ new_state = state;
+ break;
+ /* Transitions */
+ case POWER_S3S0: /* S3 -> S0 */
+#ifdef CONFIG_CHIPSET_RESUME_INIT_HOOK
+ hook_notify(HOOK_CHIPSET_RESUME_INIT);
+#endif
+ hook_notify(HOOK_CHIPSET_RESUME);
+ sleep_resume_transition();
+ power_request_complete();
+ disable_sleep(SLEEP_MASK_AP_RUN);
+ new_state = POWER_S0;
+ break;
+ case POWER_S0S3: /* S0 -> S3 */
+ sleep_notify_transition(SLEEP_NOTIFY_SUSPEND,
+ HOOK_CHIPSET_SUSPEND);
+ hook_notify(HOOK_CHIPSET_SUSPEND);
+#ifdef CONFIG_CHIPSET_RESUME_INIT_HOOK
+ hook_notify(HOOK_CHIPSET_SUSPEND_COMPLETE);
+#endif
+ sleep_suspend_transition();
+ enable_sleep(SLEEP_MASK_AP_RUN);
+ new_state = POWER_S3;
+ break;
+ case POWER_S5S3: /* S5 -> S3 (skips S4 on non-Intel systems) */
+ hook_notify(HOOK_CHIPSET_PRE_INIT);
+ hook_notify(HOOK_CHIPSET_STARTUP);
+ sleep_reset_tracking();
+ new_state = POWER_S3;
+ break;
+ case POWER_S3S5: /* S3 -> S5 (skips S4 on non-Intel systems) */
+ hook_notify(HOOK_CHIPSET_SHUTDOWN);
+ hook_notify(HOOK_CHIPSET_SHUTDOWN_COMPLETE);
+ new_state = POWER_S5;
+ break;
+ case POWER_G3S5: /* G3 -> S5 (at system init time) */
+ new_state = POWER_S5;
+ break;
+ case POWER_S5G3: /* S5 -> G3 */
+ new_state = POWER_G3;
+ break;
+#ifdef CONFIG_POWER_S0IX
+ case POWER_S0ixS0: /* S0ix -> S0 */
+ new_state = POWER_S0;
+ break;
+ case POWER_S0S0ix: /* S0 -> S0ix */
+ new_state = POWER_S0ix;
+ break;
+#endif
+ case POWER_S5S4: /* S5 -> S4 */
+ case POWER_S4S3: /* S4 -> S3 */
+ case POWER_S3S4: /* S3 -> S4 */
+ case POWER_S4S5: /* S4 -> S5 */
+ default:
+ break;
+ }
+
+ LOG_INF("MOCK: power request=%u, state=%u -> new_state=%u\n", request,
+ state, new_state);
+
+ return new_state;
+}