From: Chao Xie <chao....@marvell.com>

The general composite clock supports div/mux/gate.
marvell SOCes have many clocks that need change
div and mux together. So it need the composite
clock that supports mix/gate.

Signed-off-by: Chao Xie <chao....@marvell.com>
---
 drivers/clk/mmp/Makefile            |   3 +-
 drivers/clk/mmp/clk-mix-composite.c | 195 ++++++++++++++++++++++++++++++++++++
 drivers/clk/mmp/clk.h               |  20 ++++
 3 files changed, 217 insertions(+), 1 deletion(-)
 create mode 100644 drivers/clk/mmp/clk-mix-composite.c

diff --git a/drivers/clk/mmp/Makefile b/drivers/clk/mmp/Makefile
index 2855f7b..2cd7d94 100644
--- a/drivers/clk/mmp/Makefile
+++ b/drivers/clk/mmp/Makefile
@@ -2,7 +2,8 @@
 # Makefile for mmp specific clk
 #
 
-obj-y += clk-apbc.o clk-apmu.o clk-frac.o clk-mix.o clk-gate.o
+obj-y += clk-apbc.o clk-apmu.o clk-frac.o clk-mix.o clk-gate.o \
+        clk-mix-composite.o
 
 obj-$(CONFIG_CPU_PXA168) += clk-pxa168.o
 obj-$(CONFIG_CPU_PXA910) += clk-pxa910.o
diff --git a/drivers/clk/mmp/clk-mix-composite.c 
b/drivers/clk/mmp/clk-mix-composite.c
new file mode 100644
index 0000000..79d5286
--- /dev/null
+++ b/drivers/clk/mmp/clk-mix-composite.c
@@ -0,0 +1,195 @@
+/*
+ * mmp mix(div and mux) clock operation source file
+ *
+ * Copyright (C) 2014 Marvell
+ * Chao Xie <chao....@marvell.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+
+#include "clk.h"
+
+#define to_clk_composite(_hw) container_of(_hw, struct mmp_clk_composite, hw)
+
+static u8 mmp_clk_composite_get_parent(struct clk_hw *hw)
+{
+       struct mmp_clk_composite *composite = to_clk_composite(hw);
+       const struct clk_ops *mix_ops = composite->mix_ops;
+       struct clk_hw *mix_hw = composite->mix_hw;
+
+       mix_hw->clk = hw->clk;
+
+       return mix_ops->get_parent(mix_hw);
+}
+
+static int mmp_clk_composite_set_parent(struct clk_hw *hw, u8 index)
+{
+       struct mmp_clk_composite *composite = to_clk_composite(hw);
+       const struct clk_ops *mix_ops = composite->mix_ops;
+       struct clk_hw *mix_hw = composite->mix_hw;
+
+       mix_hw->clk = hw->clk;
+
+       return mix_ops->set_parent(mix_hw, index);
+}
+
+static int mmp_clk_composite_set_rate(struct clk_hw *hw, unsigned long rate,
+                              unsigned long parent_rate)
+{
+       struct mmp_clk_composite *composite = to_clk_composite(hw);
+       const struct clk_ops *mix_ops = composite->mix_ops;
+       struct clk_hw *mix_hw = composite->mix_hw;
+
+       mix_hw->clk = hw->clk;
+
+       return mix_ops->set_rate(mix_hw, rate, parent_rate);
+}
+
+static unsigned long mmp_clk_composite_recalc_rate(struct clk_hw *hw,
+                                           unsigned long parent_rate)
+{
+       struct mmp_clk_composite *composite = to_clk_composite(hw);
+       const struct clk_ops *mix_ops = composite->mix_ops;
+       struct clk_hw *mix_hw = composite->mix_hw;
+
+       mix_hw->clk = hw->clk;
+
+       return mix_ops->recalc_rate(mix_hw, parent_rate);
+}
+
+static long mmp_clk_composite_determine_rate(struct clk_hw *hw,
+                                       unsigned long rate,
+                                       unsigned long *best_parent_rate,
+                                       struct clk **best_parent_p)
+{
+       struct mmp_clk_composite *composite = to_clk_composite(hw);
+       const struct clk_ops *mix_ops = composite->mix_ops;
+       struct clk_hw *mix_hw = composite->mix_hw;
+
+       mix_hw->clk = hw->clk;
+
+       return mix_ops->determine_rate(mix_hw, rate, best_parent_rate,
+                                       best_parent_p);
+}
+
+static int mmp_clk_composite_set_rate_and_parent(struct clk_hw *hw,
+                                       unsigned long rate,
+                                       unsigned long parent_rate, u8 index)
+
+{
+       struct mmp_clk_composite *composite = to_clk_composite(hw);
+       const struct clk_ops *mix_ops = composite->mix_ops;
+       struct clk_hw *mix_hw = composite->mix_hw;
+
+       mix_hw->clk = hw->clk;
+
+       return mix_ops->set_rate_and_parent(mix_hw, rate, parent_rate, index);
+}
+
+static int mmp_clk_composite_is_enabled(struct clk_hw *hw)
+{
+       struct mmp_clk_composite *composite = to_clk_composite(hw);
+       const struct clk_ops *gate_ops = composite->gate_ops;
+       struct clk_hw *gate_hw = composite->gate_hw;
+
+       gate_hw->clk = hw->clk;
+
+       return gate_ops->is_enabled(gate_hw);
+}
+
+static int mmp_clk_composite_enable(struct clk_hw *hw)
+{
+       struct mmp_clk_composite *composite = to_clk_composite(hw);
+       const struct clk_ops *gate_ops = composite->gate_ops;
+       struct clk_hw *gate_hw = composite->gate_hw;
+
+       gate_hw->clk = hw->clk;
+
+       return gate_ops->enable(gate_hw);
+}
+
+static void mmp_clk_composite_disable(struct clk_hw *hw)
+{
+       struct mmp_clk_composite *composite = to_clk_composite(hw);
+       const struct clk_ops *gate_ops = composite->gate_ops;
+       struct clk_hw *gate_hw = composite->gate_hw;
+
+       gate_hw->clk = hw->clk;
+
+       gate_ops->disable(gate_hw);
+}
+
+static void mmp_clk_composite_init(struct clk_hw *hw)
+{
+       struct mmp_clk_composite *composite = to_clk_composite(hw);
+       const struct clk_ops *gate_ops = composite->gate_ops;
+       struct clk_hw *gate_hw = composite->gate_hw;
+       const struct clk_ops *mix_ops = composite->mix_ops;
+       struct clk_hw *mix_hw = composite->mix_hw;
+
+       mix_hw->clk = hw->clk;
+       gate_hw->clk = hw->clk;
+
+       if (mix_ops->init)
+               mix_ops->init(mix_hw);
+       if (gate_ops->init)
+               gate_ops->init(gate_hw);
+}
+
+static struct clk_ops mmp_clk_composite_ops = {
+       .enable = mmp_clk_composite_enable,
+       .disable = mmp_clk_composite_disable,
+       .is_enabled = mmp_clk_composite_is_enabled,
+       .determine_rate = mmp_clk_composite_determine_rate,
+       .set_rate_and_parent = mmp_clk_composite_set_rate_and_parent,
+       .set_rate = mmp_clk_composite_set_rate,
+       .recalc_rate = mmp_clk_composite_recalc_rate,
+       .get_parent = mmp_clk_composite_get_parent,
+       .set_parent = mmp_clk_composite_set_parent,
+       .init = mmp_clk_composite_init,
+};
+
+struct clk *mmp_clk_register_composite(struct device *dev, const char *name,
+                       const char **parent_names, int num_parents,
+                       struct clk_hw *mix_hw, const struct clk_ops *mix_ops,
+                       struct clk_hw *gate_hw, const struct clk_ops *gate_ops,
+                       unsigned long flags)
+{
+       struct clk *clk;
+       struct clk_init_data init;
+       struct mmp_clk_composite *composite;
+
+       if (!mix_hw || !gate_hw)
+               return ERR_PTR(-EINVAL);
+
+       composite = kzalloc(sizeof(*composite), GFP_KERNEL);
+       if (!composite) {
+               pr_err("%s: could not allocate mmp composite clk\n", __func__);
+               return ERR_PTR(-ENOMEM);
+       }
+
+       init.name = name;
+       init.flags = flags;
+       init.parent_names = parent_names;
+       init.num_parents = num_parents;
+       init.ops = &mmp_clk_composite_ops;
+
+       composite->mix_hw = mix_hw;
+       composite->mix_ops = mix_ops;
+       composite->gate_hw = gate_hw;
+       composite->gate_ops = gate_ops;
+       composite->hw.init = &init;
+
+       clk = clk_register(dev, &composite->hw);
+       if (IS_ERR(clk))
+               kfree(composite);
+
+       return clk;
+}
diff --git a/drivers/clk/mmp/clk.h b/drivers/clk/mmp/clk.h
index 9096f0a..9827a4f 100644
--- a/drivers/clk/mmp/clk.h
+++ b/drivers/clk/mmp/clk.h
@@ -120,6 +120,26 @@ extern struct clk *mmp_clk_register_gate(struct device 
*dev, const char *name,
                        spinlock_t *lock);
 
 
+/* Clock type "composite" for mix clock */
+struct mmp_clk_composite {
+       struct clk_hw hw;
+       struct clk_ops ops;
+
+       struct clk_hw *mix_hw;
+       struct clk_hw *gate_hw;
+
+       const struct clk_ops *mix_ops;
+       const struct clk_ops *gate_ops;
+};
+
+extern struct clk *mmp_clk_register_composite(struct device *dev,
+                       const char *name,
+                       const char **parent_names, int num_parents,
+                       struct clk_hw *mix_hw, const struct clk_ops *mix_ops,
+                       struct clk_hw *gate_hw, const struct clk_ops *gate_ops,
+                       unsigned long flags);
+
+
 extern struct clk *mmp_clk_register_pll2(const char *name,
                const char *parent_name, unsigned long flags);
 extern struct clk *mmp_clk_register_apbc(const char *name,
-- 
1.8.3.2

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to