[T106][ZXW-22]7520V3SCV2.01.01.02P42U09_VEC_V0.8_AP_VEC origin source commit
Change-Id: Ic6e05d89ecd62fc34f82b23dcf306c93764aec4b
diff --git a/ap/os/linux/linux-3.4.x/drivers/clk/Kconfig b/ap/os/linux/linux-3.4.x/drivers/clk/Kconfig
new file mode 100644
index 0000000..165e1fe
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/clk/Kconfig
@@ -0,0 +1,47 @@
+
+config CLKDEV_LOOKUP
+ bool
+ select HAVE_CLK
+
+config HAVE_CLK_PREPARE
+ bool
+
+config HAVE_MACH_CLKDEV
+ bool
+
+config COMMON_CLK
+ bool
+ select HAVE_CLK_PREPARE
+ ---help---
+ The common clock framework is a single definition of struct
+ clk, useful across many platforms, as well as an
+ implementation of the clock API in include/linux/clk.h.
+ Architectures utilizing the common struct clk should select
+ this option.
+
+menu "Common Clock Framework"
+ depends on COMMON_CLK
+
+config COMMON_CLK_DISABLE_UNUSED
+ bool "Disabled unused clocks at boot"
+ depends on COMMON_CLK
+ ---help---
+ Traverses the entire clock tree and disables any clocks that are
+ enabled in hardware but have not been enabled by any device drivers.
+ This saves power and keeps the software model of the clock in line
+ with reality.
+
+ If in doubt, say "N".
+
+config COMMON_CLK_DEBUG
+ bool "DebugFS representation of clock tree"
+ depends on COMMON_CLK
+ select DEBUG_FS
+ ---help---
+ Creates a directory hierchy in debugfs for visualizing the clk
+ tree structure. Each directory contains read-only members
+ that export information specific to that clk node: clk_rate,
+ clk_flags, clk_prepare_count, clk_enable_count &
+ clk_notifier_count.
+
+endmenu
diff --git a/ap/os/linux/linux-3.4.x/drivers/clk/Makefile b/ap/os/linux/linux-3.4.x/drivers/clk/Makefile
new file mode 100644
index 0000000..df513c4
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/clk/Makefile
@@ -0,0 +1,5 @@
+
+obj-$(CONFIG_CLKDEV_LOOKUP) += clkdev.o
+obj-$(CONFIG_COMMON_CLK) += clk.o clk-fixed-rate.o clk-gate.o \
+ clk-mux.o clk-divider.o
+obj-$(CONFIG_PLAT_ZTE) += zte/
diff --git a/ap/os/linux/linux-3.4.x/drivers/clk/clk-divider.c b/ap/os/linux/linux-3.4.x/drivers/clk/clk-divider.c
new file mode 100644
index 0000000..d5ac6a7
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/clk/clk-divider.c
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2011 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
+ * Copyright (C) 2011 Richard Zhao, Linaro <richard.zhao@linaro.org>
+ * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Adjustable divider clock implementation
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/string.h>
+
+/*
+ * DOC: basic adjustable divider clock that cannot gate
+ *
+ * Traits of this clock:
+ * prepare - clk_prepare only ensures that parents are prepared
+ * enable - clk_enable only ensures that parents are enabled
+ * rate - rate is adjustable. clk->rate = parent->rate / divisor
+ * parent - fixed parent. No clk_set_parent support
+ */
+
+#define to_clk_divider(_hw) container_of(_hw, struct clk_divider, hw)
+
+#define div_mask(d) ((1 << (d->width)) - 1)
+
+static unsigned long clk_divider_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_divider *divider = to_clk_divider(hw);
+ unsigned int div;
+
+ div = readl(divider->reg) >> divider->shift;
+ div &= div_mask(divider);
+
+ if (!(divider->flags & CLK_DIVIDER_ONE_BASED))
+ div++;
+
+ return parent_rate / div;
+}
+EXPORT_SYMBOL_GPL(clk_divider_recalc_rate);
+
+/*
+ * The reverse of DIV_ROUND_UP: The maximum number which
+ * divided by m is r
+ */
+#define MULT_ROUND_UP(r, m) ((r) * (m) + (m) - 1)
+
+static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
+ unsigned long *best_parent_rate)
+{
+ struct clk_divider *divider = to_clk_divider(hw);
+ int i, bestdiv = 0;
+ unsigned long parent_rate, best = 0, now, maxdiv;
+
+ if (!rate)
+ rate = 1;
+
+ maxdiv = (1 << divider->width);
+
+ if (divider->flags & CLK_DIVIDER_ONE_BASED)
+ maxdiv--;
+
+ if (!best_parent_rate) {
+ parent_rate = __clk_get_rate(__clk_get_parent(hw->clk));
+ bestdiv = DIV_ROUND_UP(parent_rate, rate);
+ bestdiv = bestdiv == 0 ? 1 : bestdiv;
+ bestdiv = bestdiv > maxdiv ? maxdiv : bestdiv;
+ return bestdiv;
+ }
+
+ /*
+ * The maximum divider we can use without overflowing
+ * unsigned long in rate * i below
+ */
+ maxdiv = min(ULONG_MAX / rate, maxdiv);
+
+ for (i = 1; i <= maxdiv; i++) {
+ parent_rate = __clk_round_rate(__clk_get_parent(hw->clk),
+ MULT_ROUND_UP(rate, i));
+ now = parent_rate / i;
+ if (now <= rate && now > best) {
+ bestdiv = i;
+ best = now;
+ *best_parent_rate = parent_rate;
+ }
+ }
+
+ if (!bestdiv) {
+ bestdiv = (1 << divider->width);
+ if (divider->flags & CLK_DIVIDER_ONE_BASED)
+ bestdiv--;
+ *best_parent_rate = __clk_round_rate(__clk_get_parent(hw->clk), 1);
+ }
+
+ return bestdiv;
+}
+
+static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ int div;
+ div = clk_divider_bestdiv(hw, rate, prate);
+
+ if (prate)
+ return *prate / div;
+ else {
+ unsigned long r;
+ r = __clk_get_rate(__clk_get_parent(hw->clk));
+ return r / div;
+ }
+}
+EXPORT_SYMBOL_GPL(clk_divider_round_rate);
+
+static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate)
+{
+ struct clk_divider *divider = to_clk_divider(hw);
+ unsigned int div;
+ unsigned long flags = 0;
+ u32 val;
+
+ div = __clk_get_rate(__clk_get_parent(hw->clk)) / rate;
+
+ if (!(divider->flags & CLK_DIVIDER_ONE_BASED))
+ div--;
+
+ if (div > div_mask(divider))
+ div = div_mask(divider);
+
+ if (divider->lock)
+ spin_lock_irqsave(divider->lock, flags);
+
+ val = readl(divider->reg);
+ val &= ~(div_mask(divider) << divider->shift);
+ val |= div << divider->shift;
+ writel(val, divider->reg);
+
+ if (divider->lock)
+ spin_unlock_irqrestore(divider->lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(clk_divider_set_rate);
+
+struct clk_ops clk_divider_ops = {
+ .recalc_rate = clk_divider_recalc_rate,
+ .round_rate = clk_divider_round_rate,
+ .set_rate = clk_divider_set_rate,
+};
+EXPORT_SYMBOL_GPL(clk_divider_ops);
+
+struct clk *clk_register_divider(struct device *dev, const char *name,
+ const char *parent_name, unsigned long flags,
+ void __iomem *reg, u8 shift, u8 width,
+ u8 clk_divider_flags, spinlock_t *lock)
+{
+ struct clk_divider *div;
+ struct clk *clk;
+
+ div = kzalloc(sizeof(struct clk_divider), GFP_KERNEL);
+
+ if (!div) {
+ pr_err("%s: could not allocate divider clk\n", __func__);
+ return NULL;
+ }
+
+ /* struct clk_divider assignments */
+ div->reg = reg;
+ div->shift = shift;
+ div->width = width;
+ div->flags = clk_divider_flags;
+ div->lock = lock;
+
+ if (parent_name) {
+ div->parent[0] = kstrdup(parent_name, GFP_KERNEL);
+ if (!div->parent[0])
+ goto out;
+ }
+
+ clk = clk_register(dev, name,
+ &clk_divider_ops, &div->hw,
+ div->parent,
+ (parent_name ? 1 : 0),
+ flags);
+ if (clk)
+ return clk;
+
+out:
+ kfree(div->parent[0]);
+ kfree(div);
+
+ return NULL;
+}
diff --git a/ap/os/linux/linux-3.4.x/drivers/clk/clk-fixed-rate.c b/ap/os/linux/linux-3.4.x/drivers/clk/clk-fixed-rate.c
new file mode 100644
index 0000000..90c79fb
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/clk/clk-fixed-rate.c
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2010-2011 Canonical Ltd <jeremy.kerr@canonical.com>
+ * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Fixed rate clock implementation
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+
+/*
+ * DOC: basic fixed-rate clock that cannot gate
+ *
+ * Traits of this clock:
+ * prepare - clk_(un)prepare only ensures parents are prepared
+ * enable - clk_enable only ensures parents are enabled
+ * rate - rate is always a fixed value. No clk_set_rate support
+ * parent - fixed parent. No clk_set_parent support
+ */
+
+#define to_clk_fixed_rate(_hw) container_of(_hw, struct clk_fixed_rate, hw)
+
+static unsigned long clk_fixed_rate_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ return to_clk_fixed_rate(hw)->fixed_rate;
+}
+EXPORT_SYMBOL_GPL(clk_fixed_rate_recalc_rate);
+
+struct clk_ops clk_fixed_rate_ops = {
+ .recalc_rate = clk_fixed_rate_recalc_rate,
+};
+EXPORT_SYMBOL_GPL(clk_fixed_rate_ops);
+
+struct clk *clk_register_fixed_rate(struct device *dev, const char *name,
+ const char *parent_name, unsigned long flags,
+ unsigned long fixed_rate)
+{
+ struct clk_fixed_rate *fixed;
+ char **parent_names = NULL;
+ u8 len;
+
+ fixed = kzalloc(sizeof(struct clk_fixed_rate), GFP_KERNEL);
+
+ if (!fixed) {
+ pr_err("%s: could not allocate fixed clk\n", __func__);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ /* struct clk_fixed_rate assignments */
+ fixed->fixed_rate = fixed_rate;
+
+ if (parent_name) {
+ parent_names = kmalloc(sizeof(char *), GFP_KERNEL);
+
+ if (! parent_names)
+ goto out;
+
+ len = sizeof(char) * strlen(parent_name);
+
+ parent_names[0] = kmalloc(len, GFP_KERNEL);
+
+ if (!parent_names[0])
+ goto out;
+
+ strncpy(parent_names[0], parent_name, len);
+ }
+
+out:
+ return clk_register(dev, name,
+ &clk_fixed_rate_ops, &fixed->hw,
+ parent_names,
+ (parent_name ? 1 : 0),
+ flags);
+}
diff --git a/ap/os/linux/linux-3.4.x/drivers/clk/clk-gate.c b/ap/os/linux/linux-3.4.x/drivers/clk/clk-gate.c
new file mode 100644
index 0000000..b5902e2
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/clk/clk-gate.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2010-2011 Canonical Ltd <jeremy.kerr@canonical.com>
+ * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Gated clock implementation
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/string.h>
+
+/**
+ * DOC: basic gatable clock which can gate and ungate it's ouput
+ *
+ * Traits of this clock:
+ * prepare - clk_(un)prepare only ensures parent is (un)prepared
+ * enable - clk_enable and clk_disable are functional & control gating
+ * rate - inherits rate from parent. No clk_set_rate support
+ * parent - fixed parent. No clk_set_parent support
+ */
+
+#define to_clk_gate(_hw) container_of(_hw, struct clk_gate, hw)
+
+static void clk_gate_set_bit(struct clk_gate *gate)
+{
+ u32 reg;
+ unsigned long flags = 0;
+
+ if (gate->lock)
+ spin_lock_irqsave(gate->lock, flags);
+
+ reg = readl(gate->reg);
+ reg |= BIT(gate->bit_idx);
+ writel(reg, gate->reg);
+
+ if (gate->lock)
+ spin_unlock_irqrestore(gate->lock, flags);
+}
+
+static void clk_gate_clear_bit(struct clk_gate *gate)
+{
+ u32 reg;
+ unsigned long flags = 0;
+
+ if (gate->lock)
+ spin_lock_irqsave(gate->lock, flags);
+
+ reg = readl(gate->reg);
+ reg &= ~BIT(gate->bit_idx);
+ writel(reg, gate->reg);
+
+ if (gate->lock)
+ spin_unlock_irqrestore(gate->lock, flags);
+}
+
+static int clk_gate_enable(struct clk_hw *hw)
+{
+ struct clk_gate *gate = to_clk_gate(hw);
+
+ if (gate->flags & CLK_GATE_SET_TO_DISABLE)
+ clk_gate_clear_bit(gate);
+ else
+ clk_gate_set_bit(gate);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(clk_gate_enable);
+
+static void clk_gate_disable(struct clk_hw *hw)
+{
+ struct clk_gate *gate = to_clk_gate(hw);
+
+ if (gate->flags & CLK_GATE_SET_TO_DISABLE)
+ clk_gate_set_bit(gate);
+ else
+ clk_gate_clear_bit(gate);
+}
+EXPORT_SYMBOL_GPL(clk_gate_disable);
+
+static int clk_gate_is_enabled(struct clk_hw *hw)
+{
+ u32 reg;
+ struct clk_gate *gate = to_clk_gate(hw);
+
+ reg = readl(gate->reg);
+
+ /* if a set bit disables this clk, flip it before masking */
+ if (gate->flags & CLK_GATE_SET_TO_DISABLE)
+ reg ^= BIT(gate->bit_idx);
+
+ reg &= BIT(gate->bit_idx);
+
+ return reg ? 1 : 0;
+}
+EXPORT_SYMBOL_GPL(clk_gate_is_enabled);
+
+struct clk_ops clk_gate_ops = {
+ .enable = clk_gate_enable,
+ .disable = clk_gate_disable,
+ .is_enabled = clk_gate_is_enabled,
+};
+EXPORT_SYMBOL_GPL(clk_gate_ops);
+
+struct clk *clk_register_gate(struct device *dev, const char *name,
+ const char *parent_name, unsigned long flags,
+ void __iomem *reg, u8 bit_idx,
+ u8 clk_gate_flags, spinlock_t *lock)
+{
+ struct clk_gate *gate;
+ struct clk *clk;
+
+ gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL);
+
+ if (!gate) {
+ pr_err("%s: could not allocate gated clk\n", __func__);
+ return NULL;
+ }
+
+ /* struct clk_gate assignments */
+ gate->reg = reg;
+ gate->bit_idx = bit_idx;
+ gate->flags = clk_gate_flags;
+ gate->lock = lock;
+
+ if (parent_name) {
+ gate->parent[0] = kstrdup(parent_name, GFP_KERNEL);
+ if (!gate->parent[0])
+ goto out;
+ }
+
+ clk = clk_register(dev, name,
+ &clk_gate_ops, &gate->hw,
+ gate->parent,
+ (parent_name ? 1 : 0),
+ flags);
+ if (clk)
+ return clk;
+out:
+ kfree(gate->parent[0]);
+ kfree(gate);
+
+ return NULL;
+}
diff --git a/ap/os/linux/linux-3.4.x/drivers/clk/clk-mux.c b/ap/os/linux/linux-3.4.x/drivers/clk/clk-mux.c
new file mode 100644
index 0000000..c71ad1f
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/clk/clk-mux.c
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2011 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
+ * Copyright (C) 2011 Richard Zhao, Linaro <richard.zhao@linaro.org>
+ * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Simple multiplexer clock implementation
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+
+/*
+ * DOC: basic adjustable multiplexer clock that cannot gate
+ *
+ * Traits of this clock:
+ * prepare - clk_prepare only ensures that parents are prepared
+ * enable - clk_enable only ensures that parents are enabled
+ * rate - rate is only affected by parent switching. No clk_set_rate support
+ * parent - parent is adjustable through clk_set_parent
+ */
+
+#define to_clk_mux(_hw) container_of(_hw, struct clk_mux, hw)
+
+static u8 clk_mux_get_parent(struct clk_hw *hw)
+{
+ struct clk_mux *mux = to_clk_mux(hw);
+ u32 val;
+
+ /*
+ * FIXME need a mux-specific flag to determine if val is bitwise or numeric
+ * e.g. sys_clkin_ck's clksel field is 3 bits wide, but ranges from 0x1
+ * to 0x7 (index starts at one)
+ * OTOH, pmd_trace_clk_mux_ck uses a separate bit for each clock, so
+ * val = 0x4 really means "bit 2, index starts at bit 0"
+ */
+ val = readl(mux->reg) >> mux->shift;
+ val &= (1 << mux->width) - 1;
+
+ if (val && (mux->flags & CLK_MUX_INDEX_BIT))
+ val = ffs(val) - 1;
+
+ if (val && (mux->flags & CLK_MUX_INDEX_ONE))
+ val--;
+
+ if (val >= __clk_get_num_parents(hw->clk))
+ return -EINVAL;
+
+ return val;
+}
+EXPORT_SYMBOL_GPL(clk_mux_get_parent);
+
+static int clk_mux_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct clk_mux *mux = to_clk_mux(hw);
+ u32 val;
+ unsigned long flags = 0;
+
+ if (mux->flags & CLK_MUX_INDEX_BIT)
+ index = (1 << ffs(index));
+
+ if (mux->flags & CLK_MUX_INDEX_ONE)
+ index++;
+
+ if (mux->lock)
+ spin_lock_irqsave(mux->lock, flags);
+
+ val = readl(mux->reg);
+ val &= ~(((1 << mux->width) - 1) << mux->shift);
+ val |= index << mux->shift;
+ writel(val, mux->reg);
+
+ if (mux->lock)
+ spin_unlock_irqrestore(mux->lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(clk_mux_set_parent);
+
+struct clk_ops clk_mux_ops = {
+ .get_parent = clk_mux_get_parent,
+ .set_parent = clk_mux_set_parent,
+};
+EXPORT_SYMBOL_GPL(clk_mux_ops);
+
+struct clk *clk_register_mux(struct device *dev, const char *name,
+ char **parent_names, u8 num_parents, unsigned long flags,
+ void __iomem *reg, u8 shift, u8 width,
+ u8 clk_mux_flags, spinlock_t *lock)
+{
+ struct clk_mux *mux;
+
+ mux = kmalloc(sizeof(struct clk_mux), GFP_KERNEL);
+
+ if (!mux) {
+ pr_err("%s: could not allocate mux clk\n", __func__);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ /* struct clk_mux assignments */
+ mux->reg = reg;
+ mux->shift = shift;
+ mux->width = width;
+ mux->flags = clk_mux_flags;
+ mux->lock = lock;
+
+ return clk_register(dev, name, &clk_mux_ops, &mux->hw,
+ parent_names, num_parents, flags);
+}
diff --git a/ap/os/linux/linux-3.4.x/drivers/clk/clk.c b/ap/os/linux/linux-3.4.x/drivers/clk/clk.c
new file mode 100644
index 0000000..4c63665
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/clk/clk.c
@@ -0,0 +1,1468 @@
+/*
+ * Copyright (C) 2010-2011 Canonical Ltd <jeremy.kerr@canonical.com>
+ * Copyright (C) 2011-2012 Linaro Ltd <mturquette@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Standard functionality for the common clock API. See Documentation/clk.txt
+ */
+
+#include <linux/clk-private.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/err.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+
+static DEFINE_SPINLOCK(enable_lock);
+static DEFINE_MUTEX(prepare_lock);
+
+static HLIST_HEAD(clk_root_list);
+static HLIST_HEAD(clk_orphan_list);
+static LIST_HEAD(clk_notifier_list);
+
+/*** debugfs support ***/
+
+#ifdef CONFIG_COMMON_CLK_DEBUG
+#include <linux/debugfs.h>
+
+static struct dentry *rootdir;
+static struct dentry *orphandir;
+static int inited = 0;
+
+/* caller must hold prepare_lock */
+static int clk_debug_create_one(struct clk *clk, struct dentry *pdentry)
+{
+ struct dentry *d;
+ int ret = -ENOMEM;
+
+ if (!clk || !pdentry) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ d = debugfs_create_dir(clk->name, pdentry);
+ if (!d)
+ goto out;
+
+ clk->dentry = d;
+
+ d = debugfs_create_u32("clk_rate", S_IRUGO, clk->dentry,
+ (u32 *)&clk->rate);
+ if (!d)
+ goto err_out;
+
+ d = debugfs_create_x32("clk_flags", S_IRUGO, clk->dentry,
+ (u32 *)&clk->flags);
+ if (!d)
+ goto err_out;
+
+ d = debugfs_create_u32("clk_prepare_count", S_IRUGO, clk->dentry,
+ (u32 *)&clk->prepare_count);
+ if (!d)
+ goto err_out;
+
+ d = debugfs_create_u32("clk_enable_count", S_IRUGO, clk->dentry,
+ (u32 *)&clk->enable_count);
+ if (!d)
+ goto err_out;
+
+ d = debugfs_create_u32("clk_notifier_count", S_IRUGO, clk->dentry,
+ (u32 *)&clk->notifier_count);
+ if (!d)
+ goto err_out;
+
+ ret = 0;
+ goto out;
+
+err_out:
+ debugfs_remove(clk->dentry);
+out:
+ return ret;
+}
+
+/* caller must hold prepare_lock */
+static int clk_debug_create_subtree(struct clk *clk, struct dentry *pdentry)
+{
+ struct clk *child;
+ struct hlist_node *tmp;
+ int ret = -EINVAL;;
+
+ if (!clk || !pdentry)
+ goto out;
+
+ ret = clk_debug_create_one(clk, pdentry);
+
+ if (ret)
+ goto out;
+
+ hlist_for_each_entry(child, tmp, &clk->children, child_node)
+ clk_debug_create_subtree(child, clk->dentry);
+
+ ret = 0;
+out:
+ return ret;
+}
+
+/**
+ * clk_debug_register - add a clk node to the debugfs clk tree
+ * @clk: the clk being added to the debugfs clk tree
+ *
+ * Dynamically adds a clk to the debugfs clk tree if debugfs has been
+ * initialized. Otherwise it bails out early since the debugfs clk tree
+ * will be created lazily by clk_debug_init as part of a late_initcall.
+ *
+ * Caller must hold prepare_lock. Only clk_init calls this function (so
+ * far) so this is taken care.
+ */
+static int clk_debug_register(struct clk *clk)
+{
+ struct clk *parent;
+ struct dentry *pdentry;
+ int ret = 0;
+
+ if (!inited)
+ goto out;
+
+ parent = clk->parent;
+
+ /*
+ * Check to see if a clk is a root clk. Also check that it is
+ * safe to add this clk to debugfs
+ */
+ if (!parent)
+ if (clk->flags & CLK_IS_ROOT)
+ pdentry = rootdir;
+ else
+ pdentry = orphandir;
+ else
+ if (parent->dentry)
+ pdentry = parent->dentry;
+ else
+ goto out;
+
+ ret = clk_debug_create_subtree(clk, pdentry);
+
+out:
+ return ret;
+}
+
+/**
+ * clk_debug_init - lazily create the debugfs clk tree visualization
+ *
+ * clks are often initialized very early during boot before memory can
+ * be dynamically allocated and well before debugfs is setup.
+ * clk_debug_init walks the clk tree hierarchy while holding
+ * prepare_lock and creates the topology as part of a late_initcall,
+ * thus insuring that clks initialized very early will still be
+ * represented in the debugfs clk tree. This function should only be
+ * called once at boot-time, and all other clks added dynamically will
+ * be done so with clk_debug_register.
+ */
+static int __init clk_debug_init(void)
+{
+ struct clk *clk;
+ struct hlist_node *tmp;
+
+ rootdir = debugfs_create_dir("clk", NULL);
+
+ if (!rootdir)
+ return -ENOMEM;
+
+ orphandir = debugfs_create_dir("orphans", rootdir);
+
+ if (!orphandir)
+ return -ENOMEM;
+
+ mutex_lock(&prepare_lock);
+
+ hlist_for_each_entry(clk, tmp, &clk_root_list, child_node)
+ clk_debug_create_subtree(clk, rootdir);
+
+ hlist_for_each_entry(clk, tmp, &clk_orphan_list, child_node)
+ clk_debug_create_subtree(clk, orphandir);
+
+ inited = 1;
+
+ mutex_unlock(&prepare_lock);
+
+ return 0;
+}
+late_initcall(clk_debug_init);
+#else
+static inline int clk_debug_register(struct clk *clk) { return 0; }
+#endif /* CONFIG_COMMON_CLK_DEBUG */
+
+#ifdef CONFIG_COMMON_CLK_DISABLE_UNUSED
+/* caller must hold prepare_lock */
+static void clk_disable_unused_subtree(struct clk *clk)
+{
+ struct clk *child;
+ struct hlist_node *tmp;
+ unsigned long flags;
+
+ if (!clk)
+ goto out;
+
+ hlist_for_each_entry(child, tmp, &clk->children, child_node)
+ clk_disable_unused_subtree(child);
+
+ spin_lock_irqsave(&enable_lock, flags);
+
+ if (clk->enable_count)
+ goto unlock_out;
+
+ if (clk->flags & CLK_IGNORE_UNUSED)
+ goto unlock_out;
+
+ if (__clk_is_enabled(clk) && clk->ops->disable)
+ clk->ops->disable(clk->hw);
+
+unlock_out:
+ spin_unlock_irqrestore(&enable_lock, flags);
+
+out:
+ return;
+}
+
+static int clk_disable_unused(void)
+{
+ struct clk *clk;
+ struct hlist_node *tmp;
+
+ mutex_lock(&prepare_lock);
+
+ hlist_for_each_entry(clk, tmp, &clk_root_list, child_node)
+ clk_disable_unused_subtree(clk);
+
+ hlist_for_each_entry(clk, tmp, &clk_orphan_list, child_node)
+ clk_disable_unused_subtree(clk);
+
+ mutex_unlock(&prepare_lock);
+
+ return 0;
+}
+late_initcall(clk_disable_unused);
+#else
+static inline int clk_disable_unused(struct clk *clk) { return 0; }
+#endif /* CONFIG_COMMON_CLK_DISABLE_UNUSED */
+
+/*** helper functions ***/
+
+inline const char *__clk_get_name(struct clk *clk)
+{
+ return !clk ? NULL : clk->name;
+}
+
+inline struct clk_hw *__clk_get_hw(struct clk *clk)
+{
+ return !clk ? NULL : clk->hw;
+}
+
+inline u8 __clk_get_num_parents(struct clk *clk)
+{
+ return !clk ? -EINVAL : clk->num_parents;
+}
+
+inline struct clk *__clk_get_parent(struct clk *clk)
+{
+ return !clk ? NULL : clk->parent;
+}
+
+inline int __clk_get_enable_count(struct clk *clk)
+{
+ return !clk ? -EINVAL : clk->enable_count;
+}
+
+inline int __clk_get_prepare_count(struct clk *clk)
+{
+ return !clk ? -EINVAL : clk->prepare_count;
+}
+
+unsigned long __clk_get_rate(struct clk *clk)
+{
+ unsigned long ret;
+
+ if (!clk) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = clk->rate;
+
+ if (clk->flags & CLK_IS_ROOT)
+ goto out;
+
+ if (!clk->parent)
+ ret = -ENODEV;
+
+out:
+ return ret;
+}
+
+inline unsigned long __clk_get_flags(struct clk *clk)
+{
+ return !clk ? -EINVAL : clk->flags;
+}
+
+int __clk_is_enabled(struct clk *clk)
+{
+ int ret;
+
+ if (!clk)
+ return -EINVAL;
+
+ /*
+ * .is_enabled is only mandatory for clocks that gate
+ * fall back to software usage counter if .is_enabled is missing
+ */
+ if (!clk->ops->is_enabled) {
+ ret = clk->enable_count ? 1 : 0;
+ goto out;
+ }
+
+ ret = clk->ops->is_enabled(clk->hw);
+out:
+ return ret;
+}
+
+static struct clk *__clk_lookup_subtree(const char *name, struct clk *clk)
+{
+ struct clk *child;
+ struct clk *ret;
+ struct hlist_node *tmp;
+
+ if (!strcmp(clk->name, name))
+ return clk;
+
+ hlist_for_each_entry(child, tmp, &clk->children, child_node) {
+ ret = __clk_lookup_subtree(name, child);
+ if (ret)
+ return ret;
+ }
+
+ return NULL;
+}
+
+struct clk *__clk_lookup(const char *name)
+{
+ struct clk *root_clk;
+ struct clk *ret;
+ struct hlist_node *tmp;
+
+ if (!name)
+ return NULL;
+
+ /* search the 'proper' clk tree first */
+ hlist_for_each_entry(root_clk, tmp, &clk_root_list, child_node) {
+ ret = __clk_lookup_subtree(name, root_clk);
+ if (ret)
+ return ret;
+ }
+
+ /* if not found, then search the orphan tree */
+ hlist_for_each_entry(root_clk, tmp, &clk_orphan_list, child_node) {
+ ret = __clk_lookup_subtree(name, root_clk);
+ if (ret)
+ return ret;
+ }
+
+ return NULL;
+}
+
+/*** clk api ***/
+
+void __clk_unprepare(struct clk *clk)
+{
+ if (!clk)
+ return;
+
+ if (WARN_ON(clk->prepare_count == 0))
+ return;
+
+ if (--clk->prepare_count > 0)
+ return;
+
+ WARN_ON(clk->enable_count > 0);
+
+ if (clk->ops->unprepare)
+ clk->ops->unprepare(clk->hw);
+
+ __clk_unprepare(clk->parent);
+}
+
+/**
+ * clk_unprepare - undo preparation of a clock source
+ * @clk: the clk being unprepare
+ *
+ * clk_unprepare may sleep, which differentiates it from clk_disable. In a
+ * simple case, clk_unprepare can be used instead of clk_disable to gate a clk
+ * if the operation may sleep. One example is a clk which is accessed over
+ * I2c. In the complex case a clk gate operation may require a fast and a slow
+ * part. It is this reason that clk_unprepare and clk_disable are not mutually
+ * exclusive. In fact clk_disable must be called before clk_unprepare.
+ */
+void clk_unprepare(struct clk *clk)
+{
+ mutex_lock(&prepare_lock);
+ __clk_unprepare(clk);
+ mutex_unlock(&prepare_lock);
+}
+EXPORT_SYMBOL_GPL(clk_unprepare);
+
+int __clk_prepare(struct clk *clk)
+{
+ int ret = 0;
+
+ if (!clk)
+ return 0;
+
+ if (clk->prepare_count == 0) {
+ ret = __clk_prepare(clk->parent);
+ if (ret)
+ return ret;
+
+ if (clk->ops->prepare) {
+ ret = clk->ops->prepare(clk->hw);
+ if (ret) {
+ __clk_unprepare(clk->parent);
+ return ret;
+ }
+ }
+ }
+
+ clk->prepare_count++;
+
+ return 0;
+}
+
+/**
+ * clk_prepare - prepare a clock source
+ * @clk: the clk being prepared
+ *
+ * clk_prepare may sleep, which differentiates it from clk_enable. In a simple
+ * case, clk_prepare can be used instead of clk_enable to ungate a clk if the
+ * operation may sleep. One example is a clk which is accessed over I2c. In
+ * the complex case a clk ungate operation may require a fast and a slow part.
+ * It is this reason that clk_prepare and clk_enable are not mutually
+ * exclusive. In fact clk_prepare must be called before clk_enable.
+ * Returns 0 on success, -EERROR otherwise.
+ */
+int clk_prepare(struct clk *clk)
+{
+ int ret;
+
+ mutex_lock(&prepare_lock);
+ ret = __clk_prepare(clk);
+ mutex_unlock(&prepare_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(clk_prepare);
+
+static void __clk_disable(struct clk *clk)
+{
+ if (!clk)
+ return;
+
+ if (WARN_ON(clk->enable_count == 0))
+ return;
+
+ if (--clk->enable_count > 0)
+ return;
+
+ if (clk->ops->disable)
+ clk->ops->disable(clk->hw);
+
+ __clk_disable(clk->parent);
+}
+
+/**
+ * clk_disable - gate a clock
+ * @clk: the clk being gated
+ *
+ * clk_disable must not sleep, which differentiates it from clk_unprepare. In
+ * a simple case, clk_disable can be used instead of clk_unprepare to gate a
+ * clk if the operation is fast and will never sleep. One example is a
+ * SoC-internal clk which is controlled via simple register writes. In the
+ * complex case a clk gate operation may require a fast and a slow part. It is
+ * this reason that clk_unprepare and clk_disable are not mutually exclusive.
+ * In fact clk_disable must be called before clk_unprepare.
+ */
+void clk_disable(struct clk *clk)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&enable_lock, flags);
+ __clk_disable(clk);
+ spin_unlock_irqrestore(&enable_lock, flags);
+}
+EXPORT_SYMBOL_GPL(clk_disable);
+
+static int __clk_enable(struct clk *clk)
+{
+ int ret = 0;
+
+ if (!clk)
+ return 0;
+
+ if (WARN_ON(clk->prepare_count == 0))
+ return -ESHUTDOWN;
+
+ if (clk->enable_count == 0) {
+ ret = __clk_enable(clk->parent);
+
+ if (ret)
+ return ret;
+
+ if (clk->ops->enable) {
+ ret = clk->ops->enable(clk->hw);
+ if (ret) {
+ __clk_disable(clk->parent);
+ return ret;
+ }
+ }
+ }
+
+ clk->enable_count++;
+ return 0;
+}
+
+/**
+ * clk_enable - ungate a clock
+ * @clk: the clk being ungated
+ *
+ * clk_enable must not sleep, which differentiates it from clk_prepare. In a
+ * simple case, clk_enable can be used instead of clk_prepare to ungate a clk
+ * if the operation will never sleep. One example is a SoC-internal clk which
+ * is controlled via simple register writes. In the complex case a clk ungate
+ * operation may require a fast and a slow part. It is this reason that
+ * clk_enable and clk_prepare are not mutually exclusive. In fact clk_prepare
+ * must be called before clk_enable. Returns 0 on success, -EERROR
+ * otherwise.
+ */
+int clk_enable(struct clk *clk)
+{
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&enable_lock, flags);
+ ret = __clk_enable(clk);
+ spin_unlock_irqrestore(&enable_lock, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(clk_enable);
+
+/**
+ * clk_get_rate - return the rate of clk
+ * @clk: the clk whose rate is being returned
+ *
+ * Simply returns the cached rate of the clk. Does not query the hardware. If
+ * clk is NULL then returns -EINVAL.
+ */
+unsigned long clk_get_rate(struct clk *clk)
+{
+ unsigned long rate;
+
+ mutex_lock(&prepare_lock);
+ rate = __clk_get_rate(clk);
+ mutex_unlock(&prepare_lock);
+
+ return rate;
+}
+EXPORT_SYMBOL_GPL(clk_get_rate);
+
+/**
+ * __clk_round_rate - round the given rate for a clk
+ * @clk: round the rate of this clock
+ *
+ * Caller must hold prepare_lock. Useful for clk_ops such as .set_rate
+ */
+unsigned long __clk_round_rate(struct clk *clk, unsigned long rate)
+{
+ unsigned long unused;
+
+ if (!clk)
+ return -EINVAL;
+
+ if (!clk->ops->round_rate)
+ return clk->rate;
+
+ if (clk->flags & CLK_SET_RATE_PARENT)
+ return clk->ops->round_rate(clk->hw, rate, &unused);
+ else
+ return clk->ops->round_rate(clk->hw, rate, NULL);
+}
+
+/**
+ * clk_round_rate - round the given rate for a clk
+ * @clk: the clk for which we are rounding a rate
+ * @rate: the rate which is to be rounded
+ *
+ * Takes in a rate as input and rounds it to a rate that the clk can actually
+ * use which is then returned. If clk doesn't support round_rate operation
+ * then the parent rate is returned.
+ */
+long clk_round_rate(struct clk *clk, unsigned long rate)
+{
+ unsigned long ret;
+
+ mutex_lock(&prepare_lock);
+ ret = __clk_round_rate(clk, rate);
+ mutex_unlock(&prepare_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(clk_round_rate);
+
+/**
+ * __clk_notify - call clk notifier chain
+ * @clk: struct clk * that is changing rate
+ * @msg: clk notifier type (see include/linux/clk.h)
+ * @old_rate: old clk rate
+ * @new_rate: new clk rate
+ *
+ * Triggers a notifier call chain on the clk rate-change notification
+ * for 'clk'. Passes a pointer to the struct clk and the previous
+ * and current rates to the notifier callback. Intended to be called by
+ * internal clock code only. Returns NOTIFY_DONE from the last driver
+ * called if all went well, or NOTIFY_STOP or NOTIFY_BAD immediately if
+ * a driver returns that.
+ */
+static int __clk_notify(struct clk *clk, unsigned long msg,
+ unsigned long old_rate, unsigned long new_rate)
+{
+ struct clk_notifier *cn;
+ struct clk_notifier_data cnd;
+ int ret = NOTIFY_DONE;
+
+ cnd.clk = clk;
+ cnd.old_rate = old_rate;
+ cnd.new_rate = new_rate;
+
+ list_for_each_entry(cn, &clk_notifier_list, node) {
+ if (cn->clk == clk) {
+ ret = srcu_notifier_call_chain(&cn->notifier_head, msg,
+ &cnd);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * __clk_recalc_rates
+ * @clk: first clk in the subtree
+ * @msg: notification type (see include/linux/clk.h)
+ *
+ * Walks the subtree of clks starting with clk and recalculates rates as it
+ * goes. Note that if a clk does not implement the .recalc_rate callback then
+ * it is assumed that the clock will take on the rate of it's parent.
+ *
+ * clk_recalc_rates also propagates the POST_RATE_CHANGE notification,
+ * if necessary.
+ *
+ * Caller must hold prepare_lock.
+ */
+static void __clk_recalc_rates(struct clk *clk, unsigned long msg)
+{
+ unsigned long old_rate;
+ unsigned long parent_rate = 0;
+ struct hlist_node *tmp;
+ struct clk *child;
+
+ old_rate = clk->rate;
+
+ if (clk->parent)
+ parent_rate = clk->parent->rate;
+
+ if (clk->ops->recalc_rate)
+ clk->rate = clk->ops->recalc_rate(clk->hw, parent_rate);
+ else
+ clk->rate = parent_rate;
+
+ /*
+ * ignore NOTIFY_STOP and NOTIFY_BAD return values for POST_RATE_CHANGE
+ * & ABORT_RATE_CHANGE notifiers
+ */
+ if (clk->notifier_count && msg)
+ __clk_notify(clk, msg, old_rate, clk->rate);
+
+ hlist_for_each_entry(child, tmp, &clk->children, child_node)
+ __clk_recalc_rates(child, msg);
+}
+
+/**
+ * __clk_speculate_rates
+ * @clk: first clk in the subtree
+ * @parent_rate: the "future" rate of clk's parent
+ *
+ * Walks the subtree of clks starting with clk, speculating rates as it
+ * goes and firing off PRE_RATE_CHANGE notifications as necessary.
+ *
+ * Unlike clk_recalc_rates, clk_speculate_rates exists only for sending
+ * pre-rate change notifications and returns early if no clks in the
+ * subtree have subscribed to the notifications. Note that if a clk does not
+ * implement the .recalc_rate callback then it is assumed that the clock will
+ * take on the rate of it's parent.
+ *
+ * Caller must hold prepare_lock.
+ */
+static int __clk_speculate_rates(struct clk *clk, unsigned long parent_rate)
+{
+ struct hlist_node *tmp;
+ struct clk *child;
+ unsigned long new_rate;
+ int ret = NOTIFY_DONE;
+
+ if (clk->ops->recalc_rate)
+ new_rate = clk->ops->recalc_rate(clk->hw, parent_rate);
+ else
+ new_rate = parent_rate;
+
+ /* abort the rate change if a driver returns NOTIFY_BAD */
+ if (clk->notifier_count)
+ ret = __clk_notify(clk, PRE_RATE_CHANGE, clk->rate, new_rate);
+
+ if (ret == NOTIFY_BAD)
+ goto out;
+
+ hlist_for_each_entry(child, tmp, &clk->children, child_node) {
+ ret = __clk_speculate_rates(child, new_rate);
+ if (ret == NOTIFY_BAD)
+ break;
+ }
+
+out:
+ return ret;
+}
+
+static void clk_calc_subtree(struct clk *clk, unsigned long new_rate)
+{
+ struct clk *child;
+ struct hlist_node *tmp;
+
+ clk->new_rate = new_rate;
+
+ hlist_for_each_entry(child, tmp, &clk->children, child_node) {
+ if (child->ops->recalc_rate)
+ child->new_rate = child->ops->recalc_rate(child->hw, new_rate);
+ else
+ child->new_rate = new_rate;
+ clk_calc_subtree(child, child->new_rate);
+ }
+}
+
+/*
+ * calculate the new rates returning the topmost clock that has to be
+ * changed.
+ */
+static struct clk *clk_calc_new_rates(struct clk *clk, unsigned long rate)
+{
+ struct clk *top = clk;
+ unsigned long best_parent_rate = clk->parent->rate;
+ unsigned long new_rate;
+
+ if (!clk->ops->round_rate && !(clk->flags & CLK_SET_RATE_PARENT)) {
+ clk->new_rate = clk->rate;
+ return NULL;
+ }
+
+ if (!clk->ops->round_rate && (clk->flags & CLK_SET_RATE_PARENT)) {
+ top = clk_calc_new_rates(clk->parent, rate);
+ new_rate = clk->new_rate = clk->parent->new_rate;
+
+ goto out;
+ }
+
+ if (clk->flags & CLK_SET_RATE_PARENT)
+ new_rate = clk->ops->round_rate(clk->hw, rate, &best_parent_rate);
+ else
+ new_rate = clk->ops->round_rate(clk->hw, rate, NULL);
+
+ if (best_parent_rate != clk->parent->rate) {
+ top = clk_calc_new_rates(clk->parent, best_parent_rate);
+
+ goto out;
+ }
+
+out:
+ clk_calc_subtree(clk, new_rate);
+
+ return top;
+}
+
+/*
+ * Notify about rate changes in a subtree. Always walk down the whole tree
+ * so that in case of an error we can walk down the whole tree again and
+ * abort the change.
+ */
+static struct clk *clk_propagate_rate_change(struct clk *clk, unsigned long event)
+{
+ struct hlist_node *tmp;
+ struct clk *child, *fail_clk = NULL;
+ int ret = NOTIFY_DONE;
+
+ if (clk->rate == clk->new_rate)
+ return 0;
+
+ if (clk->notifier_count) {
+ ret = __clk_notify(clk, event, clk->rate, clk->new_rate);
+ if (ret == NOTIFY_BAD)
+ fail_clk = clk;
+ }
+
+ hlist_for_each_entry(child, tmp, &clk->children, child_node) {
+ clk = clk_propagate_rate_change(child, event);
+ if (clk)
+ fail_clk = clk;
+ }
+
+ return fail_clk;
+}
+
+/*
+ * walk down a subtree and set the new rates notifying the rate
+ * change on the way
+ */
+static void clk_change_rate(struct clk *clk)
+{
+ struct clk *child;
+ unsigned long old_rate;
+ unsigned long best_parent_rate = 0;
+ struct hlist_node *tmp;
+
+ old_rate = clk->rate;
+
+ if (clk->parent)
+ best_parent_rate = clk->parent->rate;
+
+ if (clk->ops->set_rate)
+ clk->ops->set_rate(clk->hw, clk->new_rate);
+
+ if (clk->ops->recalc_rate)
+ clk->rate = clk->ops->recalc_rate(clk->hw, best_parent_rate);
+ else
+ clk->rate = best_parent_rate;
+
+ if (clk->notifier_count && old_rate != clk->rate)
+ __clk_notify(clk, POST_RATE_CHANGE, old_rate, clk->rate);
+
+ hlist_for_each_entry(child, tmp, &clk->children, child_node)
+ clk_change_rate(child);
+}
+
+/**
+ * clk_set_rate - specify a new rate for clk
+ * @clk: the clk whose rate is being changed
+ * @rate: the new rate for clk
+ *
+ * In the simplest case clk_set_rate will only change the rate of clk.
+ *
+ * If clk has the CLK_SET_RATE_GATE flag set and it is enabled this call
+ * will fail; only when the clk is disabled will it be able to change
+ * its rate.
+ *
+ * Setting the CLK_SET_RATE_PARENT flag allows clk_set_rate to
+ * recursively propagate up to clk's parent; whether or not this happens
+ * depends on the outcome of clk's .round_rate implementation. If
+ * *parent_rate is 0 after calling .round_rate then upstream parent
+ * propagation is ignored. If *parent_rate comes back with a new rate
+ * for clk's parent then we propagate up to clk's parent and set it's
+ * rate. Upward propagation will continue until either a clk does not
+ * support the CLK_SET_RATE_PARENT flag or .round_rate stops requesting
+ * changes to clk's parent_rate. If there is a failure during upstream
+ * propagation then clk_set_rate will unwind and restore each clk's rate
+ * that had been successfully changed. Afterwards a rate change abort
+ * notification will be propagated downstream, starting from the clk
+ * that failed.
+ *
+ * At the end of all of the rate setting, clk_set_rate internally calls
+ * __clk_recalc_rates and propagates the rate changes downstream,
+ * starting from the highest clk whose rate was changed. This has the
+ * added benefit of propagating post-rate change notifiers.
+ *
+ * Note that while post-rate change and rate change abort notifications
+ * are guaranteed to be sent to a clk only once per call to
+ * clk_set_rate, pre-change notifications will be sent for every clk
+ * whose rate is changed. Stacking pre-change notifications is noisy
+ * for the drivers subscribed to them, but this allows drivers to react
+ * to intermediate clk rate changes up until the point where the final
+ * rate is achieved at the end of upstream propagation.
+ *
+ * Returns 0 on success, -EERROR otherwise.
+ */
+int clk_set_rate(struct clk *clk, unsigned long rate)
+{
+ struct clk *top, *fail_clk;
+ int ret = 0;
+
+ /* prevent racing with updates to the clock topology */
+ mutex_lock(&prepare_lock);
+
+ /* bail early if nothing to do */
+ if (rate == clk->rate)
+ goto out;
+
+ /* calculate new rates and get the topmost changed clock */
+ top = clk_calc_new_rates(clk, rate);
+ if (!top) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* notify that we are about to change rates */
+ fail_clk = clk_propagate_rate_change(top, PRE_RATE_CHANGE);
+ if (fail_clk) {
+ pr_warn("%s: failed to set %s rate\n", __func__,
+ fail_clk->name);
+ clk_propagate_rate_change(top, ABORT_RATE_CHANGE);
+ ret = -EBUSY;
+ goto out;
+ }
+
+ /* change the rates */
+ clk_change_rate(top);
+
+ mutex_unlock(&prepare_lock);
+
+ return 0;
+out:
+ mutex_unlock(&prepare_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(clk_set_rate);
+
+/**
+ * clk_get_parent - return the parent of a clk
+ * @clk: the clk whose parent gets returned
+ *
+ * Simply returns clk->parent. Returns NULL if clk is NULL.
+ */
+struct clk *clk_get_parent(struct clk *clk)
+{
+ struct clk *parent;
+
+ mutex_lock(&prepare_lock);
+ parent = __clk_get_parent(clk);
+ mutex_unlock(&prepare_lock);
+
+ return parent;
+}
+EXPORT_SYMBOL_GPL(clk_get_parent);
+
+/*
+ * .get_parent is mandatory for clocks with multiple possible parents. It is
+ * optional for single-parent clocks. Always call .get_parent if it is
+ * available and WARN if it is missing for multi-parent clocks.
+ *
+ * For single-parent clocks without .get_parent, first check to see if the
+ * .parents array exists, and if so use it to avoid an expensive tree
+ * traversal. If .parents does not exist then walk the tree with __clk_lookup.
+ */
+static struct clk *__clk_init_parent(struct clk *clk)
+{
+ struct clk *ret = NULL;
+ u8 index;
+
+ /* handle the trivial cases */
+
+ if (!clk->num_parents)
+ goto out;
+
+ if (clk->num_parents == 1) {
+ if (IS_ERR_OR_NULL(clk->parent))
+ ret = clk->parent = __clk_lookup(clk->parent_names[0]);
+ ret = clk->parent;
+ goto out;
+ }
+
+ if (!clk->ops->get_parent) {
+ WARN(!clk->ops->get_parent,
+ "%s: multi-parent clocks must implement .get_parent\n",
+ __func__);
+ goto out;
+ };
+
+ /*
+ * Do our best to cache parent clocks in clk->parents. This prevents
+ * unnecessary and expensive calls to __clk_lookup. We don't set
+ * clk->parent here; that is done by the calling function
+ */
+
+ index = clk->ops->get_parent(clk->hw);
+
+ if (!clk->parents)
+ clk->parents =
+ kzalloc((sizeof(struct clk*) * clk->num_parents),
+ GFP_KERNEL);
+
+ if (!clk->parents)
+ ret = __clk_lookup(clk->parent_names[index]);
+ else if (!clk->parents[index])
+ ret = clk->parents[index] =
+ __clk_lookup(clk->parent_names[index]);
+ else
+ ret = clk->parents[index];
+
+out:
+ return ret;
+}
+
+void __clk_reparent(struct clk *clk, struct clk *new_parent)
+{
+#ifdef CONFIG_COMMON_CLK_DEBUG
+ struct dentry *d;
+ struct dentry *new_parent_d;
+#endif
+
+ if (!clk || !new_parent)
+ return;
+
+ hlist_del(&clk->child_node);
+
+ if (new_parent)
+ hlist_add_head(&clk->child_node, &new_parent->children);
+ else
+ hlist_add_head(&clk->child_node, &clk_orphan_list);
+
+#ifdef CONFIG_COMMON_CLK_DEBUG
+ if (!inited)
+ goto out;
+
+ if (new_parent)
+ new_parent_d = new_parent->dentry;
+ else
+ new_parent_d = orphandir;
+
+ d = debugfs_rename(clk->dentry->d_parent, clk->dentry,
+ new_parent_d, clk->name);
+ if (d)
+ clk->dentry = d;
+ else
+ pr_debug("%s: failed to rename debugfs entry for %s\n",
+ __func__, clk->name);
+out:
+#endif
+
+ clk->parent = new_parent;
+
+ __clk_recalc_rates(clk, POST_RATE_CHANGE);
+}
+
+static int __clk_set_parent(struct clk *clk, struct clk *parent)
+{
+ struct clk *old_parent;
+ unsigned long flags;
+ int ret = -EINVAL;
+ u8 i;
+
+ old_parent = clk->parent;
+
+ if (!clk->parents)
+ clk->parents = kzalloc((sizeof(struct clk*) * clk->num_parents),
+ GFP_KERNEL);
+
+ /*
+ * find index of new parent clock using cached parent ptrs,
+ * or if not yet cached, use string name comparison and cache
+ * them now to avoid future calls to __clk_lookup.
+ */
+ for (i = 0; i < clk->num_parents; i++) {
+ if (clk->parents && clk->parents[i] == parent)
+ break;
+ else if (!strcmp(clk->parent_names[i], parent->name)) {
+ if (clk->parents)
+ clk->parents[i] = __clk_lookup(parent->name);
+ break;
+ }
+ }
+
+ if (i == clk->num_parents) {
+ pr_debug("%s: clock %s is not a possible parent of clock %s\n",
+ __func__, parent->name, clk->name);
+ goto out;
+ }
+
+ /* migrate prepare and enable */
+ if (clk->prepare_count)
+ __clk_prepare(parent);
+
+ /* FIXME replace with clk_is_enabled(clk) someday */
+ spin_lock_irqsave(&enable_lock, flags);
+ if (clk->enable_count)
+ __clk_enable(parent);
+ spin_unlock_irqrestore(&enable_lock, flags);
+
+ /* change clock input source */
+ ret = clk->ops->set_parent(clk->hw, i);
+
+ /* clean up old prepare and enable */
+ spin_lock_irqsave(&enable_lock, flags);
+ if (clk->enable_count)
+ __clk_disable(old_parent);
+ spin_unlock_irqrestore(&enable_lock, flags);
+
+ if (clk->prepare_count)
+ __clk_unprepare(old_parent);
+
+out:
+ return ret;
+}
+
+/**
+ * clk_set_parent - switch the parent of a mux clk
+ * @clk: the mux clk whose input we are switching
+ * @parent: the new input to clk
+ *
+ * Re-parent clk to use parent as it's new input source. If clk has the
+ * CLK_SET_PARENT_GATE flag set then clk must be gated for this
+ * operation to succeed. After successfully changing clk's parent
+ * clk_set_parent will update the clk topology, sysfs topology and
+ * propagate rate recalculation via __clk_recalc_rates. Returns 0 on
+ * success, -EERROR otherwise.
+ */
+int clk_set_parent(struct clk *clk, struct clk *parent)
+{
+ int ret = 0;
+
+ if (!clk || !clk->ops)
+ return -EINVAL;
+
+ if (!clk->ops->set_parent)
+ return -ENOSYS;
+
+ /* prevent racing with updates to the clock topology */
+ mutex_lock(&prepare_lock);
+
+ if (clk->parent == parent)
+ goto out;
+
+ /* propagate PRE_RATE_CHANGE notifications */
+ if (clk->notifier_count)
+ ret = __clk_speculate_rates(clk, parent->rate);
+
+ /* abort if a driver objects */
+ if (ret == NOTIFY_STOP)
+ goto out;
+
+ /* only re-parent if the clock is not in use */
+ if ((clk->flags & CLK_SET_PARENT_GATE) && clk->prepare_count)
+ ret = -EBUSY;
+ else
+ ret = __clk_set_parent(clk, parent);
+
+ /* propagate ABORT_RATE_CHANGE if .set_parent failed */
+ if (ret) {
+ __clk_recalc_rates(clk, ABORT_RATE_CHANGE);
+ goto out;
+ }
+
+ /* propagate rate recalculation downstream */
+ __clk_reparent(clk, parent);
+
+out:
+ mutex_unlock(&prepare_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(clk_set_parent);
+
+/**
+ * __clk_init - initialize the data structures in a struct clk
+ * @dev: device initializing this clk, placeholder for now
+ * @clk: clk being initialized
+ *
+ * Initializes the lists in struct clk, queries the hardware for the
+ * parent and rate and sets them both.
+ *
+ * Any struct clk passed into __clk_init must have the following members
+ * populated:
+ * .name
+ * .ops
+ * .hw
+ * .parent_names
+ * .num_parents
+ * .flags
+ *
+ * Essentially, everything that would normally be passed into clk_register is
+ * assumed to be initialized already in __clk_init. The other members may be
+ * populated, but are optional.
+ *
+ * __clk_init is only exposed via clk-private.h and is intended for use with
+ * very large numbers of clocks that need to be statically initialized. It is
+ * a layering violation to include clk-private.h from any code which implements
+ * a clock's .ops; as such any statically initialized clock data MUST be in a
+ * separate C file from the logic that implements it's operations.
+ */
+void __clk_init(struct device *dev, struct clk *clk)
+{
+ int i;
+ struct clk *orphan;
+ struct hlist_node *tmp, *tmp2;
+
+ if (!clk)
+ return;
+
+ mutex_lock(&prepare_lock);
+
+ /* check to see if a clock with this name is already registered */
+ if (__clk_lookup(clk->name))
+ goto out;
+
+ /* throw a WARN if any entries in parent_names are NULL */
+ for (i = 0; i < clk->num_parents; i++)
+ WARN(!clk->parent_names[i],
+ "%s: invalid NULL in %s's .parent_names\n",
+ __func__, clk->name);
+
+ /*
+ * Allocate an array of struct clk *'s to avoid unnecessary string
+ * look-ups of clk's possible parents. This can fail for clocks passed
+ * in to clk_init during early boot; thus any access to clk->parents[]
+ * must always check for a NULL pointer and try to populate it if
+ * necessary.
+ *
+ * If clk->parents is not NULL we skip this entire block. This allows
+ * for clock drivers to statically initialize clk->parents.
+ */
+ if (clk->num_parents && !clk->parents) {
+ clk->parents = kmalloc((sizeof(struct clk*) * clk->num_parents),
+ GFP_KERNEL);
+ /*
+ * __clk_lookup returns NULL for parents that have not been
+ * clk_init'd; thus any access to clk->parents[] must check
+ * for a NULL pointer. We can always perform lazy lookups for
+ * missing parents later on.
+ */
+ if (clk->parents)
+ for (i = 0; i < clk->num_parents; i++)
+ clk->parents[i] =
+ __clk_lookup(clk->parent_names[i]);
+ }
+
+ clk->parent = __clk_init_parent(clk);
+
+ /*
+ * Populate clk->parent if parent has already been __clk_init'd. If
+ * parent has not yet been __clk_init'd then place clk in the orphan
+ * list. If clk has set the CLK_IS_ROOT flag then place it in the root
+ * clk list.
+ *
+ * Every time a new clk is clk_init'd then we walk the list of orphan
+ * clocks and re-parent any that are children of the clock currently
+ * being clk_init'd.
+ */
+ if (clk->parent)
+ hlist_add_head(&clk->child_node,
+ &clk->parent->children);
+ else if (clk->flags & CLK_IS_ROOT)
+ hlist_add_head(&clk->child_node, &clk_root_list);
+ else
+ hlist_add_head(&clk->child_node, &clk_orphan_list);
+
+ /*
+ * Set clk's rate. The preferred method is to use .recalc_rate. For
+ * simple clocks and lazy developers the default fallback is to use the
+ * parent's rate. If a clock doesn't have a parent (or is orphaned)
+ * then rate is set to zero.
+ */
+ if (clk->ops->recalc_rate)
+ clk->rate = clk->ops->recalc_rate(clk->hw,
+ __clk_get_rate(clk->parent));
+ else if (clk->parent)
+ clk->rate = clk->parent->rate;
+ else
+ clk->rate = 0;
+
+ /*
+ * walk the list of orphan clocks and reparent any that are children of
+ * this clock
+ */
+ hlist_for_each_entry_safe(orphan, tmp, tmp2, &clk_orphan_list, child_node)
+ for (i = 0; i < orphan->num_parents; i++)
+ if (!strcmp(clk->name, orphan->parent_names[i])) {
+ __clk_reparent(orphan, clk);
+ break;
+ }
+
+ /*
+ * optional platform-specific magic
+ *
+ * The .init callback is not used by any of the basic clock types, but
+ * exists for weird hardware that must perform initialization magic.
+ * Please consider other ways of solving initialization problems before
+ * using this callback, as it's use is discouraged.
+ */
+ if (clk->ops->init)
+ clk->ops->init(clk->hw);
+
+ clk_debug_register(clk);
+
+out:
+ mutex_unlock(&prepare_lock);
+
+ return;
+}
+
+/**
+ * clk_register - allocate a new clock, register it and return an opaque cookie
+ * @dev: device that is registering this clock
+ * @name: clock name
+ * @ops: operations this clock supports
+ * @hw: link to hardware-specific clock data
+ * @parent_names: array of string names for all possible parents
+ * @num_parents: number of possible parents
+ * @flags: framework-level hints and quirks
+ *
+ * clk_register is the primary interface for populating the clock tree with new
+ * clock nodes. It returns a pointer to the newly allocated struct clk which
+ * cannot be dereferenced by driver code but may be used in conjuction with the
+ * rest of the clock API.
+ */
+struct clk *clk_register(struct device *dev, const char *name,
+ const struct clk_ops *ops, struct clk_hw *hw,
+ char **parent_names, u8 num_parents, unsigned long flags)
+{
+ struct clk *clk;
+
+ clk = kzalloc(sizeof(*clk), GFP_KERNEL);
+ if (!clk)
+ return NULL;
+
+ clk->name = name;
+ clk->ops = ops;
+ clk->hw = hw;
+ clk->flags = flags;
+ clk->parent_names = parent_names;
+ clk->num_parents = num_parents;
+ hw->clk = clk;
+
+ __clk_init(dev, clk);
+
+ return clk;
+}
+EXPORT_SYMBOL_GPL(clk_register);
+
+/*** clk rate change notifiers ***/
+
+/**
+ * clk_notifier_register - add a clk rate change notifier
+ * @clk: struct clk * to watch
+ * @nb: struct notifier_block * with callback info
+ *
+ * Request notification when clk's rate changes. This uses an SRCU
+ * notifier because we want it to block and notifier unregistrations are
+ * uncommon. The callbacks associated with the notifier must not
+ * re-enter into the clk framework by calling any top-level clk APIs;
+ * this will cause a nested prepare_lock mutex.
+ *
+ * Pre-change notifier callbacks will be passed the current, pre-change
+ * rate of the clk via struct clk_notifier_data.old_rate. The new,
+ * post-change rate of the clk is passed via struct
+ * clk_notifier_data.new_rate.
+ *
+ * Post-change notifiers will pass the now-current, post-change rate of
+ * the clk in both struct clk_notifier_data.old_rate and struct
+ * clk_notifier_data.new_rate.
+ *
+ * Abort-change notifiers are effectively the opposite of pre-change
+ * notifiers: the original pre-change clk rate is passed in via struct
+ * clk_notifier_data.new_rate and the failed post-change rate is passed
+ * in via struct clk_notifier_data.old_rate.
+ *
+ * clk_notifier_register() must be called from non-atomic context.
+ * Returns -EINVAL if called with null arguments, -ENOMEM upon
+ * allocation failure; otherwise, passes along the return value of
+ * srcu_notifier_chain_register().
+ */
+int clk_notifier_register(struct clk *clk, struct notifier_block *nb)
+{
+ struct clk_notifier *cn;
+ int ret = -ENOMEM;
+
+ if (!clk || !nb)
+ return -EINVAL;
+
+ mutex_lock(&prepare_lock);
+
+ /* search the list of notifiers for this clk */
+ list_for_each_entry(cn, &clk_notifier_list, node)
+ if (cn->clk == clk)
+ break;
+
+ /* if clk wasn't in the notifier list, allocate new clk_notifier */
+ if (cn->clk != clk) {
+ cn = kzalloc(sizeof(struct clk_notifier), GFP_KERNEL);
+ if (!cn)
+ goto out;
+
+ cn->clk = clk;
+ srcu_init_notifier_head(&cn->notifier_head);
+
+ list_add(&cn->node, &clk_notifier_list);
+ }
+
+ ret = srcu_notifier_chain_register(&cn->notifier_head, nb);
+
+ clk->notifier_count++;
+
+out:
+ mutex_unlock(&prepare_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(clk_notifier_register);
+
+/**
+ * clk_notifier_unregister - remove a clk rate change notifier
+ * @clk: struct clk *
+ * @nb: struct notifier_block * with callback info
+ *
+ * Request no further notification for changes to 'clk' and frees memory
+ * allocated in clk_notifier_register.
+ *
+ * Returns -EINVAL if called with null arguments; otherwise, passes
+ * along the return value of srcu_notifier_chain_unregister().
+ */
+int clk_notifier_unregister(struct clk *clk, struct notifier_block *nb)
+{
+ struct clk_notifier *cn = NULL;
+ int ret = -EINVAL;
+
+ if (!clk || !nb)
+ return -EINVAL;
+
+ mutex_lock(&prepare_lock);
+
+ list_for_each_entry(cn, &clk_notifier_list, node)
+ if (cn->clk == clk)
+ break;
+
+ if (cn->clk == clk) {
+ ret = srcu_notifier_chain_unregister(&cn->notifier_head, nb);
+
+ clk->notifier_count--;
+
+ /* XXX the notifier code should handle this better */
+ if (!cn->notifier_head.head) {
+ srcu_cleanup_notifier_head(&cn->notifier_head);
+ list_del(&cn->node);
+ kfree(cn);
+ }
+
+ } else {
+ ret = -ENOENT;
+ }
+
+ mutex_unlock(&prepare_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(clk_notifier_unregister);
diff --git a/ap/os/linux/linux-3.4.x/drivers/clk/clkdev.c b/ap/os/linux/linux-3.4.x/drivers/clk/clkdev.c
new file mode 100644
index 0000000..6db161f
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/clk/clkdev.c
@@ -0,0 +1,175 @@
+/*
+ * drivers/clk/clkdev.c
+ *
+ * Copyright (C) 2008 Russell King.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Helper for the clk API to assist looking up a struct clk.
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/string.h>
+#include <linux/mutex.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+
+static LIST_HEAD(clocks);
+static DEFINE_MUTEX(clocks_mutex);
+
+/*
+ * Find the correct struct clk for the device and connection ID.
+ * We do slightly fuzzy matching here:
+ * An entry with a NULL ID is assumed to be a wildcard.
+ * If an entry has a device ID, it must match
+ * If an entry has a connection ID, it must match
+ * Then we take the most specific entry - with the following
+ * order of precedence: dev+con > dev only > con only.
+ */
+static struct clk_lookup *clk_find(const char *dev_id, const char *con_id)
+{
+ struct clk_lookup *p, *cl = NULL;
+ int match, best = 0;
+
+ list_for_each_entry(p, &clocks, node) {
+ match = 0;
+ if (p->dev_id) {
+ if (!dev_id || strcmp(p->dev_id, dev_id))
+ continue;
+ match += 2;
+ }
+ if (p->con_id) {
+ if (!con_id || strcmp(p->con_id, con_id))
+ continue;
+ match += 1;
+ }
+
+ if (match > best) {
+ cl = p;
+ if (match != 3)
+ best = match;
+ else
+ break;
+ }
+ }
+ return cl;
+}
+
+struct clk *clk_get_sys(const char *dev_id, const char *con_id)
+{
+ struct clk_lookup *cl;
+
+ mutex_lock(&clocks_mutex);
+ cl = clk_find(dev_id, con_id);
+ if (cl && !__clk_get(cl->clk))
+ cl = NULL;
+ mutex_unlock(&clocks_mutex);
+
+ return cl ? cl->clk : ERR_PTR(-ENOENT);
+}
+EXPORT_SYMBOL(clk_get_sys);
+
+struct clk *clk_get(struct device *dev, const char *con_id)
+{
+ const char *dev_id = dev ? dev_name(dev) : NULL;
+
+ return clk_get_sys(dev_id, con_id);
+}
+EXPORT_SYMBOL(clk_get);
+
+void clk_put(struct clk *clk)
+{
+ __clk_put(clk);
+}
+EXPORT_SYMBOL(clk_put);
+
+void clkdev_add(struct clk_lookup *cl)
+{
+ mutex_lock(&clocks_mutex);
+ list_add_tail(&cl->node, &clocks);
+ mutex_unlock(&clocks_mutex);
+}
+EXPORT_SYMBOL(clkdev_add);
+
+void __init clkdev_add_table(struct clk_lookup *cl, size_t num)
+{
+ mutex_lock(&clocks_mutex);
+ while (num--) {
+ list_add_tail(&cl->node, &clocks);
+ cl++;
+ }
+ mutex_unlock(&clocks_mutex);
+}
+
+#define MAX_DEV_ID 20
+#define MAX_CON_ID 16
+
+struct clk_lookup_alloc {
+ struct clk_lookup cl;
+ char dev_id[MAX_DEV_ID];
+ char con_id[MAX_CON_ID];
+};
+
+struct clk_lookup * __init_refok
+clkdev_alloc(struct clk *clk, const char *con_id, const char *dev_fmt, ...)
+{
+ struct clk_lookup_alloc *cla;
+
+ cla = __clkdev_alloc(sizeof(*cla));
+ if (!cla)
+ return NULL;
+
+ cla->cl.clk = clk;
+ if (con_id) {
+ strlcpy(cla->con_id, con_id, sizeof(cla->con_id));
+ cla->cl.con_id = cla->con_id;
+ }
+
+ if (dev_fmt) {
+ va_list ap;
+
+ va_start(ap, dev_fmt);
+ vscnprintf(cla->dev_id, sizeof(cla->dev_id), dev_fmt, ap);
+ cla->cl.dev_id = cla->dev_id;
+ va_end(ap);
+ }
+
+ return &cla->cl;
+}
+EXPORT_SYMBOL(clkdev_alloc);
+
+int clk_add_alias(const char *alias, const char *alias_dev_name, char *id,
+ struct device *dev)
+{
+ struct clk *r = clk_get(dev, id);
+ struct clk_lookup *l;
+
+ if (IS_ERR(r))
+ return PTR_ERR(r);
+
+ l = clkdev_alloc(r, alias, alias_dev_name);
+ clk_put(r);
+ if (!l)
+ return -ENODEV;
+ clkdev_add(l);
+ return 0;
+}
+EXPORT_SYMBOL(clk_add_alias);
+
+/*
+ * clkdev_drop - remove a clock dynamically allocated
+ */
+void clkdev_drop(struct clk_lookup *cl)
+{
+ mutex_lock(&clocks_mutex);
+ list_del(&cl->node);
+ mutex_unlock(&clocks_mutex);
+ kfree(cl);
+}
+EXPORT_SYMBOL(clkdev_drop);
diff --git a/ap/os/linux/linux-3.4.x/drivers/clk/zte/Makefile b/ap/os/linux/linux-3.4.x/drivers/clk/zte/Makefile
new file mode 100644
index 0000000..271eb91
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/clk/zte/Makefile
@@ -0,0 +1,3 @@
+obj-y += clk.o
+obj-$(CONFIG_ARCH_ZX297520V2) += clk-zx297520v2.o
+obj-$(CONFIG_ARCH_ZX297520V3) += clk-zx297520v3.o
diff --git a/ap/os/linux/linux-3.4.x/drivers/clk/zte/clk-zx297520v2.c b/ap/os/linux/linux-3.4.x/drivers/clk/zte/clk-zx297520v2.c
new file mode 100644
index 0000000..d0ec9e4
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/clk/zte/clk-zx297520v2.c
@@ -0,0 +1,1344 @@
+/*
+ * arch/arm/mach-zx297520v2/zx297520v2-clock.c
+ *
+ * Copyright (C) 2015 ZTE-TSP
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+
+#include <asm/irq.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <linux/math64.h>
+
+#include <linux/clkdev.h>
+#include <mach/spinlock.h>
+#include <mach/board.h>
+#include <mach/iomap.h>
+#include <linux/clk-private.h>
+
+#include "clk.h"
+
+#define DEFINE_ZX29_CLK(hwclk,clkops,num,names) \
+ struct clk * parents_##hwclk[num]; \
+ static struct clk hwclk##_clk = { \
+ .name = #hwclk "_clk", \
+ .hw = &hwclk.hw, \
+ .ops = &clkops, \
+ .num_parents = num, \
+ .parent_names = names, \
+ .parents = parents_##hwclk, \
+ .flags = CLK_AUTO_ROUND_PARENT, \
+}
+
+#define DEFINE_ZX29_CLK_FLAG(hwclk,clkops,num,names, flag) \
+ struct clk * parents_##hwclk[num]; \
+ static struct clk hwclk##_clk = { \
+ .name = #hwclk "_clk", \
+ .hw = &hwclk.hw, \
+ .ops = &clkops, \
+ .num_parents = num, \
+ .parent_names = names, \
+ .parents = parents_##hwclk, \
+ .flags = flag, \
+}
+
+/*
+ * enable the clk.
+ */
+static int zx297520v2_clk_enable(struct clk_hw *hw)
+{
+ unsigned int data = 0;
+ void __iomem *regaddr = NULL;
+ struct zx29_hwclk *zx29clk = to_zx29_hwclk(hw);
+
+ if(zx29clk->clk_en_reg.reg_bit_size == 0)
+ return 0;
+
+ regaddr = zx29clk->clk_en_reg.reg_addr;
+
+ hw_spin_lock(CLK_HWLOCK);
+ data=ioread32(regaddr);
+ data |= 1<<zx29clk->clk_en_reg.reg_bit_offset;
+ iowrite32(data,regaddr);
+ hw_spin_unlock(CLK_HWLOCK);
+
+ return 0;
+}
+
+/*
+ * disable the clk.
+ */
+static void zx297520v2_clk_disable(struct clk_hw *hw)
+{
+ unsigned int data = 0;
+ void __iomem *regaddr = NULL;
+ struct zx29_hwclk *zx29clk = to_zx29_hwclk(hw);
+
+ if(zx29clk->clk_en_reg.reg_bit_size == 0)
+ return ;
+
+ regaddr = zx29clk->clk_en_reg.reg_addr;
+
+ hw_spin_lock(CLK_HWLOCK);
+ data=ioread32(regaddr);
+ data &= ~(1<<zx29clk->clk_en_reg.reg_bit_offset);
+ iowrite32(data,regaddr);
+ hw_spin_unlock(CLK_HWLOCK);
+
+ return ;
+}
+
+/*
+ * get clk's enable state.
+ */
+static int zx297520v2_is_enabled(struct clk_hw *hw)
+{
+ unsigned int data = 0;
+ void __iomem *regaddr = NULL;
+ struct zx29_hwclk *zx29clk = to_zx29_hwclk(hw);
+
+ if(zx29clk->clk_en_reg.reg_bit_size == 0)
+ return 1;
+
+ regaddr = zx29clk->clk_en_reg.reg_addr;
+
+ data=ioread32(regaddr);
+ data>>=zx29clk->clk_en_reg.reg_bit_offset;
+
+ if(data & 0x1)
+ return 1;
+ else
+ return 0;
+}
+
+/*
+ * set the clk division for new rate.
+ *
+ * new_rate = parent->rate/(clk_div+1)
+ */
+static int zx297520v2_clk_set_rate(struct clk_hw *hw, unsigned long new_rate)//, unsigned long parent_rate)
+{
+ unsigned long parent_rate = 0;
+ unsigned long clk_div = 0;
+ void __iomem *regaddr = NULL;
+ unsigned int data = 0;
+ struct zx29_hwclk *zx29clk = to_zx29_hwclk(hw);
+
+ if(zx29clk->hw.clk == NULL || zx29clk->hw.clk->parent == NULL)
+ return -EINVAL;
+ if(zx29clk->clk_div_reg.reg_bit_size == 0)
+ return -EINVAL;
+
+ parent_rate = zx29clk->hw.clk->parent->rate;
+ clk_div = parent_rate/new_rate;
+ /*get the max division*/
+ if(clk_div >= (1<<zx29clk->clk_div_reg.reg_bit_size))
+ clk_div = (1<<zx29clk->clk_div_reg.reg_bit_size);
+
+ if(clk_div)
+ {
+ clk_div--;
+ }
+
+ regaddr = zx29clk->clk_div_reg.reg_addr;
+
+ hw_spin_lock(CLK_HWLOCK);
+ data=ioread32(regaddr);
+ data &= ~(((1<<(zx29clk->clk_div_reg.reg_bit_size))-1)<<zx29clk->clk_div_reg.reg_bit_offset);
+ data |= clk_div<<zx29clk->clk_div_reg.reg_bit_offset;
+ iowrite32(data,regaddr);
+ hw_spin_unlock(CLK_HWLOCK);
+
+ return 0;
+}
+
+/*
+ * get the clk's rate.
+ *
+ * new_rate = parent->rate/(clk_div+1)
+ */
+static unsigned long zx297520v2_clk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ void __iomem *regaddr = NULL;
+ unsigned int data = 0;
+ struct zx29_hwclk *zx29clk = to_zx29_hwclk(hw);
+
+ /*if no clk division, return parent's rate */
+ if(zx29clk->clk_div_reg.reg_bit_size == 0) {
+ if(zx29clk->hw.clk->num_parents)
+ return parent_rate;
+ else
+ return 0;
+ }
+
+ regaddr = zx29clk->clk_div_reg.reg_addr;
+
+ data=ioread32(regaddr);
+ data >>= zx29clk->clk_div_reg.reg_bit_offset;
+ data &= ((1<<(zx29clk->clk_div_reg.reg_bit_size))-1);
+
+ return parent_rate/(data+1);
+}
+
+#if 0
+/*
+ * get the clk's rate.
+ *
+ * new_rate = parent->rate/(2^clk_div)
+ */
+static unsigned long zx297520v2_clk_recalc_rate_x2(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ void __iomem *regaddr = NULL;
+ unsigned int data = 0;
+ struct zx29_hwclk *zx29clk = to_zx29_hwclk(hw);
+
+ if(zx29clk->clk_div_reg.reg_bit_size == 0) {
+ if(zx29clk->hw.clk->num_parents)
+ return parent_rate;
+ else
+ return 0;
+ }
+
+ regaddr = zx29clk->clk_div_reg.reg_addr;
+
+ data=ioread32(regaddr);
+ data >>= zx29clk->clk_div_reg.reg_bit_offset;
+ data &= ((1<<(zx29clk->clk_div_reg.reg_bit_size))-1);
+
+ return parent_rate/(0x1<<data);
+}
+#endif
+
+/*
+ * select parent from given parents.
+ *
+ * index < num_parents
+ */
+static int zx297520v2_clk_set_parent(struct clk_hw *hw, u8 index)
+{
+ void __iomem *regaddr = NULL;
+ unsigned int data = 0;
+ struct zx29_hwclk *zx29clk = to_zx29_hwclk(hw);
+
+ if(zx29clk->hw.clk->num_parents==0 || index>=zx29clk->hw.clk->num_parents)
+ return -EINVAL;
+ if(zx29clk->clk_sel_reg.reg_bit_size == 0)
+ return 0;
+
+ regaddr = zx29clk->clk_sel_reg.reg_addr;
+
+ hw_spin_lock(CLK_HWLOCK);
+ data=ioread32(regaddr);
+ data &= ~(((1<<(zx29clk->clk_sel_reg.reg_bit_size))-1)<<zx29clk->clk_sel_reg.reg_bit_offset);
+ data |= index<<zx29clk->clk_sel_reg.reg_bit_offset;
+ iowrite32(data,regaddr);
+ hw_spin_unlock(CLK_HWLOCK);
+
+ return 0;
+}
+
+/*
+ * get current parent select.
+ *
+ * return index begin with 0.
+ */
+static u8 zx297520v2_get_parent(struct clk_hw *hw)
+{
+ unsigned int data = 0;
+ void __iomem *regaddr = NULL;
+ struct zx29_hwclk *zx29clk = to_zx29_hwclk(hw);
+
+ if(zx29clk->hw.clk->num_parents < 2 || zx29clk->clk_sel_reg.reg_bit_size ==0)
+ return 0;
+
+ regaddr = zx29clk->clk_sel_reg.reg_addr;
+
+ data = ioread32(regaddr);
+ data >>= zx29clk->clk_sel_reg.reg_bit_offset;
+ data &= (1<<(zx29clk->clk_sel_reg.reg_bit_size))-1;
+
+ return data;
+}
+
+/*
+ * calc the rate we can support.
+ *
+ * rate: request rate(maybe we can not support exactly)
+ */
+static long zx297520v2_clk_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long * best_parent_rate)
+{
+ unsigned long parent_rate = 0;
+ unsigned long clk_div = 0;
+ struct zx29_hwclk *zx29clk = to_zx29_hwclk(hw);
+
+ if(zx29clk->hw.clk == NULL || zx29clk->hw.clk->parent == NULL)
+ return 0;
+// if(zx29clk->clk_div_reg.reg_bit_size == 0)
+// return 0;
+
+ parent_rate = zx29clk->hw.clk->parent->rate;
+ clk_div = parent_rate/rate;
+ if(clk_div >= (1<<zx29clk->clk_div_reg.reg_bit_size))
+ {
+ clk_div = (1<<zx29clk->clk_div_reg.reg_bit_size);
+ }
+
+ if(!clk_div)
+ return parent_rate;
+
+ if ((zx29clk->hw.clk->flags & CLK_NO_ODD_DIV) && (clk_div & 0x1) && (clk_div != 0x1))
+ clk_div++;
+
+ return parent_rate/(clk_div);
+}
+
+/*
+ * set auto clk gate.
+ *
+ * this only set once when system start.
+ */
+static int zx297520v2_set_auto_gate(struct clk_hw *hw, bool enable)
+{
+ unsigned int data = 0;
+ void __iomem *regaddr = NULL;
+ struct zx29_hwclk *zx29clk = to_zx29_hwclk(hw);
+
+ if(zx29clk->clk_gate_reg.reg_bit_size == 0)
+ return -EINVAL;
+
+ regaddr = zx29clk->clk_gate_reg.reg_addr;
+
+ hw_spin_lock(CLK_HWLOCK);
+ data=ioread32(regaddr);
+ if(enable)
+ data |= 1<<zx29clk->clk_gate_reg.reg_bit_offset;
+ else
+ data &= ~(1<<zx29clk->clk_gate_reg.reg_bit_offset);
+ iowrite32(data,regaddr);
+ hw_spin_unlock(CLK_HWLOCK);
+
+ return 0;
+}
+
+/**************************************************************************
+* peripheral clk divider new algorithm
+*
+* 1. default algorithm: new_rate = parent_rate/(1+clk_div)
+* 2. new algorithm: new_rate = parent_rate/(2*(integer_num+(clk_sel+fra_div/fra_base)/2))
+*
+***************************************************************************
+*/
+#define CLK_FRA_DIV(a) (zx_read_reg(a)&0xff)
+#define CLK_FRA_BASE(a) ((zx_read_reg(a)>>8)&0xff)
+#define CLK_INTEGER_NUM(a) ((zx_read_reg(a)>>16)&0xff)
+#define CLK_CLK_SEL(a) ((zx_read_reg(a)>>24)&1)
+
+/*
+{2.0, MIX_CLK_DIVIDER(0, 0xff, 1, 0) },
+{2.5, MIX_CLK_DIVIDER(0x7f, 0xff, 1, 0) },
+{3.0, MIX_CLK_DIVIDER(0, 0xff, 1, 1) },
+{3.5, MIX_CLK_DIVIDER(0x7f, 0xff, 1, 1) },
+*/
+/* d--fra_div b--fra_base n--integer_num s--clk_sel */
+#define MIX_CLK_DIVIDER(d,b,n,s) \
+ (d|(b<<8)|(n<<16)|(s<<24))
+
+#define MAX_CLK_DIVIDER 0x200//(2.0*(0xff+(1.0+0xff/0xff)/2.0))
+#define FLOAT_CALC_FACTOR (u64)(1000*1000)
+
+static void parase_float_clk_divider(u64 clk_div,
+ unsigned int *fra_div,
+ unsigned int *fra_base,
+ unsigned int *integer_num,
+ unsigned int *clk_sel)
+{
+ *integer_num = (unsigned int)div64_u64(clk_div, FLOAT_CALC_FACTOR)/2;
+ *clk_sel = (unsigned int)div64_u64(clk_div, FLOAT_CALC_FACTOR)%2;
+ *fra_base = 0xff;
+ *fra_div = (unsigned int)div64_u64((clk_div-*integer_num*FLOAT_CALC_FACTOR*2-*clk_sel*FLOAT_CALC_FACTOR)*255,
+ FLOAT_CALC_FACTOR);
+}
+
+/*
+ * set the clk division for new rate.
+ *
+ */
+static int zx297520v2_clk_set_rate_float(struct clk_hw *hw, unsigned long new_rate)
+{
+ struct zx29_hwclk *zx29clk = to_zx29_hwclk(hw);
+ unsigned int fra_div = 0;
+ unsigned int fra_base = 0;
+ unsigned int integer_num = 0;
+ unsigned int clk_sel = 0;
+ u64 tmp_parent_rate;
+ u64 clk_div = 0;
+
+ if(zx29clk->hw.clk == NULL || zx29clk->hw.clk->parent == NULL)
+ return -EINVAL;
+ if(zx29clk->clk_div_reg.reg_addr == 0)
+ return -EINVAL;
+
+ tmp_parent_rate = zx29clk->hw.clk->parent->rate*FLOAT_CALC_FACTOR;
+ clk_div = div64_u64(tmp_parent_rate, new_rate);
+
+ /*get the max division*/
+ if(clk_div >= MAX_CLK_DIVIDER*FLOAT_CALC_FACTOR)
+ clk_div = MAX_CLK_DIVIDER*FLOAT_CALC_FACTOR;
+
+ parase_float_clk_divider(clk_div, &fra_div, &fra_base, &integer_num, &clk_sel);
+
+ hw_spin_lock(CLK_HWLOCK);
+ zx_write_reg(zx29clk->clk_div_reg.reg_addr,
+ MIX_CLK_DIVIDER(fra_div, fra_base, integer_num, clk_sel));
+ hw_spin_unlock(CLK_HWLOCK);
+
+ return 0;
+}
+
+/*
+ * get the clk's rate.
+ *
+ * new_rate = parent_rate/(2*(integer_num+(clk_sel+fra_div/fra_base)/2))
+ */
+static unsigned long zx297520v2_clk_recalc_rate_float(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ void __iomem *regaddr = NULL;
+ struct zx29_hwclk *zx29clk = to_zx29_hwclk(hw);
+ unsigned int fra_div = 0;
+ unsigned int fra_base = 0;
+ unsigned int clk_sel = 0;
+ unsigned int integer_num = 0;
+ u64 tmp_parent_rate = (u64)parent_rate*FLOAT_CALC_FACTOR;
+ u64 clk_div = 0;
+
+ regaddr = zx29clk->clk_div_reg.reg_addr;
+
+ /*if no clk division, return parent's rate */
+ if(regaddr == 0) {
+ if(zx29clk->hw.clk->num_parents)
+ return parent_rate;
+ else
+ return 0;
+ }
+
+ fra_div = CLK_FRA_DIV(regaddr);
+ fra_base = CLK_FRA_BASE(regaddr);
+ clk_sel = CLK_CLK_SEL(regaddr);
+ integer_num = CLK_INTEGER_NUM(regaddr);
+
+ clk_div = (2*integer_num+clk_sel)*FLOAT_CALC_FACTOR+div64_u64(fra_div*FLOAT_CALC_FACTOR,fra_base);
+
+ return (unsigned long)div64_u64(tmp_parent_rate, clk_div);
+}
+
+/*
+ * calc the rate we can support.
+ *
+ * rate: request rate(maybe we can not support exactly)
+ */
+static long zx297520v2_clk_round_rate_float(struct clk_hw *hw, unsigned long rate,
+ unsigned long * best_parent_rate)
+{
+ struct zx29_hwclk *zx29clk = to_zx29_hwclk(hw);
+ u64 tmp_parent_rate;
+ u64 clk_div = 0;
+
+ if(zx29clk->hw.clk == NULL || zx29clk->hw.clk->parent == NULL)
+ return 0;
+ if(zx29clk->clk_div_reg.reg_addr == 0)
+ return 0;
+
+ tmp_parent_rate = zx29clk->hw.clk->parent->rate*FLOAT_CALC_FACTOR;
+ clk_div = div64_u64(tmp_parent_rate, rate);
+
+ if(clk_div >= MAX_CLK_DIVIDER*FLOAT_CALC_FACTOR)
+ clk_div = MAX_CLK_DIVIDER*FLOAT_CALC_FACTOR;
+
+ if(!clk_div)
+ return (long)div64_u64(tmp_parent_rate, FLOAT_CALC_FACTOR);
+ else
+ return (long)div64_u64(tmp_parent_rate, clk_div);
+}
+
+static int clk_rootclk_enable(struct clk_hw *hw)
+{
+ return 0;
+}
+static void clk_rootclk_disable(struct clk_hw *hw)
+{
+ return ;
+}
+
+static int clk_rootclk_is_enabled(struct clk_hw *hw)
+{
+ return 1;
+}
+
+static int clk_rootclk_set_rate(struct clk_hw *hw, unsigned long new_rate)//, unsigned long parent_rate)
+{
+ return 0;
+}
+
+static struct clk_ops peripheral_clk_ops =
+{
+ .enable = zx297520v2_clk_enable,
+ .disable = zx297520v2_clk_disable,
+ .set_auto_gate = zx297520v2_set_auto_gate,
+ .set_rate = zx297520v2_clk_set_rate,
+ .recalc_rate = zx297520v2_clk_recalc_rate,
+ .set_parent = zx297520v2_clk_set_parent,
+ .round_rate = zx297520v2_clk_round_rate,
+ .get_parent = zx297520v2_get_parent,
+ .is_enabled = zx297520v2_is_enabled,
+};
+
+static struct clk_ops peripheral_float_clk_ops =
+{
+ .enable = zx297520v2_clk_enable,
+ .disable = zx297520v2_clk_disable,
+ .set_auto_gate = zx297520v2_set_auto_gate,
+ .set_rate = zx297520v2_clk_set_rate_float,
+ .recalc_rate = zx297520v2_clk_recalc_rate_float,
+ .set_parent = zx297520v2_clk_set_parent,
+ .round_rate = zx297520v2_clk_round_rate_float,
+ .get_parent = zx297520v2_get_parent,
+ .is_enabled = zx297520v2_is_enabled,
+};
+
+static struct clk_ops cpu_clk_ops =
+{
+ .enable = zx297520v2_clk_enable,
+ .disable = zx297520v2_clk_disable,
+ .set_auto_gate = zx297520v2_set_auto_gate,
+ .set_rate = zx297520v2_clk_set_rate,
+ .recalc_rate = zx297520v2_clk_recalc_rate,
+ .set_parent = zx297520v2_clk_set_parent,
+ .round_rate = zx297520v2_clk_round_rate,
+ .get_parent = zx297520v2_get_parent,
+ .is_enabled = zx297520v2_is_enabled,
+};
+
+struct clk_ops root_clk_ops = {
+ .enable = clk_rootclk_enable,
+ .disable = clk_rootclk_disable,
+ .is_enabled = clk_rootclk_is_enabled,
+ .set_rate = clk_rootclk_set_rate,
+};
+
+
+/*
+* root clk
+*/
+DEFINE_ZX29_ROOT_CLK(main_clk_32k, 32768);
+DEFINE_ZX29_ROOT_CLK(mpll_clk_12m, 12*1000*1000);
+DEFINE_ZX29_ROOT_CLK(mpll_clk_39m, 39*1000*1000);
+DEFINE_ZX29_ROOT_CLK(mpll_clk_52m, 52*1000*1000);
+DEFINE_ZX29_ROOT_CLK(mpll_clk_78m, 78*1000*1000);
+#if CONFIG_ARCH_ZX297520V2FPGA
+DEFINE_ZX29_ROOT_CLK(main_clk_26m, 25*1000*1000);
+DEFINE_ZX29_ROOT_CLK(mpll_clk_104m, 25*1000*1000);
+#else
+DEFINE_ZX29_ROOT_CLK(main_clk_26m, 26*1000*1000);
+DEFINE_ZX29_ROOT_CLK(mpll_clk_104m, 104*1000*1000);
+#endif
+DEFINE_ZX29_ROOT_CLK(mpll_clk_124m8, 124800*1000);
+DEFINE_ZX29_ROOT_CLK(mpll_clk_156m, 156*1000*1000);
+DEFINE_ZX29_ROOT_CLK(mpll_clk_208m, 208*1000*1000);
+DEFINE_ZX29_ROOT_CLK(mpll_clk_312m, 312*1000*1000);
+DEFINE_ZX29_ROOT_CLK(mpll_clk_624m, 624*1000*1000);
+DEFINE_ZX29_ROOT_CLK(dpll_clk_491m52, 491520*1000);
+DEFINE_ZX29_ROOT_CLK(dpll_clk_122m88, 122880*1000);
+DEFINE_ZX29_ROOT_CLK(dpll_clk_81m92, 81920*1000);
+DEFINE_ZX29_ROOT_CLK(upll_clk_480m, 480*1000*1000);
+DEFINE_ZX29_ROOT_CLK(upll_clk_96m, 96*1000*1000);
+DEFINE_ZX29_ROOT_CLK(gpll_clk_25m, 25*1000*1000);
+DEFINE_ZX29_ROOT_CLK(gpll_clk_50m, 50*1000*1000);
+DEFINE_ZX29_ROOT_CLK(gpll_clk_100m, 100*1000*1000);
+DEFINE_ZX29_ROOT_CLK(gpll_clk_178m, 178*1000*1000);
+DEFINE_ZX29_ROOT_CLK(gpll_clk_200m, 200*1000*1000);
+
+
+/*
+* a1_uart0
+*/
+char* a1_uart_wclk_parents[2] =
+{
+ NAME_CLK(mpll_clk_104m),
+ NAME_CLK(main_clk_26m)
+};
+char* lsp_uart_wclk_parents[2] =
+{
+ NAME_CLK(main_clk_26m),
+ NAME_CLK(mpll_clk_104m)
+};
+static struct zx29_hwclk uart0_apb =
+{
+ .clk_en_reg ={ZX_TOP_CRM_BASE+0x5C, 13, 1},
+ .clk_gate_reg ={ZX_TOP_CRM_BASE+0x5C, 14, 1},
+};
+DEFINE_ZX29_CLK(uart0_apb,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk uart0_work =
+{
+ .clk_en_reg ={ZX_TOP_CRM_BASE+0x5C, 12, 1},
+ .clk_sel_reg ={ZX_TOP_CRM_BASE+0x40, 2, 1},
+ .clk_div_reg ={ZX_TOP_CRM_BASE+0x40, 0, 0},
+ .clk_gate_reg ={ZX_TOP_CRM_BASE+0x5C, 15, 1},
+};
+DEFINE_ZX29_CLK(uart0_work,peripheral_clk_ops,2,a1_uart_wclk_parents);
+
+/*
+* lsp_uart1
+*/
+static struct zx29_hwclk uart1_apb =
+{
+ .clk_en_reg ={ZX_LSP_CRPM_BASE+0x28, 0, 1},
+ .clk_div_reg ={ZX_LSP_CRPM_BASE+0x28, 0, 0},
+ .clk_gate_reg ={ZX_LSP_CRPM_BASE+0x28, 11, 1},
+};
+DEFINE_ZX29_CLK(uart1_apb,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk uart1_work =
+{
+ .clk_en_reg ={ZX_LSP_CRPM_BASE+0x28, 1, 1},
+ .clk_sel_reg ={ZX_LSP_CRPM_BASE+0x28, 4, 1},
+ .clk_div_reg ={ZX_LSP_CRPM_BASE+0x28, 0, 0},
+ .clk_gate_reg ={ZX_LSP_CRPM_BASE+0x28, 10, 1},
+};
+DEFINE_ZX29_CLK(uart1_work,peripheral_clk_ops,2,lsp_uart_wclk_parents);
+
+/*
+* lsp_uart2
+*/
+static struct zx29_hwclk uart2_apb =
+{
+ .clk_en_reg ={ZX_LSP_CRPM_BASE+0x3c, 0, 1},
+ .clk_div_reg ={ZX_LSP_CRPM_BASE+0x3c, 0, 0},
+ .clk_gate_reg ={ZX_LSP_CRPM_BASE+0x3c, 11, 1},
+};
+DEFINE_ZX29_CLK(uart2_apb,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk uart2_work =
+{
+ .clk_en_reg ={ZX_LSP_CRPM_BASE+0x3c, 1, 1},
+ .clk_sel_reg ={ZX_LSP_CRPM_BASE+0x3c, 4, 1},
+ .clk_div_reg ={ZX_LSP_CRPM_BASE+0x3c, 0, 0},
+ .clk_gate_reg ={ZX_LSP_CRPM_BASE+0x3c, 10, 1},
+};
+DEFINE_ZX29_CLK(uart2_work,peripheral_clk_ops,2,lsp_uart_wclk_parents);
+
+/*
+* a1_timer1
+*/
+char* a1_timer_wclk_parents[2] =
+{
+ NAME_CLK(main_clk_26m),
+ NAME_CLK(main_clk_32k)
+};
+char* lsp_timer_wclk_parents[2] =
+{
+ NAME_CLK(main_clk_32k),
+ NAME_CLK(main_clk_26m)
+};
+static struct zx29_hwclk timer1_apb =
+{
+ .clk_en_reg ={ZX_TOP_CRM_BASE+0x60, 1, 1},
+ .clk_gate_reg ={ZX_TOP_CRM_BASE+0x60, 2, 1},
+};
+DEFINE_ZX29_CLK(timer1_apb,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk timer1_work =
+{
+ .clk_en_reg ={ZX_TOP_CRM_BASE+0x60, 0, 1},
+ .clk_sel_reg ={ZX_TOP_CRM_BASE+0x44, 0, 1},
+ .clk_div_reg ={ZX_TOP_CRM_BASE+0x50, 0, 4},
+};
+DEFINE_ZX29_CLK(timer1_work,peripheral_clk_ops,2,a1_timer_wclk_parents);
+
+/*
+* a1_timer2
+*/
+static struct zx29_hwclk timer2_apb =
+{
+ .clk_en_reg ={ZX_TOP_CRM_BASE+0x60, 5, 1},
+ .clk_gate_reg ={ZX_TOP_CRM_BASE+0x60, 6, 1},
+};
+DEFINE_ZX29_CLK(timer2_apb,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk timer2_work =
+{
+ .clk_en_reg ={ZX_TOP_CRM_BASE+0x60, 4, 1},
+ .clk_sel_reg ={ZX_TOP_CRM_BASE+0x44, 1, 1},
+ .clk_div_reg ={ZX_TOP_CRM_BASE+0x50, 4, 4},
+};
+DEFINE_ZX29_CLK(timer2_work,peripheral_clk_ops,2,a1_timer_wclk_parents);
+
+/*
+* a1_timer3
+*/
+static struct zx29_hwclk timer3_apb =
+{
+ .clk_en_reg ={ZX_TOP_CRM_BASE+0x60, 9, 1},
+ .clk_gate_reg ={ZX_TOP_CRM_BASE+0x60, 10, 1},
+};
+DEFINE_ZX29_CLK(timer3_apb,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk timer3_work =
+{
+ .clk_en_reg ={ZX_TOP_CRM_BASE+0x60, 8, 1},
+ .clk_sel_reg ={ZX_TOP_CRM_BASE+0x44, 2, 1},
+ .clk_div_reg ={ZX_TOP_CRM_BASE+0x50, 8, 4},
+};
+DEFINE_ZX29_CLK(timer3_work,peripheral_clk_ops,2,a1_timer_wclk_parents);
+
+/*
+* lsp_timer0
+*/
+static struct zx29_hwclk timer0_apb =
+{
+ .clk_en_reg ={ZX_LSP_CRPM_BASE+0x44, 0, 1},
+ .clk_gate_reg ={ZX_LSP_CRPM_BASE+0x44, 11, 1},
+};
+DEFINE_ZX29_CLK(timer0_apb,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk timer0_work =
+{
+ .clk_en_reg ={ZX_LSP_CRPM_BASE+0x44, 1, 1},
+ .clk_sel_reg ={ZX_LSP_CRPM_BASE+0x44, 4, 1},
+ .clk_div_reg ={ZX_LSP_CRPM_BASE+0x44, 12, 4},
+};
+DEFINE_ZX29_CLK(timer0_work,peripheral_clk_ops,2,lsp_timer_wclk_parents);
+
+/*
+* lsp_timer4
+*/
+static struct zx29_hwclk timer4_apb =
+{
+ .clk_en_reg ={ZX_LSP_CRPM_BASE+0x4C, 0, 1},
+ .clk_gate_reg ={ZX_LSP_CRPM_BASE+0x4C, 11, 1},
+};
+DEFINE_ZX29_CLK(timer4_apb,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk timer4_work =
+{
+ .clk_en_reg ={ZX_LSP_CRPM_BASE+0x4C, 1, 1},
+ .clk_sel_reg ={ZX_LSP_CRPM_BASE+0x4C, 4, 1},
+ .clk_div_reg ={ZX_LSP_CRPM_BASE+0x4C, 12, 4},
+};
+DEFINE_ZX29_CLK(timer4_work,peripheral_clk_ops,2,lsp_timer_wclk_parents);
+
+
+/**************************************************************************
+* i2c
+***************************************************************************
+*/
+char* a1_i2c_wclk_parents[2] =
+{
+ NAME_CLK(mpll_clk_104m),
+ NAME_CLK(main_clk_26m)
+};
+char* lsp_i2c_wclk_parents[2] =
+{
+ NAME_CLK(main_clk_26m),
+ NAME_CLK(mpll_clk_104m)
+};
+
+/*
+* a1_i2c0
+*/
+static struct zx29_hwclk i2c0_apb =
+{
+ .clk_en_reg ={ZX_TOP_CRM_BASE+0x54, 8, 1},
+ .clk_gate_reg ={ZX_TOP_CRM_BASE+0x54, 10, 1},
+};
+DEFINE_ZX29_CLK(i2c0_apb,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk i2c0_work =
+{
+ .clk_en_reg ={ZX_TOP_CRM_BASE+0x54, 9, 1},
+ .clk_sel_reg ={ZX_TOP_CRM_BASE+0x3C, 1, 1},
+ .clk_gate_reg ={ZX_TOP_CRM_BASE+0x54, 11, 1},
+};
+DEFINE_ZX29_CLK(i2c0_work,peripheral_clk_ops,2,a1_i2c_wclk_parents);
+/*
+* lsp_i2c1
+*/
+static struct zx29_hwclk i2c1_apb =
+{
+ .clk_en_reg ={ZX_LSP_CRPM_BASE+0x2C, 0, 1},
+ .clk_div_reg ={ZX_LSP_CRPM_BASE+0x2C, 0, 0},
+ .clk_gate_reg ={ZX_LSP_CRPM_BASE+0x2C, 11, 1},
+};
+DEFINE_ZX29_CLK(i2c1_apb,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk i2c1_work =
+{
+ .clk_en_reg ={ZX_LSP_CRPM_BASE+0x2C, 1, 1},
+ .clk_sel_reg ={ZX_LSP_CRPM_BASE+0x2C, 4, 1},
+ .clk_div_reg ={ZX_LSP_CRPM_BASE+0x2C, 0, 0},
+ .clk_gate_reg ={ZX_LSP_CRPM_BASE+0x2C, 10, 1},
+};
+DEFINE_ZX29_CLK(i2c1_work,peripheral_clk_ops,2,lsp_i2c_wclk_parents);
+
+/**************************************************************************
+* ssp
+***************************************************************************
+*/
+char* lsp_ssp_wclk_parents[3] =
+{
+ NAME_CLK(mpll_clk_156m),
+ NAME_CLK(mpll_clk_104m),
+ NAME_CLK(main_clk_26m)
+};
+
+/*
+* lsp_ssp0
+*/
+static struct zx29_hwclk ssp0_apb =
+{
+ .clk_en_reg ={ZX_LSP_CRPM_BASE+0x30, 0, 1},
+ .clk_div_reg ={ZX_LSP_CRPM_BASE+0x30, 0, 0},
+ .clk_gate_reg ={ZX_LSP_CRPM_BASE+0x30, 11, 1},
+};
+DEFINE_ZX29_CLK(ssp0_apb,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk ssp0_work =
+{
+ .clk_en_reg ={ZX_LSP_CRPM_BASE+0x30, 1, 1},
+ .clk_sel_reg ={ZX_LSP_CRPM_BASE+0x30, 4, 2},
+ .clk_div_reg ={ZX_LSP_CRPM_BASE+0x30, 12, 4},
+ .clk_gate_reg ={ZX_LSP_CRPM_BASE+0x30, 10, 1},
+};
+DEFINE_ZX29_CLK_FLAG(ssp0_work,peripheral_clk_ops,3,lsp_ssp_wclk_parents, (CLK_AUTO_ROUND_PARENT | CLK_NO_ODD_DIV));
+/*
+* lsp_ssp1
+*/
+static struct zx29_hwclk ssp1_apb =
+{
+ .clk_en_reg ={ZX_LSP_CRPM_BASE+0x48, 0, 1},
+ .clk_div_reg ={ZX_LSP_CRPM_BASE+0x48, 0, 0},
+ .clk_gate_reg ={ZX_LSP_CRPM_BASE+0x48, 11, 1},
+};
+DEFINE_ZX29_CLK(ssp1_apb,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk ssp1_work =
+{
+ .clk_en_reg ={ZX_LSP_CRPM_BASE+0x48, 1, 1},
+ .clk_sel_reg ={ZX_LSP_CRPM_BASE+0x48, 4, 2},
+ .clk_div_reg ={ZX_LSP_CRPM_BASE+0x48, 12, 4},
+ .clk_gate_reg ={ZX_LSP_CRPM_BASE+0x48, 10, 1},
+};
+DEFINE_ZX29_CLK_FLAG(ssp1_work,peripheral_clk_ops,3,lsp_ssp_wclk_parents, (CLK_AUTO_ROUND_PARENT | CLK_NO_ODD_DIV));
+
+/**************************************************************************
+* ap wdt
+***************************************************************************
+*/
+char* lsp_wdt_wclk_parents[2] =
+{
+ NAME_CLK(main_clk_32k),
+ NAME_CLK(main_clk_26m)
+};
+
+static struct zx29_hwclk ap_wdt_apb =
+{
+ .clk_en_reg ={ZX_LSP_CRPM_BASE+0x40, 0, 1},
+ .clk_div_reg ={ZX_LSP_CRPM_BASE+0x40, 0, 0},
+ .clk_gate_reg ={ZX_LSP_CRPM_BASE+0x40, 11, 1},
+};
+DEFINE_ZX29_CLK(ap_wdt_apb,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk ap_wdt_work =
+{
+ .clk_en_reg ={ZX_LSP_CRPM_BASE+0x40, 1, 1},
+ .clk_sel_reg ={ZX_LSP_CRPM_BASE+0x40, 4, 1},
+ .clk_div_reg ={ZX_LSP_CRPM_BASE+0x40, 12, 4},
+ .clk_gate_reg ={ZX_LSP_CRPM_BASE+0x40, 10, 1},
+};
+DEFINE_ZX29_CLK(ap_wdt_work,peripheral_clk_ops,2,lsp_wdt_wclk_parents);
+
+/**************************************************************************
+* tdm
+***************************************************************************
+*/
+char* lsp_tdm_wclk_parents[3] =
+{
+ NAME_CLK(dpll_clk_81m92),
+ NAME_CLK(main_clk_26m),
+ NAME_CLK(mpll_clk_104m)
+};
+
+static struct zx29_hwclk tdm_apb =
+{
+ .clk_en_reg ={ZX_LSP_CRPM_BASE+0x50, 0, 1},
+ .clk_div_reg ={ZX_LSP_CRPM_BASE+0x50, 0, 0},
+ .clk_gate_reg ={ZX_LSP_CRPM_BASE+0x50, 11, 1},
+};
+DEFINE_ZX29_CLK(tdm_apb,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk tdm_work =
+{
+ .clk_en_reg ={ZX_LSP_CRPM_BASE+0x50, 1, 1},
+ .clk_sel_reg ={ZX_MATRIX_CRM_BASE+0x50, 0, 0},
+ .clk_div_reg ={ZX_LSP_CRPM_BASE+0x54, 24, 2},
+ .clk_gate_reg ={ZX_LSP_CRPM_BASE+0x50, 10, 1},
+};
+DEFINE_ZX29_CLK(tdm_work,peripheral_float_clk_ops,3,lsp_tdm_wclk_parents);
+
+/**************************************************************************
+* i2s
+***************************************************************************
+*/
+char* lsp_i2s_wclk_parents[2] =
+{
+ NAME_CLK(main_clk_26m),
+ NAME_CLK(mpll_clk_104m)
+};
+
+static struct zx29_hwclk i2s0_apb =
+{
+ .clk_en_reg ={ZX_LSP_CRPM_BASE+0x14, 0, 1},
+ .clk_div_reg ={ZX_LSP_CRPM_BASE+0x14, 0, 0},
+ .clk_gate_reg ={ZX_LSP_CRPM_BASE+0x14, 11, 1},
+};
+DEFINE_ZX29_CLK(i2s0_apb,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk i2s0_work =
+{
+ .clk_en_reg ={ZX_LSP_CRPM_BASE+0x14, 1, 1},
+ .clk_sel_reg ={ZX_LSP_CRPM_BASE+0x14, 4, 1},
+ .clk_div_reg ={ZX_LSP_CRPM_BASE+0x18, 0, 2},
+ .clk_gate_reg ={ZX_LSP_CRPM_BASE+0x14, 10, 1},
+};
+DEFINE_ZX29_CLK(i2s0_work,peripheral_float_clk_ops,2,lsp_i2s_wclk_parents);
+
+static struct zx29_hwclk i2s1_apb =
+{
+ .clk_en_reg ={ZX_LSP_CRPM_BASE+0x1C, 0, 1},
+ .clk_div_reg ={ZX_LSP_CRPM_BASE+0x1C, 0, 0},
+ .clk_gate_reg ={ZX_LSP_CRPM_BASE+0x1C, 11, 1},
+};
+DEFINE_ZX29_CLK(i2s1_apb,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk i2s1_work =
+{
+ .clk_en_reg ={ZX_LSP_CRPM_BASE+0x1C, 1, 1},
+ .clk_sel_reg ={ZX_LSP_CRPM_BASE+0x1C, 4, 1},
+ .clk_div_reg ={ZX_LSP_CRPM_BASE+0x20, 0, 2},
+ .clk_gate_reg ={ZX_LSP_CRPM_BASE+0x1C, 10, 1},
+};
+DEFINE_ZX29_CLK(i2s1_work,peripheral_float_clk_ops,2,lsp_i2s_wclk_parents);
+
+/**************************************************************************
+* spifc
+***************************************************************************
+*/
+char* lsp_spifc_wclk_parents[] =
+{
+ NAME_CLK(mpll_clk_156m),
+ NAME_CLK(main_clk_26m),
+ NAME_CLK(mpll_clk_124m8),
+ NAME_CLK(mpll_clk_104m),
+ NAME_CLK(mpll_clk_78m),
+ NAME_CLK(mpll_clk_52m)
+};
+
+static struct zx29_hwclk spifc_apb =
+{
+ .clk_en_reg ={ZX_LSP_CRPM_BASE+0x24, 0, 1},
+ .clk_div_reg ={ZX_LSP_CRPM_BASE+0x24, 0, 0},
+ .clk_gate_reg ={ZX_LSP_CRPM_BASE+0x24, 11, 1},
+};
+DEFINE_ZX29_CLK(spifc_apb,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk spifc_work =
+{
+ .clk_en_reg ={ZX_LSP_CRPM_BASE+0x24, 1, 1},
+ .clk_sel_reg ={ZX_LSP_CRPM_BASE+0x24, 4, 3},
+ .clk_div_reg ={ZX_LSP_CRPM_BASE+0x24, 0, 0},
+ .clk_gate_reg ={ZX_LSP_CRPM_BASE+0x24, 10, 1},
+};
+DEFINE_ZX29_CLK(spifc_work,peripheral_clk_ops,ARRAY_SIZE(lsp_spifc_wclk_parents),lsp_spifc_wclk_parents);
+
+/**************************************************************************
+* kpd
+***************************************************************************
+*/
+char* kpd_wclk_parents[] =
+{
+ NAME_CLK(main_clk_32k),
+};
+
+static struct zx29_hwclk kpd_apb =
+{
+ .clk_en_reg ={ZX_TOP_CRM_BASE+0x5C, 17, 1},
+ .clk_div_reg ={ZX_TOP_CRM_BASE+0x5C, 0, 0},
+ .clk_gate_reg ={ZX_TOP_CRM_BASE+0x5C, 19, 1},
+};
+DEFINE_ZX29_CLK(kpd_apb,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk kpd_work =
+{
+ .clk_en_reg ={ZX_TOP_CRM_BASE+0x5C, 16, 1},
+ .clk_sel_reg ={ZX_TOP_CRM_BASE+0x5C, 0, 0},
+ .clk_div_reg ={ZX_TOP_CRM_BASE+0x5C, 0, 0},
+ .clk_gate_reg ={ZX_TOP_CRM_BASE+0x5C, 18, 1},
+};
+DEFINE_ZX29_CLK(kpd_work,peripheral_clk_ops,ARRAY_SIZE(kpd_wclk_parents),kpd_wclk_parents);
+
+/**************************************************************************
+* rtc
+***************************************************************************
+*/
+char* rtc_wclk_parents[] =
+{
+ NAME_CLK(main_clk_32k),
+};
+
+static struct zx29_hwclk rtc_apb =
+{
+ .clk_en_reg ={ZX_TOP_CRM_BASE+0x54, 0, 1},
+
+};
+DEFINE_ZX29_CLK(rtc_apb,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk rtc_work =
+{
+ .clk_en_reg ={ZX_TOP_CRM_BASE+0x54, 1, 1},
+};
+DEFINE_ZX29_CLK(rtc_work,peripheral_clk_ops,ARRAY_SIZE(rtc_wclk_parents),rtc_wclk_parents);
+
+/**************************************************************************
+* hs_ahb (parent of hsic/usb ahb clk)
+***************************************************************************
+*/
+char* hs_ahb_parents[] =
+{
+ NAME_CLK(mpll_clk_104m),
+ NAME_CLK(main_clk_26m),
+ NAME_CLK(mpll_clk_78m),
+ NAME_CLK(mpll_clk_52m),
+};
+static struct zx29_hwclk hs_ahb =
+{
+/* .clk_en_reg ={ZX_TOP_CRM_BASE+0x54, 12, 1}, */
+ .clk_sel_reg ={ZX_TOP_CRM_BASE+0x3C, 4, 2},
+};
+DEFINE_ZX29_CLK(hs_ahb,peripheral_clk_ops,ARRAY_SIZE(hs_ahb_parents),hs_ahb_parents);
+
+/**************************************************************************
+* usb2.0
+***************************************************************************
+*/
+char* usb_12m_parents[] =
+{
+ NAME_CLK(mpll_clk_12m),
+};
+
+char* usb_480m_parents[] =
+{
+ NAME_CLK(upll_clk_480m),
+};
+
+char* usb_ahb_parents[] =
+{
+ "hs_ahb_clk",
+};
+static struct zx29_hwclk usb_ahb =
+{
+ .clk_en_reg ={ZX_TOP_CRM_BASE+0x6C, 4, 1},
+};
+DEFINE_ZX29_CLK(usb_ahb,peripheral_clk_ops,ARRAY_SIZE(usb_ahb_parents),usb_ahb_parents);
+
+static struct zx29_hwclk usb_12m =
+{
+ .clk_en_reg ={ZX_TOP_CRM_BASE+0x6C, 3, 1},
+};
+DEFINE_ZX29_CLK(usb_12m,peripheral_clk_ops,ARRAY_SIZE(usb_12m_parents),usb_12m_parents);
+
+/**************************************************************************
+ * HSIC
+ **************************************************************************
+ */
+static struct zx29_hwclk hsic_ahb =
+{
+ .clk_en_reg ={ZX_TOP_CRM_BASE+0x6C, 2, 1},
+};
+DEFINE_ZX29_CLK(hsic_ahb,peripheral_clk_ops,ARRAY_SIZE(usb_ahb_parents),usb_ahb_parents);
+
+static struct zx29_hwclk hsic_12m =
+{
+ .clk_en_reg ={ZX_TOP_CRM_BASE+0x6C, 1, 1},
+};
+DEFINE_ZX29_CLK(hsic_12m,peripheral_clk_ops,ARRAY_SIZE(usb_12m_parents),usb_12m_parents);
+
+static struct zx29_hwclk hsic_480m =
+{
+ .clk_en_reg ={ZX_TOP_CRM_BASE+0x6C, 0, 1},
+};
+DEFINE_ZX29_CLK(hsic_480m,peripheral_clk_ops,ARRAY_SIZE(usb_480m_parents),usb_480m_parents);
+
+/**************************************************************************
+ * GMAC
+ **************************************************************************
+ */
+char* gmac_rmii_parents[] =
+{
+ NAME_CLK(gpll_clk_50m),
+};
+
+static struct zx29_hwclk gmac_ahb =
+{
+ .clk_en_reg ={ZX_MATRIX_CRM_BASE+0x110, 2, 1},
+};
+DEFINE_ZX29_CLK(gmac_ahb,peripheral_clk_ops,ARRAY_SIZE(usb_ahb_parents),usb_ahb_parents);
+
+static struct zx29_hwclk gmac_apb =
+{
+ .clk_en_reg ={ZX_MATRIX_CRM_BASE+0x110, 1, 1},
+};
+DEFINE_ZX29_CLK(gmac_apb,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk gmac_rmii =
+{
+ .clk_en_reg ={ZX_MATRIX_CRM_BASE+0x110, 0, 1},
+};
+DEFINE_ZX29_CLK(gmac_rmii,peripheral_clk_ops,ARRAY_SIZE(gmac_rmii_parents),gmac_rmii_parents);
+
+/**************************************************************************
+ * SD
+ **************************************************************************
+ */
+char* sd0_work_parents[] =
+{
+ NAME_CLK(gpll_clk_200m),
+ NAME_CLK(main_clk_26m),
+ NAME_CLK(mpll_clk_156m),
+ NAME_CLK(gpll_clk_100m),
+ NAME_CLK(mpll_clk_78m),
+ NAME_CLK(gpll_clk_50m),
+ NAME_CLK(gpll_clk_178m),
+ NAME_CLK(gpll_clk_25m),
+
+};
+char* sd1_work_parents[] =
+{
+ NAME_CLK(gpll_clk_100m),
+ NAME_CLK(main_clk_26m),
+ NAME_CLK(mpll_clk_78m),
+ NAME_CLK(gpll_clk_50m),
+ NAME_CLK(mpll_clk_39m),
+ NAME_CLK(gpll_clk_25m),
+};
+char* sd_cdet_parents[] =
+{
+ NAME_CLK(main_clk_32k),
+};
+static struct zx29_hwclk sd0_ahb =
+{
+ .clk_en_reg ={ZX_MATRIX_CRM_BASE+0x54, 12, 1},
+ .clk_gate_reg ={ZX_MATRIX_CRM_BASE+0x54, 16, 1},
+};
+DEFINE_ZX29_CLK(sd0_ahb,peripheral_clk_ops,ARRAY_SIZE(usb_ahb_parents),usb_ahb_parents);
+
+static struct zx29_hwclk sd0_work =
+{
+ .clk_en_reg ={ZX_MATRIX_CRM_BASE+0x54, 13, 1},
+ .clk_sel_reg ={ZX_MATRIX_CRM_BASE+0x50, 4, 3},
+ .clk_gate_reg ={ZX_MATRIX_CRM_BASE+0x54, 17, 1},
+};
+DEFINE_ZX29_CLK(sd0_work,peripheral_clk_ops,ARRAY_SIZE(sd0_work_parents),sd0_work_parents);
+
+static struct zx29_hwclk sd0_cdet =
+{
+ .clk_en_reg ={ZX_MATRIX_CRM_BASE+0x54, 14, 1},
+};
+DEFINE_ZX29_CLK(sd0_cdet,peripheral_clk_ops,ARRAY_SIZE(sd_cdet_parents),sd_cdet_parents);
+
+static struct zx29_hwclk sd1_ahb =
+{
+ .clk_en_reg ={ZX_MATRIX_CRM_BASE+0x54, 4, 1},
+ .clk_gate_reg ={ZX_MATRIX_CRM_BASE+0x54, 8, 1},
+};
+DEFINE_ZX29_CLK(sd1_ahb,peripheral_clk_ops,ARRAY_SIZE(usb_ahb_parents),usb_ahb_parents);
+
+static struct zx29_hwclk sd1_work =
+{
+ .clk_en_reg ={ZX_MATRIX_CRM_BASE+0x54, 5, 1},
+ .clk_sel_reg ={ZX_MATRIX_CRM_BASE+0x50, 8, 3},
+ .clk_gate_reg ={ZX_MATRIX_CRM_BASE+0x54, 9, 1},
+};
+DEFINE_ZX29_CLK(sd1_work,peripheral_clk_ops,ARRAY_SIZE(sd1_work_parents),sd1_work_parents);
+
+static struct zx29_hwclk sd1_cdet =
+{
+ .clk_en_reg ={ZX_MATRIX_CRM_BASE+0x54, 6, 1},
+};
+DEFINE_ZX29_CLK(sd1_cdet,peripheral_clk_ops,ARRAY_SIZE(sd_cdet_parents),sd_cdet_parents);
+/*
+* axi
+*
+* can select 156/26/122.88/104/78/52/39
+* can not div
+*/
+char* axi_clk_parents[] =
+{
+ NAME_CLK(mpll_clk_156m),
+ NAME_CLK(main_clk_26m),
+ NAME_CLK(dpll_clk_122m88),
+ NAME_CLK(mpll_clk_104m),
+ NAME_CLK(mpll_clk_78m),
+ NAME_CLK(mpll_clk_52m),
+ NAME_CLK(mpll_clk_39m)
+};
+static struct zx29_hwclk axi = {
+ /* reg_addr bit_offset bit_size */
+ .clk_en_reg ={0, 0, 0},
+ .clk_sel_reg={ZX_MATRIX_CRM_BASE+0x0, 0, 3},
+ .clk_div_reg={0, 0, 0},
+};
+
+DEFINE_ZX29_CLK(axi,peripheral_clk_ops,ARRAY_SIZE(axi_clk_parents),axi_clk_parents);
+
+/*
+* ap cpu
+*
+* can select 26/52/78/104/208/312/491.52/624
+* can not div, gated with hw
+*/
+char* cpu_clk_parents[] =
+{
+ NAME_CLK(mpll_clk_624m),
+ NAME_CLK(main_clk_26m),
+ NAME_CLK(dpll_clk_491m52),
+ NAME_CLK(mpll_clk_312m),
+ NAME_CLK(mpll_clk_208m),
+ NAME_CLK(mpll_clk_104m),
+ NAME_CLK(mpll_clk_78m),
+ NAME_CLK(mpll_clk_52m)
+};
+static struct zx29_hwclk cpu_work = {
+ /* reg_addr bit_offset bit_size */
+ .clk_en_reg ={0, 0, 0},
+ .clk_sel_reg={ZX_MATRIX_CRM_BASE+0x40, 0, 3},
+ .clk_div_reg={0, 0, 0},
+};
+DEFINE_ZX29_CLK(cpu_work,cpu_clk_ops,ARRAY_SIZE(cpu_clk_parents),cpu_clk_parents);
+
+/*for zx297520v2, we do not operate pll and global resource,
+ we only support periph_clocks and clk source select */
+struct clk_lookup periph_clocks_lookups[] = {
+ /* dev_id name clk struct */
+ CLK_ZX29_CONFIG(NULL, NAME_CLK(main_clk_32k), &main_clk_32k),
+ CLK_ZX29_CONFIG(NULL, NAME_CLK(main_clk_26m), &main_clk_26m),
+ CLK_ZX29_CONFIG(NULL, NAME_CLK(mpll_clk_12m), &mpll_clk_12m),
+ CLK_ZX29_CONFIG(NULL, NAME_CLK(mpll_clk_39m), &mpll_clk_39m),
+ CLK_ZX29_CONFIG(NULL, NAME_CLK(mpll_clk_52m), &mpll_clk_52m),
+ CLK_ZX29_CONFIG(NULL, NAME_CLK(mpll_clk_78m), &mpll_clk_78m),
+ CLK_ZX29_CONFIG(NULL, NAME_CLK(mpll_clk_104m), &mpll_clk_104m),
+ CLK_ZX29_CONFIG(NULL, NAME_CLK(mpll_clk_124m8), &mpll_clk_124m8),
+ CLK_ZX29_CONFIG(NULL, NAME_CLK(mpll_clk_156m), &mpll_clk_156m),
+ CLK_ZX29_CONFIG(NULL, NAME_CLK(mpll_clk_208m), &mpll_clk_208m),
+ CLK_ZX29_CONFIG(NULL, NAME_CLK(mpll_clk_312m), &mpll_clk_312m),
+ CLK_ZX29_CONFIG(NULL, NAME_CLK(mpll_clk_624m), &mpll_clk_624m),
+ CLK_ZX29_CONFIG(NULL, NAME_CLK(dpll_clk_491m52), &dpll_clk_491m52),
+ CLK_ZX29_CONFIG(NULL, NAME_CLK(dpll_clk_122m88), &dpll_clk_122m88),
+ CLK_ZX29_CONFIG(NULL, NAME_CLK(dpll_clk_81m92), &dpll_clk_81m92),
+ CLK_ZX29_CONFIG(NULL, NAME_CLK(upll_clk_480m), &upll_clk_480m),
+ CLK_ZX29_CONFIG(NULL, NAME_CLK(upll_clk_96m), &upll_clk_96m),
+ CLK_ZX29_CONFIG(NULL, NAME_CLK(gpll_clk_25m), &gpll_clk_25m),
+ CLK_ZX29_CONFIG(NULL, NAME_CLK(gpll_clk_50m), &gpll_clk_50m),
+ CLK_ZX29_CONFIG(NULL, NAME_CLK(gpll_clk_100m), &gpll_clk_100m),
+ CLK_ZX29_CONFIG(NULL, NAME_CLK(gpll_clk_178m), &gpll_clk_178m),
+ CLK_ZX29_CONFIG(NULL, NAME_CLK(gpll_clk_200m), &gpll_clk_200m),
+
+
+ CLK_ZX29_CONFIG(NULL, "axi_clk", &axi_clk),
+
+ CLK_ZX29_CONFIG(NULL, "cpu_clk", &cpu_work_clk),
+
+ CLK_ZX29_CONFIG(NULL, "hs_ahb_clk", &hs_ahb_clk),
+
+ CLK_ZX29_CONFIG("zx29_ap_wdt.0", "work_clk", &ap_wdt_work_clk),
+ CLK_ZX29_CONFIG("zx29_ap_wdt.0", "apb_clk", &ap_wdt_apb_clk),
+
+ CLK_ZX29_CONFIG("zx29_uart.0", "work_clk", &uart0_work_clk),
+ CLK_ZX29_CONFIG("zx29_uart.0", "apb_clk", &uart0_apb_clk),
+ CLK_ZX29_CONFIG("zx29_uart.1", "work_clk", &uart1_work_clk),
+ CLK_ZX29_CONFIG("zx29_uart.1", "apb_clk", &uart1_apb_clk),
+ CLK_ZX29_CONFIG("zx29_uart.2", "work_clk", &uart2_work_clk),
+ CLK_ZX29_CONFIG("zx29_uart.2", "apb_clk", &uart2_apb_clk),
+
+ CLK_ZX29_CONFIG("zx29_ap_timer0", "work_clk", &timer0_work_clk),
+ CLK_ZX29_CONFIG("zx29_ap_timer0", "apb_clk", &timer0_apb_clk),
+ CLK_ZX29_CONFIG("zx29_ap_timer1", "work_clk", &timer1_work_clk),
+ CLK_ZX29_CONFIG("zx29_ap_timer1", "apb_clk", &timer1_apb_clk),
+ CLK_ZX29_CONFIG("zx29_ap_timer2", "work_clk", &timer2_work_clk),
+ CLK_ZX29_CONFIG("zx29_ap_timer2", "apb_clk", &timer2_apb_clk),
+ CLK_ZX29_CONFIG("zx29_ap_timer3", "work_clk", &timer3_work_clk),
+ CLK_ZX29_CONFIG("zx29_ap_timer3", "apb_clk", &timer3_apb_clk),
+ CLK_ZX29_CONFIG("zx29_ap_timer4", "work_clk", &timer4_work_clk),
+ CLK_ZX29_CONFIG("zx29_ap_timer4", "apb_clk", &timer4_apb_clk),
+
+ CLK_ZX29_CONFIG("zx29_i2c.0", "work_clk", &i2c0_work_clk),
+ CLK_ZX29_CONFIG("zx29_i2c.0", "apb_clk", &i2c0_apb_clk),
+ CLK_ZX29_CONFIG("zx29_i2c.1", "work_clk", &i2c1_work_clk),
+ CLK_ZX29_CONFIG("zx29_i2c.1", "apb_clk", &i2c1_apb_clk),
+
+ CLK_ZX29_CONFIG("zx29_ssp.0", "work_clk", &ssp0_work_clk),
+ CLK_ZX29_CONFIG("zx29_ssp.0", "apb_clk", &ssp0_apb_clk),
+ CLK_ZX29_CONFIG("zx29_ssp.1", "work_clk", &ssp1_work_clk),
+ CLK_ZX29_CONFIG("zx29_ssp.1", "apb_clk", &ssp1_apb_clk),
+
+ CLK_ZX29_CONFIG("zx29_tdm.0", "work_clk", &tdm_work_clk),
+ CLK_ZX29_CONFIG("zx29_tdm.0", "apb_clk", &tdm_apb_clk),
+
+ CLK_ZX29_CONFIG("zx29_i2s.0", "work_clk", &i2s0_work_clk),
+ CLK_ZX29_CONFIG("zx29_i2s.0", "apb_clk", &i2s0_apb_clk),
+ CLK_ZX29_CONFIG("zx29_i2s.1", "work_clk", &i2s1_work_clk),
+ CLK_ZX29_CONFIG("zx29_i2s.1", "apb_clk", &i2s1_apb_clk),
+
+ CLK_ZX29_CONFIG("zx29_spifc.0", "work_clk", &spifc_work_clk),
+ CLK_ZX29_CONFIG("zx29_spifc.0", "apb_clk", &spifc_apb_clk),
+
+ CLK_ZX29_CONFIG("zx29_kpd.0", "work_clk", &kpd_work_clk),
+ CLK_ZX29_CONFIG("zx29_kpd.0", "apb_clk", &kpd_apb_clk),
+
+ CLK_ZX29_CONFIG("zx29_rtc.0", "work_clk", &rtc_work_clk),
+ CLK_ZX29_CONFIG("zx29_rtc.0", "apb_clk", &rtc_apb_clk),
+
+ CLK_ZX29_CONFIG("zx29_usb.0", "ahb_clk", &usb_ahb_clk),
+ CLK_ZX29_CONFIG("zx29_usb.0", "12m_clk", &usb_12m_clk),
+
+ CLK_ZX29_CONFIG("zx29_hsic.0", "ahb_clk", &hsic_ahb_clk),
+ CLK_ZX29_CONFIG("zx29_hsic.0", "12m_clk", &hsic_12m_clk),
+ CLK_ZX29_CONFIG("zx29_hsic.0", "480m_clk", &hsic_480m_clk),
+
+ CLK_ZX29_CONFIG("zx29_gmac.0", "ahb_clk", &gmac_ahb_clk),
+ CLK_ZX29_CONFIG("zx29_gmac.0", "apb_clk", &gmac_apb_clk),
+ CLK_ZX29_CONFIG("zx29_gmac.0", "rmii_clk", &gmac_rmii_clk),
+
+ CLK_ZX29_CONFIG("zx29_sd.0", "ahb_clk", &sd0_ahb_clk),
+ CLK_ZX29_CONFIG("zx29_sd.0", "work_clk", &sd0_work_clk),
+ CLK_ZX29_CONFIG("zx29_sd.0", "cdet_clk", &sd0_cdet_clk),
+
+ CLK_ZX29_CONFIG("zx29_sd.1", "ahb_clk", &sd1_ahb_clk),
+ CLK_ZX29_CONFIG("zx29_sd.1", "work_clk", &sd1_work_clk),
+ CLK_ZX29_CONFIG("zx29_sd.1", "cdet_clk", &sd1_cdet_clk),
+
+ //wifi_bt
+};
+
+unsigned int periph_clocks_lookups_num=ARRAY_SIZE(periph_clocks_lookups);
+
diff --git a/ap/os/linux/linux-3.4.x/drivers/clk/zte/clk-zx297520v3.c b/ap/os/linux/linux-3.4.x/drivers/clk/zte/clk-zx297520v3.c
new file mode 100644
index 0000000..019a503
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/clk/zte/clk-zx297520v3.c
@@ -0,0 +1,1570 @@
+/*
+ * arch/arm/mach-zx297520v3/zx297520v3-clock.c
+ *
+ * Copyright (C) 2015 ZTE-TSP
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+
+#include <asm/irq.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <linux/math64.h>
+
+#include <linux/clkdev.h>
+#include <mach/spinlock.h>
+#include <mach/board.h>
+#include <mach/iomap.h>
+#include <linux/clk-private.h>
+
+#include "clk.h"
+
+#define DEFINE_ZX29_CLK(hwclk,clkops,num,names) \
+ struct clk * parents_##hwclk[num]; \
+ static struct clk hwclk##_clk = { \
+ .name = #hwclk "_clk", \
+ .hw = &hwclk.hw, \
+ .ops = &clkops, \
+ .num_parents = num, \
+ .parent_names = names, \
+ .parents = parents_##hwclk, \
+ .flags = CLK_AUTO_ROUND_PARENT, \
+}
+
+#define DEFINE_ZX29_CLK_FLAG(hwclk,clkops,num,names, flag) \
+ struct clk * parents_##hwclk[num]; \
+ static struct clk hwclk##_clk = { \
+ .name = #hwclk "_clk", \
+ .hw = &hwclk.hw, \
+ .ops = &clkops, \
+ .num_parents = num, \
+ .parent_names = names, \
+ .parents = parents_##hwclk, \
+ .flags = flag, \
+}
+
+/*
+ * enable the clk.
+ */
+static int zx297520v3_clk_enable(struct clk_hw *hw)
+{
+ unsigned int data = 0;
+ void __iomem *regaddr = NULL;
+ struct zx29_hwclk *zx29clk = to_zx29_hwclk(hw);
+
+ if(zx29clk->clk_en_reg.reg_bit_size == 0)
+ return 0;
+
+ regaddr = zx29clk->clk_en_reg.reg_addr;
+
+ hw_spin_lock(CLK_HWLOCK);
+ data=ioread32(regaddr);
+ data |= 1<<zx29clk->clk_en_reg.reg_bit_offset;
+ iowrite32(data,regaddr);
+ hw_spin_unlock(CLK_HWLOCK);
+
+ return 0;
+}
+
+/*
+ * disable the clk.
+ */
+static void zx297520v3_clk_disable(struct clk_hw *hw)
+{
+ unsigned int data = 0;
+ void __iomem *regaddr = NULL;
+ struct zx29_hwclk *zx29clk = to_zx29_hwclk(hw);
+
+ if(zx29clk->clk_en_reg.reg_bit_size == 0)
+ return ;
+
+ regaddr = zx29clk->clk_en_reg.reg_addr;
+
+ hw_spin_lock(CLK_HWLOCK);
+ data=ioread32(regaddr);
+ data &= ~(1<<zx29clk->clk_en_reg.reg_bit_offset);
+ iowrite32(data,regaddr);
+ hw_spin_unlock(CLK_HWLOCK);
+
+ return ;
+}
+
+/*
+ * get clk's enable state.
+ */
+static int zx297520v3_is_enabled(struct clk_hw *hw)
+{
+ unsigned int data = 0;
+ void __iomem *regaddr = NULL;
+ struct zx29_hwclk *zx29clk = to_zx29_hwclk(hw);
+
+ if(zx29clk->clk_en_reg.reg_bit_size == 0)
+ return 1;
+
+ regaddr = zx29clk->clk_en_reg.reg_addr;
+
+ data=ioread32(regaddr);
+ data>>=zx29clk->clk_en_reg.reg_bit_offset;
+
+ if(data & 0x1)
+ return 1;
+ else
+ return 0;
+}
+
+/*
+ * set the clk division for new rate.
+ *
+ * new_rate = parent->rate/(clk_div+1)
+ */
+static int zx297520v3_clk_set_rate(struct clk_hw *hw, unsigned long new_rate)//, unsigned long parent_rate)
+{
+ unsigned long parent_rate = 0;
+ unsigned long clk_div = 0;
+ void __iomem *regaddr = NULL;
+ unsigned int data = 0;
+ struct zx29_hwclk *zx29clk = to_zx29_hwclk(hw);
+
+ if(zx29clk->hw.clk == NULL || zx29clk->hw.clk->parent == NULL)
+ return -EINVAL;
+ if((zx29clk->clk_div_reg.reg_bit_size == 0)||(new_rate == 0))
+ return -EINVAL;
+
+ parent_rate = zx29clk->hw.clk->parent->rate;
+ clk_div = parent_rate/new_rate;
+ /*get the max division*/
+ if(clk_div >= (1<<zx29clk->clk_div_reg.reg_bit_size))
+ clk_div = (1<<zx29clk->clk_div_reg.reg_bit_size);
+
+ if(clk_div)
+ {
+ clk_div--;
+ }
+
+ regaddr = zx29clk->clk_div_reg.reg_addr;
+
+ hw_spin_lock(CLK_HWLOCK);
+ data=ioread32(regaddr);
+ data &= ~(((1<<(zx29clk->clk_div_reg.reg_bit_size))-1)<<zx29clk->clk_div_reg.reg_bit_offset);
+ data |= clk_div<<zx29clk->clk_div_reg.reg_bit_offset;
+ iowrite32(data,regaddr);
+ hw_spin_unlock(CLK_HWLOCK);
+
+ return 0;
+}
+
+/*
+ * get the clk's rate.
+ *
+ * new_rate = parent->rate/(clk_div+1)
+ */
+static unsigned long zx297520v3_clk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ void __iomem *regaddr = NULL;
+ unsigned int data = 0;
+ struct zx29_hwclk *zx29clk = to_zx29_hwclk(hw);
+
+ /*if no clk division, return parent's rate */
+ if(zx29clk->clk_div_reg.reg_bit_size == 0) {
+ if(zx29clk->hw.clk->num_parents)
+ return parent_rate;
+ else
+ return 0;
+ }
+
+ regaddr = zx29clk->clk_div_reg.reg_addr;
+
+ data=ioread32(regaddr);
+ data >>= zx29clk->clk_div_reg.reg_bit_offset;
+ data &= ((1<<(zx29clk->clk_div_reg.reg_bit_size))-1);
+
+ return parent_rate/(data+1);
+}
+
+#if 0
+/*
+ * get the clk's rate.
+ *
+ * new_rate = parent->rate/(2^clk_div)
+ */
+static unsigned long zx297520v3_clk_recalc_rate_x2(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ void __iomem *regaddr = NULL;
+ unsigned int data = 0;
+ struct zx29_hwclk *zx29clk = to_zx29_hwclk(hw);
+
+ if(zx29clk->clk_div_reg.reg_bit_size == 0) {
+ if(zx29clk->hw.clk->num_parents)
+ return parent_rate;
+ else
+ return 0;
+ }
+
+ regaddr = zx29clk->clk_div_reg.reg_addr;
+
+ data=ioread32(regaddr);
+ data >>= zx29clk->clk_div_reg.reg_bit_offset;
+ data &= ((1<<(zx29clk->clk_div_reg.reg_bit_size))-1);
+
+ return parent_rate/(0x1<<data);
+}
+#endif
+
+/*
+ * select parent from given parents.
+ *
+ * index < num_parents
+ */
+static int zx297520v3_clk_set_parent(struct clk_hw *hw, u8 index)
+{
+ void __iomem *regaddr = NULL;
+ unsigned int data = 0;
+ struct zx29_hwclk *zx29clk = to_zx29_hwclk(hw);
+
+ if(zx29clk->hw.clk->num_parents==0 || index>=zx29clk->hw.clk->num_parents)
+ return -EINVAL;
+ if(zx29clk->clk_sel_reg.reg_bit_size == 0)
+ return 0;
+
+ regaddr = zx29clk->clk_sel_reg.reg_addr;
+
+ hw_spin_lock(CLK_HWLOCK);
+ data=ioread32(regaddr);
+ data &= ~(((1<<(zx29clk->clk_sel_reg.reg_bit_size))-1)<<zx29clk->clk_sel_reg.reg_bit_offset);
+ data |= index<<zx29clk->clk_sel_reg.reg_bit_offset;
+ iowrite32(data,regaddr);
+ hw_spin_unlock(CLK_HWLOCK);
+
+ return 0;
+}
+
+/*
+ * get current parent select.
+ *
+ * return index begin with 0.
+ */
+static u8 zx297520v3_get_parent(struct clk_hw *hw)
+{
+ unsigned int data = 0;
+ void __iomem *regaddr = NULL;
+ struct zx29_hwclk *zx29clk = to_zx29_hwclk(hw);
+
+ if(zx29clk->hw.clk->num_parents < 2 || zx29clk->clk_sel_reg.reg_bit_size ==0)
+ return 0;
+
+ regaddr = zx29clk->clk_sel_reg.reg_addr;
+
+ data = ioread32(regaddr);
+ data >>= zx29clk->clk_sel_reg.reg_bit_offset;
+ data &= (1<<(zx29clk->clk_sel_reg.reg_bit_size))-1;
+
+ return data;
+}
+
+/*
+ * calc the rate we can support.
+ *
+ * rate: request rate(maybe we can not support exactly)
+ */
+static long zx297520v3_clk_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long * best_parent_rate)
+{
+ unsigned long parent_rate = 0;
+ unsigned long clk_div = 0;
+ struct zx29_hwclk *zx29clk = to_zx29_hwclk(hw);
+
+ if(zx29clk->hw.clk == NULL || zx29clk->hw.clk->parent == NULL)
+ return 0;
+// if(zx29clk->clk_div_reg.reg_bit_size == 0)
+// return 0;
+
+ parent_rate = zx29clk->hw.clk->parent->rate;
+ clk_div = parent_rate/rate;
+ if(clk_div >= (1<<zx29clk->clk_div_reg.reg_bit_size))
+ {
+ clk_div = (1<<zx29clk->clk_div_reg.reg_bit_size);
+ }
+
+ if(!clk_div)
+ return parent_rate;
+
+ if ((zx29clk->hw.clk->flags & CLK_NO_ODD_DIV) && (clk_div & 0x1) && (clk_div != 0x1))
+ clk_div++;
+
+ return parent_rate/(clk_div);
+}
+
+/*
+ * set auto clk gate.
+ *
+ * this only set once when system start.
+ */
+static int zx297520v3_set_auto_gate(struct clk_hw *hw, bool enable)
+{
+ unsigned int data = 0;
+ void __iomem *regaddr = NULL;
+ struct zx29_hwclk *zx29clk = to_zx29_hwclk(hw);
+
+ if(zx29clk->clk_gate_reg.reg_bit_size == 0)
+ return -EINVAL;
+
+ regaddr = zx29clk->clk_gate_reg.reg_addr;
+
+ hw_spin_lock(CLK_HWLOCK);
+ data=ioread32(regaddr);
+ if(enable)
+ data |= 1<<zx29clk->clk_gate_reg.reg_bit_offset;
+ else
+ data &= ~(1<<zx29clk->clk_gate_reg.reg_bit_offset);
+ iowrite32(data,regaddr);
+ hw_spin_unlock(CLK_HWLOCK);
+
+ return 0;
+}
+
+/**************************************************************************
+* peripheral clk divider new algorithm
+*
+* 1. default algorithm: new_rate = parent_rate/(1+clk_div)
+* 2. new algorithm: new_rate = parent_rate/(2*(integer_num+(clk_sel+fra_div/fra_base)/2))
+*
+***************************************************************************
+*/
+#define CLK_FRA_DIV(a) (zx_read_reg(a)&0xff)
+#define CLK_FRA_BASE(a) ((zx_read_reg(a)>>8)&0xff)
+#define CLK_INTEGER_NUM(a) ((zx_read_reg(a)>>16)&0xff)
+#define CLK_CLK_SEL(a) ((zx_read_reg(a)>>24)&1)
+
+/*
+{2.0, MIX_CLK_DIVIDER(0, 0xff, 1, 0) },
+{2.5, MIX_CLK_DIVIDER(0x7f, 0xff, 1, 0) },
+{3.0, MIX_CLK_DIVIDER(0, 0xff, 1, 1) },
+{3.5, MIX_CLK_DIVIDER(0x7f, 0xff, 1, 1) },
+*/
+/* d--fra_div b--fra_base n--integer_num s--clk_sel */
+#define MIX_CLK_DIVIDER(d,b,n,s) \
+ (d|(b<<8)|(n<<16)|(s<<24))
+
+#define MAX_CLK_DIVIDER 0x200//(2.0*(0xff+(1.0+0xff/0xff)/2.0))
+#define FLOAT_CALC_FACTOR (u64)(1000*1000)
+
+static void parase_float_clk_divider(u64 clk_div,
+ unsigned int *fra_div,
+ unsigned int *fra_base,
+ unsigned int *integer_num,
+ unsigned int *clk_sel)
+{
+ *integer_num = (unsigned int)div64_u64(clk_div, FLOAT_CALC_FACTOR)/2;
+ *clk_sel = (unsigned int)div64_u64(clk_div, FLOAT_CALC_FACTOR)%2;
+ *fra_base = 0xff;
+ *fra_div = (unsigned int)div64_u64((clk_div-*integer_num*FLOAT_CALC_FACTOR*2-*clk_sel*FLOAT_CALC_FACTOR)*255,
+ FLOAT_CALC_FACTOR);
+}
+
+/*
+ * set the clk division for new rate.
+ *
+ */
+static int zx297520v3_clk_set_rate_float(struct clk_hw *hw, unsigned long new_rate)
+{
+ struct zx29_hwclk *zx29clk = to_zx29_hwclk(hw);
+ unsigned int fra_div = 0;
+ unsigned int fra_base = 0;
+ unsigned int integer_num = 0;
+ unsigned int clk_sel = 0;
+ u64 tmp_parent_rate;
+ u64 clk_div = 0;
+
+ if(zx29clk->hw.clk == NULL || zx29clk->hw.clk->parent == NULL)
+ return -EINVAL;
+ if(zx29clk->clk_div_reg.reg_addr == 0)
+ return -EINVAL;
+
+ tmp_parent_rate = zx29clk->hw.clk->parent->rate*FLOAT_CALC_FACTOR;
+ clk_div = div64_u64(tmp_parent_rate, new_rate);
+
+ /*get the max division*/
+ if(clk_div >= MAX_CLK_DIVIDER*FLOAT_CALC_FACTOR)
+ clk_div = MAX_CLK_DIVIDER*FLOAT_CALC_FACTOR;
+
+ parase_float_clk_divider(clk_div, &fra_div, &fra_base, &integer_num, &clk_sel);
+
+ hw_spin_lock(CLK_HWLOCK);
+ zx_write_reg(zx29clk->clk_div_reg.reg_addr,
+ MIX_CLK_DIVIDER(fra_div, fra_base, integer_num, clk_sel));
+ hw_spin_unlock(CLK_HWLOCK);
+
+ return 0;
+}
+
+/*
+ * get the clk's rate.
+ *
+ * new_rate = parent_rate/(2*(integer_num+(clk_sel+fra_div/fra_base)/2))
+ */
+static unsigned long zx297520v3_clk_recalc_rate_float(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ void __iomem *regaddr = NULL;
+ struct zx29_hwclk *zx29clk = to_zx29_hwclk(hw);
+ unsigned int fra_div = 0;
+ unsigned int fra_base = 0;
+ unsigned int clk_sel = 0;
+ unsigned int integer_num = 0;
+ u64 tmp_parent_rate = (u64)parent_rate*FLOAT_CALC_FACTOR;
+ u64 clk_div = 0;
+
+ regaddr = zx29clk->clk_div_reg.reg_addr;
+
+ /*if no clk division, return parent's rate */
+ if(regaddr == 0) {
+ if(zx29clk->hw.clk->num_parents)
+ return parent_rate;
+ else
+ return 0;
+ }
+
+ fra_div = CLK_FRA_DIV(regaddr);
+ fra_base = CLK_FRA_BASE(regaddr);
+ clk_sel = CLK_CLK_SEL(regaddr);
+ integer_num = CLK_INTEGER_NUM(regaddr);
+
+ clk_div = (2*integer_num+clk_sel)*FLOAT_CALC_FACTOR+div64_u64(fra_div*FLOAT_CALC_FACTOR,fra_base);
+
+ return (unsigned long)div64_u64(tmp_parent_rate, clk_div);
+}
+
+/*
+ * calc the rate we can support.
+ *
+ * rate: request rate(maybe we can not support exactly)
+ */
+static long zx297520v3_clk_round_rate_float(struct clk_hw *hw, unsigned long rate,
+ unsigned long * best_parent_rate)
+{
+ struct zx29_hwclk *zx29clk = to_zx29_hwclk(hw);
+ u64 tmp_parent_rate;
+ u64 clk_div = 0;
+
+ if(zx29clk->hw.clk == NULL || zx29clk->hw.clk->parent == NULL)
+ return 0;
+ if(zx29clk->clk_div_reg.reg_addr == 0)
+ return 0;
+
+ tmp_parent_rate = zx29clk->hw.clk->parent->rate*FLOAT_CALC_FACTOR;
+ clk_div = div64_u64(tmp_parent_rate, rate);
+
+ if(clk_div >= MAX_CLK_DIVIDER*FLOAT_CALC_FACTOR)
+ clk_div = MAX_CLK_DIVIDER*FLOAT_CALC_FACTOR;
+
+ if(!clk_div)
+ return (long)div64_u64(tmp_parent_rate, FLOAT_CALC_FACTOR);
+ else
+ return (long)div64_u64(tmp_parent_rate, clk_div);
+}
+
+static int clk_rootclk_enable(struct clk_hw *hw)
+{
+ return 0;
+}
+static void clk_rootclk_disable(struct clk_hw *hw)
+{
+ return ;
+}
+
+static int clk_rootclk_is_enabled(struct clk_hw *hw)
+{
+ return 1;
+}
+
+static int clk_rootclk_set_rate(struct clk_hw *hw, unsigned long new_rate)//, unsigned long parent_rate)
+{
+ return 0;
+}
+
+static struct clk_ops peripheral_clk_ops =
+{
+ .enable = zx297520v3_clk_enable,
+ .disable = zx297520v3_clk_disable,
+ .set_auto_gate = zx297520v3_set_auto_gate,
+ .set_rate = zx297520v3_clk_set_rate,
+ .recalc_rate = zx297520v3_clk_recalc_rate,
+ .set_parent = zx297520v3_clk_set_parent,
+ .round_rate = zx297520v3_clk_round_rate,
+ .get_parent = zx297520v3_get_parent,
+ .is_enabled = zx297520v3_is_enabled,
+};
+
+static struct clk_ops peripheral_float_clk_ops =
+{
+ .enable = zx297520v3_clk_enable,
+ .disable = zx297520v3_clk_disable,
+ .set_auto_gate = zx297520v3_set_auto_gate,
+ .set_rate = zx297520v3_clk_set_rate_float,
+ .recalc_rate = zx297520v3_clk_recalc_rate_float,
+ .set_parent = zx297520v3_clk_set_parent,
+ .round_rate = zx297520v3_clk_round_rate_float,
+ .get_parent = zx297520v3_get_parent,
+ .is_enabled = zx297520v3_is_enabled,
+};
+
+static struct clk_ops cpu_clk_ops =
+{
+ .enable = zx297520v3_clk_enable,
+ .disable = zx297520v3_clk_disable,
+ .set_auto_gate = zx297520v3_set_auto_gate,
+ .set_rate = zx297520v3_clk_set_rate,
+ .recalc_rate = zx297520v3_clk_recalc_rate,
+ .set_parent = zx297520v3_clk_set_parent,
+ .round_rate = zx297520v3_clk_round_rate,
+ .get_parent = zx297520v3_get_parent,
+ .is_enabled = zx297520v3_is_enabled,
+};
+
+struct clk_ops root_clk_ops = {
+ .enable = clk_rootclk_enable,
+ .disable = clk_rootclk_disable,
+ .is_enabled = clk_rootclk_is_enabled,
+ .set_rate = clk_rootclk_set_rate,
+};
+
+
+/*
+* root clk
+*/
+DEFINE_ZX29_ROOT_CLK(main_clk_32k, 32768);
+DEFINE_ZX29_ROOT_CLK(mpll_clk_12m, 12*1000*1000);
+DEFINE_ZX29_ROOT_CLK(mpll_clk_24m, 24*1000*1000);
+DEFINE_ZX29_ROOT_CLK(mpll_clk_39m, 39*1000*1000);
+DEFINE_ZX29_ROOT_CLK(mpll_clk_52m, 52*1000*1000);
+DEFINE_ZX29_ROOT_CLK(mpll_clk_78m, 78*1000*1000);
+#if CONFIG_ARCH_ZX297520v3FPGA
+DEFINE_ZX29_ROOT_CLK(main_clk_26m, 25*1000*1000);
+DEFINE_ZX29_ROOT_CLK(mpll_clk_104m, 25*1000*1000);
+#else
+DEFINE_ZX29_ROOT_CLK(main_clk_26m, 26*1000*1000);
+DEFINE_ZX29_ROOT_CLK(mpll_clk_104m, 104*1000*1000);
+#endif
+DEFINE_ZX29_ROOT_CLK(mpll_clk_124m8, 124800*1000);
+DEFINE_ZX29_ROOT_CLK(mpll_clk_156m, 156*1000*1000);
+DEFINE_ZX29_ROOT_CLK(mpll_clk_208m, 208*1000*1000);
+DEFINE_ZX29_ROOT_CLK(mpll_clk_312m, 312*1000*1000);
+DEFINE_ZX29_ROOT_CLK(mpll_clk_624m, 624*1000*1000);
+DEFINE_ZX29_ROOT_CLK(dpll_clk_491m52, 491520*1000);
+DEFINE_ZX29_ROOT_CLK(dpll_clk_122m88, 122880*1000);
+DEFINE_ZX29_ROOT_CLK(dpll_clk_81m92, 81920*1000);
+DEFINE_ZX29_ROOT_CLK(upll_clk_480m, 480*1000*1000);
+DEFINE_ZX29_ROOT_CLK(upll_clk_96m, 96*1000*1000);
+DEFINE_ZX29_ROOT_CLK(gpll_clk_25m, 25*1000*1000);
+DEFINE_ZX29_ROOT_CLK(gpll_clk_50m, 50*1000*1000);
+DEFINE_ZX29_ROOT_CLK(gpll_clk_100m, 100*1000*1000);
+DEFINE_ZX29_ROOT_CLK(gpll_clk_178m, 178*1000*1000);
+DEFINE_ZX29_ROOT_CLK(gpll_clk_200m, 200*1000*1000);
+
+
+/*
+* a1_uart0
+*/
+char* a1_uart_wclk_parents[2] =
+{
+ NAME_CLK(main_clk_26m),
+ NAME_CLK(mpll_clk_104m)
+};
+char* lsp_uart_wclk_parents[2] =
+{
+ NAME_CLK(main_clk_26m),
+ NAME_CLK(mpll_clk_104m)
+};
+static struct zx29_hwclk uart0_apb =
+{
+ .clk_en_reg ={ZX_TOP_CRM_BASE+0x5C, 13, 1},
+ .clk_gate_reg ={ZX_TOP_CRM_BASE+0x5C, 14, 1},
+};
+DEFINE_ZX29_CLK(uart0_apb,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk uart0_work =
+{
+ .clk_en_reg ={ZX_TOP_CRM_BASE+0x5C, 12, 1},
+ .clk_sel_reg ={ZX_TOP_CRM_BASE+0x40, 2, 1},
+ .clk_div_reg ={ZX_TOP_CRM_BASE+0x40, 0, 0},
+ .clk_gate_reg ={ZX_TOP_CRM_BASE+0x5C, 15, 1},
+};
+DEFINE_ZX29_CLK(uart0_work,peripheral_clk_ops,2,a1_uart_wclk_parents);
+
+/*
+* lsp_uart1
+*/
+static struct zx29_hwclk uart1_apb =
+{
+ .clk_en_reg ={ZX_LSP_CRPM_BASE+0x28, 0, 1},
+ .clk_div_reg ={ZX_LSP_CRPM_BASE+0x28, 0, 0},
+ .clk_gate_reg ={ZX_LSP_CRPM_BASE+0x28, 11, 1},
+};
+DEFINE_ZX29_CLK(uart1_apb,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk uart1_work =
+{
+ .clk_en_reg ={ZX_LSP_CRPM_BASE+0x28, 1, 1},
+ .clk_sel_reg ={ZX_LSP_CRPM_BASE+0x28, 4, 1},
+ .clk_div_reg ={ZX_LSP_CRPM_BASE+0x28, 0, 0},
+ .clk_gate_reg ={ZX_LSP_CRPM_BASE+0x28, 10, 1},
+};
+DEFINE_ZX29_CLK(uart1_work,peripheral_clk_ops,2,lsp_uart_wclk_parents);
+
+/*
+* lsp_uart2
+*/
+static struct zx29_hwclk uart2_apb =
+{
+ .clk_en_reg ={ZX_LSP_CRPM_BASE+0x3c, 0, 1},
+ .clk_div_reg ={ZX_LSP_CRPM_BASE+0x3c, 0, 0},
+ .clk_gate_reg ={ZX_LSP_CRPM_BASE+0x3c, 11, 1},
+};
+DEFINE_ZX29_CLK(uart2_apb,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk uart2_work =
+{
+ .clk_en_reg ={ZX_LSP_CRPM_BASE+0x3c, 1, 1},
+ .clk_sel_reg ={ZX_LSP_CRPM_BASE+0x3c, 4, 1},
+ .clk_div_reg ={ZX_LSP_CRPM_BASE+0x3c, 0, 0},
+ .clk_gate_reg ={ZX_LSP_CRPM_BASE+0x3c, 10, 1},
+};
+DEFINE_ZX29_CLK(uart2_work,peripheral_clk_ops,2,lsp_uart_wclk_parents);
+
+/*
+* a1_timer1
+*/
+char* a1_timer_wclk_parents[2] =
+{
+ NAME_CLK(main_clk_32k),
+ NAME_CLK(main_clk_26m)
+};
+char* lsp_timer_wclk_parents[2] =
+{
+ NAME_CLK(main_clk_32k),
+ NAME_CLK(main_clk_26m)
+};
+static struct zx29_hwclk timer1_apb =
+{
+ .clk_en_reg ={ZX_TOP_CRM_BASE+0x60, 1, 1},
+ .clk_gate_reg ={ZX_TOP_CRM_BASE+0x60, 2, 1},
+};
+DEFINE_ZX29_CLK(timer1_apb,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk timer1_work =
+{
+ .clk_en_reg ={ZX_TOP_CRM_BASE+0x60, 0, 1},
+ .clk_sel_reg ={ZX_TOP_CRM_BASE+0x44, 0, 1},
+ .clk_div_reg ={ZX_TOP_CRM_BASE+0x50, 0, 4},
+};
+DEFINE_ZX29_CLK(timer1_work,peripheral_clk_ops,2,a1_timer_wclk_parents);
+
+/*
+* a1_timer2
+*/
+static struct zx29_hwclk timer2_apb =
+{
+ .clk_en_reg ={ZX_TOP_CRM_BASE+0x60, 5, 1},
+ .clk_gate_reg ={ZX_TOP_CRM_BASE+0x60, 6, 1},
+};
+DEFINE_ZX29_CLK(timer2_apb,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk timer2_work =
+{
+ .clk_en_reg ={ZX_TOP_CRM_BASE+0x60, 4, 1},
+ .clk_sel_reg ={ZX_TOP_CRM_BASE+0x44, 1, 1},
+ .clk_div_reg ={ZX_TOP_CRM_BASE+0x50, 4, 4},
+};
+DEFINE_ZX29_CLK(timer2_work,peripheral_clk_ops,2,a1_timer_wclk_parents);
+
+/*
+* a1_timer3
+*/
+static struct zx29_hwclk timer3_apb =
+{
+ .clk_en_reg ={ZX_TOP_CRM_BASE+0x60, 9, 1},
+ .clk_gate_reg ={ZX_TOP_CRM_BASE+0x60, 10, 1},
+};
+DEFINE_ZX29_CLK(timer3_apb,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk timer3_work =
+{
+ .clk_en_reg ={ZX_TOP_CRM_BASE+0x60, 8, 1},
+ .clk_sel_reg ={ZX_TOP_CRM_BASE+0x44, 2, 1},
+ .clk_div_reg ={ZX_TOP_CRM_BASE+0x50, 8, 4},
+};
+DEFINE_ZX29_CLK(timer3_work,peripheral_clk_ops,2,a1_timer_wclk_parents);
+
+/*
+* ps_timer1
+*/
+static struct zx29_hwclk ps_timer1_apb =
+{
+ .clk_en_reg ={ZX_TOP_CRM_BASE+0x5c, 9, 1},
+ .clk_gate_reg ={ZX_TOP_CRM_BASE+0x5c, 10, 1},
+};
+DEFINE_ZX29_CLK(ps_timer1_apb,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk ps_timer1_work =
+{
+ .clk_en_reg ={ZX_TOP_CRM_BASE+0x5c, 8, 1},
+ .clk_sel_reg ={ZX_TOP_CRM_BASE+0x40, 1, 1},
+ .clk_div_reg ={ZX_TOP_CRM_BASE+0x4c, 4, 4},
+};
+DEFINE_ZX29_CLK(ps_timer1_work,peripheral_clk_ops,2,a1_timer_wclk_parents);
+
+/*
+* ps_timer2
+*/
+static struct zx29_hwclk ps_timer2_apb =
+{
+ .clk_en_reg ={ZX_TOP_CRM_BASE+0x5c, 5, 1},
+ .clk_gate_reg ={ZX_TOP_CRM_BASE+0x5c, 6, 1},
+};
+DEFINE_ZX29_CLK(ps_timer2_apb,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk ps_timer2_work =
+{
+ .clk_en_reg ={ZX_TOP_CRM_BASE+0x5c, 4, 1},
+ .clk_sel_reg ={ZX_TOP_CRM_BASE+0x40, 0, 1},
+ .clk_div_reg ={ZX_TOP_CRM_BASE+0x4c, 0, 4},
+};
+DEFINE_ZX29_CLK(ps_timer2_work,peripheral_clk_ops,2,a1_timer_wclk_parents);
+
+
+/*
+* lsp_timer0
+*/
+static struct zx29_hwclk timer0_apb =
+{
+ .clk_en_reg ={ZX_LSP_CRPM_BASE+0x44, 0, 1},
+ .clk_gate_reg ={ZX_LSP_CRPM_BASE+0x44, 11, 1},
+};
+DEFINE_ZX29_CLK(timer0_apb,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk timer0_work =
+{
+ .clk_en_reg ={ZX_LSP_CRPM_BASE+0x44, 1, 1},
+ .clk_sel_reg ={ZX_LSP_CRPM_BASE+0x44, 4, 1},
+ .clk_div_reg ={ZX_LSP_CRPM_BASE+0x44, 12, 4},
+};
+DEFINE_ZX29_CLK(timer0_work,peripheral_clk_ops,2,lsp_timer_wclk_parents);
+
+/*
+* lsp_timer4
+*/
+static struct zx29_hwclk timer4_apb =
+{
+ .clk_en_reg ={ZX_LSP_CRPM_BASE+0x4C, 0, 1},
+ .clk_gate_reg ={ZX_LSP_CRPM_BASE+0x4C, 11, 1},
+};
+DEFINE_ZX29_CLK(timer4_apb,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk timer4_work =
+{
+ .clk_en_reg ={ZX_LSP_CRPM_BASE+0x4C, 1, 1},
+ .clk_sel_reg ={ZX_LSP_CRPM_BASE+0x4C, 4, 1},
+ .clk_div_reg ={ZX_LSP_CRPM_BASE+0x4C, 12, 4},
+};
+DEFINE_ZX29_CLK(timer4_work,peripheral_clk_ops,2,lsp_timer_wclk_parents);
+
+/*
+* ps_timer0
+*/
+static struct zx29_hwclk ps_timer0_apb =
+{
+ .clk_en_reg ={ZX_LSP_CRPM_BASE+0x4, 0, 1},
+ .clk_gate_reg ={ZX_LSP_CRPM_BASE+0x4, 11, 1},
+};
+DEFINE_ZX29_CLK(ps_timer0_apb,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk ps_timer0_work =
+{
+ .clk_en_reg ={ZX_LSP_CRPM_BASE+0x4, 1, 1},
+ .clk_sel_reg ={ZX_LSP_CRPM_BASE+0x4, 4, 1},
+ .clk_div_reg ={ZX_LSP_CRPM_BASE+0x4, 12, 4},
+};
+DEFINE_ZX29_CLK(ps_timer0_work,peripheral_clk_ops,2,lsp_timer_wclk_parents);
+
+
+
+/**************************************************************************
+* i2c
+***************************************************************************
+*/
+char* a1_i2c_wclk_parents[2] =
+{
+ NAME_CLK(main_clk_26m),
+ NAME_CLK(mpll_clk_104m)
+};
+char* lsp_i2c_wclk_parents[2] =
+{
+ NAME_CLK(main_clk_26m),
+ NAME_CLK(mpll_clk_104m)
+};
+
+/*
+* a1_i2c0
+*/
+static struct zx29_hwclk i2c0_apb =
+{
+ .clk_en_reg ={ZX_TOP_CRM_BASE+0x54, 8, 1},
+ .clk_gate_reg ={ZX_TOP_CRM_BASE+0x54, 10, 1},
+};
+DEFINE_ZX29_CLK(i2c0_apb,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk i2c0_work =
+{
+ .clk_en_reg ={ZX_TOP_CRM_BASE+0x54, 9, 1},
+ .clk_sel_reg ={ZX_TOP_CRM_BASE+0x3C, 1, 1},
+ .clk_gate_reg ={ZX_TOP_CRM_BASE+0x54, 11, 1},
+};
+DEFINE_ZX29_CLK(i2c0_work,peripheral_clk_ops,2,a1_i2c_wclk_parents);
+/*
+* lsp_i2c1
+*/
+static struct zx29_hwclk i2c1_apb =
+{
+ .clk_en_reg ={ZX_LSP_CRPM_BASE+0x2C, 0, 1},
+ .clk_div_reg ={ZX_LSP_CRPM_BASE+0x2C, 0, 0},
+ .clk_gate_reg ={ZX_LSP_CRPM_BASE+0x2C, 11, 1},
+};
+DEFINE_ZX29_CLK(i2c1_apb,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk i2c1_work =
+{
+ .clk_en_reg ={ZX_LSP_CRPM_BASE+0x2C, 1, 1},
+ .clk_sel_reg ={ZX_LSP_CRPM_BASE+0x2C, 4, 1},
+ .clk_div_reg ={ZX_LSP_CRPM_BASE+0x2C, 0, 0},
+ .clk_gate_reg ={ZX_LSP_CRPM_BASE+0x2C, 10, 1},
+};
+DEFINE_ZX29_CLK(i2c1_work,peripheral_clk_ops,2,lsp_i2c_wclk_parents);
+
+/**************************************************************************
+* ssp
+***************************************************************************
+*/
+char* lsp_ssp_wclk_parents[3] =
+{
+ NAME_CLK(main_clk_26m),
+ NAME_CLK(mpll_clk_156m),
+ NAME_CLK(mpll_clk_104m)
+};
+
+/*
+* lsp_ssp0
+*/
+static struct zx29_hwclk ssp0_apb =
+{
+ .clk_en_reg ={ZX_LSP_CRPM_BASE+0x30, 0, 1},
+ .clk_div_reg ={ZX_LSP_CRPM_BASE+0x30, 0, 0},
+ .clk_gate_reg ={ZX_LSP_CRPM_BASE+0x30, 11, 1},
+};
+DEFINE_ZX29_CLK(ssp0_apb,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk ssp0_work =
+{
+ .clk_en_reg ={ZX_LSP_CRPM_BASE+0x30, 1, 1},
+ .clk_sel_reg ={ZX_LSP_CRPM_BASE+0x30, 4, 2},
+ .clk_div_reg ={ZX_LSP_CRPM_BASE+0x30, 12, 4},
+ .clk_gate_reg ={ZX_LSP_CRPM_BASE+0x30, 10, 1},
+};
+DEFINE_ZX29_CLK_FLAG(ssp0_work,peripheral_clk_ops,3,lsp_ssp_wclk_parents, (CLK_AUTO_ROUND_PARENT | CLK_NO_ODD_DIV));
+/*
+* lsp_ssp1
+*/
+static struct zx29_hwclk ssp1_apb =
+{
+ .clk_en_reg ={ZX_LSP_CRPM_BASE+0x48, 0, 1},
+ .clk_div_reg ={ZX_LSP_CRPM_BASE+0x48, 0, 0},
+ .clk_gate_reg ={ZX_LSP_CRPM_BASE+0x48, 11, 1},
+};
+DEFINE_ZX29_CLK(ssp1_apb,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk ssp1_work =
+{
+ .clk_en_reg ={ZX_LSP_CRPM_BASE+0x48, 1, 1},
+ .clk_sel_reg ={ZX_LSP_CRPM_BASE+0x48, 4, 2},
+ .clk_div_reg ={ZX_LSP_CRPM_BASE+0x48, 12, 4},
+ .clk_gate_reg ={ZX_LSP_CRPM_BASE+0x48, 10, 1},
+};
+DEFINE_ZX29_CLK_FLAG(ssp1_work,peripheral_clk_ops,3,lsp_ssp_wclk_parents, (CLK_AUTO_ROUND_PARENT | CLK_NO_ODD_DIV));
+
+/**************************************************************************
+* ap wdt
+***************************************************************************
+*/
+char* lsp_wdt_wclk_parents[2] =
+{
+ NAME_CLK(main_clk_32k),
+ NAME_CLK(main_clk_26m)
+};
+
+static struct zx29_hwclk ap_wdt_apb =
+{
+ .clk_en_reg ={ZX_LSP_CRPM_BASE+0x40, 0, 1},
+ .clk_div_reg ={ZX_LSP_CRPM_BASE+0x40, 0, 0},
+ .clk_gate_reg ={ZX_LSP_CRPM_BASE+0x40, 11, 1},
+};
+DEFINE_ZX29_CLK(ap_wdt_apb,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk ap_wdt_work =
+{
+ .clk_en_reg ={ZX_LSP_CRPM_BASE+0x40, 1, 1},
+ .clk_sel_reg ={ZX_LSP_CRPM_BASE+0x40, 4, 1},
+ .clk_div_reg ={ZX_LSP_CRPM_BASE+0x40, 12, 4},
+ .clk_gate_reg ={ZX_LSP_CRPM_BASE+0x40, 10, 1},
+};
+DEFINE_ZX29_CLK(ap_wdt_work,peripheral_clk_ops,2,lsp_wdt_wclk_parents);
+
+/**************************************************************************
+* tdm
+***************************************************************************
+*/
+char* lsp_tdm_wclk_parents[3] =
+{
+ NAME_CLK(main_clk_26m),
+ NAME_CLK(dpll_clk_122m88),
+ NAME_CLK(mpll_clk_104m)
+};
+
+static struct zx29_hwclk tdm_apb =
+{
+ .clk_en_reg ={ZX_LSP_CRPM_BASE+0x50, 0, 1},
+ .clk_div_reg ={ZX_LSP_CRPM_BASE+0x50, 0, 0},
+ .clk_gate_reg ={ZX_LSP_CRPM_BASE+0x50, 11, 1},
+};
+DEFINE_ZX29_CLK(tdm_apb,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk tdm_work =
+{
+ .clk_en_reg ={ZX_LSP_CRPM_BASE+0x50, 1, 1},
+ .clk_sel_reg ={ZX_MATRIX_CRM_BASE+0x50, 24, 2},
+// .clk_div_reg ={ZX_LSP_CRPM_BASE+0x54, 24, 2},
+ .clk_gate_reg ={ZX_LSP_CRPM_BASE+0x50, 10, 1},
+};
+DEFINE_ZX29_CLK(tdm_work,peripheral_float_clk_ops,3,lsp_tdm_wclk_parents);
+
+/**************************************************************************
+* i2s
+***************************************************************************
+*/
+char* lsp_i2s_wclk_parents[2] =
+{
+ NAME_CLK(main_clk_26m),
+ NAME_CLK(dpll_clk_122m88),
+ NAME_CLK(mpll_clk_104m)
+};
+
+static struct zx29_hwclk i2s0_apb =
+{
+ .clk_en_reg ={ZX_LSP_CRPM_BASE+0x14, 0, 1},
+ .clk_div_reg ={ZX_LSP_CRPM_BASE+0x14, 0, 0},
+ .clk_gate_reg ={ZX_LSP_CRPM_BASE+0x14, 11, 1},
+};
+DEFINE_ZX29_CLK(i2s0_apb,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk i2s0_work =
+{
+ .clk_en_reg ={ZX_LSP_CRPM_BASE+0x14, 1, 1},
+ .clk_sel_reg ={ZX_LSP_CRPM_BASE+0x14, 4, 2},
+// .clk_div_reg ={ZX_LSP_CRPM_BASE+0x18, 0, 2},
+ .clk_gate_reg ={ZX_LSP_CRPM_BASE+0x14, 10, 1},
+};
+DEFINE_ZX29_CLK(i2s0_work,peripheral_float_clk_ops,2,lsp_i2s_wclk_parents);
+
+static struct zx29_hwclk i2s1_apb =
+{
+ .clk_en_reg ={ZX_LSP_CRPM_BASE+0x1C, 0, 1},
+ .clk_div_reg ={ZX_LSP_CRPM_BASE+0x1C, 0, 0},
+ .clk_gate_reg ={ZX_LSP_CRPM_BASE+0x1C, 11, 1},
+};
+DEFINE_ZX29_CLK(i2s1_apb,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk i2s1_work =
+{
+ .clk_en_reg ={ZX_LSP_CRPM_BASE+0x1C, 1, 1},
+ .clk_sel_reg ={ZX_LSP_CRPM_BASE+0x1C, 4, 2},
+// .clk_div_reg ={ZX_LSP_CRPM_BASE+0x20, 0, 2},
+ .clk_gate_reg ={ZX_LSP_CRPM_BASE+0x1C, 10, 1},
+};
+DEFINE_ZX29_CLK(i2s1_work,peripheral_float_clk_ops,2,lsp_i2s_wclk_parents);
+
+/**************************************************************************
+* spifc
+***************************************************************************
+*/
+char* lsp_spifc_wclk_parents[] =
+{
+ NAME_CLK(main_clk_26m),
+ NAME_CLK(mpll_clk_156m),
+ NAME_CLK(mpll_clk_124m8),
+ NAME_CLK(mpll_clk_104m),
+ NAME_CLK(mpll_clk_78m),
+ NAME_CLK(mpll_clk_52m)
+};
+
+static struct zx29_hwclk spifc_apb =
+{
+ .clk_en_reg ={ZX_LSP_CRPM_BASE+0x24, 0, 1},
+ .clk_div_reg ={ZX_LSP_CRPM_BASE+0x24, 0, 0},
+ .clk_gate_reg ={ZX_LSP_CRPM_BASE+0x24, 11, 1},
+};
+DEFINE_ZX29_CLK(spifc_apb,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk spifc_work =
+{
+ .clk_en_reg ={ZX_LSP_CRPM_BASE+0x24, 1, 1},
+ .clk_sel_reg ={ZX_LSP_CRPM_BASE+0x24, 4, 3},
+ .clk_div_reg ={ZX_LSP_CRPM_BASE+0x24, 0, 0},
+ .clk_gate_reg ={ZX_LSP_CRPM_BASE+0x24, 10, 1},
+};
+DEFINE_ZX29_CLK(spifc_work,peripheral_clk_ops,ARRAY_SIZE(lsp_spifc_wclk_parents),lsp_spifc_wclk_parents);
+
+/**************************************************************************
+* kpd
+***************************************************************************
+*/
+char* kpd_wclk_parents[] =
+{
+ NAME_CLK(main_clk_32k),
+};
+
+static struct zx29_hwclk kpd_apb =
+{
+ .clk_en_reg ={ZX_TOP_CRM_BASE+0x5C, 17, 1},
+ .clk_div_reg ={ZX_TOP_CRM_BASE+0x5C, 0, 0},
+ .clk_gate_reg ={ZX_TOP_CRM_BASE+0x5C, 19, 1},
+};
+DEFINE_ZX29_CLK(kpd_apb,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk kpd_work =
+{
+ .clk_en_reg ={ZX_TOP_CRM_BASE+0x5C, 16, 1},
+ .clk_sel_reg ={ZX_TOP_CRM_BASE+0x5C, 0, 0},
+ .clk_div_reg ={ZX_TOP_CRM_BASE+0x5C, 0, 0},
+ .clk_gate_reg ={ZX_TOP_CRM_BASE+0x5C, 18, 1},
+};
+DEFINE_ZX29_CLK(kpd_work,peripheral_clk_ops,ARRAY_SIZE(kpd_wclk_parents),kpd_wclk_parents);
+
+/**************************************************************************
+* rtc
+***************************************************************************
+*/
+char* rtc_wclk_parents[] =
+{
+ NAME_CLK(main_clk_32k),
+};
+
+static struct zx29_hwclk rtc_apb =
+{
+ .clk_en_reg ={ZX_TOP_CRM_BASE+0x54, 0, 1},
+
+};
+DEFINE_ZX29_CLK(rtc_apb,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk rtc_work =
+{
+ .clk_en_reg ={ZX_TOP_CRM_BASE+0x54, 1, 1},
+};
+DEFINE_ZX29_CLK(rtc_work,peripheral_clk_ops,ARRAY_SIZE(rtc_wclk_parents),rtc_wclk_parents);
+
+/**************************************************************************
+* usim1
+***************************************************************************
+*/
+char* usim1_wclk_parents[] =
+{
+ NAME_CLK(main_clk_26m),
+};
+
+static struct zx29_hwclk usim1_apb =
+{
+ .clk_en_reg ={ZX_TOP_CRM_BASE+0x54, 29, 1},
+ .clk_gate_reg ={ZX_TOP_CRM_BASE+0x54, 31, 1},
+
+};
+DEFINE_ZX29_CLK(usim1_apb,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk usim1_work =
+{
+ .clk_en_reg ={ZX_TOP_CRM_BASE+0x54, 28, 1},
+ .clk_gate_reg ={ZX_TOP_CRM_BASE+0x54, 30, 1},
+ .clk_div_reg ={ZX_TOP_CRM_BASE+0x48, 12, 1},
+
+
+};
+DEFINE_ZX29_CLK(usim1_work,peripheral_clk_ops,ARRAY_SIZE(usim1_wclk_parents),usim1_wclk_parents);
+
+/**************************************************************************
+* hs_ahb (parent of hsic/usb ahb clk)
+***************************************************************************
+*/
+char* hs_ahb_parents[] =
+{
+ NAME_CLK(main_clk_26m),
+ NAME_CLK(mpll_clk_104m),
+ NAME_CLK(mpll_clk_78m)
+};
+static struct zx29_hwclk hs_ahb =
+{
+/* .clk_en_reg ={ZX_TOP_CRM_BASE+0x54, 12, 1}, */
+ .clk_sel_reg ={ZX_TOP_CRM_BASE+0x3C, 4, 2},
+};
+DEFINE_ZX29_CLK(hs_ahb,peripheral_clk_ops,ARRAY_SIZE(hs_ahb_parents),hs_ahb_parents);
+
+/**************************************************************************
+* usb2.0
+***************************************************************************
+*/
+char* usb_12m_parents[] =
+{
+ NAME_CLK(mpll_clk_12m),
+};
+
+char* usb_24m_parents[] =
+{
+ NAME_CLK(mpll_clk_24m),
+};
+
+char* usb_480m_parents[] =
+{
+ NAME_CLK(upll_clk_480m),
+};
+
+char* usb_ahb_parents[] =
+{
+ "hs_ahb_clk",
+};
+static struct zx29_hwclk usb_ahb =
+{
+ .clk_en_reg ={ZX_TOP_CRM_BASE+0x6C, 4, 1},
+};
+DEFINE_ZX29_CLK(usb_ahb,peripheral_clk_ops,ARRAY_SIZE(usb_ahb_parents),usb_ahb_parents);
+
+static struct zx29_hwclk usb_24m =
+{
+ .clk_en_reg ={ZX_TOP_CRM_BASE+0x6C, 3, 1},
+};
+DEFINE_ZX29_CLK(usb_24m,peripheral_clk_ops,ARRAY_SIZE(usb_24m_parents),usb_24m_parents);
+
+/**************************************************************************
+ * HSIC
+ **************************************************************************
+ */
+static struct zx29_hwclk hsic_ahb =
+{
+ .clk_en_reg ={ZX_TOP_CRM_BASE+0x6C, 2, 1},
+};
+DEFINE_ZX29_CLK(hsic_ahb,peripheral_clk_ops,ARRAY_SIZE(usb_ahb_parents),usb_ahb_parents);
+
+static struct zx29_hwclk hsic_12m =
+{
+ .clk_en_reg ={ZX_TOP_CRM_BASE+0x6C, 1, 1},
+};
+DEFINE_ZX29_CLK(hsic_12m,peripheral_clk_ops,ARRAY_SIZE(usb_12m_parents),usb_12m_parents);
+
+
+/**************************************************************************
+ * GMAC
+ **************************************************************************
+ */
+char* gmac_rmii_parents[] =
+{
+ NAME_CLK(gpll_clk_50m),
+};
+
+static struct zx29_hwclk gmac_ahb =
+{
+ .clk_en_reg ={ZX_MATRIX_CRM_BASE+0x110, 2, 1},
+};
+DEFINE_ZX29_CLK(gmac_ahb,peripheral_clk_ops,ARRAY_SIZE(usb_ahb_parents),usb_ahb_parents);
+
+static struct zx29_hwclk gmac_apb =
+{
+ .clk_en_reg ={ZX_MATRIX_CRM_BASE+0x110, 1, 1},
+};
+DEFINE_ZX29_CLK(gmac_apb,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk gmac_rmii =
+{
+ .clk_en_reg ={ZX_MATRIX_CRM_BASE+0x110, 0, 1},
+};
+DEFINE_ZX29_CLK(gmac_rmii,peripheral_clk_ops,ARRAY_SIZE(gmac_rmii_parents),gmac_rmii_parents);
+
+/**************************************************************************
+ * SD
+ **************************************************************************
+ */
+char* sd0_work_parents[] =
+{
+ NAME_CLK(main_clk_26m),
+ NAME_CLK(mpll_clk_156m),
+ NAME_CLK(gpll_clk_100m),
+ NAME_CLK(mpll_clk_78m),
+ NAME_CLK(gpll_clk_50m),
+ NAME_CLK(gpll_clk_25m),
+
+};
+char* sd1_work_parents[] =
+{
+ NAME_CLK(main_clk_26m),
+ NAME_CLK(gpll_clk_100m),
+ NAME_CLK(mpll_clk_78m),
+ NAME_CLK(gpll_clk_50m),
+ NAME_CLK(mpll_clk_39m),
+ NAME_CLK(gpll_clk_25m),
+};
+char* sd_cdet_parents[] =
+{
+ NAME_CLK(main_clk_32k),
+};
+static struct zx29_hwclk sd0_ahb =
+{
+ .clk_en_reg ={ZX_MATRIX_CRM_BASE+0x54, 12, 1},
+ .clk_gate_reg ={ZX_MATRIX_CRM_BASE+0x54, 16, 1},
+};
+DEFINE_ZX29_CLK(sd0_ahb,peripheral_clk_ops,ARRAY_SIZE(usb_ahb_parents),usb_ahb_parents);
+
+static struct zx29_hwclk sd0_work =
+{
+ .clk_en_reg ={ZX_MATRIX_CRM_BASE+0x54, 13, 1},
+ .clk_sel_reg ={ZX_MATRIX_CRM_BASE+0x50, 4, 3},
+ .clk_gate_reg ={ZX_MATRIX_CRM_BASE+0x54, 17, 1},
+};
+DEFINE_ZX29_CLK(sd0_work,peripheral_clk_ops,ARRAY_SIZE(sd0_work_parents),sd0_work_parents);
+
+static struct zx29_hwclk sd0_cdet =
+{
+ .clk_en_reg ={ZX_MATRIX_CRM_BASE+0x54, 14, 1},
+};
+DEFINE_ZX29_CLK(sd0_cdet,peripheral_clk_ops,ARRAY_SIZE(sd_cdet_parents),sd_cdet_parents);
+
+static struct zx29_hwclk sd1_ahb =
+{
+ .clk_en_reg ={ZX_MATRIX_CRM_BASE+0x54, 4, 1},
+ .clk_gate_reg ={ZX_MATRIX_CRM_BASE+0x54, 8, 1},
+};
+DEFINE_ZX29_CLK(sd1_ahb,peripheral_clk_ops,ARRAY_SIZE(usb_ahb_parents),usb_ahb_parents);
+
+static struct zx29_hwclk sd1_work =
+{
+ .clk_en_reg ={ZX_MATRIX_CRM_BASE+0x54, 5, 1},
+ .clk_sel_reg ={ZX_MATRIX_CRM_BASE+0x50, 8, 3},
+ .clk_gate_reg ={ZX_MATRIX_CRM_BASE+0x54, 9, 1},
+};
+DEFINE_ZX29_CLK(sd1_work,peripheral_clk_ops,ARRAY_SIZE(sd1_work_parents),sd1_work_parents);
+
+static struct zx29_hwclk sd1_cdet =
+{
+ .clk_en_reg ={ZX_MATRIX_CRM_BASE+0x54, 6, 1},
+};
+DEFINE_ZX29_CLK(sd1_cdet,peripheral_clk_ops,ARRAY_SIZE(sd_cdet_parents),sd_cdet_parents);
+
+
+/**************************************************************************
+ * EDCP
+ **************************************************************************
+ */
+char* edcp_work_parents[] =
+{
+ NAME_CLK(main_clk_26m),
+ NAME_CLK(mpll_clk_208m),
+ NAME_CLK(mpll_clk_156m),
+ NAME_CLK(mpll_clk_104m),
+};
+
+static struct zx29_hwclk edcp_ahb =
+{
+ .clk_en_reg ={ZX_MATRIX_CRM_BASE+0x64, 1, 1},
+};
+DEFINE_ZX29_CLK(edcp_ahb,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk edcp_syncaxi =
+{
+ .clk_en_reg ={ZX_MATRIX_CRM_BASE+0x64, 0, 1},
+};
+DEFINE_ZX29_CLK(edcp_syncaxi,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk edcp_asyncaxi =
+{
+ .clk_en_reg ={ZX_MATRIX_CRM_BASE+0x64, 2, 1},
+ .clk_sel_reg ={ZX_MATRIX_CRM_BASE+0x50, 16, 2},
+};
+DEFINE_ZX29_CLK(edcp_asyncaxi,peripheral_clk_ops,ARRAY_SIZE(edcp_work_parents),edcp_work_parents);
+
+
+/**************************************************************************
+ * VOU
+ **************************************************************************
+ */
+static struct zx29_hwclk vou_osd_apb =
+{
+ .clk_en_reg ={ZX_MATRIX_CRM_BASE+0x168, 18, 1},
+ .clk_gate_reg ={ZX_MATRIX_CRM_BASE+0x168, 19, 1},
+};
+DEFINE_ZX29_CLK(vou_osd_apb,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk vou_osd_axi =
+{
+ .clk_en_reg ={ZX_MATRIX_CRM_BASE+0x168, 16, 1},
+ .clk_gate_reg ={ZX_MATRIX_CRM_BASE+0x168, 17, 1},
+};
+DEFINE_ZX29_CLK(vou_osd_axi,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk vou_osd_vedio =
+{
+ .clk_en_reg ={ZX_MATRIX_CRM_BASE+0x168, 15, 1},
+};
+DEFINE_ZX29_CLK(vou_osd_vedio,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk vou_osd_graphic =
+{
+ .clk_en_reg ={ZX_MATRIX_CRM_BASE+0x168, 14, 1},
+};
+DEFINE_ZX29_CLK(vou_osd_graphic,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk vou_osd_mainmix =
+{
+ .clk_en_reg ={ZX_MATRIX_CRM_BASE+0x168, 13, 1},
+};
+DEFINE_ZX29_CLK(vou_osd_mainmix,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk vou_osd_ppu =
+{
+ .clk_en_reg ={ZX_MATRIX_CRM_BASE+0x168, 12, 1},
+};
+DEFINE_ZX29_CLK(vou_osd_ppu,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk vou_csc_work =
+{
+ .clk_en_reg ={ZX_MATRIX_CRM_BASE+0x168, 6, 1},
+};
+DEFINE_ZX29_CLK(vou_csc_work,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk vou_csc_apb =
+{
+ .clk_en_reg ={ZX_MATRIX_CRM_BASE+0x168, 4, 1},
+ .clk_gate_reg ={ZX_MATRIX_CRM_BASE+0x168, 5, 1},
+};
+DEFINE_ZX29_CLK(vou_csc_apb,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk vou_axi =
+{
+ .clk_en_reg ={ZX_MATRIX_CRM_BASE+0x168, 2, 1},
+};
+DEFINE_ZX29_CLK(vou_axi,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk vou_apb =
+{
+ .clk_en_reg ={ZX_MATRIX_CRM_BASE+0x168, 1, 1},
+};
+DEFINE_ZX29_CLK(vou_apb,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk vou_work =
+{
+ .clk_en_reg ={ZX_MATRIX_CRM_BASE+0x168, 0, 1},
+};
+DEFINE_ZX29_CLK(vou_work,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk vou_mcu_work =
+{
+ .clk_en_reg ={ZX_MATRIX_CRM_BASE+0x168, 10, 1},
+};
+DEFINE_ZX29_CLK(vou_mcu_work,peripheral_clk_ops,0,NULL);
+
+static struct zx29_hwclk vou_mcu_apb =
+{
+ .clk_en_reg ={ZX_MATRIX_CRM_BASE+0x168, 8, 1},
+ .clk_gate_reg ={ZX_MATRIX_CRM_BASE+0x168, 9, 1},
+};
+DEFINE_ZX29_CLK(vou_mcu_apb,peripheral_clk_ops,0,NULL);
+
+/*
+* axi
+*
+* can select 156/26/122.88/104/78/52/39
+* can not div
+*/
+char* axi_clk_parents[] =
+{
+ NAME_CLK(main_clk_26m),
+ NAME_CLK(mpll_clk_156m),
+ NAME_CLK(mpll_clk_124m8),
+ NAME_CLK(mpll_clk_104m),
+ NAME_CLK(mpll_clk_78m),
+ NAME_CLK(mpll_clk_52m),
+ NAME_CLK(mpll_clk_39m)
+};
+static struct zx29_hwclk axi = {
+ /* reg_addr bit_offset bit_size */
+ .clk_en_reg ={0, 0, 0},
+ .clk_sel_reg={ZX_MATRIX_CRM_BASE+0x0, 0, 3},
+ .clk_div_reg={0, 0, 0},
+};
+
+DEFINE_ZX29_CLK(axi,peripheral_clk_ops,ARRAY_SIZE(axi_clk_parents),axi_clk_parents);
+
+/*
+* ap cpu
+*
+* can select 26/52/78/104/208/312/491.52/624
+* can not div, gated with hw
+*/
+char* cpu_clk_parents[] =
+{
+ NAME_CLK(main_clk_26m),
+ NAME_CLK(mpll_clk_624m),
+ NAME_CLK(mpll_clk_312m),
+ NAME_CLK(mpll_clk_156m)
+
+};
+static struct zx29_hwclk cpu_work = {
+ /* reg_addr bit_offset bit_size */
+ .clk_en_reg ={0, 0, 0},
+ .clk_sel_reg={ZX_MATRIX_CRM_BASE+0x20, 0, 2},
+ .clk_div_reg={0, 0, 0},
+};
+DEFINE_ZX29_CLK(cpu_work,cpu_clk_ops,ARRAY_SIZE(cpu_clk_parents),cpu_clk_parents);
+
+/*for zx297520v3, we do not operate pll and global resource,
+ we only support periph_clocks and clk source select */
+struct clk_lookup periph_clocks_lookups[] = {
+ /* dev_id name clk struct */
+ CLK_ZX29_CONFIG(NULL, NAME_CLK(main_clk_32k), &main_clk_32k),
+ CLK_ZX29_CONFIG(NULL, NAME_CLK(main_clk_26m), &main_clk_26m),
+ CLK_ZX29_CONFIG(NULL, NAME_CLK(mpll_clk_12m), &mpll_clk_12m),
+ CLK_ZX29_CONFIG(NULL, NAME_CLK(mpll_clk_24m), &mpll_clk_24m),
+ CLK_ZX29_CONFIG(NULL, NAME_CLK(mpll_clk_39m), &mpll_clk_39m),
+ CLK_ZX29_CONFIG(NULL, NAME_CLK(mpll_clk_52m), &mpll_clk_52m),
+ CLK_ZX29_CONFIG(NULL, NAME_CLK(mpll_clk_78m), &mpll_clk_78m),
+ CLK_ZX29_CONFIG(NULL, NAME_CLK(mpll_clk_104m), &mpll_clk_104m),
+ CLK_ZX29_CONFIG(NULL, NAME_CLK(mpll_clk_124m8), &mpll_clk_124m8),
+ CLK_ZX29_CONFIG(NULL, NAME_CLK(mpll_clk_156m), &mpll_clk_156m),
+ CLK_ZX29_CONFIG(NULL, NAME_CLK(mpll_clk_208m), &mpll_clk_208m),
+ CLK_ZX29_CONFIG(NULL, NAME_CLK(mpll_clk_312m), &mpll_clk_312m),
+ CLK_ZX29_CONFIG(NULL, NAME_CLK(mpll_clk_624m), &mpll_clk_624m),
+ CLK_ZX29_CONFIG(NULL, NAME_CLK(dpll_clk_491m52), &dpll_clk_491m52),
+ CLK_ZX29_CONFIG(NULL, NAME_CLK(dpll_clk_122m88), &dpll_clk_122m88),
+ CLK_ZX29_CONFIG(NULL, NAME_CLK(dpll_clk_81m92), &dpll_clk_81m92),
+ CLK_ZX29_CONFIG(NULL, NAME_CLK(upll_clk_480m), &upll_clk_480m),
+ CLK_ZX29_CONFIG(NULL, NAME_CLK(upll_clk_96m), &upll_clk_96m),
+ CLK_ZX29_CONFIG(NULL, NAME_CLK(gpll_clk_25m), &gpll_clk_25m),
+ CLK_ZX29_CONFIG(NULL, NAME_CLK(gpll_clk_50m), &gpll_clk_50m),
+ CLK_ZX29_CONFIG(NULL, NAME_CLK(gpll_clk_100m), &gpll_clk_100m),
+ CLK_ZX29_CONFIG(NULL, NAME_CLK(gpll_clk_178m), &gpll_clk_178m),
+ CLK_ZX29_CONFIG(NULL, NAME_CLK(gpll_clk_200m), &gpll_clk_200m),
+
+
+ CLK_ZX29_CONFIG(NULL, "axi_clk", &axi_clk),
+
+ CLK_ZX29_CONFIG(NULL, "cpu_clk", &cpu_work_clk),
+
+ CLK_ZX29_CONFIG(NULL, "hs_ahb_clk", &hs_ahb_clk),
+
+ CLK_ZX29_CONFIG("zx29_ap_wdt.0", "work_clk", &ap_wdt_work_clk),
+ CLK_ZX29_CONFIG("zx29_ap_wdt.0", "apb_clk", &ap_wdt_apb_clk),
+
+ CLK_ZX29_CONFIG("zx29_uart.0", "work_clk", &uart0_work_clk),
+ CLK_ZX29_CONFIG("zx29_uart.0", "apb_clk", &uart0_apb_clk),
+ CLK_ZX29_CONFIG("zx29_uart.1", "work_clk", &uart1_work_clk),
+ CLK_ZX29_CONFIG("zx29_uart.1", "apb_clk", &uart1_apb_clk),
+ CLK_ZX29_CONFIG("zx29_uart.2", "work_clk", &uart2_work_clk),
+ CLK_ZX29_CONFIG("zx29_uart.2", "apb_clk", &uart2_apb_clk),
+
+ CLK_ZX29_CONFIG("zx29_ap_timer0", "work_clk", &timer0_work_clk),
+ CLK_ZX29_CONFIG("zx29_ap_timer0", "apb_clk", &timer0_apb_clk),
+ CLK_ZX29_CONFIG("zx29_ap_timer1", "work_clk", &timer1_work_clk),
+ CLK_ZX29_CONFIG("zx29_ap_timer1", "apb_clk", &timer1_apb_clk),
+ CLK_ZX29_CONFIG("zx29_ap_timer2", "work_clk", &timer2_work_clk),
+ CLK_ZX29_CONFIG("zx29_ap_timer2", "apb_clk", &timer2_apb_clk),
+ CLK_ZX29_CONFIG("zx29_ap_timer3", "work_clk", &timer3_work_clk),
+ CLK_ZX29_CONFIG("zx29_ap_timer3", "apb_clk", &timer3_apb_clk),
+ CLK_ZX29_CONFIG("zx29_ap_timer4", "work_clk", &timer4_work_clk),
+ CLK_ZX29_CONFIG("zx29_ap_timer4", "apb_clk", &timer4_apb_clk),
+
+ CLK_ZX29_CONFIG("zx29_ps_timer0", "work_clk", &ps_timer0_work_clk),
+ CLK_ZX29_CONFIG("zx29_ps_timer0", "apb_clk", &ps_timer0_apb_clk),
+ CLK_ZX29_CONFIG("zx29_ps_timer1", "work_clk", &ps_timer1_work_clk),
+ CLK_ZX29_CONFIG("zx29_ps_timer1", "apb_clk", &ps_timer1_apb_clk),
+ CLK_ZX29_CONFIG("zx29_ps_timer2", "work_clk", &ps_timer2_work_clk),
+ CLK_ZX29_CONFIG("zx29_ps_timer2", "apb_clk", &ps_timer2_apb_clk),
+
+ CLK_ZX29_CONFIG("zx29_i2c.0", "work_clk", &i2c0_work_clk),
+ CLK_ZX29_CONFIG("zx29_i2c.0", "apb_clk", &i2c0_apb_clk),
+ CLK_ZX29_CONFIG("zx29_i2c.1", "work_clk", &i2c1_work_clk),
+ CLK_ZX29_CONFIG("zx29_i2c.1", "apb_clk", &i2c1_apb_clk),
+
+ CLK_ZX29_CONFIG("zx29_ssp.0", "work_clk", &ssp0_work_clk),
+ CLK_ZX29_CONFIG("zx29_ssp.0", "apb_clk", &ssp0_apb_clk),
+ CLK_ZX29_CONFIG("zx29_ssp.1", "work_clk", &ssp1_work_clk),
+ CLK_ZX29_CONFIG("zx29_ssp.1", "apb_clk", &ssp1_apb_clk),
+
+ CLK_ZX29_CONFIG("zx29_tdm.0", "work_clk", &tdm_work_clk),
+ CLK_ZX29_CONFIG("zx29_tdm.0", "apb_clk", &tdm_apb_clk),
+
+ CLK_ZX29_CONFIG("zx29_i2s.0", "work_clk", &i2s0_work_clk),
+ CLK_ZX29_CONFIG("zx29_i2s.0", "apb_clk", &i2s0_apb_clk),
+ CLK_ZX29_CONFIG("zx29_i2s.1", "work_clk", &i2s1_work_clk),
+ CLK_ZX29_CONFIG("zx29_i2s.1", "apb_clk", &i2s1_apb_clk),
+
+ CLK_ZX29_CONFIG("zx29_spifc.0", "work_clk", &spifc_work_clk),
+ CLK_ZX29_CONFIG("zx29_spifc.0", "apb_clk", &spifc_apb_clk),
+
+ CLK_ZX29_CONFIG("zx29_kpd.0", "work_clk", &kpd_work_clk),
+ CLK_ZX29_CONFIG("zx29_kpd.0", "apb_clk", &kpd_apb_clk),
+
+ CLK_ZX29_CONFIG("zx29_rtc.0", "work_clk", &rtc_work_clk),
+ CLK_ZX29_CONFIG("zx29_rtc.0", "apb_clk", &rtc_apb_clk),
+
+ CLK_ZX29_CONFIG("zx29_usim1.0", "work_clk", &usim1_work_clk),
+ CLK_ZX29_CONFIG("zx29_usim1.0", "apb_clk", &usim1_apb_clk),
+
+ CLK_ZX29_CONFIG("zx29_usb.0", "ahb_clk", &usb_ahb_clk),
+ CLK_ZX29_CONFIG("zx29_usb.0", "24m_clk", &usb_24m_clk),
+
+ CLK_ZX29_CONFIG("zx29_hsic.0", "ahb_clk", &hsic_ahb_clk),
+ CLK_ZX29_CONFIG("zx29_hsic.0", "12m_clk", &hsic_12m_clk),
+
+ CLK_ZX29_CONFIG("zx29_gmac.0", "ahb_clk", &gmac_ahb_clk),
+ CLK_ZX29_CONFIG("zx29_gmac.0", "apb_clk", &gmac_apb_clk),
+ CLK_ZX29_CONFIG("zx29_gmac.0", "rmii_clk", &gmac_rmii_clk),
+
+ CLK_ZX29_CONFIG("zx29_sd.0", "ahb_clk", &sd0_ahb_clk),
+ CLK_ZX29_CONFIG("zx29_sd.0", "work_clk", &sd0_work_clk),
+ CLK_ZX29_CONFIG("zx29_sd.0", "cdet_clk", &sd0_cdet_clk),
+
+ CLK_ZX29_CONFIG("zx29_sd.1", "ahb_clk", &sd1_ahb_clk),
+ CLK_ZX29_CONFIG("zx29_sd.1", "work_clk", &sd1_work_clk),
+ CLK_ZX29_CONFIG("zx29_sd.1", "cdet_clk", &sd1_cdet_clk),
+
+ CLK_ZX29_CONFIG("zx29_edcp.0", "ahb_clk", &edcp_ahb_clk),
+ CLK_ZX29_CONFIG("zx29_edcp.0", "sync_axi_clk", &edcp_syncaxi_clk),
+ CLK_ZX29_CONFIG("zx29_edcp.0", "async_axi_clk",&edcp_asyncaxi_clk),
+
+ CLK_ZX29_CONFIG("zx29_vou", "osd_apb_clk", &vou_osd_apb_clk),
+ CLK_ZX29_CONFIG("zx29_vou", "osd_axi_clk", &vou_osd_axi_clk),
+ CLK_ZX29_CONFIG("zx29_vou", "osd_vedio_clk", &vou_osd_vedio_clk),
+ CLK_ZX29_CONFIG("zx29_vou", "osd_graphic_clk", &vou_osd_graphic_clk),
+ CLK_ZX29_CONFIG("zx29_vou", "osd_mainmix_clk", &vou_osd_mainmix_clk),
+ CLK_ZX29_CONFIG("zx29_vou", "osd_ppu_clk", &vou_osd_ppu_clk),
+ CLK_ZX29_CONFIG("zx29_vou", "csc_work_clk", &vou_csc_work_clk),
+ CLK_ZX29_CONFIG("zx29_vou", "csc_apb_clk", &vou_csc_apb_clk),
+ CLK_ZX29_CONFIG("zx29_vou", "axi_clk", &vou_axi_clk),
+ CLK_ZX29_CONFIG("zx29_vou", "apb_clk", &vou_apb_clk),
+ CLK_ZX29_CONFIG("zx29_vou", "work_clk", &vou_work_clk),
+ CLK_ZX29_CONFIG("zx29_vou", "mcu_work_clk", &vou_mcu_work_clk),
+ CLK_ZX29_CONFIG("zx29_vou", "mcu_apb_clk", &vou_mcu_apb_clk),
+ //wifi_bt
+};
+
+unsigned int periph_clocks_lookups_num=ARRAY_SIZE(periph_clocks_lookups);
+
diff --git a/ap/os/linux/linux-3.4.x/drivers/clk/zte/clk.c b/ap/os/linux/linux-3.4.x/drivers/clk/zte/clk.c
new file mode 100644
index 0000000..e458e2b
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/clk/zte/clk.c
@@ -0,0 +1,823 @@
+/*
+ * linux/arch/arm/mach-zx297520v2/clock.c
+ *
+ * Copyright (C) 2013 ZTE-TSP <geanfeng@zte.com.cn>
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/list.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/debugfs.h>
+
+#include <linux/clkdev.h>
+#include <mach/iomap.h>
+#include <linux/clk-private.h>
+
+#include <mach/board.h>
+#include <mach/debug.h>
+#include "clk.h"
+
+static DEFINE_SPINLOCK(enable_lock);
+static DEFINE_MUTEX(prepare_lock);
+static LIST_HEAD(clocks);
+
+/*** helper functions ***/
+
+inline const char *__clk_get_name(struct clk *clk)
+{
+ return !clk ? NULL : clk->name;
+}
+
+inline struct clk_hw *__clk_get_hw(struct clk *clk)
+{
+ return !clk ? NULL : clk->hw;
+}
+
+inline u8 __clk_get_num_parents(struct clk *clk)
+{
+ return !clk ? -EINVAL : clk->num_parents;
+}
+
+inline struct clk *__clk_get_parent(struct clk *clk)
+{
+ return !clk ? NULL : clk->parent;
+}
+
+inline int __clk_get_enable_count(struct clk *clk)
+{
+ return !clk ? -EINVAL : clk->enable_count;
+}
+
+static unsigned long __clk_get_rate(struct clk *clk)
+{
+ unsigned long ret;
+
+ if (!clk) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = clk->rate;
+
+ if (clk->flags & CLK_IS_ROOT)
+ goto out;
+
+ if (!clk->parent)
+ ret = -ENODEV;
+
+out:
+ return ret;
+}
+
+inline unsigned long __clk_get_flags(struct clk *clk)
+{
+ return !clk ? -EINVAL : clk->flags;
+}
+
+static int __clk_is_enabled(struct clk *clk)
+{
+ int ret;
+
+ if (!clk)
+ return -EINVAL;
+
+ /*
+ * .is_enabled is only mandatory for clocks that gate
+ * fall back to software usage counter if .is_enabled is missing
+ */
+ if (!clk->ops->is_enabled) {
+ ret = clk->enable_count ? 1 : 0;
+ goto out;
+ }
+
+ ret = clk->ops->is_enabled(clk->hw);
+out:
+ return ret;
+}
+
+/*** clk api ***/
+
+static void __clk_disable(struct clk *clk)
+{
+ if (!clk)
+ return;
+
+ if (WARN_ON(clk->enable_count == 0))
+ return;
+
+ if (--clk->enable_count > 0)
+ return;
+
+ if (clk->ops->disable)
+ clk->ops->disable(clk->hw);
+
+ __clk_disable(clk->parent);
+}
+
+/**
+ * clk_disable - gate a clock
+ * @clk: the clk being gated
+ *
+ * clk_disable must not sleep, which differentiates it from clk_unprepare. In
+ * a simple case, clk_disable can be used instead of clk_unprepare to gate a
+ * clk if the operation is fast and will never sleep. One example is a
+ * SoC-internal clk which is controlled via simple register writes. In the
+ * complex case a clk gate operation may require a fast and a slow part. It is
+ * this reason that clk_unprepare and clk_disable are not mutually exclusive.
+ * In fact clk_disable must be called before clk_unprepare.
+ */
+void clk_disable(struct clk *clk)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&enable_lock, flags);
+ __clk_disable(clk);
+ spin_unlock_irqrestore(&enable_lock, flags);
+}
+EXPORT_SYMBOL_GPL(clk_disable);
+
+static int __clk_enable(struct clk *clk)
+{
+ int ret = 0;
+
+ if (!clk)
+ return 0;
+
+ if (clk->enable_count == 0) {
+ ret = __clk_enable(clk->parent);
+
+ if (ret)
+ return ret;
+
+ if (clk->ops->enable) {
+ ret = clk->ops->enable(clk->hw);
+ if (ret) {
+ __clk_disable(clk->parent);
+ return ret;
+ }
+ }
+ }
+
+ clk->enable_count++;
+ return 0;
+}
+
+/**
+ * clk_enable - ungate a clock
+ * @clk: the clk being ungated
+ *
+ * clk_enable must not sleep, which differentiates it from clk_prepare. In a
+ * simple case, clk_enable can be used instead of clk_prepare to ungate a clk
+ * if the operation will never sleep. One example is a SoC-internal clk which
+ * is controlled via simple register writes. In the complex case a clk ungate
+ * operation may require a fast and a slow part. It is this reason that
+ * clk_enable and clk_prepare are not mutually exclusive. In fact clk_prepare
+ * must be called before clk_enable. Returns 0 on success, -EERROR
+ * otherwise.
+ */
+int clk_enable(struct clk *clk)
+{
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&enable_lock, flags);
+ ret = __clk_enable(clk);
+ spin_unlock_irqrestore(&enable_lock, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL(clk_enable);
+
+int clk_is_enabled(struct clk *clk)
+{
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&enable_lock, flags);
+ ret = __clk_is_enabled(clk);
+ spin_unlock_irqrestore(&enable_lock, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL(clk_is_enabled);
+
+/**
+ * clk_get_rate - return the rate of clk
+ * @clk: the clk whose rate is being returned
+ *
+ * Simply returns the cached rate of the clk. Does not query the hardware. If
+ * clk is NULL then returns -EINVAL.
+ */
+unsigned long clk_get_rate(struct clk *clk)
+{
+ unsigned long rate;
+
+ mutex_lock(&prepare_lock);
+ rate = __clk_get_rate(clk);
+ mutex_unlock(&prepare_lock);
+
+ return rate;
+}
+EXPORT_SYMBOL_GPL(clk_get_rate);
+
+/**
+ * __clk_round_rate - round the given rate for a clk
+ * @clk: round the rate of this clock
+ *
+ * Caller must hold prepare_lock. Useful for clk_ops such as .set_rate
+ */
+static unsigned long __clk_round_rate(struct clk *clk, unsigned long rate, struct clk **best_parent)
+{
+ int i;
+ unsigned long tmp_rate;
+ unsigned long tmp_rate2;
+ unsigned long best_rate;
+ unsigned long diff_rate;
+ struct clk *old_parent;
+ struct clk *sel_parent;
+
+ if (!clk)
+ return -EINVAL;
+
+ if (!clk->ops->round_rate)
+ return clk->rate;
+
+ sel_parent = clk->parent;
+
+ if (clk->num_parents > 1 && (clk->flags & CLK_AUTO_ROUND_PARENT)) {
+ old_parent = clk->parent;
+ best_rate = 0;
+ diff_rate = rate;
+
+ for ( i=0; i < clk->num_parents; i++) {
+ clk->parent = clk->parents[i];
+ tmp_rate = clk->ops->round_rate(clk->hw, rate, NULL);
+
+ if (tmp_rate > rate)
+ tmp_rate2 = tmp_rate - rate;
+ else
+ tmp_rate2 = rate - tmp_rate;
+
+ if (tmp_rate2 == 0) {
+ best_rate = tmp_rate;
+ sel_parent = clk->parent;
+ break;
+ } else if (diff_rate > tmp_rate2) {
+ diff_rate = tmp_rate2;
+ best_rate = tmp_rate; /*sel match clock*/
+ sel_parent = clk->parent;
+ } else if (best_rate == 0) {
+ best_rate = tmp_rate;
+ }
+ }
+ clk->parent = old_parent;
+ }
+ else
+ {
+ best_rate = clk->ops->round_rate(clk->hw, rate, NULL);
+ }
+
+ if(best_parent)
+ *best_parent = sel_parent;
+ return best_rate;
+}
+
+/**
+ * clk_round_rate - round the given rate for a clk
+ * @clk: the clk for which we are rounding a rate
+ * @rate: the rate which is to be rounded
+ *
+ * Takes in a rate as input and rounds it to a rate that the clk can actually
+ * use which is then returned. If clk doesn't support round_rate operation
+ * then the parent rate is returned.
+ */
+long clk_round_rate(struct clk *clk, unsigned long rate)
+{
+ unsigned long ret;
+
+ mutex_lock(&prepare_lock);
+ ret = __clk_round_rate(clk, rate, NULL);
+ mutex_unlock(&prepare_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(clk_round_rate);
+
+
+/**
+ * clk_get_parent - return the parent of a clk
+ * @clk: the clk whose parent gets returned
+ *
+ * Simply returns clk->parent. Returns NULL if clk is NULL.
+ */
+struct clk *clk_get_parent(struct clk *clk)
+{
+ struct clk *parent;
+
+ mutex_lock(&prepare_lock);
+ parent = __clk_get_parent(clk);
+ mutex_unlock(&prepare_lock);
+
+ return parent;
+}
+EXPORT_SYMBOL_GPL(clk_get_parent);
+
+/**
+ * __clk_recalc_rates
+ * @clk: first clk in the subtree
+ * @msg: notification type (see include/linux/clk.h)
+ *
+ * Walks the subtree of clks starting with clk and recalculates rates as it
+ * goes. Note that if a clk does not implement the .recalc_rate callback then
+ * it is assumed that the clock will take on the rate of it's parent.
+ *
+ * clk_recalc_rates also propagates the POST_RATE_CHANGE notification,
+ * if necessary.
+ *
+ * Caller must hold prepare_lock.
+ */
+static void __clk_recalc_rates(struct clk *clk)
+{
+ unsigned long parent_rate = 0;
+
+ if (clk->parent) {
+ __clk_recalc_rates(clk->parent);
+ parent_rate = clk->parent->rate;
+ } else {
+ parent_rate = clk->rate;
+ }
+
+ if (clk->ops->recalc_rate)
+ clk->rate = clk->ops->recalc_rate(clk->hw, parent_rate);
+ else
+ clk->rate = parent_rate;
+
+}
+
+static int __clk_set_parent(struct clk *clk, struct clk *parent)
+{
+ struct clk *old_parent;
+ unsigned long flags;
+ int ret = -EINVAL;
+ u8 i;
+
+ old_parent = clk->parent;
+
+ if (!clk->parents)
+ return -EINVAL;
+
+ /*
+ * find index of new parent clock using cached parent ptrs,
+ * or if not yet cached, use string name comparison and cache
+ * them now to avoid future calls to __clk_lookup.
+ */
+ for (i = 0; i < clk->num_parents; i++) {
+ if (clk->parents && clk->parents[i] == parent)
+ break;
+ else if (!strcmp(clk->parent_names[i], parent->name)) {
+ if (clk->parents) {
+ clk->parents[i] = clk_get(NULL, parent->name);
+ clk_put(clk->parents[i]);
+ }
+ break;
+ }
+ }
+
+ if (i == clk->num_parents) {
+ pr_debug("%s: clock %s is not a possible parent of clock %s\n",
+ __func__, parent->name, clk->name);
+ goto out;
+ }
+
+ /* FIXME replace with clk_is_enabled(clk) someday */
+ spin_lock_irqsave(&enable_lock, flags);
+ if (clk->enable_count)
+ __clk_enable(parent);
+ spin_unlock_irqrestore(&enable_lock, flags);
+
+ /* change clock input source */
+ ret = clk->ops->set_parent(clk->hw, i);
+
+ /* clean up old prepare and enable */
+ spin_lock_irqsave(&enable_lock, flags);
+ if (clk->enable_count)
+ __clk_disable(old_parent);
+ spin_unlock_irqrestore(&enable_lock, flags);
+
+out:
+ return ret;
+}
+
+/**
+ * clk_set_parent - switch the parent of a mux clk
+ * @clk: the mux clk whose input we are switching
+ * @parent: the new input to clk
+ *
+ * Re-parent clk to use parent as it's new input source. If clk has the
+ * CLK_SET_PARENT_GATE flag set then clk must be gated for this
+ * operation to succeed. After successfully changing clk's parent
+ * clk_set_parent will update the clk topology, sysfs topology and
+ * propagate rate recalculation via __clk_recalc_rates. Returns 0 on
+ * success, -EERROR otherwise.
+ */
+int clk_set_parent(struct clk *clk, struct clk *parent)
+{
+ int ret = 0;
+
+ if (!clk || !clk->ops)
+ return -EINVAL;
+
+ if (!clk->ops->set_parent || !clk->num_parents)
+ return -ENOSYS;
+
+ /* prevent racing with updates to the clock topology */
+ mutex_lock(&prepare_lock);
+
+ if (clk->parent == parent)
+ goto out;
+
+ if(clk->flags & CLK_AUTO_ROUND_PARENT) {
+ clk->flags &= ~CLK_AUTO_ROUND_PARENT;/*once set parent, cancel round parent*/
+ }
+
+ ret = __clk_set_parent(clk, parent);
+
+ if (ret) {
+ __clk_recalc_rates(clk);
+ goto out;
+ }
+
+ clk->parent = parent;
+
+ __clk_recalc_rates(clk);
+
+out:
+ mutex_unlock(&prepare_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(clk_set_parent);
+/**
+ * clk_set_rate - specify a new rate for clk
+ * @clk: the clk whose rate is being changed
+ * @rate: the new rate for clk
+ *
+ * In the simplest case clk_set_rate will only change the rate of clk.
+ *
+ * If clk has the CLK_SET_RATE_GATE flag set and it is enabled this call
+ * will fail; only when the clk is disabled will it be able to change
+ * its rate.
+ *
+ * Setting the CLK_SET_RATE_PARENT flag allows clk_set_rate to
+ * recursively propagate up to clk's parent; whether or not this happens
+ * depends on the outcome of clk's .round_rate implementation. If
+ * *parent_rate is 0 after calling .round_rate then upstream parent
+ * propagation is ignored. If *parent_rate comes back with a new rate
+ * for clk's parent then we propagate up to clk's parent and set it's
+ * rate. Upward propagation will continue until either a clk does not
+ * support the CLK_SET_RATE_PARENT flag or .round_rate stops requesting
+ * changes to clk's parent_rate. If there is a failure during upstream
+ * propagation then clk_set_rate will unwind and restore each clk's rate
+ * that had been successfully changed. Afterwards a rate change abort
+ * notification will be propagated downstream, starting from the clk
+ * that failed.
+ *
+ * At the end of all of the rate setting, clk_set_rate internally calls
+ * __clk_recalc_rates and propagates the rate changes downstream,
+ * starting from the highest clk whose rate was changed. This has the
+ * added benefit of propagating post-rate change notifiers.
+ *
+ * Note that while post-rate change and rate change abort notifications
+ * are guaranteed to be sent to a clk only once per call to
+ * clk_set_rate, pre-change notifications will be sent for every clk
+ * whose rate is changed. Stacking pre-change notifications is noisy
+ * for the drivers subscribed to them, but this allows drivers to react
+ * to intermediate clk rate changes up until the point where the final
+ * rate is achieved at the end of upstream propagation.
+ *
+ * Returns 0 on success, -EERROR otherwise.
+ */
+int clk_set_rate(struct clk *clk, unsigned long rate)
+{
+ struct clk *best_parent = NULL;
+
+ /* prevent racing with updates to the clock topology */
+ mutex_lock(&prepare_lock);
+
+ /* bail early if nothing to do */
+ if (clk == NULL || rate == clk->rate)
+ goto out;
+
+ clk->new_rate = __clk_round_rate(clk, rate, &best_parent);
+
+ if(clk->num_parents && best_parent != NULL && best_parent != clk->parent) {
+ __clk_set_parent(clk, best_parent);
+ clk->parent = best_parent;
+ }
+
+ /* change the rates */
+ if (clk->ops->set_rate)
+ clk->ops->set_rate(clk->hw, clk->new_rate);
+
+ if (clk->ops->recalc_rate)
+ __clk_recalc_rates(clk);
+
+ WARN(!clk->parent,"%s,clk %s 's parent is NULL.\n",__func__, clk->name);
+
+ mutex_unlock(&prepare_lock);
+
+ return 0;
+out:
+ mutex_unlock(&prepare_lock);
+
+ return -ENOSYS;
+}
+EXPORT_SYMBOL(clk_set_rate);
+/**
+ * clk_set_auto_gate - set the clock auto gate
+ * @clk: clock source
+ * @enable: enable auto gate true or false
+ *
+ * Returns success (0) or negative errno.
+ */
+int clk_set_auto_gate(struct clk *clk, bool enable)
+{
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&enable_lock, flags);
+ if (clk->ops->set_auto_gate)
+ ret = clk->ops->set_auto_gate(clk->hw, enable);
+ spin_unlock_irqrestore(&enable_lock, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(clk_set_auto_gate);
+
+/**
+ * clk_register - allocate a new clock, register it and return an opaque cookie
+ * @dev: device that is registering this clock
+ * @name: clock name
+ * @ops: operations this clock supports
+ * @hw: link to hardware-specific clock data
+ * @parent_names: array of string names for all possible parents
+ * @num_parents: number of possible parents
+ * @flags: framework-level hints and quirks
+ *
+ * clk_register is the primary interface for populating the clock tree with new
+ * clock nodes. It returns a pointer to the newly allocated struct clk which
+ * cannot be dereferenced by driver code but may be used in conjuction with the
+ * rest of the clock API.
+ */
+static int clk_register(struct clk *clk)
+{
+ int i, index;
+
+ if(clk == NULL)
+ return -EINVAL;
+
+ if(clk->hw != NULL)
+ clk->hw->clk = clk;
+
+ /* throw a WARN if any entries in parent_names are NULL */
+ for (i = 0; i < clk->num_parents; i++)
+ WARN(!clk->parent_names[i],
+ "%s: invalid NULL in %s's .parent_names\n",
+ __func__, clk->name);
+
+ /*
+ * Allocate an array of struct clk *'s to avoid unnecessary string
+ * look-ups of clk's possible parents. This can fail for clocks passed
+ * in to clk_init during early boot; thus any access to clk->parents[]
+ * must always check for a NULL pointer and try to populate it if
+ * necessary.
+ *
+ * If clk->parents is not NULL we skip this entire block. This allows
+ * for clock drivers to statically initialize clk->parents.
+ */
+ for (i = 0; i < clk->num_parents; i++){
+ clk->parents[i] = clk_get(NULL,clk->parent_names[i]);
+ clk_put(clk->parents[i]);
+ }
+
+ /*get clk's parent used*/
+ if(clk->num_parents) {
+ index = clk->ops->get_parent(clk->hw);
+ if(index < clk->num_parents)
+ clk->parent = clk->parents[index];
+ else
+ WARN(1,"clk %s get parent error.\n",clk->name);
+ }
+
+ /*
+ * Set clk's rate. The preferred method is to use .recalc_rate. For
+ * simple clocks and lazy developers the default fallback is to use the
+ * parent's rate. If a clock doesn't have a parent (or is orphaned)
+ * then rate is set to zero.
+ */
+ __clk_recalc_rates(clk);
+
+ /*
+ * optional platform-specific magic
+ *
+ * The .init callback is not used by any of the basic clock types, but
+ * exists for weird hardware that must perform initialization magic.
+ * Please consider other ways of solving initialization problems before
+ * using this callback, as it's use is discouraged.
+ */
+ if (clk->ops->init)
+ clk->ops->init(clk->hw);
+
+ return 0;
+}
+/**
+ * clk_disable_unused - disable unused clk
+ */
+static void clk_disable_unused(struct clk *clk)
+{
+ unsigned long flags;
+
+ if (!clk)
+ goto out;
+
+ spin_lock_irqsave(&enable_lock, flags);
+
+ if (clk->enable_count)
+ goto unlock_out;
+
+ if (clk->flags & CLK_IGNORE_UNUSED)
+ goto unlock_out;
+
+ if (__clk_is_enabled(clk) && clk->ops->disable) {
+ clk->ops->disable(clk->hw);
+ printk(KERN_DEBUG "clk %s: start disabled\n",clk->name);
+ }
+
+unlock_out:
+ spin_unlock_irqrestore(&enable_lock, flags);
+
+out:
+ return;
+}
+/**
+ * clk_show - print clock debug info
+ */
+static int clk_show(struct seq_file *s, void *v)
+{
+ struct clk *clk;
+
+ seq_printf(s, "%-20s %-20s %-9s %-9s\n", "name","parent","enable","rate");
+ mutex_lock(&prepare_lock);
+ list_for_each_entry(clk, &clocks, list) {
+ if(clk->parent)
+ seq_printf(s, "%-20s %-20s %-9u %-9lu\n", clk->name, clk->parent->name, \
+ clk->enable_count, clk->rate);
+ else
+ seq_printf(s, "%-20s %-20s %-9u %-9lu\n", clk->name, "root", \
+ clk->enable_count, clk->rate);
+ }
+ mutex_unlock(&prepare_lock);
+ return 0;
+}
+
+/**
+ * clk_open
+ */
+static int clk_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, clk_show, inode->i_private);
+}
+
+/**
+ * clock debug fs
+ */
+struct dentry * clk_debugfs = NULL;
+static const struct file_operations clk_debugfs_fops = {
+ .owner = THIS_MODULE,
+ .open = clk_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+static void clk_debugfs_init(void)
+{
+ clk_debugfs = debugfs_create_file("clocks", S_IRUSR, NULL,NULL,
+ &clk_debugfs_fops);
+ return ;
+}
+
+/**
+ * "/sys/zte/test/clk_info"
+ */
+static ssize_t clk_info_show(struct kobject *kobj, struct kobj_attribute *attr,
+ char *buf)
+{
+ char *s = buf;
+ struct clk *clk;
+
+ s += sprintf(s, "%-20s %-20s %-9s %-9s\n", "name","parent","enable","rate");
+ mutex_lock(&prepare_lock);
+ list_for_each_entry(clk, &clocks, list) {
+ if(clk->parent)
+ s += sprintf(s, "%-20s %-20s %-9u %-9lu\n", clk->name, clk->parent->name, \
+ clk->enable_count, clk->rate);
+ else
+ s += sprintf(s, "%-20s %-20s %-9u %-9lu\n", clk->name, "root", \
+ clk->enable_count, clk->rate);
+ }
+ mutex_unlock(&prepare_lock);
+
+ return (s - buf);
+}
+
+static ssize_t clk_info_store(struct kobject *kobj, struct kobj_attribute *attr,
+ const char *buf, size_t n)
+{
+
+ return (n);
+}
+
+zte_attr(clk_info);
+
+static struct attribute *g[] = {
+ &clk_info_attr.attr,
+ NULL,
+};
+
+static struct attribute_group zx29_clk_attribute_group = {
+ .attrs = g,
+};
+int __init zx_clk_test_init(void)
+{
+ int ret;
+
+ ret = sysfs_create_group(zx_test_kobj, &zx29_clk_attribute_group);
+
+ pr_info("[DEBUG] create test clk sysfs interface OK.\n");
+
+ return 0;
+}
+
+/* zx29 clocks init*/
+
+static void __init __zx29_clock_init(struct clk_lookup *clock_tbl, unsigned num_clocks)
+{
+ unsigned n;
+ struct clk *clk;
+
+ mutex_lock(&prepare_lock);
+ /*register to devlist*/
+ for (n = 0; n < num_clocks; n++) {
+ clkdev_add(&clock_tbl[n]);
+ list_add_tail(&clock_tbl[n].clk->list, &clocks);
+ }
+ /*register to clktree*/
+ for (n = 0; n < num_clocks; n++) {
+ clk = clock_tbl[n].clk;
+ clk_register(clk);
+ }
+ mutex_unlock(&prepare_lock);
+
+ return ;
+}
+
+void __init zx29_clock_init(void)
+{
+ __zx29_clock_init(periph_clocks_lookups, periph_clocks_lookups_num);
+
+ pr_info("[CLK] zx29 tsp clk init ok.\n");
+}
+
+/*
+ * Several unused clocks may be active. Turn them off.
+ */
+static int __init zx29_disable_unused_clocks(void)
+{
+ struct clk *clk;
+
+ mutex_lock(&prepare_lock);
+ list_for_each_entry(clk, &clocks, list) {
+ printk(KERN_DEBUG "clk %s: rate = %lu\n",clk->name,clk->rate);
+//zxp clk_disable_unused(clk);
+ }
+ mutex_unlock(&prepare_lock);
+
+ clk_debugfs_init();
+
+ return 0;
+}
+late_initcall(zx29_disable_unused_clocks);
diff --git a/ap/os/linux/linux-3.4.x/drivers/clk/zte/clk.h b/ap/os/linux/linux-3.4.x/drivers/clk/zte/clk.h
new file mode 100644
index 0000000..0d5a5fb
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/clk/zte/clk.h
@@ -0,0 +1,130 @@
+/*
+ * linux/arch/arm/mach-zx297520v2/mach/clock.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef CLOCK_H
+#define CLOCK_H
+
+#include <linux/clkdev.h>
+#include <mach/iomap.h>
+#include <mach/board.h>
+
+#define name_main_clk_32k "main_clk_32k"
+#define name_main_clk_26m "main_clk_26m"
+#define name_mpll_clk_12m "mpll_clk_12m"
+#define name_mpll_clk_24m "mpll_clk_24m"
+#define name_mpll_clk_39m "mpll_clk_39m"
+#define name_mpll_clk_52m "mpll_clk_52m"
+#define name_mpll_clk_78m "mpll_clk_78m"
+#define name_mpll_clk_104m "mpll_clk_104m"
+#define name_mpll_clk_124m8 "mpll_clk_124m8"
+#define name_mpll_clk_156m "mpll_clk_156m"
+#define name_mpll_clk_208m "mpll_clk_208m"
+#define name_mpll_clk_312m "mpll_clk_312m"
+#define name_mpll_clk_624m "mpll_clk_624m"
+#define name_dpll_clk_491m52 "dpll_clk_491m52"
+#define name_dpll_clk_122m88 "dpll_clk_122m88"
+#define name_dpll_clk_81m92 "dpll_clk_81m92"
+#define name_upll_clk_480m "upll_clk_480m"
+#define name_upll_clk_96m "upll_clk_96m"
+#define name_gpll_clk_25m "gpll_clk_25m"
+#define name_gpll_clk_50m "gpll_clk_50m"
+#define name_gpll_clk_100m "gpll_clk_100m"
+#define name_gpll_clk_178m "gpll_clk_178m"
+#define name_gpll_clk_200m "gpll_clk_200m"
+
+/*
+ * flags used across common struct clk. these flags should only affect the
+ * top-level framework. custom flags for dealing with hardware specifics
+ * belong in struct clk_foo
+ */
+#define CLK_IGNORE_UNUSED BIT(3) /* do not gate even if unused */
+#define CLK_IS_ROOT BIT(4) /* root clk, has no parent */
+#define CLK_AUTO_ROUND_PARENT BIT(5) /* auto round parent to select rate */
+#define CLK_NO_ODD_DIV BIT(6) /* no div or even div */
+
+struct clk;
+
+/**
+ * struct clk_hw - handle for traversing from a struct clk to its corresponding
+ * hardware-specific structure. struct clk_hw should be declared within struct
+ * clk_foo and then referenced by the struct clk instance that uses struct
+ * clk_foo's clk_ops
+ *
+ * clk: pointer to the struct clk instance that points back to this struct
+ * clk_hw instance
+ */
+struct clk_hw
+{
+ struct clk *clk;
+};
+
+struct clk_ops {
+ int (*prepare)(struct clk_hw *hw);
+ void (*unprepare)(struct clk_hw *hw);
+
+ int (*enable)(struct clk_hw *hw);
+ void (*disable)(struct clk_hw *hw);
+ int (*is_enabled)(struct clk_hw *hw);
+
+ unsigned long (*recalc_rate)(struct clk_hw *hw,
+ unsigned long parent_rate);
+ long (*round_rate)(struct clk_hw *hw, unsigned long,
+ unsigned long *);
+ int (*set_rate)(struct clk_hw *hw, unsigned long);
+
+ int (*set_parent)(struct clk_hw *hw, u8 index);
+ u8 (*get_parent)(struct clk_hw *hw);
+ void (*init)(struct clk_hw *hw); /*called when register to clk_dev*/
+ int (*set_auto_gate)(struct clk_hw *hw, bool enable);
+};
+struct clk
+{
+ const char *name;
+ const struct clk_ops *ops;
+ struct clk_hw *hw;
+ struct clk *parent;
+ char **parent_names;
+ struct clk **parents;
+ u8 num_parents;
+ unsigned long rate;
+ unsigned long new_rate;
+ unsigned long flags;
+ unsigned int enable_count;
+ struct list_head list;
+};
+
+#define CLK_ZX29_CONFIG(dev, con, ck) \
+ { \
+ .dev_id = dev, \
+ .con_id = con, \
+ .clk = ck, \
+ }
+
+#define NAME_CLK(clk) name_##clk
+
+#define DEFINE_ZX29_ROOT_CLK(root_clk,clk_rate) \
+static struct clk root_clk = { \
+ .name = NAME_CLK(root_clk), \
+ .rate = clk_rate, \
+ .ops = &root_clk_ops, \
+ .flags = CLK_IS_ROOT, \
+}
+
+struct zx29_hwclk
+{
+ struct clk_hw hw;
+ struct zx29_reg_conf clk_en_reg;
+ struct zx29_reg_conf clk_sel_reg;
+ struct zx29_reg_conf clk_div_reg;
+ struct zx29_reg_conf clk_gate_reg;
+};
+
+#define to_zx29_hwclk(_hw) container_of(_hw, struct zx29_hwclk, hw)
+
+extern struct clk_lookup periph_clocks_lookups[];
+extern unsigned int periph_clocks_lookups_num;
+#endif