1 /*
2  * Arm SCP/MCP Software
3  * Copyright (c) 2019-2021, Arm Limited and Contributors. All rights reserved.
4  *
5  * SPDX-License-Identifier: BSD-3-Clause
6  */
7 
8 #include "juno_clock.h"
9 #include "juno_id.h"
10 #include "juno_scc.h"
11 #include "system_clock.h"
12 
13 #include <mod_clock.h>
14 #include <mod_juno_hdlcd.h>
15 
16 #include <fwk_assert.h>
17 #include <fwk_id.h>
18 #include <fwk_log.h>
19 #include <fwk_mm.h>
20 #include <fwk_module.h>
21 #include <fwk_module_idx.h>
22 #include <fwk_status.h>
23 
24 #include <inttypes.h>
25 #include <stddef.h>
26 #include <stdint.h>
27 
28 /*
29  * This driver supports the following policy with regards to how both HDLCDs
30  * share the PLL:
31  *
32  * Shared:
33  * When both HDLCDs require different frequencies that can only be generated
34  * using the PLL, then the last HDLCD requesting the new frequency will succeed.
35  * This implies that both HDLCDs will have the same frequency when using the
36  * PLL. Due to this constraint, the module assumes that the HDLCD clocks have
37  * the same rate limits.
38  */
39 
40 struct juno_hdlcd_dev_ctx {
41     const struct mod_juno_hdlcd_dev_config *config;
42     const struct mod_clock_driver_response_api *driver_response_api;
43     const struct mod_juno_hdlcd_drv_api *driver_api;
44     int index;
45 };
46 
47 struct juno_hdlcd_ctx {
48     const struct mod_juno_hdlcd_config *config;
49 
50     /* Shared PLL rate value */
51     uint32_t current_pll_rate;
52 
53     /*
54      * Identifier of the clock for which the async request is on-going.
55      */
56     fwk_id_t request_clock_id;
57 };
58 
59 static struct juno_hdlcd_dev_ctx *ctx_table;
60 static struct juno_hdlcd_ctx module_ctx;
61 
62 static enum juno_idx_platform platform;
63 
64 /*
65  * Helper functions
66  */
67 
round_rate(struct juno_hdlcd_dev_ctx * ctx,enum mod_clock_round_mode round_mode,uint32_t rate,uint32_t * rounded_rate)68 static int round_rate(struct juno_hdlcd_dev_ctx *ctx,
69                      enum mod_clock_round_mode round_mode,
70                      uint32_t rate,
71                      uint32_t *rounded_rate)
72 {
73     uint32_t round_up = (uint32_t)(FWK_ALIGN_NEXT(rate, ctx->config->min_step));
74     uint32_t round_down =
75         (uint32_t)(FWK_ALIGN_PREVIOUS(rate, ctx->config->min_step));
76 
77     switch (round_mode) {
78     case MOD_CLOCK_ROUND_MODE_NONE:
79         *rounded_rate = rate;
80         break;
81 
82     case MOD_CLOCK_ROUND_MODE_NEAREST:
83         /* Select the rounded rate that is closest to the given rate */
84         if ((rate - round_down) > (round_up - rate)) {
85             *rounded_rate = round_up;
86         } else {
87             *rounded_rate = round_down;
88         }
89         break;
90 
91     case MOD_CLOCK_ROUND_MODE_DOWN:
92         *rounded_rate = round_down;
93         break;
94 
95     case MOD_CLOCK_ROUND_MODE_UP:
96         *rounded_rate = round_up;
97         break;
98 
99     default:
100         return FWK_E_SUPPORT;
101 
102     }
103 
104     if ((*rounded_rate % ctx->config->min_step) != 0) {
105         return FWK_E_RANGE;
106     }
107 
108     return FWK_SUCCESS;
109 }
110 
enable_pll(fwk_id_t clock_id,struct juno_hdlcd_dev_ctx * ctx)111 static void enable_pll(fwk_id_t clock_id, struct juno_hdlcd_dev_ctx *ctx)
112 {
113     FWK_LOG_INFO(
114         "[HDLCD%u] Setting PLL R0:0x%" PRIX32 " R1:0x%" PRIX32,
115         fwk_id_get_element_idx(clock_id),
116         ctx->config->lookup_table[ctx->index].pll.REG0,
117         ctx->config->lookup_table[ctx->index].pll.REG1);
118     /*
119      * The pre-calculated PLL_R0 contains the reset bit cleared which means
120      * it will re-enable the PLL.
121      */
122     SCC->PLL[PLL_IDX_HDLCD].REG1 =
123         ctx->config->lookup_table[ctx->index].pll.REG1;
124     SCC->PLL[PLL_IDX_HDLCD].REG0 =
125         ctx->config->lookup_table[ctx->index].pll.REG0;
126 
127     /* Switch HDLCD controller to use PLL clock source */
128     *ctx->config->scc_control &= ~SCC_HDLCD_CONTROL_PXLCLK_SEL;
129 }
130 
131 /*
132  * HDLCD Driver Response API
133  */
134 
juno_hdlcd_request_complete(fwk_id_t dev_id,struct mod_clock_driver_resp_params * response_param)135 void juno_hdlcd_request_complete(fwk_id_t dev_id,
136     struct mod_clock_driver_resp_params *response_param)
137 {
138     struct juno_hdlcd_dev_ctx *ctx;
139 
140     fwk_assert(response_param != NULL);
141 
142     ctx = ctx_table + fwk_id_get_element_idx(module_ctx.request_clock_id);
143 
144     if (response_param->status == FWK_SUCCESS) {
145         enable_pll(module_ctx.request_clock_id, ctx);
146     }
147 
148     ctx->driver_response_api->request_complete(ctx->config->clock_hal_id,
149         response_param);
150 
151     module_ctx.request_clock_id = FWK_ID_NONE;
152 }
153 
154 static const struct mod_clock_driver_response_api hdlcd_driver_response_api = {
155     .request_complete = juno_hdlcd_request_complete,
156 };
157 
158 /*
159  * Clock driver API
160  */
juno_hdlcd_set_rate(fwk_id_t clock_id,uint64_t rate,enum mod_clock_round_mode round_mode)161 static int juno_hdlcd_set_rate(fwk_id_t clock_id, uint64_t rate,
162     enum mod_clock_round_mode round_mode)
163 {
164     int status;
165     struct juno_hdlcd_dev_ctx *ctx;
166     uint32_t clock_rate;
167     uint32_t rounded_rate;
168 
169     ctx = ctx_table + fwk_id_get_element_idx(clock_id);
170 
171     /* Only the first 32-bits are taken */
172     if (rate > UINT32_MAX) {
173         return FWK_E_RANGE;
174     }
175 
176     status = round_rate(ctx, round_mode, (uint32_t)rate, &rounded_rate);
177     if (status != FWK_SUCCESS) {
178         return status;
179     }
180 
181     if ((rounded_rate < ctx->config->min_rate) ||
182         (rounded_rate > ctx->config->max_rate)) {
183         return FWK_E_RANGE;
184     }
185 
186     if (platform == JUNO_IDX_PLATFORM_RTL) {
187         if (!fwk_id_is_equal(module_ctx.request_clock_id, FWK_ID_NONE)) {
188             return FWK_E_BUSY;
189         }
190     }
191 
192     /*
193      * Clock rate is always twice the pixel clock rate. This is because Juno has
194      * an implicit "divide by 2" stage.
195      */
196     clock_rate = rounded_rate * 2;
197 
198     /*
199      * If the target rate matches the external OSC, then use it.
200      */
201     if (clock_rate == PXL_CLK_IN_RATE) {
202         /* Switch HDLCD controller to use external OSC clock source */
203         *ctx->config->scc_control &= ~SCC_HDLCD_CONTROL_PXLCLK_SEL;
204         *ctx->config->scc_control |= SCC_HDLCD_CONTROL_PXLCLK_SEL_CLKIN;
205 
206         FWK_LOG_INFO(
207             "[HDLCD%u]: Request:%" PRIu32 "Hz",
208             fwk_id_get_element_idx(clock_id),
209             rounded_rate);
210 
211         return FWK_SUCCESS;
212     }
213 
214     /*
215      * Check if we can re-use the current PLL frequency.
216      */
217     if (clock_rate == module_ctx.current_pll_rate) {
218         /* Switch HDLCD controller to use PLL clock source */
219         *ctx->config->scc_control &= ~SCC_HDLCD_CONTROL_PXLCLK_SEL;
220         *ctx->config->scc_control |= SCC_HDLCD_CONTROL_PXLCLK_SEL_PLL;
221 
222         FWK_LOG_INFO(
223             "[HDLCD%u]: Request:%" PRIu32 "Hz",
224             fwk_id_get_element_idx(clock_id),
225             rounded_rate);
226 
227         return FWK_SUCCESS;
228     }
229 
230     /*
231      * Due to the sharing policy: go ahead and change the PLL even if the other
232      * PLL is already using it
233      */
234     /* Find entry on the look-up table */
235     ctx->index = (int)((clock_rate - PXL_CLK_IN_RATE) / (500 * FWK_KHZ));
236     if ((ctx->index < 0) ||
237         ((unsigned int)ctx->index >= ctx->config->lookup_table_count)) {
238         return FWK_E_RANGE;
239     }
240 
241     FWK_LOG_INFO(
242         "[HDLCD%u] Entry index:%d",
243         fwk_id_get_element_idx(clock_id),
244         ctx->index);
245 
246     /* Hold PLL in reset during the configuration process */
247     SCC->PLL[PLL_IDX_HDLCD].REG0 = (PLL_REG0_PLL_RESET | PLL_REG0_HARD_BYPASS);
248 
249     module_ctx.current_pll_rate = clock_rate;
250     if (platform == JUNO_IDX_PLATFORM_RTL) {
251         /* CLK_HDLCD_REFCLK is an external I2C based oscillator. */
252         status = ctx->driver_api->set_rate_from_index(ctx->config->driver_id,
253             ctx->index);
254         if ((status != FWK_PENDING) && (status != FWK_SUCCESS)) {
255             FWK_LOG_ERR("[HDLCD] Failed to set board clock");
256             return FWK_E_DEVICE;
257         }
258         if (status == FWK_PENDING) {
259             module_ctx.request_clock_id = clock_id;
260         }
261         return status;
262     }
263 
264     enable_pll(clock_id, ctx);
265 
266     return FWK_SUCCESS;
267 }
268 
juno_hdlcd_get_rate(fwk_id_t clock_id,uint64_t * rate)269 static int juno_hdlcd_get_rate(fwk_id_t clock_id, uint64_t *rate)
270 {
271     struct juno_hdlcd_dev_ctx *ctx;
272 
273     ctx = ctx_table + fwk_id_get_element_idx(clock_id);
274 
275     /* Find out the clock source in use */
276     if ((*ctx->config->scc_control & SCC_HDLCD_CONTROL_PXLCLK_SEL) ==
277         SCC_HDLCD_CONTROL_PXLCLK_SEL_CLKIN) {
278         *rate = PXL_CLK_IN_RATE;
279 
280     } else {
281         *rate = module_ctx.current_pll_rate;
282     }
283 
284     /* All frequencies are double the target pixel clock frequency */
285     *rate /= 2;
286 
287     return FWK_SUCCESS;
288 }
289 
juno_hdlcd_get_rate_from_index(fwk_id_t clock_id,unsigned int rate_index,uint64_t * rate)290 static int juno_hdlcd_get_rate_from_index(fwk_id_t clock_id,
291                                           unsigned int rate_index,
292                                           uint64_t *rate)
293 {
294     return FWK_E_SUPPORT;
295 }
296 
juno_hdlcd_set_state(fwk_id_t clock_id,enum mod_clock_state state)297 static int juno_hdlcd_set_state(fwk_id_t clock_id,
298                                 enum mod_clock_state state)
299 {
300     if (state != MOD_CLOCK_STATE_RUNNING) {
301         return FWK_E_SUPPORT;
302     }
303 
304     return FWK_SUCCESS;
305 }
306 
juno_hdlcd_get_state(fwk_id_t clock_id,enum mod_clock_state * state)307 static int juno_hdlcd_get_state(fwk_id_t clock_id,
308                                 enum mod_clock_state *state)
309 {
310     *state = MOD_CLOCK_STATE_RUNNING;
311 
312     return FWK_SUCCESS;
313 }
314 
juno_hdlcd_get_range(fwk_id_t clock_id,struct mod_clock_range * range)315 static int juno_hdlcd_get_range(fwk_id_t clock_id,
316                                 struct mod_clock_range *range)
317 {
318     struct juno_hdlcd_dev_ctx *ctx;
319 
320     ctx = ctx_table + fwk_id_get_element_idx(clock_id);
321 
322     range->min = ctx->config->min_rate;
323     range->max = ctx->config->max_rate;
324     range->step = ctx->config->min_step;
325     range->rate_type = ctx->config->rate_type;
326 
327     return FWK_SUCCESS;
328 }
329 
330 static const struct mod_clock_drv_api clock_driver_api = {
331     .set_rate = juno_hdlcd_set_rate,
332     .get_rate = juno_hdlcd_get_rate,
333     .get_rate_from_index = juno_hdlcd_get_rate_from_index,
334     .set_state = juno_hdlcd_set_state,
335     .get_state = juno_hdlcd_get_state,
336     .get_range = juno_hdlcd_get_range,
337 };
338 
339 /*
340  * Framework handlers
341  */
juno_hdlcd_init(fwk_id_t module_id,unsigned int element_count,const void * data)342 static int juno_hdlcd_init(fwk_id_t module_id,
343                            unsigned int element_count,
344                            const void *data)
345 {
346     ctx_table = fwk_mm_calloc(element_count, sizeof(*ctx_table));
347 
348     return FWK_SUCCESS;
349 }
350 
juno_hdlcd_dev_init(fwk_id_t element_id,unsigned int sub_element_count,const void * data)351 static int juno_hdlcd_dev_init(fwk_id_t element_id,
352                                unsigned int sub_element_count,
353                                const void *data)
354 {
355     static uint64_t min_rate;
356     static uint64_t max_rate;
357     const struct mod_juno_hdlcd_dev_config *config = data;
358 
359     fwk_assert(config->min_rate != 0);
360     fwk_assert(config->max_rate != 0);
361 
362     if (min_rate == 0) {
363         min_rate = config->min_rate;
364     }
365     if (max_rate == 0) {
366         max_rate = config->max_rate;
367     }
368 
369     /*
370      * All the HDLCD clocks should have the same max_rate and the same min_rate
371      * due to the PLL shared policy.
372      */
373     fwk_assert(min_rate == config->min_rate);
374     fwk_assert(max_rate == config->max_rate);
375 
376     ctx_table[fwk_id_get_element_idx(element_id)].config = config;
377 
378     return FWK_SUCCESS;
379 }
380 
juno_hdlcd_bind(fwk_id_t id,unsigned int round)381 static int juno_hdlcd_bind(fwk_id_t id, unsigned int round)
382 {
383     int status;
384     struct juno_hdlcd_dev_ctx *ctx;
385 
386     /* Only bind in first round of calls */
387     if (round > 0) {
388         return FWK_SUCCESS;
389     }
390 
391     if (fwk_id_is_type(id, FWK_ID_TYPE_MODULE)) {
392         return FWK_SUCCESS;
393     }
394 
395     ctx = ctx_table + fwk_id_get_element_idx(id);
396 
397     /* Bind to clock HAL */
398     status = fwk_module_bind(ctx->config->clock_hal_id,
399         ctx->config->clock_api_id, &ctx->driver_response_api);
400     if (status != FWK_SUCCESS) {
401         return FWK_E_HANDLER;
402     }
403 
404     /* Bind to the driver API */
405     if (platform == JUNO_IDX_PLATFORM_RTL) {
406         status = fwk_module_bind(ctx->config->driver_id,
407             ctx->config->driver_api_id, &ctx->driver_api);
408         if (status != FWK_SUCCESS) {
409             return FWK_E_HANDLER;
410         }
411     }
412 
413     return FWK_SUCCESS;
414 }
415 
juno_hdlcd_process_bind_request(fwk_id_t source_id,fwk_id_t target_id,fwk_id_t api_id,const void ** api)416 static int juno_hdlcd_process_bind_request(fwk_id_t source_id,
417                                            fwk_id_t target_id,
418                                            fwk_id_t api_id,
419                                            const void **api)
420 {
421     if (fwk_id_is_equal(api_id, mod_juno_hdlcd_api_id_clock_driver)) {
422         *api = &clock_driver_api;
423     } else if (fwk_id_is_equal(
424                    api_id, mod_juno_hdlcd_api_id_hdlcd_driver_response)) {
425         *api = &hdlcd_driver_response_api;
426     } else {
427         return FWK_E_HANDLER;
428     }
429 
430     return FWK_SUCCESS;
431 }
432 
juno_hdlcd_start(fwk_id_t id)433 static int juno_hdlcd_start(fwk_id_t id)
434 {
435     unsigned int nf;
436     unsigned int nr;
437     unsigned int od;
438     int status;
439 
440     if (!fwk_id_is_type(id, FWK_ID_TYPE_MODULE)) {
441         return FWK_SUCCESS;
442     }
443 
444     status = juno_id_get_platform(&platform);
445     if (status != FWK_SUCCESS) {
446         return status;
447     }
448 
449     /* Read current PLL frequency */
450     nf = ((SCC->PLL[PLL_IDX_HDLCD].REG0 & PLL_REG0_NF) >> PLL_REG0_NF_POS) + 1;
451     nr = ((SCC->PLL[PLL_IDX_HDLCD].REG1 & PLL_REG1_NR) >> PLL_REG1_NR_POS) + 1;
452     od = ((SCC->PLL[PLL_IDX_HDLCD].REG1 & PLL_REG1_OD) >> PLL_REG1_OD_POS) + 1;
453 
454     module_ctx.current_pll_rate =
455         (uint32_t)(((uint64_t)(PXL_REF_CLK_RATE)*nf) / (nr * od));
456     module_ctx.request_clock_id = FWK_ID_NONE;
457 
458     return FWK_SUCCESS;
459 }
460 
461 const struct fwk_module module_juno_hdlcd = {
462     .api_count = (unsigned int)MOD_JUNO_HDLCD_API_COUNT,
463     .type = FWK_MODULE_TYPE_HAL,
464     .init = juno_hdlcd_init,
465     .element_init = juno_hdlcd_dev_init,
466     .bind = juno_hdlcd_bind,
467     .process_bind_request = juno_hdlcd_process_bind_request,
468     .start = juno_hdlcd_start,
469 };
470