blob: 9ea55e3c859f0b1ee764e977029a7f796d3799fc [file] [log] [blame]
Ghennadi Procopciucfc26eb02024-06-11 18:39:58 +03001/*
2 * Copyright 2024 NXP
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6#include <errno.h>
7
Ghennadi Procopciuc9dee8e42024-06-12 09:25:17 +03008#include <s32cc-clk-regs.h>
9
Ghennadi Procopciuc0d525772024-06-12 08:09:19 +030010#include <common/debug.h>
Ghennadi Procopciucfc26eb02024-06-11 18:39:58 +030011#include <drivers/clk.h>
Ghennadi Procopciuc9dee8e42024-06-12 09:25:17 +030012#include <lib/mmio.h>
Ghennadi Procopciuc0d525772024-06-12 08:09:19 +030013#include <s32cc-clk-modules.h>
14#include <s32cc-clk-utils.h>
15
16#define MAX_STACK_DEPTH (15U)
17
Ghennadi Procopciuc9dee8e42024-06-12 09:25:17 +030018struct s32cc_clk_drv {
19 uintptr_t fxosc_base;
20};
21
Ghennadi Procopciuc0d525772024-06-12 08:09:19 +030022static int update_stack_depth(unsigned int *depth)
23{
24 if (*depth == 0U) {
25 return -ENOMEM;
26 }
27
28 (*depth)--;
29 return 0;
30}
Ghennadi Procopciucfc26eb02024-06-11 18:39:58 +030031
Ghennadi Procopciuc9dee8e42024-06-12 09:25:17 +030032static struct s32cc_clk_drv *get_drv(void)
33{
34 static struct s32cc_clk_drv driver = {
35 .fxosc_base = FXOSC_BASE_ADDR,
36 };
37
38 return &driver;
39}
40
41static int enable_module(const struct s32cc_clk_obj *module, unsigned int *depth);
42
43static int enable_clk_module(const struct s32cc_clk_obj *module,
44 const struct s32cc_clk_drv *drv,
45 unsigned int *depth)
46{
47 const struct s32cc_clk *clk = s32cc_obj2clk(module);
48 int ret;
49
50 ret = update_stack_depth(depth);
51 if (ret != 0) {
52 return ret;
53 }
54
55 if (clk == NULL) {
56 return -EINVAL;
57 }
58
59 if (clk->module != NULL) {
60 return enable_module(clk->module, depth);
61 }
62
63 if (clk->pclock != NULL) {
64 return enable_clk_module(&clk->pclock->desc, drv, depth);
65 }
66
67 return -EINVAL;
68}
69
70static void enable_fxosc(const struct s32cc_clk_drv *drv)
71{
72 uintptr_t fxosc_base = drv->fxosc_base;
73 uint32_t ctrl;
74
75 ctrl = mmio_read_32(FXOSC_CTRL(fxosc_base));
76 if ((ctrl & FXOSC_CTRL_OSCON) != U(0)) {
77 return;
78 }
79
80 ctrl = FXOSC_CTRL_COMP_EN;
81 ctrl &= ~FXOSC_CTRL_OSC_BYP;
82 ctrl |= FXOSC_CTRL_EOCV(0x1);
83 ctrl |= FXOSC_CTRL_GM_SEL(0x7);
84 mmio_write_32(FXOSC_CTRL(fxosc_base), ctrl);
85
86 /* Switch ON the crystal oscillator. */
87 mmio_setbits_32(FXOSC_CTRL(fxosc_base), FXOSC_CTRL_OSCON);
88
89 /* Wait until the clock is stable. */
90 while ((mmio_read_32(FXOSC_STAT(fxosc_base)) & FXOSC_STAT_OSC_STAT) == U(0)) {
91 }
92}
93
94static int enable_osc(const struct s32cc_clk_obj *module,
95 const struct s32cc_clk_drv *drv,
96 unsigned int *depth)
97{
98 const struct s32cc_osc *osc = s32cc_obj2osc(module);
99 int ret = 0;
100
101 ret = update_stack_depth(depth);
102 if (ret != 0) {
103 return ret;
104 }
105
106 switch (osc->source) {
107 case S32CC_FXOSC:
108 enable_fxosc(drv);
109 break;
110 /* FIRC and SIRC oscillators are enabled by default */
111 case S32CC_FIRC:
112 break;
113 case S32CC_SIRC:
114 break;
115 default:
116 ERROR("Invalid oscillator %d\n", osc->source);
117 ret = -EINVAL;
118 break;
119 };
120
121 return ret;
122}
123
124static int enable_module(const struct s32cc_clk_obj *module, unsigned int *depth)
125{
126 const struct s32cc_clk_drv *drv = get_drv();
127 int ret = 0;
128
129 ret = update_stack_depth(depth);
130 if (ret != 0) {
131 return ret;
132 }
133
134 if (drv == NULL) {
135 return -EINVAL;
136 }
137
138 switch (module->type) {
139 case s32cc_osc_t:
140 ret = enable_osc(module, drv, depth);
141 break;
142 case s32cc_clk_t:
143 ret = enable_clk_module(module, drv, depth);
144 break;
Ghennadi Procopciuc7277b972024-06-12 09:53:18 +0300145 case s32cc_clkmux_t:
146 ret = -ENOTSUP;
147 break;
Ghennadi Procopciuc8384d182024-06-12 10:53:06 +0300148 case s32cc_shared_clkmux_t:
149 ret = -ENOTSUP;
150 break;
Ghennadi Procopciuc7277b972024-06-12 09:53:18 +0300151 case s32cc_pll_t:
152 ret = -ENOTSUP;
153 break;
154 case s32cc_pll_out_div_t:
Ghennadi Procopciuc2be71a32024-06-12 12:06:36 +0300155 case s32cc_fixed_div_t:
Ghennadi Procopciuc7277b972024-06-12 09:53:18 +0300156 ret = -ENOTSUP;
157 break;
Ghennadi Procopciuc9dee8e42024-06-12 09:25:17 +0300158 default:
159 ret = -EINVAL;
160 break;
161 }
162
163 return ret;
164}
165
Ghennadi Procopciucfc26eb02024-06-11 18:39:58 +0300166static int s32cc_clk_enable(unsigned long id)
167{
Ghennadi Procopciuc9dee8e42024-06-12 09:25:17 +0300168 unsigned int depth = MAX_STACK_DEPTH;
169 const struct s32cc_clk *clk;
170
171 clk = s32cc_get_arch_clk(id);
172 if (clk == NULL) {
173 return -EINVAL;
174 }
175
176 return enable_module(&clk->desc, &depth);
Ghennadi Procopciucfc26eb02024-06-11 18:39:58 +0300177}
178
179static void s32cc_clk_disable(unsigned long id)
180{
181}
182
183static bool s32cc_clk_is_enabled(unsigned long id)
184{
185 return false;
186}
187
188static unsigned long s32cc_clk_get_rate(unsigned long id)
189{
190 return 0;
191}
192
Ghennadi Procopciuc0d525772024-06-12 08:09:19 +0300193static int set_module_rate(const struct s32cc_clk_obj *module,
194 unsigned long rate, unsigned long *orate,
195 unsigned int *depth);
196
197static int set_osc_freq(const struct s32cc_clk_obj *module, unsigned long rate,
198 unsigned long *orate, unsigned int *depth)
199{
200 struct s32cc_osc *osc = s32cc_obj2osc(module);
201 int ret;
202
203 ret = update_stack_depth(depth);
204 if (ret != 0) {
205 return ret;
206 }
207
208 if ((osc->freq != 0UL) && (rate != osc->freq)) {
209 ERROR("Already initialized oscillator. freq = %lu\n",
210 osc->freq);
211 return -EINVAL;
212 }
213
214 osc->freq = rate;
215 *orate = osc->freq;
216
217 return 0;
218}
219
220static int set_clk_freq(const struct s32cc_clk_obj *module, unsigned long rate,
221 unsigned long *orate, unsigned int *depth)
222{
223 const struct s32cc_clk *clk = s32cc_obj2clk(module);
224 int ret;
225
226 ret = update_stack_depth(depth);
227 if (ret != 0) {
228 return ret;
229 }
230
231 if ((clk->min_freq != 0UL) && (clk->max_freq != 0UL) &&
232 ((rate < clk->min_freq) || (rate > clk->max_freq))) {
233 ERROR("%lu frequency is out of the allowed range: [%lu:%lu]\n",
234 rate, clk->min_freq, clk->max_freq);
235 return -EINVAL;
236 }
237
238 if (clk->module != NULL) {
239 return set_module_rate(clk->module, rate, orate, depth);
240 }
241
242 if (clk->pclock != NULL) {
243 return set_clk_freq(&clk->pclock->desc, rate, orate, depth);
244 }
245
246 return -EINVAL;
247}
248
Ghennadi Procopciuce18cf332024-06-12 11:55:32 +0300249static int set_pll_freq(const struct s32cc_clk_obj *module, unsigned long rate,
250 unsigned long *orate, unsigned int *depth)
251{
252 struct s32cc_pll *pll = s32cc_obj2pll(module);
253 int ret;
254
255 ret = update_stack_depth(depth);
256 if (ret != 0) {
257 return ret;
258 }
259
260 if ((pll->vco_freq != 0UL) && (pll->vco_freq != rate)) {
261 ERROR("PLL frequency was already set\n");
262 return -EINVAL;
263 }
264
265 pll->vco_freq = rate;
266 *orate = pll->vco_freq;
267
268 return 0;
269}
270
Ghennadi Procopciuc907f6542024-06-12 12:00:15 +0300271static int set_pll_div_freq(const struct s32cc_clk_obj *module, unsigned long rate,
272 unsigned long *orate, unsigned int *depth)
273{
274 struct s32cc_pll_out_div *pdiv = s32cc_obj2plldiv(module);
275 const struct s32cc_pll *pll;
276 unsigned long prate, dc;
277 int ret;
278
279 ret = update_stack_depth(depth);
280 if (ret != 0) {
281 return ret;
282 }
283
284 if (pdiv->parent == NULL) {
285 ERROR("Failed to identify PLL divider's parent\n");
286 return -EINVAL;
287 }
288
289 pll = s32cc_obj2pll(pdiv->parent);
290 if (pll == NULL) {
291 ERROR("The parent of the PLL DIV is invalid\n");
292 return -EINVAL;
293 }
294
295 prate = pll->vco_freq;
296
297 /**
298 * The PLL is not initialized yet, so let's take a risk
299 * and accept the proposed rate.
300 */
301 if (prate == 0UL) {
302 pdiv->freq = rate;
303 *orate = rate;
304 return 0;
305 }
306
307 /* Decline in case the rate cannot fit PLL's requirements. */
308 dc = prate / rate;
309 if ((prate / dc) != rate) {
310 return -EINVAL;
311 }
312
313 pdiv->freq = rate;
314 *orate = pdiv->freq;
315
316 return 0;
317}
318
Ghennadi Procopciuce6946b62024-06-12 12:29:54 +0300319static int set_fixed_div_freq(const struct s32cc_clk_obj *module, unsigned long rate,
320 unsigned long *orate, unsigned int *depth)
321{
322 const struct s32cc_fixed_div *fdiv = s32cc_obj2fixeddiv(module);
323 int ret;
324
325 ret = update_stack_depth(depth);
326 if (ret != 0) {
327 return ret;
328 }
329
330 if (fdiv->parent == NULL) {
331 ERROR("The divider doesn't have a valid parent\b");
332 return -EINVAL;
333 }
334
335 ret = set_module_rate(fdiv->parent, rate * fdiv->rate_div, orate, depth);
336
337 /* Update the output rate based on the parent's rate */
338 *orate /= fdiv->rate_div;
339
340 return ret;
341}
342
Ghennadi Procopciuca6a39e82024-06-12 13:05:05 +0300343static int set_mux_freq(const struct s32cc_clk_obj *module, unsigned long rate,
344 unsigned long *orate, unsigned int *depth)
345{
346 const struct s32cc_clkmux *mux = s32cc_obj2clkmux(module);
347 const struct s32cc_clk *clk = s32cc_get_arch_clk(mux->source_id);
348 int ret;
349
350 ret = update_stack_depth(depth);
351 if (ret != 0) {
352 return ret;
353 }
354
355 if (clk == NULL) {
356 ERROR("Mux (id:%" PRIu8 ") without a valid source (%lu)\n",
357 mux->index, mux->source_id);
358 return -EINVAL;
359 }
360
361 return set_module_rate(&clk->desc, rate, orate, depth);
362}
363
Ghennadi Procopciuc0d525772024-06-12 08:09:19 +0300364static int set_module_rate(const struct s32cc_clk_obj *module,
365 unsigned long rate, unsigned long *orate,
366 unsigned int *depth)
367{
368 int ret = 0;
369
370 ret = update_stack_depth(depth);
371 if (ret != 0) {
372 return ret;
373 }
374
375 switch (module->type) {
376 case s32cc_clk_t:
377 ret = set_clk_freq(module, rate, orate, depth);
378 break;
379 case s32cc_osc_t:
380 ret = set_osc_freq(module, rate, orate, depth);
381 break;
Ghennadi Procopciuce18cf332024-06-12 11:55:32 +0300382 case s32cc_pll_t:
383 ret = set_pll_freq(module, rate, orate, depth);
384 break;
Ghennadi Procopciuc907f6542024-06-12 12:00:15 +0300385 case s32cc_pll_out_div_t:
386 ret = set_pll_div_freq(module, rate, orate, depth);
387 break;
Ghennadi Procopciuce6946b62024-06-12 12:29:54 +0300388 case s32cc_fixed_div_t:
389 ret = set_fixed_div_freq(module, rate, orate, depth);
390 break;
Ghennadi Procopciuc7277b972024-06-12 09:53:18 +0300391 case s32cc_clkmux_t:
Ghennadi Procopciuca6a39e82024-06-12 13:05:05 +0300392 ret = set_mux_freq(module, rate, orate, depth);
393 break;
Ghennadi Procopciuc8384d182024-06-12 10:53:06 +0300394 case s32cc_shared_clkmux_t:
Ghennadi Procopciuca6a39e82024-06-12 13:05:05 +0300395 ret = set_mux_freq(module, rate, orate, depth);
Ghennadi Procopciuc7277b972024-06-12 09:53:18 +0300396 break;
Ghennadi Procopciuc0d525772024-06-12 08:09:19 +0300397 default:
398 ret = -EINVAL;
399 break;
400 }
401
402 return ret;
403}
404
Ghennadi Procopciucfc26eb02024-06-11 18:39:58 +0300405static int s32cc_clk_set_rate(unsigned long id, unsigned long rate,
406 unsigned long *orate)
407{
Ghennadi Procopciuc0d525772024-06-12 08:09:19 +0300408 unsigned int depth = MAX_STACK_DEPTH;
409 const struct s32cc_clk *clk;
410 int ret;
411
412 clk = s32cc_get_arch_clk(id);
413 if (clk == NULL) {
414 return -EINVAL;
415 }
416
417 ret = set_module_rate(&clk->desc, rate, orate, &depth);
418 if (ret != 0) {
419 ERROR("Failed to set frequency (%lu MHz) for clock %lu\n",
420 rate, id);
421 }
422
423 return ret;
Ghennadi Procopciucfc26eb02024-06-11 18:39:58 +0300424}
425
426static int s32cc_clk_get_parent(unsigned long id)
427{
428 return -ENOTSUP;
429}
430
431static int s32cc_clk_set_parent(unsigned long id, unsigned long parent_id)
432{
Ghennadi Procopciucaa45fa92024-06-12 10:02:07 +0300433 const struct s32cc_clk *parent;
434 const struct s32cc_clk *clk;
435 bool valid_source = false;
436 struct s32cc_clkmux *mux;
437 uint8_t i;
438
439 clk = s32cc_get_arch_clk(id);
440 if (clk == NULL) {
441 return -EINVAL;
442 }
443
444 parent = s32cc_get_arch_clk(parent_id);
445 if (parent == NULL) {
446 return -EINVAL;
447 }
448
449 if (!is_s32cc_clk_mux(clk)) {
450 ERROR("Clock %lu is not a mux\n", id);
451 return -EINVAL;
452 }
453
454 mux = s32cc_clk2mux(clk);
455 if (mux == NULL) {
456 ERROR("Failed to cast clock %lu to clock mux\n", id);
457 return -EINVAL;
458 }
459
460 for (i = 0; i < mux->nclks; i++) {
461 if (mux->clkids[i] == parent_id) {
462 valid_source = true;
463 break;
464 }
465 }
466
467 if (!valid_source) {
468 ERROR("Clock %lu is not a valid clock for mux %lu\n",
469 parent_id, id);
470 return -EINVAL;
471 }
472
473 mux->source_id = parent_id;
474
475 return 0;
Ghennadi Procopciucfc26eb02024-06-11 18:39:58 +0300476}
477
478void s32cc_clk_register_drv(void)
479{
480 static const struct clk_ops s32cc_clk_ops = {
481 .enable = s32cc_clk_enable,
482 .disable = s32cc_clk_disable,
483 .is_enabled = s32cc_clk_is_enabled,
484 .get_rate = s32cc_clk_get_rate,
485 .set_rate = s32cc_clk_set_rate,
486 .get_parent = s32cc_clk_get_parent,
487 .set_parent = s32cc_clk_set_parent,
488 };
489
490 clk_register(&s32cc_clk_ops);
491}
492