1 /*
2  * Arm SCP/MCP Software
3  * Copyright (c) 2021-2022, Arm Limited and Contributors. All rights reserved.
4  *
5  * SPDX-License-Identifier: BSD-3-Clause
6  */
7 
8 #ifdef BUILD_HAS_CLOCK_TREE_MGMT
9 #    include "clock.h"
10 
11 #    include <mod_clock.h>
12 
13 #    include <fwk_assert.h>
14 #    include <fwk_core.h>
15 #    include <fwk_event.h>
16 #    include <fwk_id.h>
17 #    include <fwk_list.h>
18 #    include <fwk_log.h>
19 #    include <fwk_mm.h>
20 #    include <fwk_module.h>
21 #    include <fwk_notification.h>
22 #    include <fwk_status.h>
23 
24 #    include <stdbool.h>
25 #    include <stddef.h>
26 #    include <stdint.h>
27 
clk_mgmt_send_event_set(struct clock_set_state_params * params,fwk_id_t target)28 static int clk_mgmt_send_event_set(
29     struct clock_set_state_params *params,
30     fwk_id_t target)
31 {
32     struct fwk_event event;
33 
34     event = (struct fwk_event){
35         .target_id = target,
36         .id = mod_clock_event_id_set_state_pre_request,
37     };
38     memcpy(event.params, params, sizeof(struct clock_set_state_params));
39 
40     return fwk_put_event(&event);
41 }
42 
clk_mgmt_send_event_rate(struct clock_set_rate_params * params,fwk_id_t target)43 static int clk_mgmt_send_event_rate(
44     struct clock_set_rate_params *params,
45     fwk_id_t target)
46 {
47     struct fwk_event event;
48 
49     event = (struct fwk_event){
50         .target_id = target,
51         .id = mod_clock_event_id_set_rate_pre_request,
52     };
53     memcpy(event.params, params, sizeof(struct clock_set_rate_params));
54 
55     return fwk_put_event(&event);
56 }
57 
clk_mgmt_complete_response(struct clock_dev_ctx * ctx,struct clock_set_state_params * event_params)58 static int clk_mgmt_complete_response(
59     struct clock_dev_ctx *ctx,
60     struct clock_set_state_params *event_params)
61 {
62     struct mod_clock_driver_resp_params response_params;
63 
64     ctx->state_transition.state = CLOCK_STATE_TRANSITION_IDLE;
65     /*
66      * It responds to the caller depending if it was other clock or
67      * if it is the transition initiator.
68      */
69     if (!ctx->state_transition.is_transition_initiator) {
70         /*
71          * If requested_state is STOPPED there is no need to send a event to the
72          * caller.
73          */
74         if (event_params->target_state != MOD_CLOCK_STATE_STOPPED) {
75             return clk_mgmt_send_event_set(
76                 event_params, ctx->state_transition.caller_id);
77         }
78 
79         return FWK_SUCCESS;
80     }
81 
82     ctx->state_transition.is_transition_initiator = false;
83     response_params.status = event_params->caller_status;
84     response_params.value.state =
85         (enum mod_clock_state)event_params->target_state;
86 
87     /*
88      * This is the last clock dev in the tree, respond to the caller.
89      */
90     clock_request_complete(ctx->id, &response_params);
91 
92     return FWK_SUCCESS;
93 }
94 
clk_mgmt_complete_transition(struct clock_dev_ctx * ctx,struct clock_set_state_params * event_params)95 static int clk_mgmt_complete_transition(
96     struct clock_dev_ctx *ctx,
97     struct clock_set_state_params *event_params)
98 {
99     int status;
100     status = ctx->api->set_state(
101         ctx->config->driver_id,
102         (enum mod_clock_state)event_params->target_state);
103 
104     /* Async drivers not supported with clock tree management */
105     if (status == FWK_PENDING) {
106         FWK_LOG_WARN(
107             "[CLOCK] Async drivers not supported with clock tree mgmt");
108         return FWK_E_SUPPORT;
109     }
110     event_params->caller_status = status;
111     return status;
112 }
113 
clock_management_process_state(const struct fwk_event * event)114 int clock_management_process_state(const struct fwk_event *event)
115 {
116     struct clock_set_state_params *event_params =
117         (struct clock_set_state_params *)event->params;
118     struct clock_dev_ctx *ctx;
119     enum mod_clock_state current_state;
120     fwk_id_t target_id;
121     uint32_t *state;
122     int status;
123 
124     clock_get_ctx(event->target_id, &ctx);
125     state = &ctx->state_transition.state;
126 
127     switch (*state) {
128     /*
129      * At this stage when the first event arrives. It saves the caller_id
130      * and depending on what the transition type it will call its parent
131      * or children updating the state to the correspondent value.
132      */
133     case CLOCK_STATE_TRANSITION_IDLE:
134         ctx->state_transition.caller_id = event->source_id;
135         ctx->state_transition.pending_responses = 0;
136         status = ctx->api->get_state(ctx->config->driver_id, &current_state);
137         /* Async drivers not supported with clock tree management */
138         if (status == FWK_PENDING) {
139             FWK_LOG_WARN(
140                 "[CLOCK] Async drivers not supported with clock tree mgmt");
141             return FWK_E_SUPPORT;
142         }
143         if (status != FWK_SUCCESS) {
144             event_params->caller_status = status;
145             return clk_mgmt_complete_response(ctx, event_params);
146         }
147 
148         if (event_params->target_state == MOD_CLOCK_STATE_RUNNING) {
149             if (current_state == event_params->target_state) {
150                 ctx->ref_count++;
151                 return clk_mgmt_complete_response(ctx, event_params);
152             }
153 
154             if (fwk_id_is_equal(ctx->parent_id, FWK_ID_NONE)) {
155                 status = clk_mgmt_complete_transition(ctx, event_params);
156 
157                 /*
158                  * This should not be reached because. asynchronous drivers
159                  * are not supported.
160                  */
161                 fwk_assert(status != FWK_E_SUPPORT);
162 
163                 if (status == FWK_E_SUPPORT) {
164                     return status;
165                 } else if (status == FWK_SUCCESS) {
166                     ctx->ref_count++;
167                 }
168 
169                 return clk_mgmt_complete_response(ctx, event_params);
170             }
171 
172             *state = CLOCK_STATE_TRANSITION_WAIT_PARENT;
173             target_id = ctx->parent_id;
174 
175             /* Raise event */
176             return clk_mgmt_send_event_set(event_params, target_id);
177 
178         } else if (event_params->target_state == MOD_CLOCK_STATE_STOPPED) {
179             ctx->ref_count--;
180 
181             if (ctx->ref_count == 0) {
182                 status = clk_mgmt_complete_transition(ctx, event_params);
183 
184                 /*
185                  * This should not be reached because. asynchronous drivers
186                  * are not supported.
187                  */
188                 fwk_assert(status != FWK_E_SUPPORT);
189 
190                 if (status == FWK_E_SUPPORT) {
191                     return status;
192                 }
193 
194                 status = clk_mgmt_complete_response(ctx, event_params);
195                 if (status != FWK_SUCCESS) {
196                     return status;
197                 }
198                 /*
199                  * A sanity check is performed and turn off every unneeded
200                  * clock. If ref_count == 0 it will send an event to its parent
201                  * to decrement their ref_count and turn it off in case it is
202                  * not longer used by an external agent or one of its child.
203                  */
204                 if (!fwk_id_is_equal(ctx->parent_id, FWK_ID_NONE)) {
205                     return clk_mgmt_send_event_set(
206                         event_params, ctx->parent_id);
207                 }
208 
209                 return status;
210             }
211 
212             return clk_mgmt_complete_response(ctx, event_params);
213         } else {
214             return FWK_E_PARAM;
215         }
216         break;
217 
218     /*
219      * This state waits until parent clock completes running state.
220      */
221     case CLOCK_STATE_TRANSITION_WAIT_PARENT:
222 
223         /*
224          * The parent has sent back an event to its child once their clock has
225          * changed state successfully.
226          */
227         if (!fwk_id_is_equal(event->source_id, ctx->parent_id)) {
228             /*
229              * Assume this node is waiting for its parent to switch ON. Before
230              * the parent respond, the same node received a request from one
231              * of its children. This statement is going to return error to the
232              * caller
233              */
234             event_params->caller_status = FWK_E_BUSY;
235             return clk_mgmt_send_event_set(event_params, event->source_id);
236         }
237         if (event_params->caller_status == FWK_SUCCESS) {
238             status = clk_mgmt_complete_transition(ctx, event_params);
239 
240             /*
241              * This should not be reached because. asynchronous drivers
242              * are not supported.
243              */
244             fwk_assert(status != FWK_E_SUPPORT);
245 
246             if (status == FWK_E_SUPPORT) {
247                 return status;
248             } else if (status == FWK_SUCCESS) {
249                 ctx->ref_count++;
250             } else {
251                 /*
252                  * When transition can not be completed it is returned
253                  * immediately to the parent the status and a STOPPED state
254                  * to revert the state request.
255                  */
256                 event_params->target_state = MOD_CLOCK_STATE_STOPPED;
257                 status = clk_mgmt_send_event_set(event_params, ctx->parent_id);
258                 if (status != FWK_SUCCESS) {
259                     return status;
260                 }
261                 event_params->target_state = MOD_CLOCK_STATE_RUNNING;
262             }
263         }
264 
265         return clk_mgmt_complete_response(ctx, event_params);
266 
267     default:
268         return FWK_E_STATE;
269     }
270 
271     return FWK_SUCCESS;
272 }
273 
clock_management_process_rate(const struct fwk_event * event)274 int clock_management_process_rate(const struct fwk_event *event)
275 {
276     struct clock_set_rate_params *event_params =
277         (struct clock_set_rate_params *)event->params;
278     struct clock_dev_ctx *ctx;
279     struct clock_dev_ctx *child = NULL;
280     struct fwk_slist *c_node = NULL;
281     uint64_t in_rate, out_rate;
282     int status;
283 
284     clock_get_ctx(event->target_id, &ctx);
285     in_rate = event_params->input_rate;
286 
287     /*
288      * Every child node receive new rate input rate and update its internal
289      * output rate to match with its internal settings.
290      */
291     FWK_LIST_FOR_EACH(
292         &ctx->children_list, c_node, struct clock_dev_ctx, child_node, child)
293     {
294         if (child->api->update_input_rate != NULL) {
295             status = child->api->update_input_rate(
296                 child->config->driver_id, in_rate, &out_rate);
297             if (status != FWK_SUCCESS) {
298                 return status;
299             }
300 
301             event_params->input_rate = out_rate;
302             clk_mgmt_send_event_rate(event_params, child->id);
303         }
304     }
305     return FWK_SUCCESS;
306 }
307 
clock_is_single_node(struct clock_dev_ctx * ctx)308 bool clock_is_single_node(struct clock_dev_ctx *ctx)
309 {
310     return fwk_id_is_equal(ctx->parent_id, FWK_ID_NONE) &&
311         fwk_list_is_empty(&ctx->children_list);
312 }
313 
314 /* Sub-routine of 'clock_start()' Build the clock tree */
clock_connect_tree(struct clock_ctx * module_ctx)315 int clock_connect_tree(struct clock_ctx *module_ctx)
316 {
317     unsigned int clk_idx, parent_idx;
318     int status;
319     enum mod_clock_state current_state;
320     struct clock_dev_ctx *clk, *parent;
321 
322     for (clk_idx = 0; clk_idx < module_ctx->dev_count; clk_idx++) {
323         clk = &(module_ctx->dev_ctx_table[clk_idx]);
324 
325         if (!fwk_optional_id_is_defined(clk->config->parent_id)) {
326             clk->parent_id = FWK_ID_NONE;
327             continue;
328         }
329 
330         clk->parent_id = clk->config->parent_id;
331         parent_idx = fwk_id_get_element_idx(clk->parent_id);
332 
333         parent = &(module_ctx->dev_ctx_table[parent_idx]);
334         if (parent == NULL) {
335             return FWK_E_DATA;
336         }
337 
338         fwk_list_push_tail(&parent->children_list, &clk->child_node);
339 
340         status = clk->api->get_state(clk->config->driver_id, &current_state);
341         if (status == FWK_PENDING) {
342             FWK_LOG_WARN(
343                 "[CLOCK] Async drivers not supported with clock tree mgmt");
344             return FWK_E_SUPPORT;
345         } else if (status != FWK_SUCCESS) {
346             return status;
347         }
348 
349         if (current_state != MOD_CLOCK_STATE_STOPPED) {
350             parent->ref_count++;
351         }
352     }
353 
354     return FWK_SUCCESS;
355 }
356 
357 #endif
358