feat(amd): add test for register notifier EEMI API
Add a test case to validate the register notification handling
functionality of the EEMI API in the TF-A test framework. The test:
- Registers a notifier using `test_register_notifier()`.
- Waits for a notification within a predefined timeout.
- If received, unregisters the notifier using
`test_unregister_notifier()`.
Change-Id: I4454ef8644851a2dedc0a224fa220062fed1d635
Signed-off-by: Madhav Bhatt <madhav.bhatt@amd.com>
diff --git a/tftf/tests/plat/amd/common/common_files/eemi_api.c b/tftf/tests/plat/amd/common/common_files/eemi_api.c
index 6dcb632..29d9426 100644
--- a/tftf/tests/plat/amd/common/common_files/eemi_api.c
+++ b/tftf/tests/plat/amd/common/common_files/eemi_api.c
@@ -7,6 +7,8 @@
#include "eemi_api.h"
#include "xpm_defs.h"
+xpm_notifier * notifier_store = NULL;
+
static int do_feature_check(const uint32_t api_id)
{
smc_args args;
@@ -101,3 +103,123 @@
return ret;
}
+
+int xpm_request_node(const uint32_t device_id, const uint32_t capabilities,
+ const uint32_t qos, const uint32_t ack)
+{
+ uint32_t ret_payload[PAYLOAD_ARG_CNT];
+
+ return eemi_call(PM_REQUEST_NODE, ((uint64_t)capabilities << 32 | device_id),
+ ((uint64_t)ack << 32 | qos), 0, 0, 0, 0, 0, ret_payload);
+}
+
+int xpm_release_node(const uint32_t device_id)
+{
+ uint32_t ret_payload[PAYLOAD_ARG_CNT];
+
+ return eemi_call(PM_RELEASE_NODE, device_id, 0, 0, 0, 0, 0, 0, ret_payload);
+}
+
+int xpm_set_requirement(const uint32_t device_id, const uint32_t capabilities,
+ const uint32_t qos, const uint32_t ack)
+{
+ uint32_t ret_payload[PAYLOAD_ARG_CNT];
+
+ return eemi_call(PM_SET_REQUIREMENT, ((uint64_t)capabilities << 32 | device_id),
+ ((uint64_t)ack << 32 | qos), 0, 0, 0, 0, 0, ret_payload);
+}
+
+irq_handler_t xpm_notifier_cb(void *data)
+{
+ uint32_t ret_payload[PAYLOAD_ARG_CNT];
+
+ /* Get the IPI payload from TF-A */
+ (void)eemi_call(PM_GET_CALLBACK_DATA, 0, 0, 0, 0, 0, 0, 0, ret_payload);
+
+ if (!notifier_store)
+ return NULL;
+
+ if (ret_payload[0] != PM_NOTIFY_CB) {
+ notifier_store->received = 0;
+ tftf_testcase_printf("unexpected callback type\n");
+ } else {
+ notifier_store->received = 1;
+ }
+
+ (void)notifier_store->callback(notifier_store);
+
+ return 0;
+}
+
+int xpm_register_notifier(xpm_notifier * const notifier)
+{
+ uint32_t ret_payload[PAYLOAD_ARG_CNT];
+ uint32_t wake = 1U;
+ uint32_t enable = 1U;
+ uint32_t sgi_num = NOTIFIER_SGI;
+ uint32_t reset = 0U;
+ int ret;
+
+ if (!notifier)
+ return PM_RET_ERROR_ARGS;
+
+ /* Save the pointer to the structure so the notifier handler can access it */
+ notifier_store = notifier;
+
+ ret = tftf_irq_register_handler(sgi_num, (irq_handler_t)xpm_notifier_cb);
+ if (ret != PM_RET_SUCCESS) {
+ tftf_testcase_printf("failed to register the irq handler\n");
+ return ret;
+ }
+
+ tftf_irq_enable(sgi_num, IRQ_PRIORITY);
+
+ /* Register PM event notifier */
+ ret = eemi_call(PM_REGISTER_NOTIFIER, ((uint64_t)notifier->event << 32 | notifier->node),
+ ((uint64_t)enable << 32 | wake), 0, 0, 0, 0, 0, ret_payload);
+ if (ret != PM_RET_SUCCESS) {
+ tftf_testcase_printf("failed to register the event notifier\n");
+ return ret;
+ }
+
+ /* Register the SGI number with TF-A */
+ ret = eemi_call(TF_A_PM_REGISTER_SGI, ((uint64_t)reset << 32 | sgi_num),
+ 0, 0, 0, 0, 0, 0, ret_payload);
+ if (ret != PM_RET_SUCCESS)
+ tftf_testcase_printf("failed to register the SGI with TF-A\n");
+
+ return ret;
+}
+
+int xpm_unregister_notifier(xpm_notifier * const notifier)
+{
+ uint32_t ret_payload[PAYLOAD_ARG_CNT];
+ uint32_t wake = 0U;
+ uint32_t enable = 0U;
+ uint32_t reset = 1U;
+ int ret;
+
+ /* Unregister PM event notifier */
+ ret = eemi_call(PM_REGISTER_NOTIFIER, ((uint64_t)notifier->event << 32 | notifier->node),
+ ((uint64_t)enable << 32 | wake), 0, 0, 0, 0, 0, ret_payload);
+ if (ret != PM_RET_SUCCESS) {
+ tftf_testcase_printf("failed to unregister the event notifier\n");
+ return ret;
+ }
+
+ /* Unregister the SGI number with TF-A */
+ ret = eemi_call(TF_A_PM_REGISTER_SGI, ((uint64_t)reset << 32 | 0),
+ 0, 0, 0, 0, 0, 0, ret_payload);
+ if (ret != PM_RET_SUCCESS) {
+ tftf_testcase_printf("failed to unregister the SGI with TF-A\n");
+ return ret;
+ }
+
+ tftf_irq_disable(NOTIFIER_SGI);
+
+ tftf_irq_unregister_handler(NOTIFIER_SGI);
+ if (ret != PM_RET_SUCCESS)
+ tftf_testcase_printf("failed to unregister the IRQ handler\n");
+
+ return ret;
+}
diff --git a/tftf/tests/plat/amd/common/common_files/eemi_api.h b/tftf/tests/plat/amd/common/common_files/eemi_api.h
index 59de2ca..ebed5a7 100644
--- a/tftf/tests/plat/amd/common/common_files/eemi_api.h
+++ b/tftf/tests/plat/amd/common/common_files/eemi_api.h
@@ -9,8 +9,26 @@
#include "xpm_defs.h"
+typedef struct xpm_notifier {
+ void(*const callback)(struct xpm_notifier * const notifier); /**< Callback handler */
+ const uint32_t node; /**< Node to receive notifications about */
+ uint32_t event; /**< Event type to receive notifications about */
+ uint32_t received_event; /**< Event received from PLM */
+ uint32_t flags; /**< Flags */
+ uint32_t oppoint; /**< Operating point in node */
+ uint32_t received; /**< How many times the notification has been received */
+ struct xpm_notifier *next; /**< Pointer to next notifier in linked list */
+} xpm_notifier;
+
int xpm_get_api_version(uint32_t *version);
int xpm_get_chip_id(uint32_t *id_code, uint32_t *version);
int xpm_feature_check(const uint32_t api_id, uint32_t *const version);
+int xpm_request_node(const uint32_t device_id, const uint32_t capabilities,
+ const uint32_t qos, const uint32_t ack);
+int xpm_release_node(const uint32_t device_id);
+int xpm_set_requirement(const uint32_t device_id, const uint32_t capabilities,
+ const uint32_t qos, const uint32_t ack);
+int xpm_register_notifier(xpm_notifier * const notifier);
+int xpm_unregister_notifier(xpm_notifier * const notifier);
#endif /* __EEMI_API_H__ */
diff --git a/tftf/tests/plat/amd/common/common_files/xpm_defs.h b/tftf/tests/plat/amd/common/common_files/xpm_defs.h
index 90d5ddd..0e48418 100644
--- a/tftf/tests/plat/amd/common/common_files/xpm_defs.h
+++ b/tftf/tests/plat/amd/common/common_files/xpm_defs.h
@@ -13,6 +13,8 @@
#define PM_SIP_SVC 0xC2000000U
#define PAYLOAD_ARG_CNT 7U
+#define NOTIFIER_SGI 15U
+#define IRQ_PRIORITY 0U
#define upper_32_bits(n) ((uint32_t)(((n) >> 32U)))
#define lower_32_bits(n) ((uint32_t)((n) & 0xffffffffU))
@@ -27,6 +29,7 @@
/* TF-A only commands */
#define PM_GET_CALLBACK_DATA 0xa01U
+#define TF_A_PM_REGISTER_SGI 0xa04U
/* API IDs */
enum pm_api_id {
@@ -109,4 +112,26 @@
PM_API_MAX /**< 0x4B */
};
+/* Node capabilities */
+#define PM_CAP_ACCESS 1U
+#define PM_CAP_CONTEXT 2U
+
+/*
+ * PM notify events
+ */
+enum xpm_notify_event {
+ EVENT_STATE_CHANGE = 1U, /**< State change event */
+ EVENT_ZERO_USERS = 2U, /**< Zero user event */
+ EVENT_CPU_IDLE_FORCE_PWRDWN = 4U, /**< CPU idle event during force pwr down */
+};
+
+/**
+ * PM API callback IDs
+ */
+enum pm_api_cb_id {
+ PM_INIT_SUSPEND_CB = 30U, /**< Suspend callback */
+ PM_ACKNOWLEDGE_CB = 31U, /**< Acknowledge callback */
+ PM_NOTIFY_CB = 32U, /**< Notify callback */
+};
+
#endif /* XPM_DEFS_H_ */
diff --git a/tftf/tests/plat/amd/common/common_files/xpm_nodeid.h b/tftf/tests/plat/amd/common/common_files/xpm_nodeid.h
new file mode 100644
index 0000000..e485b67
--- /dev/null
+++ b/tftf/tests/plat/amd/common/common_files/xpm_nodeid.h
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2025, Advanced Micro Devices, Inc. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef XPM_NODEID_H_
+#define XPM_NODEID_H_
+
+/*
+ * Device Nodes
+ */
+#define PM_DEV_USB_0 0x18224018U
+
+#endif /* XPM_NODEID_H_ */
diff --git a/tftf/tests/plat/amd/common/register_notifier_test/register_notifier_test.c b/tftf/tests/plat/amd/common/register_notifier_test/register_notifier_test.c
new file mode 100644
index 0000000..748c5f5
--- /dev/null
+++ b/tftf/tests/plat/amd/common/register_notifier_test/register_notifier_test.c
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2025, Advanced Micro Devices, Inc. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include "eemi_api.h"
+#include "xpm_defs.h"
+#include "xpm_nodeid.h"
+#include <timer.h>
+
+#define TIMEOUT_MS 0x1000U
+
+static void test_notify_cb(xpm_notifier *notifier);
+
+xpm_notifier notifier = {
+ .callback = test_notify_cb,
+ .node = PM_DEV_USB_0,
+ .event = EVENT_STATE_CHANGE,
+ .flags = 0,
+};
+
+/*
+ * This callback routine is invoked when the notification arrives.
+ */
+void test_notify_cb(xpm_notifier *notifier)
+{
+ tftf_testcase_printf("Received notification: Node=0x%x, Event=%d, OPP=%d\n",
+ notifier->node, (int)notifier->event, (int)notifier->oppoint);
+}
+
+/*
+ * This function is used to register a notification with change state
+ * mode and change the requirements of the particular node.
+ */
+static int test_register_notifier(void)
+{
+ int32_t status;
+
+ status = xpm_request_node(notifier.node, PM_CAP_ACCESS, 0, 0);
+ if (status != PM_RET_SUCCESS) {
+ tftf_testcase_printf("%s ERROR to request the node for Node Id: 0x%x, "
+ "Status: 0x%x\n", __func__, notifier.node, status);
+ return status;
+ }
+
+ status = xpm_register_notifier(¬ifier);
+ if (status != PM_RET_SUCCESS) {
+ tftf_testcase_printf("%s ERROR to register the notifier, Status: 0x%x\n",
+ __func__, status);
+ return status;
+ }
+
+ /*
+ * Setting the requirement to 0 after registering the notifier triggers the
+ * EVENT_STATE_CHANGE event for the PM_DEV_USB_0 node.
+ */
+ status = xpm_set_requirement(notifier.node, 0, 0, 0);
+ if (status != PM_RET_SUCCESS)
+ tftf_testcase_printf("%s ERROR to set the requirement for Node Id: 0x%x, "
+ "Status: 0x%x\n", __func__, notifier.node, status);
+
+ return status;
+}
+
+/*
+ * This function is used to unregister the notifier.
+ */
+static int test_unregister_notifier(void)
+{
+ int32_t status;
+
+ status = xpm_unregister_notifier(¬ifier);
+ if (status != PM_RET_SUCCESS)
+ tftf_testcase_printf("%s ERROR to unregister the notifier\n", __func__);
+
+ status = xpm_release_node(notifier.node);
+ if (status != PM_RET_SUCCESS) {
+ tftf_testcase_printf("%s ERROR to release 0x%x node, "
+ "Status: 0x%x\n", __func__, notifier.node, status);
+ return TEST_RESULT_FAIL;
+ }
+
+ return status;
+}
+
+/*
+ * This function tests the register notification handling in the test
+ * environment. It first attempts to register a notifier using
+ * test_register_notifier(). If successful, it waits for a notification to be
+ * received within a specified timeout period. If a notification is received
+ * before the timeout, it proceeds to unregister the notifier using
+ * test_unregister_notifier().
+ */
+test_result_t test_notifier_api(void)
+{
+ uint32_t timeout_counter = 0U;
+ int32_t status;
+ test_result_t result = TEST_RESULT_SUCCESS;
+
+ status = test_register_notifier();
+ if (status != PM_RET_SUCCESS)
+ result = TEST_RESULT_FAIL;
+
+ while ((notifier.received == 0) && (timeout_counter < TIMEOUT_MS)) {
+ tftf_timer_sleep(1); // Sleep for 1 millisecond
+ timeout_counter++;
+ }
+
+ if (!notifier.received) {
+ tftf_testcase_printf("%s ERROR timeout\n", __func__);
+ result = TEST_RESULT_FAIL;
+ }
+
+ status = test_unregister_notifier();
+ if (status != PM_RET_SUCCESS)
+ result = TEST_RESULT_FAIL;
+
+ return result;
+}
diff --git a/tftf/tests/tests-versal.xml b/tftf/tests/tests-versal.xml
index 547bf1d..ce681dd 100644
--- a/tftf/tests/tests-versal.xml
+++ b/tftf/tests/tests-versal.xml
@@ -15,6 +15,7 @@
<testcase name="Read PM API Version" function="test_pm_api_version" />
<testcase name="Get Platform Chip ID" function="test_get_chip_id" />
<testcase name="Feature Check" function="test_feature_check" />
+ <testcase name="Notifier EEMI API" function="test_notifier_api" />
</testsuite>
</testsuites>