diff mbox

[v2,1/3] clk: meson: Add support for Meson clock controller

Message ID 1431773333-23567-2-git-send-email-carlo@caione.org (mailing list archive)
State New, archived
Headers show

Commit Message

Carlo Caione May 16, 2015, 10:48 a.m. UTC
From: Carlo Caione <carlo@endlessm.com>

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: Carlo Caione <carlo@endlessm.com>
---
 drivers/clk/Makefile                     |   1 +
 drivers/clk/meson/Makefile               |   5 +
 drivers/clk/meson/clk-cpu.c              | 291 +++++++++++++++++++++++++++++++
 drivers/clk/meson/clk-pll.c              | 231 ++++++++++++++++++++++++
 drivers/clk/meson/clkc.c                 | 234 +++++++++++++++++++++++++
 drivers/clk/meson/clkc.h                 | 187 ++++++++++++++++++++
 include/dt-bindings/clock/meson8b-clkc.h |  20 +++
 7 files changed, 969 insertions(+)
 create mode 100644 drivers/clk/meson/Makefile
 create mode 100644 drivers/clk/meson/clk-cpu.c
 create mode 100644 drivers/clk/meson/clk-pll.c
 create mode 100644 drivers/clk/meson/clkc.c
 create mode 100644 drivers/clk/meson/clkc.h
 create mode 100644 include/dt-bindings/clock/meson8b-clkc.h

Comments

Stephen Boyd May 28, 2015, 9:55 p.m. UTC | #1
On 05/16, Carlo Caione wrote:
> diff --git a/drivers/clk/meson/clk-cpu.c b/drivers/clk/meson/clk-cpu.c
> new file mode 100644
> index 0000000..88f4606
> --- /dev/null
> +++ b/drivers/clk/meson/clk-cpu.c
> @@ -0,0 +1,291 @@
> +/*
> + * 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  +--[/2]-+----------|2| MUX1
> + * [sys_pll]---|1|   |--[/3]------------|1|-|1|
> + *             | |---+------------------|0| | |----- [a5_clk]
> + *          +--|0|                          | |
> + * [xtal]---+-------------------------------|0|
> + *
> + *
> + *
> + */
> +
> +#include <linux/clk-provider.h>
> +#include <linux/clkdev.h>

Is this include used?

> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/module.h>

Is this include used?

> +#include <linux/of_address.h>
> +#include <linux/slab.h>
> +#include <linux/string.h>

Is this include used?

Missing

#include <linux/spinlock.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
> +
> +#define MULT_ROUND_UP(r, m) ((r) * (m) + (m) - 1)
> +
> +#include "clkc.h"
> +
> +struct meson_clk_cpu {
> +	struct notifier_block	clk_nb;
> +	struct clk_hw		hw;
> +	void __iomem		*base;
> +	u16			reg_off;
> +	spinlock_t		*lock;
> +};
> +#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 bool is_valid_div(int i)
> +{
> +	if ((i % 2) && (i != 1) && (i != 3))
> +		return 0;
> +	return 1;
> +}
> +
> +static bool is_best_div(unsigned long rate, unsigned long now,
> +			unsigned long best)
> +{
> +	return abs(rate - now) < abs(rate - best);
> +}
> +
> +static int meson_clk_cpu_get_div(struct clk_hw *hw, unsigned long rate,
> +				 unsigned long *best_parent_rate)
> +{
> +	unsigned long maxdiv, parent_rate, now;
> +	unsigned long best = 0;
> +	unsigned long parent_rate_saved = *best_parent_rate;
> +	int i, bestdiv = 0;
> +
> +	maxdiv = 2 * PMASK(MESON_N_WIDTH);
> +	maxdiv = min(ULONG_MAX / rate, maxdiv);
> +
> +	for (i = 1; i <= maxdiv; i++) {
> +		if (!is_valid_div(i))
> +			continue;
> +		if (rate * i == parent_rate_saved) {
> +			*best_parent_rate = parent_rate_saved;
> +			return i;
> +		}
> +		parent_rate = __clk_round_rate(__clk_get_parent(hw->clk),
> +				MULT_ROUND_UP(rate, i));
> +		now = DIV_ROUND_UP(parent_rate, i);
> +		if (is_best_div(rate, now, best)) {
> +			bestdiv = i;
> +			best = now;
> +			*best_parent_rate = parent_rate;
> +		}
> +	}
> +
> +	return bestdiv;
> +}
> +
> +static long meson_clk_cpu_round_rate(struct clk_hw *hw, unsigned long rate,
> +				     unsigned long *prate)
> +{
> +	int div;
> +
> +	div = meson_clk_cpu_get_div(hw, rate, prate);
> +
> +	return DIV_ROUND_UP(*prate, div);
> +}

How much of this is the same as the generic divider code? Can you
use the divider_*() functions for this code?

> +
> +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;
> +
> +	spin_lock(clk_cpu->lock);

We don't need irqsave? What is this locking against? I only see
notifiers using it and notifiers are synchronized already.

> +
> +	/* 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);
> +
> +	spin_unlock(clk_cpu->lock);
> +
> +	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;
> +
> +	spin_lock(clk_cpu->lock);
> +
> +	/* 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);
> +
> +	spin_unlock(clk_cpu->lock);
> +
> +	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(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->lock = lock;
> +	clk_cpu->base = reg_base;
> +	clk_cpu->reg_off = clk_conf->reg_off;
> +	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;
> +}
> +
> diff --git a/drivers/clk/meson/clk-pll.c b/drivers/clk/meson/clk-pll.c
> new file mode 100644
> index 0000000..662d694
> --- /dev/null
> +++ b/drivers/clk/meson/clk-pll.c
> @@ -0,0 +1,231 @@
> +/*
> + * 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/clkdev.h>

Is this include used?

> +#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);
> +	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 struct pll_rate_table *meson_clk_get_pll_settings(struct meson_clk_pll *pll,
> +							 unsigned long rate)
> +{
> +	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--;

Do we somehow know that a delay-- takes so much time? It would
seem better to have a udelay() here. Otherwise we're left to the
speed of the CPU to execute these instructions.

> +	}
> +	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;
> +	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(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;
> +
> +	/* We usually don't touch PLLs */
> +	init.flags |= CLK_IGNORE_UNUSED;

Is this even important if the ops don't have an enable/disable
(read-only ops)?

> +
> +	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;
> +}
> diff --git a/drivers/clk/meson/clkc.c b/drivers/clk/meson/clkc.c
> new file mode 100644
> index 0000000..8805772
> --- /dev/null
> +++ b/drivers/clk/meson/clkc.c
> @@ -0,0 +1,234 @@
> +/*
> + * 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,

This should be 

struct clk ** __init meson_clk_init(struct ...)

> +				   unsigned long nr_clks)
> +{
> +	clks = kcalloc(nr_clks, sizeof(struct clk *), GFP_KERNEL);

sizeof(**clks) ?

> +	if (!clks) {
> +		pr_err("%s: could not allocate clock lookup table\n", __func__);

No need for error messages on allocation failures. Please run
checkpatch.

> +		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(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;
> +	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)
> +			return ERR_PTR(-ENOMEM);

Did we just leak memory if we had a mux?

> +
> +		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)
> +			return ERR_PTR(-ENOMEM);

ditto

> +
> +		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);
> +
> +	return clk;

Why not just return clk_register_composite() then?

> +}
> +
> +static struct clk __init *meson_clk_register_fixed_factor(struct clk_conf *clk_conf,
> +							  void __iomem *clk_base)
> +{
> +	struct clk *clk;
> +	struct fixed_fact_conf *fixed_fact_conf;
> +	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(struct clk_conf *clk_conf,
> +							void __iomem *clk_base)
> +{
> +	struct clk *clk;
> +	struct fixed_rate_conf *fixed_rate_conf;
> +	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(struct clk_conf *clk_confs,
> +				    unsigned int nr_confs,

size_t?

> +				    void __iomem *clk_base)
> +{
> +	unsigned int i;
> +	struct clk *clk = NULL;
> +
> +	for (i = 0; i < nr_confs; i++) {
> +		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;
	break;

> +		}
> +
> +		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);
> +	}
> +}
> diff --git a/drivers/clk/meson/clkc.h b/drivers/clk/meson/clkc.h
> new file mode 100644
> index 0000000..f189228
> --- /dev/null
> +++ b/drivers/clk/meson/clkc.h
> @@ -0,0 +1,187 @@
> +/*
> + * 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/>.
> + */
> +
> +#ifndef __CLKC_H
> +#define __CLKC_H
> +
> +#include <linux/clk.h>

What is this include for?

> +
> +#define PMASK(width)			((1U << (width)) - 1)
> +#define SETPMASK(width, shift)		(PMASK(width) << (shift))
> +#define CLRPMASK(width, shift)		(~(SETPMASK(width, shift)))
> +

We have GENMASK for these sorts of things, please use it.

> +#define PARM_GET(width, shift, reg)					\
> +	(((reg) & SETPMASK(width, shift)) >> (shift))
> +#define PARM_SET(width, shift, reg, val)				\
> +	(((reg) & CLRPMASK(width, shift)) | (val << (shift)))
> +
> +#define MESON_PARM_APPLICABLE(p)		(!!((p)->width))
> +
> +struct parm {
> +	u16	reg_off;
> +	u8	shift;
> +	u8	width;
> +};
> +#define PARM(_r, _s, _w)						\
> +	{								\
> +		.reg_off	= (_r),					\
> +		.shift		= (_s),					\
> +		.width		= (_w),					\
> +	}								\
> +
> +struct pll_rate_table {
> +	unsigned long	rate;
> +	u16		m;
> +	u16		n;
> +	u16		od;
> +};
> +#define PLL_RATE(_r, _m, _n, _od)					\
> +	{								\
> +		.rate		= (_r),					\
> +		.m		= (_m),					\
> +		.n		= (_n),					\
> +		.od		= (_od),				\
> +	}								\
> +
> +struct pll_conf {
> +	struct pll_rate_table	*rate_table;
> +	struct parm		m;
> +	struct parm		n;
> +	struct parm		od;
> +};
> +
> +struct fixed_fact_conf {
> +	unsigned int	div;
> +	unsigned int	mult;
> +	struct parm	div_parm;
> +	struct parm	mult_parm;
> +};
> +
> +struct fixed_rate_conf {
> +	unsigned long	rate;
> +	struct parm	rate_parm;
> +};
> +
> +struct composite_conf {
> +	struct parm		mux_parm;
> +	struct parm		div_parm;
> +	struct parm		gate_parm;
> +	struct clk_div_table	*div_table;
> +	u32			*mux_table;
> +	u8			mux_flags;
> +	u8			div_flags;
> +	u8			gate_flags;
> +};
> +
> +#define PNAME(x) static const char *x[] __initconst

Is this used?

> +
> +enum clk_type {
> +	clk_fixed_factor,
> +	clk_fixed_rate,
> +	clk_composite,
> +	clk_cpu,
> +	clk_pll,
> +};

Please use upper case for enums

> +
> +struct clk_conf {
> +	u16				reg_off;
> +	enum clk_type			clk_type;
> +	unsigned int			clk_id;
> +	const char			*clk_name;
> +	const char			**clks_parent;
> +	int				num_parents;
> +	unsigned long			flags;
> +	union {
> +		struct fixed_fact_conf	fixed_fact;
> +		struct fixed_rate_conf	fixed_rate;
> +		struct composite_conf	*composite;
> +		struct pll_conf		*pll;
> +	} conf;
> +};
> +
> +#define FIXED_RATE_P(_ro, _ci, _cn, _f, _c)				\
> +	{								\
> +		.reg_off			= (_ro),		\
> +		.clk_type			= clk_fixed_rate,	\
> +		.clk_id				= (_ci),		\
> +		.clk_name			= (_cn),		\
> +		.flags				= (_f),			\
> +		.conf.fixed_rate.rate_parm	= _c,			\
> +	}								\
> +
> +#define FIXED_RATE(_ci, _cn, _f, _r)					\
> +	{								\
> +		.clk_type			= clk_fixed_rate,	\
> +		.clk_id				= (_ci),		\
> +		.clk_name			= (_cn),		\
> +		.flags				= (_f),			\
> +		.conf.fixed_rate.rate		= (_r),			\
> +	}								\
> +
> +#define PLL(_ro, _ci, _cn, _cp, _f, _c)					\
> +	{								\
> +		.reg_off			= (_ro),		\
> +		.clk_type			= clk_pll,		\
> +		.clk_id				= (_ci),		\
> +		.clk_name			= (_cn),		\
> +		.clks_parent			= (_cp),		\
> +		.num_parents			= ARRAY_SIZE(_cp),	\
> +		.flags				= (_f),			\
> +		.conf.pll			= (_c),			\
> +	}								\
> +
> +#define FIXED_FACTOR_DIV(_ci, _cn, _cp, _f, _d)				\
> +	{								\
> +		.clk_type			= clk_fixed_factor,	\
> +		.clk_id				= (_ci),		\
> +		.clk_name			= (_cn),		\
> +		.clks_parent			= (_cp),		\
> +		.num_parents			= ARRAY_SIZE(_cp),	\
> +		.conf.fixed_fact.div		= (_d),			\
> +	}								\
> +
> +#define CPU(_ro, _ci, _cn, _cp)						\
> +	{								\
> +		.reg_off			= (_ro),		\
> +		.clk_type			= clk_cpu,		\
> +		.clk_id				= (_ci),		\
> +		.clk_name			= (_cn),		\
> +		.clks_parent			= (_cp),		\
> +		.num_parents			= ARRAY_SIZE(_cp),	\
> +	}								\
> +
> +#define COMPOSITE(_ro, _ci, _cn, _cp, _f, _c)				\
> +	{								\
> +		.reg_off			= (_ro),		\
> +		.clk_type			= clk_composite,	\
> +		.clk_id				= (_ci),		\
> +		.clk_name			= (_cn),		\
> +		.clks_parent			= (_cp),		\
> +		.num_parents			= ARRAY_SIZE(_cp),	\
> +		.flags				= (_f),			\
> +		.conf.composite			= (_c),			\
> +	}								\

I couldn't find any usage of these defines. If they're used later
in the series, please add them when they're used.

> +
> +
> +#endif /* __CLKC_H */
> diff --git a/include/dt-bindings/clock/meson8b-clkc.h b/include/dt-bindings/clock/meson8b-clkc.h
> new file mode 100644
> index 0000000..9986c38
> --- /dev/null
> +++ b/include/dt-bindings/clock/meson8b-clkc.h
> @@ -0,0 +1,20 @@
> +/*
> + * Meson8b clock tree IDs
> + */

Does this need an ifdef guard?

> +
> +#define CLKID_UNUSED		0
> +#define CLKID_XTAL		1
> +#define CLKID_PLL_FIXED		2
> +#define CLKID_PLL_VID		3
> +#define CLKID_PLL_SYS		4
> +#define CLKID_FCLK_DIV2		5
> +#define CLKID_FCLK_DIV3		6
> +#define CLKID_FCLK_DIV4		7
> +#define CLKID_FCLK_DIV5		8
> +#define CLKID_FCLK_DIV7		9
> +#define CLKID_CLK81		10
> +#define CLKID_MALI		11
> +#define CLKID_CPUCLK		12
> +#define CLKID_ZERO		13
> +
> +#define CLK_NR_CLKS		(CLKID_ZERO + 1)
> -- 
> 1.9.1
>
Carlo Caione May 29, 2015, 5:45 p.m. UTC | #2
On Thu, May 28, 2015 at 11:55 PM, Stephen Boyd <sboyd@codeaurora.org> wrote:
>> +
>> +#include <linux/clk-provider.h>
>> +#include <linux/clkdev.h>
>
> Is this include used?
>
>> +#include <linux/delay.h>
>> +#include <linux/err.h>
>> +#include <linux/io.h>
>> +#include <linux/module.h>
>
> Is this include used?
>
>> +#include <linux/of_address.h>
>> +#include <linux/slab.h>
>> +#include <linux/string.h>
>
> Is this include used?
>
> Missing
>
> #include <linux/spinlock.h>
>
> ?

I'll fix all the headers in v3.

>> +static int meson_clk_cpu_get_div(struct clk_hw *hw, unsigned long rate,
>> +                              unsigned long *best_parent_rate)
>> +{
>> +     unsigned long maxdiv, parent_rate, now;
>> +     unsigned long best = 0;
>> +     unsigned long parent_rate_saved = *best_parent_rate;
>> +     int i, bestdiv = 0;
>> +
>> +     maxdiv = 2 * PMASK(MESON_N_WIDTH);
>> +     maxdiv = min(ULONG_MAX / rate, maxdiv);
>> +
>> +     for (i = 1; i <= maxdiv; i++) {
>> +             if (!is_valid_div(i))
>> +                     continue;
>> +             if (rate * i == parent_rate_saved) {
>> +                     *best_parent_rate = parent_rate_saved;
>> +                     return i;
>> +             }
>> +             parent_rate = __clk_round_rate(__clk_get_parent(hw->clk),
>> +                             MULT_ROUND_UP(rate, i));
>> +             now = DIV_ROUND_UP(parent_rate, i);
>> +             if (is_best_div(rate, now, best)) {
>> +                     bestdiv = i;
>> +                     best = now;
>> +                     *best_parent_rate = parent_rate;
>> +             }
>> +     }
>> +
>> +     return bestdiv;
>> +}
>> +
>> +static long meson_clk_cpu_round_rate(struct clk_hw *hw, unsigned long rate,
>> +                                  unsigned long *prate)
>> +{
>> +     int div;
>> +
>> +     div = meson_clk_cpu_get_div(hw, rate, prate);
>> +
>> +     return DIV_ROUND_UP(*prate, div);
>> +}
>
> How much of this is the same as the generic divider code? Can you
> use the divider_*() functions for this code?

Yes. When I submitted the v1 the divider_*() functions weren't exported yet.

>> +static int meson_clk_cpu_pre_rate_change(struct meson_clk_cpu *clk_cpu,
>> +                                      struct clk_notifier_data *ndata)
>> +{
>> +     u32 cpu_clk_cntl;
>> +
>> +     spin_lock(clk_cpu->lock);
>
> We don't need irqsave? What is this locking against? I only see
> notifiers using it and notifiers are synchronized already.

Didn't notice that. I'll fix it.

>> +
>> +#include <linux/clk-provider.h>
>> +#include <linux/clkdev.h>
>
> Is this include used?

It isn't.

>> +
>> +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--;
>
> Do we somehow know that a delay-- takes so much time? It would
> seem better to have a udelay() here. Otherwise we're left to the
> speed of the CPU to execute these instructions.

Actually this is not a delay cycle but we exit as soon as the
MESON_PLL_LOCK bit is flipped so in theory we want to cycle as fast as
possible.

>> +
>> +     /* We usually don't touch PLLs */
>> +     init.flags |= CLK_IGNORE_UNUSED;
>
> Is this even important if the ops don't have an enable/disable
> (read-only ops)?

Yeah, not really important.

>> +
>> +struct clk __init **meson_clk_init(struct device_node *np,
>
> This should be
>
> struct clk ** __init meson_clk_init(struct ...)

Agree

>> +                                unsigned long nr_clks)
>> +{
>> +     clks = kcalloc(nr_clks, sizeof(struct clk *), GFP_KERNEL);
>
> sizeof(**clks) ?

yeah ( sizeof(*clks) )

>> +     if (!clks) {
>> +             pr_err("%s: could not allocate clock lookup table\n", __func__);
>
> No need for error messages on allocation failures. Please run
> checkpatch.

I did. Probably missed that.

>> +     if (MESON_PARM_APPLICABLE(&composite_conf->div_parm)) {
>> +             div = kzalloc(sizeof(*div), GFP_KERNEL);
>> +             if (!div)
>> +                     return ERR_PTR(-ENOMEM);
>
> Did we just leak memory if we had a mux?
>
>> +
>> +             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)
>> +                     return ERR_PTR(-ENOMEM);
>
> ditto

Error path in v3.

>> +
>> +     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);
>> +
>> +     return clk;
>
> Why not just return clk_register_composite() then?

Because it is missing the error checking on clk. I'll fix it.

>> +
>> +void __init meson_clk_register_clks(struct clk_conf *clk_confs,
>> +                                 unsigned int nr_confs,
>
> size_t?

Ok

>> +             case clk_pll:
>> +                     clk = meson_clk_register_pll(clk_conf, clk_base,
>> +                                                  &clk_lock);
>> +                     break;
>
> default:
>         clk = NULL;
>         break;

Agree

>> +
>> +#include <linux/clk.h>
>
> What is this include for?

Nothing, I guess?

>> +
>> +#define PMASK(width)                 ((1U << (width)) - 1)
>> +#define SETPMASK(width, shift)               (PMASK(width) << (shift))
>> +#define CLRPMASK(width, shift)               (~(SETPMASK(width, shift)))
>> +
>
> We have GENMASK for these sorts of things, please use it.

Oh, nice to have that.

>> +
>> +#define PNAME(x) static const char *x[] __initconst
>
> Is this used?

Yep, it is in meson8b-clkc.c

>> +
>> +enum clk_type {
>> +     clk_fixed_factor,
>> +     clk_fixed_rate,
>> +     clk_composite,
>> +     clk_cpu,
>> +     clk_pll,
>> +};
>
> Please use upper case for enums

Agree

>> +
>> +#define FIXED_RATE_P(_ro, _ci, _cn, _f, _c)                          \
>> +     {                                                               \
>> +             .reg_off                        = (_ro),                \
>> +             .clk_type                       = clk_fixed_rate,       \
>> +             .clk_id                         = (_ci),                \
>> +             .clk_name                       = (_cn),                \
>> +             .flags                          = (_f),                 \
>> +             .conf.fixed_rate.rate_parm      = _c,                   \
>> +     }                                                               \
>> +
>> +#define FIXED_RATE(_ci, _cn, _f, _r)                                 \
>> +     {                                                               \
>> +             .clk_type                       = clk_fixed_rate,       \
>> +             .clk_id                         = (_ci),                \
>> +             .clk_name                       = (_cn),                \
>> +             .flags                          = (_f),                 \
>> +             .conf.fixed_rate.rate           = (_r),                 \
>> +     }                                                               \
>> +
>> +#define PLL(_ro, _ci, _cn, _cp, _f, _c)                                      \
>> +     {                                                               \
>> +             .reg_off                        = (_ro),                \
>> +             .clk_type                       = clk_pll,              \
>> +             .clk_id                         = (_ci),                \
>> +             .clk_name                       = (_cn),                \
>> +             .clks_parent                    = (_cp),                \
>> +             .num_parents                    = ARRAY_SIZE(_cp),      \
>> +             .flags                          = (_f),                 \
>> +             .conf.pll                       = (_c),                 \
>> +     }                                                               \
>> +
>> +#define FIXED_FACTOR_DIV(_ci, _cn, _cp, _f, _d)                              \
>> +     {                                                               \
>> +             .clk_type                       = clk_fixed_factor,     \
>> +             .clk_id                         = (_ci),                \
>> +             .clk_name                       = (_cn),                \
>> +             .clks_parent                    = (_cp),                \
>> +             .num_parents                    = ARRAY_SIZE(_cp),      \
>> +             .conf.fixed_fact.div            = (_d),                 \
>> +     }                                                               \
>> +
>> +#define CPU(_ro, _ci, _cn, _cp)                                              \
>> +     {                                                               \
>> +             .reg_off                        = (_ro),                \
>> +             .clk_type                       = clk_cpu,              \
>> +             .clk_id                         = (_ci),                \
>> +             .clk_name                       = (_cn),                \
>> +             .clks_parent                    = (_cp),                \
>> +             .num_parents                    = ARRAY_SIZE(_cp),      \
>> +     }                                                               \
>> +
>> +#define COMPOSITE(_ro, _ci, _cn, _cp, _f, _c)                                \
>> +     {                                                               \
>> +             .reg_off                        = (_ro),                \
>> +             .clk_type                       = clk_composite,        \
>> +             .clk_id                         = (_ci),                \
>> +             .clk_name                       = (_cn),                \
>> +             .clks_parent                    = (_cp),                \
>> +             .num_parents                    = ARRAY_SIZE(_cp),      \
>> +             .flags                          = (_f),                 \
>> +             .conf.composite                 = (_c),                 \
>> +     }                                                               \
>
> I couldn't find any usage of these defines. If they're used later
> in the series, please add them when they're used.

These defines are used later in the Meson8b specific file and I think
they belong to this patch since they define all the basic clock types
we can find on all the Meson SoCs.

>> +
>> +#endif /* __CLKC_H */
>> diff --git a/include/dt-bindings/clock/meson8b-clkc.h b/include/dt-bindings/clock/meson8b-clkc.h
>> new file mode 100644
>> index 0000000..9986c38
>> --- /dev/null
>> +++ b/include/dt-bindings/clock/meson8b-clkc.h
>> @@ -0,0 +1,20 @@
>> +/*
>> + * Meson8b clock tree IDs
>> + */
>
> Does this need an ifdef guard?

Yep.

Thank you for your review.
diff mbox

Patch

diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 3d00c25..9c4ddb1 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -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/
diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile
new file mode 100644
index 0000000..66c6d8d
--- /dev/null
+++ b/drivers/clk/meson/Makefile
@@ -0,0 +1,5 @@ 
+#
+# Makefile for Meson specific clk
+#
+
+obj-y += clkc.o clk-pll.o clk-cpu.o
diff --git a/drivers/clk/meson/clk-cpu.c b/drivers/clk/meson/clk-cpu.c
new file mode 100644
index 0000000..88f4606
--- /dev/null
+++ b/drivers/clk/meson/clk-cpu.c
@@ -0,0 +1,291 @@ 
+/*
+ * 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  +--[/2]-+----------|2| MUX1
+ * [sys_pll]---|1|   |--[/3]------------|1|-|1|
+ *             | |---+------------------|0| | |----- [a5_clk]
+ *          +--|0|                          | |
+ * [xtal]---+-------------------------------|0|
+ *
+ *
+ *
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.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>
+
+#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
+
+#define MULT_ROUND_UP(r, m) ((r) * (m) + (m) - 1)
+
+#include "clkc.h"
+
+struct meson_clk_cpu {
+	struct notifier_block	clk_nb;
+	struct clk_hw		hw;
+	void __iomem		*base;
+	u16			reg_off;
+	spinlock_t		*lock;
+};
+#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 bool is_valid_div(int i)
+{
+	if ((i % 2) && (i != 1) && (i != 3))
+		return 0;
+	return 1;
+}
+
+static bool is_best_div(unsigned long rate, unsigned long now,
+			unsigned long best)
+{
+	return abs(rate - now) < abs(rate - best);
+}
+
+static int meson_clk_cpu_get_div(struct clk_hw *hw, unsigned long rate,
+				 unsigned long *best_parent_rate)
+{
+	unsigned long maxdiv, parent_rate, now;
+	unsigned long best = 0;
+	unsigned long parent_rate_saved = *best_parent_rate;
+	int i, bestdiv = 0;
+
+	maxdiv = 2 * PMASK(MESON_N_WIDTH);
+	maxdiv = min(ULONG_MAX / rate, maxdiv);
+
+	for (i = 1; i <= maxdiv; i++) {
+		if (!is_valid_div(i))
+			continue;
+		if (rate * i == parent_rate_saved) {
+			*best_parent_rate = parent_rate_saved;
+			return i;
+		}
+		parent_rate = __clk_round_rate(__clk_get_parent(hw->clk),
+				MULT_ROUND_UP(rate, i));
+		now = DIV_ROUND_UP(parent_rate, i);
+		if (is_best_div(rate, now, best)) {
+			bestdiv = i;
+			best = now;
+			*best_parent_rate = parent_rate;
+		}
+	}
+
+	return bestdiv;
+}
+
+static long meson_clk_cpu_round_rate(struct clk_hw *hw, unsigned long rate,
+				     unsigned long *prate)
+{
+	int div;
+
+	div = meson_clk_cpu_get_div(hw, rate, prate);
+
+	return DIV_ROUND_UP(*prate, div);
+}
+
+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;
+
+	spin_lock(clk_cpu->lock);
+
+	/* 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);
+
+	spin_unlock(clk_cpu->lock);
+
+	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;
+
+	spin_lock(clk_cpu->lock);
+
+	/* 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);
+
+	spin_unlock(clk_cpu->lock);
+
+	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(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->lock = lock;
+	clk_cpu->base = reg_base;
+	clk_cpu->reg_off = clk_conf->reg_off;
+	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;
+}
+
diff --git a/drivers/clk/meson/clk-pll.c b/drivers/clk/meson/clk-pll.c
new file mode 100644
index 0000000..662d694
--- /dev/null
+++ b/drivers/clk/meson/clk-pll.c
@@ -0,0 +1,231 @@ 
+/*
+ * 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/clkdev.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);
+	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 struct pll_rate_table *meson_clk_get_pll_settings(struct meson_clk_pll *pll,
+							 unsigned long rate)
+{
+	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;
+	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(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;
+
+	/* We usually don't touch PLLs */
+	init.flags |= CLK_IGNORE_UNUSED;
+
+	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;
+}
diff --git a/drivers/clk/meson/clkc.c b/drivers/clk/meson/clkc.c
new file mode 100644
index 0000000..8805772
--- /dev/null
+++ b/drivers/clk/meson/clkc.c
@@ -0,0 +1,234 @@ 
+/*
+ * 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(struct clk *), GFP_KERNEL);
+	if (!clks) {
+		pr_err("%s: could not allocate clock lookup table\n", __func__);
+		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(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;
+	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)
+			return ERR_PTR(-ENOMEM);
+
+		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)
+			return ERR_PTR(-ENOMEM);
+
+		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);
+
+	return clk;
+}
+
+static struct clk __init *meson_clk_register_fixed_factor(struct clk_conf *clk_conf,
+							  void __iomem *clk_base)
+{
+	struct clk *clk;
+	struct fixed_fact_conf *fixed_fact_conf;
+	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(struct clk_conf *clk_conf,
+							void __iomem *clk_base)
+{
+	struct clk *clk;
+	struct fixed_rate_conf *fixed_rate_conf;
+	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(struct clk_conf *clk_confs,
+				    unsigned int nr_confs,
+				    void __iomem *clk_base)
+{
+	unsigned int i;
+	struct clk *clk = NULL;
+
+	for (i = 0; i < nr_confs; i++) {
+		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;
+		}
+
+		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);
+	}
+}
diff --git a/drivers/clk/meson/clkc.h b/drivers/clk/meson/clkc.h
new file mode 100644
index 0000000..f189228
--- /dev/null
+++ b/drivers/clk/meson/clkc.h
@@ -0,0 +1,187 @@ 
+/*
+ * 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/>.
+ */
+
+#ifndef __CLKC_H
+#define __CLKC_H
+
+#include <linux/clk.h>
+
+#define PMASK(width)			((1U << (width)) - 1)
+#define SETPMASK(width, shift)		(PMASK(width) << (shift))
+#define CLRPMASK(width, shift)		(~(SETPMASK(width, shift)))
+
+#define PARM_GET(width, shift, reg)					\
+	(((reg) & SETPMASK(width, shift)) >> (shift))
+#define PARM_SET(width, shift, reg, val)				\
+	(((reg) & CLRPMASK(width, shift)) | (val << (shift)))
+
+#define MESON_PARM_APPLICABLE(p)		(!!((p)->width))
+
+struct parm {
+	u16	reg_off;
+	u8	shift;
+	u8	width;
+};
+#define PARM(_r, _s, _w)						\
+	{								\
+		.reg_off	= (_r),					\
+		.shift		= (_s),					\
+		.width		= (_w),					\
+	}								\
+
+struct pll_rate_table {
+	unsigned long	rate;
+	u16		m;
+	u16		n;
+	u16		od;
+};
+#define PLL_RATE(_r, _m, _n, _od)					\
+	{								\
+		.rate		= (_r),					\
+		.m		= (_m),					\
+		.n		= (_n),					\
+		.od		= (_od),				\
+	}								\
+
+struct pll_conf {
+	struct pll_rate_table	*rate_table;
+	struct parm		m;
+	struct parm		n;
+	struct parm		od;
+};
+
+struct fixed_fact_conf {
+	unsigned int	div;
+	unsigned int	mult;
+	struct parm	div_parm;
+	struct parm	mult_parm;
+};
+
+struct fixed_rate_conf {
+	unsigned long	rate;
+	struct parm	rate_parm;
+};
+
+struct composite_conf {
+	struct parm		mux_parm;
+	struct parm		div_parm;
+	struct parm		gate_parm;
+	struct clk_div_table	*div_table;
+	u32			*mux_table;
+	u8			mux_flags;
+	u8			div_flags;
+	u8			gate_flags;
+};
+
+#define PNAME(x) static const char *x[] __initconst
+
+enum clk_type {
+	clk_fixed_factor,
+	clk_fixed_rate,
+	clk_composite,
+	clk_cpu,
+	clk_pll,
+};
+
+struct clk_conf {
+	u16				reg_off;
+	enum clk_type			clk_type;
+	unsigned int			clk_id;
+	const char			*clk_name;
+	const char			**clks_parent;
+	int				num_parents;
+	unsigned long			flags;
+	union {
+		struct fixed_fact_conf	fixed_fact;
+		struct fixed_rate_conf	fixed_rate;
+		struct composite_conf	*composite;
+		struct pll_conf		*pll;
+	} conf;
+};
+
+#define FIXED_RATE_P(_ro, _ci, _cn, _f, _c)				\
+	{								\
+		.reg_off			= (_ro),		\
+		.clk_type			= clk_fixed_rate,	\
+		.clk_id				= (_ci),		\
+		.clk_name			= (_cn),		\
+		.flags				= (_f),			\
+		.conf.fixed_rate.rate_parm	= _c,			\
+	}								\
+
+#define FIXED_RATE(_ci, _cn, _f, _r)					\
+	{								\
+		.clk_type			= clk_fixed_rate,	\
+		.clk_id				= (_ci),		\
+		.clk_name			= (_cn),		\
+		.flags				= (_f),			\
+		.conf.fixed_rate.rate		= (_r),			\
+	}								\
+
+#define PLL(_ro, _ci, _cn, _cp, _f, _c)					\
+	{								\
+		.reg_off			= (_ro),		\
+		.clk_type			= clk_pll,		\
+		.clk_id				= (_ci),		\
+		.clk_name			= (_cn),		\
+		.clks_parent			= (_cp),		\
+		.num_parents			= ARRAY_SIZE(_cp),	\
+		.flags				= (_f),			\
+		.conf.pll			= (_c),			\
+	}								\
+
+#define FIXED_FACTOR_DIV(_ci, _cn, _cp, _f, _d)				\
+	{								\
+		.clk_type			= clk_fixed_factor,	\
+		.clk_id				= (_ci),		\
+		.clk_name			= (_cn),		\
+		.clks_parent			= (_cp),		\
+		.num_parents			= ARRAY_SIZE(_cp),	\
+		.conf.fixed_fact.div		= (_d),			\
+	}								\
+
+#define CPU(_ro, _ci, _cn, _cp)						\
+	{								\
+		.reg_off			= (_ro),		\
+		.clk_type			= clk_cpu,		\
+		.clk_id				= (_ci),		\
+		.clk_name			= (_cn),		\
+		.clks_parent			= (_cp),		\
+		.num_parents			= ARRAY_SIZE(_cp),	\
+	}								\
+
+#define COMPOSITE(_ro, _ci, _cn, _cp, _f, _c)				\
+	{								\
+		.reg_off			= (_ro),		\
+		.clk_type			= clk_composite,	\
+		.clk_id				= (_ci),		\
+		.clk_name			= (_cn),		\
+		.clks_parent			= (_cp),		\
+		.num_parents			= ARRAY_SIZE(_cp),	\
+		.flags				= (_f),			\
+		.conf.composite			= (_c),			\
+	}								\
+
+struct clk **meson_clk_init(struct device_node *np, unsigned long nr_clks);
+void meson_clk_register_clks(struct clk_conf *clk_confs,
+			     unsigned int nr_confs, void __iomem *clk_base);
+struct clk *meson_clk_register_cpu(struct clk_conf *clk_conf,
+				   void __iomem *reg_base, spinlock_t *lock);
+struct clk *meson_clk_register_pll(struct clk_conf *clk_conf,
+				   void __iomem *reg_base, spinlock_t *lock);
+
+#endif /* __CLKC_H */
diff --git a/include/dt-bindings/clock/meson8b-clkc.h b/include/dt-bindings/clock/meson8b-clkc.h
new file mode 100644
index 0000000..9986c38
--- /dev/null
+++ b/include/dt-bindings/clock/meson8b-clkc.h
@@ -0,0 +1,20 @@ 
+/*
+ * Meson8b clock tree IDs
+ */
+
+#define CLKID_UNUSED		0
+#define CLKID_XTAL		1
+#define CLKID_PLL_FIXED		2
+#define CLKID_PLL_VID		3
+#define CLKID_PLL_SYS		4
+#define CLKID_FCLK_DIV2		5
+#define CLKID_FCLK_DIV3		6
+#define CLKID_FCLK_DIV4		7
+#define CLKID_FCLK_DIV5		8
+#define CLKID_FCLK_DIV7		9
+#define CLKID_CLK81		10
+#define CLKID_MALI		11
+#define CLKID_CPUCLK		12
+#define CLKID_ZERO		13
+
+#define CLK_NR_CLKS		(CLKID_ZERO + 1)