jh71x0_clkc.c revision 1.1 1 /* $NetBSD: jh71x0_clkc.c,v 1.1 2024/07/27 07:09:50 skrll Exp $ */
2
3 /*-
4 * Copyright (c) 2023 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Nick Hudson
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: jh71x0_clkc.c,v 1.1 2024/07/27 07:09:50 skrll Exp $");
34
35 #include <sys/param.h>
36
37 #include <sys/bus.h>
38 #include <sys/device.h>
39
40 #include <dev/clk/clk_backend.h>
41
42 #include <dev/fdt/fdtvar.h>
43
44 #include <riscv/starfive/jh71x0_clkc.h>
45
46 #define RD4(sc, reg) \
47 bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
48 #define WR4(sc, reg, val) \
49 bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
50
51
52 static void
53 jh71x0_clkc_update(struct jh71x0_clkc_softc * const sc,
54 struct jh71x0_clkc_clk *jcc, uint32_t set, uint32_t clr)
55 {
56 // lock
57 uint32_t val = RD4(sc, jcc->jcc_reg);
58 val &= ~clr;
59 val |= set;
60 WR4(sc, jcc->jcc_reg, val);
61 }
62
63 /*
64 * FIXED_FACTOR operations
65 */
66
67 static u_int
68 jh71x0_clkc_fixed_factor_get_parent_rate(struct clk *clk)
69 {
70 struct clk *clk_parent = clk_get_parent(clk);
71 if (clk_parent == NULL)
72 return 0;
73
74 return clk_get_rate(clk_parent);
75 }
76
77 u_int
78 jh71x0_clkc_fixed_factor_get_rate(struct jh71x0_clkc_softc *sc,
79 struct jh71x0_clkc_clk *jcc)
80 {
81 KASSERT(jcc->jcc_type == JH71X0CLK_FIXED_FACTOR);
82
83 struct jh71x0_clkc_fixed_factor * const jcff = &jcc->jcc_ffactor;
84 struct clk *clk = &jcc->jcc_clk;
85
86 uint64_t rate = jh71x0_clkc_fixed_factor_get_parent_rate(clk);
87 if (rate == 0)
88 return 0;
89
90 rate *= jcff->jcff_mult;
91 rate /= jcff->jcff_div;
92
93 return rate;
94 }
95
96 static int
97 jh71x0_clkc_fixed_factor_set_parent_rate(struct clk *clk, u_int rate)
98 {
99 struct clk *clk_parent = clk_get_parent(clk);
100 if (clk_parent == NULL)
101 return ENXIO;
102
103 return clk_set_rate(clk_parent, rate);
104 }
105
106 int
107 jh71x0_clkc_fixed_factor_set_rate(struct jh71x0_clkc_softc *sc,
108 struct jh71x0_clkc_clk *jcc, u_int rate)
109 {
110 KASSERT(jcc->jcc_type == JH71X0CLK_FIXED_FACTOR);
111
112 struct jh71x0_clkc_fixed_factor * const jcff = &jcc->jcc_ffactor;
113 struct clk *clk = &jcc->jcc_clk;
114
115
116 uint64_t tmp = rate;
117 tmp *= jcff->jcff_div;
118 tmp /= jcff->jcff_mult;
119
120 return jh71x0_clkc_fixed_factor_set_parent_rate(clk, tmp);
121 }
122
123 const char *
124 jh71x0_clkc_fixed_factor_get_parent(struct jh71x0_clkc_softc *sc,
125 struct jh71x0_clkc_clk *jcc)
126 {
127 KASSERT(jcc->jcc_type == JH71X0CLK_FIXED_FACTOR);
128
129 struct jh71x0_clkc_fixed_factor * const jcff = &jcc->jcc_ffactor;
130
131 return jcff->jcff_parent;
132 }
133
134
135 /*
136 * MUX operations
137 */
138
139 int
140 jh71x0_clkc_mux_set_parent(struct jh71x0_clkc_softc *sc,
141 struct jh71x0_clkc_clk *jcc, const char *name)
142 {
143 KASSERT(jcc->jcc_type == JH71X0CLK_MUX);
144
145 struct jh71x0_clkc_mux * const jcm = &jcc->jcc_mux;
146
147 size_t i;
148 for (i = 0; i < jcm->jcm_nparents; i++) {
149 if (jcm->jcm_parents[i] != NULL &&
150 strcmp(jcm->jcm_parents[i], name) == 0)
151 break;
152 }
153 if (i >= jcm->jcm_nparents)
154 return EINVAL;
155
156 uint32_t val = RD4(sc, jcc->jcc_reg);
157 val &= ~JH71X0_CLK_MUX_MASK;
158 val |= __SHIFTIN(i, JH71X0_CLK_MUX_MASK);
159 WR4(sc, jcc->jcc_reg, val);
160
161 return 0;
162 }
163
164
165 const char *
166 jh71x0_clkc_mux_get_parent(struct jh71x0_clkc_softc *sc,
167 struct jh71x0_clkc_clk *jcc)
168 {
169 KASSERT(jcc->jcc_type == JH71X0CLK_MUX);
170
171 uint32_t val = RD4(sc, jcc->jcc_reg);
172 size_t pindex = __SHIFTOUT(val, JH71X0_CLK_MUX_MASK);
173
174 if (pindex >= jcc->jcc_mux.jcm_nparents)
175 return NULL;
176
177 return jcc->jcc_mux.jcm_parents[pindex];
178 }
179
180
181 /*
182 * GATE operations
183 */
184
185 int
186 jh71x0_clkc_gate_enable(struct jh71x0_clkc_softc *sc,
187 struct jh71x0_clkc_clk *jcc, int enable)
188 {
189 KASSERT(jcc->jcc_type == JH71X0CLK_GATE);
190
191 jh71x0_clkc_update(sc, jcc,
192 (enable ? JH71X0_CLK_ENABLE : 0), JH71X0_CLK_ENABLE);
193
194 return 0;
195 }
196
197 const char *
198 jh71x0_clkc_gate_get_parent(struct jh71x0_clkc_softc *sc,
199 struct jh71x0_clkc_clk *jcc)
200 {
201 KASSERT(jcc->jcc_type == JH71X0CLK_GATE);
202
203 struct jh71x0_clkc_gate *jcc_gate = &jcc->jcc_gate;
204
205 return jcc_gate->jcg_parent;
206 }
207
208
209 /*
210 * DIVIDER operations
211 */
212
213 u_int
214 jh71x0_clkc_div_get_rate(struct jh71x0_clkc_softc *sc,
215 struct jh71x0_clkc_clk *jcc)
216 {
217 KASSERT(jcc->jcc_type == JH71X0CLK_DIV);
218
219 struct clk * const clk = &jcc->jcc_clk;
220 struct clk * const clk_parent = clk_get_parent(clk);
221
222 if (clk_parent == NULL)
223 return 0;
224
225 u_int rate = clk_get_rate(clk_parent);
226 if (rate == 0)
227 return 0;
228
229 uint32_t val = RD4(sc, jcc->jcc_reg);
230 uint32_t div = __SHIFTOUT(val, JH71X0_CLK_DIV_MASK);
231
232 return rate / div;
233 }
234
235 int
236 jh71x0_clkc_div_set_rate(struct jh71x0_clkc_softc *sc,
237 struct jh71x0_clkc_clk *jcc, u_int new_rate)
238 {
239 KASSERT(jcc->jcc_type == JH71X0CLK_DIV);
240
241 struct jh71x0_clkc_div * const jcc_div = &jcc->jcc_div;
242 struct clk * const clk = &jcc->jcc_clk;
243 struct clk * const clk_parent = clk_get_parent(clk);
244
245 if (clk_parent == NULL)
246 return ENXIO;
247
248 if (jcc_div->jcd_maxdiv == 0)
249 return ENXIO;
250
251 u_int parent_rate = clk_get_rate(clk_parent);
252
253 if (parent_rate == 0) {
254 return (new_rate == 0) ? 0 : ERANGE;
255 }
256 u_int ratio = howmany(parent_rate, new_rate);
257 u_int div = uimin(ratio, jcc_div->jcd_maxdiv);
258
259 jh71x0_clkc_update(sc, jcc,
260 __SHIFTIN(div, JH71X0_CLK_DIV_MASK), JH71X0_CLK_DIV_MASK);
261
262 return 0;
263 }
264
265 const char *
266 jh71x0_clkc_div_get_parent(struct jh71x0_clkc_softc *sc,
267 struct jh71x0_clkc_clk *jcc)
268 {
269 KASSERT(jcc->jcc_type == JH71X0CLK_DIV);
270
271 struct jh71x0_clkc_div *jcc_div = &jcc->jcc_div;
272
273 return jcc_div->jcd_parent;
274 }
275
276
277 /*
278 * FRACTIONAL DIVIDER operations
279 */
280
281 u_int
282 jh71x0_clkc_fracdiv_get_rate(struct jh71x0_clkc_softc *sc,
283 struct jh71x0_clkc_clk *jcc)
284 {
285 KASSERT(jcc->jcc_type == JH71X0CLK_FRACDIV);
286
287 struct clk * const clk = &jcc->jcc_clk;
288 struct clk * const clk_parent = clk_get_parent(clk);
289
290 if (clk_parent == NULL)
291 return 0;
292
293
294 u_int rate = clk_get_rate(clk_parent);
295 if (rate == 0)
296 return 0;
297
298 uint32_t val = RD4(sc, jcc->jcc_reg);
299 unsigned long div100 =
300 100UL * __SHIFTOUT(val, JH71X0_CLK_INT_MASK) +
301 __SHIFTOUT(val, JH71X0_CLK_FRAC_MASK);
302
303 return (div100 >= JH71X0_CLK_FRAC_MIN) ? 100UL * rate / div100 : 0;
304 }
305
306 int
307 jh71x0_clkc_fracdiv_set_rate(struct jh71x0_clkc_softc *sc,
308 struct jh71x0_clkc_clk *jcc, u_int new_rate)
309 {
310 KASSERT(jcc->jcc_type == JH71X0CLK_FRACDIV);
311
312 // struct jh71x0_clkc_fracdiv * const jcc_fracdiv = &jcc->jcc_fracdiv;
313 struct clk * const clk = &jcc->jcc_clk;
314 struct clk * const clk_parent = clk_get_parent(clk);
315
316 if (clk_parent == NULL)
317 return ENXIO;
318
319 #if 0
320 if (jcc_div->jcd_maxdiv == 0)
321 return ENXIO;
322
323 u_int parent_rate = clk_get_rate(clk_parent);
324
325 if (parent_rate == 0) {
326 return (new_rate == 0) ? 0 : ERANGE;
327 }
328 u_int ratio = howmany(parent_rate, new_rate);
329 u_int div = uimin(ratio, jcc_div->jcd_maxdiv);
330
331 jh71x0_clkc_update(sc, jcc,
332 __SHIFTIN(div, JH71X0_CLK_DIV_MASK), JH71X0_CLK_DIV_MASK);
333 #endif
334
335 return 0;
336 }
337
338 const char *
339 jh71x0_clkc_fracdiv_get_parent(struct jh71x0_clkc_softc *sc,
340 struct jh71x0_clkc_clk *jcc)
341 {
342 KASSERT(jcc->jcc_type == JH71X0CLK_FRACDIV);
343
344 struct jh71x0_clkc_fracdiv *jcc_fracdiv = &jcc->jcc_fracdiv;
345
346 return jcc_fracdiv->jcd_parent;
347 }
348
349
350 /*
351 * INV operations
352 */
353 const char *
354 jh71x0_clkc_inv_get_parent(struct jh71x0_clkc_softc *sc,
355 struct jh71x0_clkc_clk *jcc)
356 {
357 KASSERT(jcc->jcc_type == JH71X0CLK_INV);
358
359 struct jh71x0_clkc_inv * const jci = &jcc->jcc_inv;
360
361 return jci->jci_parent;
362 }
363
364
365 struct jh71x0_clkc_clkops jh71x0_clkc_gate_ops = {
366 .jcco_enable = jh71x0_clkc_gate_enable,
367 .jcco_getparent = jh71x0_clkc_gate_get_parent,
368 };
369
370 struct jh71x0_clkc_clkops jh71x0_clkc_div_ops = {
371 .jcco_setrate = jh71x0_clkc_div_set_rate,
372 .jcco_getrate = jh71x0_clkc_div_get_rate,
373 .jcco_getparent = jh71x0_clkc_div_get_parent,
374 };
375
376 struct jh71x0_clkc_clkops jh71x0_clkc_fracdiv_ops = {
377 .jcco_setrate = jh71x0_clkc_fracdiv_set_rate,
378 .jcco_getrate = jh71x0_clkc_fracdiv_get_rate,
379 .jcco_getparent = jh71x0_clkc_fracdiv_get_parent,
380 };
381
382 struct jh71x0_clkc_clkops jh71x0_clkc_ffactor_ops = {
383 .jcco_setrate = jh71x0_clkc_fixed_factor_set_rate,
384 .jcco_getrate = jh71x0_clkc_fixed_factor_get_rate,
385 .jcco_getparent = jh71x0_clkc_fixed_factor_get_parent,
386 };
387
388
389 struct jh71x0_clkc_clkops jh71x0_clkc_mux_ops = {
390 .jcco_setparent = jh71x0_clkc_mux_set_parent,
391 .jcco_getparent = jh71x0_clkc_mux_get_parent,
392 };
393
394
395 struct jh71x0_clkc_clkops jh71x0_clkc_inv_ops = {
396 .jcco_getparent = jh71x0_clkc_inv_get_parent,
397 };
398
399 static struct clk *
400 jh71x0_clkc_get(void *priv, const char *name)
401 {
402 struct jh71x0_clkc_softc * const sc = priv;
403
404 for (u_int id = 0; id < sc->sc_nclks; id++) {
405 struct jh71x0_clkc_clk * const jcc = &sc->sc_clk[id];
406
407 if (strcmp(name, jcc->jcc_clk.name) == 0) {
408 return &jcc->jcc_clk;
409 }
410 }
411
412 return NULL;
413 }
414
415 static void
416 jh71x0_clkc_put(void *priv, struct clk *clk)
417 {
418 }
419
420
421 static int
422 jh71x0_clkc_set_rate(void *priv, struct clk *clk, u_int rate)
423 {
424 struct jh71x0_clkc_softc * const sc = priv;
425 struct jh71x0_clkc_clk * const jcc =
426 container_of(clk, struct jh71x0_clkc_clk, jcc_clk);
427
428 if (clk->flags & CLK_SET_RATE_PARENT) {
429 struct clk *clk_parent = clk_get_parent(clk);
430 if (clk_parent == NULL) {
431 aprint_debug("%s: no parent for %s\n", __func__,
432 jcc->jcc_clk.name);
433 return ENXIO;
434 }
435 return clk_set_rate(clk_parent, rate);
436 }
437
438 if (jcc->jcc_ops->jcco_setrate)
439 return jcc->jcc_ops->jcco_setrate(sc, jcc, rate);
440
441 return ENXIO;
442 }
443
444 static u_int
445 jh71x0_clkc_get_rate(void *priv, struct clk *clk)
446 {
447 struct jh71x0_clkc_softc * const sc = priv;
448 struct jh71x0_clkc_clk * const jcc =
449 container_of(clk, struct jh71x0_clkc_clk, jcc_clk);
450
451 if (jcc->jcc_ops->jcco_getrate)
452 return jcc->jcc_ops->jcco_getrate(sc, jcc);
453
454 struct clk * const clk_parent = clk_get_parent(clk);
455 if (clk_parent == NULL) {
456 aprint_debug("%s: no parent for %s\n", __func__,
457 jcc->jcc_clk.name);
458 return 0;
459 }
460
461 return clk_get_rate(clk_parent);
462 }
463
464 static int
465 jh71x0_clkc_enable(void *priv, struct clk *clk)
466 {
467 struct jh71x0_clkc_softc * const sc = priv;
468 struct jh71x0_clkc_clk * const jcc =
469 container_of(clk, struct jh71x0_clkc_clk, jcc_clk);
470
471 struct clk * const clk_parent = clk_get_parent(clk);
472 if (clk_parent != NULL) {
473 int error = clk_enable(clk_parent);
474 if (error != 0)
475 return error;
476 }
477
478 switch (jcc->jcc_type) {
479 case JH71X0CLK_GATE:
480 jh71x0_clkc_update(sc, jcc, JH71X0_CLK_ENABLE, 0);
481 break;
482
483 case JH71X0CLK_DIV: {
484 struct jh71x0_clkc_div * const jcc_div = &jcc->jcc_div;
485 if (jcc_div->jcd_flags & JH71X0CLKC_DIV_GATE) {
486 jh71x0_clkc_update(sc, jcc, JH71X0_CLK_ENABLE, 0);
487 break;
488 }
489 break;
490 }
491
492 case JH71X0CLK_FIXED_FACTOR:
493 case JH71X0CLK_MUX:
494 case JH71X0CLK_INV:
495 break;
496
497 default:
498 printf("%s: type %d\n", __func__, jcc->jcc_type);
499 return ENXIO;
500 }
501 return 0;
502 }
503
504 static int
505 jh71x0_clkc_disable(void *priv, struct clk *clk)
506 {
507 struct jh71x0_clkc_softc * const sc = priv;
508 struct jh71x0_clkc_clk * const jcc =
509 container_of(clk, struct jh71x0_clkc_clk, jcc_clk);
510
511 switch (jcc->jcc_type) {
512 case JH71X0CLK_GATE:
513 jh71x0_clkc_update(sc, jcc, JH71X0_CLK_ENABLE, 0);
514 return 0;
515
516 default:
517 return ENXIO;
518 }
519 }
520
521
522
523 static struct jh71x0_clkc_clk *
524 jh71x0_clkc_clock_find(struct jh71x0_clkc_softc *sc, const char *name)
525 {
526 for (size_t id = 0; id < sc->sc_nclks; id++) {
527 struct jh71x0_clkc_clk * const jcc = &sc->sc_clk[id];
528
529 if (jcc->jcc_clk.name == NULL)
530 continue;
531 if (strcmp(jcc->jcc_clk.name, name) == 0)
532 return jcc;
533 }
534
535 return NULL;
536 }
537
538
539
540 static int
541 jh71x0_clkc_set_parent(void *priv, struct clk *clk,
542 struct clk *clk_parent)
543 {
544 struct jh71x0_clkc_softc * const sc = priv;
545 struct jh71x0_clkc_clk * const jcc =
546 container_of(clk, struct jh71x0_clkc_clk, jcc_clk);
547
548 if (jcc->jcc_ops->jcco_setparent == NULL)
549 return EINVAL;
550
551 return jcc->jcc_ops->jcco_setparent(sc, jcc, clk_parent->name);
552 }
553
554
555 static struct clk *
556 jh71x0_clkc_get_parent(void *priv, struct clk *clk)
557 {
558 struct jh71x0_clkc_softc * const sc = priv;
559 struct jh71x0_clkc_clk * const jcc =
560 container_of(clk, struct jh71x0_clkc_clk, jcc_clk);
561
562 if (jcc->jcc_ops->jcco_getparent == NULL)
563 return NULL;
564
565 const char *parent = jcc->jcc_ops->jcco_getparent(sc, jcc);
566 if (parent == NULL)
567 return NULL;
568
569 struct jh71x0_clkc_clk *jcc_parent = jh71x0_clkc_clock_find(sc, parent);
570 if (jcc_parent != NULL)
571 return &jcc_parent->jcc_clk;
572
573 /* No parent in this domain, try FDT */
574 return fdtbus_clock_get(sc->sc_phandle, parent);
575 }
576
577
578 const struct clk_funcs jh71x0_clkc_funcs = {
579 .get = jh71x0_clkc_get,
580 .put = jh71x0_clkc_put,
581 .set_rate = jh71x0_clkc_set_rate,
582 .get_rate = jh71x0_clkc_get_rate,
583 .enable = jh71x0_clkc_enable,
584 .disable = jh71x0_clkc_disable,
585 .set_parent = jh71x0_clkc_set_parent,
586 .get_parent = jh71x0_clkc_get_parent,
587 };
588
589