blob: d571fa671b089b80eff420807ed80229e1d52b2a [file] [log] [blame]
Kongyang Liu80bf1f72024-06-11 17:41:14 +08001// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2023 Inochi Amaoto <inochiama@outlook.com>
4 */
5
6#include <dm.h>
7#include <div64.h>
8#include <linux/clk-provider.h>
9#include <linux/io.h>
10
11#include "clk-common.h"
12#include "clk-ip.h"
13
14static int get_parent_index(struct clk *clk, const char *const *parent_name,
15 u8 num_parents)
16{
17 const char *name = clk_hw_get_name(clk);
18 int i;
19
20 for (i = 0; i < num_parents; i++) {
21 if (!strcmp(name, parent_name[i]))
22 return i;
23 }
24
25 return -1;
26}
27
28/* GATE */
29#define to_cv1800b_clk_gate(_clk) \
30 container_of(_clk, struct cv1800b_clk_gate, clk)
31
32static int gate_enable(struct clk *clk)
33{
34 struct cv1800b_clk_gate *gate = to_cv1800b_clk_gate(clk);
35
36 return cv1800b_clk_setbit(gate->base, &gate->gate);
37}
38
39static int gate_disable(struct clk *clk)
40{
41 struct cv1800b_clk_gate *gate = to_cv1800b_clk_gate(clk);
42
43 return cv1800b_clk_clrbit(gate->base, &gate->gate);
44}
45
46static ulong gate_get_rate(struct clk *clk)
47{
48 return clk_get_parent_rate(clk);
49}
50
51const struct clk_ops cv1800b_clk_gate_ops = {
52 .disable = gate_disable,
53 .enable = gate_enable,
54 .get_rate = gate_get_rate,
55};
56
57U_BOOT_DRIVER(cv1800b_clk_gate) = {
58 .name = "cv1800b_clk_gate",
59 .id = UCLASS_CLK,
60 .ops = &cv1800b_clk_gate_ops,
61 .flags = DM_FLAG_PRE_RELOC,
62};
63
64/* DIV */
65#define CLK_DIV_EN_FACTOR BIT(3)
66
67#define to_cv1800b_clk_div(_clk) container_of(_clk, struct cv1800b_clk_div, clk)
68
69static int div_enable(struct clk *clk)
70{
71 struct cv1800b_clk_div *div = to_cv1800b_clk_div(clk);
72
73 return cv1800b_clk_setbit(div->base, &div->gate);
74}
75
76static int div_disable(struct clk *clk)
77{
78 struct cv1800b_clk_div *div = to_cv1800b_clk_div(clk);
79
80 return cv1800b_clk_clrbit(div->base, &div->gate);
81}
82
83static ulong div_get_rate(struct clk *clk)
84{
85 struct cv1800b_clk_div *div = to_cv1800b_clk_div(clk);
86 ulong val;
87
88 if (div->div_init == 0 ||
89 readl(div->base + div->div.offset) & CLK_DIV_EN_FACTOR)
90 val = cv1800b_clk_getfield(div->base, &div->div);
91 else
92 val = div->div_init;
93
94 return DIV_ROUND_UP_ULL(clk_get_parent_rate(clk), val);
95}
96
97static ulong div_set_rate(struct clk *clk, ulong rate)
98{
99 struct cv1800b_clk_div *div = to_cv1800b_clk_div(clk);
100 ulong parent_rate = clk_get_parent_rate(clk);
101 u32 val;
102
103 val = DIV_ROUND_UP_ULL(parent_rate, rate);
104 val = min_t(u32, val, clk_div_mask(div->div.width));
105
106 cv1800b_clk_setfield(div->base, &div->div, val);
107 if (div->div_init > 0)
108 setbits_le32(div->base + div->div.offset, CLK_DIV_EN_FACTOR);
109
110 return DIV_ROUND_UP_ULL(parent_rate, val);
111}
112
113const struct clk_ops cv1800b_clk_div_ops = {
114 .disable = div_disable,
115 .enable = div_enable,
116 .get_rate = div_get_rate,
117 .set_rate = div_set_rate,
118};
119
120U_BOOT_DRIVER(cv1800b_clk_div) = {
121 .name = "cv1800b_clk_div",
122 .id = UCLASS_CLK,
123 .ops = &cv1800b_clk_div_ops,
124 .flags = DM_FLAG_PRE_RELOC,
125};
126
127#define to_cv1800b_clk_bypass_div(_clk) \
128 container_of(_clk, struct cv1800b_clk_bypass_div, div.clk)
129
130static ulong bypass_div_get_rate(struct clk *clk)
131{
132 struct cv1800b_clk_bypass_div *div = to_cv1800b_clk_bypass_div(clk);
133
134 if (cv1800b_clk_getbit(div->div.base, &div->bypass))
135 return 0;
136
137 return div_get_rate(clk);
138}
139
140static ulong bypass_div_set_rate(struct clk *clk, ulong rate)
141{
142 struct cv1800b_clk_bypass_div *div = to_cv1800b_clk_bypass_div(clk);
143
144 if (cv1800b_clk_getbit(div->div.base, &div->bypass))
145 return 0;
146
147 return div_set_rate(clk, rate);
148}
149
150static int bypass_div_set_parent(struct clk *clk, struct clk *pclk)
151{
152 struct cv1800b_clk_bypass_div *div = to_cv1800b_clk_bypass_div(clk);
153
154 if (pclk->id == CV1800B_CLK_BYPASS) {
155 cv1800b_clk_setbit(div->div.base, &div->bypass);
156 return 0;
157 }
158
159 if (strcmp(clk_hw_get_name(pclk), div->div.parent_name))
160 return -EINVAL;
161
162 cv1800b_clk_clrbit(div->div.base, &div->bypass);
163 return 0;
164}
165
166const struct clk_ops cv1800b_clk_bypass_div_ops = {
167 .disable = div_disable,
168 .enable = div_enable,
169 .get_rate = bypass_div_get_rate,
170 .set_rate = bypass_div_set_rate,
171 .set_parent = bypass_div_set_parent,
172};
173
174U_BOOT_DRIVER(cv1800b_clk_bypass_div) = {
175 .name = "cv1800b_clk_bypass_div",
176 .id = UCLASS_CLK,
177 .ops = &cv1800b_clk_bypass_div_ops,
178 .flags = DM_FLAG_PRE_RELOC,
179};
180
181/* FIXED DIV */
182#define to_cv1800b_clk_fixed_div(_clk) \
183 container_of(_clk, struct cv1800b_clk_fixed_div, clk)
184
185static int fixed_div_enable(struct clk *clk)
186{
187 struct cv1800b_clk_fixed_div *div = to_cv1800b_clk_fixed_div(clk);
188
189 return cv1800b_clk_setbit(div->base, &div->gate);
190}
191
192static int fixed_div_disable(struct clk *clk)
193{
194 struct cv1800b_clk_fixed_div *div = to_cv1800b_clk_fixed_div(clk);
195
196 return cv1800b_clk_clrbit(div->base, &div->gate);
197}
198
199static ulong fixed_div_get_rate(struct clk *clk)
200{
201 struct cv1800b_clk_fixed_div *div = to_cv1800b_clk_fixed_div(clk);
202
203 return DIV_ROUND_UP_ULL(clk_get_parent_rate(clk), div->div);
204}
205
206const struct clk_ops cv1800b_clk_fixed_div_ops = {
207 .disable = fixed_div_disable,
208 .enable = fixed_div_enable,
209 .get_rate = fixed_div_get_rate,
210};
211
212U_BOOT_DRIVER(cv1800b_clk_fixed_div) = {
213 .name = "cv1800b_clk_fixed_div",
214 .id = UCLASS_CLK,
215 .ops = &cv1800b_clk_fixed_div_ops,
216 .flags = DM_FLAG_PRE_RELOC,
217};
218
219#define to_cv1800b_clk_bypass_fixed_div(_clk) \
220 container_of(_clk, struct cv1800b_clk_bypass_fixed_div, div.clk)
221
222static ulong bypass_fixed_div_get_rate(struct clk *clk)
223{
224 struct cv1800b_clk_bypass_fixed_div *div =
225 to_cv1800b_clk_bypass_fixed_div(clk);
226
227 if (cv1800b_clk_getbit(div->div.base, &div->bypass))
228 return 0;
229
230 return fixed_div_get_rate(clk);
231}
232
233static int bypass_fixed_div_set_parent(struct clk *clk, struct clk *pclk)
234{
235 struct cv1800b_clk_bypass_fixed_div *div =
236 to_cv1800b_clk_bypass_fixed_div(clk);
237
238 if (pclk->id == CV1800B_CLK_BYPASS) {
239 cv1800b_clk_setbit(div->div.base, &div->bypass);
240 return 0;
241 }
242
243 if (strcmp(clk_hw_get_name(pclk), div->div.parent_name))
244 return -EINVAL;
245
246 cv1800b_clk_clrbit(div->div.base, &div->bypass);
247 return 0;
248}
249
250const struct clk_ops cv1800b_clk_bypass_fixed_div_ops = {
251 .disable = fixed_div_disable,
252 .enable = fixed_div_enable,
253 .get_rate = bypass_fixed_div_get_rate,
254 .set_parent = bypass_fixed_div_set_parent,
255};
256
257U_BOOT_DRIVER(cv1800b_clk_bypass_fixed_div) = {
258 .name = "cv1800b_clk_bypass_fixed_div",
259 .id = UCLASS_CLK,
260 .ops = &cv1800b_clk_bypass_fixed_div_ops,
261 .flags = DM_FLAG_PRE_RELOC,
262};
263
264/* MUX */
265#define to_cv1800b_clk_mux(_clk) container_of(_clk, struct cv1800b_clk_mux, clk)
266
267static int mux_enable(struct clk *clk)
268{
269 struct cv1800b_clk_mux *mux = to_cv1800b_clk_mux(clk);
270
271 return cv1800b_clk_setbit(mux->base, &mux->gate);
272}
273
274static int mux_disable(struct clk *clk)
275{
276 struct cv1800b_clk_mux *mux = to_cv1800b_clk_mux(clk);
277
278 return cv1800b_clk_clrbit(mux->base, &mux->gate);
279}
280
281static ulong mux_get_rate(struct clk *clk)
282{
283 struct cv1800b_clk_mux *mux = to_cv1800b_clk_mux(clk);
284 ulong val;
285
286 if (mux->div_init == 0 ||
287 readl(mux->base + mux->div.offset) & CLK_DIV_EN_FACTOR)
288 val = cv1800b_clk_getfield(mux->base, &mux->div);
289 else
290 val = mux->div_init;
291
292 return DIV_ROUND_UP_ULL(clk_get_parent_rate(clk), val);
293}
294
295static ulong mux_set_rate(struct clk *clk, ulong rate)
296{
297 struct cv1800b_clk_mux *mux = to_cv1800b_clk_mux(clk);
298 ulong parent_rate = clk_get_parent_rate(clk);
299 ulong val;
300
301 val = DIV_ROUND_UP_ULL(parent_rate, rate);
302 val = min_t(u32, val, clk_div_mask(mux->div.width));
303
304 cv1800b_clk_setfield(mux->base, &mux->div, val);
305 if (mux->div_init > 0)
306 setbits_le32(mux->base + mux->div.offset, CLK_DIV_EN_FACTOR);
307
308 return DIV_ROUND_UP_ULL(parent_rate, val);
309}
310
311static int mux_set_parent(struct clk *clk, struct clk *pclk)
312{
313 struct cv1800b_clk_mux *mux = to_cv1800b_clk_mux(clk);
314 int index = get_parent_index(pclk, mux->parent_names, mux->num_parents);
315
316 if (index < 0)
317 return -EINVAL;
318
319 cv1800b_clk_setfield(mux->base, &mux->mux, index);
320 return 0;
321}
322
323const struct clk_ops cv1800b_clk_mux_ops = {
324 .disable = mux_disable,
325 .enable = mux_enable,
326 .get_rate = mux_get_rate,
327 .set_rate = mux_set_rate,
328 .set_parent = mux_set_parent,
329};
330
331U_BOOT_DRIVER(cv1800b_clk_mux) = {
332 .name = "cv1800b_clk_mux",
333 .id = UCLASS_CLK,
334 .ops = &cv1800b_clk_mux_ops,
335 .flags = DM_FLAG_PRE_RELOC,
336};
337
338#define to_cv1800b_clk_bypass_mux(_clk) \
339 container_of(_clk, struct cv1800b_clk_bypass_mux, mux.clk)
340
341static ulong bypass_mux_get_rate(struct clk *clk)
342{
343 struct cv1800b_clk_bypass_mux *mux = to_cv1800b_clk_bypass_mux(clk);
344
345 if (cv1800b_clk_getbit(mux->mux.base, &mux->bypass))
346 return 0;
347
348 return mux_get_rate(clk);
349}
350
351static ulong bypass_mux_set_rate(struct clk *clk, ulong rate)
352{
353 struct cv1800b_clk_bypass_mux *mux = to_cv1800b_clk_bypass_mux(clk);
354
355 if (cv1800b_clk_getbit(mux->mux.base, &mux->bypass))
356 return 0;
357
358 return mux_set_rate(clk, rate);
359}
360
361static int bypass_mux_set_parent(struct clk *clk, struct clk *pclk)
362{
363 struct cv1800b_clk_bypass_mux *mux = to_cv1800b_clk_bypass_mux(clk);
364 int index;
365
366 if (pclk->id == CV1800B_CLK_BYPASS) {
367 cv1800b_clk_setbit(mux->mux.base, &mux->bypass);
368 return 0;
369 }
370
371 index = get_parent_index(pclk, mux->mux.parent_names,
372 mux->mux.num_parents);
373 if (index < 0)
374 return -EINVAL;
375
376 cv1800b_clk_clrbit(mux->mux.base, &mux->bypass);
377 cv1800b_clk_setfield(mux->mux.base, &mux->mux.mux, index);
378 return 0;
379}
380
381const struct clk_ops cv1800b_clk_bypass_mux_ops = {
382 .disable = mux_disable,
383 .enable = mux_enable,
384 .get_rate = bypass_mux_get_rate,
385 .set_rate = bypass_mux_set_rate,
386 .set_parent = bypass_mux_set_parent,
387};
388
389U_BOOT_DRIVER(cv1800b_clk_bypass_mux) = {
390 .name = "cv1800b_clk_bypass_mux",
391 .id = UCLASS_CLK,
392 .ops = &cv1800b_clk_bypass_mux_ops,
393 .flags = DM_FLAG_PRE_RELOC,
394};
395
396/* MMUX */
397#define to_cv1800b_clk_mmux(_clk) \
398 container_of(_clk, struct cv1800b_clk_mmux, clk)
399
400static int mmux_enable(struct clk *clk)
401{
402 struct cv1800b_clk_mmux *mmux = to_cv1800b_clk_mmux(clk);
403
404 return cv1800b_clk_setbit(mmux->base, &mmux->gate);
405}
406
407static int mmux_disable(struct clk *clk)
408{
409 struct cv1800b_clk_mmux *mmux = to_cv1800b_clk_mmux(clk);
410
411 return cv1800b_clk_clrbit(mmux->base, &mmux->gate);
412}
413
414static ulong mmux_get_rate(struct clk *clk)
415{
416 struct cv1800b_clk_mmux *mmux = to_cv1800b_clk_mmux(clk);
417 int clk_sel = 1;
418 ulong reg, val;
419
420 if (cv1800b_clk_getbit(mmux->base, &mmux->bypass))
421 return 0;
422
423 if (cv1800b_clk_getbit(mmux->base, &mmux->clk_sel))
424 clk_sel = 0;
425
426 reg = readl(mmux->base + mmux->div[clk_sel].offset);
427
428 if (mmux->div_init[clk_sel] == 0 || reg & CLK_DIV_EN_FACTOR)
429 val = cv1800b_clk_getfield(mmux->base, &mmux->div[clk_sel]);
430 else
431 val = mmux->div_init[clk_sel];
432
433 return DIV_ROUND_UP_ULL(clk_get_parent_rate(clk), val);
434}
435
436static ulong mmux_set_rate(struct clk *clk, ulong rate)
437{
438 struct cv1800b_clk_mmux *mmux = to_cv1800b_clk_mmux(clk);
439 int clk_sel = 1;
440 ulong parent_rate = clk_get_parent_rate(clk);
441 ulong val;
442
443 if (cv1800b_clk_getbit(mmux->base, &mmux->bypass))
444 return 0;
445
446 if (cv1800b_clk_getbit(mmux->base, &mmux->clk_sel))
447 clk_sel = 0;
448
449 val = DIV_ROUND_UP_ULL(parent_rate, rate);
450 val = min_t(u32, val, clk_div_mask(mmux->div[clk_sel].width));
451
452 cv1800b_clk_setfield(mmux->base, &mmux->div[clk_sel], val);
453 if (mmux->div_init[clk_sel] > 0)
454 setbits_le32(mmux->base + mmux->div[clk_sel].offset,
455 CLK_DIV_EN_FACTOR);
456
457 return DIV_ROUND_UP_ULL(parent_rate, val);
458}
459
460static int mmux_set_parent(struct clk *clk, struct clk *pclk)
461{
462 struct cv1800b_clk_mmux *mmux = to_cv1800b_clk_mmux(clk);
463 const char *pname = clk_hw_get_name(pclk);
464 int i;
465 u8 clk_sel, index;
466
467 if (pclk->id == CV1800B_CLK_BYPASS) {
468 cv1800b_clk_setbit(mmux->base, &mmux->bypass);
469 return 0;
470 }
471
472 for (i = 0; i < mmux->num_parents; i++) {
473 if (!strcmp(pname, mmux->parent_infos[i].name))
474 break;
475 }
476
477 if (i == mmux->num_parents)
478 return -EINVAL;
479
480 clk_sel = mmux->parent_infos[i].clk_sel;
481 index = mmux->parent_infos[i].index;
482 cv1800b_clk_clrbit(mmux->base, &mmux->bypass);
483 if (clk_sel)
484 cv1800b_clk_clrbit(mmux->base, &mmux->clk_sel);
485 else
486 cv1800b_clk_setbit(mmux->base, &mmux->clk_sel);
487
488 cv1800b_clk_setfield(mmux->base, &mmux->mux[clk_sel], index);
489 return 0;
490}
491
492const struct clk_ops cv1800b_clk_mmux_ops = {
493 .disable = mmux_disable,
494 .enable = mmux_enable,
495 .get_rate = mmux_get_rate,
496 .set_rate = mmux_set_rate,
497 .set_parent = mmux_set_parent,
498};
499
500U_BOOT_DRIVER(cv1800b_clk_mmux) = {
501 .name = "cv1800b_clk_mmux",
502 .id = UCLASS_CLK,
503 .ops = &cv1800b_clk_mmux_ops,
504 .flags = DM_FLAG_PRE_RELOC,
505};
506
507/* AUDIO CLK */
508#define to_cv1800b_clk_audio(_clk) \
509 container_of(_clk, struct cv1800b_clk_audio, clk)
510
511static int aclk_enable(struct clk *clk)
512{
513 struct cv1800b_clk_audio *aclk = to_cv1800b_clk_audio(clk);
514
515 cv1800b_clk_setbit(aclk->base, &aclk->src_en);
516 cv1800b_clk_setbit(aclk->base, &aclk->output_en);
517 return 0;
518}
519
520static int aclk_disable(struct clk *clk)
521{
522 struct cv1800b_clk_audio *aclk = to_cv1800b_clk_audio(clk);
523
524 cv1800b_clk_clrbit(aclk->base, &aclk->src_en);
525 cv1800b_clk_clrbit(aclk->base, &aclk->output_en);
526 return 0;
527}
528
529static ulong aclk_get_rate(struct clk *clk)
530{
531 struct cv1800b_clk_audio *aclk = to_cv1800b_clk_audio(clk);
532 u64 parent_rate = clk_get_parent_rate(clk);
533 u32 m, n;
534
535 if (!cv1800b_clk_getbit(aclk->base, &aclk->div_en))
536 return 0;
537
538 m = cv1800b_clk_getfield(aclk->base, &aclk->m);
539 n = cv1800b_clk_getfield(aclk->base, &aclk->n);
540
541 return DIV_ROUND_UP_ULL(n * parent_rate, m * 2);
542}
543
544static u32 gcd(u32 a, u32 b)
545{
546 u32 t;
547
548 while (b != 0) {
549 t = a % b;
550 a = b;
551 b = t;
552 }
553 return a;
554}
555
556static void aclk_determine_mn(ulong parent_rate, ulong rate, u32 *m, u32 *n)
557{
558 u32 tm = parent_rate / 2;
559 u32 tn = rate;
560 u32 tcommon = gcd(tm, tn);
561 *m = tm / tcommon;
562 *n = tn / tcommon;
563}
564
565static ulong aclk_set_rate(struct clk *clk, ulong rate)
566{
567 struct cv1800b_clk_audio *aclk = to_cv1800b_clk_audio(clk);
568 ulong parent_rate = clk_get_parent_rate(clk);
569 u32 m, n;
570
571 aclk_determine_mn(parent_rate, rate, &m, &n);
572
573 cv1800b_clk_setfield(aclk->base, &aclk->m, m);
574 cv1800b_clk_setfield(aclk->base, &aclk->n, n);
575
576 cv1800b_clk_setbit(aclk->base, &aclk->div_en);
577 cv1800b_clk_setbit(aclk->base, &aclk->div_up);
578
579 return DIV_ROUND_UP_ULL(parent_rate * n, m * 2);
580}
581
582const struct clk_ops cv1800b_clk_audio_ops = {
583 .disable = aclk_disable,
584 .enable = aclk_enable,
585 .get_rate = aclk_get_rate,
586 .set_rate = aclk_set_rate,
587};
588
589U_BOOT_DRIVER(cv1800b_clk_audio) = {
590 .name = "cv1800b_clk_audio",
591 .id = UCLASS_CLK,
592 .ops = &cv1800b_clk_audio_ops,
593 .flags = DM_FLAG_PRE_RELOC,
594};