blob: 6d8e3d1ac85bb361c069ac5affd30f28386457d0 [file] [log] [blame]
Ghennadi Procopciuc3a580e92024-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 Procopciuc8ab34352024-06-12 09:25:17 +03008#include <s32cc-clk-regs.h>
9
Ghennadi Procopciucd9373512024-06-12 08:09:19 +030010#include <common/debug.h>
Ghennadi Procopciuc3a580e92024-06-11 18:39:58 +030011#include <drivers/clk.h>
Ghennadi Procopciuc8ab34352024-06-12 09:25:17 +030012#include <lib/mmio.h>
Ghennadi Procopciucd9373512024-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 Procopciuc8ab34352024-06-12 09:25:17 +030018struct s32cc_clk_drv {
19 uintptr_t fxosc_base;
20};
21
Ghennadi Procopciucd9373512024-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 Procopciuc3a580e92024-06-11 18:39:58 +030031
Ghennadi Procopciuc8ab34352024-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 Procopciuca8be7482024-06-12 09:53:18 +0300145 case s32cc_clkmux_t:
146 ret = -ENOTSUP;
147 break;
Ghennadi Procopciuc3fa91a92024-06-12 10:53:06 +0300148 case s32cc_shared_clkmux_t:
149 ret = -ENOTSUP;
150 break;
Ghennadi Procopciuca8be7482024-06-12 09:53:18 +0300151 case s32cc_pll_t:
152 ret = -ENOTSUP;
153 break;
154 case s32cc_pll_out_div_t:
155 ret = -ENOTSUP;
156 break;
Ghennadi Procopciuc8ab34352024-06-12 09:25:17 +0300157 default:
158 ret = -EINVAL;
159 break;
160 }
161
162 return ret;
163}
164
Ghennadi Procopciuc3a580e92024-06-11 18:39:58 +0300165static int s32cc_clk_enable(unsigned long id)
166{
Ghennadi Procopciuc8ab34352024-06-12 09:25:17 +0300167 unsigned int depth = MAX_STACK_DEPTH;
168 const struct s32cc_clk *clk;
169
170 clk = s32cc_get_arch_clk(id);
171 if (clk == NULL) {
172 return -EINVAL;
173 }
174
175 return enable_module(&clk->desc, &depth);
Ghennadi Procopciuc3a580e92024-06-11 18:39:58 +0300176}
177
178static void s32cc_clk_disable(unsigned long id)
179{
180}
181
182static bool s32cc_clk_is_enabled(unsigned long id)
183{
184 return false;
185}
186
187static unsigned long s32cc_clk_get_rate(unsigned long id)
188{
189 return 0;
190}
191
Ghennadi Procopciucd9373512024-06-12 08:09:19 +0300192static int set_module_rate(const struct s32cc_clk_obj *module,
193 unsigned long rate, unsigned long *orate,
194 unsigned int *depth);
195
196static int set_osc_freq(const struct s32cc_clk_obj *module, unsigned long rate,
197 unsigned long *orate, unsigned int *depth)
198{
199 struct s32cc_osc *osc = s32cc_obj2osc(module);
200 int ret;
201
202 ret = update_stack_depth(depth);
203 if (ret != 0) {
204 return ret;
205 }
206
207 if ((osc->freq != 0UL) && (rate != osc->freq)) {
208 ERROR("Already initialized oscillator. freq = %lu\n",
209 osc->freq);
210 return -EINVAL;
211 }
212
213 osc->freq = rate;
214 *orate = osc->freq;
215
216 return 0;
217}
218
219static int set_clk_freq(const struct s32cc_clk_obj *module, unsigned long rate,
220 unsigned long *orate, unsigned int *depth)
221{
222 const struct s32cc_clk *clk = s32cc_obj2clk(module);
223 int ret;
224
225 ret = update_stack_depth(depth);
226 if (ret != 0) {
227 return ret;
228 }
229
230 if ((clk->min_freq != 0UL) && (clk->max_freq != 0UL) &&
231 ((rate < clk->min_freq) || (rate > clk->max_freq))) {
232 ERROR("%lu frequency is out of the allowed range: [%lu:%lu]\n",
233 rate, clk->min_freq, clk->max_freq);
234 return -EINVAL;
235 }
236
237 if (clk->module != NULL) {
238 return set_module_rate(clk->module, rate, orate, depth);
239 }
240
241 if (clk->pclock != NULL) {
242 return set_clk_freq(&clk->pclock->desc, rate, orate, depth);
243 }
244
245 return -EINVAL;
246}
247
Ghennadi Procopciuc7ad4e232024-06-12 11:55:32 +0300248static int set_pll_freq(const struct s32cc_clk_obj *module, unsigned long rate,
249 unsigned long *orate, unsigned int *depth)
250{
251 struct s32cc_pll *pll = s32cc_obj2pll(module);
252 int ret;
253
254 ret = update_stack_depth(depth);
255 if (ret != 0) {
256 return ret;
257 }
258
259 if ((pll->vco_freq != 0UL) && (pll->vco_freq != rate)) {
260 ERROR("PLL frequency was already set\n");
261 return -EINVAL;
262 }
263
264 pll->vco_freq = rate;
265 *orate = pll->vco_freq;
266
267 return 0;
268}
269
Ghennadi Procopciucde950ef2024-06-12 12:00:15 +0300270static int set_pll_div_freq(const struct s32cc_clk_obj *module, unsigned long rate,
271 unsigned long *orate, unsigned int *depth)
272{
273 struct s32cc_pll_out_div *pdiv = s32cc_obj2plldiv(module);
274 const struct s32cc_pll *pll;
275 unsigned long prate, dc;
276 int ret;
277
278 ret = update_stack_depth(depth);
279 if (ret != 0) {
280 return ret;
281 }
282
283 if (pdiv->parent == NULL) {
284 ERROR("Failed to identify PLL divider's parent\n");
285 return -EINVAL;
286 }
287
288 pll = s32cc_obj2pll(pdiv->parent);
289 if (pll == NULL) {
290 ERROR("The parent of the PLL DIV is invalid\n");
291 return -EINVAL;
292 }
293
294 prate = pll->vco_freq;
295
296 /**
297 * The PLL is not initialized yet, so let's take a risk
298 * and accept the proposed rate.
299 */
300 if (prate == 0UL) {
301 pdiv->freq = rate;
302 *orate = rate;
303 return 0;
304 }
305
306 /* Decline in case the rate cannot fit PLL's requirements. */
307 dc = prate / rate;
308 if ((prate / dc) != rate) {
309 return -EINVAL;
310 }
311
312 pdiv->freq = rate;
313 *orate = pdiv->freq;
314
315 return 0;
316}
317
Ghennadi Procopciucd9373512024-06-12 08:09:19 +0300318static int set_module_rate(const struct s32cc_clk_obj *module,
319 unsigned long rate, unsigned long *orate,
320 unsigned int *depth)
321{
322 int ret = 0;
323
324 ret = update_stack_depth(depth);
325 if (ret != 0) {
326 return ret;
327 }
328
329 switch (module->type) {
330 case s32cc_clk_t:
331 ret = set_clk_freq(module, rate, orate, depth);
332 break;
333 case s32cc_osc_t:
334 ret = set_osc_freq(module, rate, orate, depth);
335 break;
Ghennadi Procopciuc7ad4e232024-06-12 11:55:32 +0300336 case s32cc_pll_t:
337 ret = set_pll_freq(module, rate, orate, depth);
338 break;
Ghennadi Procopciucde950ef2024-06-12 12:00:15 +0300339 case s32cc_pll_out_div_t:
340 ret = set_pll_div_freq(module, rate, orate, depth);
341 break;
Ghennadi Procopciuca8be7482024-06-12 09:53:18 +0300342 case s32cc_clkmux_t:
Ghennadi Procopciuc3fa91a92024-06-12 10:53:06 +0300343 case s32cc_shared_clkmux_t:
Ghennadi Procopciuca8be7482024-06-12 09:53:18 +0300344 ret = -ENOTSUP;
345 break;
Ghennadi Procopciucd9373512024-06-12 08:09:19 +0300346 default:
347 ret = -EINVAL;
348 break;
349 }
350
351 return ret;
352}
353
Ghennadi Procopciuc3a580e92024-06-11 18:39:58 +0300354static int s32cc_clk_set_rate(unsigned long id, unsigned long rate,
355 unsigned long *orate)
356{
Ghennadi Procopciucd9373512024-06-12 08:09:19 +0300357 unsigned int depth = MAX_STACK_DEPTH;
358 const struct s32cc_clk *clk;
359 int ret;
360
361 clk = s32cc_get_arch_clk(id);
362 if (clk == NULL) {
363 return -EINVAL;
364 }
365
366 ret = set_module_rate(&clk->desc, rate, orate, &depth);
367 if (ret != 0) {
368 ERROR("Failed to set frequency (%lu MHz) for clock %lu\n",
369 rate, id);
370 }
371
372 return ret;
Ghennadi Procopciuc3a580e92024-06-11 18:39:58 +0300373}
374
375static int s32cc_clk_get_parent(unsigned long id)
376{
377 return -ENOTSUP;
378}
379
380static int s32cc_clk_set_parent(unsigned long id, unsigned long parent_id)
381{
Ghennadi Procopciuc12e7a2c2024-06-12 10:02:07 +0300382 const struct s32cc_clk *parent;
383 const struct s32cc_clk *clk;
384 bool valid_source = false;
385 struct s32cc_clkmux *mux;
386 uint8_t i;
387
388 clk = s32cc_get_arch_clk(id);
389 if (clk == NULL) {
390 return -EINVAL;
391 }
392
393 parent = s32cc_get_arch_clk(parent_id);
394 if (parent == NULL) {
395 return -EINVAL;
396 }
397
398 if (!is_s32cc_clk_mux(clk)) {
399 ERROR("Clock %lu is not a mux\n", id);
400 return -EINVAL;
401 }
402
403 mux = s32cc_clk2mux(clk);
404 if (mux == NULL) {
405 ERROR("Failed to cast clock %lu to clock mux\n", id);
406 return -EINVAL;
407 }
408
409 for (i = 0; i < mux->nclks; i++) {
410 if (mux->clkids[i] == parent_id) {
411 valid_source = true;
412 break;
413 }
414 }
415
416 if (!valid_source) {
417 ERROR("Clock %lu is not a valid clock for mux %lu\n",
418 parent_id, id);
419 return -EINVAL;
420 }
421
422 mux->source_id = parent_id;
423
424 return 0;
Ghennadi Procopciuc3a580e92024-06-11 18:39:58 +0300425}
426
427void s32cc_clk_register_drv(void)
428{
429 static const struct clk_ops s32cc_clk_ops = {
430 .enable = s32cc_clk_enable,
431 .disable = s32cc_clk_disable,
432 .is_enabled = s32cc_clk_is_enabled,
433 .get_rate = s32cc_clk_get_rate,
434 .set_rate = s32cc_clk_set_rate,
435 .get_parent = s32cc_clk_get_parent,
436 .set_parent = s32cc_clk_set_parent,
437 };
438
439 clk_register(&s32cc_clk_ops);
440}
441