diff --git a/module/scmi/src/mod_scmi.c b/module/scmi/src/mod_scmi.c index 05aec0ee1979557bb8ea492d44cbad9210a73f32..3aa5d0fe86a1bfbe507e508429c86eac7c52dd1a 100644 --- a/module/scmi/src/mod_scmi.c +++ b/module/scmi/src/mod_scmi.c @@ -983,6 +983,9 @@ static int scmi_bind(fwk_id_t id, unsigned int round) scmi_ctx.scmi_protocol_requester_id_to_idx[scmi_protocol_id] = (uint8_t)(protocol_idx); protocol_req->message_handler = protocol_api->message_handler; +#ifdef BUILD_HAS_SCMI_NOTIFICATIONS + protocol_req->notification_handler = protocol_api->notification_handler; +#endif } #ifdef BUILD_HAS_MOD_RESOURCE_PERMS diff --git a/module/scmi_system_power_req/include/internal/scmi_system_power_req.h b/module/scmi_system_power_req/include/internal/scmi_system_power_req.h new file mode 100644 index 0000000000000000000000000000000000000000..60f3e5996425b5a59aac6d6851acc0d38d802420 --- /dev/null +++ b/module/scmi_system_power_req/include/internal/scmi_system_power_req.h @@ -0,0 +1,49 @@ +/* + * Arm SCP/MCP Software + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef INTERNAL_SCMI_SYS_POWER_REQ_H +#define INTERNAL_SCMI_SYS_POWER_REQ_H + +#include + +/*! + * \brief Flag mask for set state notification + */ +#define STATE_NOTIFY_FLAGS_MASK 0x1U + +/*! + * \brief Notification Payload + */ +struct scmi_sys_power_notification_payload { + /*! Agent ID receiving notification */ + uint32_t agent_id; + + /*! System Power command flags*/ + uint32_t flags; + + /*! System Power state being transitioned to */ + uint32_t system_state; +}; + +/*! + * \brief Notification Subscription Payload + */ +struct scmi_sys_power_req_state_notify_a2p { + /*! Notifications flags*/ + uint32_t flags; +}; + +/* + * Assert that the notification parameters size is more than the + * size of the state and flags + */ +static_assert( + (sizeof(((struct scmi_sys_power_notification_payload *)0)->flags) + + sizeof(((struct scmi_sys_power_notification_payload *)0)->system_state)) < + FWK_EVENT_PARAMETERS_SIZE); + +#endif /* INTERNAL_SCMI_SYS_POWER_REQ_H */ diff --git a/module/scmi_system_power_req/include/mod_scmi_system_power_req.h b/module/scmi_system_power_req/include/mod_scmi_system_power_req.h index 2122f711e20539a7b22e95a7dd7bb0668ae67c52..0007eb22ef36af7afcb9b8b4005aa813f734259e 100644 --- a/module/scmi_system_power_req/include/mod_scmi_system_power_req.h +++ b/module/scmi_system_power_req/include/mod_scmi_system_power_req.h @@ -1,6 +1,6 @@ /* * Arm SCP/MCP Software - * Copyright (c) 2022-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2022-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -8,10 +8,12 @@ #ifndef MOD_SCMI_SYSTEM_POWER_REQ_H #define MOD_SCMI_SYSTEM_POWER_REQ_H +#include #include #include #include +#include #include #include @@ -36,6 +38,18 @@ struct scmi_sys_power_req_state_set_a2p { uint32_t system_state; }; +/* Supported notifications */ +enum mod_system_power_req_notifications { + MOD_SYS_POWER_REQ_STATE_CHANGE_NOTIFICATION, + MOD_SYS_POWER_REQ_NOTIFICATION_COUNT, +}; + +/* System change notification */ +static const fwk_id_t mod_scmi_system_power_notification_system_power_change = + FWK_ID_NOTIFICATION_INIT( + FWK_MODULE_IDX_SCMI_SYSTEM_POWER_REQ, + MOD_SYS_POWER_REQ_STATE_CHANGE_NOTIFICATION); + /*! * \brief System Power Requester module restricted interface. * @@ -90,6 +104,22 @@ struct mod_system_power_requester_api { * */ int (*get_req_state)(fwk_id_t id, uint32_t *state); + +#ifdef BUILD_HAS_SCMI_NOTIFICATIONS + /*! + * \brief Subscribe to scmi notifications of system power state change + * Used for agents. + * \param id ID to determine which element to subscribe + * + * \retval ::FWK_SUCCESS The subscription message is sent successfully. + * + * \retval ::FWK_PENDING The power state transition request was submitted. + * + * \retval ::FWK_E_ACCESS Invalid access, the framework has rejected the + * call to the API. + */ + int (*notification_subscribe)(fwk_id_t id); +#endif }; /*! diff --git a/module/scmi_system_power_req/src/mod_scmi_system_power_req.c b/module/scmi_system_power_req/src/mod_scmi_system_power_req.c index d9870946f9252cfbdc2dba1a8165c3f48bc133ac..94e38a435df0cd64fefc3f423e228d3857332503 100644 --- a/module/scmi_system_power_req/src/mod_scmi_system_power_req.c +++ b/module/scmi_system_power_req/src/mod_scmi_system_power_req.c @@ -1,9 +1,10 @@ /* * Arm SCP/MCP Software - * Copyright (c) 2022-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2022-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ +#include #include #include @@ -19,6 +20,7 @@ #include #include #include +#include #include #include @@ -70,6 +72,15 @@ static int scmi_system_power_req_message_handler( size_t payload_size, unsigned int message_id); +#ifdef BUILD_HAS_SCMI_NOTIFICATIONS +static int scmi_system_power_req_notification_handler( + fwk_id_t protocol_id, + fwk_id_t service_id, + const uint32_t *payload, + size_t payload_size, + unsigned int message_id); +#endif + /* * Internal variables. */ @@ -178,6 +189,9 @@ static struct mod_scmi_to_protocol_api scmi_system_power_req_scmi_to_protocol_api = { .get_scmi_protocol_id = scmi_system_power_req_get_scmi_protocol_id, .message_handler = scmi_system_power_req_message_handler, +#ifdef BUILD_HAS_SCMI_NOTIFICATIONS + .notification_handler = scmi_system_power_req_notification_handler, +#endif }; static bool try_get_element_idx_from_service( @@ -339,12 +353,115 @@ static int scmi_system_power_req_get_state(fwk_id_t id, uint32_t *state) return FWK_E_PARAM; } +#ifdef BUILD_HAS_SCMI_NOTIFICATIONS +/* Agent subscribe to SCMI system power notifications */ +static int scmi_system_power_req_notification_subscribe(fwk_id_t id) +{ + int status; + struct scmi_sys_power_req_state_notify_a2p payload; + uint8_t token; + bool request_ack_by_interrupt; + uint32_t element_id; + + if (!try_get_element_idx(id, &element_id)) { + return FWK_E_RANGE; + } + + struct scmi_system_power_req_dev_ctx *dev_ctx = + &(mod_ctx.dev_ctx_table[element_id]); + + /* No ack needed */ + request_ack_by_interrupt = false; + token = (uint8_t)0; + + payload.flags = STATE_NOTIFY_FLAGS_MASK; + + /* Send to platform to subscribe to system power change notification */ + status = mod_ctx.scmi_api->scmi_send_message( + MOD_SCMI_SYS_POWER_STATE_NOTIFY, + MOD_SCMI_PROTOCOL_ID_SYS_POWER, + token, + dev_ctx->config->service_id, + (const void *)&payload, + sizeof(payload), + request_ack_by_interrupt); + + return status; +} +#endif + static const struct mod_system_power_requester_api scmi_system_power_req_driver_api = { .set_req_state = scmi_system_power_req_set_state, .get_req_state = scmi_system_power_req_get_state, +#ifdef BUILD_HAS_SCMI_NOTIFICATIONS + .notification_subscribe = scmi_system_power_req_notification_subscribe, +#endif }; +/* + * SCMI notifications APIs + */ +#ifdef BUILD_HAS_SCMI_NOTIFICATIONS + +/* System power SCMI notification handler */ +static int scmi_system_power_req_notification_handler( + fwk_id_t protocol_id, + fwk_id_t service_id, + const uint32_t *payload, + size_t payload_size, + unsigned int message_id) +{ +# ifdef BUILD_HAS_NOTIFICATION + struct scmi_sys_power_notification_payload *parameters; + int status = FWK_SUCCESS; + + if (payload_size != sizeof(struct scmi_sys_power_notification_payload)) { + return FWK_E_PARAM; + } + + unsigned int count = 0; + + /* + * Framework notification is propagated when SCMI system power + * notification is received + */ + struct fwk_event notification_event = { + .id = mod_scmi_system_power_notification_system_power_change, + .response_requested = false, + .source_id = FWK_ID_MODULE_INIT(FWK_MODULE_IDX_SCMI_SYSTEM_POWER_REQ), + }; + + parameters = (struct scmi_sys_power_notification_payload *)payload; + + /* Add the system state to the notification parameter */ + fwk_str_memcpy( + notification_event.params, + ¶meters->system_state, + sizeof(parameters->system_state)); + + /* + * Send forceful or graceful to the notification params + */ + fwk_str_memcpy( + ¬ification_event.params[sizeof(parameters->system_state)], + ¶meters->flags, + sizeof(parameters->flags)); + + status = fwk_notification_notify(¬ification_event, &count); + + return status; + +# else + /* + * Framework notification is not supported + */ + return FWK_E_SUPPORT; + +# endif +} + +#endif /* * Framework handlers */ @@ -353,7 +470,6 @@ static int scmi_system_power_req_init( unsigned int element_count, const void *data) { - unsigned int i; /* We definitely need elements in this module. */ if (element_count == 0) { return FWK_E_SUPPORT; @@ -363,15 +479,6 @@ static int scmi_system_power_req_init( mod_ctx.dev_ctx_table = fwk_mm_calloc(element_count, sizeof(mod_ctx.dev_ctx_table[0])); - /* - * Configure each element's state at the startup with that set in the - * config file. - */ - for (i = 0; i < element_count; i++) { - mod_ctx.dev_ctx_table[i].state = - mod_ctx.dev_ctx_table[i].config->start_state; - } - return FWK_SUCCESS; } @@ -397,6 +504,12 @@ static int scmi_system_power_req_elem_init( dev_ctx->config = mod_sys_pow_req_config; + /* + * Configure each element's state at the startup with that set in the + * config file. + */ + dev_ctx->state = dev_ctx->config->start_state; + return FWK_SUCCESS; } diff --git a/module/scmi_system_power_req/test/CMakeLists.txt b/module/scmi_system_power_req/test/CMakeLists.txt index 739ed7b5d97b7e30741a0c2ca3868a235b443b9b..dc7d2b3100e20c2ed734a7b3834f7bf95bc3325a 100644 --- a/module/scmi_system_power_req/test/CMakeLists.txt +++ b/module/scmi_system_power_req/test/CMakeLists.txt @@ -1,6 +1,6 @@ # # Arm SCP/MCP Software -# Copyright (c) 2022, Arm Limited and Contributors. All rights reserved. +# Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -22,5 +22,13 @@ list(APPEND MOCK_REPLACEMENTS fwk_module) list(APPEND MOCK_REPLACEMENTS fwk_mm) list(APPEND MOCK_REPLACEMENTS fwk_id) list(APPEND MOCK_REPLACEMENTS fwk_core) +list(APPEND MOCK_REPLACEMENTS fwk_notification) +list(APPEND MOCK_REPLACEMENTS fwk_string) include(${SCP_ROOT}/unit_test/module_common.cmake) + +target_compile_definitions(${UNIT_TEST_TARGET} PUBLIC + "BUILD_HAS_SCMI_NOTIFICATIONS") + +target_compile_definitions(${UNIT_TEST_TARGET} PUBLIC + "BUILD_HAS_NOTIFICATION") diff --git a/module/scmi_system_power_req/test/mod_scmi_system_power_req_unit_test.c b/module/scmi_system_power_req/test/mod_scmi_system_power_req_unit_test.c index a9c252c727dce55b8e5df7e04c297fde2d7d3848..aebcfa680aa3e4eabf7afc451cbc1af32a0d876b 100644 --- a/module/scmi_system_power_req/test/mod_scmi_system_power_req_unit_test.c +++ b/module/scmi_system_power_req/test/mod_scmi_system_power_req_unit_test.c @@ -1,6 +1,6 @@ /* * Arm SCP/MCP Software - * Copyright (c) 2022-2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2022-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -11,8 +11,9 @@ #include #include #include +#include +#include #include - #include #include @@ -22,6 +23,8 @@ #include UNIT_TEST_SRC +#define SCMI_SYS_POWER_STATE_SET_NOTIFY 0x000 + enum scp_sys_pow_nums { MOD_SCMI_SYS_POWER_REQ_IDX_0, MOD_SCMI_SYS_POWER_REQ_IDX_1, @@ -374,6 +377,54 @@ void test_function_scmi_system_power_req_set_state_no_response(void) TEST_ASSERT_EQUAL(status, FWK_SUCCESS); } +void test_function_scmi_system_power_req_notification_subscribe(void) +{ + int status; + fwk_id_t id; + uint8_t scmi_protocol_id = (uint8_t)MOD_SCMI_PROTOCOL_ID_SYS_POWER; + uint8_t scmi_message_id = (uint8_t)MOD_SCMI_SYS_POWER_STATE_NOTIFY; + + const struct scmi_sys_power_req_state_notify_a2p payload = { + .flags = STATE_NOTIFY_FLAGS_MASK, + }; + + fwk_id_get_element_idx_ExpectAnyArgsAndReturn(1); + fwk_id_is_type_ExpectAnyArgsAndReturn(true); + + scmi_send_message_ExpectWithArrayAndReturn( + scmi_message_id, + scmi_protocol_id, + 0, + dev_ctx[MOD_SCMI_SYS_POWER_REQ_IDX_1].config->service_id, + (const void *)&payload, + sizeof(payload), + sizeof(payload), + false, + FWK_SUCCESS); + + status = scmi_system_power_req_notification_subscribe(id); + TEST_ASSERT_EQUAL(status, FWK_SUCCESS); +} + +void test_function_scmi_system_power_req_notification_subscribe_err(void) +{ + int status; + fwk_id_t id; + + /* Element number >= element count */ + fwk_id_get_element_idx_ExpectAnyArgsAndReturn(2); + fwk_id_is_type_ExpectAnyArgsAndReturn(true); + + status = scmi_system_power_req_notification_subscribe(id); + TEST_ASSERT_EQUAL(status, FWK_E_RANGE); + + /* id is not element */ + fwk_id_is_type_ExpectAnyArgsAndReturn(false); + + status = scmi_system_power_req_notification_subscribe(id); + TEST_ASSERT_EQUAL(status, FWK_E_RANGE); +} + void test_scmi_system_power_req_state_set_handler(void) { int status; @@ -459,6 +510,82 @@ void test_scmi_system_power_req_message_handler(void) TEST_ASSERT_EQUAL(status, FWK_SUCCESS); } +void test_scmi_scmi_system_power_req_notification_handler(void) +{ + int status; + fwk_id_t service_id; + fwk_id_t protocol_id; + unsigned int message_id = SCMI_SYS_POWER_STATE_SET_NOTIFY; + unsigned int count = 0; + + const struct scmi_sys_power_notification_payload payload = { + .system_state = MOD_PD_STATE_OFF, + .flags = 1, + }; + + size_t payload_size = sizeof(payload); + + struct fwk_event notification_event = { + .id = mod_scmi_system_power_notification_system_power_change, + .response_requested = false, + .source_id = FWK_ID_MODULE_INIT(FWK_MODULE_IDX_SCMI_SYSTEM_POWER_REQ), + }; + + TEST_ASSERT_TRUE( + sizeof(notification_event.params) > + (sizeof(payload.system_state) + sizeof(payload.flags))); + + fwk_str_memcpy_ExpectWithArray( + notification_event.params, + sizeof(notification_event.params), + &payload.system_state, + sizeof(payload.system_state), + sizeof(payload.system_state)); + + fwk_str_memcpy_ExpectWithArray( + ¬ification_event.params[sizeof(payload.system_state)], + sizeof(notification_event.params[sizeof(payload.system_state)]), + &payload.flags, + sizeof(payload.flags), + sizeof(payload.flags)); + + fwk_notification_notify_ExpectWithArrayAndReturn( + ¬ification_event, 1, &count, 1, FWK_SUCCESS); + + status = scmi_system_power_req_notification_handler( + protocol_id, + service_id, + (const uint32_t *)&payload, + payload_size, + message_id); + + TEST_ASSERT_EQUAL(status, FWK_SUCCESS); +} + +void test_scmi_scmi_system_power_req_notification_handler_wrong_size(void) +{ + int status; + fwk_id_t service_id; + fwk_id_t protocol_id; + unsigned int message_id = SCMI_SYS_POWER_STATE_SET_NOTIFY; + + const struct scmi_sys_power_notification_payload payload = { + .system_state = MOD_PD_STATE_OFF, + .flags = 0, + }; + + size_t payload_size = sizeof(payload) + 1; + + status = scmi_system_power_req_notification_handler( + protocol_id, + service_id, + (const uint32_t *)&payload, + payload_size, + message_id); + + TEST_ASSERT_EQUAL(status, FWK_E_PARAM); +} + int scmi_test_main(void) { UNITY_BEGIN(); @@ -470,9 +597,13 @@ int scmi_test_main(void) RUN_TEST(test_function_scmi_system_power_req_get_state); RUN_TEST(test_function_scmi_system_power_req_set_state); RUN_TEST(test_function_scmi_system_power_req_set_state_no_response); + RUN_TEST(test_function_scmi_system_power_req_notification_subscribe); + RUN_TEST(test_function_scmi_system_power_req_notification_subscribe_err); RUN_TEST(test_scmi_system_power_req_state_set_handler); RUN_TEST(test_scmi_system_power_req_get_scmi_protocol_id); RUN_TEST(test_scmi_system_power_req_message_handler); + RUN_TEST(test_scmi_scmi_system_power_req_notification_handler); + RUN_TEST(test_scmi_scmi_system_power_req_notification_handler_wrong_size); return UNITY_END(); }