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, ¤t_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, ¤t_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