xref: /netbsd-src/sys/arch/riscv/starfive/jh71x0_clkc.c (revision 5ba7948dde72014892196cb46717fae4e291af4d)
1 /* $NetBSD: jh71x0_clkc.c,v 1.4 2024/09/18 08:31: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.4 2024/09/18 08:31: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 	KASSERT(i <= __SHIFTOUT_MASK(JH71X0_CLK_MUX_MASK));
157 
158 	uint32_t val = RD4(sc, jcc->jcc_reg);
159 	val &= ~JH71X0_CLK_MUX_MASK;
160 	val |= __SHIFTIN(i, JH71X0_CLK_MUX_MASK);
161 	WR4(sc, jcc->jcc_reg, val);
162 
163 	return 0;
164 }
165 
166 
167 const char *
168 jh71x0_clkc_mux_get_parent(struct jh71x0_clkc_softc *sc,
169     struct jh71x0_clkc_clk *jcc)
170 {
171 	KASSERT(jcc->jcc_type == JH71X0CLK_MUX);
172 
173 	uint32_t val = RD4(sc, jcc->jcc_reg);
174 	size_t pindex = __SHIFTOUT(val, JH71X0_CLK_MUX_MASK);
175 
176 	if (pindex >= jcc->jcc_mux.jcm_nparents)
177 		return NULL;
178 
179 	return jcc->jcc_mux.jcm_parents[pindex];
180 }
181 
182 
183 /*
184  * GATE operations
185  */
186 
187 int
188 jh71x0_clkc_gate_enable(struct jh71x0_clkc_softc *sc,
189     struct jh71x0_clkc_clk *jcc, int enable)
190 {
191 	KASSERT(jcc->jcc_type == JH71X0CLK_GATE);
192 
193 	jh71x0_clkc_update(sc, jcc,
194 	    (enable ? JH71X0_CLK_ENABLE : 0), JH71X0_CLK_ENABLE);
195 
196 	return 0;
197 }
198 
199 const char *
200 jh71x0_clkc_gate_get_parent(struct jh71x0_clkc_softc *sc,
201     struct jh71x0_clkc_clk *jcc)
202 {
203 	KASSERT(jcc->jcc_type == JH71X0CLK_GATE);
204 
205 	struct jh71x0_clkc_gate *jcc_gate = &jcc->jcc_gate;
206 
207 	return jcc_gate->jcg_parent;
208 }
209 
210 
211 /*
212  * DIVIDER operations
213  */
214 
215 u_int
216 jh71x0_clkc_div_get_rate(struct jh71x0_clkc_softc *sc,
217     struct jh71x0_clkc_clk *jcc)
218 {
219 	KASSERT(jcc->jcc_type == JH71X0CLK_DIV);
220 
221 	struct clk * const clk = &jcc->jcc_clk;
222 	struct clk * const clk_parent = clk_get_parent(clk);
223 
224 	if (clk_parent == NULL)
225 		return 0;
226 
227 	u_int rate = clk_get_rate(clk_parent);
228 	if (rate == 0)
229 		return 0;
230 
231 	uint32_t val = RD4(sc, jcc->jcc_reg);
232 	uint32_t div = __SHIFTOUT(val, JH71X0_CLK_DIV_MASK);
233 
234 	return rate / div;
235 }
236 
237 int
238 jh71x0_clkc_div_set_rate(struct jh71x0_clkc_softc *sc,
239     struct jh71x0_clkc_clk *jcc, u_int new_rate)
240 {
241 	KASSERT(jcc->jcc_type == JH71X0CLK_DIV);
242 
243 	struct jh71x0_clkc_div * const jcc_div = &jcc->jcc_div;
244 	struct clk * const clk = &jcc->jcc_clk;
245 	struct clk * const clk_parent = clk_get_parent(clk);
246 
247 	if (clk_parent == NULL)
248 		return ENXIO;
249 
250 	if (jcc_div->jcd_maxdiv == 0)
251 		return ENXIO;
252 
253 	u_int parent_rate = clk_get_rate(clk_parent);
254 	if (parent_rate == 0) {
255 		return (new_rate == 0) ? 0 : ERANGE;
256 	}
257 	u_int ratio = howmany(parent_rate, new_rate);
258 	u_int div = uimin(ratio, jcc_div->jcd_maxdiv);
259 
260 	KASSERT(div <= __SHIFTOUT_MASK(JH71X0_CLK_DIV_MASK));
261 
262 	jh71x0_clkc_update(sc, jcc,
263 	    __SHIFTIN(div, JH71X0_CLK_DIV_MASK), JH71X0_CLK_DIV_MASK);
264 
265 	return 0;
266 }
267 
268 const char *
269 jh71x0_clkc_div_get_parent(struct jh71x0_clkc_softc *sc,
270     struct jh71x0_clkc_clk *jcc)
271 {
272 	KASSERT(jcc->jcc_type == JH71X0CLK_DIV);
273 
274 	struct jh71x0_clkc_div *jcc_div = &jcc->jcc_div;
275 
276 	return jcc_div->jcd_parent;
277 }
278 
279 
280 /*
281  * FRACTIONAL DIVIDER operations
282  */
283 
284 u_int
285 jh71x0_clkc_fracdiv_get_rate(struct jh71x0_clkc_softc *sc,
286     struct jh71x0_clkc_clk *jcc)
287 {
288 	KASSERT(jcc->jcc_type == JH71X0CLK_FRACDIV);
289 
290 	struct clk * const clk = &jcc->jcc_clk;
291 	struct clk * const clk_parent = clk_get_parent(clk);
292 
293 	if (clk_parent == NULL)
294 		return 0;
295 
296 
297 	u_int rate = clk_get_rate(clk_parent);
298 	if (rate == 0)
299 		return 0;
300 
301 	uint32_t val = RD4(sc, jcc->jcc_reg);
302 	unsigned long div100 =
303 	    100UL * __SHIFTOUT(val, JH71X0_CLK_INT_MASK) +
304 		    __SHIFTOUT(val, JH71X0_CLK_FRAC_MASK);
305 
306 	return (div100 >= JH71X0_CLK_FRAC_MIN) ? 100UL * rate / div100 : 0;
307 }
308 
309 int
310 jh71x0_clkc_fracdiv_set_rate(struct jh71x0_clkc_softc *sc,
311     struct jh71x0_clkc_clk *jcc, u_int new_rate)
312 {
313 	KASSERT(jcc->jcc_type == JH71X0CLK_FRACDIV);
314 
315 	struct clk * const clk = &jcc->jcc_clk;
316 	struct clk * const clk_parent = clk_get_parent(clk);
317 
318 	if (clk_parent == NULL)
319 		return ENXIO;
320 
321 #if 0
322 	if (jcc_div->jcd_maxdiv == 0)
323 		return ENXIO;
324 
325 	u_int parent_rate = clk_get_rate(clk_parent);
326 
327 	if (parent_rate == 0) {
328 		return (new_rate == 0) ? 0 : ERANGE;
329 	}
330 	u_int ratio = howmany(parent_rate, new_rate);
331 	u_int div = uimin(ratio, jcc_div->jcd_maxdiv);
332 
333 	KASSERT(div <= __SHIFTOUT_MASK(JH71X0_CLK_DIV_MASK));
334 
335 	jh71x0_clkc_update(sc, jcc,
336 	    __SHIFTIN(div, JH71X0_CLK_DIV_MASK), JH71X0_CLK_DIV_MASK);
337 #endif
338 
339 	return 0;
340 }
341 
342 const char *
343 jh71x0_clkc_fracdiv_get_parent(struct jh71x0_clkc_softc *sc,
344     struct jh71x0_clkc_clk *jcc)
345 {
346 	KASSERT(jcc->jcc_type == JH71X0CLK_FRACDIV);
347 
348 	struct jh71x0_clkc_fracdiv *jcc_fracdiv = &jcc->jcc_fracdiv;
349 
350 	return jcc_fracdiv->jcd_parent;
351 }
352 
353 
354 /*
355  * MUXDIV operations
356  */
357 
358 
359 int
360 jh71x0_clkc_muxdiv_set_parent(struct jh71x0_clkc_softc *sc,
361     struct jh71x0_clkc_clk *jcc, const char *name)
362 {
363 	KASSERT(jcc->jcc_type == JH71X0CLK_MUXDIV);
364 
365 	struct jh71x0_clkc_muxdiv * const jcmd = &jcc->jcc_muxdiv;
366 
367 	size_t i;
368 	for (i = 0; i < jcmd->jcmd_nparents; i++) {
369 		if (jcmd->jcmd_parents[i] != NULL &&
370 		    strcmp(jcmd->jcmd_parents[i], name) == 0)
371 			break;
372 	}
373 	if (i >= jcmd->jcmd_nparents)
374 		return EINVAL;
375 
376 	KASSERT(i <= __SHIFTOUT_MASK(JH71X0_CLK_MUX_MASK));
377 
378 	uint32_t val = RD4(sc, jcc->jcc_reg);
379 	val &= ~JH71X0_CLK_MUX_MASK;
380 	val |= __SHIFTIN(i, JH71X0_CLK_MUX_MASK);
381 	WR4(sc, jcc->jcc_reg, val);
382 
383 	return 0;
384 }
385 
386 
387 const char *
388 jh71x0_clkc_muxdiv_get_parent(struct jh71x0_clkc_softc *sc,
389     struct jh71x0_clkc_clk *jcc)
390 {
391 	KASSERT(jcc->jcc_type == JH71X0CLK_MUXDIV);
392 
393 	uint32_t val = RD4(sc, jcc->jcc_reg);
394 	size_t pindex = __SHIFTOUT(val, JH71X0_CLK_MUX_MASK);
395 
396 	if (pindex >= jcc->jcc_muxdiv.jcmd_nparents)
397 		return NULL;
398 
399 	return jcc->jcc_muxdiv.jcmd_parents[pindex];
400 }
401 
402 
403 
404 u_int
405 jh71x0_clkc_muxdiv_get_rate(struct jh71x0_clkc_softc *sc,
406     struct jh71x0_clkc_clk *jcc)
407 {
408 	KASSERT(jcc->jcc_type == JH71X0CLK_MUXDIV);
409 
410 	struct clk * const clk = &jcc->jcc_clk;
411 	struct clk * const clk_parent = clk_get_parent(clk);
412 
413 	if (clk_parent == NULL)
414 		return 0;
415 
416 	u_int rate = clk_get_rate(clk_parent);
417 	if (rate == 0)
418 		return 0;
419 
420 	uint32_t val = RD4(sc, jcc->jcc_reg);
421 	uint32_t div = __SHIFTOUT(val, JH71X0_CLK_DIV_MASK);
422 
423 	return rate / div;
424 }
425 
426 int
427 jh71x0_clkc_muxdiv_set_rate(struct jh71x0_clkc_softc *sc,
428     struct jh71x0_clkc_clk *jcc, u_int new_rate)
429 {
430 	KASSERT(jcc->jcc_type == JH71X0CLK_MUXDIV);
431 
432 	struct jh71x0_clkc_muxdiv * const jcc_muxdiv = &jcc->jcc_muxdiv;
433 	struct clk * const clk = &jcc->jcc_clk;
434 	struct clk * const clk_parent = clk_get_parent(clk);
435 
436 	if (clk_parent == NULL)
437 		return ENXIO;
438 
439 	if (jcc_muxdiv->jcmd_maxdiv == 0)
440 		return ENXIO;
441 
442 	u_int parent_rate = clk_get_rate(clk_parent);
443 	if (parent_rate == 0) {
444 		return (new_rate == 0) ? 0 : ERANGE;
445 	}
446 	u_int ratio = howmany(parent_rate, new_rate);
447 	u_int div = uimin(ratio, jcc_muxdiv->jcmd_maxdiv);
448 
449 	KASSERT(div <= __SHIFTOUT_MASK(JH71X0_CLK_DIV_MASK));
450 
451 	jh71x0_clkc_update(sc, jcc,
452 	    __SHIFTIN(div, JH71X0_CLK_DIV_MASK), JH71X0_CLK_DIV_MASK);
453 
454 	return 0;
455 }
456 
457 
458 /*
459  * INV operations
460  */
461 const char *
462 jh71x0_clkc_inv_get_parent(struct jh71x0_clkc_softc *sc,
463     struct jh71x0_clkc_clk *jcc)
464 {
465 	KASSERT(jcc->jcc_type == JH71X0CLK_INV);
466 
467 	struct jh71x0_clkc_inv * const jci = &jcc->jcc_inv;
468 
469 	return jci->jci_parent;
470 }
471 
472 
473 struct jh71x0_clkc_clkops jh71x0_clkc_gate_ops = {
474 	.jcco_enable = jh71x0_clkc_gate_enable,
475 	.jcco_getparent = jh71x0_clkc_gate_get_parent,
476 };
477 
478 struct jh71x0_clkc_clkops jh71x0_clkc_div_ops = {
479 	.jcco_setrate = jh71x0_clkc_div_set_rate,
480 	.jcco_getrate = jh71x0_clkc_div_get_rate,
481 	.jcco_getparent = jh71x0_clkc_div_get_parent,
482 };
483 
484 struct jh71x0_clkc_clkops jh71x0_clkc_fracdiv_ops = {
485 	.jcco_setrate = jh71x0_clkc_fracdiv_set_rate,
486 	.jcco_getrate = jh71x0_clkc_fracdiv_get_rate,
487 	.jcco_getparent = jh71x0_clkc_fracdiv_get_parent,
488 };
489 
490 struct jh71x0_clkc_clkops jh71x0_clkc_ffactor_ops = {
491 	.jcco_setrate = jh71x0_clkc_fixed_factor_set_rate,
492 	.jcco_getrate = jh71x0_clkc_fixed_factor_get_rate,
493 	.jcco_getparent = jh71x0_clkc_fixed_factor_get_parent,
494 };
495 
496 
497 struct jh71x0_clkc_clkops jh71x0_clkc_mux_ops = {
498 	.jcco_setparent = jh71x0_clkc_mux_set_parent,
499 	.jcco_getparent = jh71x0_clkc_mux_get_parent,
500 };
501 
502 struct jh71x0_clkc_clkops jh71x0_clkc_muxdiv_ops = {
503 	.jcco_setrate = jh71x0_clkc_muxdiv_set_rate,
504 	.jcco_getrate = jh71x0_clkc_muxdiv_get_rate,
505 	.jcco_setparent = jh71x0_clkc_muxdiv_set_parent,
506 	.jcco_getparent = jh71x0_clkc_muxdiv_get_parent,
507 };
508 
509 
510 struct jh71x0_clkc_clkops jh71x0_clkc_inv_ops = {
511 	.jcco_getparent = jh71x0_clkc_inv_get_parent,
512 };
513 
514 static struct clk *
515 jh71x0_clkc_get(void *priv, const char *name)
516 {
517 	struct jh71x0_clkc_softc * const sc = priv;
518 
519 	for (u_int id = 0; id < sc->sc_nclks; id++) {
520 		struct jh71x0_clkc_clk * const jcc = &sc->sc_clk[id];
521 
522 		if (strcmp(name, jcc->jcc_clk.name) == 0) {
523 			return &jcc->jcc_clk;
524 		}
525 	}
526 
527 	return NULL;
528 }
529 
530 static void
531 jh71x0_clkc_put(void *priv, struct clk *clk)
532 {
533 }
534 
535 
536 static int
537 jh71x0_clkc_set_rate(void *priv, struct clk *clk, u_int rate)
538 {
539 	struct jh71x0_clkc_softc * const sc = priv;
540 	struct jh71x0_clkc_clk * const jcc =
541 	    container_of(clk, struct jh71x0_clkc_clk, jcc_clk);
542 
543 	if (clk->flags & CLK_SET_RATE_PARENT) {
544 		struct clk *clk_parent = clk_get_parent(clk);
545 		if (clk_parent == NULL) {
546 			aprint_debug("%s: no parent for %s\n", __func__,
547 			    jcc->jcc_clk.name);
548 			return ENXIO;
549 		}
550 		return clk_set_rate(clk_parent, rate);
551 	}
552 
553 	if (jcc->jcc_ops->jcco_setrate)
554 		return jcc->jcc_ops->jcco_setrate(sc, jcc, rate);
555 
556 	return ENXIO;
557 }
558 
559 static u_int
560 jh71x0_clkc_get_rate(void *priv, struct clk *clk)
561 {
562 	struct jh71x0_clkc_softc * const sc = priv;
563 	struct jh71x0_clkc_clk * const jcc =
564 	    container_of(clk, struct jh71x0_clkc_clk, jcc_clk);
565 
566 	if (jcc->jcc_ops->jcco_getrate)
567 		return jcc->jcc_ops->jcco_getrate(sc, jcc);
568 
569 	struct clk * const clk_parent = clk_get_parent(clk);
570 	if (clk_parent == NULL) {
571 		aprint_debug("%s: no parent for %s\n", __func__,
572 		    jcc->jcc_clk.name);
573 		return 0;
574 	}
575 
576 	return clk_get_rate(clk_parent);
577 }
578 
579 static int
580 jh71x0_clkc_enable(void *priv, struct clk *clk)
581 {
582 	struct jh71x0_clkc_softc * const sc = priv;
583 	struct jh71x0_clkc_clk * const jcc =
584 	    container_of(clk, struct jh71x0_clkc_clk, jcc_clk);
585 
586 	struct clk * const clk_parent = clk_get_parent(clk);
587 	if (clk_parent != NULL) {
588 		int error = clk_enable(clk_parent);
589 		if (error != 0)
590 			return error;
591 	}
592 
593 	switch (jcc->jcc_type) {
594 	case JH71X0CLK_GATE:
595 		jh71x0_clkc_update(sc, jcc, JH71X0_CLK_ENABLE, 0);
596 		break;
597 
598 	case JH71X0CLK_DIV: {
599 		struct jh71x0_clkc_div * const jcc_div = &jcc->jcc_div;
600 		if (jcc_div->jcd_flags & JH71X0CLKC_DIV_GATE) {
601 			jh71x0_clkc_update(sc, jcc, JH71X0_CLK_ENABLE, 0);
602 		}
603 		break;
604 	    }
605 
606 	case JH71X0CLK_MUX: {
607 		struct jh71x0_clkc_mux * const jcc_mux = &jcc->jcc_mux;
608 		if (jcc_mux->jcm_flags & JH71X0CLKC_MUX_GATE) {
609 			jh71x0_clkc_update(sc, jcc, JH71X0_CLK_ENABLE, 0);
610 		}
611 		break;
612 	    }
613 
614 	case JH71X0CLK_FIXED_FACTOR:
615 	case JH71X0CLK_INV:
616 	case JH71X0CLK_MUXDIV:
617 		break;
618 
619 	default:
620 		printf("%s: type %d\n", __func__, jcc->jcc_type);
621 		return ENXIO;
622 	}
623 	return 0;
624 }
625 
626 static int
627 jh71x0_clkc_disable(void *priv, struct clk *clk)
628 {
629 	struct jh71x0_clkc_softc * const sc = priv;
630 	struct jh71x0_clkc_clk * const jcc =
631 	    container_of(clk, struct jh71x0_clkc_clk, jcc_clk);
632 
633 	switch (jcc->jcc_type) {
634 	case JH71X0CLK_GATE:
635 		jh71x0_clkc_update(sc, jcc, 0, JH71X0_CLK_ENABLE);
636 		break;
637 
638 	case JH71X0CLK_DIV: {
639 		struct jh71x0_clkc_div * const jcc_div = &jcc->jcc_div;
640 		if (jcc_div->jcd_flags & JH71X0CLKC_DIV_GATE) {
641 			jh71x0_clkc_update(sc, jcc, 0, JH71X0_CLK_ENABLE);
642 		}
643 		break;
644 	    }
645 
646 	case JH71X0CLK_MUX: {
647 		struct jh71x0_clkc_mux * const jcc_mux = &jcc->jcc_mux;
648 		if (jcc_mux->jcm_flags & JH71X0CLKC_MUX_GATE) {
649 			jh71x0_clkc_update(sc, jcc, 0, JH71X0_CLK_ENABLE);
650 		}
651 		break;
652 	    }
653 
654 	case JH71X0CLK_FIXED_FACTOR:
655 	case JH71X0CLK_INV:
656 	case JH71X0CLK_MUXDIV:
657 		break;
658 
659 	default:
660 		return ENXIO;
661 	}
662 	return 0;
663 }
664 
665 
666 
667 static struct jh71x0_clkc_clk *
668 jh71x0_clkc_clock_find(struct jh71x0_clkc_softc *sc, const char *name)
669 {
670 	for (size_t id = 0; id < sc->sc_nclks; id++) {
671 		struct jh71x0_clkc_clk * const jcc = &sc->sc_clk[id];
672 
673 		if (jcc->jcc_clk.name == NULL)
674 			continue;
675 		if (strcmp(jcc->jcc_clk.name, name) == 0)
676 			return jcc;
677 	}
678 
679 	return NULL;
680 }
681 
682 
683 
684 static int
685 jh71x0_clkc_set_parent(void *priv, struct clk *clk,
686     struct clk *clk_parent)
687 {
688 	struct jh71x0_clkc_softc * const sc = priv;
689 	struct jh71x0_clkc_clk * const jcc =
690 	    container_of(clk, struct jh71x0_clkc_clk, jcc_clk);
691 
692 	if (jcc->jcc_ops->jcco_setparent == NULL)
693 		return EINVAL;
694 
695 	return jcc->jcc_ops->jcco_setparent(sc, jcc, clk_parent->name);
696 }
697 
698 
699 static struct clk *
700 jh71x0_clkc_get_parent(void *priv, struct clk *clk)
701 {
702 	struct jh71x0_clkc_softc * const sc = priv;
703 	struct jh71x0_clkc_clk * const jcc =
704 	    container_of(clk, struct jh71x0_clkc_clk, jcc_clk);
705 
706 	if (jcc->jcc_ops->jcco_getparent == NULL)
707 		return NULL;
708 
709 	const char *parent = jcc->jcc_ops->jcco_getparent(sc, jcc);
710 	if (parent == NULL)
711 		return NULL;
712 
713 	struct jh71x0_clkc_clk *jcc_parent = jh71x0_clkc_clock_find(sc, parent);
714 	if (jcc_parent != NULL)
715 		return &jcc_parent->jcc_clk;
716 
717 	/* No parent in this domain, try FDT */
718 	return fdtbus_clock_get(sc->sc_phandle, parent);
719 }
720 
721 
722 const struct clk_funcs jh71x0_clkc_funcs = {
723 	.get = jh71x0_clkc_get,
724 	.put = jh71x0_clkc_put,
725 	.set_rate = jh71x0_clkc_set_rate,
726 	.get_rate = jh71x0_clkc_get_rate,
727 	.enable = jh71x0_clkc_enable,
728 	.disable = jh71x0_clkc_disable,
729 	.set_parent = jh71x0_clkc_set_parent,
730 	.get_parent = jh71x0_clkc_get_parent,
731 };
732 
733