blob: b1cffee46c2f9c9762b81b1f84759ceb06ffc7bb [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 Procopciucb5101c42024-06-12 14:21:39 +030013#include <s32cc-clk-ids.h>
Ghennadi Procopciucd9373512024-06-12 08:09:19 +030014#include <s32cc-clk-modules.h>
15#include <s32cc-clk-utils.h>
16
17#define MAX_STACK_DEPTH (15U)
18
Ghennadi Procopciucb5101c42024-06-12 14:21:39 +030019/* This is used for floating-point precision calculations. */
20#define FP_PRECISION (100000000UL)
21
Ghennadi Procopciuc8ab34352024-06-12 09:25:17 +030022struct s32cc_clk_drv {
23 uintptr_t fxosc_base;
Ghennadi Procopciucb5101c42024-06-12 14:21:39 +030024 uintptr_t armpll_base;
Ghennadi Procopciuc8ab34352024-06-12 09:25:17 +030025};
26
Ghennadi Procopciucd9373512024-06-12 08:09:19 +030027static int update_stack_depth(unsigned int *depth)
28{
29 if (*depth == 0U) {
30 return -ENOMEM;
31 }
32
33 (*depth)--;
34 return 0;
35}
Ghennadi Procopciuc3a580e92024-06-11 18:39:58 +030036
Ghennadi Procopciuc8ab34352024-06-12 09:25:17 +030037static struct s32cc_clk_drv *get_drv(void)
38{
39 static struct s32cc_clk_drv driver = {
40 .fxosc_base = FXOSC_BASE_ADDR,
Ghennadi Procopciucb5101c42024-06-12 14:21:39 +030041 .armpll_base = ARMPLL_BASE_ADDR,
Ghennadi Procopciuc8ab34352024-06-12 09:25:17 +030042 };
43
44 return &driver;
45}
46
47static int enable_module(const struct s32cc_clk_obj *module, unsigned int *depth);
48
49static int enable_clk_module(const struct s32cc_clk_obj *module,
50 const struct s32cc_clk_drv *drv,
51 unsigned int *depth)
52{
53 const struct s32cc_clk *clk = s32cc_obj2clk(module);
54 int ret;
55
56 ret = update_stack_depth(depth);
57 if (ret != 0) {
58 return ret;
59 }
60
61 if (clk == NULL) {
62 return -EINVAL;
63 }
64
65 if (clk->module != NULL) {
66 return enable_module(clk->module, depth);
67 }
68
69 if (clk->pclock != NULL) {
70 return enable_clk_module(&clk->pclock->desc, drv, depth);
71 }
72
73 return -EINVAL;
74}
75
Ghennadi Procopciucb5101c42024-06-12 14:21:39 +030076static int get_base_addr(enum s32cc_clk_source id, const struct s32cc_clk_drv *drv,
77 uintptr_t *base)
78{
79 int ret = 0;
80
81 switch (id) {
82 case S32CC_FXOSC:
83 *base = drv->fxosc_base;
84 break;
85 case S32CC_ARM_PLL:
86 *base = drv->armpll_base;
87 break;
88 case S32CC_CGM1:
89 ret = -ENOTSUP;
90 break;
91 case S32CC_FIRC:
92 break;
93 case S32CC_SIRC:
94 break;
95 default:
96 ret = -EINVAL;
97 break;
98 }
99
100 if (ret != 0) {
101 ERROR("Unknown clock source id: %u\n", id);
102 }
103
104 return ret;
105}
106
Ghennadi Procopciuc8ab34352024-06-12 09:25:17 +0300107static void enable_fxosc(const struct s32cc_clk_drv *drv)
108{
109 uintptr_t fxosc_base = drv->fxosc_base;
110 uint32_t ctrl;
111
112 ctrl = mmio_read_32(FXOSC_CTRL(fxosc_base));
113 if ((ctrl & FXOSC_CTRL_OSCON) != U(0)) {
114 return;
115 }
116
117 ctrl = FXOSC_CTRL_COMP_EN;
118 ctrl &= ~FXOSC_CTRL_OSC_BYP;
119 ctrl |= FXOSC_CTRL_EOCV(0x1);
120 ctrl |= FXOSC_CTRL_GM_SEL(0x7);
121 mmio_write_32(FXOSC_CTRL(fxosc_base), ctrl);
122
123 /* Switch ON the crystal oscillator. */
124 mmio_setbits_32(FXOSC_CTRL(fxosc_base), FXOSC_CTRL_OSCON);
125
126 /* Wait until the clock is stable. */
127 while ((mmio_read_32(FXOSC_STAT(fxosc_base)) & FXOSC_STAT_OSC_STAT) == U(0)) {
128 }
129}
130
131static int enable_osc(const struct s32cc_clk_obj *module,
132 const struct s32cc_clk_drv *drv,
133 unsigned int *depth)
134{
135 const struct s32cc_osc *osc = s32cc_obj2osc(module);
136 int ret = 0;
137
138 ret = update_stack_depth(depth);
139 if (ret != 0) {
140 return ret;
141 }
142
143 switch (osc->source) {
144 case S32CC_FXOSC:
145 enable_fxosc(drv);
146 break;
147 /* FIRC and SIRC oscillators are enabled by default */
148 case S32CC_FIRC:
149 break;
150 case S32CC_SIRC:
151 break;
152 default:
153 ERROR("Invalid oscillator %d\n", osc->source);
154 ret = -EINVAL;
155 break;
156 };
157
158 return ret;
159}
160
Ghennadi Procopciucb5101c42024-06-12 14:21:39 +0300161static int get_pll_mfi_mfn(unsigned long pll_vco, unsigned long ref_freq,
162 uint32_t *mfi, uint32_t *mfn)
163
164{
165 unsigned long vco;
166 unsigned long mfn64;
167
168 /* FRAC-N mode */
169 *mfi = (uint32_t)(pll_vco / ref_freq);
170
171 /* MFN formula : (double)(pll_vco % ref_freq) / ref_freq * 18432.0 */
172 mfn64 = pll_vco % ref_freq;
173 mfn64 *= FP_PRECISION;
174 mfn64 /= ref_freq;
175 mfn64 *= 18432UL;
176 mfn64 /= FP_PRECISION;
177
178 if (mfn64 > UINT32_MAX) {
179 return -EINVAL;
180 }
181
182 *mfn = (uint32_t)mfn64;
183
184 vco = ((unsigned long)*mfn * FP_PRECISION) / 18432UL;
185 vco += (unsigned long)*mfi * FP_PRECISION;
186 vco *= ref_freq;
187 vco /= FP_PRECISION;
188
189 if (vco != pll_vco) {
190 ERROR("Failed to find MFI and MFN settings for PLL freq %lu. Nearest freq = %lu\n",
191 pll_vco, vco);
192 return -EINVAL;
193 }
194
195 return 0;
196}
197
198static struct s32cc_clkmux *get_pll_mux(const struct s32cc_pll *pll)
199{
200 const struct s32cc_clk_obj *source = pll->source;
201 const struct s32cc_clk *clk;
202
203 if (source == NULL) {
204 ERROR("Failed to identify PLL's parent\n");
205 return NULL;
206 }
207
208 if (source->type != s32cc_clk_t) {
209 ERROR("The parent of the PLL isn't a clock\n");
210 return NULL;
211 }
212
213 clk = s32cc_obj2clk(source);
214
215 if (clk->module == NULL) {
216 ERROR("The clock isn't connected to a module\n");
217 return NULL;
218 }
219
220 source = clk->module;
221
222 if ((source->type != s32cc_clkmux_t) &&
223 (source->type != s32cc_shared_clkmux_t)) {
224 ERROR("The parent of the PLL isn't a MUX\n");
225 return NULL;
226 }
227
228 return s32cc_obj2clkmux(source);
229}
230
231static void disable_odiv(uintptr_t pll_addr, uint32_t div_index)
232{
233 mmio_clrbits_32(PLLDIG_PLLODIV(pll_addr, div_index), PLLDIG_PLLODIV_DE);
234}
235
236static void disable_odivs(uintptr_t pll_addr, uint32_t ndivs)
237{
238 uint32_t i;
239
240 for (i = 0; i < ndivs; i++) {
241 disable_odiv(pll_addr, i);
242 }
243}
244
245static void enable_pll_hw(uintptr_t pll_addr)
246{
247 /* Enable the PLL. */
248 mmio_write_32(PLLDIG_PLLCR(pll_addr), 0x0);
249
250 /* Poll until PLL acquires lock. */
251 while ((mmio_read_32(PLLDIG_PLLSR(pll_addr)) & PLLDIG_PLLSR_LOCK) == 0U) {
252 }
253}
254
255static void disable_pll_hw(uintptr_t pll_addr)
256{
257 mmio_write_32(PLLDIG_PLLCR(pll_addr), PLLDIG_PLLCR_PLLPD);
258}
259
260static int program_pll(const struct s32cc_pll *pll, uintptr_t pll_addr,
261 const struct s32cc_clk_drv *drv, uint32_t sclk_id,
262 unsigned long sclk_freq)
263{
264 uint32_t rdiv = 1, mfi, mfn;
265 int ret;
266
267 ret = get_pll_mfi_mfn(pll->vco_freq, sclk_freq, &mfi, &mfn);
268 if (ret != 0) {
269 return -EINVAL;
270 }
271
272 /* Disable ODIVs*/
273 disable_odivs(pll_addr, pll->ndividers);
274
275 /* Disable PLL */
276 disable_pll_hw(pll_addr);
277
278 /* Program PLLCLKMUX */
279 mmio_write_32(PLLDIG_PLLCLKMUX(pll_addr), sclk_id);
280
281 /* Program VCO */
282 mmio_clrsetbits_32(PLLDIG_PLLDV(pll_addr),
283 PLLDIG_PLLDV_RDIV_MASK | PLLDIG_PLLDV_MFI_MASK,
284 PLLDIG_PLLDV_RDIV_SET(rdiv) | PLLDIG_PLLDV_MFI(mfi));
285
286 mmio_write_32(PLLDIG_PLLFD(pll_addr),
287 PLLDIG_PLLFD_MFN_SET(mfn) | PLLDIG_PLLFD_SMDEN);
288
289 enable_pll_hw(pll_addr);
290
291 return ret;
292}
293
294static int enable_pll(const struct s32cc_clk_obj *module,
295 const struct s32cc_clk_drv *drv,
296 unsigned int *depth)
297{
298 const struct s32cc_pll *pll = s32cc_obj2pll(module);
299 const struct s32cc_clkmux *mux;
300 uintptr_t pll_addr = UL(0x0);
301 unsigned long sclk_freq;
302 uint32_t sclk_id;
303 int ret;
304
305 ret = update_stack_depth(depth);
306 if (ret != 0) {
307 return ret;
308 }
309
310 mux = get_pll_mux(pll);
311 if (mux == NULL) {
312 return -EINVAL;
313 }
314
315 if (pll->instance != mux->module) {
316 ERROR("MUX type is not in sync with PLL ID\n");
317 return -EINVAL;
318 }
319
320 ret = get_base_addr(pll->instance, drv, &pll_addr);
321 if (ret != 0) {
322 ERROR("Failed to detect PLL instance\n");
323 return ret;
324 }
325
326 switch (mux->source_id) {
327 case S32CC_CLK_FIRC:
328 sclk_freq = 48U * MHZ;
329 sclk_id = 0;
330 break;
331 case S32CC_CLK_FXOSC:
332 sclk_freq = 40U * MHZ;
333 sclk_id = 1;
334 break;
335 default:
336 ERROR("Invalid source selection for PLL 0x%lx\n",
337 pll_addr);
338 return -EINVAL;
339 };
340
341 return program_pll(pll, pll_addr, drv, sclk_id, sclk_freq);
342}
343
Ghennadi Procopciuc8ab34352024-06-12 09:25:17 +0300344static int enable_module(const struct s32cc_clk_obj *module, unsigned int *depth)
345{
346 const struct s32cc_clk_drv *drv = get_drv();
347 int ret = 0;
348
349 ret = update_stack_depth(depth);
350 if (ret != 0) {
351 return ret;
352 }
353
354 if (drv == NULL) {
355 return -EINVAL;
356 }
357
358 switch (module->type) {
359 case s32cc_osc_t:
360 ret = enable_osc(module, drv, depth);
361 break;
362 case s32cc_clk_t:
363 ret = enable_clk_module(module, drv, depth);
364 break;
Ghennadi Procopciucb5101c42024-06-12 14:21:39 +0300365 case s32cc_pll_t:
366 ret = enable_pll(module, drv, depth);
367 break;
Ghennadi Procopciuca8be7482024-06-12 09:53:18 +0300368 case s32cc_clkmux_t:
369 ret = -ENOTSUP;
370 break;
Ghennadi Procopciuc3fa91a92024-06-12 10:53:06 +0300371 case s32cc_shared_clkmux_t:
372 ret = -ENOTSUP;
373 break;
Ghennadi Procopciucb5101c42024-06-12 14:21:39 +0300374 case s32cc_pll_out_div_t:
Ghennadi Procopciuca8be7482024-06-12 09:53:18 +0300375 ret = -ENOTSUP;
376 break;
Ghennadi Procopciuc44e21302024-06-12 12:06:36 +0300377 case s32cc_fixed_div_t:
Ghennadi Procopciuca8be7482024-06-12 09:53:18 +0300378 ret = -ENOTSUP;
379 break;
Ghennadi Procopciuc8ab34352024-06-12 09:25:17 +0300380 default:
381 ret = -EINVAL;
382 break;
383 }
384
385 return ret;
386}
387
Ghennadi Procopciuc3a580e92024-06-11 18:39:58 +0300388static int s32cc_clk_enable(unsigned long id)
389{
Ghennadi Procopciuc8ab34352024-06-12 09:25:17 +0300390 unsigned int depth = MAX_STACK_DEPTH;
391 const struct s32cc_clk *clk;
392
393 clk = s32cc_get_arch_clk(id);
394 if (clk == NULL) {
395 return -EINVAL;
396 }
397
398 return enable_module(&clk->desc, &depth);
Ghennadi Procopciuc3a580e92024-06-11 18:39:58 +0300399}
400
401static void s32cc_clk_disable(unsigned long id)
402{
403}
404
405static bool s32cc_clk_is_enabled(unsigned long id)
406{
407 return false;
408}
409
410static unsigned long s32cc_clk_get_rate(unsigned long id)
411{
412 return 0;
413}
414
Ghennadi Procopciucd9373512024-06-12 08:09:19 +0300415static int set_module_rate(const struct s32cc_clk_obj *module,
416 unsigned long rate, unsigned long *orate,
417 unsigned int *depth);
418
419static int set_osc_freq(const struct s32cc_clk_obj *module, unsigned long rate,
420 unsigned long *orate, unsigned int *depth)
421{
422 struct s32cc_osc *osc = s32cc_obj2osc(module);
423 int ret;
424
425 ret = update_stack_depth(depth);
426 if (ret != 0) {
427 return ret;
428 }
429
430 if ((osc->freq != 0UL) && (rate != osc->freq)) {
431 ERROR("Already initialized oscillator. freq = %lu\n",
432 osc->freq);
433 return -EINVAL;
434 }
435
436 osc->freq = rate;
437 *orate = osc->freq;
438
439 return 0;
440}
441
442static int set_clk_freq(const struct s32cc_clk_obj *module, unsigned long rate,
443 unsigned long *orate, unsigned int *depth)
444{
445 const struct s32cc_clk *clk = s32cc_obj2clk(module);
446 int ret;
447
448 ret = update_stack_depth(depth);
449 if (ret != 0) {
450 return ret;
451 }
452
453 if ((clk->min_freq != 0UL) && (clk->max_freq != 0UL) &&
454 ((rate < clk->min_freq) || (rate > clk->max_freq))) {
455 ERROR("%lu frequency is out of the allowed range: [%lu:%lu]\n",
456 rate, clk->min_freq, clk->max_freq);
457 return -EINVAL;
458 }
459
460 if (clk->module != NULL) {
461 return set_module_rate(clk->module, rate, orate, depth);
462 }
463
464 if (clk->pclock != NULL) {
465 return set_clk_freq(&clk->pclock->desc, rate, orate, depth);
466 }
467
468 return -EINVAL;
469}
470
Ghennadi Procopciuc7ad4e232024-06-12 11:55:32 +0300471static int set_pll_freq(const struct s32cc_clk_obj *module, unsigned long rate,
472 unsigned long *orate, unsigned int *depth)
473{
474 struct s32cc_pll *pll = s32cc_obj2pll(module);
475 int ret;
476
477 ret = update_stack_depth(depth);
478 if (ret != 0) {
479 return ret;
480 }
481
482 if ((pll->vco_freq != 0UL) && (pll->vco_freq != rate)) {
483 ERROR("PLL frequency was already set\n");
484 return -EINVAL;
485 }
486
487 pll->vco_freq = rate;
488 *orate = pll->vco_freq;
489
490 return 0;
491}
492
Ghennadi Procopciucde950ef2024-06-12 12:00:15 +0300493static int set_pll_div_freq(const struct s32cc_clk_obj *module, unsigned long rate,
494 unsigned long *orate, unsigned int *depth)
495{
496 struct s32cc_pll_out_div *pdiv = s32cc_obj2plldiv(module);
497 const struct s32cc_pll *pll;
498 unsigned long prate, dc;
499 int ret;
500
501 ret = update_stack_depth(depth);
502 if (ret != 0) {
503 return ret;
504 }
505
506 if (pdiv->parent == NULL) {
507 ERROR("Failed to identify PLL divider's parent\n");
508 return -EINVAL;
509 }
510
511 pll = s32cc_obj2pll(pdiv->parent);
512 if (pll == NULL) {
513 ERROR("The parent of the PLL DIV is invalid\n");
514 return -EINVAL;
515 }
516
517 prate = pll->vco_freq;
518
519 /**
520 * The PLL is not initialized yet, so let's take a risk
521 * and accept the proposed rate.
522 */
523 if (prate == 0UL) {
524 pdiv->freq = rate;
525 *orate = rate;
526 return 0;
527 }
528
529 /* Decline in case the rate cannot fit PLL's requirements. */
530 dc = prate / rate;
531 if ((prate / dc) != rate) {
532 return -EINVAL;
533 }
534
535 pdiv->freq = rate;
536 *orate = pdiv->freq;
537
538 return 0;
539}
540
Ghennadi Procopciuc65739db2024-06-12 12:29:54 +0300541static int set_fixed_div_freq(const struct s32cc_clk_obj *module, unsigned long rate,
542 unsigned long *orate, unsigned int *depth)
543{
544 const struct s32cc_fixed_div *fdiv = s32cc_obj2fixeddiv(module);
545 int ret;
546
547 ret = update_stack_depth(depth);
548 if (ret != 0) {
549 return ret;
550 }
551
552 if (fdiv->parent == NULL) {
553 ERROR("The divider doesn't have a valid parent\b");
554 return -EINVAL;
555 }
556
557 ret = set_module_rate(fdiv->parent, rate * fdiv->rate_div, orate, depth);
558
559 /* Update the output rate based on the parent's rate */
560 *orate /= fdiv->rate_div;
561
562 return ret;
563}
564
Ghennadi Procopciuc64e0c222024-06-12 13:05:05 +0300565static int set_mux_freq(const struct s32cc_clk_obj *module, unsigned long rate,
566 unsigned long *orate, unsigned int *depth)
567{
568 const struct s32cc_clkmux *mux = s32cc_obj2clkmux(module);
569 const struct s32cc_clk *clk = s32cc_get_arch_clk(mux->source_id);
570 int ret;
571
572 ret = update_stack_depth(depth);
573 if (ret != 0) {
574 return ret;
575 }
576
577 if (clk == NULL) {
578 ERROR("Mux (id:%" PRIu8 ") without a valid source (%lu)\n",
579 mux->index, mux->source_id);
580 return -EINVAL;
581 }
582
583 return set_module_rate(&clk->desc, rate, orate, depth);
584}
585
Ghennadi Procopciucd9373512024-06-12 08:09:19 +0300586static int set_module_rate(const struct s32cc_clk_obj *module,
587 unsigned long rate, unsigned long *orate,
588 unsigned int *depth)
589{
590 int ret = 0;
591
592 ret = update_stack_depth(depth);
593 if (ret != 0) {
594 return ret;
595 }
596
597 switch (module->type) {
598 case s32cc_clk_t:
599 ret = set_clk_freq(module, rate, orate, depth);
600 break;
601 case s32cc_osc_t:
602 ret = set_osc_freq(module, rate, orate, depth);
603 break;
Ghennadi Procopciuc7ad4e232024-06-12 11:55:32 +0300604 case s32cc_pll_t:
605 ret = set_pll_freq(module, rate, orate, depth);
606 break;
Ghennadi Procopciucde950ef2024-06-12 12:00:15 +0300607 case s32cc_pll_out_div_t:
608 ret = set_pll_div_freq(module, rate, orate, depth);
609 break;
Ghennadi Procopciuc65739db2024-06-12 12:29:54 +0300610 case s32cc_fixed_div_t:
611 ret = set_fixed_div_freq(module, rate, orate, depth);
612 break;
Ghennadi Procopciuca8be7482024-06-12 09:53:18 +0300613 case s32cc_clkmux_t:
Ghennadi Procopciuc64e0c222024-06-12 13:05:05 +0300614 ret = set_mux_freq(module, rate, orate, depth);
615 break;
Ghennadi Procopciuc3fa91a92024-06-12 10:53:06 +0300616 case s32cc_shared_clkmux_t:
Ghennadi Procopciuc64e0c222024-06-12 13:05:05 +0300617 ret = set_mux_freq(module, rate, orate, depth);
Ghennadi Procopciuca8be7482024-06-12 09:53:18 +0300618 break;
Ghennadi Procopciucd9373512024-06-12 08:09:19 +0300619 default:
620 ret = -EINVAL;
621 break;
622 }
623
624 return ret;
625}
626
Ghennadi Procopciuc3a580e92024-06-11 18:39:58 +0300627static int s32cc_clk_set_rate(unsigned long id, unsigned long rate,
628 unsigned long *orate)
629{
Ghennadi Procopciucd9373512024-06-12 08:09:19 +0300630 unsigned int depth = MAX_STACK_DEPTH;
631 const struct s32cc_clk *clk;
632 int ret;
633
634 clk = s32cc_get_arch_clk(id);
635 if (clk == NULL) {
636 return -EINVAL;
637 }
638
639 ret = set_module_rate(&clk->desc, rate, orate, &depth);
640 if (ret != 0) {
641 ERROR("Failed to set frequency (%lu MHz) for clock %lu\n",
642 rate, id);
643 }
644
645 return ret;
Ghennadi Procopciuc3a580e92024-06-11 18:39:58 +0300646}
647
648static int s32cc_clk_get_parent(unsigned long id)
649{
650 return -ENOTSUP;
651}
652
653static int s32cc_clk_set_parent(unsigned long id, unsigned long parent_id)
654{
Ghennadi Procopciuc12e7a2c2024-06-12 10:02:07 +0300655 const struct s32cc_clk *parent;
656 const struct s32cc_clk *clk;
657 bool valid_source = false;
658 struct s32cc_clkmux *mux;
659 uint8_t i;
660
661 clk = s32cc_get_arch_clk(id);
662 if (clk == NULL) {
663 return -EINVAL;
664 }
665
666 parent = s32cc_get_arch_clk(parent_id);
667 if (parent == NULL) {
668 return -EINVAL;
669 }
670
671 if (!is_s32cc_clk_mux(clk)) {
672 ERROR("Clock %lu is not a mux\n", id);
673 return -EINVAL;
674 }
675
676 mux = s32cc_clk2mux(clk);
677 if (mux == NULL) {
678 ERROR("Failed to cast clock %lu to clock mux\n", id);
679 return -EINVAL;
680 }
681
682 for (i = 0; i < mux->nclks; i++) {
683 if (mux->clkids[i] == parent_id) {
684 valid_source = true;
685 break;
686 }
687 }
688
689 if (!valid_source) {
690 ERROR("Clock %lu is not a valid clock for mux %lu\n",
691 parent_id, id);
692 return -EINVAL;
693 }
694
695 mux->source_id = parent_id;
696
697 return 0;
Ghennadi Procopciuc3a580e92024-06-11 18:39:58 +0300698}
699
700void s32cc_clk_register_drv(void)
701{
702 static const struct clk_ops s32cc_clk_ops = {
703 .enable = s32cc_clk_enable,
704 .disable = s32cc_clk_disable,
705 .is_enabled = s32cc_clk_is_enabled,
706 .get_rate = s32cc_clk_get_rate,
707 .set_rate = s32cc_clk_set_rate,
708 .get_parent = s32cc_clk_get_parent,
709 .set_parent = s32cc_clk_set_parent,
710 };
711
712 clk_register(&s32cc_clk_ops);
713}
714