It adds a generic clock implementation which applies on several
modern imx generations.

Signed-off-by: Shawn Guo <[email protected]>
---
 arch/arm/mach-imx/clock.c |  222 +++++++++++++++++++++++++++++++++++++++++++++
 arch/arm/mach-imx/clock.h |   64 +++++++++++++
 2 files changed, 286 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/mach-imx/clock.c
 create mode 100644 arch/arm/mach-imx/clock.h

diff --git a/arch/arm/mach-imx/clock.c b/arch/arm/mach-imx/clock.c
new file mode 100644
index 0000000..6243622
--- /dev/null
+++ b/arch/arm/mach-imx/clock.c
@@ -0,0 +1,222 @@
+/*
+ * Copyright 2011 Freescale Semiconductor, Inc.
+ * Copyright 2011 Linaro Ltd.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <asm/processor.h>
+#include "clock.h"
+
+int clk_imx_enable(struct clk_hw *hw)
+{
+       struct clk_imx_gate *g = to_clk_imx(hw)->gate;
+       u32 val;
+
+       if (!g)
+               return 0;
+
+       val = readl_relaxed(g->reg);
+       if (g->gate_set_bit)
+               val &= ~g->mask;
+       else
+               val |= g->mask;
+       writel_relaxed(val, g->reg);
+
+       return 0;
+}
+
+void clk_imx_disable(struct clk_hw *hw)
+{
+       struct clk_imx_gate *g = to_clk_imx(hw)->gate;
+       u32 val;
+
+       if (!g)
+               return;
+
+       val = readl_relaxed(g->reg);
+       if (g->gate_set_bit)
+               val |= g->mask;
+       else
+               val &= ~g->mask;
+       writel_relaxed(val, g->reg);
+}
+
+struct clk *clk_imx_get_parent(struct clk_hw *hw)
+{
+       struct clk_imx_mux *m = to_clk_imx(hw)->mux;
+       u32 i;
+
+       if (!m)
+               return to_clk_imx(hw)->parent->clk;
+
+       i = readl_relaxed(m->reg) >> m->shift;
+       i &= (1 << m->width) - 1;
+
+       if (i >= m->num_parents)
+               return ERR_PTR(-EINVAL);
+
+       return m->parents[i]->clk;
+}
+
+static int clk_imx_busy_wait(struct clk_imx_busy *b)
+{
+       int timeout = 0x100000;
+
+       while ((readl_relaxed(b->reg) & b->mask) && --timeout)
+               cpu_relax();
+
+       if (unlikely(!timeout))
+               return -EBUSY;
+
+       return 0;
+}
+
+static int clk_imx_set_parent(struct clk_hw *hw, struct clk *parent)
+{
+       struct clk_imx_mux *m = to_clk_imx(hw)->mux;
+       int i;
+       u32 val;
+
+       if (!m)
+               return -EINVAL;
+
+       for (i = 0; i < m->num_parents; i++)
+               if (parent == m->parents[i]->clk)
+                       break;
+
+       if (i == m->num_parents)
+               return -EINVAL;
+
+       val = readl_relaxed(m->reg);
+       val &= ~(((1 << m->width) - 1) << m->shift);
+       val |= i << m->shift;
+       writel_relaxed(val, m->reg);
+
+       return (m->busy) ? clk_imx_busy_wait(m->busy) : 0;
+}
+
+static unsigned long clk_imx_recalc_rate(struct clk_hw *hw)
+{
+       struct clk_imx_div *d = to_clk_imx(hw)->div;
+       u32 val, pred, podf;
+
+       if (!d)
+               return clk_get_rate(clk_get_parent(hw->clk));
+
+       val = readl_relaxed(d->reg);
+       pred = (val >> d->shift_pred & ((1 << d->width_pred) - 1)) + 1;
+       podf = (val >> d->shift_podf & ((1 << d->width_podf) - 1)) + 1;
+
+       return clk_get_rate(clk_get_parent(hw->clk)) / (pred * podf);
+}
+
+static void calc_pred_podf_dividers(u32 div, u32 *pred, u32 *podf)
+{
+       u32 min_pred, temp_pred, old_err, err;
+
+       if (div >= 512) {
+               *pred = 8;
+               *podf = 64;
+       } else if (div >= 8) {
+               min_pred = (div - 1) / 64 + 1;
+               old_err = 8;
+               for (temp_pred = 8; temp_pred >= min_pred; temp_pred--) {
+                       err = div % temp_pred;
+                       if (err == 0) {
+                               *pred = temp_pred;
+                               break;
+                       }
+                       err = temp_pred - err;
+                       if (err < old_err) {
+                               old_err = err;
+                               *pred = temp_pred;
+                       }
+               }
+               *podf = (div + *pred - 1) / *pred;
+       } else if (div < 8) {
+               *pred = div;
+               *podf = 1;
+       }
+}
+
+static long clk_imx_round_rate(struct clk_hw *hw, unsigned long rate)
+{
+       struct clk_imx_div *d = to_clk_imx(hw)->div;
+       u32 div, div_max, pred = 0, podf;
+       unsigned long parent_rate = clk_get_rate(clk_get_parent(hw->clk));
+
+       if (!d)
+               return -EINVAL;
+
+       div = parent_rate / rate;
+       if (div == 0 || parent_rate % rate)
+               div++;
+
+       if (d->width_pred) {
+               calc_pred_podf_dividers(div, &pred, &podf);
+               div = pred * podf;
+       } else {
+               div_max = 1 << d->width_podf;
+               if (div > div_max)
+                       div = div_max;
+       }
+
+       return parent_rate / div;
+}
+
+static int clk_imx_set_rate(struct clk_hw *hw, unsigned long rate,
+                           unsigned long *parent_rate)
+{
+       struct clk_imx_div *d = to_clk_imx(hw)->div;
+       u32 val, div, pred = 0, podf;
+
+       if (!d)
+               return -EINVAL;
+
+       *parent_rate = clk_get_rate(clk_get_parent(hw->clk));
+
+       div = *parent_rate / rate;
+       if (div == 0)
+               div++;
+
+       if ((*parent_rate / div != rate) || div >
+           (1 << d->width_pred) * (1 << d->width_podf))
+               return -EINVAL;
+
+       if (d->width_pred) {
+               calc_pred_podf_dividers(div, &pred, &podf);
+       } else {
+               pred = 1;
+               podf = div;
+       }
+
+       val = readl_relaxed(d->reg);
+       val &= ~(((1 << d->width_pred) - 1) << d->shift_pred);
+       val &= ~(((1 << d->width_podf) - 1) << d->shift_podf);
+       val |= (pred - 1) << d->shift_pred;
+       val |= (podf - 1) << d->shift_podf;
+       writel_relaxed(val, d->reg);
+
+       return (d->busy) ? clk_imx_busy_wait(d->busy) : 0;
+}
+
+const struct clk_hw_ops clk_imx_ops = {
+       .enable         = clk_imx_enable,
+       .disable        = clk_imx_disable,
+       .recalc_rate    = clk_imx_recalc_rate,
+       .round_rate     = clk_imx_round_rate,
+       .set_rate       = clk_imx_set_rate,
+       .get_parent     = clk_imx_get_parent,
+       .set_parent     = clk_imx_set_parent,
+};
diff --git a/arch/arm/mach-imx/clock.h b/arch/arm/mach-imx/clock.h
new file mode 100644
index 0000000..49e42b9
--- /dev/null
+++ b/arch/arm/mach-imx/clock.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2011 Freescale Semiconductor, Inc.
+ * Copyright 2011 Linaro Ltd.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+#ifndef __MACH_IMX_CLK_H
+#define __MACH_IMX_CLK_H
+
+#include <linux/types.h>
+#include <linux/clk.h>
+
+struct clk_imx_busy {
+       void __iomem *reg;
+       u32 mask;
+};
+
+struct clk_imx_gate {
+       void __iomem *reg;
+       u32 mask;
+       int gate_set_bit;
+       int powerup_set_bit;
+};
+
+struct clk_imx_div {
+       void __iomem *reg;
+       u32 shift_pred;
+       u32 width_pred;
+       u32 shift_podf;
+       u32 width_podf;
+       struct clk_imx_busy *busy;
+};
+
+struct clk_imx_mux {
+       void __iomem *reg;
+       u32 shift;
+       u32 width;
+       struct clk_imx_busy *busy;
+       struct clk_hw **parents;
+       int num_parents;
+};
+
+struct clk_hw_imx {
+       struct clk_hw hw;
+       struct clk_imx_gate *gate;
+       struct clk_imx_div *div;
+       struct clk_imx_mux *mux;
+       struct clk_hw *parent;
+};
+
+#define to_clk_imx(c) container_of(c, struct clk_hw_imx, hw)
+
+extern const struct clk_hw_ops clk_imx_ops;
+
+extern int clk_imx_enable(struct clk_hw *hw);
+extern void clk_imx_disable(struct clk_hw *hw);
+extern struct clk *clk_imx_get_parent(struct clk_hw *hw);
+
+#endif /* __MACH_IMX_CLK_H */
-- 
1.7.4.1

_______________________________________________
devicetree-discuss mailing list
[email protected]
https://lists.ozlabs.org/listinfo/devicetree-discuss

Reply via email to