1 /*
2 * Arm SCP/MCP Software
3 * Copyright (c) 2022-2023, Linaro Limited and Contributors. All rights
4 * reserved.
5 *
6 * SPDX-License-Identifier: BSD-3-Clause
7 *
8 * Description:
9 * Interface SCP-firmware clock module with OP-TEE clock resources.
10 */
11
12 #include <fwk_macros.h>
13 #include <fwk_mm.h>
14 #include <fwk_module.h>
15 #include <fwk_log.h>
16
17 #include <mod_clock.h>
18 #include <mod_optee_clock.h>
19
20 #include <compiler.h>
21 #include <drivers/clk.h>
22 #include <tee_api_types.h>
23
24 #include <stdbool.h>
25
26 #define MOD_NAME "[SCMI CLOCK] "
27
28 /* OP-TEE clock device context */
29 struct optee_clock_dev_ctx {
30 struct clk *clk;
31 bool enabled;
32 };
33
34 /* OP-TEE clock module context */
35 struct optee_clock_module_ctx {
36 struct optee_clock_dev_ctx *dev_ctx;
37 unsigned int dev_count;
38 };
39
40 static struct optee_clock_module_ctx module_ctx;
41
elt_id_to_ctx(fwk_id_t dev_id)42 static struct optee_clock_dev_ctx *elt_id_to_ctx(fwk_id_t dev_id)
43 {
44 if (!fwk_module_is_valid_element_id(dev_id)) {
45 return NULL;
46 }
47
48 return module_ctx.dev_ctx + fwk_id_get_element_idx(dev_id);
49 }
50
is_exposed(struct optee_clock_dev_ctx * ctx)51 static bool is_exposed(struct optee_clock_dev_ctx *ctx)
52 {
53 return ctx->clk != NULL;
54 }
55
56 /*
57 * Clock driver API functions
58 */
get_rate(fwk_id_t dev_id,uint64_t * rate)59 static int get_rate(fwk_id_t dev_id, uint64_t *rate)
60 {
61 struct optee_clock_dev_ctx *ctx = elt_id_to_ctx(dev_id);
62
63 if ((ctx == NULL) || (rate == NULL)) {
64 return FWK_E_PARAM;
65 }
66
67 if (!is_exposed(ctx)) {
68 *rate = 0;
69 return FWK_SUCCESS;
70 }
71
72 *rate = clk_get_rate(ctx->clk);
73
74 FWK_LOG_DEBUG(
75 MOD_NAME "SCMI optee_clock (%u/\"%s\"): clk_get_rate() = %" PRIu64,
76 fwk_id_get_element_idx(dev_id),
77 clk_get_name(ctx->clk),
78 *rate);
79
80 return FWK_SUCCESS;
81 }
82
set_state(fwk_id_t dev_id,enum mod_clock_state state)83 static int set_state(fwk_id_t dev_id, enum mod_clock_state state)
84 {
85 struct optee_clock_dev_ctx *ctx = elt_id_to_ctx(dev_id);
86
87 if (ctx == NULL) {
88 return FWK_E_PARAM;
89 }
90
91 switch (state) {
92 case MOD_CLOCK_STATE_STOPPED:
93 case MOD_CLOCK_STATE_RUNNING:
94 break;
95 default:
96 return FWK_E_PARAM;
97 }
98
99 if (!is_exposed(ctx)) {
100 if (state == MOD_CLOCK_STATE_STOPPED) {
101 return FWK_SUCCESS;
102 } else {
103 return FWK_E_ACCESS;
104 }
105 }
106
107 if (state == MOD_CLOCK_STATE_STOPPED) {
108 if (ctx->enabled) {
109 FWK_LOG_DEBUG(
110 MOD_NAME "SCMI optee_clock (%u/\"%s\") disable",
111 fwk_id_get_element_idx(dev_id),
112 clk_get_name(ctx->clk));
113
114 clk_disable(ctx->clk);
115 ctx->enabled = false;
116 } else {
117 FWK_LOG_DEBUG(
118 MOD_NAME "SCMI optee_clock (%u/\"%s\") is already OFF",
119 fwk_id_get_element_idx(dev_id),
120 clk_get_name(ctx->clk));
121 }
122 } else {
123 if (!ctx->enabled) {
124 FWK_LOG_DEBUG(
125 MOD_NAME "SCMI optee_clock (%u/\"%s\") enable",
126 fwk_id_get_element_idx(dev_id),
127 clk_get_name(ctx->clk));
128
129 clk_enable(ctx->clk);
130 ctx->enabled = true;
131 } else {
132 FWK_LOG_DEBUG(
133 MOD_NAME "SCMI optee_clock (%u/\"%s\") is already ON",
134 fwk_id_get_element_idx(dev_id),
135 clk_get_name(ctx->clk));
136 }
137 }
138
139 return FWK_SUCCESS;
140 }
141
get_state(fwk_id_t dev_id,enum mod_clock_state * state)142 static int get_state(fwk_id_t dev_id, enum mod_clock_state *state)
143 {
144 struct optee_clock_dev_ctx *ctx = elt_id_to_ctx(dev_id);
145
146 if ((ctx == NULL) || (state == NULL)) {
147 return FWK_E_PARAM;
148 }
149
150 if (!is_exposed(ctx)) {
151 *state = MOD_CLOCK_STATE_STOPPED;
152 return FWK_SUCCESS;
153 }
154
155 if (ctx->enabled) {
156 *state = MOD_CLOCK_STATE_RUNNING;
157 } else {
158 *state = MOD_CLOCK_STATE_STOPPED;
159 }
160
161 FWK_LOG_DEBUG(
162 MOD_NAME "SCMI optee_clock (%u/\"%s\") is %s",
163 fwk_id_get_element_idx(dev_id),
164 clk_get_name(ctx->clk),
165 *state == MOD_CLOCK_STATE_STOPPED ? "off" : "on");
166
167 return FWK_SUCCESS;
168 }
169
get_range(fwk_id_t dev_id,struct mod_clock_range * range)170 static int get_range(fwk_id_t dev_id, struct mod_clock_range *range)
171 {
172 struct optee_clock_dev_ctx *ctx = elt_id_to_ctx(dev_id);
173 unsigned long rate;
174 size_t rate_count;
175 size_t rate_idx;
176 TEE_Result res;
177
178 if ((ctx == NULL) || (range == NULL)) {
179 return FWK_E_PARAM;
180 }
181
182 range->rate_type = MOD_CLOCK_RATE_TYPE_DISCRETE;
183
184 if (!is_exposed(ctx)) {
185 range->min = 0;
186 range->max = 0;
187 range->rate_count = 1;
188
189 return FWK_SUCCESS;
190 }
191
192 res = clk_get_rates_array(ctx->clk, 0, NULL, &rate_count);
193 if (res == TEE_ERROR_NOT_SUPPORTED) {
194 range->min = clk_get_rate(ctx->clk);
195 range->max = range->min;
196 range->rate_count = 1;
197
198 return FWK_SUCCESS;
199 }
200
201 range->rate_type = MOD_CLOCK_RATE_TYPE_DISCRETE;
202 range->rate_count = rate_count;
203 range->min = UINT64_MAX;
204 range->max = 0;
205
206 for (rate_idx = 0; rate_idx < rate_count; rate_idx++) {
207 size_t count = 1;
208
209 res = clk_get_rates_array(ctx->clk, rate_idx, &rate, &count);
210 fwk_assert(!res && count == 1);
211
212 if (rate > range->max) {
213 range->max = rate;
214 } else if (rate < range->min) {
215 range->min = rate;
216 }
217 }
218
219 return FWK_SUCCESS;
220 }
221
set_rate(fwk_id_t dev_id,uint64_t rate,enum mod_clock_round_mode round_mode)222 static int set_rate(fwk_id_t dev_id, uint64_t rate,
223 enum mod_clock_round_mode round_mode)
224 {
225 struct optee_clock_dev_ctx *ctx = elt_id_to_ctx(dev_id);
226 TEE_Result res;
227
228 if (ctx == NULL) {
229 return FWK_E_PARAM;
230 }
231
232 if (!is_exposed(ctx)) {
233 return FWK_E_ACCESS;
234 }
235
236 res = clk_set_rate(ctx->clk, rate);
237 if (res == TEE_ERROR_NOT_SUPPORTED) {
238 return FWK_E_SUPPORT;
239 }
240
241 FWK_LOG_DEBUG(
242 MOD_NAME "SCMI optee_clock (%u/\"%s\"): rate = %" PRIu64,
243 fwk_id_get_element_idx(dev_id),
244 clk_get_name(ctx->clk),
245 rate);
246
247 return FWK_SUCCESS;
248 }
249
get_rate_from_index(fwk_id_t dev_id,unsigned int rate_index,uint64_t * rate)250 static int get_rate_from_index(fwk_id_t dev_id,
251 unsigned int rate_index, uint64_t *rate)
252 {
253 struct optee_clock_dev_ctx *ctx = elt_id_to_ctx(dev_id);
254 unsigned long rate_ul;
255 size_t rate_count;
256 TEE_Result res;
257
258 if ((ctx == NULL) || (rate == NULL)) {
259 return FWK_E_PARAM;
260 }
261
262 if (!is_exposed(ctx)) {
263 *rate = 0;
264 return FWK_SUCCESS;
265 }
266
267 res = clk_get_rates_array(ctx->clk, 0, NULL, &rate_count);
268 if (res == TEE_ERROR_NOT_SUPPORTED) {
269 if (rate_index > 0) {
270 return FWK_E_PARAM;
271 }
272
273 *rate = clk_get_rate(ctx->clk);
274 return FWK_SUCCESS;
275 }
276
277 if (rate_index > rate_count) {
278 return FWK_E_PARAM;
279 }
280
281 rate_count = 1;
282 res = clk_get_rates_array(ctx->clk, rate_index, &rate_ul, &rate_count);
283 fwk_assert(!res && rate_count == 1);
284 *rate = rate_ul;
285
286 FWK_LOG_DEBUG(
287 MOD_NAME "SCMI optee_clock (%u/\"%s\"): rate(index %u) = %lu",
288 fwk_id_get_element_idx(dev_id),
289 clk_get_name(ctx->clk),
290 rate_index,
291 rate_ul);
292
293 return FWK_SUCCESS;
294 }
295
stub_process_power_transition(fwk_id_t dev_id,unsigned int state)296 static int stub_process_power_transition(fwk_id_t dev_id, unsigned int state)
297 {
298 return FWK_E_SUPPORT;
299 }
300
stub_pending_power_transition(fwk_id_t dev_id,unsigned int current_state,unsigned int next_state)301 static int stub_pending_power_transition(fwk_id_t dev_id,
302 unsigned int current_state,
303 unsigned int next_state)
304 {
305 return FWK_E_SUPPORT;
306 }
307
308 static const struct mod_clock_drv_api api_optee_clock = {
309 .get_rate = get_rate,
310 .set_state = set_state,
311 .get_state = get_state,
312 .get_range = get_range,
313 .get_rate_from_index = get_rate_from_index,
314 .set_rate = set_rate,
315 /* Not supported */
316 .process_power_transition = stub_process_power_transition,
317 .process_pending_power_transition = stub_pending_power_transition,
318 };
319
320 /*
321 * Framework handler functions
322 */
323
optee_clock_init(fwk_id_t module_id,unsigned int count,const void * data)324 static int optee_clock_init(fwk_id_t module_id, unsigned int count,
325 const void *data)
326 {
327 if (count == 0) {
328 return FWK_SUCCESS;
329 }
330
331 module_ctx.dev_count = count;
332 module_ctx.dev_ctx = fwk_mm_calloc(count, sizeof(*module_ctx.dev_ctx));
333
334 return FWK_SUCCESS;
335 }
336
optee_clock_element_init(fwk_id_t element_id,unsigned int dev_count,const void * data)337 static int optee_clock_element_init(fwk_id_t element_id, unsigned int dev_count,
338 const void *data)
339 {
340 struct optee_clock_dev_ctx *ctx = elt_id_to_ctx(element_id);
341 const struct mod_optee_clock_config *config = data;
342 TEE_Result res;
343
344 ctx->clk = config->clk;
345 if (ctx->clk) {
346 ctx->enabled = config->default_enabled;
347
348 if (ctx->enabled) {
349 res = clk_enable(ctx->clk);
350 if (res) {
351 return FWK_E_DEVICE;
352 }
353 }
354 }
355
356 return FWK_SUCCESS;
357 }
358
optee_clock_process_bind_request(fwk_id_t requester_id,fwk_id_t id,fwk_id_t api_type,const void ** api)359 static int optee_clock_process_bind_request(fwk_id_t requester_id, fwk_id_t id,
360 fwk_id_t api_type, const void **api)
361 {
362 *api = &api_optee_clock;
363
364 return FWK_SUCCESS;
365 }
366
367 const struct fwk_module module_optee_clock = {
368 .type = FWK_MODULE_TYPE_DRIVER,
369 .api_count = 1,
370 .event_count = 0,
371 .init = optee_clock_init,
372 .element_init = optee_clock_element_init,
373 .process_bind_request = optee_clock_process_bind_request,
374 };
375