summaryrefslogtreecommitdiff
path: root/zephyr/subsys
diff options
context:
space:
mode:
authorBernardo Perez Priego <bernardo.perez.priego@intel.com>2022-06-01 19:14:29 -0700
committerChromeos LUCI <chromeos-scoped@luci-project-accounts.iam.gserviceaccount.com>2022-11-04 01:11:53 +0000
commit85f233b5bb37f3a337abaef4f6aaa0a46fda6f0b (patch)
tree49303c06b1df5a26fab228830bb4a1790077f449 /zephyr/subsys
parent5e39fae931d7fe889fd073f1ba3016f84855a61b (diff)
downloadchrome-ec-85f233b5bb37f3a337abaef4f6aaa0a46fda6f0b.tar.gz
zephyr: ap_pwrseq: Add Power Signals Emulator
Power Signal Emulator provides an engine in which a given set of defined power signals connection is instantiated to simulate platform behavior. This emulator is created to supports executing AP Power Sequence test by configuring power signals behavior using devicetree. BUG=b:222933615 TEST=zmake test test-ap_power BRANCH=None Signed-off-by: Bernardo Perez Priego <bernardo.perez.priego@intel.com> Change-Id: If1b1af2d287e23fe73e77986356bd3c50d56f148 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3684435 Code-Coverage: Zoss <zoss-cl-coverage@prod.google.com> Reviewed-by: Peter Marheine <pmarheine@chromium.org> Reviewed-by: Andrew McRae <amcrae@google.com>
Diffstat (limited to 'zephyr/subsys')
-rw-r--r--zephyr/subsys/CMakeLists.txt1
-rw-r--r--zephyr/subsys/emul/CMakeLists.txt2
-rw-r--r--zephyr/subsys/emul/Kconfig5
-rw-r--r--zephyr/subsys/emul/ap_pwrseq/CMakeLists.txt1
-rw-r--r--zephyr/subsys/emul/ap_pwrseq/Kconfig34
-rw-r--r--zephyr/subsys/emul/ap_pwrseq/emul_power_signals.c550
6 files changed, 593 insertions, 0 deletions
diff --git a/zephyr/subsys/CMakeLists.txt b/zephyr/subsys/CMakeLists.txt
index a3837b94e8..dfd1a35871 100644
--- a/zephyr/subsys/CMakeLists.txt
+++ b/zephyr/subsys/CMakeLists.txt
@@ -1,3 +1,4 @@
# SPDX-License-Identifier: Apache-2.0
add_subdirectory(ap_pwrseq)
+add_subdirectory(emul)
diff --git a/zephyr/subsys/emul/CMakeLists.txt b/zephyr/subsys/emul/CMakeLists.txt
new file mode 100644
index 0000000000..3394afcc94
--- /dev/null
+++ b/zephyr/subsys/emul/CMakeLists.txt
@@ -0,0 +1,2 @@
+
+add_subdirectory("ap_pwrseq")
diff --git a/zephyr/subsys/emul/Kconfig b/zephyr/subsys/emul/Kconfig
new file mode 100644
index 0000000000..48011312d5
--- /dev/null
+++ b/zephyr/subsys/emul/Kconfig
@@ -0,0 +1,5 @@
+# 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.
+
+rsource "ap_pwrseq/Kconfig"
diff --git a/zephyr/subsys/emul/ap_pwrseq/CMakeLists.txt b/zephyr/subsys/emul/ap_pwrseq/CMakeLists.txt
new file mode 100644
index 0000000000..476232d13b
--- /dev/null
+++ b/zephyr/subsys/emul/ap_pwrseq/CMakeLists.txt
@@ -0,0 +1 @@
+zephyr_library_sources_ifdef(CONFIG_EMUL_POWER_SIGNALS emul_power_signals.c)
diff --git a/zephyr/subsys/emul/ap_pwrseq/Kconfig b/zephyr/subsys/emul/ap_pwrseq/Kconfig
new file mode 100644
index 0000000000..309ee08eae
--- /dev/null
+++ b/zephyr/subsys/emul/ap_pwrseq/Kconfig
@@ -0,0 +1,34 @@
+# 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.
+
+if ZTEST
+
+config EMUL_POWER_SIGNALS
+ bool "Enable Power Signals Emulator"
+ help
+ Power signals emulator imitates the behavior of power signals
+ that interact with the EC within a given platform. It enables testing
+ AP Power Sequence execution in a virtual environment.
+
+if EMUL_POWER_SIGNALS
+
+module = EMUL_POWER_SIGNALS
+module-str = Power Signals Emulator
+source "subsys/logging/Kconfig.template.log_config"
+
+config EMUL_POWER_SIGNALS_WORK_QUEUE_STACK_SIZE
+ int "Power Signals Emulator internal work queue stack size"
+ default 1024
+ help
+ Power Signal Emulator has its own dedicated work queue, this defines
+ work queue thread stack size.
+
+config EMUL_POWER_SIGNALS_WORK_QUEUE_PRIO
+ int "Power Signals Emulator internal work queue thread priority"
+ default 0
+ help
+ Defines work queue thread thread priority.
+
+endif # EMUL_POWER_SIGNALS
+endif # ZTEST
diff --git a/zephyr/subsys/emul/ap_pwrseq/emul_power_signals.c b/zephyr/subsys/emul/ap_pwrseq/emul_power_signals.c
new file mode 100644
index 0000000000..9f68a18bdc
--- /dev/null
+++ b/zephyr/subsys/emul/ap_pwrseq/emul_power_signals.c
@@ -0,0 +1,550 @@
+/* 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/drivers/espi.h>
+#include <zephyr/drivers/espi_emul.h>
+#include <zephyr/drivers/gpio/gpio_emul.h>
+#include <zephyr/kernel.h>
+#include <zephyr/logging/log.h>
+#include <zephyr/ztest.h>
+
+#include "ap_power/ap_power.h"
+#include "ap_power/ap_power_events.h"
+#include <zephyr/drivers/adc.h>
+#include "chipset.h"
+#include "emul/emul_power_signals.h"
+#include "power_signals.h"
+#include "test_state.h"
+
+LOG_MODULE_REGISTER(emul_power_signal, CONFIG_EMUL_POWER_SIGNALS_LOG_LEVEL);
+
+/**
+ * @brief Power signal source type.
+ */
+enum power_signal_emul_source {
+ PWR_SIG_EMUL_SRC_GPIO,
+ PWR_SIG_EMUL_SRC_VW,
+ PWR_SIG_EMUL_SRC_EXT,
+ PWR_SIG_EMUL_SRC_ADC,
+};
+
+/**
+ * @brief Power signal containers definition.
+ */
+union power_signal_emul_signal_spec {
+ struct gpio_dt_spec gpio;
+ struct adc_dt_spec adc;
+};
+
+/**
+ * @brief Power signal descriptor.
+ */
+struct power_signal_emul_signal_desc {
+ const enum power_signal enum_id;
+ const char *name;
+ const enum power_signal_emul_source source;
+ const union power_signal_emul_signal_spec spec;
+};
+
+/**
+ * @brief Power signal output definition.
+ */
+struct power_signal_emul_output {
+ struct power_signal_emul_signal_desc desc;
+ const int assert_value;
+ const int assert_delay_ms;
+ const int deassert_value;
+ const int deassert_delay_ms;
+ const int init_value;
+ const bool retain;
+ const bool invert;
+ struct k_work_delayable d_work;
+ int value;
+};
+
+enum power_signal_edge {
+ EDGE_ACTIVE_ON_ASSERT,
+ EDGE_ACTIVE_ON_DEASSERT,
+ EDGE_ACTIVE_ON_BOTH,
+};
+
+/**
+ * @brief Power signal input definition.
+ */
+struct power_signal_emul_input {
+ struct power_signal_emul_signal_desc desc;
+ const int assert_value;
+ const int init_value;
+ const bool retain;
+ const enum power_signal_edge edge;
+ struct gpio_callback cb;
+ int value;
+};
+
+/**
+ * @brief Power signal node definition,
+ * One node contains at least one input signal and one or more output
+ * signals.
+ */
+struct power_signal_emul_node {
+ const char *name;
+ struct power_signal_emul_input input;
+ const int outputs_count;
+ struct power_signal_emul_output *const outputs;
+};
+
+#define EMUL_POWER_SIGNAL_GET_SOURCE(inst) \
+ COND_CODE_1( \
+ DT_NODE_HAS_COMPAT(inst, intel_ap_pwrseq_gpio), \
+ (PWR_SIG_EMUL_SRC_GPIO), \
+ (COND_CODE_1( \
+ DT_NODE_HAS_COMPAT(inst, intel_ap_pwrseq_vw), \
+ (PWR_SIG_EMUL_SRC_VW), \
+ (COND_CODE_1(DT_NODE_HAS_COMPAT( \
+ inst, intel_ap_pwrseq_external), \
+ (PWR_SIG_EMUL_SRC_EXT), \
+ (PWR_SIG_EMUL_SRC_ADC))))))
+
+#define EMUL_POWER_SIGNAL_GET_SIGNAL_SPEC(inst, dir_signal) \
+ { \
+ COND_CODE_1(DT_NODE_HAS_COMPAT(DT_PROP(inst, dir_signal), \
+ intel_ap_pwrseq_gpio), \
+ (.gpio = GPIO_DT_SPEC_GET( \
+ DT_PROP(inst, dir_signal), gpios)), \
+ ()) \
+ }
+
+#define EMUL_POWER_SIGNAL_GET_SIGNAL(inst, dir) \
+ { \
+ .enum_id = PWR_SIGNAL_ENUM(DT_PROP(inst, dir)), \
+ .name = DT_PROP(DT_PROP(inst, dir), enum_name), \
+ .source = EMUL_POWER_SIGNAL_GET_SOURCE(DT_PROP(inst, dir)), \
+ .spec = EMUL_POWER_SIGNAL_GET_SIGNAL_SPEC(inst, dir), \
+ }
+
+#define EMUL_POWER_SIGNAL_IN_DEF(inst) \
+ { \
+ .desc = EMUL_POWER_SIGNAL_GET_SIGNAL(inst, input_signal), \
+ .assert_value = DT_PROP(inst, assert_value), \
+ .init_value = DT_PROP_OR(inst, init_value, 0), \
+ .edge = DT_STRING_TOKEN(inst, edge), \
+ .retain = !DT_NODE_HAS_PROP(inst, init_value), \
+ }
+
+#define EMUL_POWER_SIGNAL_OUT_DEF(inst) \
+ { \
+ .desc = EMUL_POWER_SIGNAL_GET_SIGNAL(inst, output_signal), \
+ .assert_value = DT_PROP(inst, assert_value), \
+ .assert_delay_ms = DT_PROP(inst, assert_delay_ms), \
+ .deassert_value = DT_PROP(inst, deassert_value), \
+ .deassert_delay_ms = DT_PROP(inst, deassert_delay_ms), \
+ .init_value = DT_PROP_OR(inst, init_value, 0), \
+ .retain = !DT_NODE_HAS_PROP(inst, init_value), \
+ .invert = DT_PROP(inst, invert_value), \
+ },
+
+#define EMUL_POWER_SIGNAL_OUT_ARRAY_DEF(inst) \
+ static struct power_signal_emul_output DT_CAT(inst, _output)[] = { \
+ DT_FOREACH_CHILD_STATUS_OKAY(inst, EMUL_POWER_SIGNAL_OUT_DEF) \
+ };
+
+#define EMUL_POWER_SIGNAL_GET_INPUT_ENUM(inst) ENUM_##inst##_OUTPUT,
+
+#define EMUL_POWER_SIGNAL_NODES_DEF(inst) \
+ enum { \
+ DT_FOREACH_CHILD_STATUS_OKAY(inst, \
+ EMUL_POWER_SIGNAL_GET_INPUT_ENUM) \
+ DT_CAT(inst, _OUTPUT_COUNT), \
+ }; \
+ __COND_CODE(IS_EQ(DT_CAT(inst, _OUTPUT_COUNT), 0), (), \
+ (EMUL_POWER_SIGNAL_OUT_ARRAY_DEF(inst))) \
+ static struct power_signal_emul_node DT_CAT(inst, _node) = { \
+ .name = DT_NODE_FULL_NAME(inst), \
+ .input = EMUL_POWER_SIGNAL_IN_DEF(inst), \
+ .outputs_count = inst##_OUTPUT_COUNT, \
+ .outputs = __COND_CODE(IS_EQ(DT_CAT(inst, _OUTPUT_COUNT), 0), \
+ (NULL), (DT_CAT(inst, _output))), \
+ };
+
+DT_FOREACH_STATUS_OKAY(intel_ap_pwr_signal_emul, EMUL_POWER_SIGNAL_NODES_DEF)
+
+#define EMUL_POWER_SIGNAL_NODES_ARRAY_GET_NODES_REFS_WITH_COMMA(inst) \
+ (&DT_CAT(inst, _node)),
+
+#define EMUL_POWER_SIGNAL_NODES_ARRAY_GET_NODES_REFS(inst) \
+ EMUL_POWER_SIGNAL_NODES_ARRAY_GET_NODES_REFS_WITH_COMMA(inst)
+
+#define EMUL_POWER_SIGNAL_NODES_ARRAY_GET_IO_SIGNALS(inst, prop, idx) \
+ EMUL_POWER_SIGNAL_NODES_ARRAY_GET_NODES_REFS( \
+ DT_PHANDLE_BY_IDX(inst, prop, idx))
+
+#define EMUL_POWER_SIGNAL_NODES_ARRAY_DEF(inst) \
+ static struct power_signal_emul_node *DT_CAT(inst, _nodes)[] = { \
+ DT_FOREACH_PROP_ELEM( \
+ inst, nodes, \
+ EMUL_POWER_SIGNAL_NODES_ARRAY_GET_IO_SIGNALS) \
+ };
+
+DT_FOREACH_STATUS_OKAY(intel_ap_pwr_test_platform,
+ EMUL_POWER_SIGNAL_NODES_ARRAY_DEF)
+
+#define EMUL_POWER_SIGNAL_TEST_PLATFORM_GET_NODES_REFS_ITEM(inst) \
+ DT_CAT(inst, _nodes),
+
+#define EMUL_POWER_SIGNAL_TEST_PLATFORM_GET_NODES_REFS(inst) \
+ EMUL_POWER_SIGNAL_TEST_PLATFORM_GET_NODES_REFS_ITEM(inst)
+
+#define EMUL_POWER_SIGNAL_TEST_PLATFORM_GET_IO_SIGNALS(inst, prop, idx) \
+ EMUL_POWER_SIGNAL_TEST_PLATFORM_GET_NODES_REFS( \
+ DT_PHANDLE_BY_IDX(inst, prop, idx))
+
+#define EMUL_POWER_SIGNAL_TEST_PLATFORM_GET_NODES(inst) \
+ { \
+ DT_FOREACH_PROP_ELEM( \
+ inst, nodes, \
+ EMUL_POWER_SIGNAL_TEST_PLATFORM_GET_IO_SIGNALS) \
+ }
+
+#define EMUL_POWER_SIGNAL_TEST_PLATFORM_DEF(inst) \
+ const struct power_signal_emul_test_platform inst = { \
+ .name_id = DT_NODE_FULL_NAME(inst), \
+ .nodes_count = DT_PROP_LEN(inst, nodes), \
+ .nodes = DT_CAT(inst, _nodes), \
+ };
+
+DT_FOREACH_STATUS_OKAY(intel_ap_pwr_test_platform,
+ EMUL_POWER_SIGNAL_TEST_PLATFORM_DEF)
+
+static K_KERNEL_STACK_DEFINE(work_q_stack,
+ CONFIG_EMUL_POWER_SIGNALS_WORK_QUEUE_STACK_SIZE);
+
+struct k_work_q work_q;
+
+static const struct power_signal_emul_test_platform *cur_test_platform;
+
+static bool emul_ready;
+
+/**
+ * @brief Set GPIO type power signal to specified value.
+ *
+ * @param spec Pointer to container for GPIO pin information specified in
+ * devicetree.
+ * @param value Value to be set on GPIO.
+ */
+static void power_signal_emul_set_gpio_value(const struct gpio_dt_spec *spec,
+ int value)
+{
+ gpio_flags_t gpio_flags;
+ int ret;
+
+ ret = gpio_emul_flags_get(spec->port, spec->pin, &gpio_flags);
+ zassert_ok(ret, "Getting GPIO flags!!");
+
+ if (gpio_flags & GPIO_INPUT) {
+ ret = gpio_emul_input_set(spec->port, spec->pin, value);
+ } else if (gpio_flags & GPIO_OUTPUT) {
+ ret = gpio_pin_set(spec->port, spec->pin, value);
+ }
+ zassert_ok(ret, "Setting GPIO value!!");
+}
+
+/**
+ * @brief Set power signal to specified value.
+ *
+ * @param desc Pointer to power signal descriptor.
+ * @param value Value to be set on power signal.
+ */
+static void
+power_signal_emul_set_value(struct power_signal_emul_signal_desc *desc,
+ int value)
+{
+ LOG_DBG("Set Signal %s -> %d", desc->name, value);
+
+ switch (desc->source) {
+ case PWR_SIG_EMUL_SRC_GPIO:
+ power_signal_emul_set_gpio_value(&desc->spec.gpio, !!value);
+ break;
+
+ case PWR_SIG_EMUL_SRC_EXT:
+ zassert_ok(power_signal_set(desc->enum_id, value),
+ "Setting %s Signal value!!", desc->name);
+ __fallthrough;
+
+ case PWR_SIG_EMUL_SRC_VW:
+ power_signal_interrupt(desc->enum_id, value);
+ break;
+
+ default:
+ zassert_unreachable("Undefined Signal %s!!", desc->name);
+ }
+}
+
+/**
+ * @brief Get GPIO type power signal value.
+ *
+ * @param spec Pointer to container for GPIO pin information specified in
+ * devicetree.
+ *
+ * @return GPIO type power signal value.
+ */
+static int power_signal_emul_get_gpio_value(const struct gpio_dt_spec *spec)
+{
+ gpio_flags_t gpio_flags;
+ int ret;
+
+ ret = gpio_emul_flags_get(spec->port, spec->pin, &gpio_flags);
+ zassert_ok(ret, "Getting GPIO flags!!");
+
+ if (gpio_flags & GPIO_INPUT) {
+ ret = gpio_pin_get(spec->port, spec->pin);
+ } else if (gpio_flags & GPIO_OUTPUT) {
+ ret = gpio_emul_output_get(spec->port, spec->pin);
+ }
+
+ return ret;
+}
+
+/**
+ * @brief Get power signal value.
+ *
+ * @param desc Pointer to power signal descriptor.
+ *
+ * @return Power signal value.
+ */
+static int
+power_signal_emul_get_value(struct power_signal_emul_signal_desc *desc)
+{
+ int ret;
+
+ if (desc->source == PWR_SIG_EMUL_SRC_GPIO) {
+ ret = power_signal_emul_get_gpio_value(&desc->spec.gpio);
+ } else {
+ ret = power_signal_get(desc->enum_id);
+ }
+
+ return ret;
+}
+
+/**
+ * @brief Handle GPIO type power signal interrupt.
+ *
+ * @param port Pointer to GPIO device.
+ * @param cb Original struct gpio_callback owning this handler.
+ * @param pins Mask of pins that triggers the callback handler.
+ */
+static void emul_power_signal_gpio_interrupt(const struct device *port,
+ struct gpio_callback *cb,
+ gpio_port_pins_t pins)
+{
+ struct power_signal_emul_input *in_signal =
+ CONTAINER_OF(cb, struct power_signal_emul_input, cb);
+ struct power_signal_emul_node *node =
+ CONTAINER_OF(in_signal, struct power_signal_emul_node, input);
+ int value;
+ int delay;
+
+ value = power_signal_emul_get_value(&in_signal->desc);
+ if (value == in_signal->value) {
+ return;
+ }
+
+ in_signal->value = value;
+
+ if (!emul_ready) {
+ return;
+ }
+
+ if (in_signal->edge == EDGE_ACTIVE_ON_DEASSERT &&
+ value == in_signal->assert_value) {
+ return;
+ } else if (in_signal->edge == EDGE_ACTIVE_ON_ASSERT &&
+ value != in_signal->assert_value) {
+ return;
+ }
+
+ LOG_DBG("INT: Set Signal %s -> %d", in_signal->desc.name, value);
+ for (int i = 0; i < node->outputs_count; i++) {
+ struct power_signal_emul_output *out_signal = &node->outputs[i];
+
+ out_signal->value = (value == in_signal->assert_value) ^
+ out_signal->invert ?
+ out_signal->assert_value :
+ out_signal->deassert_value;
+
+ delay = (value == in_signal->assert_value) ?
+ out_signal->assert_delay_ms :
+ out_signal->deassert_delay_ms;
+
+ LOG_DBG("INT: Delay Signal %s", out_signal->desc.name);
+ k_work_schedule_for_queue(&work_q, &out_signal->d_work,
+ K_MSEC(delay));
+ }
+}
+
+/**
+ * @brief Handle power signal delayed work.
+ *
+ * This will set power signal value accordingly.
+ *
+ * @param work Pointer to work structure.
+ */
+static void emul_signal_work_hanlder(struct k_work *work)
+{
+ struct k_work_delayable *d_work = k_work_delayable_from_work(work);
+ struct power_signal_emul_output *out_signal =
+ CONTAINER_OF(d_work, struct power_signal_emul_output, d_work);
+
+ power_signal_emul_set_value(&out_signal->desc, out_signal->value);
+}
+
+/**
+ * @brief Initialize power signal emulator node.
+ *
+ * This will enable corresponding initiator power signal interruption and
+ * its handler's power signals work structures.
+ *
+ * @param node Pointer to node containing power signals.
+ */
+static int power_signal_init_node(struct power_signal_emul_node *node)
+{
+ struct power_signal_emul_input *in_signal = &node->input;
+ struct power_signal_emul_output *out_signal;
+
+ if (!node->outputs_count) {
+ LOG_ERR("Node does not have output signal!!");
+ return -EINVAL;
+ }
+
+ LOG_DBG("Initializing node: %s", node->name);
+ for (int i = 0; i < node->outputs_count; i++) {
+ out_signal = &node->outputs[i];
+
+ if (out_signal->retain) {
+ out_signal->value =
+ power_signal_emul_get_value(&out_signal->desc);
+ } else {
+ /* Not retaining previous value, override */
+ power_signal_emul_set_value(&out_signal->desc,
+ out_signal->init_value);
+ out_signal->value = out_signal->init_value;
+ }
+ k_work_init_delayable(&out_signal->d_work,
+ emul_signal_work_hanlder);
+ }
+
+ if (in_signal->retain) {
+ in_signal->value =
+ power_signal_emul_get_value(&in_signal->desc);
+ } else {
+ /* Not retaining previous value, override */
+ power_signal_emul_set_value(&in_signal->desc,
+ in_signal->init_value);
+ in_signal->value = in_signal->init_value;
+ }
+ if (in_signal->desc.source == PWR_SIG_EMUL_SRC_GPIO) {
+ gpio_init_callback(&in_signal->cb,
+ emul_power_signal_gpio_interrupt,
+ BIT(in_signal->desc.spec.gpio.pin));
+
+ gpio_add_callback(in_signal->desc.spec.gpio.port,
+ &in_signal->cb);
+
+ gpio_pin_interrupt_configure_dt(&in_signal->desc.spec.gpio,
+ GPIO_INT_EDGE_BOTH);
+ }
+ return 0;
+}
+
+/** See description in emul_power_signals.h */
+int power_signal_emul_load(
+ const struct power_signal_emul_test_platform *test_platform)
+{
+ int ret;
+
+ if (cur_test_platform) {
+ LOG_ERR("Power Signal Emulator Busy!!");
+ return -EBUSY;
+ }
+
+ cur_test_platform = test_platform;
+
+ LOG_DBG("Loading Emulator test: %s", cur_test_platform->name_id);
+
+ for (int i = 0; i < cur_test_platform->nodes_count; i++) {
+ ret = power_signal_init_node(cur_test_platform->nodes[i]);
+ if (ret) {
+ power_signal_emul_unload();
+ return ret;
+ }
+ }
+
+ emul_ready = true;
+ LOG_DBG("Loading Emulator test Done");
+ return 0;
+}
+
+/** See description in emul_power_signals.h */
+int power_signal_emul_unload(void)
+{
+ struct power_signal_emul_node *node;
+ struct power_signal_emul_output *out_signal;
+ struct power_signal_emul_input *in_signal;
+
+ if (!cur_test_platform) {
+ LOG_ERR("No Test Platform Loaded!!");
+ return -EINVAL;
+ }
+
+ emul_ready = false;
+ for (int i = 0; i < cur_test_platform->nodes_count; i++) {
+ node = cur_test_platform->nodes[i];
+ in_signal = &node->input;
+
+ if (in_signal->desc.source != PWR_SIG_EMUL_SRC_GPIO) {
+ /* Currently, Only output GPIO signals are supported */
+ continue;
+ }
+
+ for (int j = 0; j < node->outputs_count; j++) {
+ static struct k_work_sync work_sync;
+
+ out_signal = &node->outputs[j];
+ k_work_cancel_delayable_sync(&out_signal->d_work,
+ &work_sync);
+ }
+ gpio_pin_interrupt_configure_dt(&in_signal->desc.spec.gpio,
+ GPIO_INT_DISABLE);
+ if (in_signal->cb.handler) {
+ gpio_remove_callback(in_signal->desc.spec.gpio.port,
+ &in_signal->cb);
+ }
+ }
+ cur_test_platform = NULL;
+ return 0;
+}
+
+/**
+ * @brief Initialize power signal emulator internal work queue.
+ *
+ * @param dev Unused parameter.
+ *
+ * @return 0 Return success only.
+ */
+static int power_signal_emul_work_q_init(const struct device *dev)
+{
+ ARG_UNUSED(dev);
+ struct k_work_queue_config cfg = {
+ .name = "psignal_emul",
+ .no_yield = true,
+ };
+
+ k_work_queue_start(&work_q, work_q_stack,
+ K_KERNEL_STACK_SIZEOF(work_q_stack),
+ CONFIG_EMUL_POWER_SIGNALS_WORK_QUEUE_PRIO, &cfg);
+ return 0;
+}
+
+SYS_INIT(power_signal_emul_work_q_init, POST_KERNEL,
+ CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);