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