From 9da6daaf2c581b1ce3218ae5890bd4863153920d Mon Sep 17 00:00:00 2001 From: Leandro Belli Date: Mon, 19 Apr 2021 17:31:37 +0100 Subject: [PATCH 1/8] scmi_clock: skip message when the agent has requested the state This patch changes the behaviour of the policy handling removing a call to the HAL when the agent has already requested a state. Signed-off-by: Leandro Belli Change-Id: Id97cfea3d697582f3ea87547cd909b0da77aee47 --- module/scmi_clock/src/mod_scmi_clock.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/module/scmi_clock/src/mod_scmi_clock.c b/module/scmi_clock/src/mod_scmi_clock.c index 61563c2d9..7687534be 100644 --- a/module/scmi_clock/src/mod_scmi_clock.c +++ b/module/scmi_clock/src/mod_scmi_clock.c @@ -517,8 +517,7 @@ FWK_WEAK int mod_scmi_clock_config_set_policy( case MOD_CLOCK_STATE_RUNNING: /* The agent has already requested to set state to RUNNING */ if (clock_state == MOD_CLOCK_STATE_RUNNING) { - status = FWK_SUCCESS; - break; + return FWK_SUCCESS; } /* set the clock state for this agent to RUNNING */ @@ -547,9 +546,9 @@ FWK_WEAK int mod_scmi_clock_config_set_policy( case MOD_CLOCK_STATE_STOPPED: /* The agent has already requested to set state to STOPPED */ if (clock_state == MOD_CLOCK_STATE_STOPPED) { - status = FWK_SUCCESS; - break; + return FWK_SUCCESS; } + /* error to try and stop a stopped clock */ if (clock_count[clock_dev_id] == 0) { scmi_clock_ctx.scmi_api->get_agent_id(service_id, &agent_id); -- GitLab From 59bc0be0de8b4f61d8fbc632e82ab6cd5b3c2701 Mon Sep 17 00:00:00 2001 From: Leandro Belli Date: Tue, 24 Nov 2020 16:34:28 +0000 Subject: [PATCH 2/8] clock: arrange internal context variables This patch arranges internal context variables for clock module in order to improve code readability and prepare this module for upcoming clock management framework. Signed-off-by: Leandro Belli Change-Id: Ib86900e228339f9ea859c730a0b630485471cd03 --- module/clock/src/clock.h | 40 ++++++++++++++ module/clock/src/mod_clock.c | 102 ++++++++++++----------------------- 2 files changed, 75 insertions(+), 67 deletions(-) diff --git a/module/clock/src/clock.h b/module/clock/src/clock.h index 24ecb8300..fc17c80cf 100644 --- a/module/clock/src/clock.h +++ b/module/clock/src/clock.h @@ -12,6 +12,46 @@ #include +/* Device context */ +struct clock_dev_ctx { + /* Pointer to the element configuration data */ + const struct mod_clock_dev_config *config; + + /* Driver API */ + struct mod_clock_drv_api *api; + + struct { + /* Cookie for the pre-transition notification response */ + unsigned int pre_power_transition_cookie; + + /* Counter of notifications sent */ + unsigned int transition_pending_sent; + + /* Status of the pending transition */ + unsigned int transition_pending_response_status; + } pd_notif; + + struct { + /* A request is on-going */ + bool is_ongoing; + + /* Cookie for the response event */ + uint32_t cookie; + } request; +}; + +/* Module context */ +struct clock_ctx { + /* Pointer to the module configuration data */ + const struct mod_clock_config *config; + + /* Table of elements context */ + struct clock_dev_ctx *dev_ctx_table; +}; + +/* Get context helper function */ +void clock_get_ctx(fwk_id_t clock_id, struct clock_dev_ctx **ctx); + /* * Clock event indexes. */ diff --git a/module/clock/src/mod_clock.c b/module/clock/src/mod_clock.c index b646fbd48..b774bda13 100644 --- a/module/clock/src/mod_clock.c +++ b/module/clock/src/mod_clock.c @@ -23,38 +23,6 @@ #include #include -/* Device context */ -struct clock_dev_ctx { - /* Pointer to the element configuration data */ - const struct mod_clock_dev_config *config; - - /* Driver API */ - struct mod_clock_drv_api *api; - - /* Cookie for the pre-transition notification response */ - unsigned int pd_pre_power_transition_notification_cookie; - - /* Counter of notifications sent */ - unsigned int transition_pending_notifications_sent; - - /* Status of the pending transition */ - unsigned int transition_pending_response_status; - - /* A request is on-going */ - bool is_request_ongoing; - - /* Cookie for the response event */ - uint32_t cookie; -}; - -/* Module context */ -struct clock_ctx { - /* Pointer to the module configuration data */ - const struct mod_clock_config *config; - - /* Table of elements context */ - struct clock_dev_ctx *dev_ctx_table; -}; static struct clock_ctx module_ctx; @@ -74,16 +42,15 @@ static int process_response_event(const struct fwk_event *event) ctx = &module_ctx.dev_ctx_table[fwk_id_get_element_idx(event->target_id)]; - status = fwk_thread_get_delayed_response(event->target_id, - ctx->cookie, - &resp_event); + status = fwk_thread_get_delayed_response( + event->target_id, ctx->request.cookie, &resp_event); if (status != FWK_SUCCESS) { return status; } resp_params->status = event_params->status; resp_params->value = event_params->value; - ctx->is_request_ongoing = false; + ctx->request.is_ongoing = false; return fwk_thread_put_event(&resp_event); } @@ -95,7 +62,7 @@ static int process_request_event(const struct fwk_event *event, ctx = &module_ctx.dev_ctx_table[fwk_id_get_element_idx(event->target_id)]; - ctx->cookie = event->cookie; + ctx->request.cookie = event->cookie; resp_event->is_delayed_response = true; return FWK_SUCCESS; @@ -120,16 +87,16 @@ static int create_async_request( return status; } - ctx->is_request_ongoing = true; + ctx->request.is_ongoing = true; - /* - * Signal the result of the request is pending and will arrive later - * through an event. - */ + /* + * Signal the result of the request is pending and will arrive later + * through an event. + */ return FWK_PENDING; } -static void get_ctx(fwk_id_t clock_id, struct clock_dev_ctx **ctx) +void clock_get_ctx(fwk_id_t clock_id, struct clock_dev_ctx **ctx) { fwk_assert(fwk_module_is_valid_element_id(clock_id)); @@ -140,8 +107,9 @@ static void get_ctx(fwk_id_t clock_id, struct clock_dev_ctx **ctx) * Driver response API. */ -void request_complete(fwk_id_t dev_id, - struct mod_clock_driver_resp_params *response) +void clock_request_complete( + fwk_id_t dev_id, + struct mod_clock_driver_resp_params *response) { int status; struct fwk_event event; @@ -171,7 +139,7 @@ void request_complete(fwk_id_t dev_id, } static struct mod_clock_driver_response_api clock_driver_response_api = { - .request_complete = request_complete, + .request_complete = clock_request_complete, }; /* @@ -184,10 +152,10 @@ static int clock_set_rate(fwk_id_t clock_id, uint64_t rate, int status; struct clock_dev_ctx *ctx; - get_ctx(clock_id, &ctx); + clock_get_ctx(clock_id, &ctx); /* Concurrency is not supported */ - if (ctx->is_request_ongoing) { + if (ctx->request.is_ongoing) { return FWK_E_BUSY; } @@ -207,14 +175,14 @@ static int clock_get_rate(fwk_id_t clock_id, uint64_t *rate) int status; struct clock_dev_ctx *ctx; - get_ctx(clock_id, &ctx); + clock_get_ctx(clock_id, &ctx); if (rate == NULL) { return FWK_E_PARAM; } /* Concurrency is not supported */ - if (ctx->is_request_ongoing) { + if (ctx->request.is_ongoing) { return FWK_E_BUSY; } @@ -234,7 +202,7 @@ static int clock_get_rate_from_index(fwk_id_t clock_id, unsigned int rate_index, { struct clock_dev_ctx *ctx; - get_ctx(clock_id, &ctx); + clock_get_ctx(clock_id, &ctx); if (rate == NULL) { return FWK_E_PARAM; @@ -249,10 +217,10 @@ static int clock_set_state(fwk_id_t clock_id, enum mod_clock_state state) int status; struct clock_dev_ctx *ctx; - get_ctx(clock_id, &ctx); + clock_get_ctx(clock_id, &ctx); /* Concurrency is not supported */ - if (ctx->is_request_ongoing) { + if (ctx->request.is_ongoing) { return FWK_E_BUSY; } @@ -272,14 +240,14 @@ static int clock_get_state(fwk_id_t clock_id, enum mod_clock_state *state) int status; struct clock_dev_ctx *ctx; - get_ctx(clock_id, &ctx); + clock_get_ctx(clock_id, &ctx); if (state == NULL) { return FWK_E_PARAM; } /* Concurrency is not supported */ - if (ctx->is_request_ongoing) { + if (ctx->request.is_ongoing) { return FWK_E_BUSY; } @@ -299,7 +267,7 @@ static int clock_get_info(fwk_id_t clock_id, struct mod_clock_info *info) int status; struct clock_dev_ctx *ctx; - get_ctx(clock_id, &ctx); + clock_get_ctx(clock_id, &ctx); if (info == NULL) { return FWK_E_PARAM; @@ -491,11 +459,11 @@ static int clock_process_pd_pre_transition_notification( pd_resp_params->status = status; if (status != FWK_SUCCESS) { - ctx->transition_pending_notifications_sent = 0; + ctx->pd_notif.transition_pending_sent = 0; return status; } - ctx->transition_pending_response_status = FWK_SUCCESS; + ctx->pd_notif.transition_pending_response_status = FWK_SUCCESS; out_params = (struct clock_notification_params *)outbound_event.params; @@ -512,17 +480,16 @@ static int clock_process_pd_pre_transition_notification( /* Notify subscribers of the pending clock state change */ status = fwk_notification_notify( - &outbound_event, - &(ctx->transition_pending_notifications_sent)); + &outbound_event, &(ctx->pd_notif.transition_pending_sent)); if (status != FWK_SUCCESS) { pd_resp_params->status = status; return status; } - if (ctx->transition_pending_notifications_sent > 0) { + if (ctx->pd_notif.transition_pending_sent > 0) { /* There are one or more subscribers that must respond */ resp_event->is_delayed_response = true; - ctx->pd_pre_power_transition_notification_cookie = event->cookie; + ctx->pd_notif.pre_power_transition_cookie = event->cookie; } return status; @@ -578,7 +545,7 @@ static int clock_process_notification_response( struct fwk_event pd_response_event = { .id = module_ctx.config->pd_pre_transition_notification_id, .target_id = ctx->config->pd_source_id, - .cookie = ctx->pd_pre_power_transition_notification_cookie, + .cookie = ctx->pd_notif.pre_power_transition_cookie, .is_notification = true, .is_response = true, .is_delayed_response = true, @@ -588,7 +555,7 @@ static int clock_process_notification_response( mod_clock_notification_id_state_change_pending)); /* At least one notification response must be outstanding */ - if (!fwk_expect(ctx->transition_pending_notifications_sent != 0)) { + if (!fwk_expect(ctx->pd_notif.transition_pending_sent != 0)) { return FWK_E_PANIC; } @@ -603,10 +570,10 @@ static int clock_process_notification_response( * that is pending. */ if (resp_params->status != FWK_SUCCESS) { - ctx->transition_pending_response_status = resp_params->status; + ctx->pd_notif.transition_pending_response_status = resp_params->status; } - if ((--(ctx->transition_pending_notifications_sent)) == 0) { + if ((--(ctx->pd_notif.transition_pending_sent)) == 0) { /* * If this is the final response then the response to the power domain * notification can be sent. @@ -614,7 +581,8 @@ static int clock_process_notification_response( pd_resp_params = (struct mod_pd_power_state_pre_transition_notification_resp_params *)pd_response_event.params; - pd_resp_params->status = ctx->transition_pending_response_status; + pd_resp_params->status = + ctx->pd_notif.transition_pending_response_status; fwk_thread_put_event(&pd_response_event); } -- GitLab From 84c949df40485c89aa4f39823b067b4fc9ce8aa2 Mon Sep 17 00:00:00 2001 From: Leandro Belli Date: Tue, 24 Nov 2020 17:46:28 +0000 Subject: [PATCH 3/8] clock: add internal tree configuration This patch enables tree configuration for clock management framework. The default configuration is a single node clock so the tree is disabled for existing platforms. The newly added tree is not yet used. This will allow to use a new clock management framework which will be capable of handling clock nodes dependencies. Signed-off-by: Leandro Belli Change-Id: I9146cf44169e0ced97379d15b5b5a5f15406a836 --- module/clock/CMakeLists.txt | 6 ++- module/clock/include/mod_clock.h | 8 +++ module/clock/src/Makefile | 2 +- module/clock/src/clock.h | 33 ++++++++++++ module/clock/src/clock_tree_management.c | 68 ++++++++++++++++++++++++ module/clock/src/mod_clock.c | 9 +++- 6 files changed, 121 insertions(+), 5 deletions(-) create mode 100644 module/clock/src/clock_tree_management.c diff --git a/module/clock/CMakeLists.txt b/module/clock/CMakeLists.txt index 7166bc12b..2de363db8 100644 --- a/module/clock/CMakeLists.txt +++ b/module/clock/CMakeLists.txt @@ -10,7 +10,9 @@ add_library(${SCP_MODULE_TARGET} SCP_MODULE) target_include_directories(${SCP_MODULE_TARGET} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include") -target_sources(${SCP_MODULE_TARGET} - PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/src/mod_clock.c") +target_sources( + ${SCP_MODULE_TARGET} + PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/src/mod_clock.c" + "${CMAKE_CURRENT_SOURCE_DIR}/src/clock_tree_management.c") target_link_libraries(${SCP_MODULE_TARGET} PRIVATE module-power-domain) diff --git a/module/clock/include/mod_clock.h b/module/clock/include/mod_clock.h index 0603a556e..fe93a87b7 100644 --- a/module/clock/include/mod_clock.h +++ b/module/clock/include/mod_clock.h @@ -191,6 +191,14 @@ struct mod_clock_dev_config { * to receive notifications from the power domain module. */ fwk_id_t pd_source_id; + + /*! + * \brief Assigned clock parent optional identifier. + * + * \details If the clock parent is not defined, it will not be connected + * to the tree of clocks and it will be left as a single node. + */ + fwk_optional_id_t parent_id; }; /*! diff --git a/module/clock/src/Makefile b/module/clock/src/Makefile index 6d1f68b47..111ce8c6f 100644 --- a/module/clock/src/Makefile +++ b/module/clock/src/Makefile @@ -6,6 +6,6 @@ # BS_LIB_NAME := Clock HAL -BS_LIB_SOURCES := mod_clock.c +BS_LIB_SOURCES := mod_clock.c clock_tree_management.c include $(BS_DIR)/lib.mk diff --git a/module/clock/src/clock.h b/module/clock/src/clock.h index fc17c80cf..1b3e8b6c6 100644 --- a/module/clock/src/clock.h +++ b/module/clock/src/clock.h @@ -11,9 +11,13 @@ #include #include +#include /* Device context */ struct clock_dev_ctx { + /* Identifier of the clock */ + fwk_id_t id; + /* Pointer to the element configuration data */ const struct mod_clock_dev_config *config; @@ -38,6 +42,29 @@ struct clock_dev_ctx { /* Cookie for the response event */ uint32_t cookie; } request; + + /* Parent identifier */ + fwk_id_t parent_id; + + /* List all clock children if any */ + struct fwk_slist children_list; + + /* + * Node in the parent list if not the root + */ + struct fwk_slist_node child_node; + + /* Clock state transition */ + struct { + /* Number of pending responses */ + uint32_t pending_responses; + + /* Clock state transition */ + uint32_t state; + } state_transition; + + /* Reference count */ + uint32_t ref_count; }; /* Module context */ @@ -47,11 +74,17 @@ struct clock_ctx { /* Table of elements context */ struct clock_dev_ctx *dev_ctx_table; + + /* Number of clocks devices */ + uint32_t dev_count; }; /* Get context helper function */ void clock_get_ctx(fwk_id_t clock_id, struct clock_dev_ctx **ctx); +/* Connect clock tree interconnecting parent to children nodes */ +int clock_connect_tree(struct clock_ctx *module_ctx); + /* * Clock event indexes. */ diff --git a/module/clock/src/clock_tree_management.c b/module/clock/src/clock_tree_management.c new file mode 100644 index 000000000..83da4365c --- /dev/null +++ b/module/clock/src/clock_tree_management.c @@ -0,0 +1,68 @@ +/* + * Arm SCP/MCP Software + * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "clock.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* Sub-routine of 'clock_start()' Build the clock tree */ +int clock_connect_tree(struct clock_ctx *module_ctx) +{ + unsigned int clk_idx, parent_idx; + int status; + enum mod_clock_state current_state; + struct clock_dev_ctx *clk, *parent; + + for (clk_idx = 0; clk_idx < module_ctx->dev_count; clk_idx++) { + clk = &(module_ctx->dev_ctx_table[clk_idx]); + + if (!fwk_optional_id_is_defined(clk->config->parent_id)) { + clk->parent_id = FWK_ID_NONE; + continue; + } + + clk->parent_id = clk->config->parent_id; + parent_idx = fwk_id_get_element_idx(clk->parent_id); + + parent = &(module_ctx->dev_ctx_table[parent_idx]); + if (parent == NULL) { + return FWK_E_DATA; + } + + fwk_list_push_tail(&parent->children_list, &clk->child_node); + + status = clk->api->get_state(clk->id, ¤t_state); + if (status == FWK_PENDING) { + FWK_LOG_WARN( + "[CLOCK] Async drivers not supported with clock tree mgmt"); + return FWK_E_SUPPORT; + } else if (status != FWK_SUCCESS) { + return status; + } + + if (current_state != MOD_CLOCK_STATE_STOPPED) { + parent->ref_count++; + } + } + + return FWK_SUCCESS; +} \ No newline at end of file diff --git a/module/clock/src/mod_clock.c b/module/clock/src/mod_clock.c index b774bda13..7d17f3f08 100644 --- a/module/clock/src/mod_clock.c +++ b/module/clock/src/mod_clock.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -312,6 +313,8 @@ static int clock_init(fwk_id_t module_id, unsigned int element_count, module_ctx.config = config; module_ctx.dev_ctx_table = fwk_mm_calloc(element_count, sizeof(struct clock_dev_ctx)); + module_ctx.dev_count = element_count; + return FWK_SUCCESS; } @@ -323,6 +326,8 @@ static int clock_dev_init(fwk_id_t element_id, unsigned int sub_element_count, ctx = &module_ctx.dev_ctx_table[fwk_id_get_element_idx(element_id)]; ctx->config = dev_config; + fwk_list_init(&ctx->children_list); + ctx->id = element_id; return FWK_SUCCESS; } @@ -352,9 +357,9 @@ static int clock_start(fwk_id_t id) int status; struct clock_dev_ctx *ctx; - /* Nothing to be done at the module level */ + /* Clock tree is initialized */ if (!fwk_id_is_type(id, FWK_ID_TYPE_ELEMENT)) { - return FWK_SUCCESS; + return clock_connect_tree(&module_ctx); } ctx = &module_ctx.dev_ctx_table[fwk_id_get_element_idx(id)]; -- GitLab From be7c2dc898d3458fa42982fbaf8282c115454681 Mon Sep 17 00:00:00 2001 From: Leandro Belli Date: Tue, 9 Mar 2021 17:00:18 +0000 Subject: [PATCH 4/8] clock: Add BS_FIRMWARE_HAS_CLOCK_TREE_MGMT flag This patch adds BS_FIRMWARE_HAS_CLOCK_TREE_MGMT to enable or disable clock management tree. Signed-off-by: Leandro Belli Change-Id: I66f81c064190a33c67ac973676440dfd4b38f984 --- framework/CMakeLists.txt | 4 +++ module/clock/src/clock.h | 10 +++++-- module/clock/src/clock_tree_management.c | 35 +++++++++++++----------- module/clock/src/mod_clock.c | 7 +++++ tools/build_system/doc.md | 8 ++++++ tools/build_system/firmware.mk | 6 ++++ tools/build_system/rules.mk | 4 +++ 7 files changed, 55 insertions(+), 19 deletions(-) diff --git a/framework/CMakeLists.txt b/framework/CMakeLists.txt index d46d905b1..c09690e14 100644 --- a/framework/CMakeLists.txt +++ b/framework/CMakeLists.txt @@ -61,6 +61,10 @@ if(CMAKE_BUILD_TYPE STREQUAL "Debug") target_compile_definitions(framework PUBLIC BUILD_MODE_DEBUG) endif() +if(SCP_ENABLE_CLOCK_TREE_MGMT) + target_compile_definitions(framework PUBLIC "BUILD_HAS_CLOCK_TREE_MGMT") +endif() + # # Handle the framework logging filter level. # diff --git a/module/clock/src/clock.h b/module/clock/src/clock.h index 1b3e8b6c6..af24d7922 100644 --- a/module/clock/src/clock.h +++ b/module/clock/src/clock.h @@ -15,9 +15,6 @@ /* Device context */ struct clock_dev_ctx { - /* Identifier of the clock */ - fwk_id_t id; - /* Pointer to the element configuration data */ const struct mod_clock_dev_config *config; @@ -43,6 +40,10 @@ struct clock_dev_ctx { uint32_t cookie; } request; +#ifdef BUILD_HAS_CLOCK_TREE_MGMT + /* Identifier of the clock */ + fwk_id_t id; + /* Parent identifier */ fwk_id_t parent_id; @@ -65,6 +66,7 @@ struct clock_dev_ctx { /* Reference count */ uint32_t ref_count; +#endif }; /* Module context */ @@ -82,8 +84,10 @@ struct clock_ctx { /* Get context helper function */ void clock_get_ctx(fwk_id_t clock_id, struct clock_dev_ctx **ctx); +#ifdef BUILD_HAS_CLOCK_TREE_MGMT /* Connect clock tree interconnecting parent to children nodes */ int clock_connect_tree(struct clock_ctx *module_ctx); +#endif /* * Clock event indexes. diff --git a/module/clock/src/clock_tree_management.c b/module/clock/src/clock_tree_management.c index 83da4365c..59825cf8a 100644 --- a/module/clock/src/clock_tree_management.c +++ b/module/clock/src/clock_tree_management.c @@ -5,24 +5,25 @@ * SPDX-License-Identifier: BSD-3-Clause */ -#include "clock.h" +#ifdef BUILD_HAS_CLOCK_TREE_MGMT +# include "clock.h" -#include +# include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include -#include -#include -#include +# include +# include +# include /* Sub-routine of 'clock_start()' Build the clock tree */ int clock_connect_tree(struct clock_ctx *module_ctx) @@ -65,4 +66,6 @@ int clock_connect_tree(struct clock_ctx *module_ctx) } return FWK_SUCCESS; -} \ No newline at end of file +} + +#endif \ No newline at end of file diff --git a/module/clock/src/mod_clock.c b/module/clock/src/mod_clock.c index 7d17f3f08..9f7cb7a30 100644 --- a/module/clock/src/mod_clock.c +++ b/module/clock/src/mod_clock.c @@ -326,8 +326,11 @@ static int clock_dev_init(fwk_id_t element_id, unsigned int sub_element_count, ctx = &module_ctx.dev_ctx_table[fwk_id_get_element_idx(element_id)]; ctx->config = dev_config; + +#ifdef BUILD_HAS_CLOCK_TREE_MGMT fwk_list_init(&ctx->children_list); ctx->id = element_id; +#endif return FWK_SUCCESS; } @@ -359,7 +362,11 @@ static int clock_start(fwk_id_t id) /* Clock tree is initialized */ if (!fwk_id_is_type(id, FWK_ID_TYPE_ELEMENT)) { +#ifdef BUILD_HAS_CLOCK_TREE_MGMT return clock_connect_tree(&module_ctx); +#else + return FWK_SUCCESS; +#endif } ctx = &module_ctx.dev_ctx_table[fwk_id_get_element_idx(id)]; diff --git a/tools/build_system/doc.md b/tools/build_system/doc.md index f80935ed9..ac4d7a777 100644 --- a/tools/build_system/doc.md +++ b/tools/build_system/doc.md @@ -104,6 +104,8 @@ The following parameters are optional (default is 'no' when omitted): * __BUILD_HAS_SCMI_SENSOR_EVENTS__ - SCMI Sensor trip points. When set to yes, the platform supports the configuration of trip points and generates SCMI notifications on cross-over events for the trip points. +* __BS_FIRMWARE_HAS_CLOCK_TREE_MGMT__ - Clock tree management. When set +to yes, the platform can provide support for clock tree management. The format of the __BS_FIRMWARE_MODULES__ parameter can be seen in the following example: @@ -261,6 +263,12 @@ SCMI Sensor Event Notifications {#section_scmi_sensor_event_notifications} When building a firmware and its dependencies, the BUILD_HAS_SCMI_SENSOR_EVENTS parameter controls if SCMI event notifications are enabled. +Clock Tree Management {#section_clock_tree_management} +=============================== + +When building a firmware and its dependencies, the BUILD_HAS_CLOCK_TREE_MGMT +parameter controls if Clock Tree Management is enabled. + Definitions =========== diff --git a/tools/build_system/firmware.mk b/tools/build_system/firmware.mk index 7f1e8cccc..a689e5cf7 100644 --- a/tools/build_system/firmware.mk +++ b/tools/build_system/firmware.mk @@ -226,6 +226,12 @@ else BUILD_HAS_SCMI_SENSOR_EVENTS := no endif +ifeq ($(BS_FIRMWARE_HAS_CLOCK_TREE_MGMT),yes) + BUILD_HAS_CLOCK_TREE_MGMT := yes +else + BUILD_HAS_CLOCK_TREE_MGMT := no +endif + # Add directories to the list of targets to build LIB_TARGETS_y += $(patsubst %,$(MODULES_DIR)/%/src, \ $(BUILD_STANDARD_MODULES)) diff --git a/tools/build_system/rules.mk b/tools/build_system/rules.mk index e8e287ddd..eb74a7300 100644 --- a/tools/build_system/rules.mk +++ b/tools/build_system/rules.mk @@ -37,6 +37,10 @@ ifeq ($(BUILD_HAS_SCMI_SENSOR_EVENTS),yes) endif endif +ifeq ($(BUILD_HAS_CLOCK_TREE_MGMT),yes) + DEFINES += BUILD_HAS_CLOCK_TREE_MGMT +endif + export AS := $(CC) export LD := $(CC) -- GitLab From 2e4bdfd2fd5e4fee3fd11c4bd7ec955ab58a7f2a Mon Sep 17 00:00:00 2001 From: Leandro Belli Date: Mon, 30 Nov 2020 17:55:15 +0000 Subject: [PATCH 5/8] clock: Add clock management for setting state This patch adds set_state function for clock module using the new clock management framework. Every clock state change is handled as an asynchronous request and the response is returned through a delayed event if only the clock tree is defined. If not it calls driver API straight away. At this stage, driver API for set state is only supported in the synchronous mode. Signed-off-by: Leandro Belli Change-Id: I04c6cb2fb810b59774bd0e9c01c11b8536eab69c --- module/clock/include/mod_clock.h | 2 +- module/clock/src/clock.h | 57 +++++- module/clock/src/clock_tree_management.c | 231 +++++++++++++++++++++++ module/clock/src/mod_clock.c | 52 ++++- 4 files changed, 333 insertions(+), 9 deletions(-) diff --git a/module/clock/include/mod_clock.h b/module/clock/include/mod_clock.h index fe93a87b7..2642f6471 100644 --- a/module/clock/include/mod_clock.h +++ b/module/clock/include/mod_clock.h @@ -572,7 +572,7 @@ enum mod_clock_event_idx { MOD_CLOCK_EVENT_IDX_COUNT }; - /*! +/*! * \brief Request event identifiers. * * \details These identifiers are used by the clients that expect to receive a diff --git a/module/clock/src/clock.h b/module/clock/src/clock.h index af24d7922..76f6c3ef5 100644 --- a/module/clock/src/clock.h +++ b/module/clock/src/clock.h @@ -10,6 +10,7 @@ #include +#include #include #include @@ -59,9 +60,12 @@ struct clock_dev_ctx { struct { /* Number of pending responses */ uint32_t pending_responses; - /* Clock state transition */ uint32_t state; + /* Clock id caller to transition */ + fwk_id_t caller_id; + /* Flag indicating if this clock is the starter of transition */ + bool is_transition_initiator; } state_transition; /* Reference count */ @@ -81,10 +85,46 @@ struct clock_ctx { uint32_t dev_count; }; +/* Set state event parameters */ +struct clock_set_state_params { + uint32_t target_state; + int caller_status; +}; + +/* Set rate event parameters */ +struct clock_set_rate_params { + uint64_t input_rate; +}; + +/* List of state transition management state */ +enum clock_state_transition_stage { + /* This is the idle state */ + CLOCK_STATE_TRANSITION_IDLE, + + /* This state wait for parent run response */ + CLOCK_STATE_TRANSITION_WAIT_PARENT, + + /* Number of states in a transition */ + CLOCK_STATE_TRANSITION_COUNT +}; + /* Get context helper function */ void clock_get_ctx(fwk_id_t clock_id, struct clock_dev_ctx **ctx); +/* Clock request complete function */ +void clock_request_complete( + fwk_id_t dev_id, + struct mod_clock_driver_resp_params *response); + #ifdef BUILD_HAS_CLOCK_TREE_MGMT +/* Clock tree management state machine for setting state */ +int clock_management_process_state(const struct fwk_event *event); + +/* Check if a clock is a single node. It returns true or false whether Clock is + * a single node or not. + */ +bool clock_is_single_node(struct clock_dev_ctx *ctx); + /* Connect clock tree interconnecting parent to children nodes */ int clock_connect_tree(struct clock_ctx *module_ctx); #endif @@ -94,6 +134,11 @@ int clock_connect_tree(struct clock_ctx *module_ctx); */ enum clock_event_idx { CLOCK_EVENT_IDX_RESPONSE = MOD_CLOCK_EVENT_IDX_COUNT, + +#ifdef BUILD_HAS_CLOCK_TREE_MGMT + CLOCK_EVENT_IDX_SET_STATE_PRE_REQUEST, +#endif + CLOCK_EVENT_IDX_COUNT }; @@ -105,4 +150,12 @@ enum clock_event_idx { static const fwk_id_t mod_clock_event_id_response = FWK_ID_EVENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_EVENT_IDX_RESPONSE); -#endif /* CLOCK_H */ +#ifdef BUILD_HAS_CLOCK_TREE_MGMT +/* Identifier of the set state pre-request */ +static const fwk_id_t mod_clock_event_id_set_state_pre_request = + FWK_ID_EVENT_INIT( + FWK_MODULE_IDX_CLOCK, + CLOCK_EVENT_IDX_SET_STATE_PRE_REQUEST); +#endif + +#endif /* CLOCK_H */ \ No newline at end of file diff --git a/module/clock/src/clock_tree_management.c b/module/clock/src/clock_tree_management.c index 59825cf8a..a560da829 100644 --- a/module/clock/src/clock_tree_management.c +++ b/module/clock/src/clock_tree_management.c @@ -25,6 +25,237 @@ # include # include +static int clk_mgmt_send_event_set( + struct clock_set_state_params *params, + fwk_id_t target) +{ + struct fwk_event event; + + event = (struct fwk_event){ + .target_id = target, + .id = mod_clock_event_id_set_state_pre_request, + }; + memcpy(event.params, params, sizeof(struct clock_set_state_params)); + + return fwk_thread_put_event(&event); +} + +static int clk_mgmt_complete_response( + struct clock_dev_ctx *ctx, + struct clock_set_state_params *event_params) +{ + struct mod_clock_driver_resp_params response_params; + + ctx->state_transition.state = CLOCK_STATE_TRANSITION_IDLE; + /* + * It responds to the caller depending if it was other clock or + * if it is the transition initiator. + */ + if (!ctx->state_transition.is_transition_initiator) { + /* + * If requested_state is STOPPED there is no need to send a event to the + * caller. + */ + if (event_params->target_state != MOD_CLOCK_STATE_STOPPED) { + return clk_mgmt_send_event_set( + event_params, ctx->state_transition.caller_id); + } + + return FWK_SUCCESS; + } + + ctx->state_transition.is_transition_initiator = false; + response_params.status = event_params->caller_status; + response_params.value.state = + (enum mod_clock_state)event_params->target_state; + + /* + * This is the last clock dev in the tree, respond to the caller. + */ + clock_request_complete(ctx->id, &response_params); + + return FWK_SUCCESS; +} + +static int clk_mgmt_complete_transition( + struct clock_dev_ctx *ctx, + struct clock_set_state_params *event_params) +{ + int status; + status = ctx->api->set_state( + ctx->config->driver_id, + (enum mod_clock_state)event_params->target_state); + + /* Async drivers not supported with clock tree management */ + if (status == FWK_PENDING) { + FWK_LOG_WARN( + "[CLOCK] Async drivers not supported with clock tree mgmt"); + return FWK_E_SUPPORT; + } + event_params->caller_status = status; + return status; +} + +int clock_management_process_state(const struct fwk_event *event) +{ + struct clock_set_state_params *event_params = + (struct clock_set_state_params *)event->params; + struct clock_dev_ctx *ctx; + enum mod_clock_state current_state; + fwk_id_t target_id; + uint32_t *state; + int status; + + clock_get_ctx(event->target_id, &ctx); + state = &ctx->state_transition.state; + + switch (*state) { + /* + * At this stage when the first event arrives. It saves the caller_id + * and depending on what the transition type it will call its parent + * or children updating the state to the correspondent value. + */ + case CLOCK_STATE_TRANSITION_IDLE: + ctx->state_transition.caller_id = event->source_id; + ctx->state_transition.pending_responses = 0; + status = ctx->api->get_state(ctx->config->driver_id, ¤t_state); + /* Async drivers not supported with clock tree management */ + if (status == FWK_PENDING) { + FWK_LOG_WARN( + "[CLOCK] Async drivers not supported with clock tree mgmt"); + return FWK_E_SUPPORT; + } + if (status != FWK_SUCCESS) { + event_params->caller_status = status; + return clk_mgmt_complete_response(ctx, event_params); + } + + if (event_params->target_state == MOD_CLOCK_STATE_RUNNING) { + if (current_state == event_params->target_state) { + ctx->ref_count++; + return clk_mgmt_complete_response(ctx, event_params); + } + + if (fwk_id_is_equal(ctx->parent_id, FWK_ID_NONE)) { + status = clk_mgmt_complete_transition(ctx, event_params); + if (status == FWK_E_SUPPORT) { + /* + * This should not be reached because. asynchronous drivers + * are not supported. + */ + fwk_assert(status == FWK_E_SUPPORT); + return status; + } else if (status == FWK_SUCCESS) { + ctx->ref_count++; + } + + return clk_mgmt_complete_response(ctx, event_params); + } + + *state = CLOCK_STATE_TRANSITION_WAIT_PARENT; + target_id = ctx->parent_id; + + /* Raise event */ + return clk_mgmt_send_event_set(event_params, target_id); + + } else if (event_params->target_state == MOD_CLOCK_STATE_STOPPED) { + ctx->ref_count--; + + if (ctx->ref_count == 0) { + status = clk_mgmt_complete_transition(ctx, event_params); + if (status == FWK_E_SUPPORT) { + /* + * This should not be reached because. asynchronous drivers + * are not supported. + */ + fwk_assert(status == FWK_E_SUPPORT); + return status; + } + + status = clk_mgmt_complete_response(ctx, event_params); + if (status != FWK_SUCCESS) { + return status; + } + /* + * A sanity check is performed and turn off every unneeded + * clock. If ref_count == 0 it will send an event to its parent + * to decrement their ref_count and turn it off in case it is + * not longer used by an external agent or one of its child. + */ + if (!fwk_id_is_equal(ctx->parent_id, FWK_ID_NONE)) { + return clk_mgmt_send_event_set( + event_params, ctx->parent_id); + } + + return status; + } + + return clk_mgmt_complete_response(ctx, event_params); + } else { + return FWK_E_PARAM; + } + break; + + /* + * This state waits until parent clock completes running state. + */ + case CLOCK_STATE_TRANSITION_WAIT_PARENT: + + /* + * The parent has sent back an event to its child once his clock has + * changed state successfully. + */ + if (!fwk_id_is_equal(event->source_id, ctx->parent_id)) { + /* + * Assume this node is waiting for its parent to switch ON. Before + * the parent respond, the same node received a request from one + * of its children. This statement is going to return error to the + * caller + */ + event_params->caller_status = FWK_E_BUSY; + return clk_mgmt_send_event_set(event_params, event->source_id); + } + if (event_params->caller_status == FWK_SUCCESS) { + status = clk_mgmt_complete_transition(ctx, event_params); + if (status == FWK_E_SUPPORT) { + /* + * This should not be reached because. asynchronous drivers + * are not supported. + */ + fwk_assert(status == FWK_E_SUPPORT); + return status; + } else if (status == FWK_SUCCESS) { + ctx->ref_count++; + } else { + /* + * When transition can not be completed it is returned + * immediately to the parent the status and a STOPPED state + * to revert the state request. + */ + event_params->target_state = MOD_CLOCK_STATE_STOPPED; + status = clk_mgmt_send_event_set(event_params, ctx->parent_id); + if (status != FWK_SUCCESS) { + return status; + } + event_params->target_state = MOD_CLOCK_STATE_RUNNING; + } + } + + return clk_mgmt_complete_response(ctx, event_params); + + default: + return FWK_E_STATE; + } + + return FWK_SUCCESS; +} + +bool clock_is_single_node(struct clock_dev_ctx *ctx) +{ + return fwk_id_is_equal(ctx->parent_id, FWK_ID_NONE) && + fwk_list_is_empty(&ctx->children_list); +} + /* Sub-routine of 'clock_start()' Build the clock tree */ int clock_connect_tree(struct clock_ctx *module_ctx) { diff --git a/module/clock/src/mod_clock.c b/module/clock/src/mod_clock.c index 9f7cb7a30..6ae234536 100644 --- a/module/clock/src/mod_clock.c +++ b/module/clock/src/mod_clock.c @@ -218,22 +218,57 @@ static int clock_set_state(fwk_id_t clock_id, enum mod_clock_state state) int status; struct clock_dev_ctx *ctx; - clock_get_ctx(clock_id, &ctx); +#ifdef BUILD_HAS_CLOCK_TREE_MGMT + struct fwk_event event; + struct clock_set_state_params *event_params; +#endif + clock_get_ctx(clock_id, &ctx); /* Concurrency is not supported */ if (ctx->request.is_ongoing) { return FWK_E_BUSY; } +#ifdef BUILD_HAS_CLOCK_TREE_MGMT + if (ctx->state_transition.state != CLOCK_STATE_TRANSITION_IDLE) { + return FWK_E_BUSY; + } + + if (clock_is_single_node(ctx)) { + status = ctx->api->set_state(ctx->config->driver_id, state); + if (status == FWK_PENDING) { + return create_async_request( + ctx, clock_id, mod_clock_event_id_set_state_request); + } + return status; + } + + status = create_async_request( + ctx, clock_id, mod_clock_event_id_set_state_request); + if (status != FWK_PENDING) { + return status; + } + + event = (struct fwk_event){ + .target_id = clock_id, + .id = mod_clock_event_id_set_state_pre_request, + }; + event_params = (struct clock_set_state_params *)event.params; + event_params->target_state = state; + ctx->state_transition.is_transition_initiator = true; + status = fwk_thread_put_event(&event); + if (status != FWK_SUCCESS) { + return status; + } + return FWK_PENDING; +#else status = ctx->api->set_state(ctx->config->driver_id, state); if (status == FWK_PENDING) { return create_async_request( - ctx, - clock_id, - mod_clock_event_id_set_state_request); - } else { - return status; + ctx, clock_id, mod_clock_event_id_set_state_request); } + return status; +#endif } static int clock_get_state(fwk_id_t clock_id, enum mod_clock_state *state) @@ -645,6 +680,11 @@ static int clock_process_event(const struct fwk_event *event, case MOD_CLOCK_EVENT_IDX_GET_STATE_REQUEST: return process_request_event(event, resp_event); +#ifdef BUILD_HAS_CLOCK_TREE_MGMT + case CLOCK_EVENT_IDX_SET_STATE_PRE_REQUEST: + return clock_management_process_state(event); +#endif + case CLOCK_EVENT_IDX_RESPONSE: return process_response_event(event); -- GitLab From fcb4c846397e6b72e18c405e38aa4ec777aa54ff Mon Sep 17 00:00:00 2001 From: Leandro Belli Date: Tue, 13 Apr 2021 18:04:35 +0100 Subject: [PATCH 6/8] clock: Add clock management for setting rate This patch implements setting rate functionality using the clock management framework. When a clock change its rate it must update children rates in order to keep values updated. This functionality can be allowed having BS_FIRMWARE__HAS_CLOCK_TREE_MGMT build flag and creating update_rate_from_input method for clock driver. Signed-off-by: Leandro Belli Change-Id: I11477086db8c924800f68e30f51715a7bad66460 --- module/clock/include/mod_clock.h | 31 +++++++++++++++ module/clock/src/clock.h | 10 +++++ module/clock/src/clock_tree_management.c | 49 ++++++++++++++++++++++++ module/clock/src/mod_clock.c | 32 +++++++++++++++- 4 files changed, 120 insertions(+), 2 deletions(-) diff --git a/module/clock/include/mod_clock.h b/module/clock/include/mod_clock.h index 2642f6471..4d6f6eade 100644 --- a/module/clock/include/mod_clock.h +++ b/module/clock/include/mod_clock.h @@ -393,6 +393,37 @@ struct mod_clock_drv_api { * \return One of the standard framework error codes. */ int (*process_power_transition)(fwk_id_t clock_id, unsigned int state); + + /*! + * \brief Update the output rate according to the specified input + * value. It is just to keep track of the current input/output. It + * should not be used to change any setting. + * + * \note This function is optional. If it is not needed, the pointer may be + * set to NULL. This function must be synchronous and every status + * return different from FWK_SUCCESS will be handle as an error. + * + * Behaviour example: + * In -> 300MHz + * Out <- 150MHz - /2 divider. + * + * New input: + * In -> 600MHz + * Out <- 300MHz (result) Preserve /2 divider. + * + * \param clock_id Clock device identifier. + * + * \param in_rate Desired input parent rate in Hertz. + * + * \param[out] out_rate The current clock rate in Hertz. + * + * \retval ::FWK_SUCCESS The operation succeeded. + * \return One of the standard framework error codes. + */ + int (*update_input_rate)( + fwk_id_t clock_id, + uint64_t in_rate, + uint64_t *out_rate); }; /*! diff --git a/module/clock/src/clock.h b/module/clock/src/clock.h index 76f6c3ef5..645454f49 100644 --- a/module/clock/src/clock.h +++ b/module/clock/src/clock.h @@ -120,6 +120,9 @@ void clock_request_complete( /* Clock tree management state machine for setting state */ int clock_management_process_state(const struct fwk_event *event); +/* Clock tree management state machine for setting rate */ +int clock_management_process_rate(const struct fwk_event *event); + /* Check if a clock is a single node. It returns true or false whether Clock is * a single node or not. */ @@ -137,6 +140,7 @@ enum clock_event_idx { #ifdef BUILD_HAS_CLOCK_TREE_MGMT CLOCK_EVENT_IDX_SET_STATE_PRE_REQUEST, + CLOCK_EVENT_IDX_SET_RATE_PRE_REQUEST, #endif CLOCK_EVENT_IDX_COUNT @@ -156,6 +160,12 @@ static const fwk_id_t mod_clock_event_id_set_state_pre_request = FWK_ID_EVENT_INIT( FWK_MODULE_IDX_CLOCK, CLOCK_EVENT_IDX_SET_STATE_PRE_REQUEST); + +/* Identifier of the set rate pre-request */ +static const fwk_id_t mod_clock_event_id_set_rate_pre_request = + FWK_ID_EVENT_INIT( + FWK_MODULE_IDX_CLOCK, + CLOCK_EVENT_IDX_SET_RATE_PRE_REQUEST); #endif #endif /* CLOCK_H */ \ No newline at end of file diff --git a/module/clock/src/clock_tree_management.c b/module/clock/src/clock_tree_management.c index a560da829..60d0e6efd 100644 --- a/module/clock/src/clock_tree_management.c +++ b/module/clock/src/clock_tree_management.c @@ -40,6 +40,21 @@ static int clk_mgmt_send_event_set( return fwk_thread_put_event(&event); } +static int clk_mgmt_send_event_rate( + struct clock_set_rate_params *params, + fwk_id_t target) +{ + struct fwk_event event; + + event = (struct fwk_event){ + .target_id = target, + .id = mod_clock_event_id_set_rate_pre_request, + }; + memcpy(event.params, params, sizeof(struct clock_set_rate_params)); + + return fwk_thread_put_event(&event); +} + static int clk_mgmt_complete_response( struct clock_dev_ctx *ctx, struct clock_set_state_params *event_params) @@ -250,6 +265,40 @@ int clock_management_process_state(const struct fwk_event *event) return FWK_SUCCESS; } +int clock_management_process_rate(const struct fwk_event *event) +{ + struct clock_set_rate_params *event_params = + (struct clock_set_rate_params *)event->params; + struct clock_dev_ctx *ctx; + struct clock_dev_ctx *child = NULL; + struct fwk_slist *c_node = NULL; + uint64_t in_rate, out_rate; + int status; + + clock_get_ctx(event->target_id, &ctx); + in_rate = event_params->input_rate; + + /* + * Every child node receive new rate input rate and update its internal + * output rate to match with its internal settings. + */ + FWK_LIST_FOR_EACH( + &ctx->children_list, c_node, struct clock_dev_ctx, child_node, child) + { + if (child->api->update_input_rate != NULL) { + status = child->api->update_input_rate( + child->config->driver_id, in_rate, &out_rate); + if (status != FWK_SUCCESS) { + return status; + } + + event_params->input_rate = out_rate; + clk_mgmt_send_event_rate(event_params, child->id); + } + } + return FWK_SUCCESS; +} + bool clock_is_single_node(struct clock_dev_ctx *ctx) { return fwk_id_is_equal(ctx->parent_id, FWK_ID_NONE) && diff --git a/module/clock/src/mod_clock.c b/module/clock/src/mod_clock.c index 6ae234536..6f6d17b08 100644 --- a/module/clock/src/mod_clock.c +++ b/module/clock/src/mod_clock.c @@ -153,6 +153,11 @@ static int clock_set_rate(fwk_id_t clock_id, uint64_t rate, int status; struct clock_dev_ctx *ctx; +#ifdef BUILD_HAS_CLOCK_TREE_MGMT + struct fwk_event event; + struct clock_set_rate_params *event_params; +#endif + clock_get_ctx(clock_id, &ctx); /* Concurrency is not supported */ @@ -160,15 +165,35 @@ static int clock_set_rate(fwk_id_t clock_id, uint64_t rate, return FWK_E_BUSY; } +#ifdef BUILD_HAS_CLOCK_TREE_MGMT + + status = ctx->api->set_rate(ctx->config->driver_id, rate, round_mode); + if (status == FWK_PENDING) { + return create_async_request( + ctx, clock_id, mod_clock_event_id_set_rate_request); + } + if (clock_is_single_node(ctx)) { + return status; + } + + event = (struct fwk_event){ + .target_id = clock_id, + .id = mod_clock_event_id_set_rate_pre_request, + }; + event_params = (struct clock_set_rate_params *)event.params; + event_params->input_rate = rate; + + return fwk_thread_put_event(&event); +#else status = ctx->api->set_rate(ctx->config->driver_id, rate, round_mode); if (status == FWK_PENDING) { return create_async_request( ctx, clock_id, mod_clock_event_id_set_rate_request); - } else { - return status; } + return status; +#endif } static int clock_get_rate(fwk_id_t clock_id, uint64_t *rate) @@ -683,6 +708,9 @@ static int clock_process_event(const struct fwk_event *event, #ifdef BUILD_HAS_CLOCK_TREE_MGMT case CLOCK_EVENT_IDX_SET_STATE_PRE_REQUEST: return clock_management_process_state(event); + + case CLOCK_EVENT_IDX_SET_RATE_PRE_REQUEST: + return clock_management_process_rate(event); #endif case CLOCK_EVENT_IDX_RESPONSE: -- GitLab From 0760d1a8a252a540b5054ffb96edfafcc053a30e Mon Sep 17 00:00:00 2001 From: Leandro Belli Date: Wed, 14 Apr 2021 18:10:09 +0100 Subject: [PATCH 7/8] mock_clock: update_rate_for_input method implementation. This patch implements update_rate_for_input method to be used for testing clock management framework. It allows to test set_rate functionality. It simply tries to copy input_rate into output_rate value. Signed-off-by: Leandro Belli Change-Id: Ibda369f4cb4067b462081a8770ffd4e7167e9605 --- module/mock_clock/src/mod_mock_clock.c | 30 ++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/module/mock_clock/src/mod_mock_clock.c b/module/mock_clock/src/mod_mock_clock.c index a131077ec..445845b47 100644 --- a/module/mock_clock/src/mod_mock_clock.c +++ b/module/mock_clock/src/mod_mock_clock.c @@ -183,6 +183,35 @@ static int mod_mock_clock_process_power_transition( return FWK_SUCCESS; } +static int mod_mock_clock_update_input_rate( + fwk_id_t clock_id, + uint64_t input_rate, + uint64_t *output_rate) +{ + struct mod_mock_clock_element_ctx *ctx; + const struct mod_mock_clock_rate *rate_entry; + int status; + + ctx = mod_mock_clock_get_ctx(clock_id); + + if (ctx->state == MOD_CLOCK_STATE_STOPPED || !ctx->rate_initialized) { + return FWK_E_STATE; + } + /* + * Performs the same operation as set_rate synchronously + */ + status = get_rate_entry(ctx, input_rate, &rate_entry); + if (status != FWK_SUCCESS) { + return FWK_E_PARAM; + } + + ctx->current_rate_index = rate_entry - ctx->config->rate_table; + + mod_mock_clock_get_rate(clock_id, output_rate); + + return FWK_SUCCESS; +} + static const struct mod_clock_drv_api mod_mock_clock_driver_api = { .set_rate = mod_mock_clock_set_rate, .get_rate = mod_mock_clock_get_rate, @@ -191,6 +220,7 @@ static const struct mod_clock_drv_api mod_mock_clock_driver_api = { .get_state = mod_mock_clock_get_state, .get_range = mod_mock_clock_get_range, .process_power_transition = mod_mock_clock_process_power_transition, + .update_input_rate = mod_mock_clock_update_input_rate, }; static const struct mod_clock_driver_response_api -- GitLab From e82fda744bd93ee45019b124af78be859fef1b6b Mon Sep 17 00:00:00 2001 From: Leandro Belli Date: Thu, 4 Mar 2021 18:03:04 +0000 Subject: [PATCH 8/8] doc/clock: Add documentation for Clock Management Framework This patch adds documentation explaining the purpose and application example to use Clock Management Framework. Signed-off-by: Leandro Belli Change-Id: Ie54437a0c43f22e1ca73a4a060bc74bda270b9f9 --- .../clock/doc/clock_management_framework.md | 312 ++++++++++++++++++ 1 file changed, 312 insertions(+) create mode 100644 module/clock/doc/clock_management_framework.md diff --git a/module/clock/doc/clock_management_framework.md b/module/clock/doc/clock_management_framework.md new file mode 100644 index 000000000..2247c92cf --- /dev/null +++ b/module/clock/doc/clock_management_framework.md @@ -0,0 +1,312 @@ +\ingroup GroupModules Modules +\defgroup GroupClock Clock HAL + + +# Clock Management Framework + + +## Overview + +As platform requirements get more complex the SCP must be able to support the +associated clock topologies. This implementation allows to declare the +available clocks and their association to each other in the Clock Tree +(preferred). +Whenever a state change request is received the clock framework will enable and +disable the relevant clock nodes to fulfil the request. + +## Considerations + +This simple set of rules encapsulates the clock management philosophy. + +### Rules for setting state + +- If one clock in the tree is enabled then all parent clocks in the tree are +enabled. +- If one clock in the tree is disabled, then all children clocks in the tree must be disabled. +- A clock must not be enabled until all the assigned parent clocks in the +clock tree have been enabled. +- A clock must not be disabled if any of its children clocks are enabled. + +--- + +## Setting state operation (enable/disable) + +A clock operation may be initiated either by: +- SCMI request: A standard request defined by the SCMI Clock Protocol. +- Event: A clocks event handler for any operation may initiate a clock +operation on the current clock. + +A clock operation will respond to each request with either of: +- SCMI response: A standard response defined by the SCMI Clock Protocol. +- Event: Every operation event received by the current clock will be ACKed +with a status. + +### Operation example +In the following example we consider enable and disable functionality. +#### Clock tree +``` ++-----------+ +| CLOCK-0 | ++-+---------+ + | + | +------------+ + +--> CLOCK-1 | + +-+----------+ + | + | + | +------------+ + +---> CLOCK-2 | + | +------------+ + | + | +------------+ + +---> CLOCK-3 | + +------------+ +``` + +#### Enable __Clock-2__ + +In this scenario, it is assumed that all clocks before the operation are +__disabled__. + +1. OSPM Agent sends SCMI request to enable Clock-2. +2. Clock-2 sends Pre-Enable Event to Clock-1. +3. Clock-1 sends Pre-Enable Event to Clock-0. +4. Clock-0 enable operation completes and increase internal reference count. +5. Clock-0 sends Post-Enable Event ACK to Clock-1. +6. Clock-1 enable operation completes and increase internal reference count. +7. Clock-1 sends Post-Enable Event ACK to Clock-2. +8. Clock-2 enable operation completes and increase internal reference count. +9. Clock-2 responds to original SCMI request. + +NOTE: In case the enable fails, the active clock node sends a post event to the +parent, so the parent can decrease the reference count, which was incremented +earlier. + +``` + OSPM CLOCK-0 CLOCK-1 CLOCK-2 CLOCK-3 + AGENT + + + + + + + | | | | | +1) +------------------------------->+ | + | | | | | +2) | | +<---------+ | + | | | | | +3) | +<---------+ | | + | | | | | +4) | +---+ | | | + | | | | | | + | +<--+ | | | + | | | | | +5) | +--------->+ | | + | | | | | +6) | | +---+ | | + | | | | | | + | | +<--+ | | + | | | | | +7) | | +--------->+ | + | | | | | +8) | | | +---+ | + | | | | | | + | | | +<--+ | + | | | | | +9) +<-------------------------------+ | + | | | | | + + + + + + +``` + +#### Disable __Clock-1__ - Scenario 1 +In this scenario, it is assumed that all clocks before the operation +are __enabled__. Clock 1 was externally set and it has its reference count +equals to 3. + +1. OSPM Agent sends SCMI request to disable Clock-1 +2. Clock-1 checks if reference count is 0. + - Hence it does not turn off Clock-1. + - Clock-1 decreases its internal reference count. +3. Clock-1 responds to original SCMI request. + + +``` + OSPM CLOCK-0 CLOCK-1 CLOCK-2 CLOCK-3 + AGENT + + + + + + + | | | | | +1) +-------------------->+ | | + | | | | | +2) | | +---+ | | + | | | | | | + | | +<--+ | | + | | | | | +3) +<--------------------+ | | + | | | | | + + + + + + + +``` + +#### Disable __Clock-2__ - Scenario 2 +In this scenario is assumed that clocks 0, 1 and 2 are __enabled__ before the +operation and clock 3 is disabled. Clock 2 was externally set and +it has its reference count equals to 1. + + +1. OSPM Agent sends SCMI request to disable Clock-2 +2. Clock-2 decreases internal reference count and checks if reference count +is 0. + - Clock-2 disable operation completes. +3. Clock-2 responds to original SCMI request. +4. Clock-2 sends Post-Disable Event to Clock-1. +5. Clock-1 decrements its reference count. It has reference count equal to 0. + - Clock-1 disable operation completes. +6. Clock-1 sends Post-Disable Event to Clock-0. +7. Clock-0 decrements its reference count. It has reference count equal to 0. + - Clock-0 disable operation completes. + +``` + OSPM CLOCK-0 CLOCK-1 CLOCK-2 CLOCK-3 + AGENT + + + + + + + | | | | | +1) +------------------------------->+ | + | | | | | +2) | | | +---+ | + | | | | | | + | | | +<--+ | + | | | | | +3) +<-------------------------------+ | + | | | | | +4) | | +<---------+ | + | | | | | +5) | | +---+ | | + | | | | | | + | | +<--+ | | + | | | | | +6) | +<---------+ | | + | | | | | +7) | +---+ | | | + | | | | | | + | +<--+ | | | + | | | | | + + + + + + + +``` + +## Setting rate operation + +A clock set rate operation may be initiated by a SCMI request. A clock operation +will respond to each request with a SCMI response. +During this operation the selected clock node will notify every children clock +with its new output rate (which is the new input rate for its children). Later, +every child clock can decide how to behave accordingly. + +### Operation example +#### Clock arrangement + +``` ++-----------+ +| CLOCK-0 | ++-+---------+ + | + | +------------+ + +--> CLOCK-1 | + +-+----------+ + | + | + | +------------+ + +---> CLOCK-2 | + +------------+ +``` +#### Initial rates + +* Clock-0 @ 300MHz +* Clock-1 = A/3 +* Clock-2 = B/2 + +``` ++-----------+ +| CLOCK-0 | @ 300MHz ++-+---------+ + | + | +------------+ + +--> CLOCK-1 | @ 100MHz + +-+----------+ + | + | + | +------------+ + +---> CLOCK-2 | @ 50MHz + +------------+ +``` +1. OSPM Agent sends SCMI request to set Clock-0 @ 180MHz. +2. Clock-0 responds to original SCMI request. +3. Clock-0 sends an Event to Clock-1 with its new rate. +4. Clock-1 updates its output rate. Clock-1 @ 60MHz. +5. Clock-1 sends an Event to Clock-2 with its new rate. +6. Clock-2 updates its output rate. Clock-2 @ 30MHz. + +## Limitations + +At this time, the clock tree management only supports synchronous drivers. Using +asynchronous drivers terminate execution. It is not recommended to use at this +time. + +## Appendix + +### Configuration example + +To configure a clock tree as it is described above. The build flag +```BS_FIRMWARE_HAS_CLOCK_TREE_MGMT``` should be set and ```parent_id``` +should be defined. + +```config_mod_clock.c``` + +```C +static const struct fwk_element clock_dev_desc_table[] = { +... + [CLOCK_IDX_MOCK_0] = + { + .name = "MOCK_CLOCK_0", + .data = &((struct mod_clock_dev_config){ + ... + }), + }, + [CLOCK_IDX_MOCK_1] = + { + .name = "MOCK_CLOCK_1", + .data = &((struct mod_clock_dev_config){ + ... + .parent_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_IDX_MOCK_0), + }), + }, + [CLOCK_IDX_MOCK_2] = + { + .name = "MOCK_CLOCK_2", + .data = &((struct mod_clock_dev_config){ + ... + .parent_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_IDX_MOCK_1), + }), + }, + [CLOCK_IDX_MOCK_3] = + { + .name = "MOCK_CLOCK_3", + .data = &((struct mod_clock_dev_config){ + ... + .parent_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_IDX_MOCK_1), + }), + }, +... +}; +``` + +```firmware.mk``` + +``` +... +BS_FIRMWARE_HAS_CLOCK_TREE_MGMT := yes +... +``` + +NOTE: There are no limitations on the clock exposed via SCMI (with respect to +their position on the tree). Every clock node can be exposed to agents to +controlled externally. \ No newline at end of file -- GitLab