/* $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,
};

