summaryrefslogtreecommitdiff
path: root/zephyr/subsys/ap_pwrseq/ap_pwrseq_sm.c
diff options
context:
space:
mode:
Diffstat (limited to 'zephyr/subsys/ap_pwrseq/ap_pwrseq_sm.c')
-rw-r--r--zephyr/subsys/ap_pwrseq/ap_pwrseq_sm.c262
1 files changed, 262 insertions, 0 deletions
diff --git a/zephyr/subsys/ap_pwrseq/ap_pwrseq_sm.c b/zephyr/subsys/ap_pwrseq/ap_pwrseq_sm.c
new file mode 100644
index 0000000000..538aae8bc7
--- /dev/null
+++ b/zephyr/subsys/ap_pwrseq/ap_pwrseq_sm.c
@@ -0,0 +1,262 @@
+/* Copyright 2023 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_pwrseq_sm.h"
+
+#include <zephyr/logging/log.h>
+
+LOG_MODULE_DECLARE(ap_pwrseq, CONFIG_AP_PWRSEQ_LOG_LEVEL);
+
+struct ap_pwrseq_sm_data {
+ /* Zephyr SMF context */
+ struct smf_ctx smf;
+ /* Pointer to array of states structures */
+ const struct ap_pwrseq_smf **states;
+ /* Bitfiled of events */
+ uint32_t events;
+ /* Id of current thread executing state machine */
+ k_tid_t tid;
+ /* State entering during state transition */
+ enum ap_pwrseq_state entry;
+ /* State exiting during state transition */
+ enum ap_pwrseq_state exit;
+ /* Flag to inform if current `run` action has been handled */
+ bool run_handled;
+ /* Flag to inform if current `entry` action has been handled */
+ bool entry_handled;
+ /* Flag to inform if current `exit` action has been handled */
+ bool exit_handled;
+ /* Flag to inform that state transition is in progress */
+ bool in_transition;
+};
+
+/**
+ * Declare weak `struct smf_state` definitions of all ACPI states for
+ * architecture and chipset level. These are used as placeholder to keep AP
+ * power sequence state machine hierarchy in case corresponding state action
+ * handlers are not provided by implementation.
+ **/
+#define AP_POWER_ARCH_STATE_WEAK_DEFINE(name) \
+ const struct smf_state __weak arch_##name##_actions = \
+ SMF_CREATE_STATE(NULL, NULL, NULL, NULL);
+
+#define AP_POWER_CHIPSET_STATE_WEAK_DEFINE(name) \
+ const struct smf_state __weak chipset_##name##_actions = \
+ SMF_CREATE_STATE(NULL, NULL, NULL, &arch_##name##_actions);
+
+/**
+ * Declare weak `struct ap_pwrseq_smf` definitions of all ACPI states for
+ * application level. These are used as placeholder to keep AP
+ * power sequence state machine hierarchy in case corresponding state action
+ * handlers are not provided by implementation.
+ **/
+#define AP_POWER_APP_STATE_WEAK_DEFINE(name) \
+ const struct ap_pwrseq_smf __weak app_state_##name = { \
+ .actions = SMF_CREATE_STATE(NULL, NULL, NULL, \
+ &chipset_##name##_actions), \
+ .state = name \
+ };
+
+#define AP_POWER_STATE_WEAK_DEFINE(name) \
+ AP_POWER_ARCH_STATE_WEAK_DEFINE(name) \
+ AP_POWER_CHIPSET_STATE_WEAK_DEFINE(name) \
+ AP_POWER_APP_STATE_WEAK_DEFINE(name)
+
+AP_POWER_STATE_WEAK_DEFINE(AP_POWER_STATE_G3)
+AP_POWER_STATE_WEAK_DEFINE(AP_POWER_STATE_S5)
+AP_POWER_STATE_WEAK_DEFINE(AP_POWER_STATE_S4)
+AP_POWER_STATE_WEAK_DEFINE(AP_POWER_STATE_S3)
+AP_POWER_STATE_WEAK_DEFINE(AP_POWER_STATE_S2)
+AP_POWER_STATE_WEAK_DEFINE(AP_POWER_STATE_S1)
+AP_POWER_STATE_WEAK_DEFINE(AP_POWER_STATE_S0)
+
+#define AP_PWRSEQ_STATE_DEFINE(name) [name] = &app_state_##name
+
+/* Sub States defines */
+#define AP_PWRSEQ_APP_SUB_STATE_DEFINE(state) [state] = &app_state_##state,
+
+#define AP_PWRSEQ_APP_SUB_STATE_DEFINE_(state) \
+ AP_PWRSEQ_APP_SUB_STATE_DEFINE(state)
+
+#define AP_PWRSEQ_EACH_APP_SUB_STATE_NODE_DEFINE__(node_id, prop, idx) \
+ AP_PWRSEQ_APP_SUB_STATE_DEFINE_( \
+ DT_CAT6(node_id, _P_, prop, _IDX_, idx, _STRING_UPPER_TOKEN))
+
+#define AP_PWRSEQ_EACH_CHIPSET_SUB_STATE_NODE_DEFINE(state) \
+ [state] = &chipset_##state##_actions,
+
+#define AP_PWRSEQ_EACH_CHIPSET_SUB_STATE_NODE_DEFINE_(state) \
+ AP_PWRSEQ_EACH_CHIPSET_SUB_STATE_NODE_DEFINE(state)
+
+#define AP_PWRSEQ_EACH_CHIPSET_SUB_STATE_NODE_DEFINE__(node_id, prop, idx) \
+ AP_PWRSEQ_EACH_CHIPSET_SUB_STATE_NODE_DEFINE_( \
+ DT_CAT6(node_id, _P_, prop, _IDX_, idx, _STRING_UPPER_TOKEN))
+
+#define AP_PWRSEQ_EACH_SUB_STATE_NODE_CHILD_DEFINE(node_id) \
+ COND_CODE_1( \
+ DT_NODE_HAS_PROP(node_id, chipset), \
+ (DT_FOREACH_PROP_ELEM( \
+ node_id, chipset, \
+ AP_PWRSEQ_EACH_CHIPSET_SUB_STATE_NODE_DEFINE__)), \
+ (COND_CODE_1( \
+ DT_NODE_HAS_PROP(node_id, application), \
+ (DT_FOREACH_PROP_ELEM( \
+ node_id, application, \
+ AP_PWRSEQ_EACH_APP_SUB_STATE_NODE_DEFINE__)), \
+ ())))
+
+/**
+ * @brief Array containing power state action handlers for all state and
+ * and substates available for AP power sequence state machine, these items
+ * correspond to `enum ap_pwrseq_state`.
+ **/
+static const struct ap_pwrseq_smf *ap_pwrseq_states[AP_POWER_STATE_COUNT] = {
+ AP_PWRSEQ_STATE_DEFINE(AP_POWER_STATE_G3),
+ AP_PWRSEQ_STATE_DEFINE(AP_POWER_STATE_S5),
+ AP_PWRSEQ_STATE_DEFINE(AP_POWER_STATE_S4),
+ AP_PWRSEQ_STATE_DEFINE(AP_POWER_STATE_S3),
+ AP_PWRSEQ_STATE_DEFINE(AP_POWER_STATE_S2),
+ AP_PWRSEQ_STATE_DEFINE(AP_POWER_STATE_S1),
+ AP_PWRSEQ_STATE_DEFINE(AP_POWER_STATE_S0),
+ DT_FOREACH_STATUS_OKAY(ap_pwrseq_sub_states,
+ AP_PWRSEQ_EACH_SUB_STATE_NODE_CHILD_DEFINE)
+};
+
+static struct ap_pwrseq_sm_data sm_data_0 = {
+ .states = ap_pwrseq_states,
+};
+
+/* Private functions available only for AP Power Sequence subsystem driver. */
+void *ap_pwrseq_sm_get_instance(void)
+{
+ return &sm_data_0;
+}
+
+int ap_pwrseq_sm_init(void *const data, k_tid_t tid,
+ enum ap_pwrseq_state init_state)
+{
+ struct ap_pwrseq_sm_data *sm_data = data;
+
+ if (sm_data->smf.current || sm_data->tid) {
+ return -EPERM;
+ }
+
+ if (init_state >= AP_POWER_STATE_COUNT) {
+ return -EINVAL;
+ }
+
+ sm_data->entry = sm_data->exit = AP_POWER_STATE_UNDEF;
+ smf_set_initial(&sm_data->smf,
+ (const struct smf_state *)sm_data->states[init_state]);
+ sm_data->tid = tid;
+
+ return 0;
+}
+
+int ap_pwrseq_sm_run_state(void *const data, uint32_t events)
+{
+ struct ap_pwrseq_sm_data *sm_data = data;
+ int ret;
+
+ if (sm_data->tid != k_current_get()) {
+ /* Called by wrong thread */
+ return -EPERM;
+ }
+
+ if (sm_data->smf.current == NULL) {
+ return -EINVAL;
+ }
+
+ sm_data->in_transition = false;
+ sm_data->entry = sm_data->exit = AP_POWER_STATE_UNDEF;
+ sm_data->run_handled = false;
+ sm_data->events = events;
+
+ ret = smf_run_state((struct smf_ctx *const)sm_data);
+
+ return ret;
+}
+
+enum ap_pwrseq_state ap_pwrseq_sm_get_cur_state(void *const data)
+{
+ struct ap_pwrseq_sm_data *sm_data = data;
+
+ if (!sm_data->smf.current) {
+ return AP_POWER_STATE_UNDEF;
+ }
+
+ return ((struct ap_pwrseq_smf *)sm_data->smf.current)->state;
+}
+
+/* Public functions for action handlers implementation. */
+int ap_pwrseq_sm_set_state(void *const data, enum ap_pwrseq_state state)
+{
+ struct ap_pwrseq_sm_data *sm_data = data;
+
+ if (sm_data->tid != k_current_get()) {
+ /* Called by wrong thread */
+ return -EPERM;
+ }
+
+ if (state >= AP_POWER_STATE_COUNT ||
+ /* Only one state transition is permited within `run` iterations */
+ sm_data->in_transition) {
+ return -EINVAL;
+ }
+
+ /* Transition has started, update corresponding flags */
+ sm_data->in_transition = true;
+ sm_data->entry_handled = sm_data->exit_handled = false;
+ sm_data->entry = state;
+ sm_data->exit = ((struct ap_pwrseq_smf *)sm_data->smf.current)->state;
+ smf_set_state((struct smf_ctx *const)&sm_data->smf,
+ (const struct smf_state *)sm_data->states[state]);
+
+ return 0;
+}
+
+bool ap_pwrseq_sm_is_event_set(void *const data, enum ap_pwrseq_event event)
+{
+ struct ap_pwrseq_sm_data *sm_data = data;
+
+ return ((sm_data->events & BIT(event)) == BIT(event));
+}
+
+enum ap_pwrseq_state ap_pwrseq_sm_get_entry_state(void *const data)
+{
+ struct ap_pwrseq_sm_data *sm_data = data;
+
+ if (sm_data->tid != k_current_get()) {
+ /* Called by wrong thread */
+ return -EPERM;
+ }
+
+ return sm_data->entry;
+}
+
+enum ap_pwrseq_state ap_pwrseq_sm_get_exit_state(void *const data)
+{
+ struct ap_pwrseq_sm_data *sm_data = data;
+
+ if (sm_data->tid != k_current_get()) {
+ /* Called by wrong thread */
+ return -EPERM;
+ }
+
+ return sm_data->exit;
+}
+
+#define AP_POWER_SM_HANDLER_DEF(action) \
+ void ap_pwrseq_sm_exec_##action##_handler( \
+ void *const data, ap_pwr_state_action_handler handler) \
+ { \
+ struct ap_pwrseq_sm_data *sm_data = data; \
+ if (handler && !sm_data->action##_handled) \
+ sm_data->action##_handled = !!handler(data); \
+ }
+
+AP_POWER_SM_HANDLER_DEF(entry)
+AP_POWER_SM_HANDLER_DEF(run)
+AP_POWER_SM_HANDLER_DEF(exit)