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