[PATCH 2/3] clk: owl: add clock driver for Actions S900 SoC

Manivannan Sadhasivam manivannan.sadhasivam at linaro.org
Tue Oct 31 12:54:22 PDT 2017


This patch adds clock driver for Actions Semi OWL
series S900 SoC.

Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam at linaro.org>
---
 MAINTAINERS                                    |   5 +
 drivers/clk/Makefile                           |   1 +
 drivers/clk/owl/Makefile                       |   2 +
 drivers/clk/owl/clk-factor.c                   | 270 ++++++++++++
 drivers/clk/owl/clk-pll.c                      | 346 +++++++++++++++
 drivers/clk/owl/clk-s900.c                     | 587 +++++++++++++++++++++++++
 drivers/clk/owl/clk.c                          | 318 ++++++++++++++
 drivers/clk/owl/clk.h                          | 301 +++++++++++++
 include/dt-bindings/clock/actions,s900-clock.h | 141 ++++++
 9 files changed, 1971 insertions(+)
 create mode 100644 drivers/clk/owl/Makefile
 create mode 100644 drivers/clk/owl/clk-factor.c
 create mode 100644 drivers/clk/owl/clk-pll.c
 create mode 100644 drivers/clk/owl/clk-s900.c
 create mode 100644 drivers/clk/owl/clk.c
 create mode 100644 drivers/clk/owl/clk.h
 create mode 100644 include/dt-bindings/clock/actions,s900-clock.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 2d3d750..beae8aa 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1098,6 +1098,11 @@ F:	arch/arm/mach-*/
 F:	arch/arm/plat-*/
 T:	git git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc.git
 
+ARM/ACTIONS SEMI SoC CLOCK SUPPORT
+L:	Manivannan Sadhasivam <manivannan.sadhasivam at linaro.org>
+S:	Maintained
+F:	drivers/clk/owl/
+
 ARM/ACTIONS SEMI ARCHITECTURE
 M:	Andreas Färber <afaerber at suse.de>
 L:	linux-arm-kernel at lists.infradead.org (moderated for non-subscribers)
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index c99f363..821c1e1 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -75,6 +75,7 @@ endif
 obj-y					+= mvebu/
 obj-$(CONFIG_ARCH_MXS)			+= mxs/
 obj-$(CONFIG_COMMON_CLK_NXP)		+= nxp/
+obj-$(CONFIG_ARCH_ACTIONS)		+= owl/
 obj-$(CONFIG_MACH_PISTACHIO)		+= pistachio/
 obj-$(CONFIG_COMMON_CLK_PXA)		+= pxa/
 obj-$(CONFIG_COMMON_CLK_QCOM)		+= qcom/
diff --git a/drivers/clk/owl/Makefile b/drivers/clk/owl/Makefile
new file mode 100644
index 0000000..dbba0af
--- /dev/null
+++ b/drivers/clk/owl/Makefile
@@ -0,0 +1,2 @@
+obj-y				+= clk.o clk-pll.o clk-factor.o
+obj-$(CONFIG_ARCH_ACTIONS)	+= clk-s900.o
diff --git a/drivers/clk/owl/clk-factor.c b/drivers/clk/owl/clk-factor.c
new file mode 100644
index 0000000..6429acd
--- /dev/null
+++ b/drivers/clk/owl/clk-factor.c
@@ -0,0 +1,270 @@
+/*
+ * Copyright (c) 2014 Actions Semi Inc.
+ * David Liu <liuwei at actions-semi.com>
+ *
+ * Copyright (c) 2017 Linaro Ltd.
+ * Author: Manivannan Sadhasivam <manivannan.sadhasivam at linaro.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License v2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that 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-provider.h>
+#include <linux/slab.h>
+#include "clk.h"
+
+#define to_owl_factor(_hw)	container_of(_hw, struct owl_factor, hw)
+#define div_mask(d)		((1 << ((d)->width)) - 1)
+
+static unsigned int _get_table_maxval(const struct clk_factor_table *table)
+{
+	unsigned int maxval = 0;
+	const struct clk_factor_table *clkt;
+
+	for (clkt = table; clkt->div; clkt++)
+		if (clkt->val > maxval)
+			maxval = clkt->val;
+	return maxval;
+}
+
+static int _get_table_div_mul(const struct clk_factor_table *table,
+			unsigned int val, unsigned int *mul, unsigned int *div)
+{
+	const struct clk_factor_table *clkt;
+
+	for (clkt = table; clkt->div; clkt++) {
+		if (clkt->val == val) {
+			*mul = clkt->mul;
+			*div = clkt->div;
+			return 1;
+		}
+	}
+	return 0;
+}
+
+static unsigned int _get_table_val(const struct clk_factor_table *table,
+			unsigned long rate, unsigned long parent_rate)
+{
+	const struct clk_factor_table *clkt;
+	int val = -1;
+	u64 calc_rate;
+
+	for (clkt = table; clkt->div; clkt++) {
+		calc_rate = parent_rate * clkt->mul;
+		do_div(calc_rate, clkt->div);
+
+		if ((unsigned long)calc_rate <= rate) {
+			val = clkt->val;
+			break;
+		}
+	}
+
+	if (val == -1)
+		val = _get_table_maxval(table);
+
+	return val;
+}
+
+static int clk_val_best(struct clk_hw *hw, unsigned long rate,
+			unsigned long *best_parent_rate)
+{
+	struct owl_factor *factor = to_owl_factor(hw);
+	const struct clk_factor_table *clkt = factor->table;
+	unsigned long parent_rate, try_parent_rate, best = 0, now;
+	unsigned long parent_rate_saved = *best_parent_rate;
+	int bestval = 0;
+
+	if (!rate)
+		rate = 1;
+
+	if (!(clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT)) {
+		parent_rate = *best_parent_rate;
+		bestval = _get_table_val(clkt, rate, parent_rate);
+		return bestval;
+	}
+
+	for (clkt = factor->table; clkt->div; clkt++) {
+		try_parent_rate = rate * clkt->div / clkt->mul;
+
+		if (try_parent_rate == parent_rate_saved) {
+			pr_debug("%s: [%d %d %d] found try_parent_rate %ld\n",
+				__func__, clkt->val, clkt->mul, clkt->div,
+				try_parent_rate);
+			/*
+			 * It's the most ideal case if the requested rate can be
+			 * divided from parent clock without any need to change
+			 * parent rate, so return the divider immediately.
+			 */
+			*best_parent_rate = parent_rate_saved;
+			return clkt->val;
+		}
+
+		parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw),
+				try_parent_rate);
+		now = DIV_ROUND_UP(parent_rate, clkt->div) * clkt->mul;
+		if (now <= rate && now > best) {
+			bestval = clkt->val;
+			best = now;
+			*best_parent_rate = parent_rate;
+		}
+	}
+
+	if (!bestval) {
+		bestval = _get_table_maxval(clkt);
+		*best_parent_rate = clk_hw_round_rate(
+				clk_hw_get_parent(hw), 1);
+	}
+
+	pr_debug("%s: return bestval %d\n", __func__, bestval);
+
+	return bestval;
+}
+
+/**
+ * owl_factor_round_rate() - round a clock frequency
+ * @hw:	handle between common and hardware-specific interfaces
+ * @rate: desired clock frequency
+ * @prate: clock frequency of parent clock
+ */
+static long owl_factor_round_rate(struct clk_hw *hw, unsigned long rate,
+			unsigned long *parent_rate)
+{
+	struct owl_factor *factor = to_owl_factor(hw);
+	const struct clk_factor_table *clkt = factor->table;
+	unsigned int val, mul = 0, div = 1;
+
+	val = clk_val_best(hw, rate, parent_rate);
+	_get_table_div_mul(clkt, val, &mul, &div);
+
+	return *parent_rate * mul / div;
+}
+
+/**
+ * owl_factor_recalc_rate() - recalculate clock frequency
+ * @hw:	handle between common and hardware-specific interfaces
+ * @parent_rate: clock frequency of parent clock
+ */
+static unsigned long owl_factor_recalc_rate(struct clk_hw *hw,
+			unsigned long parent_rate)
+{
+	struct owl_factor *factor = to_owl_factor(hw);
+	const struct clk_factor_table *clkt = factor->table;
+	u64 rate;
+	u32 val, mul, div;
+
+	div = 0;
+	mul = 0;
+
+	val = readl(factor->reg) >> factor->shift;
+	val &= div_mask(factor);
+
+	_get_table_div_mul(clkt, val, &mul, &div);
+	if (!div) {
+		WARN(!(factor->flags & CLK_DIVIDER_ALLOW_ZERO),
+			"%s: Zero divisor and CLK_DIVIDER_ALLOW_ZERO not set\n",
+			__clk_get_name(hw->clk));
+		return parent_rate;
+	}
+
+	rate = (u64)parent_rate * mul;
+	do_div(rate, div);
+
+	return rate;
+}
+
+/**
+ * owl_factor_set_rate() - set clock frequency
+ * @hw: handle between common and hardware-specific interfaces
+ * @parent_rate: clock frequency of parent clock
+ */
+static int owl_factor_set_rate(struct clk_hw *hw, unsigned long rate,
+			unsigned long parent_rate)
+{
+	struct owl_factor *factor = to_owl_factor(hw);
+	unsigned long flags = 0;
+	u32 val, v;
+
+	val = _get_table_val(factor->table, rate, parent_rate);
+
+	pr_debug("%s: get_table_val %d\n", __func__, val);
+
+	if (val > div_mask(factor))
+		val = div_mask(factor);
+
+	if (factor->lock)
+		spin_lock_irqsave(factor->lock, flags);
+
+	v = readl(factor->reg);
+	v &= ~(div_mask(factor) << factor->shift);
+	v |= val << factor->shift;
+	writel(v, factor->reg);
+
+	if (factor->lock)
+		spin_unlock_irqrestore(factor->lock, flags);
+
+	return 0;
+}
+
+const struct clk_ops owl_factor_ops = {
+	.round_rate	= owl_factor_round_rate,
+	.recalc_rate	= owl_factor_recalc_rate,
+	.set_rate	= owl_factor_set_rate,
+};
+
+/**
+ * owl_factor_clk_register() - register pll with the clock framework
+ * @name: pll name
+ * @parent: parent clock name
+ * @reg: pointer to pll control register
+ * @pll_status: pointer to pll status register
+ * @lock_index: bit index to this pll's lock status bit in pll_status
+ * @lock: register lock
+ */
+struct clk_hw *owl_factor_clk_register(struct device *dev, const char *name,
+		const char *parent_name, unsigned long flags,
+		void __iomem *reg, u8 shift, u8 width,
+		u8 clk_factor_flags, const struct clk_factor_table *table,
+		spinlock_t *lock)
+
+{
+	struct owl_factor *factor;
+	struct clk_init_data initd;
+	struct clk_hw *clk_hw;
+	int ret;
+
+	factor = kzalloc(sizeof(*factor), GFP_KERNEL);
+	if (!factor)
+		return ERR_PTR(-ENOMEM);
+
+	initd.name = name;
+	initd.ops = &owl_factor_ops;
+	initd.flags = flags;
+	initd.parent_names = (parent_name ? &parent_name : NULL);
+	initd.num_parents = (parent_name ? 1 : 0);
+
+	factor->reg = reg;
+	factor->shift = shift;
+	factor->width = width;
+	factor->flags = clk_factor_flags;
+	factor->lock = lock;
+	factor->hw.init = &initd;
+	factor->table = table;
+
+	clk_hw = &factor->hw;
+	ret = clk_hw_register(dev, clk_hw);
+	if (ret) {
+		kfree(factor);
+		clk_hw = ERR_PTR(ret);
+	}
+
+	return clk_hw;
+}
diff --git a/drivers/clk/owl/clk-pll.c b/drivers/clk/owl/clk-pll.c
new file mode 100644
index 0000000..ef98e90
--- /dev/null
+++ b/drivers/clk/owl/clk-pll.c
@@ -0,0 +1,346 @@
+/*
+ * Copyright (c) 2014 Actions Semi Inc.
+ * David Liu <liuwei at actions-semi.com>
+ *
+ * Copyright (c) 2017 Linaro Ltd.
+ * Author: Manivannan Sadhasivam <manivannan.sadhasivam at linaro.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License v2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that 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-provider.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include "clk.h"
+
+/**
+ * struct owl_pll
+ * @hw: handle between common and hardware-specific interfaces
+ * @reg: pll control register
+ * @lock: register lock
+ * @bfreq: base frequency of the pll. pll frequency = bfreq * mul
+ * @enable_bit: enable bit for pll
+ * @shift: shift to the multiplier bit field
+ * @width: width of the multiplier bit field
+ * @min_mul: minimum multiple for the pll
+ * @max_mul: maximum multiple for the pll
+ * @pll_flags: flags for the pll
+ * @table: pll table
+ */
+struct owl_pll {
+	struct clk_hw		hw;
+	void __iomem		*reg;
+	spinlock_t		*lock;
+	unsigned long		bfreq;
+	u8			enable_bit;
+	u8			shift;
+	u8			width;
+	u8			min_mul;
+	u8			max_mul;
+	u8			pll_flags;
+	const struct clk_pll_table *table;
+};
+
+#define to_owl_pll(_hw)		container_of(_hw, struct owl_pll, hw)
+#define mul_mask(m)		((1 << ((m)->width)) - 1)
+#define PLL_STABILITY_WAIT_US	(50)
+
+/**
+ * owl_pll_calculate_mul() - calculate multiple for specific rate
+ * @pll: owl pll
+ * @rate: desired clock frequency
+ */
+static u32 owl_pll_calculate_mul(struct owl_pll *pll, unsigned long rate)
+{
+	u32 mul;
+
+	mul = DIV_ROUND_CLOSEST(rate, pll->bfreq);
+	if (mul < pll->min_mul)
+		mul = pll->min_mul;
+	else if (mul > pll->max_mul)
+		mul = pll->max_mul;
+
+	return mul &= mul_mask(pll);
+}
+
+static unsigned int _get_table_rate(const struct clk_pll_table *table,
+		unsigned int val)
+{
+	const struct clk_pll_table *clkt;
+
+	for (clkt = table; clkt->rate; clkt++)
+		if (clkt->val == val)
+			return clkt->rate;
+
+	return 0;
+}
+
+static const struct clk_pll_table *_get_pll_table(
+		const struct clk_pll_table *table, unsigned long rate)
+{
+	const struct clk_pll_table *clkt;
+
+	for (clkt = table; clkt->rate; clkt++) {
+		if (clkt->rate == rate) {
+			table = clkt;
+			break;
+		} else if (clkt->rate < rate)
+			table = clkt;
+	}
+
+	return table;
+}
+
+/**
+ * owl_pll_round_rate() - round a clock frequency
+ * @hw: handle between common and hardware-specific interfaces
+ * @rate: desired clock frequency
+ * @parent_rate: clock frequency of parent clock
+ */
+static long owl_pll_round_rate(struct clk_hw *hw, unsigned long rate,
+		unsigned long *parent_rate)
+{
+	struct owl_pll *pll = to_owl_pll(hw);
+	const struct clk_pll_table *clkt;
+	u32 mul;
+
+	if (pll->table) {
+		clkt = _get_pll_table(pll->table, rate);
+		return clkt->rate;
+	}
+
+	/* fixed frequency */
+	if (pll->width == 0)
+		return pll->bfreq;
+
+	mul = owl_pll_calculate_mul(pll, rate);
+
+	return pll->bfreq * mul;
+}
+
+/**
+ * owl_pll_recalc_rate() - recalculate pll clock frequency
+ * @hw:	handle between common and hardware-specific interfaces
+ * @parent_rate: clock frequency of parent clock
+ */
+static unsigned long owl_pll_recalc_rate(struct clk_hw *hw,
+		unsigned long parent_rate)
+{
+	struct owl_pll *pll = to_owl_pll(hw);
+	unsigned long rate;
+	u32 val, mul;
+
+	if (pll->table) {
+		val = readl(pll->reg) >> pll->shift;
+		val &= mul_mask(pll);
+
+		rate = _get_table_rate(pll->table, val);
+
+		return rate;
+	}
+
+	/* fixed frequency */
+	if (pll->width == 0)
+		return pll->bfreq;
+
+	mul = (readl(pll->reg) >> pll->shift) & mul_mask(pll);
+
+	return pll->bfreq * mul;
+}
+
+/**
+ * owl_pll_is_enabled - check if pll is enabled
+ * @hw: handle between common and hardware-specific interfaces
+ *
+ * Not sure this is a good idea, but since disabled means bypassed for
+ * this clock implementation we say we are always enabled.
+ */
+static int owl_pll_is_enabled(struct clk_hw *hw)
+{
+	struct owl_pll *pll = to_owl_pll(hw);
+	unsigned long flags = 0;
+	u32 v;
+
+	if (pll->lock)
+		spin_lock_irqsave(pll->lock, flags);
+
+	v = readl(pll->reg);
+
+	if (pll->lock)
+		spin_unlock_irqrestore(pll->lock, flags);
+
+	return !!(v & BIT(pll->enable_bit));
+}
+
+/**
+ * owl_pll_enable - enable pll clock
+ * @hw:	handle between common and hardware-specific interfaces
+ */
+static int owl_pll_enable(struct clk_hw *hw)
+{
+	struct owl_pll *pll = to_owl_pll(hw);
+	unsigned long flags = 0;
+	u32 v;
+
+	/* exit if pll is enabled */
+	if (owl_pll_is_enabled(hw))
+		return 0;
+
+	if (pll->lock)
+		spin_lock_irqsave(pll->lock, flags);
+
+	v = readl(pll->reg);
+	v |= BIT(pll->enable_bit);
+	writel(v, pll->reg);
+
+	if (pll->lock)
+		spin_unlock_irqrestore(pll->lock, flags);
+
+	udelay(PLL_STABILITY_WAIT_US);
+
+	return 0;
+}
+
+/**
+ * owl_pll_disable - disable pll clock
+ * @hw:	handle between common and hardware-specific interfaces
+ */
+static void owl_pll_disable(struct clk_hw *hw)
+{
+	struct owl_pll *pll = to_owl_pll(hw);
+	unsigned long flags = 0;
+	u32 v;
+
+	/* exit if pll is disabled */
+	if (!owl_pll_is_enabled(hw))
+		return;
+
+	if (pll->lock)
+		spin_lock_irqsave(pll->lock, flags);
+
+	v = readl(pll->reg);
+	v &= ~BIT(pll->enable_bit);
+	writel(v, pll->reg);
+
+	if (pll->lock)
+		spin_unlock_irqrestore(pll->lock, flags);
+}
+
+/**
+ * owl_pll_set_rate - set pll rate
+ * @hw: handle between common and hardware-specific interfaces
+ * @rate: desired clock frequency
+ * @parent_rate: clock frequency of parent
+ */
+static int owl_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+		unsigned long parent_rate)
+{
+	struct owl_pll *pll = to_owl_pll(hw);
+	const struct clk_pll_table *clkt;
+	unsigned long flags = 0;
+	u32 val, v;
+
+	pr_debug("%s: rate %ld, parent_rate %ld, before set rate: reg 0x%x\n",
+		__func__, rate, parent_rate, readl(pll->reg));
+
+	/* fixed frequency */
+	if (pll->width == 0)
+		return 0;
+
+	if (pll->table) {
+		clkt = _get_pll_table(pll->table, rate);
+		val = clkt->val;
+	} else {
+		val = owl_pll_calculate_mul(pll, rate);
+	}
+
+	if (pll->lock)
+		spin_lock_irqsave(pll->lock, flags);
+
+	v = readl(pll->reg);
+	v &= ~mul_mask(pll);
+	v |= val << pll->shift;
+	writel(v, pll->reg);
+
+	udelay(PLL_STABILITY_WAIT_US);
+
+	if (pll->lock)
+		spin_unlock_irqrestore(pll->lock, flags);
+
+	pr_debug("%s: after set rate: reg 0x%x\n", __func__,
+		readl(pll->reg));
+
+	return 0;
+}
+
+static const struct clk_ops owl_pll_ops = {
+	.enable = owl_pll_enable,
+	.disable = owl_pll_disable,
+	.is_enabled = owl_pll_is_enabled,
+	.round_rate = owl_pll_round_rate,
+	.recalc_rate = owl_pll_recalc_rate,
+	.set_rate = owl_pll_set_rate,
+};
+
+/**
+ * owl_clk_register_pll() - register pll with the clock framework
+ * @name: pll name
+ * @parent: parent clock name
+ * @reg: pointer to pll control register
+ * @pll_status: pointer to pll status register
+ * @lock_index: bit index to this pll's lock status bit in @pll_status
+ * @lock: register lock
+ */
+struct clk_hw *owl_pll_clk_register(const char *name, const char *parent_name,
+		unsigned long flags, void __iomem *reg, unsigned long bfreq,
+		u8 enable_bit, u8 shift, u8 width, u8 min_mul, u8 max_mul,
+		u8 pll_flags, const struct clk_pll_table *table,
+		spinlock_t *lock)
+{
+	struct owl_pll *pll;
+	struct clk_hw *clk_hw;
+	struct clk_init_data initd;
+	int ret;
+
+	pll = kmalloc(sizeof(*pll), GFP_KERNEL);
+	if (!pll)
+		return ERR_PTR(-ENOMEM);
+
+	initd.name = name;
+	initd.parent_names = (parent_name ? &parent_name : NULL);
+	initd.num_parents = (parent_name ? 1 : 0);
+	initd.ops = &owl_pll_ops;
+	initd.flags = flags;
+
+	pll->hw.init = &initd;
+	pll->bfreq = bfreq;
+	pll->enable_bit = enable_bit;
+	pll->shift = shift;
+	pll->width = width;
+	pll->min_mul = min_mul;
+	pll->max_mul = max_mul;
+	pll->pll_flags = pll_flags;
+	pll->table = table;
+	pll->reg = reg;
+	pll->lock = lock;
+
+	clk_hw = &pll->hw;
+	ret = clk_hw_register(NULL, clk_hw);
+	if (ret) {
+		kfree(pll);
+		clk_hw = ERR_PTR(ret);
+	}
+
+	return clk_hw;
+}
diff --git a/drivers/clk/owl/clk-s900.c b/drivers/clk/owl/clk-s900.c
new file mode 100644
index 0000000..f8d3e33
--- /dev/null
+++ b/drivers/clk/owl/clk-s900.c
@@ -0,0 +1,587 @@
+/*
+ * Copyright (c) 2014 Actions Semi Inc.
+ * David Liu <liuwei at actions-semi.com>
+ *
+ * Copyright (c) 2017 Linaro Ltd.
+ * Author: Manivannan Sadhasivam <manivannan.sadhasivam at linaro.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License v2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that 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-provider.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <dt-bindings/clock/actions,s900-clock.h>
+#include "clk.h"
+
+#define CMU_COREPLL		(0x0000)
+#define CMU_DEVPLL		(0x0004)
+#define CMU_DDRPLL		(0x0008)
+#define CMU_NANDPLL		(0x000C)
+#define CMU_DISPLAYPLL		(0x0010)
+#define CMU_AUDIOPLL		(0x0014)
+#define CMU_TVOUTPLL		(0x0018)
+#define CMU_BUSCLK		(0x001C)
+#define CMU_SENSORCLK		(0x0020)
+#define CMU_LCDCLK		(0x0024)
+#define CMU_DSICLK		(0x0028)
+#define CMU_CSICLK		(0x002C)
+#define CMU_DECLK		(0x0030)
+#define CMU_BISPCLK		(0x0034)
+#define CMU_IMXCLK		(0x0038)
+#define CMU_HDECLK		(0x003C)
+#define CMU_VDECLK		(0x0040)
+#define CMU_VCECLK		(0x0044)
+#define CMU_NANDCCLK		(0x004C)
+#define CMU_SD0CLK		(0x0050)
+#define CMU_SD1CLK		(0x0054)
+#define CMU_SD2CLK		(0x0058)
+#define CMU_UART0CLK		(0x005C)
+#define CMU_UART1CLK		(0x0060)
+#define CMU_UART2CLK		(0x0064)
+#define CMU_PWM0CLK		(0x0070)
+#define CMU_PWM1CLK		(0x0074)
+#define CMU_PWM2CLK		(0x0078)
+#define CMU_PWM3CLK		(0x007C)
+#define CMU_USBPLL		(0x0080)
+#define CMU_ASSISTPLL		(0x0084)
+#define CMU_EDPCLK		(0x0088)
+#define CMU_GPU3DCLK		(0x0090)
+#define CMU_CORECTL		(0x009C)
+#define CMU_DEVCLKEN0		(0x00A0)
+#define CMU_DEVCLKEN1		(0x00A4)
+#define CMU_DEVRST0		(0x00A8)
+#define CMU_DEVRST1		(0x00AC)
+#define CMU_UART3CLK		(0x00B0)
+#define CMU_UART4CLK		(0x00B4)
+#define CMU_UART5CLK		(0x00B8)
+#define CMU_UART6CLK		(0x00BC)
+#define CMU_TLSCLK		(0x00C0)
+#define CMU_SD3CLK		(0x00C4)
+#define CMU_PWM4CLK		(0x00C8)
+#define CMU_PWM5CLK		(0x00CC)
+
+static struct clk_pll_table clk_audio_pll_table[] = {
+	{0, 45158400}, {1, 49152000},
+	{0, 0},
+};
+
+static struct clk_pll_table clk_edp_pll_table[] = {
+	{0, 810000000}, {1, 1350000000}, {2, 2700000000},
+	{0, 0},
+};
+
+/* pll clocks */
+static struct owl_pll_clock s900_pll_clks[] = {
+	{ CLK_CORE_PLL, "core_pll", NULL, 0, CMU_COREPLL, 24000000, 9, 0, 8,  5, 107, 0, NULL, },
+	{ CLK_DEV_PLL, "dev_pll",  NULL, 0, CMU_DEVPLL, 6000000, 8, 0, 8, 20, 180, 0, NULL, },
+	{ CLK_DDR_PLL, "ddr_pll",  NULL, 0, CMU_DDRPLL, 24000000, 8, 0, 8,  5,  45, 0, NULL, },
+	{ CLK_NAND_PLL, "nand_pll", NULL, 0, CMU_NANDPLL, 6000000, 8, 0, 8,  4, 100, 0, NULL, },
+	{ CLK_DISPLAY_PLL, "display_pll", NULL, 0, CMU_DISPLAYPLL, 6000000, 8, 0, 8, 20, 180, 0, NULL, },
+	{ CLK_ASSIST_PLL, "assist_pll", NULL, 0, CMU_ASSISTPLL, 500000000, 0, 0, 0, 0, 0, CLK_OWL_PLL_FIXED_FREQ, NULL, },
+	{ CLK_AUDIO_PLL, "audio_pll", NULL, 0, CMU_AUDIOPLL, 0, 4, 0, 1, 0, 0, 0, clk_audio_pll_table, },
+
+	{ CLK_EDP_PLL, "edp_pll", "24M_edp", 0, CMU_EDPCLK, 0, 9, 0, 2, 0, 0, 0, clk_edp_pll_table, },
+};
+
+static const char *cpu_clk_mux_p[] = { "losc", "hosc", "core_pll", };
+static const char *dev_clk_p[] = { "hosc", "dev_pll", };
+static const char *noc_clk_mux_p[] = { "dev_clk", "assist_pll", };
+static const char *dmm_clk_mux_p[] = { "dev_clk", "nand_pll", "assist_pll", "ddr_clk_src", };
+
+static const char *bisp_clk_mux_p[] = { "assist_pll", "dev_clk", };
+static const char *csi_clk_mux_p[] = { "display_pll", "dev_clk", };
+static const char *de_clk_mux_p[] = { "assist_pll", "dev_clk", };
+static const char *eth_mac_clk_mux_p[] = { "assist_pll", };
+static const char *gpu_clk_mux_p[] = { "dev_clk", "display_pll", "", "ddr_clk_src", };
+static const char *hde_clk_mux_p[] = { "dev_clk", "display_pll", "", "ddr_clk_src", };
+static const char *i2c_clk_mux_p[] = { "assist_pll", };
+static const char *imx_clk_mux_p[] = { "assist_pll", "dev_clk", };
+static const char *lcd_clk_mux_p[] = { "display_pll", "nand_pll", };
+static const char *nand_clk_mux_p[] = { "dev_clk", "nand_pll", };
+static const char *pwm_clk_mux_p[] = { "hosc" };
+static const char *sd_clk_mux_p[] = { "dev_clk", "nand_pll", };
+static const char *sensor_clk_mux_p[] = { "hosc", "bisp", };
+static const char *speed_sensor_clk_mux_p[] = { "hosc", };
+static const char *spi_clk_mux_p[] = { "ahb_clk", };
+static const char *thermal_sensor_clk_mux_p[] = { "hosc", };
+static const char *uart_clk_mux_p[] = { "hosc", "dev_pll", };
+static const char *vce_clk_mux_p[] = { "dev_clk", "display_pll", "assist_pll", "ddr_clk_src", };
+static const char *i2s_clk_mux_p[] = { "audio_pll", };
+
+static const char *edp_clk_mux_p[] = { "assist_pll", "display_pll", };
+
+/* mux clocks */
+static struct owl_mux_clock s900_mux_clks[] = {
+	{ CLK_CPU,  "cpu_clk", cpu_clk_mux_p, ARRAY_SIZE(cpu_clk_mux_p), CLK_SET_RATE_PARENT, CMU_BUSCLK, 0, 2, 0, "cpu_clk", },
+	{ CLK_DEV,  "dev_clk", dev_clk_p, ARRAY_SIZE(dev_clk_p), CLK_SET_RATE_PARENT, CMU_DEVPLL, 12, 1, 0, "dev_clk", },
+	{ CLK_NOC_CLK_MUX,  "noc_clk_mux", noc_clk_mux_p, ARRAY_SIZE(noc_clk_mux_p), CLK_SET_RATE_PARENT, CMU_BUSCLK, 7, 1, 0, },
+};
+
+static struct clk_div_table nand_div_table[] = {
+	{0, 1},   {1, 2},   {2, 4},   {3, 6},
+	{4, 8},   {5, 10},  {6, 12},  {7, 14},
+	{8, 16},  {9, 18},  {10, 20}, {11, 22},
+	{12, 24}, {13, 26}, {14, 28}, {15, 30},
+	{0, 0},
+};
+
+static struct clk_factor_table sd_factor_table[] = {
+	/* bit0 ~ 4 */
+	{0, 1, 1}, {1, 1, 2}, {2, 1, 3}, {3, 1, 4},
+	{4, 1, 5}, {5, 1, 6}, {6, 1, 7}, {7, 1, 8},
+	{8, 1, 9}, {9, 1, 10}, {10, 1, 11}, {11, 1, 12},
+	{12, 1, 13}, {13, 1, 14}, {14, 1, 15}, {15, 1, 16},
+	{16, 1, 17}, {17, 1, 18}, {18, 1, 19}, {19, 1, 20},
+	{20, 1, 21}, {21, 1, 22}, {22, 1, 23}, {23, 1, 24},
+	{24, 1, 25}, {25, 1, 26}, {26, 1, 27}, {27, 1, 28},
+	{28, 1, 29}, {29, 1, 30}, {30, 1, 31}, {31, 1, 32},
+
+	/* bit8: /128 */
+	{256, 1, 1 * 128}, {257, 1, 2 * 128}, {258, 1, 3 * 128}, {259, 1, 4 * 128},
+	{260, 1, 5 * 128}, {261, 1, 6 * 128}, {262, 1, 7 * 128}, {263, 1, 8 * 128},
+	{264, 1, 9 * 128}, {265, 1, 10 * 128}, {266, 1, 11 * 128}, {267, 1, 12 * 128},
+	{268, 1, 13 * 128}, {269, 1, 14 * 128}, {270, 1, 15 * 128}, {271, 1, 16 * 128},
+	{272, 1, 17 * 128}, {273, 1, 18 * 128}, {274, 1, 19 * 128}, {275, 1, 20 * 128},
+	{276, 1, 21 * 128}, {277, 1, 22 * 128}, {278, 1, 23 * 128}, {279, 1, 24 * 128},
+	{280, 1, 25 * 128}, {281, 1, 26 * 128}, {282, 1, 27 * 128}, {283, 1, 28 * 128},
+	{284, 1, 29 * 128}, {285, 1, 30 * 128}, {286, 1, 31 * 128}, {287, 1, 32 * 128},
+
+	{0, 0},
+};
+
+static struct clk_div_table apb_div_table[] = {
+	{1, 2},   {2, 3},   {3, 4},
+	{0, 0},
+};
+
+static struct clk_div_table eth_mac_div_table[] = {
+	{0, 2},   {1, 4},
+	{0, 0},
+};
+
+static struct clk_div_table rmii_ref_div_table[] = {
+	{0, 4},   {1, 10},
+	{0, 0},
+};
+
+static struct clk_div_table usb3_mac_div_table[] = {
+	{1, 2},   {2, 3},   {3, 4},
+	{0, 8},
+};
+
+static struct clk_div_table i2s_div_table[] = {
+	{0, 1},   {1, 2},   {2, 3},   {3, 4},
+	{4, 6},   {5, 8},   {6, 12},  {7, 16},
+	{8, 24},
+	{0, 0},
+};
+
+static struct clk_div_table hdmia_div_table[] = {
+	{0, 1},   {1, 2},   {2, 3},   {3, 4},
+	{4, 6},   {5, 8},   {6, 12},  {7, 16},
+	{8, 24},
+	{0, 0},
+};
+
+
+/* divider clocks */
+static struct owl_divider_clock s900_div_clks[] = {
+	{ CLK_NOC_CLK_DIV, "noc_clk_div", "noc_clk", 0, CMU_BUSCLK, 19, 1, 0, NULL, },
+	{ CLK_AHB, "ahb_clk", "noc_clk_div", 0, CMU_BUSCLK, 4, 1, 0, NULL, "ahb_clk", },
+	{ CLK_APB, "apb_clk", "ahb_clk", 0, CMU_BUSCLK, 8, 2, 0, apb_div_table, "apb_clk", },
+	{ CLK_USB3_MAC, "usb3_mac", "assist_pll", 0, CMU_ASSISTPLL, 12, 2, 0, usb3_mac_div_table, "usb3_mac", },
+	{ CLK_RMII_REF, "rmii_ref", "assist_pll", 0, CMU_ASSISTPLL, 8, 1, 0, rmii_ref_div_table, "rmii_ref", },
+};
+
+static struct clk_factor_table dmm_factor_table[] = {
+	{0, 1, 1}, {1, 2, 3}, {2, 1, 2}, {3, 1, 3},
+	{4, 1, 4},
+	{0, 0, 0},
+};
+
+static struct clk_factor_table noc_factor_table[] = {
+	{0, 1, 1},   {1, 2, 3},   {2, 1, 2}, {3, 1, 3},  {4, 1, 4},
+	{0, 0, 0},
+};
+
+static struct clk_factor_table bisp_factor_table[] = {
+	{0, 1, 1}, {1, 2, 3}, {2, 1, 2}, {3, 2, 5},
+	{4, 1, 3}, {5, 1, 4}, {6, 1, 6}, {7, 1, 8},
+	{0, 0, 0},
+};
+
+/* divider clocks */
+static struct owl_factor_clock s900_factor_clks[] = {
+	{ CLK_NOC, "noc_clk", "noc_clk_mux", 0, CMU_BUSCLK, 16, 3, 0, noc_factor_table, "noc_clk", },
+	{ CLK_DE1, "de_clk1", "de_clk", 0, CMU_DECLK, 0, 3, 0, bisp_factor_table, "de_clk1", },
+	{ CLK_DE2, "de_clk2", "de_clk", 0, CMU_DECLK, 4, 3, 0, bisp_factor_table, "de_clk2", },
+	{ CLK_DE3, "de_clk3", "de_clk", 0, CMU_DECLK, 8, 3, 0, bisp_factor_table, "de_clk3", },
+};
+
+/* gate clocks */
+static struct owl_gate_clock s900_gate_clks[] = {
+	{ CLK_GPIO,  "gpio", "apb_clk", 0, CMU_DEVCLKEN0, 18, 0, "gpio", },
+	{ CLK_GPU,   "gpu", NULL, 0, CMU_DEVCLKEN0, 30, 0, "gpu", },
+	{ CLK_DMAC,  "dmac", "noc_clk_div", 0, CMU_DEVCLKEN0, 1, 0, "dmac", },
+	{ CLK_TIMER,  "timer", "hosc", 0, CMU_DEVCLKEN1, 27, 0, "timer", },
+	{ CLK_DSI,  "dsi_clk", NULL, 0, CMU_DEVCLKEN0, 12, 0, "dsi", },
+
+	{ CLK_DDR0,  "ddr0_clk", "ddr_pll", CLK_IGNORE_UNUSED, CMU_DEVCLKEN0, 31, 0, "ddr0", },
+	{ CLK_DDR1,  "ddr1_clk", "ddr_pll", CLK_IGNORE_UNUSED, CMU_DEVCLKEN0, 29, 0, "ddr1", },
+
+	{ CLK_USB3_480MPLL0,	"usb3_480mpll0",	NULL, 0, CMU_USBPLL, 3, 0, "usb3_480mpll0", },
+	{ CLK_USB3_480MPHY0,	"usb3_480mphy0",	NULL, 0, CMU_USBPLL, 2, 0, "usb3_480mphy0", },
+	{ CLK_USB3_5GPHY,	"usb3_5gphy",		NULL, 0, CMU_USBPLL, 1, 0, "usb3_5gphy", },
+	{ CLK_USB3_CCE,		"usb3_cce",		NULL, 0, CMU_USBPLL, 0, 0, "usb3_cce", },
+
+	{ CLK_24M_EDP,		"24M_edp",		"diff_24M", 0, CMU_EDPCLK, 8, 0, "24M_edp", },
+	{ CLK_EDP_LINK,		"edp_link",		"edp_pll", 0, CMU_DEVCLKEN0, 10, 0, "edp_link", },
+
+	{ CLK_USB2H0_PLLEN,	"usbh0_pllen",	NULL, 0, CMU_USBPLL, 12, 0, "usbh0_pllen", },
+	{ CLK_USB2H0_PHY,	"usbh0_phy",	NULL, 0, CMU_USBPLL, 10, 0, "usbh0_phy", },
+	{ CLK_USB2H0_CCE,	"usbh0_cce",		NULL, 0, CMU_USBPLL, 8, 0, "usbh0_cce", },
+
+	{ CLK_USB2H1_PLLEN,	"usbh1_pllen",	NULL, 0, CMU_USBPLL, 13, 0, "usbh1_pllen", },
+	{ CLK_USB2H1_PHY,	"usbh1_phy",	NULL, 0, CMU_USBPLL, 11, 0, "usbh1_phy", },
+	{ CLK_USB2H1_CCE,	"usbh1_cce",		NULL, 0, CMU_USBPLL, 9, 0, "usbh1_cce", },
+};
+
+static struct owl_composite_clock s900_composite_clks[] = {
+	COMP_FACTOR_CLK(CLK_BISP, "bisp", 0,
+			C_MUX(bisp_clk_mux_p, CMU_BISPCLK, 4, 1, 0),
+			C_GATE(CMU_DEVCLKEN0, 14, 0),
+			C_FACTOR(CMU_BISPCLK, 0, 3, bisp_factor_table, 0)),
+
+	COMP_DIV_CLK(CLK_CSI0, "csi0", 0,
+			C_MUX(csi_clk_mux_p, CMU_CSICLK, 4, 1, 0),
+			C_GATE(CMU_DEVCLKEN0, 13, 0),
+			C_DIVIDER(CMU_CSICLK, 0, 4, NULL, 0)),
+
+	COMP_DIV_CLK(CLK_CSI1, "csi1", 0,
+			C_MUX(csi_clk_mux_p, CMU_CSICLK, 20, 1, 0),
+			C_GATE(CMU_DEVCLKEN0, 15, 0),
+			C_DIVIDER(CMU_CSICLK, 16, 4, NULL, 0)),
+
+	COMP_PASS_CLK(CLK_DE, "de_clk", 0,
+			C_MUX(de_clk_mux_p, CMU_DECLK, 12, 1, 0),
+			C_GATE(CMU_DEVCLKEN0, 8, 0)),
+
+	COMP_FACTOR_CLK(CLK_DMM, "dmm", CLK_IGNORE_UNUSED,
+			C_MUX(dmm_clk_mux_p, CMU_BUSCLK, 10, 2, 0),
+			C_GATE(CMU_DEVCLKEN0, 19, 0),
+			C_FACTOR(CMU_BUSCLK, 12, 3, dmm_factor_table, 0)),
+
+	COMP_FACTOR_CLK(CLK_EDP, "edp_clk", 0,
+			C_MUX(edp_clk_mux_p, CMU_EDPCLK, 19, 1, 0),
+			C_GATE(CMU_DEVCLKEN0, 10, 0),
+			C_FACTOR(CMU_EDPCLK, 16, 3, bisp_factor_table, 0)),
+
+	COMP_DIV_CLK(CLK_ETH_MAC, "eth_mac", 0,
+			C_MUX_F(eth_mac_clk_mux_p, 0),
+			C_GATE(CMU_DEVCLKEN1, 22, 0),
+			C_DIVIDER(CMU_ASSISTPLL, 10, 1, eth_mac_div_table, 0)),
+
+	COMP_FACTOR_CLK(CLK_GPU_CORE, "gpu_core", 0,
+			C_MUX(gpu_clk_mux_p, CMU_GPU3DCLK, 4, 2, 0),
+			C_GATE(CMU_GPU3DCLK, 15, 0),
+			C_FACTOR(CMU_GPU3DCLK, 0, 3, bisp_factor_table, 0)),
+
+	COMP_FACTOR_CLK(CLK_GPU_MEM, "gpu_mem", 0,
+			C_MUX(gpu_clk_mux_p, CMU_GPU3DCLK, 20, 2, 0),
+			C_GATE(CMU_GPU3DCLK, 14, 0),
+			C_FACTOR(CMU_GPU3DCLK, 16, 3, bisp_factor_table, 0)),
+
+	COMP_FACTOR_CLK(CLK_GPU_SYS, "gpu_sys", 0,
+			C_MUX(gpu_clk_mux_p, CMU_GPU3DCLK, 28, 2, 0),
+			C_GATE(CMU_GPU3DCLK, 13, 0),
+			C_FACTOR(CMU_GPU3DCLK, 24, 3, bisp_factor_table, 0)),
+
+	COMP_FACTOR_CLK(CLK_HDE, "hde", 0,
+			C_MUX(hde_clk_mux_p, CMU_HDECLK, 4, 2, 0),
+			C_GATE(CMU_DEVCLKEN0, 27, 0),
+			C_FACTOR(CMU_HDECLK, 0, 3, bisp_factor_table, 0)),
+
+	COMP_DIV_CLK(CLK_HDMI_AUDIO, "hdmia", 0,
+			C_MUX(i2s_clk_mux_p, CMU_AUDIOPLL, 24, 1, 0),
+			C_GATE(CMU_DEVCLKEN0, 22, 0),
+			C_DIVIDER(CMU_AUDIOPLL, 24, 4, hdmia_div_table, 0)),
+
+	COMP_FIXED_FACTOR_CLK(CLK_I2C0, "i2c0", 0,
+			C_MUX_F(i2c_clk_mux_p, 0),
+			C_GATE(CMU_DEVCLKEN1, 14, 0),
+			C_FIXED_FACTOR(1, 5)),
+
+	COMP_FIXED_FACTOR_CLK(CLK_I2C1, "i2c1", 0,
+			C_MUX_F(i2c_clk_mux_p, 0),
+			C_GATE(CMU_DEVCLKEN1, 15, 0),
+			C_FIXED_FACTOR(1, 5)),
+
+	COMP_FIXED_FACTOR_CLK(CLK_I2C2, "i2c2", 0,
+			C_MUX_F(i2c_clk_mux_p, 0),
+			C_GATE(CMU_DEVCLKEN1, 30, 0),
+			C_FIXED_FACTOR(1, 5)),
+
+	COMP_FIXED_FACTOR_CLK(CLK_I2C3, "i2c3", 0,
+			C_MUX_F(i2c_clk_mux_p, 0),
+			C_GATE(CMU_DEVCLKEN1, 31, 0),
+			C_FIXED_FACTOR(1, 5)),
+
+	COMP_FIXED_FACTOR_CLK(CLK_I2C4, "i2c4", 0,
+			C_MUX_F(i2c_clk_mux_p, 0),
+			C_GATE(CMU_DEVCLKEN0, 17, 0),
+			C_FIXED_FACTOR(1, 5)),
+
+	COMP_FIXED_FACTOR_CLK(CLK_I2C5, "i2c5", 0,
+			C_MUX_F(i2c_clk_mux_p, 0),
+			C_GATE(CMU_DEVCLKEN1, 1, 0),
+			C_FIXED_FACTOR(1, 5)),
+
+	COMP_DIV_CLK(CLK_I2SRX, "i2srx", 0,
+			C_MUX(i2s_clk_mux_p, CMU_AUDIOPLL, 24, 1, 0),
+			C_GATE(CMU_DEVCLKEN0, 21, 0),
+			C_DIVIDER(CMU_AUDIOPLL, 20, 4, i2s_div_table, 0)),
+
+	COMP_DIV_CLK(CLK_I2STX, "i2stx", 0,
+			C_MUX(i2s_clk_mux_p, CMU_AUDIOPLL, 24, 1, 0),
+			C_GATE(CMU_DEVCLKEN0, 20, 0),
+			C_DIVIDER(CMU_AUDIOPLL, 16, 4, i2s_div_table, 0)),
+
+	COMP_FACTOR_CLK(CLK_IMX, "imx", 0,
+			C_MUX(imx_clk_mux_p, CMU_IMXCLK, 4, 1, 0),
+			C_GATE(CMU_DEVCLKEN1, 17, 0),
+			C_FACTOR(CMU_IMXCLK, 0, 3, bisp_factor_table, 0)),
+
+	COMP_DIV_CLK(CLK_LCD, "lcd", 0,
+			C_MUX(lcd_clk_mux_p, CMU_LCDCLK, 12, 2, 0),
+			C_GATE(CMU_DEVCLKEN0, 9, 0),
+			C_DIVIDER(CMU_LCDCLK, 0, 5, NULL, 0)),
+
+	COMP_DIV_CLK(CLK_NAND0, "nand0", CLK_SET_RATE_PARENT,
+			C_MUX(nand_clk_mux_p, CMU_NANDCCLK, 8, 1, 0),
+			C_GATE(CMU_DEVCLKEN0, 4, 0),
+			C_DIVIDER(CMU_NANDCCLK, 0, 4, nand_div_table, 0)),
+
+	COMP_DIV_CLK(CLK_NAND1, "nand1", CLK_SET_RATE_PARENT,
+			C_MUX(nand_clk_mux_p, CMU_NANDCCLK, 24, 1, 0),
+			C_GATE(CMU_DEVCLKEN0, 11, 0),
+			C_DIVIDER(CMU_NANDCCLK, 16, 4, nand_div_table, 0)),
+
+	COMP_DIV_CLK(CLK_PWM0, "pwm0", 0,
+			C_MUX_F(pwm_clk_mux_p, 0),
+			C_GATE(CMU_DEVCLKEN1, 23, 0),
+			C_DIVIDER(CMU_PWM0CLK, 0, 6, NULL, 0)),
+
+	COMP_DIV_CLK(CLK_PWM0, "pwm1", 0,
+			C_MUX_F(pwm_clk_mux_p, 0),
+			C_GATE(CMU_DEVCLKEN1, 24, 0),
+			C_DIVIDER(CMU_PWM1CLK, 0, 6, NULL, 0)),
+	/*
+	 * pwm2 may be for backlight, do not gate it
+	 * even it is "unused", because it may be
+	 * enabled at boot stage, and in kernel, driver
+	 * has no effective method to know the real status,
+	 * so, the best way is keeping it as what it was.
+	 */
+	COMP_DIV_CLK(CLK_PWM0, "pwm2", CLK_IGNORE_UNUSED,
+			C_MUX_F(pwm_clk_mux_p, 0),
+			C_GATE(CMU_DEVCLKEN1, 25, 0),
+			C_DIVIDER(CMU_PWM2CLK, 0, 6, NULL, 0)),
+
+	COMP_DIV_CLK(CLK_PWM0, "pwm3", 0,
+			C_MUX_F(pwm_clk_mux_p, 0),
+			C_GATE(CMU_DEVCLKEN1, 26, 0),
+			C_DIVIDER(CMU_PWM3CLK, 0, 6, NULL, 0)),
+
+	COMP_DIV_CLK(CLK_PWM0, "pwm4", 0,
+			C_MUX_F(pwm_clk_mux_p, 0),
+			C_GATE(CMU_DEVCLKEN1, 4, 0),
+			C_DIVIDER(CMU_PWM4CLK, 0, 6, NULL, 0)),
+
+	COMP_DIV_CLK(CLK_PWM5, "pwm5", 0,
+			C_MUX_F(pwm_clk_mux_p, 0),
+			C_GATE(CMU_DEVCLKEN1, 5, 0),
+			C_DIVIDER(CMU_PWM5CLK, 0, 6, NULL, 0)),
+
+	COMP_FACTOR_CLK(CLK_SD0, "sd0", 0,
+			C_MUX(sd_clk_mux_p, CMU_SD0CLK, 9, 1, 0),
+			C_GATE(CMU_DEVCLKEN0, 5, 0),
+			C_FACTOR(CMU_SD0CLK, 0, 9, sd_factor_table, 0)),
+
+	COMP_FACTOR_CLK(CLK_SD1, "sd1", 0,
+			C_MUX(sd_clk_mux_p, CMU_SD1CLK, 9, 1, 0),
+			C_GATE(CMU_DEVCLKEN0, 6, 0),
+			C_FACTOR(CMU_SD1CLK, 0, 9, sd_factor_table, 0)),
+
+	COMP_FACTOR_CLK(CLK_SD2, "sd2", 0,
+			C_MUX(sd_clk_mux_p, CMU_SD2CLK, 9, 1, 0),
+			C_GATE(CMU_DEVCLKEN0, 7, 0),
+			C_FACTOR(CMU_SD2CLK, 0, 9, sd_factor_table, 0)),
+
+	COMP_FACTOR_CLK(CLK_SD3, "sd3", 0,
+			C_MUX(sd_clk_mux_p, CMU_SD3CLK, 9, 1, 0),
+			C_GATE(CMU_DEVCLKEN0, 16, 0),
+			C_FACTOR(CMU_SD3CLK, 0, 9, sd_factor_table, 0)),
+
+	COMP_DIV_CLK(CLK_SENSOR, "sensor", 0,
+			C_MUX(sensor_clk_mux_p, CMU_SENSORCLK, 4, 1, 0),
+			C_NULL,
+			C_DIVIDER(CMU_SENSORCLK, 0, 4, NULL, 0)),
+
+	COMP_DIV_CLK(CLK_SPEED_SENSOR, "speed_sensor", 0,
+			C_MUX_F(speed_sensor_clk_mux_p, 0),
+			C_GATE(CMU_DEVCLKEN1, 0, 0),
+			C_DIVIDER(CMU_TLSCLK, 0, 4, NULL, CLK_DIVIDER_POWER_OF_TWO)),
+
+	COMP_PASS_CLK(CLK_SPI0, "spi0", 0,
+			C_MUX_F(spi_clk_mux_p, 0),
+			C_GATE(CMU_DEVCLKEN1, 10, 0)),
+
+	COMP_PASS_CLK(CLK_SPI1, "spi1", 0,
+			C_MUX_F(spi_clk_mux_p, 0),
+			C_GATE(CMU_DEVCLKEN1, 11, 0)),
+
+	COMP_PASS_CLK(CLK_SPI2, "spi2", 0,
+			C_MUX_F(spi_clk_mux_p, 0),
+			C_GATE(CMU_DEVCLKEN1, 12, 0)),
+
+	COMP_PASS_CLK(CLK_SPI3, "spi3", 0,
+			C_MUX_F(spi_clk_mux_p, 0),
+			C_GATE(CMU_DEVCLKEN1, 13, 0)),
+
+	COMP_DIV_CLK(CLK_THERMAL_SENSOR, "thermal_sensor", 0,
+			C_MUX_F(thermal_sensor_clk_mux_p, 0),
+			C_GATE(CMU_DEVCLKEN1, 2, 0),
+			C_DIVIDER(CMU_TLSCLK, 8, 4, NULL, CLK_DIVIDER_POWER_OF_TWO)),
+
+	COMP_DIV_CLK(CLK_UART0, "uart0", 0,
+			C_MUX(uart_clk_mux_p, CMU_UART0CLK, 16, 1, 0),
+			C_GATE(CMU_DEVCLKEN1, 6, 0),
+			C_DIVIDER(CMU_UART0CLK, 0, 8, NULL, CLK_DIVIDER_ROUND_CLOSEST)),
+
+	COMP_DIV_CLK(CLK_UART1, "uart1", 0,
+			C_MUX(uart_clk_mux_p, CMU_UART1CLK, 16, 1, 0),
+			C_GATE(CMU_DEVCLKEN1, 7, 0),
+			C_DIVIDER(CMU_UART1CLK, 1, 8, NULL, CLK_DIVIDER_ROUND_CLOSEST)),
+
+	COMP_DIV_CLK(CLK_UART2, "uart2", 0,
+			C_MUX(uart_clk_mux_p, CMU_UART2CLK, 16, 1, 0),
+			C_GATE(CMU_DEVCLKEN1, 8, 0),
+			C_DIVIDER(CMU_UART2CLK, 0, 8, NULL, CLK_DIVIDER_ROUND_CLOSEST)),
+
+	COMP_DIV_CLK(CLK_UART3, "uart3", 0,
+			C_MUX(uart_clk_mux_p, CMU_UART3CLK, 16, 1, 0),
+			C_GATE(CMU_DEVCLKEN1, 19, 0),
+			C_DIVIDER(CMU_UART3CLK, 0, 8, NULL, CLK_DIVIDER_ROUND_CLOSEST)),
+
+	COMP_DIV_CLK(CLK_UART4, "uart4", 0,
+			C_MUX(uart_clk_mux_p, CMU_UART4CLK, 16, 1, 0),
+			C_GATE(CMU_DEVCLKEN1, 20, 0),
+			C_DIVIDER(CMU_UART4CLK, 0, 8, NULL, CLK_DIVIDER_ROUND_CLOSEST)),
+
+	COMP_DIV_CLK(CLK_UART5, "uart5", 0,
+			C_MUX(uart_clk_mux_p, CMU_UART5CLK, 16, 1, 0),
+			C_GATE(CMU_DEVCLKEN1, 21, 0),
+			C_DIVIDER(CMU_UART5CLK, 0, 8, NULL, CLK_DIVIDER_ROUND_CLOSEST)),
+
+	COMP_DIV_CLK(CLK_UART6, "uart6", 0,
+			C_MUX(uart_clk_mux_p, CMU_UART6CLK, 16, 1, 0),
+			C_GATE(CMU_DEVCLKEN1, 18, 0),
+			C_DIVIDER(CMU_UART6CLK, 0, 8, NULL, CLK_DIVIDER_ROUND_CLOSEST)),
+
+	COMP_FACTOR_CLK(CLK_VCE, "vce", 0,
+			C_MUX(vce_clk_mux_p, CMU_VCECLK, 4, 2, 0),
+			C_GATE(CMU_DEVCLKEN0, 26, 0),
+			C_FACTOR(CMU_VCECLK, 0, 3, bisp_factor_table, 0)),
+
+	COMP_FACTOR_CLK(CLK_VDE, "vde", 0,
+			C_MUX(hde_clk_mux_p, CMU_VDECLK, 4, 2, 0),
+			C_GATE(CMU_DEVCLKEN0, 25, 0),
+			C_FACTOR(CMU_VDECLK, 0, 3, bisp_factor_table, 0)),
+};
+
+static int s900_clk_probe(struct platform_device *pdev)
+{
+	struct owl_clk_provider *ctx;
+	struct device_node *np = pdev->dev.of_node;
+	struct resource *res;
+	void __iomem *base;
+	int i;
+
+	ctx = kzalloc(sizeof(struct owl_clk_provider) +
+			sizeof(*ctx->clk_data.hws) * CLK_NR_CLKS, GFP_KERNEL);
+	if (!ctx)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	for (i = 0; i < CLK_NR_CLKS; ++i)
+		ctx->clk_data.hws[i] = ERR_PTR(-ENOENT);
+
+	ctx->reg_base = base;
+	ctx->clk_data.num = CLK_NR_CLKS;
+	spin_lock_init(&ctx->lock);
+
+	/* register pll clocks */
+	owl_clk_register_pll(ctx, s900_pll_clks,
+			ARRAY_SIZE(s900_pll_clks));
+
+	/* register divider clocks */
+	owl_clk_register_divider(ctx, s900_div_clks,
+			ARRAY_SIZE(s900_div_clks));
+
+	/* register factor divider clocks */
+	owl_clk_register_factor(ctx, s900_factor_clks,
+			ARRAY_SIZE(s900_factor_clks));
+
+	/* register mux clocks */
+	owl_clk_register_mux(ctx, s900_mux_clks,
+			ARRAY_SIZE(s900_mux_clks));
+
+	/* register gate clocks */
+	owl_clk_register_gate(ctx, s900_gate_clks,
+			ARRAY_SIZE(s900_gate_clks));
+
+	/* register composite clocks */
+	owl_clk_register_composite(ctx, s900_composite_clks,
+			ARRAY_SIZE(s900_composite_clks));
+
+	return of_clk_add_hw_provider(np, of_clk_hw_onecell_get,
+				&ctx->clk_data);
+
+}
+
+static const struct of_device_id s900_clk_of_match[] = {
+	{ .compatible = "actions,s900-clock", },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, s900_clk_of_match);
+
+static struct platform_driver s900_clk_driver = {
+	.probe = s900_clk_probe,
+	.driver = {
+		.name = "s900-clk",
+		.of_match_table = s900_clk_of_match,
+	},
+};
+
+static int __init s900_clk_init(void)
+{
+	return platform_driver_register(&s900_clk_driver);
+}
+core_initcall(s900_clk_init);
diff --git a/drivers/clk/owl/clk.c b/drivers/clk/owl/clk.c
new file mode 100644
index 0000000..bf31b31
--- /dev/null
+++ b/drivers/clk/owl/clk.c
@@ -0,0 +1,318 @@
+/*
+ * Copyright (c) 2014 Actions Semi Inc.
+ * David Liu <liuwei at actions-semi.com>
+ *
+ * Copyright (c) 2017 Linaro Ltd.
+ * Author: Manivannan Sadhasivam <manivannan.sadhasivam at linaro.org>
+ *
+ * based on
+ *
+ * samsung/clk.h
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2013 Linaro Ltd.
+ * Author: Thomas Abraham <thomas.ab at owl.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License v2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that 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-provider.h>
+#include <linux/slab.h>
+#include "clk.h"
+
+void owl_clk_add_hw_data(struct owl_clk_provider *ctx, struct clk_hw *clk_hw,
+				unsigned int id)
+{
+	if (id)
+		ctx->clk_data.hws[id] = clk_hw;
+}
+
+/* register a list of fixed factor clocks */
+void owl_clk_register_fixed_factor(struct owl_clk_provider *ctx,
+			struct owl_fixed_factor_clock *clks, int nums)
+{
+	struct clk_hw *clk_hw;
+	int i;
+
+	for (i = 0; i < nums; i++) {
+		clk_hw = clk_hw_register_fixed_factor(NULL, clks[i].name,
+				clks[i].parent_name, clks[i].flags,
+				clks[i].mult, clks[i].div);
+		if (IS_ERR(clk_hw)) {
+			pr_err("%s: failed to register clock %s\n",
+			       __func__, clks[i].name);
+			continue;
+		}
+
+		owl_clk_add_hw_data(ctx, clk_hw, clks[i].id);
+	}
+}
+
+/* register a list of pll clocks */
+void owl_clk_register_pll(struct owl_clk_provider *ctx,
+			struct owl_pll_clock *clks, int nums)
+{
+	struct clk_hw *clk_hw;
+	int i;
+
+	for (i = 0; i < nums; i++) {
+		clk_hw = owl_pll_clk_register(clks[i].name, clks[i].parent_name,
+				clks[i].flags, ctx->reg_base + clks[i].offset,
+				clks[i].bfreq, clks[i].enable_bit,
+				clks[i].shift, clks[i].width,
+				clks[i].min_mul, clks[i].max_mul,
+				clks[i].pll_flags, clks[i].table,
+				&ctx->lock);
+		if (IS_ERR(clk_hw)) {
+			pr_err("%s: failed to register clock %s\n",
+				__func__, clks[i].name);
+			continue;
+
+		}
+
+		owl_clk_add_hw_data(ctx, clk_hw, clks[i].id);
+	}
+}
+
+/* register a list of divider clocks */
+void owl_clk_register_divider(struct owl_clk_provider *ctx,
+		struct owl_divider_clock *clks, int nums)
+{
+	struct clk_hw *clk_hw;
+	int i;
+
+	for (i = 0; i < nums; i++) {
+		clk_hw = clk_hw_register_divider_table(NULL, clks[i].name,
+				clks[i].parent_name, clks[i].flags,
+				ctx->reg_base + clks[i].offset, clks[i].shift,
+				clks[i].width, clks[i].div_flags,
+				clks[i].table, &ctx->lock);
+		if (IS_ERR(clk_hw)) {
+			pr_err("%s: failed to register clock %s\n",
+				__func__, clks[i].name);
+			continue;
+		}
+
+		owl_clk_add_hw_data(ctx, clk_hw, clks[i].id);
+	}
+}
+
+/* register a list of factor divider clocks */
+void owl_clk_register_factor(struct owl_clk_provider *ctx,
+		struct owl_factor_clock *clks, int nums)
+{
+	struct clk_hw *clk_hw;
+	int i;
+
+	for (i = 0; i < nums; i++) {
+		clk_hw = owl_factor_clk_register(NULL, clks[i].name,
+				clks[i].parent_name, clks[i].flags,
+				ctx->reg_base + clks[i].offset, clks[i].shift,
+				clks[i].width, clks[i].div_flags,
+				clks[i].table, &ctx->lock);
+		if (IS_ERR(clk_hw)) {
+			pr_err("%s: failed to register clock %s\n",
+				__func__, clks[i].name);
+			continue;
+		}
+
+		owl_clk_add_hw_data(ctx, clk_hw, clks[i].id);
+	}
+}
+
+/* register a list of mux clocks */
+void owl_clk_register_mux(struct owl_clk_provider *ctx,
+		struct owl_mux_clock *clks, int nums)
+{
+	struct clk_hw *clk_hw;
+	int i;
+
+	for (i = 0; i < nums; i++) {
+		clk_hw = clk_hw_register_mux(NULL, clks[i].name,
+				clks[i].parent_names, clks[i].num_parents,
+				clks[i].flags, ctx->reg_base + clks[i].offset,
+				clks[i].shift, clks[i].width,
+				clks[i].mux_flags, &ctx->lock);
+		if (IS_ERR(clk_hw)) {
+			pr_err("%s: failed to register clock %s\n",
+				__func__, clks[i].name);
+			continue;
+		}
+
+		owl_clk_add_hw_data(ctx, clk_hw, clks[i].id);
+	}
+}
+
+/* register a list of gate clocks */
+void owl_clk_register_gate(struct owl_clk_provider *ctx,
+		struct owl_gate_clock *clks, int nums)
+{
+	struct clk_hw *clk_hw;
+	int i;
+
+	for (i = 0; i < nums; i++) {
+		clk_hw = clk_hw_register_gate(NULL, clks[i].name,
+				clks[i].parent_name, clks[i].flags,
+				ctx->reg_base + clks[i].offset,
+				clks[i].bit_idx, clks[i].gate_flags,
+				&ctx->lock);
+		if (IS_ERR(clk_hw)) {
+			pr_err("%s: failed to register clock %s\n",
+			       __func__, clks[i].name);
+			continue;
+		}
+
+		owl_clk_add_hw_data(ctx, clk_hw, clks[i].id);
+	}
+}
+
+static struct clk_hw *_register_composite(struct owl_clk_provider *ctx,
+			struct owl_composite_clock *cclk)
+{
+	struct clk_hw *clk_hw;
+	struct owl_mux_clock *amux;
+	struct owl_gate_clock *agate;
+	union rate_clock *arate;
+	struct clk_gate *gate = NULL;
+	struct clk_mux *mux = NULL;
+	struct clk_fixed_factor *fixed_factor = NULL;
+	struct clk_divider *div = NULL;
+	struct owl_factor *factor = NULL;
+	struct clk_hw *mux_hw = NULL;
+	struct clk_hw *gate_hw = NULL;
+	struct clk_hw *rate_hw = NULL;
+	const struct clk_ops *rate_ops = NULL;
+	const char *clk_name = cclk->name;
+	const char **parent_names;
+	int i, num_parents;
+
+	amux = &cclk->mux;
+	agate = &cclk->gate;
+	arate = &cclk->rate;
+
+	parent_names = NULL;
+	num_parents = 0;
+
+	if (amux->id) {
+		num_parents = amux->num_parents;
+		if (num_parents > 0) {
+			parent_names = kzalloc((sizeof(char *) * num_parents),
+					GFP_KERNEL);
+			if (!parent_names)
+				return ERR_PTR(-ENOMEM);
+
+			for (i = 0; i < num_parents; i++)
+				parent_names[i] = kstrdup(amux->parent_names[i],
+						GFP_KERNEL);
+		}
+
+		mux = kzalloc(sizeof(*mux), GFP_KERNEL);
+		if (!mux)
+			return NULL;
+
+		/* set up gate properties */
+		mux->reg = ctx->reg_base + amux->offset;
+		mux->shift = amux->shift;
+		mux->mask = BIT(amux->width) - 1;
+		mux->flags = amux->mux_flags;
+		mux->lock = &ctx->lock;
+		mux_hw = &mux->hw;
+	}
+
+	if (arate->fixed_factor.id) {
+		switch (cclk->type) {
+		case OWL_COMPOSITE_TYPE_FIXED_FACTOR:
+			fixed_factor = kzalloc(sizeof(*fixed_factor),
+					GFP_KERNEL);
+			if (!fixed_factor)
+				return NULL;
+			fixed_factor->mult = arate->fixed_factor.mult;
+			fixed_factor->div = arate->fixed_factor.div;
+
+			rate_ops = &clk_fixed_factor_ops;
+			rate_hw = &fixed_factor->hw;
+			break;
+
+		case OWL_COMPOSITE_TYPE_DIVIDER:
+			div = kzalloc(sizeof(*div), GFP_KERNEL);
+			if (!div)
+				return NULL;
+			div->reg = ctx->reg_base + arate->div.offset;
+			div->shift = arate->div.shift;
+			div->width = arate->div.width;
+			div->flags = arate->div.div_flags;
+			div->table = arate->div.table;
+			div->lock = &ctx->lock;
+
+			rate_ops = &clk_divider_ops;
+			rate_hw = &div->hw;
+			break;
+
+		case OWL_COMPOSITE_TYPE_FACTOR:
+			factor = kzalloc(sizeof(*factor), GFP_KERNEL);
+			if (!factor)
+				return NULL;
+			factor->reg = ctx->reg_base + arate->factor.offset;
+			factor->shift = arate->factor.shift;
+			factor->width = arate->factor.width;
+			factor->flags = arate->factor.div_flags;
+			factor->table = arate->factor.table;
+			factor->lock = &ctx->lock;
+
+			rate_ops = &owl_factor_ops;
+			rate_hw = &factor->hw;
+			break;
+
+		default:
+			break;
+		}
+	}
+
+	if (agate->id) {
+		gate = kzalloc(sizeof(*gate), GFP_KERNEL);
+		if (!gate)
+			return ERR_PTR(-ENOMEM);
+
+		/* set up gate properties */
+		gate->reg = ctx->reg_base + agate->offset;
+		gate->bit_idx = agate->bit_idx;
+		gate->lock = &ctx->lock;
+		gate_hw = &gate->hw;
+	}
+
+	clk_hw = clk_hw_register_composite(NULL, clk_name,
+			parent_names, num_parents,
+			mux_hw, &clk_mux_ops,
+			rate_hw, rate_ops,
+			gate_hw, &clk_gate_ops, cclk->flags);
+
+	return clk_hw;
+}
+
+/* register a list of composite clocks */
+void owl_clk_register_composite(struct owl_clk_provider *ctx,
+		struct owl_composite_clock *clks, int nums)
+{
+	struct clk_hw *clk_hw;
+	int i;
+
+	for (i = 0; i < nums; i++) {
+		clk_hw = _register_composite(ctx, &clks[i]);
+		if (IS_ERR(clk_hw)) {
+			pr_err("%s: failed to register clock %s\n",
+				__func__, clks[i].name);
+			continue;
+		}
+
+		owl_clk_add_hw_data(ctx, clk_hw, clks[i].id);
+	}
+}
diff --git a/drivers/clk/owl/clk.h b/drivers/clk/owl/clk.h
new file mode 100644
index 0000000..f3d852b
--- /dev/null
+++ b/drivers/clk/owl/clk.h
@@ -0,0 +1,301 @@
+/*
+ * Copyright (c) 2014 Actions Semi Inc.
+ * David Liu <liuwei at actions-semi.com>
+ *
+ * Copyright (c) 2017 Linaro Ltd.
+ * Author: Manivannan Sadhasivam <manivannan.sadhasivam at linaro.org>
+ *
+ * based on
+ *
+ * samsung/clk.h
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2013 Linaro Ltd.
+ * Author: Thomas Abraham <thomas.ab at owl.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License v2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that 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 __OWL_CLK_H
+#define __OWL_CLK_H
+
+#include <linux/clk-provider.h>
+
+struct owl_clk_provider {
+	void __iomem		*reg_base;
+	struct clk_hw_onecell_data clk_data;
+	spinlock_t		lock;
+};
+
+struct owl_fixed_factor_clock {
+	unsigned int		id;
+	char			*name;
+	const char		*parent_name;
+	unsigned long		flags;
+	unsigned int		mult;
+	unsigned int		div;
+};
+
+/* last entry should have rate = 0 */
+struct clk_pll_table {
+	unsigned int		val;
+	unsigned long		rate;
+};
+
+struct owl_pll_clock {
+	unsigned int		id;
+	const char		*name;
+	const char		*parent_name;
+	unsigned long		flags;
+	unsigned long		offset;
+	unsigned long		bfreq;
+	u8			enable_bit;
+	u8			shift;
+	u8			width;
+	u8			min_mul;
+	u8			max_mul;
+	u8			pll_flags;
+	const struct clk_pll_table *table;
+};
+
+#define CLK_OWL_PLL_FIXED_FREQ	BIT(0)
+
+struct owl_divider_clock {
+	unsigned int		id;
+	const char		*name;
+	const char		*parent_name;
+	unsigned long		flags;
+	unsigned long		offset;
+	u8			shift;
+	u8			width;
+	u8			div_flags;
+	struct clk_div_table	*table;
+	const char		*alias;
+};
+
+struct clk_factor_table {
+	unsigned int		val;
+	unsigned int		mul;
+	unsigned int		div;
+};
+
+struct owl_factor_clock {
+	unsigned int		id;
+	const char		*name;
+	const char		*parent_name;
+	unsigned long		flags;
+	unsigned long		offset;
+	u8			shift;
+	u8			width;
+	u8			div_flags;
+	struct clk_factor_table	*table;
+	const char		*alias;
+};
+
+struct owl_factor {
+	struct clk_hw		hw;
+	void __iomem		*reg;
+	u8			shift;
+	u8			width;
+	u8			flags;
+	const struct clk_factor_table *table;
+	spinlock_t		*lock;
+};
+
+extern const struct clk_ops owl_factor_ops;
+
+struct owl_mux_clock {
+	unsigned int		id;
+	const char		*name;
+	const char		**parent_names;
+	u8			num_parents;
+	unsigned long		flags;
+	unsigned long		offset;
+	u8			shift;
+	u8			width;
+	u8			mux_flags;
+	const char		*alias;
+};
+
+struct owl_gate_clock {
+	unsigned int		id;
+	const char		*name;
+	const char		*parent_name;
+	unsigned long		flags;
+	unsigned long		offset;
+	u8			bit_idx;
+	u8			gate_flags;
+	const char		*alias;
+};
+
+union rate_clock {
+	struct owl_fixed_factor_clock	fixed_factor;
+	struct owl_divider_clock	div;
+	struct owl_factor_clock		factor;
+};
+
+struct owl_composite_clock {
+	unsigned int		id;
+	const char		*name;
+	unsigned int		type;
+	unsigned long		flags;
+
+	struct owl_mux_clock	mux;
+	struct owl_gate_clock	gate;
+	union rate_clock	rate;
+};
+
+#define OWL_COMPOSITE_TYPE_DIVIDER         1
+#define OWL_COMPOSITE_TYPE_FACTOR          2
+#define OWL_COMPOSITE_TYPE_FIXED_FACTOR    3
+#define OWL_COMPOSITE_TYPE_PASS            10
+
+#define COMP_FIXED_FACTOR_CLK(_id, _name, _flags, _mux, _gate, _fixed_factor) \
+	{								\
+		.id		= _id,					\
+		.name		= _name,				\
+		.type		= OWL_COMPOSITE_TYPE_FIXED_FACTOR,	\
+		.flags		= _flags,				\
+		.mux		= _mux,					\
+		.gate		= _gate,				\
+		.rate.fixed_factor = _fixed_factor,			\
+	}
+
+#define COMP_DIV_CLK(_id, _name, _flags, _mux, _gate, _div)		\
+	{								\
+		.id		= _id,					\
+		.name		= _name,				\
+		.type		= OWL_COMPOSITE_TYPE_DIVIDER,		\
+		.flags		= _flags,				\
+		.mux		= _mux,					\
+		.gate		= _gate,				\
+		.rate.div	= _div,					\
+	}
+
+#define COMP_FACTOR_CLK(_id, _name, _flags, _mux, _gate, _factor)	\
+	{								\
+		.id		= _id,					\
+		.name		= _name,				\
+		.type		= OWL_COMPOSITE_TYPE_FACTOR,		\
+		.flags		= _flags,				\
+		.mux		= _mux,					\
+		.gate		= _gate,				\
+		.rate.factor	= _factor,				\
+	}
+
+#define COMP_PASS_CLK(_id, _name, _flags, _mux, _gate)			\
+	{								\
+		.id		= _id,					\
+		.name		= _name,				\
+		.type		= OWL_COMPOSITE_TYPE_PASS,		\
+		.flags		= _flags,				\
+		.mux		= _mux,					\
+		.gate		= _gate,				\
+	}
+
+
+#define C_MUX(p, o, s, w, mf)						\
+	{								\
+		.id		= -1,					\
+		.parent_names	= p,					\
+		.num_parents	= ARRAY_SIZE(p),			\
+		.offset		= o,					\
+		.shift		= s,					\
+		.width		= w,					\
+		.mux_flags	= mf,					\
+	}
+
+/* fixed mux, only one parent */
+#define C_MUX_F(p, mf)							\
+	{								\
+		.id		= -1,					\
+		.parent_names	= p,					\
+		.num_parents	= 1,					\
+		.mux_flags = mf,					\
+	}
+
+#define C_GATE(o, b, gf)						\
+	{								\
+		.id		= -1,					\
+		.offset		= o,					\
+		.bit_idx	= b,					\
+		.gate_flags	= gf,					\
+	}
+
+#define C_NULL								\
+	{								\
+		.id		= 0,					\
+	}
+
+#define C_FIXED_FACTOR(m, d)						\
+	{								\
+		.id		= -1,					\
+		.mult		= m,					\
+		.div		= d,					\
+	}
+
+#define C_DIVIDER(o, s, w, t, df)					\
+	{								\
+		.id		= -1,					\
+		.offset		= o,					\
+		.shift		= s,					\
+		.width		= w,					\
+		.table		= t,					\
+		.div_flags	= df,					\
+	}
+
+#define C_FACTOR(o, s, w, t, df)					\
+	{								\
+		.id		= -1,					\
+		.offset		= o,					\
+		.shift		= s,					\
+		.width		= w,					\
+		.table		= t,					\
+		.div_flags	= df,					\
+	}
+
+extern void owl_clk_register_pll(struct owl_clk_provider *ctx,
+		struct owl_pll_clock *clks, int nums);
+
+extern void owl_clk_register_fixed_factor(
+		struct owl_clk_provider *ctx,
+		struct owl_fixed_factor_clock *clks,
+		int nums);
+
+extern void owl_clk_register_divider(struct owl_clk_provider *ctx,
+		struct owl_divider_clock *clks, int nums);
+
+extern void owl_clk_register_factor(struct owl_clk_provider *ctx,
+		struct owl_factor_clock *clks, int nums);
+
+extern void owl_clk_register_mux(struct owl_clk_provider *ctx,
+		struct owl_mux_clock *clks, int nums);
+
+extern void owl_clk_register_gate(struct owl_clk_provider *ctx,
+		struct owl_gate_clock *clks, int nums);
+
+extern void owl_clk_register_composite(struct owl_clk_provider *ctx,
+		struct owl_composite_clock *clks, int nums);
+
+extern struct clk_hw *owl_pll_clk_register(const char *name,
+		const char *parent_name, unsigned long flags,
+		void __iomem *reg, unsigned long bfreq, u8 enable_bit,
+		u8 shift, u8 width, u8 min_mul, u8 max_mul, u8 pll_flags,
+		const struct clk_pll_table *table, spinlock_t *lock);
+
+extern struct clk_hw *owl_factor_clk_register(struct device *dev,
+		const char *name, const char *parent_name,
+		unsigned long flags, void __iomem *reg, u8 shift,
+		u8 width, u8 clk_factor_flags,
+		const struct clk_factor_table *table, spinlock_t *lock);
+
+#endif /* __OWL_CLK_H */
diff --git a/include/dt-bindings/clock/actions,s900-clock.h b/include/dt-bindings/clock/actions,s900-clock.h
new file mode 100644
index 0000000..8056c27
--- /dev/null
+++ b/include/dt-bindings/clock/actions,s900-clock.h
@@ -0,0 +1,141 @@
+/*
+ * Device Tree binding constants for Actions S900 SoC clock controller
+ *
+ * Copyright (c) 2014 Actions Semi Inc.
+ * Copyright (c) 2017 Linaro Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License v2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that 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 __DT_BINDINGS_CLOCK_S900_H
+#define __DT_BINDINGS_CLOCK_S900_H
+
+#define CLK_NONE			0
+
+/* fixed rate clocks */
+#define CLK_LOSC			1
+#define CLK_HOSC			2
+
+/* pll clocks */
+#define CLK_CORE_PLL			3
+#define CLK_DEV_PLL			4
+#define CLK_DDR_PLL			5
+#define CLK_NAND_PLL			6
+#define CLK_DISPLAY_PLL			7
+#define CLK_DSI_PLL			8
+#define CLK_ASSIST_PLL			9
+#define CLK_AUDIO_PLL			10
+
+/* system clock */
+#define CLK_CPU				15
+#define CLK_DEV				16
+#define CLK_NOC				17
+#define CLK_NOC_CLK_MUX			18
+#define CLK_NOC_CLK_DIV			19
+#define CLK_AHB				20
+#define CLK_APB				21
+#define CLK_DMAC			22
+
+/* peripheral device clock */
+#define CLK_GPIO			23
+
+#define CLK_BISP			24
+#define CLK_CSI0			25
+#define CLK_CSI1			26
+
+#define CLK_DE				27
+#define CLK_DE1				28
+#define CLK_DE2				29
+#define CLK_DE3				30
+#define CLK_DSI				32
+
+#define CLK_GPU				33
+#define CLK_GPU_CORE			34
+#define CLK_GPU_MEM			35
+#define CLK_GPU_SYS			36
+
+#define CLK_HDE				37
+#define CLK_I2C0			38
+#define CLK_I2C1			39
+#define CLK_I2C2			40
+#define CLK_I2C3			41
+#define CLK_I2C4			42
+#define CLK_I2C5			43
+#define CLK_I2SRX			44
+#define CLK_I2STX			45
+#define CLK_IMX				46
+#define CLK_LCD				47
+#define CLK_NAND0			48
+#define CLK_NAND1			49
+#define CLK_PWM0			50
+#define CLK_PWM1			51
+#define CLK_PWM2			52
+#define CLK_PWM3			53
+#define CLK_PWM4			54
+#define CLK_PWM5			55
+#define CLK_SD0				56
+#define CLK_SD1				57
+#define CLK_SD2				58
+#define CLK_SD3				59
+#define CLK_SENSOR			60
+#define CLK_SPEED_SENSOR		61
+#define CLK_SPI0			62
+#define CLK_SPI1			63
+#define CLK_SPI2			64
+#define CLK_SPI3			65
+#define CLK_THERMAL_SENSOR		66
+#define CLK_UART0			67
+#define CLK_UART1			68
+#define CLK_UART2			69
+#define CLK_UART3			70
+#define CLK_UART4			71
+#define CLK_UART5			72
+#define CLK_UART6			73
+#define CLK_VCE				74
+#define CLK_VDE				75
+
+#define CLK_USB3_480MPLL0		76
+#define CLK_USB3_480MPHY0		77
+#define CLK_USB3_5GPHY			78
+#define CLK_USB3_CCE			79
+#define CLK_USB3_MAC			80
+
+#define CLK_TIMER			83
+
+#define CLK_HDMI_AUDIO			84
+
+#define CLK_24M				85
+
+#define CLK_EDP				86
+
+#define CLK_24M_EDP			87
+#define CLK_EDP_PLL			88
+#define CLK_EDP_LINK			89
+
+#define CLK_USB2H0_PLLEN		90
+#define CLK_USB2H0_PHY			91
+#define CLK_USB2H0_CCE			92
+#define CLK_USB2H1_PLLEN		93
+#define CLK_USB2H1_PHY			94
+#define CLK_USB2H1_CCE			95
+
+#define CLK_DDR0			96
+#define CLK_DDR1			97
+#define CLK_DMM				98
+
+#define CLK_ETH_MAC			99
+#define CLK_RMII_REF			100
+
+#define CLK_NR_CLKS			110
+
+#endif /* __DT_BINDINGS_CLOCK_S900_H */
-- 
2.7.4




More information about the linux-arm-kernel mailing list