/* $NetBSD: jh71x0_clkc.c,v 1.4 2024/09/18 08:31:50 skrll Exp $ */

/*-
* Copyright (c) 2023 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Nick Hudson
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in the
*    documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: jh71x0_clkc.c,v 1.4 2024/09/18 08:31:50 skrll Exp $");

#include <sys/param.h>

#include <sys/bus.h>
#include <sys/device.h>

#include <dev/clk/clk_backend.h>

#include <dev/fdt/fdtvar.h>

#include <riscv/starfive/jh71x0_clkc.h>

#define RD4(sc, reg)                                                    \
       bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
#define WR4(sc, reg, val)                                               \
       bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))


static void
jh71x0_clkc_update(struct jh71x0_clkc_softc * const sc,
   struct jh71x0_clkc_clk *jcc, uint32_t set, uint32_t clr)
{
       // lock
       uint32_t val = RD4(sc, jcc->jcc_reg);
       val &= ~clr;
       val |=  set;
       WR4(sc, jcc->jcc_reg, val);
}

/*
* FIXED_FACTOR operations
*/

static u_int
jh71x0_clkc_fixed_factor_get_parent_rate(struct clk *clk)
{
       struct clk *clk_parent = clk_get_parent(clk);
       if (clk_parent == NULL)
               return 0;

       return clk_get_rate(clk_parent);
}

u_int
jh71x0_clkc_fixed_factor_get_rate(struct jh71x0_clkc_softc *sc,
   struct jh71x0_clkc_clk *jcc)
{
       KASSERT(jcc->jcc_type == JH71X0CLK_FIXED_FACTOR);

       struct jh71x0_clkc_fixed_factor * const jcff = &jcc->jcc_ffactor;
       struct clk *clk = &jcc->jcc_clk;

       uint64_t rate = jh71x0_clkc_fixed_factor_get_parent_rate(clk);
       if (rate == 0)
               return 0;

       rate *= jcff->jcff_mult;
       rate /= jcff->jcff_div;

       return rate;
}

static int
jh71x0_clkc_fixed_factor_set_parent_rate(struct clk *clk, u_int rate)
{
       struct clk *clk_parent = clk_get_parent(clk);
       if (clk_parent == NULL)
               return ENXIO;

       return clk_set_rate(clk_parent, rate);
}

int
jh71x0_clkc_fixed_factor_set_rate(struct jh71x0_clkc_softc *sc,
   struct jh71x0_clkc_clk *jcc, u_int rate)
{
       KASSERT(jcc->jcc_type == JH71X0CLK_FIXED_FACTOR);

       struct jh71x0_clkc_fixed_factor * const jcff = &jcc->jcc_ffactor;
       struct clk *clk = &jcc->jcc_clk;


       uint64_t tmp = rate;
       tmp *= jcff->jcff_div;
       tmp /= jcff->jcff_mult;

       return jh71x0_clkc_fixed_factor_set_parent_rate(clk, tmp);
}

const char *
jh71x0_clkc_fixed_factor_get_parent(struct jh71x0_clkc_softc *sc,
   struct jh71x0_clkc_clk *jcc)
{
       KASSERT(jcc->jcc_type == JH71X0CLK_FIXED_FACTOR);

       struct jh71x0_clkc_fixed_factor * const jcff = &jcc->jcc_ffactor;

       return jcff->jcff_parent;
}


/*
* MUX operations
*/

int
jh71x0_clkc_mux_set_parent(struct jh71x0_clkc_softc *sc,
   struct jh71x0_clkc_clk *jcc, const char *name)
{
       KASSERT(jcc->jcc_type == JH71X0CLK_MUX);

       struct jh71x0_clkc_mux * const jcm = &jcc->jcc_mux;

       size_t i;
       for (i = 0; i < jcm->jcm_nparents; i++) {
               if (jcm->jcm_parents[i] != NULL &&
                   strcmp(jcm->jcm_parents[i], name) == 0)
                       break;
       }
       if (i >= jcm->jcm_nparents)
               return EINVAL;

       KASSERT(i <= __SHIFTOUT_MASK(JH71X0_CLK_MUX_MASK));

       uint32_t val = RD4(sc, jcc->jcc_reg);
       val &= ~JH71X0_CLK_MUX_MASK;
       val |= __SHIFTIN(i, JH71X0_CLK_MUX_MASK);
       WR4(sc, jcc->jcc_reg, val);

       return 0;
}


const char *
jh71x0_clkc_mux_get_parent(struct jh71x0_clkc_softc *sc,
   struct jh71x0_clkc_clk *jcc)
{
       KASSERT(jcc->jcc_type == JH71X0CLK_MUX);

       uint32_t val = RD4(sc, jcc->jcc_reg);
       size_t pindex = __SHIFTOUT(val, JH71X0_CLK_MUX_MASK);

       if (pindex >= jcc->jcc_mux.jcm_nparents)
               return NULL;

       return jcc->jcc_mux.jcm_parents[pindex];
}


/*
* GATE operations
*/

int
jh71x0_clkc_gate_enable(struct jh71x0_clkc_softc *sc,
   struct jh71x0_clkc_clk *jcc, int enable)
{
       KASSERT(jcc->jcc_type == JH71X0CLK_GATE);

       jh71x0_clkc_update(sc, jcc,
           (enable ? JH71X0_CLK_ENABLE : 0), JH71X0_CLK_ENABLE);

       return 0;
}

const char *
jh71x0_clkc_gate_get_parent(struct jh71x0_clkc_softc *sc,
   struct jh71x0_clkc_clk *jcc)
{
       KASSERT(jcc->jcc_type == JH71X0CLK_GATE);

       struct jh71x0_clkc_gate *jcc_gate = &jcc->jcc_gate;

       return jcc_gate->jcg_parent;
}


/*
* DIVIDER operations
*/

u_int
jh71x0_clkc_div_get_rate(struct jh71x0_clkc_softc *sc,
   struct jh71x0_clkc_clk *jcc)
{
       KASSERT(jcc->jcc_type == JH71X0CLK_DIV);

       struct clk * const clk = &jcc->jcc_clk;
       struct clk * const clk_parent = clk_get_parent(clk);

       if (clk_parent == NULL)
               return 0;

       u_int rate = clk_get_rate(clk_parent);
       if (rate == 0)
               return 0;

       uint32_t val = RD4(sc, jcc->jcc_reg);
       uint32_t div = __SHIFTOUT(val, JH71X0_CLK_DIV_MASK);

       return rate / div;
}

int
jh71x0_clkc_div_set_rate(struct jh71x0_clkc_softc *sc,
   struct jh71x0_clkc_clk *jcc, u_int new_rate)
{
       KASSERT(jcc->jcc_type == JH71X0CLK_DIV);

       struct jh71x0_clkc_div * const jcc_div = &jcc->jcc_div;
       struct clk * const clk = &jcc->jcc_clk;
       struct clk * const clk_parent = clk_get_parent(clk);

       if (clk_parent == NULL)
               return ENXIO;

       if (jcc_div->jcd_maxdiv == 0)
               return ENXIO;

       u_int parent_rate = clk_get_rate(clk_parent);
       if (parent_rate == 0) {
               return (new_rate == 0) ? 0 : ERANGE;
       }
       u_int ratio = howmany(parent_rate, new_rate);
       u_int div = uimin(ratio, jcc_div->jcd_maxdiv);

       KASSERT(div <= __SHIFTOUT_MASK(JH71X0_CLK_DIV_MASK));

       jh71x0_clkc_update(sc, jcc,
           __SHIFTIN(div, JH71X0_CLK_DIV_MASK), JH71X0_CLK_DIV_MASK);

       return 0;
}

const char *
jh71x0_clkc_div_get_parent(struct jh71x0_clkc_softc *sc,
   struct jh71x0_clkc_clk *jcc)
{
       KASSERT(jcc->jcc_type == JH71X0CLK_DIV);

       struct jh71x0_clkc_div *jcc_div = &jcc->jcc_div;

       return jcc_div->jcd_parent;
}


/*
* FRACTIONAL DIVIDER operations
*/

u_int
jh71x0_clkc_fracdiv_get_rate(struct jh71x0_clkc_softc *sc,
   struct jh71x0_clkc_clk *jcc)
{
       KASSERT(jcc->jcc_type == JH71X0CLK_FRACDIV);

       struct clk * const clk = &jcc->jcc_clk;
       struct clk * const clk_parent = clk_get_parent(clk);

       if (clk_parent == NULL)
               return 0;


       u_int rate = clk_get_rate(clk_parent);
       if (rate == 0)
               return 0;

       uint32_t val = RD4(sc, jcc->jcc_reg);
       unsigned long div100 =
           100UL * __SHIFTOUT(val, JH71X0_CLK_INT_MASK) +
                   __SHIFTOUT(val, JH71X0_CLK_FRAC_MASK);

       return (div100 >= JH71X0_CLK_FRAC_MIN) ? 100UL * rate / div100 : 0;
}

int
jh71x0_clkc_fracdiv_set_rate(struct jh71x0_clkc_softc *sc,
   struct jh71x0_clkc_clk *jcc, u_int new_rate)
{
       KASSERT(jcc->jcc_type == JH71X0CLK_FRACDIV);

       struct clk * const clk = &jcc->jcc_clk;
       struct clk * const clk_parent = clk_get_parent(clk);

       if (clk_parent == NULL)
               return ENXIO;

#if 0
       if (jcc_div->jcd_maxdiv == 0)
               return ENXIO;

       u_int parent_rate = clk_get_rate(clk_parent);

       if (parent_rate == 0) {
               return (new_rate == 0) ? 0 : ERANGE;
       }
       u_int ratio = howmany(parent_rate, new_rate);
       u_int div = uimin(ratio, jcc_div->jcd_maxdiv);

       KASSERT(div <= __SHIFTOUT_MASK(JH71X0_CLK_DIV_MASK));

       jh71x0_clkc_update(sc, jcc,
           __SHIFTIN(div, JH71X0_CLK_DIV_MASK), JH71X0_CLK_DIV_MASK);
#endif

       return 0;
}

const char *
jh71x0_clkc_fracdiv_get_parent(struct jh71x0_clkc_softc *sc,
   struct jh71x0_clkc_clk *jcc)
{
       KASSERT(jcc->jcc_type == JH71X0CLK_FRACDIV);

       struct jh71x0_clkc_fracdiv *jcc_fracdiv = &jcc->jcc_fracdiv;

       return jcc_fracdiv->jcd_parent;
}


/*
* MUXDIV operations
*/


int
jh71x0_clkc_muxdiv_set_parent(struct jh71x0_clkc_softc *sc,
   struct jh71x0_clkc_clk *jcc, const char *name)
{
       KASSERT(jcc->jcc_type == JH71X0CLK_MUXDIV);

       struct jh71x0_clkc_muxdiv * const jcmd = &jcc->jcc_muxdiv;

       size_t i;
       for (i = 0; i < jcmd->jcmd_nparents; i++) {
               if (jcmd->jcmd_parents[i] != NULL &&
                   strcmp(jcmd->jcmd_parents[i], name) == 0)
                       break;
       }
       if (i >= jcmd->jcmd_nparents)
               return EINVAL;

       KASSERT(i <= __SHIFTOUT_MASK(JH71X0_CLK_MUX_MASK));

       uint32_t val = RD4(sc, jcc->jcc_reg);
       val &= ~JH71X0_CLK_MUX_MASK;
       val |= __SHIFTIN(i, JH71X0_CLK_MUX_MASK);
       WR4(sc, jcc->jcc_reg, val);

       return 0;
}


const char *
jh71x0_clkc_muxdiv_get_parent(struct jh71x0_clkc_softc *sc,
   struct jh71x0_clkc_clk *jcc)
{
       KASSERT(jcc->jcc_type == JH71X0CLK_MUXDIV);

       uint32_t val = RD4(sc, jcc->jcc_reg);
       size_t pindex = __SHIFTOUT(val, JH71X0_CLK_MUX_MASK);

       if (pindex >= jcc->jcc_muxdiv.jcmd_nparents)
               return NULL;

       return jcc->jcc_muxdiv.jcmd_parents[pindex];
}



u_int
jh71x0_clkc_muxdiv_get_rate(struct jh71x0_clkc_softc *sc,
   struct jh71x0_clkc_clk *jcc)
{
       KASSERT(jcc->jcc_type == JH71X0CLK_MUXDIV);

       struct clk * const clk = &jcc->jcc_clk;
       struct clk * const clk_parent = clk_get_parent(clk);

       if (clk_parent == NULL)
               return 0;

       u_int rate = clk_get_rate(clk_parent);
       if (rate == 0)
               return 0;

       uint32_t val = RD4(sc, jcc->jcc_reg);
       uint32_t div = __SHIFTOUT(val, JH71X0_CLK_DIV_MASK);

       return rate / div;
}

int
jh71x0_clkc_muxdiv_set_rate(struct jh71x0_clkc_softc *sc,
   struct jh71x0_clkc_clk *jcc, u_int new_rate)
{
       KASSERT(jcc->jcc_type == JH71X0CLK_MUXDIV);

       struct jh71x0_clkc_muxdiv * const jcc_muxdiv = &jcc->jcc_muxdiv;
       struct clk * const clk = &jcc->jcc_clk;
       struct clk * const clk_parent = clk_get_parent(clk);

       if (clk_parent == NULL)
               return ENXIO;

       if (jcc_muxdiv->jcmd_maxdiv == 0)
               return ENXIO;

       u_int parent_rate = clk_get_rate(clk_parent);
       if (parent_rate == 0) {
               return (new_rate == 0) ? 0 : ERANGE;
       }
       u_int ratio = howmany(parent_rate, new_rate);
       u_int div = uimin(ratio, jcc_muxdiv->jcmd_maxdiv);

       KASSERT(div <= __SHIFTOUT_MASK(JH71X0_CLK_DIV_MASK));

       jh71x0_clkc_update(sc, jcc,
           __SHIFTIN(div, JH71X0_CLK_DIV_MASK), JH71X0_CLK_DIV_MASK);

       return 0;
}


/*
* INV operations
*/
const char *
jh71x0_clkc_inv_get_parent(struct jh71x0_clkc_softc *sc,
   struct jh71x0_clkc_clk *jcc)
{
       KASSERT(jcc->jcc_type == JH71X0CLK_INV);

       struct jh71x0_clkc_inv * const jci = &jcc->jcc_inv;

       return jci->jci_parent;
}


struct jh71x0_clkc_clkops jh71x0_clkc_gate_ops = {
       .jcco_enable = jh71x0_clkc_gate_enable,
       .jcco_getparent = jh71x0_clkc_gate_get_parent,
};

struct jh71x0_clkc_clkops jh71x0_clkc_div_ops = {
       .jcco_setrate = jh71x0_clkc_div_set_rate,
       .jcco_getrate = jh71x0_clkc_div_get_rate,
       .jcco_getparent = jh71x0_clkc_div_get_parent,
};

struct jh71x0_clkc_clkops jh71x0_clkc_fracdiv_ops = {
       .jcco_setrate = jh71x0_clkc_fracdiv_set_rate,
       .jcco_getrate = jh71x0_clkc_fracdiv_get_rate,
       .jcco_getparent = jh71x0_clkc_fracdiv_get_parent,
};

struct jh71x0_clkc_clkops jh71x0_clkc_ffactor_ops = {
       .jcco_setrate = jh71x0_clkc_fixed_factor_set_rate,
       .jcco_getrate = jh71x0_clkc_fixed_factor_get_rate,
       .jcco_getparent = jh71x0_clkc_fixed_factor_get_parent,
};


struct jh71x0_clkc_clkops jh71x0_clkc_mux_ops = {
       .jcco_setparent = jh71x0_clkc_mux_set_parent,
       .jcco_getparent = jh71x0_clkc_mux_get_parent,
};

struct jh71x0_clkc_clkops jh71x0_clkc_muxdiv_ops = {
       .jcco_setrate = jh71x0_clkc_muxdiv_set_rate,
       .jcco_getrate = jh71x0_clkc_muxdiv_get_rate,
       .jcco_setparent = jh71x0_clkc_muxdiv_set_parent,
       .jcco_getparent = jh71x0_clkc_muxdiv_get_parent,
};


struct jh71x0_clkc_clkops jh71x0_clkc_inv_ops = {
       .jcco_getparent = jh71x0_clkc_inv_get_parent,
};

static struct clk *
jh71x0_clkc_get(void *priv, const char *name)
{
       struct jh71x0_clkc_softc * const sc = priv;

       for (u_int id = 0; id < sc->sc_nclks; id++) {
               struct jh71x0_clkc_clk * const jcc = &sc->sc_clk[id];

               if (strcmp(name, jcc->jcc_clk.name) == 0) {
                       return &jcc->jcc_clk;
               }
       }

       return NULL;
}

static void
jh71x0_clkc_put(void *priv, struct clk *clk)
{
}


static int
jh71x0_clkc_set_rate(void *priv, struct clk *clk, u_int rate)
{
       struct jh71x0_clkc_softc * const sc = priv;
       struct jh71x0_clkc_clk * const jcc =
           container_of(clk, struct jh71x0_clkc_clk, jcc_clk);

       if (clk->flags & CLK_SET_RATE_PARENT) {
               struct clk *clk_parent = clk_get_parent(clk);
               if (clk_parent == NULL) {
                       aprint_debug("%s: no parent for %s\n", __func__,
                           jcc->jcc_clk.name);
                       return ENXIO;
               }
               return clk_set_rate(clk_parent, rate);
       }

       if (jcc->jcc_ops->jcco_setrate)
               return jcc->jcc_ops->jcco_setrate(sc, jcc, rate);

       return ENXIO;
}

static u_int
jh71x0_clkc_get_rate(void *priv, struct clk *clk)
{
       struct jh71x0_clkc_softc * const sc = priv;
       struct jh71x0_clkc_clk * const jcc =
           container_of(clk, struct jh71x0_clkc_clk, jcc_clk);

       if (jcc->jcc_ops->jcco_getrate)
               return jcc->jcc_ops->jcco_getrate(sc, jcc);

       struct clk * const clk_parent = clk_get_parent(clk);
       if (clk_parent == NULL) {
               aprint_debug("%s: no parent for %s\n", __func__,
                   jcc->jcc_clk.name);
               return 0;
       }

       return clk_get_rate(clk_parent);
}

static int
jh71x0_clkc_enable(void *priv, struct clk *clk)
{
       struct jh71x0_clkc_softc * const sc = priv;
       struct jh71x0_clkc_clk * const jcc =
           container_of(clk, struct jh71x0_clkc_clk, jcc_clk);

       struct clk * const clk_parent = clk_get_parent(clk);
       if (clk_parent != NULL) {
               int error = clk_enable(clk_parent);
               if (error != 0)
                       return error;
       }

       switch (jcc->jcc_type) {
       case JH71X0CLK_GATE:
               jh71x0_clkc_update(sc, jcc, JH71X0_CLK_ENABLE, 0);
               break;

       case JH71X0CLK_DIV: {
               struct jh71x0_clkc_div * const jcc_div = &jcc->jcc_div;
               if (jcc_div->jcd_flags & JH71X0CLKC_DIV_GATE) {
                       jh71x0_clkc_update(sc, jcc, JH71X0_CLK_ENABLE, 0);
               }
               break;
           }

       case JH71X0CLK_MUX: {
               struct jh71x0_clkc_mux * const jcc_mux = &jcc->jcc_mux;
               if (jcc_mux->jcm_flags & JH71X0CLKC_MUX_GATE) {
                       jh71x0_clkc_update(sc, jcc, JH71X0_CLK_ENABLE, 0);
               }
               break;
           }

       case JH71X0CLK_FIXED_FACTOR:
       case JH71X0CLK_INV:
       case JH71X0CLK_MUXDIV:
               break;

       default:
               printf("%s: type %d\n", __func__, jcc->jcc_type);
               return ENXIO;
       }
       return 0;
}

static int
jh71x0_clkc_disable(void *priv, struct clk *clk)
{
       struct jh71x0_clkc_softc * const sc = priv;
       struct jh71x0_clkc_clk * const jcc =
           container_of(clk, struct jh71x0_clkc_clk, jcc_clk);

       switch (jcc->jcc_type) {
       case JH71X0CLK_GATE:
               jh71x0_clkc_update(sc, jcc, 0, JH71X0_CLK_ENABLE);
               break;

       case JH71X0CLK_DIV: {
               struct jh71x0_clkc_div * const jcc_div = &jcc->jcc_div;
               if (jcc_div->jcd_flags & JH71X0CLKC_DIV_GATE) {
                       jh71x0_clkc_update(sc, jcc, 0, JH71X0_CLK_ENABLE);
               }
               break;
           }

       case JH71X0CLK_MUX: {
               struct jh71x0_clkc_mux * const jcc_mux = &jcc->jcc_mux;
               if (jcc_mux->jcm_flags & JH71X0CLKC_MUX_GATE) {
                       jh71x0_clkc_update(sc, jcc, 0, JH71X0_CLK_ENABLE);
               }
               break;
           }

       case JH71X0CLK_FIXED_FACTOR:
       case JH71X0CLK_INV:
       case JH71X0CLK_MUXDIV:
               break;

       default:
               return ENXIO;
       }
       return 0;
}



static struct jh71x0_clkc_clk *
jh71x0_clkc_clock_find(struct jh71x0_clkc_softc *sc, const char *name)
{
       for (size_t id = 0; id < sc->sc_nclks; id++) {
               struct jh71x0_clkc_clk * const jcc = &sc->sc_clk[id];

               if (jcc->jcc_clk.name == NULL)
                       continue;
               if (strcmp(jcc->jcc_clk.name, name) == 0)
                       return jcc;
       }

       return NULL;
}



static int
jh71x0_clkc_set_parent(void *priv, struct clk *clk,
   struct clk *clk_parent)
{
       struct jh71x0_clkc_softc * const sc = priv;
       struct jh71x0_clkc_clk * const jcc =
           container_of(clk, struct jh71x0_clkc_clk, jcc_clk);

       if (jcc->jcc_ops->jcco_setparent == NULL)
               return EINVAL;

       return jcc->jcc_ops->jcco_setparent(sc, jcc, clk_parent->name);
}


static struct clk *
jh71x0_clkc_get_parent(void *priv, struct clk *clk)
{
       struct jh71x0_clkc_softc * const sc = priv;
       struct jh71x0_clkc_clk * const jcc =
           container_of(clk, struct jh71x0_clkc_clk, jcc_clk);

       if (jcc->jcc_ops->jcco_getparent == NULL)
               return NULL;

       const char *parent = jcc->jcc_ops->jcco_getparent(sc, jcc);
       if (parent == NULL)
               return NULL;

       struct jh71x0_clkc_clk *jcc_parent = jh71x0_clkc_clock_find(sc, parent);
       if (jcc_parent != NULL)
               return &jcc_parent->jcc_clk;

       /* No parent in this domain, try FDT */
       return fdtbus_clock_get(sc->sc_phandle, parent);
}


const struct clk_funcs jh71x0_clkc_funcs = {
       .get = jh71x0_clkc_get,
       .put = jh71x0_clkc_put,
       .set_rate = jh71x0_clkc_set_rate,
       .get_rate = jh71x0_clkc_get_rate,
       .enable = jh71x0_clkc_enable,
       .disable = jh71x0_clkc_disable,
       .set_parent = jh71x0_clkc_set_parent,
       .get_parent = jh71x0_clkc_get_parent,
};