Home | History | Annotate | Line # | Download | only in starfive
      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