Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7a29a869 authored by Carlo Caione's avatar Carlo Caione Committed by Stephen Boyd
Browse files

clk: meson: Add support for Meson clock controller



This patchset adds the infrastructure for registering and managing the
core clocks found on Amlogic MesonX SoCs. In particular:

- PLLs
- CPU clock
- Fixed rate clocks, fixed factor clocks, ...

Signed-off-by: default avatarCarlo Caione <carlo@endlessm.com>
Signed-off-by: default avatarStephen Boyd <sboyd@codeaurora.org>
parent b787f68c
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -55,6 +55,7 @@ ifeq ($(CONFIG_COMMON_CLK), y)
obj-$(CONFIG_ARCH_MMP)			+= mmp/
endif
obj-$(CONFIG_PLAT_ORION)		+= mvebu/
obj-$(CONFIG_ARCH_MESON)		+= meson/
obj-$(CONFIG_ARCH_MXS)			+= mxs/
obj-$(CONFIG_MACH_PISTACHIO)		+= pistachio/
obj-$(CONFIG_COMMON_CLK_PXA)		+= pxa/
+5 −0
Original line number Diff line number Diff line
#
# Makefile for Meson specific clk
#

obj-y += clkc.o clk-pll.o clk-cpu.o
+234 −0
Original line number Diff line number Diff line
/*
 * Copyright (c) 2015 Endless Mobile, Inc.
 * Author: Carlo Caione <carlo@endlessm.com>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU General Public License,
 * version 2, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/*
 * CPU clock path:
 *
 *                           +-[/N]-----|3|
 *             MUX2  +--[/3]-+----------|2| MUX1
 * [sys_pll]---|1|   |--[/2]------------|1|-|1|
 *             | |---+------------------|0| | |----- [a5_clk]
 *          +--|0|                          | |
 * [xtal]---+-------------------------------|0|
 *
 *
 *
 */

#include <linux/delay.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/slab.h>
#include <linux/clk-provider.h>

#define MESON_CPU_CLK_CNTL1		0x00
#define MESON_CPU_CLK_CNTL		0x40

#define MESON_CPU_CLK_MUX1		BIT(7)
#define MESON_CPU_CLK_MUX2		BIT(0)

#define MESON_N_WIDTH			9
#define MESON_N_SHIFT			20
#define MESON_SEL_WIDTH			2
#define MESON_SEL_SHIFT			2

#include "clkc.h"

struct meson_clk_cpu {
	struct notifier_block		clk_nb;
	const struct clk_div_table	*div_table;
	struct clk_hw			hw;
	void __iomem			*base;
	u16				reg_off;
};
#define to_meson_clk_cpu_hw(_hw) container_of(_hw, struct meson_clk_cpu, hw)
#define to_meson_clk_cpu_nb(_nb) container_of(_nb, struct meson_clk_cpu, clk_nb)

static long meson_clk_cpu_round_rate(struct clk_hw *hw, unsigned long rate,
				     unsigned long *prate)
{
	struct meson_clk_cpu *clk_cpu = to_meson_clk_cpu_hw(hw);

	return divider_round_rate(hw, rate, prate, clk_cpu->div_table,
				  MESON_N_WIDTH, CLK_DIVIDER_ROUND_CLOSEST);
}

static int meson_clk_cpu_set_rate(struct clk_hw *hw, unsigned long rate,
				  unsigned long parent_rate)
{
	struct meson_clk_cpu *clk_cpu = to_meson_clk_cpu_hw(hw);
	unsigned int div, sel, N = 0;
	u32 reg;

	div = DIV_ROUND_UP(parent_rate, rate);

	if (div <= 3) {
		sel = div - 1;
	} else {
		sel = 3;
		N = div / 2;
	}

	reg = readl(clk_cpu->base + clk_cpu->reg_off + MESON_CPU_CLK_CNTL1);
	reg = PARM_SET(MESON_N_WIDTH, MESON_N_SHIFT, reg, N);
	writel(reg, clk_cpu->base + clk_cpu->reg_off + MESON_CPU_CLK_CNTL1);

	reg = readl(clk_cpu->base + clk_cpu->reg_off + MESON_CPU_CLK_CNTL);
	reg = PARM_SET(MESON_SEL_WIDTH, MESON_SEL_SHIFT, reg, sel);
	writel(reg, clk_cpu->base + clk_cpu->reg_off + MESON_CPU_CLK_CNTL);

	return 0;
}

static unsigned long meson_clk_cpu_recalc_rate(struct clk_hw *hw,
					       unsigned long parent_rate)
{
	struct meson_clk_cpu *clk_cpu = to_meson_clk_cpu_hw(hw);
	unsigned int N, sel;
	unsigned int div = 1;
	u32 reg;

	reg = readl(clk_cpu->base + clk_cpu->reg_off + MESON_CPU_CLK_CNTL1);
	N = PARM_GET(MESON_N_WIDTH, MESON_N_SHIFT, reg);

	reg = readl(clk_cpu->base + clk_cpu->reg_off + MESON_CPU_CLK_CNTL);
	sel = PARM_GET(MESON_SEL_WIDTH, MESON_SEL_SHIFT, reg);

	if (sel < 3)
		div = sel + 1;
	else
		div = 2 * N;

	return parent_rate / div;
}

static int meson_clk_cpu_pre_rate_change(struct meson_clk_cpu *clk_cpu,
					 struct clk_notifier_data *ndata)
{
	u32 cpu_clk_cntl;

	/* switch MUX1 to xtal */
	cpu_clk_cntl = readl(clk_cpu->base + clk_cpu->reg_off
				+ MESON_CPU_CLK_CNTL);
	cpu_clk_cntl &= ~MESON_CPU_CLK_MUX1;
	writel(cpu_clk_cntl, clk_cpu->base + clk_cpu->reg_off
				+ MESON_CPU_CLK_CNTL);
	udelay(100);

	/* switch MUX2 to sys-pll */
	cpu_clk_cntl |= MESON_CPU_CLK_MUX2;
	writel(cpu_clk_cntl, clk_cpu->base + clk_cpu->reg_off
				+ MESON_CPU_CLK_CNTL);

	return 0;
}

static int meson_clk_cpu_post_rate_change(struct meson_clk_cpu *clk_cpu,
					  struct clk_notifier_data *ndata)
{
	u32 cpu_clk_cntl;

	/* switch MUX1 to divisors' output */
	cpu_clk_cntl = readl(clk_cpu->base + clk_cpu->reg_off
				+ MESON_CPU_CLK_CNTL);
	cpu_clk_cntl |= MESON_CPU_CLK_MUX1;
	writel(cpu_clk_cntl, clk_cpu->base + clk_cpu->reg_off
				+ MESON_CPU_CLK_CNTL);
	udelay(100);

	return 0;
}

/*
 * This clock notifier is called when the frequency of the of the parent
 * PLL clock is to be changed. We use the xtal input as temporary parent
 * while the PLL frequency is stabilized.
 */
static int meson_clk_cpu_notifier_cb(struct notifier_block *nb,
				     unsigned long event, void *data)
{
	struct clk_notifier_data *ndata = data;
	struct meson_clk_cpu *clk_cpu = to_meson_clk_cpu_nb(nb);
	int ret = 0;

	if (event == PRE_RATE_CHANGE)
		ret = meson_clk_cpu_pre_rate_change(clk_cpu, ndata);
	else if (event == POST_RATE_CHANGE)
		ret = meson_clk_cpu_post_rate_change(clk_cpu, ndata);

	return notifier_from_errno(ret);
}

static const struct clk_ops meson_clk_cpu_ops = {
	.recalc_rate	= meson_clk_cpu_recalc_rate,
	.round_rate	= meson_clk_cpu_round_rate,
	.set_rate	= meson_clk_cpu_set_rate,
};

struct clk *meson_clk_register_cpu(const struct clk_conf *clk_conf,
				   void __iomem *reg_base,
				   spinlock_t *lock)
{
	struct clk *clk;
	struct clk *pclk;
	struct meson_clk_cpu *clk_cpu;
	struct clk_init_data init;
	int ret;

	clk_cpu = kzalloc(sizeof(*clk_cpu), GFP_KERNEL);
	if (!clk_cpu)
		return ERR_PTR(-ENOMEM);

	clk_cpu->base = reg_base;
	clk_cpu->reg_off = clk_conf->reg_off;
	clk_cpu->div_table = clk_conf->conf.div_table;
	clk_cpu->clk_nb.notifier_call = meson_clk_cpu_notifier_cb;

	init.name = clk_conf->clk_name;
	init.ops = &meson_clk_cpu_ops;
	init.flags = clk_conf->flags | CLK_GET_RATE_NOCACHE;
	init.flags |= CLK_SET_RATE_PARENT;
	init.parent_names = clk_conf->clks_parent;
	init.num_parents = 1;

	clk_cpu->hw.init = &init;

	pclk = __clk_lookup(clk_conf->clks_parent[0]);
	if (!pclk) {
		pr_err("%s: could not lookup parent clock %s\n",
				__func__, clk_conf->clks_parent[0]);
		return ERR_PTR(-EINVAL);
	}

	ret = clk_notifier_register(pclk, &clk_cpu->clk_nb);
	if (ret) {
		pr_err("%s: failed to register clock notifier for %s\n",
				__func__, clk_conf->clk_name);
		return ERR_PTR(-EINVAL);
	}

	clk = clk_register(NULL, &clk_cpu->hw);
	if (IS_ERR(clk)) {
		clk_notifier_unregister(pclk, &clk_cpu->clk_nb);
		kfree(clk_cpu);
	}

	return clk;
}
+227 −0
Original line number Diff line number Diff line
/*
 * Copyright (c) 2015 Endless Mobile, Inc.
 * Author: Carlo Caione <carlo@endlessm.com>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU General Public License,
 * version 2, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/*
 * In the most basic form, a Meson PLL is composed as follows:
 *
 *                     PLL
 *      +------------------------------+
 *      |                              |
 * in -----[ /N ]---[ *M ]---[ >>OD ]----->> out
 *      |         ^        ^           |
 *      +------------------------------+
 *                |        |
 *               FREF     VCO
 *
 * out = (in * M / N) >> OD
 */

#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/slab.h>
#include <linux/string.h>

#include "clkc.h"

#define MESON_PLL_RESET				BIT(29)
#define MESON_PLL_LOCK				BIT(31)

struct meson_clk_pll {
	struct clk_hw	hw;
	void __iomem	*base;
	struct pll_conf	*conf;
	unsigned int	rate_count;
	spinlock_t	*lock;
};
#define to_meson_clk_pll(_hw) container_of(_hw, struct meson_clk_pll, hw)

static unsigned long meson_clk_pll_recalc_rate(struct clk_hw *hw,
						unsigned long parent_rate)
{
	struct meson_clk_pll *pll = to_meson_clk_pll(hw);
	struct parm *p;
	unsigned long parent_rate_mhz = parent_rate / 1000000;
	unsigned long rate_mhz;
	u16 n, m, od;
	u32 reg;

	p = &pll->conf->n;
	reg = readl(pll->base + p->reg_off);
	n = PARM_GET(p->width, p->shift, reg);

	p = &pll->conf->m;
	reg = readl(pll->base + p->reg_off);
	m = PARM_GET(p->width, p->shift, reg);

	p = &pll->conf->od;
	reg = readl(pll->base + p->reg_off);
	od = PARM_GET(p->width, p->shift, reg);

	rate_mhz = (parent_rate_mhz * m / n) >> od;

	return rate_mhz * 1000000;
}

static long meson_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
				     unsigned long *parent_rate)
{
	struct meson_clk_pll *pll = to_meson_clk_pll(hw);
	const struct pll_rate_table *rate_table = pll->conf->rate_table;
	int i;

	for (i = 0; i < pll->rate_count; i++) {
		if (rate <= rate_table[i].rate)
			return rate_table[i].rate;
	}

	/* else return the smallest value */
	return rate_table[0].rate;
}

static const struct pll_rate_table *meson_clk_get_pll_settings(struct meson_clk_pll *pll,
							       unsigned long rate)
{
	const struct pll_rate_table *rate_table = pll->conf->rate_table;
	int i;

	for (i = 0; i < pll->rate_count; i++) {
		if (rate == rate_table[i].rate)
			return &rate_table[i];
	}
	return NULL;
}

static int meson_clk_pll_wait_lock(struct meson_clk_pll *pll,
				   struct parm *p_n)
{
	int delay = 24000000;
	u32 reg;

	while (delay > 0) {
		reg = readl(pll->base + p_n->reg_off);

		if (reg & MESON_PLL_LOCK)
			return 0;
		delay--;
	}
	return -ETIMEDOUT;
}

static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
				  unsigned long parent_rate)
{
	struct meson_clk_pll *pll = to_meson_clk_pll(hw);
	struct parm *p;
	const struct pll_rate_table *rate_set;
	unsigned long old_rate;
	int ret = 0;
	u32 reg;

	if (parent_rate == 0 || rate == 0)
		return -EINVAL;

	old_rate = rate;

	rate_set = meson_clk_get_pll_settings(pll, rate);
	if (!rate_set)
		return -EINVAL;

	/* PLL reset */
	p = &pll->conf->n;
	reg = readl(pll->base + p->reg_off);
	writel(reg | MESON_PLL_RESET, pll->base + p->reg_off);

	reg = PARM_SET(p->width, p->shift, reg, rate_set->n);
	writel(reg, pll->base + p->reg_off);

	p = &pll->conf->m;
	reg = readl(pll->base + p->reg_off);
	reg = PARM_SET(p->width, p->shift, reg, rate_set->m);
	writel(reg, pll->base + p->reg_off);

	p = &pll->conf->od;
	reg = readl(pll->base + p->reg_off);
	reg = PARM_SET(p->width, p->shift, reg, rate_set->od);
	writel(reg, pll->base + p->reg_off);

	p = &pll->conf->n;
	ret = meson_clk_pll_wait_lock(pll, p);
	if (ret) {
		pr_warn("%s: pll did not lock, trying to restore old rate %lu\n",
			__func__, old_rate);
		meson_clk_pll_set_rate(hw, old_rate, parent_rate);
	}

	return ret;
}

static const struct clk_ops meson_clk_pll_ops = {
	.recalc_rate	= meson_clk_pll_recalc_rate,
	.round_rate	= meson_clk_pll_round_rate,
	.set_rate	= meson_clk_pll_set_rate,
};

static const struct clk_ops meson_clk_pll_ro_ops = {
	.recalc_rate	= meson_clk_pll_recalc_rate,
};

struct clk *meson_clk_register_pll(const struct clk_conf *clk_conf,
				   void __iomem *reg_base,
				   spinlock_t *lock)
{
	struct clk *clk;
	struct meson_clk_pll *clk_pll;
	struct clk_init_data init;

	clk_pll = kzalloc(sizeof(*clk_pll), GFP_KERNEL);
	if (!clk_pll)
		return ERR_PTR(-ENOMEM);

	clk_pll->base = reg_base + clk_conf->reg_off;
	clk_pll->lock = lock;
	clk_pll->conf = clk_conf->conf.pll;

	init.name = clk_conf->clk_name;
	init.flags = clk_conf->flags | CLK_GET_RATE_NOCACHE;

	init.parent_names = &clk_conf->clks_parent[0];
	init.num_parents = 1;
	init.ops = &meson_clk_pll_ro_ops;

	/* If no rate_table is specified we assume the PLL is read-only */
	if (clk_pll->conf->rate_table) {
		int len;

		for (len = 0; clk_pll->conf->rate_table[len].rate != 0; )
			len++;

		 clk_pll->rate_count = len;
		 init.ops = &meson_clk_pll_ops;
	}

	clk_pll->hw.init = &init;

	clk = clk_register(NULL, &clk_pll->hw);
	if (IS_ERR(clk))
		kfree(clk_pll);

	return clk;
}
+250 −0
Original line number Diff line number Diff line
/*
 * Copyright (c) 2015 Endless Mobile, Inc.
 * Author: Carlo Caione <carlo@endlessm.com>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU General Public License,
 * version 2, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/mfd/syscon.h>
#include <linux/slab.h>

#include "clkc.h"

static DEFINE_SPINLOCK(clk_lock);

static struct clk **clks;
static struct clk_onecell_data clk_data;

struct clk ** __init meson_clk_init(struct device_node *np,
				   unsigned long nr_clks)
{
	clks = kcalloc(nr_clks, sizeof(*clks), GFP_KERNEL);
	if (!clks)
		return ERR_PTR(-ENOMEM);

	clk_data.clks = clks;
	clk_data.clk_num = nr_clks;
	of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);

	return clks;
}

static void meson_clk_add_lookup(struct clk *clk, unsigned int id)
{
	if (clks && id)
		clks[id] = clk;
}

static struct clk * __init
meson_clk_register_composite(const struct clk_conf *clk_conf,
			     void __iomem *clk_base)
{
	struct clk *clk;
	struct clk_mux *mux = NULL;
	struct clk_divider *div = NULL;
	struct clk_gate *gate = NULL;
	const struct clk_ops *mux_ops = NULL;
	const struct composite_conf *composite_conf;

	composite_conf = clk_conf->conf.composite;

	if (clk_conf->num_parents > 1) {
		mux = kzalloc(sizeof(*mux), GFP_KERNEL);
		if (!mux)
			return ERR_PTR(-ENOMEM);

		mux->reg = clk_base + clk_conf->reg_off
				+ composite_conf->mux_parm.reg_off;
		mux->shift = composite_conf->mux_parm.shift;
		mux->mask = BIT(composite_conf->mux_parm.width) - 1;
		mux->flags = composite_conf->mux_flags;
		mux->lock = &clk_lock;
		mux->table = composite_conf->mux_table;
		mux_ops = (composite_conf->mux_flags & CLK_MUX_READ_ONLY) ?
			  &clk_mux_ro_ops : &clk_mux_ops;
	}

	if (MESON_PARM_APPLICABLE(&composite_conf->div_parm)) {
		div = kzalloc(sizeof(*div), GFP_KERNEL);
		if (!div) {
			clk = ERR_PTR(-ENOMEM);
			goto error;
		}

		div->reg = clk_base + clk_conf->reg_off
				+ composite_conf->div_parm.reg_off;
		div->shift = composite_conf->div_parm.shift;
		div->width = composite_conf->div_parm.width;
		div->lock = &clk_lock;
		div->flags = composite_conf->div_flags;
		div->table = composite_conf->div_table;
	}

	if (MESON_PARM_APPLICABLE(&composite_conf->gate_parm)) {
		gate = kzalloc(sizeof(*gate), GFP_KERNEL);
		if (!gate) {
			clk = ERR_PTR(-ENOMEM);
			goto error;
		}

		gate->reg = clk_base + clk_conf->reg_off
				+ composite_conf->div_parm.reg_off;
		gate->bit_idx = composite_conf->gate_parm.shift;
		gate->flags = composite_conf->gate_flags;
		gate->lock = &clk_lock;
	}

	clk = clk_register_composite(NULL, clk_conf->clk_name,
				    clk_conf->clks_parent,
				    clk_conf->num_parents,
				    mux ? &mux->hw : NULL, mux_ops,
				    div ? &div->hw : NULL, &clk_divider_ops,
				    gate ? &gate->hw : NULL, &clk_gate_ops,
				    clk_conf->flags);
	if (IS_ERR(clk))
		goto error;

	return clk;

error:
	kfree(gate);
	kfree(div);
	kfree(mux);

	return clk;
}

static struct clk * __init
meson_clk_register_fixed_factor(const struct clk_conf *clk_conf,
				void __iomem *clk_base)
{
	struct clk *clk;
	const struct fixed_fact_conf *fixed_fact_conf;
	const struct parm *p;
	unsigned int mult, div;
	u32 reg;

	fixed_fact_conf = &clk_conf->conf.fixed_fact;

	mult = clk_conf->conf.fixed_fact.mult;
	div = clk_conf->conf.fixed_fact.div;

	if (!mult) {
		mult = 1;
		p = &fixed_fact_conf->mult_parm;
		if (MESON_PARM_APPLICABLE(p)) {
			reg = readl(clk_base + clk_conf->reg_off + p->reg_off);
			mult = PARM_GET(p->width, p->shift, reg);
		}
	}

	if (!div) {
		div = 1;
		p = &fixed_fact_conf->div_parm;
		if (MESON_PARM_APPLICABLE(p)) {
			reg = readl(clk_base + clk_conf->reg_off + p->reg_off);
			mult = PARM_GET(p->width, p->shift, reg);
		}
	}

	clk = clk_register_fixed_factor(NULL,
			clk_conf->clk_name,
			clk_conf->clks_parent[0],
			clk_conf->flags,
			mult, div);

	return clk;
}

static struct clk * __init
meson_clk_register_fixed_rate(const struct clk_conf *clk_conf,
			      void __iomem *clk_base)
{
	struct clk *clk;
	const struct fixed_rate_conf *fixed_rate_conf;
	const struct parm *r;
	unsigned long rate;
	u32 reg;

	fixed_rate_conf = &clk_conf->conf.fixed_rate;
	rate = fixed_rate_conf->rate;

	if (!rate) {
		r = &fixed_rate_conf->rate_parm;
		reg = readl(clk_base + clk_conf->reg_off + r->reg_off);
		rate = PARM_GET(r->width, r->shift, reg);
	}

	rate *= 1000000;

	clk = clk_register_fixed_rate(NULL,
			clk_conf->clk_name,
			clk_conf->num_parents
				? clk_conf->clks_parent[0] : NULL,
			clk_conf->flags, rate);

	return clk;
}

void __init meson_clk_register_clks(const struct clk_conf *clk_confs,
				    size_t nr_confs,
				    void __iomem *clk_base)
{
	unsigned int i;
	struct clk *clk = NULL;

	for (i = 0; i < nr_confs; i++) {
		const struct clk_conf *clk_conf = &clk_confs[i];

		switch (clk_conf->clk_type) {
		case CLK_FIXED_RATE:
			clk = meson_clk_register_fixed_rate(clk_conf,
							    clk_base);
			break;
		case CLK_FIXED_FACTOR:
			clk = meson_clk_register_fixed_factor(clk_conf,
							      clk_base);
			break;
		case CLK_COMPOSITE:
			clk = meson_clk_register_composite(clk_conf,
							   clk_base);
			break;
		case CLK_CPU:
			clk = meson_clk_register_cpu(clk_conf, clk_base,
						     &clk_lock);
			break;
		case CLK_PLL:
			clk = meson_clk_register_pll(clk_conf, clk_base,
						     &clk_lock);
			break;
		default:
			clk = NULL;
		}

		if (!clk) {
			pr_err("%s: unknown clock type %d\n", __func__,
			       clk_conf->clk_type);
			continue;
		}

		if (IS_ERR(clk)) {
			pr_warn("%s: Unable to create %s clock\n", __func__,
				clk_conf->clk_name);
			continue;
		}

		meson_clk_add_lookup(clk, clk_conf->clk_id);
	}
}
Loading