ARM: imx: add common clock support for clk busy
authorShawn Guo <shawn.guo@linaro.org>
Wed, 4 Apr 2012 08:20:56 +0000 (16:20 +0800)
committerSascha Hauer <s.hauer@pengutronix.de>
Wed, 2 May 2012 10:08:26 +0000 (12:08 +0200)
Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
arch/arm/mach-imx/Makefile
arch/arm/mach-imx/clk-busy.c [new file with mode: 0644]
arch/arm/mach-imx/clk.h

index 4d6be8d33a263b614756560757dfcd060fa04743..ae0a77960c22f9dda2c288eebd131bac6f672399 100644 (file)
@@ -12,7 +12,7 @@ obj-$(CONFIG_SOC_IMX35) += mm-imx3.o cpu-imx35.o clock-imx35.o ehci-imx35.o pm-i
 obj-$(CONFIG_SOC_IMX5) += cpu-imx5.o mm-imx5.o clock-mx51-mx53.o ehci-imx5.o pm-imx5.o cpu_op-mx51.o
 
 obj-$(CONFIG_COMMON_CLK) += clk-pllv1.o clk-pllv2.o clk-pllv3.o clk-gate2.o \
-                           clk-pfd.o
+                           clk-pfd.o clk-busy.o
 
 # Support for CMOS sensor interface
 obj-$(CONFIG_MX1_VIDEO) += mx1-camera-fiq.o mx1-camera-fiq-ksym.o
diff --git a/arch/arm/mach-imx/clk-busy.c b/arch/arm/mach-imx/clk-busy.c
new file mode 100644 (file)
index 0000000..1a7a8dd
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * Copyright 2012 Freescale Semiconductor, Inc.
+ * Copyright 2012 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/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/err.h>
+#include "clk.h"
+
+static int clk_busy_wait(void __iomem *reg, u8 shift)
+{
+       unsigned long timeout = jiffies + msecs_to_jiffies(10);
+
+       while (readl_relaxed(reg) & (1 << shift))
+               if (time_after(jiffies, timeout))
+                       return -ETIMEDOUT;
+
+       return 0;
+}
+
+struct clk_busy_divider {
+       struct clk_divider div;
+       const struct clk_ops *div_ops;
+       void __iomem *reg;
+       u8 shift;
+};
+
+static inline struct clk_busy_divider *to_clk_busy_divider(struct clk_hw *hw)
+{
+       struct clk_divider *div = container_of(hw, struct clk_divider, hw);
+
+       return container_of(div, struct clk_busy_divider, div);
+}
+
+static unsigned long clk_busy_divider_recalc_rate(struct clk_hw *hw,
+                                                 unsigned long parent_rate)
+{
+       struct clk_busy_divider *busy = to_clk_busy_divider(hw);
+
+       return busy->div_ops->recalc_rate(&busy->div.hw, parent_rate);
+}
+
+static long clk_busy_divider_round_rate(struct clk_hw *hw, unsigned long rate,
+                                       unsigned long *prate)
+{
+       struct clk_busy_divider *busy = to_clk_busy_divider(hw);
+
+       return busy->div_ops->round_rate(&busy->div.hw, rate, prate);
+}
+
+static int clk_busy_divider_set_rate(struct clk_hw *hw, unsigned long rate,
+               unsigned long parent_rate)
+{
+       struct clk_busy_divider *busy = to_clk_busy_divider(hw);
+       int ret;
+
+       ret = busy->div_ops->set_rate(&busy->div.hw, rate, parent_rate);
+       if (!ret)
+               ret = clk_busy_wait(busy->reg, busy->shift);
+
+       return ret;
+}
+
+static struct clk_ops clk_busy_divider_ops = {
+       .recalc_rate = clk_busy_divider_recalc_rate,
+       .round_rate = clk_busy_divider_round_rate,
+       .set_rate = clk_busy_divider_set_rate,
+};
+
+struct clk *imx_clk_busy_divider(const char *name, const char *parent_name,
+                                void __iomem *reg, u8 shift, u8 width,
+                                void __iomem *busy_reg, u8 busy_shift)
+{
+       struct clk_busy_divider *busy;
+       struct clk *clk;
+       struct clk_init_data init;
+
+       busy = kzalloc(sizeof(*busy), GFP_KERNEL);
+       if (!busy)
+               return ERR_PTR(-ENOMEM);
+
+       busy->reg = busy_reg;
+       busy->shift = busy_shift;
+
+       busy->div.reg = reg;
+       busy->div.shift = shift;
+       busy->div.width = width;
+       busy->div.lock = &imx_ccm_lock;
+       busy->div_ops = &clk_divider_ops;
+
+       init.name = name;
+       init.ops = &clk_busy_divider_ops;
+       init.flags = CLK_SET_RATE_PARENT;
+       init.parent_names = &parent_name;
+       init.num_parents = 1;
+
+       busy->div.hw.init = &init;
+
+       clk = clk_register(NULL, &busy->div.hw);
+       if (!clk)
+               kfree(busy);
+
+       return clk;
+}
+
+struct clk_busy_mux {
+       struct clk_mux mux;
+       const struct clk_ops *mux_ops;
+       void __iomem *reg;
+       u8 shift;
+};
+
+static inline struct clk_busy_mux *to_clk_busy_mux(struct clk_hw *hw)
+{
+       struct clk_mux *mux = container_of(hw, struct clk_mux, hw);
+
+       return container_of(mux, struct clk_busy_mux, mux);
+}
+
+static u8 clk_busy_mux_get_parent(struct clk_hw *hw)
+{
+       struct clk_busy_mux *busy = to_clk_busy_mux(hw);
+
+       return busy->mux_ops->get_parent(&busy->mux.hw);
+}
+
+static int clk_busy_mux_set_parent(struct clk_hw *hw, u8 index)
+{
+       struct clk_busy_mux *busy = to_clk_busy_mux(hw);
+       int ret;
+
+       ret = busy->mux_ops->set_parent(&busy->mux.hw, index);
+       if (!ret)
+               ret = clk_busy_wait(busy->reg, busy->shift);
+
+       return ret;
+}
+
+struct clk_ops clk_busy_mux_ops = {
+       .get_parent = clk_busy_mux_get_parent,
+       .set_parent = clk_busy_mux_set_parent,
+};
+
+struct clk *imx_clk_busy_mux(const char *name, void __iomem *reg, u8 shift,
+                            u8 width, void __iomem *busy_reg, u8 busy_shift,
+                            const char **parent_names, int num_parents)
+{
+       struct clk_busy_mux *busy;
+       struct clk *clk;
+       struct clk_init_data init;
+
+       busy = kzalloc(sizeof(*busy), GFP_KERNEL);
+       if (!busy)
+               return ERR_PTR(-ENOMEM);
+
+       busy->reg = busy_reg;
+       busy->shift = busy_shift;
+
+       busy->mux.reg = reg;
+       busy->mux.shift = shift;
+       busy->mux.width = width;
+       busy->mux.lock = &imx_ccm_lock;
+       busy->mux_ops = &clk_mux_ops;
+
+       init.name = name;
+       init.ops = &clk_busy_mux_ops;
+       init.flags = 0;
+       init.parent_names = parent_names;
+       init.num_parents = num_parents;
+
+       busy->mux.hw.init = &init;
+
+       clk = clk_register(NULL, &busy->mux.hw);
+       if (IS_ERR(clk))
+               kfree(busy);
+
+       return clk;
+}
index 52970996f39a1a5acb1b13dcb1781a6b58a839a9..1bf64fe2523c5b58b264d7f6cead63568cf2d8af 100644 (file)
@@ -39,6 +39,14 @@ static inline struct clk *imx_clk_gate2(const char *name, const char *parent,
 struct clk *imx_clk_pfd(const char *name, const char *parent_name,
                void __iomem *reg, u8 idx);
 
+struct clk *imx_clk_busy_divider(const char *name, const char *parent_name,
+                                void __iomem *reg, u8 shift, u8 width,
+                                void __iomem *busy_reg, u8 busy_shift);
+
+struct clk *imx_clk_busy_mux(const char *name, void __iomem *reg, u8 shift,
+                            u8 width, void __iomem *busy_reg, u8 busy_shift,
+                            const char **parent_names, int num_parents);
+
 static inline struct clk *imx_clk_fixed(const char *name, int rate)
 {
        return clk_register_fixed_rate(NULL, name, NULL, CLK_IS_ROOT, rate);