Merge tag 'sunxi-clocks-for-4.4' of https://git.kernel.org/pub/scm/linux/kernel/git...
authorStephen Boyd <sboyd@codeaurora.org>
Wed, 21 Oct 2015 23:29:03 +0000 (16:29 -0700)
committerStephen Boyd <sboyd@codeaurora.org>
Wed, 21 Oct 2015 23:29:03 +0000 (16:29 -0700)
Pull Allwinner clock additions for 4.4 from Maxime Ripard:

  - Support for the Audio PLL and child clocks
  - Support for the A33 AHB gates
  - New clk-multiplier generic driver

* tag 'sunxi-clocks-for-4.4' of https://git.kernel.org/pub/scm/linux/kernel/git/mripard/linux:
  clk: sunxi: mod1 clock support
  clk: sunxi: codec clock support
  clk: sunxi: pll2: Add A13 support
  clk: sunxi: Add a driver for the PLL2
  clk: Add a basic multiplier clock
  clk: sunxi: Add A33 gates support

drivers/clk/Makefile
drivers/clk/clk-multiplier.c [new file with mode: 0644]
drivers/clk/sunxi/Makefile
drivers/clk/sunxi/clk-a10-codec.c [new file with mode: 0644]
drivers/clk/sunxi/clk-a10-mod1.c [new file with mode: 0644]
drivers/clk/sunxi/clk-a10-pll2.c [new file with mode: 0644]
drivers/clk/sunxi/clk-simple-gates.c
include/dt-bindings/clock/sun4i-a10-pll2.h [new file with mode: 0644]
include/linux/clk-provider.h

index e44feca..58fd167 100644 (file)
@@ -6,6 +6,7 @@ obj-$(CONFIG_COMMON_CLK)        += clk-divider.o
 obj-$(CONFIG_COMMON_CLK)       += clk-fixed-factor.o
 obj-$(CONFIG_COMMON_CLK)       += clk-fixed-rate.o
 obj-$(CONFIG_COMMON_CLK)       += clk-gate.o
+obj-$(CONFIG_COMMON_CLK)       += clk-multiplier.o
 obj-$(CONFIG_COMMON_CLK)       += clk-mux.o
 obj-$(CONFIG_COMMON_CLK)       += clk-composite.o
 obj-$(CONFIG_COMMON_CLK)       += clk-fractional-divider.o
diff --git a/drivers/clk/clk-multiplier.c b/drivers/clk/clk-multiplier.c
new file mode 100644 (file)
index 0000000..43ec269
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2015 Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * 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.
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+
+#define to_clk_multiplier(_hw) container_of(_hw, struct clk_multiplier, hw)
+
+static unsigned long __get_mult(struct clk_multiplier *mult,
+                               unsigned long rate,
+                               unsigned long parent_rate)
+{
+       if (mult->flags & CLK_MULTIPLIER_ROUND_CLOSEST)
+               return DIV_ROUND_CLOSEST(rate, parent_rate);
+
+       return rate / parent_rate;
+}
+
+static unsigned long clk_multiplier_recalc_rate(struct clk_hw *hw,
+                                               unsigned long parent_rate)
+{
+       struct clk_multiplier *mult = to_clk_multiplier(hw);
+       unsigned long val;
+
+       val = clk_readl(mult->reg) >> mult->shift;
+       val &= GENMASK(mult->width - 1, 0);
+
+       if (!val && mult->flags & CLK_MULTIPLIER_ZERO_BYPASS)
+               val = 1;
+
+       return parent_rate * val;
+}
+
+static bool __is_best_rate(unsigned long rate, unsigned long new,
+                          unsigned long best, unsigned long flags)
+{
+       if (flags & CLK_MULTIPLIER_ROUND_CLOSEST)
+               return abs(rate - new) < abs(rate - best);
+
+       return new >= rate && new < best;
+}
+
+static unsigned long __bestmult(struct clk_hw *hw, unsigned long rate,
+                               unsigned long *best_parent_rate,
+                               u8 width, unsigned long flags)
+{
+       unsigned long orig_parent_rate = *best_parent_rate;
+       unsigned long parent_rate, current_rate, best_rate = ~0;
+       unsigned int i, bestmult = 0;
+
+       if (!(clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT))
+               return rate / *best_parent_rate;
+
+       for (i = 1; i < ((1 << width) - 1); i++) {
+               if (rate == orig_parent_rate * i) {
+                       /*
+                        * This is the best case for us if we have a
+                        * perfect match without changing the parent
+                        * rate.
+                        */
+                       *best_parent_rate = orig_parent_rate;
+                       return i;
+               }
+
+               parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw),
+                                               rate / i);
+               current_rate = parent_rate * i;
+
+               if (__is_best_rate(rate, current_rate, best_rate, flags)) {
+                       bestmult = i;
+                       best_rate = current_rate;
+                       *best_parent_rate = parent_rate;
+               }
+       }
+
+       return bestmult;
+}
+
+static long clk_multiplier_round_rate(struct clk_hw *hw, unsigned long rate,
+                                 unsigned long *parent_rate)
+{
+       struct clk_multiplier *mult = to_clk_multiplier(hw);
+       unsigned long factor = __bestmult(hw, rate, parent_rate,
+                                         mult->width, mult->flags);
+
+       return *parent_rate * factor;
+}
+
+static int clk_multiplier_set_rate(struct clk_hw *hw, unsigned long rate,
+                              unsigned long parent_rate)
+{
+       struct clk_multiplier *mult = to_clk_multiplier(hw);
+       unsigned long factor = __get_mult(mult, rate, parent_rate);
+       unsigned long flags = 0;
+       unsigned long val;
+
+       if (mult->lock)
+               spin_lock_irqsave(mult->lock, flags);
+       else
+               __acquire(mult->lock);
+
+       val = clk_readl(mult->reg);
+       val &= ~GENMASK(mult->width + mult->shift - 1, mult->shift);
+       val |= factor << mult->shift;
+       clk_writel(val, mult->reg);
+
+       if (mult->lock)
+               spin_unlock_irqrestore(mult->lock, flags);
+       else
+               __release(mult->lock);
+
+       return 0;
+}
+
+const struct clk_ops clk_multiplier_ops = {
+       .recalc_rate    = clk_multiplier_recalc_rate,
+       .round_rate     = clk_multiplier_round_rate,
+       .set_rate       = clk_multiplier_set_rate,
+};
+EXPORT_SYMBOL_GPL(clk_multiplier_ops);
+
+struct clk *clk_register_multiplier(struct device *dev, const char *name,
+                                   const char *parent_name,
+                                   unsigned long flags,
+                                   void __iomem *reg, u8 shift, u8 width,
+                                   u8 clk_mult_flags, spinlock_t *lock)
+{
+       struct clk_init_data init;
+       struct clk_multiplier *mult;
+       struct clk *clk;
+
+       mult = kmalloc(sizeof(*mult), GFP_KERNEL);
+       if (!mult)
+               return ERR_PTR(-ENOMEM);
+
+       init.name = name;
+       init.ops = &clk_multiplier_ops;
+       init.flags = flags | CLK_IS_BASIC;
+       init.parent_names = &parent_name;
+       init.num_parents = 1;
+
+       mult->reg = reg;
+       mult->shift = shift;
+       mult->width = width;
+       mult->flags = clk_mult_flags;
+       mult->lock = lock;
+       mult->hw.init = &init;
+
+       clk = clk_register(dev, &mult->hw);
+       if (IS_ERR(clk))
+               kfree(mult);
+
+       return clk;
+}
+EXPORT_SYMBOL_GPL(clk_register_multiplier);
+
+void clk_unregister_multiplier(struct clk *clk)
+{
+       struct clk_multiplier *mult;
+       struct clk_hw *hw;
+
+       hw = __clk_get_hw(clk);
+       if (!hw)
+               return;
+
+       mult = to_clk_multiplier(hw);
+
+       clk_unregister(clk);
+       kfree(mult);
+}
+EXPORT_SYMBOL_GPL(clk_unregister_multiplier);
index f5a35b8..cb4c299 100644 (file)
@@ -3,7 +3,10 @@
 #
 
 obj-y += clk-sunxi.o clk-factors.o
+obj-y += clk-a10-codec.o
 obj-y += clk-a10-hosc.o
+obj-y += clk-a10-mod1.o
+obj-y += clk-a10-pll2.o
 obj-y += clk-a20-gmac.o
 obj-y += clk-mod0.o
 obj-y += clk-simple-gates.o
diff --git a/drivers/clk/sunxi/clk-a10-codec.c b/drivers/clk/sunxi/clk-a10-codec.c
new file mode 100644 (file)
index 0000000..ac321d6
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2013 Emilio López
+ *
+ * Emilio López <emilio@elopez.com.ar>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+
+#define SUN4I_CODEC_GATE       31
+
+static void __init sun4i_codec_clk_setup(struct device_node *node)
+{
+       struct clk *clk;
+       const char *clk_name = node->name, *parent_name;
+       void __iomem *reg;
+
+       reg = of_io_request_and_map(node, 0, of_node_full_name(node));
+       if (IS_ERR(reg))
+               return;
+
+       of_property_read_string(node, "clock-output-names", &clk_name);
+       parent_name = of_clk_get_parent_name(node, 0);
+
+       clk = clk_register_gate(NULL, clk_name, parent_name,
+                               CLK_SET_RATE_PARENT, reg,
+                               SUN4I_CODEC_GATE, 0, NULL);
+
+       if (!IS_ERR(clk))
+               of_clk_add_provider(node, of_clk_src_simple_get, clk);
+}
+CLK_OF_DECLARE(sun4i_codec, "allwinner,sun4i-a10-codec-clk",
+              sun4i_codec_clk_setup);
diff --git a/drivers/clk/sunxi/clk-a10-mod1.c b/drivers/clk/sunxi/clk-a10-mod1.c
new file mode 100644 (file)
index 0000000..e9d870d
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2013 Emilio López
+ *
+ * Emilio López <emilio@elopez.com.ar>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+
+static DEFINE_SPINLOCK(mod1_lock);
+
+#define SUN4I_MOD1_ENABLE      31
+#define SUN4I_MOD1_MUX         16
+#define SUN4I_MOD1_MUX_WIDTH   2
+#define SUN4I_MOD1_MAX_PARENTS 4
+
+static void __init sun4i_mod1_clk_setup(struct device_node *node)
+{
+       struct clk *clk;
+       struct clk_mux *mux;
+       struct clk_gate *gate;
+       const char *parents[4];
+       const char *clk_name = node->name;
+       void __iomem *reg;
+       int i;
+
+       reg = of_io_request_and_map(node, 0, of_node_full_name(node));
+       if (IS_ERR(reg))
+               return;
+
+       mux = kzalloc(sizeof(*mux), GFP_KERNEL);
+       if (!mux)
+               goto err_unmap;
+
+       gate = kzalloc(sizeof(*gate), GFP_KERNEL);
+       if (!gate)
+               goto err_free_mux;
+
+       of_property_read_string(node, "clock-output-names", &clk_name);
+       i = of_clk_parent_fill(node, parents, SUN4I_MOD1_MAX_PARENTS);
+
+       gate->reg = reg;
+       gate->bit_idx = SUN4I_MOD1_ENABLE;
+       gate->lock = &mod1_lock;
+       mux->reg = reg;
+       mux->shift = SUN4I_MOD1_MUX;
+       mux->mask = BIT(SUN4I_MOD1_MUX_WIDTH) - 1;
+       mux->lock = &mod1_lock;
+
+       clk = clk_register_composite(NULL, clk_name, parents, i,
+                                    &mux->hw, &clk_mux_ops,
+                                    NULL, NULL,
+                                    &gate->hw, &clk_gate_ops, 0);
+       if (IS_ERR(clk))
+               goto err_free_gate;
+
+       of_clk_add_provider(node, of_clk_src_simple_get, clk);
+
+       return;
+
+err_free_gate:
+       kfree(gate);
+err_free_mux:
+       kfree(mux);
+err_unmap:
+       iounmap(reg);
+}
+CLK_OF_DECLARE(sun4i_mod1, "allwinner,sun4i-a10-mod1-clk",
+              sun4i_mod1_clk_setup);
diff --git a/drivers/clk/sunxi/clk-a10-pll2.c b/drivers/clk/sunxi/clk-a10-pll2.c
new file mode 100644 (file)
index 0000000..5484c31
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ * Copyright 2013 Emilio López
+ * Emilio López <emilio@elopez.com.ar>
+ *
+ * Copyright 2015 Maxime Ripard
+ * Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+
+#include <dt-bindings/clock/sun4i-a10-pll2.h>
+
+#define SUN4I_PLL2_ENABLE              31
+
+#define SUN4I_PLL2_PRE_DIV_SHIFT       0
+#define SUN4I_PLL2_PRE_DIV_WIDTH       5
+#define SUN4I_PLL2_PRE_DIV_MASK                GENMASK(SUN4I_PLL2_PRE_DIV_WIDTH - 1, 0)
+
+#define SUN4I_PLL2_N_SHIFT             8
+#define SUN4I_PLL2_N_WIDTH             7
+#define SUN4I_PLL2_N_MASK              GENMASK(SUN4I_PLL2_N_WIDTH - 1, 0)
+
+#define SUN4I_PLL2_POST_DIV_SHIFT      26
+#define SUN4I_PLL2_POST_DIV_WIDTH      4
+#define SUN4I_PLL2_POST_DIV_MASK       GENMASK(SUN4I_PLL2_POST_DIV_WIDTH - 1, 0)
+
+#define SUN4I_PLL2_POST_DIV_VALUE      4
+
+#define SUN4I_PLL2_OUTPUTS             4
+
+struct sun4i_pll2_data {
+       u32     post_div_offset;
+       u32     pre_div_flags;
+};
+
+static DEFINE_SPINLOCK(sun4i_a10_pll2_lock);
+
+static void __init sun4i_pll2_setup(struct device_node *node,
+                                   struct sun4i_pll2_data *data)
+{
+       const char *clk_name = node->name, *parent;
+       struct clk **clks, *base_clk, *prediv_clk;
+       struct clk_onecell_data *clk_data;
+       struct clk_multiplier *mult;
+       struct clk_gate *gate;
+       void __iomem *reg;
+       u32 val;
+
+       reg = of_io_request_and_map(node, 0, of_node_full_name(node));
+       if (IS_ERR(reg))
+               return;
+
+       clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
+       if (!clk_data)
+               goto err_unmap;
+
+       clks = kcalloc(SUN4I_PLL2_OUTPUTS, sizeof(struct clk *), GFP_KERNEL);
+       if (!clks)
+               goto err_free_data;
+
+       parent = of_clk_get_parent_name(node, 0);
+       prediv_clk = clk_register_divider(NULL, "pll2-prediv",
+                                         parent, 0, reg,
+                                         SUN4I_PLL2_PRE_DIV_SHIFT,
+                                         SUN4I_PLL2_PRE_DIV_WIDTH,
+                                         data->pre_div_flags,
+                                         &sun4i_a10_pll2_lock);
+       if (!prediv_clk) {
+               pr_err("Couldn't register the prediv clock\n");
+               goto err_free_array;
+       }
+
+       /* Setup the gate part of the PLL2 */
+       gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL);
+       if (!gate)
+               goto err_unregister_prediv;
+
+       gate->reg = reg;
+       gate->bit_idx = SUN4I_PLL2_ENABLE;
+       gate->lock = &sun4i_a10_pll2_lock;
+
+       /* Setup the multiplier part of the PLL2 */
+       mult = kzalloc(sizeof(struct clk_multiplier), GFP_KERNEL);
+       if (!mult)
+               goto err_free_gate;
+
+       mult->reg = reg;
+       mult->shift = SUN4I_PLL2_N_SHIFT;
+       mult->width = 7;
+       mult->flags = CLK_MULTIPLIER_ZERO_BYPASS |
+                       CLK_MULTIPLIER_ROUND_CLOSEST;
+       mult->lock = &sun4i_a10_pll2_lock;
+
+       parent = __clk_get_name(prediv_clk);
+       base_clk = clk_register_composite(NULL, "pll2-base",
+                                         &parent, 1,
+                                         NULL, NULL,
+                                         &mult->hw, &clk_multiplier_ops,
+                                         &gate->hw, &clk_gate_ops,
+                                         CLK_SET_RATE_PARENT);
+       if (!base_clk) {
+               pr_err("Couldn't register the base multiplier clock\n");
+               goto err_free_multiplier;
+       }
+
+       parent = __clk_get_name(base_clk);
+
+       /*
+        * PLL2-1x
+        *
+        * This is supposed to have a post divider, but we won't need
+        * to use it, we just need to initialise it to 4, and use a
+        * fixed divider.
+        */
+       val = readl(reg);
+       val &= ~(SUN4I_PLL2_POST_DIV_MASK << SUN4I_PLL2_POST_DIV_SHIFT);
+       val |= (SUN4I_PLL2_POST_DIV_VALUE - data->post_div_offset) << SUN4I_PLL2_POST_DIV_SHIFT;
+       writel(val, reg);
+
+       of_property_read_string_index(node, "clock-output-names",
+                                     SUN4I_A10_PLL2_1X, &clk_name);
+       clks[SUN4I_A10_PLL2_1X] = clk_register_fixed_factor(NULL, clk_name,
+                                                           parent,
+                                                           CLK_SET_RATE_PARENT,
+                                                           1,
+                                                           SUN4I_PLL2_POST_DIV_VALUE);
+       WARN_ON(IS_ERR(clks[SUN4I_A10_PLL2_1X]));
+
+       /*
+        * PLL2-2x
+        *
+        * This clock doesn't use the post divider, and really is just
+        * a fixed divider from the PLL2 base clock.
+        */
+       of_property_read_string_index(node, "clock-output-names",
+                                     SUN4I_A10_PLL2_2X, &clk_name);
+       clks[SUN4I_A10_PLL2_2X] = clk_register_fixed_factor(NULL, clk_name,
+                                                           parent,
+                                                           CLK_SET_RATE_PARENT,
+                                                           1, 2);
+       WARN_ON(IS_ERR(clks[SUN4I_A10_PLL2_2X]));
+
+       /* PLL2-4x */
+       of_property_read_string_index(node, "clock-output-names",
+                                     SUN4I_A10_PLL2_4X, &clk_name);
+       clks[SUN4I_A10_PLL2_4X] = clk_register_fixed_factor(NULL, clk_name,
+                                                           parent,
+                                                           CLK_SET_RATE_PARENT,
+                                                           1, 1);
+       WARN_ON(IS_ERR(clks[SUN4I_A10_PLL2_4X]));
+
+       /* PLL2-8x */
+       of_property_read_string_index(node, "clock-output-names",
+                                     SUN4I_A10_PLL2_8X, &clk_name);
+       clks[SUN4I_A10_PLL2_8X] = clk_register_fixed_factor(NULL, clk_name,
+                                                           parent,
+                                                           CLK_SET_RATE_PARENT,
+                                                           2, 1);
+       WARN_ON(IS_ERR(clks[SUN4I_A10_PLL2_8X]));
+
+       clk_data->clks = clks;
+       clk_data->clk_num = SUN4I_PLL2_OUTPUTS;
+       of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+
+       return;
+
+err_free_multiplier:
+       kfree(mult);
+err_free_gate:
+       kfree(gate);
+err_unregister_prediv:
+       clk_unregister_divider(prediv_clk);
+err_free_array:
+       kfree(clks);
+err_free_data:
+       kfree(clk_data);
+err_unmap:
+       iounmap(reg);
+}
+
+static struct sun4i_pll2_data sun4i_a10_pll2_data = {
+       .pre_div_flags  = CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO,
+};
+
+static void __init sun4i_a10_pll2_setup(struct device_node *node)
+{
+       sun4i_pll2_setup(node, &sun4i_a10_pll2_data);
+}
+
+CLK_OF_DECLARE(sun4i_a10_pll2, "allwinner,sun4i-a10-pll2-clk",
+              sun4i_a10_pll2_setup);
+
+static struct sun4i_pll2_data sun5i_a13_pll2_data = {
+       .post_div_offset        = 1,
+};
+
+static void __init sun5i_a13_pll2_setup(struct device_node *node)
+{
+       sun4i_pll2_setup(node, &sun5i_a13_pll2_data);
+}
+
+CLK_OF_DECLARE(sun5i_a13_pll2, "allwinner,sun5i-a13-pll2-clk",
+              sun5i_a13_pll2_setup);
index 6ce9118..0214c65 100644 (file)
@@ -128,6 +128,8 @@ CLK_OF_DECLARE(sun8i_a23_apb1, "allwinner,sun8i-a23-apb1-gates-clk",
               sunxi_simple_gates_init);
 CLK_OF_DECLARE(sun8i_a23_apb2, "allwinner,sun8i-a23-apb2-gates-clk",
               sunxi_simple_gates_init);
+CLK_OF_DECLARE(sun8i_a33_ahb1, "allwinner,sun8i-a33-ahb1-gates-clk",
+              sunxi_simple_gates_init);
 CLK_OF_DECLARE(sun9i_a80_ahb0, "allwinner,sun9i-a80-ahb0-gates-clk",
               sunxi_simple_gates_init);
 CLK_OF_DECLARE(sun9i_a80_ahb1, "allwinner,sun9i-a80-ahb1-gates-clk",
diff --git a/include/dt-bindings/clock/sun4i-a10-pll2.h b/include/dt-bindings/clock/sun4i-a10-pll2.h
new file mode 100644 (file)
index 0000000..071c811
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2015 Maxime Ripard
+ *
+ * Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * This file is dual-licensed: you can use it either under the terms
+ * of the GPL or the X11 license, at your option. Note that this dual
+ * licensing only applies to this file, and not this project as a
+ * whole.
+ *
+ *  a) This file 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.
+ *
+ *     This file is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ *
+ * Or, alternatively,
+ *
+ *  b) Permission is hereby granted, free of charge, to any person
+ *     obtaining a copy of this software and associated documentation
+ *     files (the "Software"), to deal in the Software without
+ *     restriction, including without limitation the rights to use,
+ *     copy, modify, merge, publish, distribute, sublicense, and/or
+ *     sell copies of the Software, and to permit persons to whom the
+ *     Software is furnished to do so, subject to the following
+ *     conditions:
+ *
+ *     The above copyright notice and this permission notice shall be
+ *     included in all copies or substantial portions of the Software.
+ *
+ *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ *     OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef __DT_BINDINGS_CLOCK_SUN4I_A10_PLL2_H_
+#define __DT_BINDINGS_CLOCK_SUN4I_A10_PLL2_H_
+
+#define SUN4I_A10_PLL2_1X      0
+#define SUN4I_A10_PLL2_2X      1
+#define SUN4I_A10_PLL2_4X      2
+#define SUN4I_A10_PLL2_8X      3
+
+#endif /* __DT_BINDINGS_CLOCK_SUN4I_A10_PLL2_H_ */
index bbb8fed..e9a4d1e 100644 (file)
@@ -519,6 +519,48 @@ struct clk *clk_register_fractional_divider(struct device *dev,
                void __iomem *reg, u8 mshift, u8 mwidth, u8 nshift, u8 nwidth,
                u8 clk_divider_flags, spinlock_t *lock);
 
+/**
+ * struct clk_multiplier - adjustable multiplier clock
+ *
+ * @hw:                handle between common and hardware-specific interfaces
+ * @reg:       register containing the multiplier
+ * @shift:     shift to the multiplier bit field
+ * @width:     width of the multiplier bit field
+ * @lock:      register lock
+ *
+ * Clock with an adjustable multiplier affecting its output frequency.
+ * Implements .recalc_rate, .set_rate and .round_rate
+ *
+ * Flags:
+ * CLK_MULTIPLIER_ZERO_BYPASS - By default, the multiplier is the value read
+ *     from the register, with 0 being a valid value effectively
+ *     zeroing the output clock rate. If CLK_MULTIPLIER_ZERO_BYPASS is
+ *     set, then a null multiplier will be considered as a bypass,
+ *     leaving the parent rate unmodified.
+ * CLK_MULTIPLIER_ROUND_CLOSEST - Makes the best calculated divider to be
+ *     rounded to the closest integer instead of the down one.
+ */
+struct clk_multiplier {
+       struct clk_hw   hw;
+       void __iomem    *reg;
+       u8              shift;
+       u8              width;
+       u8              flags;
+       spinlock_t      *lock;
+};
+
+#define CLK_MULTIPLIER_ZERO_BYPASS             BIT(0)
+#define CLK_MULTIPLIER_ROUND_CLOSEST   BIT(1)
+
+extern const struct clk_ops clk_multiplier_ops;
+
+struct clk *clk_register_multiplier(struct device *dev, const char *name,
+                                   const char *parent_name,
+                                   unsigned long flags,
+                                   void __iomem *reg, u8 shift, u8 width,
+                                   u8 clk_mult_flags, spinlock_t *lock);
+void clk_unregister_multiplier(struct clk *clk);
+
 /***
  * struct clk_composite - aggregate clock of mux, divider and gate clocks
  *