All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4 0/5] clk: sunxi: Add support for the Audio PLL
@ 2015-10-12 20:43 ` Maxime Ripard
  0 siblings, 0 replies; 24+ messages in thread
From: Maxime Ripard @ 2015-10-12 20:43 UTC (permalink / raw)
  To: Mike Turquette, Stephen Boyd, Emilio Lopez
  Cc: linux-arm-kernel, Chen-Yu Tsai, Hans de Goede, linux-clk,
	linux-sunxi, Maxime Ripard

Hi,

This serie adds support for the PLL2 aka the Audio PLL on the
Allwinner A10 and the later SoCs.

This is the last part of the audio codec support.

This serie is built on top of a generic clk-multiplier driver to
handle clock that multiply their parent clock rate (mostly PLL's), in
order to provide the driver for the PLL2 base clock, and then adds the
drivers for the clock that derive from the Audio PLL.

Thanks!
Maxime

Changes from v3:
  - Fixed the header issues: removed unused ones, added some others
  - Added the __acquire / __release call when a spinlock is not defined

Changes from v2:
  - Renamed clk-factor to clk-multiplier
  - Added an exception for the A13 clock

Changes from v1:
  - Removed a bogus of_iomap in the mod1 clock driver
  - Wrote the clk-factor driver
  - Converted the PLL2 clock to that driver
      

Emilio López (2):
  clk: sunxi: codec clock support
  clk: sunxi: mod1 clock support

Maxime Ripard (3):
  clk: Add a basic multiplier clock
  clk: sunxi: Add a driver for the PLL2
  clk: sunxi: pll2: Add A13 support

 drivers/clk/Makefile                       |   1 +
 drivers/clk/clk-multiplier.c               | 181 ++++++++++++++++++++++++
 drivers/clk/sunxi/Makefile                 |   3 +
 drivers/clk/sunxi/clk-a10-codec.c          |  44 ++++++
 drivers/clk/sunxi/clk-a10-mod1.c           |  81 +++++++++++
 drivers/clk/sunxi/clk-a10-pll2.c           | 216 +++++++++++++++++++++++++++++
 include/dt-bindings/clock/sun4i-a10-pll2.h |  53 +++++++
 include/linux/clk-provider.h               |  42 ++++++
 8 files changed, 621 insertions(+)
 create mode 100644 drivers/clk/clk-multiplier.c
 create mode 100644 drivers/clk/sunxi/clk-a10-codec.c
 create mode 100644 drivers/clk/sunxi/clk-a10-mod1.c
 create mode 100644 drivers/clk/sunxi/clk-a10-pll2.c
 create mode 100644 include/dt-bindings/clock/sun4i-a10-pll2.h

-- 
2.5.3

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH v4 0/5] clk: sunxi: Add support for the Audio PLL
@ 2015-10-12 20:43 ` Maxime Ripard
  0 siblings, 0 replies; 24+ messages in thread
From: Maxime Ripard @ 2015-10-12 20:43 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

This serie adds support for the PLL2 aka the Audio PLL on the
Allwinner A10 and the later SoCs.

This is the last part of the audio codec support.

This serie is built on top of a generic clk-multiplier driver to
handle clock that multiply their parent clock rate (mostly PLL's), in
order to provide the driver for the PLL2 base clock, and then adds the
drivers for the clock that derive from the Audio PLL.

Thanks!
Maxime

Changes from v3:
  - Fixed the header issues: removed unused ones, added some others
  - Added the __acquire / __release call when a spinlock is not defined

Changes from v2:
  - Renamed clk-factor to clk-multiplier
  - Added an exception for the A13 clock

Changes from v1:
  - Removed a bogus of_iomap in the mod1 clock driver
  - Wrote the clk-factor driver
  - Converted the PLL2 clock to that driver
      

Emilio L?pez (2):
  clk: sunxi: codec clock support
  clk: sunxi: mod1 clock support

Maxime Ripard (3):
  clk: Add a basic multiplier clock
  clk: sunxi: Add a driver for the PLL2
  clk: sunxi: pll2: Add A13 support

 drivers/clk/Makefile                       |   1 +
 drivers/clk/clk-multiplier.c               | 181 ++++++++++++++++++++++++
 drivers/clk/sunxi/Makefile                 |   3 +
 drivers/clk/sunxi/clk-a10-codec.c          |  44 ++++++
 drivers/clk/sunxi/clk-a10-mod1.c           |  81 +++++++++++
 drivers/clk/sunxi/clk-a10-pll2.c           | 216 +++++++++++++++++++++++++++++
 include/dt-bindings/clock/sun4i-a10-pll2.h |  53 +++++++
 include/linux/clk-provider.h               |  42 ++++++
 8 files changed, 621 insertions(+)
 create mode 100644 drivers/clk/clk-multiplier.c
 create mode 100644 drivers/clk/sunxi/clk-a10-codec.c
 create mode 100644 drivers/clk/sunxi/clk-a10-mod1.c
 create mode 100644 drivers/clk/sunxi/clk-a10-pll2.c
 create mode 100644 include/dt-bindings/clock/sun4i-a10-pll2.h

-- 
2.5.3

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH v4 1/5] clk: Add a basic multiplier clock
  2015-10-12 20:43 ` Maxime Ripard
@ 2015-10-12 20:43   ` Maxime Ripard
  -1 siblings, 0 replies; 24+ messages in thread
From: Maxime Ripard @ 2015-10-12 20:43 UTC (permalink / raw)
  To: Mike Turquette, Stephen Boyd, Emilio Lopez
  Cc: linux-arm-kernel, Chen-Yu Tsai, Hans de Goede, linux-clk,
	linux-sunxi, Maxime Ripard

Some clocks are using a multiplier component, however, unlike their mux,
gate or divider counterpart, these factors don't have a basic clock
implementation.

This leads to code duplication across platforms that want to use that kind
of clocks, and the impossibility to use the composite clocks with such a
clock without defining your own rate operations.

Create such a driver in order to remove these issues, and hopefully factor
the implementations, reducing code size across platforms and consolidating
the various implementations.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 drivers/clk/Makefile         |   1 +
 drivers/clk/clk-multiplier.c | 181 +++++++++++++++++++++++++++++++++++++++++++
 include/linux/clk-provider.h |  42 ++++++++++
 3 files changed, 224 insertions(+)
 create mode 100644 drivers/clk/clk-multiplier.c

diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index d08b3e5985be..0b2101039508 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -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
index 000000000000..75fee96ea1ee
--- /dev/null
+++ b/drivers/clk/clk-multiplier.c
@@ -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, 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 * i == orig_parent_rate) {
+			/*
+			 * 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, 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);
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index 3ecc07d0da77..6a7dfe33a317 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -518,6 +518,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
  *
-- 
2.5.3

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v4 1/5] clk: Add a basic multiplier clock
@ 2015-10-12 20:43   ` Maxime Ripard
  0 siblings, 0 replies; 24+ messages in thread
From: Maxime Ripard @ 2015-10-12 20:43 UTC (permalink / raw)
  To: linux-arm-kernel

Some clocks are using a multiplier component, however, unlike their mux,
gate or divider counterpart, these factors don't have a basic clock
implementation.

This leads to code duplication across platforms that want to use that kind
of clocks, and the impossibility to use the composite clocks with such a
clock without defining your own rate operations.

Create such a driver in order to remove these issues, and hopefully factor
the implementations, reducing code size across platforms and consolidating
the various implementations.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 drivers/clk/Makefile         |   1 +
 drivers/clk/clk-multiplier.c | 181 +++++++++++++++++++++++++++++++++++++++++++
 include/linux/clk-provider.h |  42 ++++++++++
 3 files changed, 224 insertions(+)
 create mode 100644 drivers/clk/clk-multiplier.c

diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index d08b3e5985be..0b2101039508 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -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
index 000000000000..75fee96ea1ee
--- /dev/null
+++ b/drivers/clk/clk-multiplier.c
@@ -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, 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 * i == orig_parent_rate) {
+			/*
+			 * 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, 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);
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index 3ecc07d0da77..6a7dfe33a317 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -518,6 +518,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
  *
-- 
2.5.3

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v4 2/5] clk: sunxi: Add a driver for the PLL2
  2015-10-12 20:43 ` Maxime Ripard
@ 2015-10-12 20:43   ` Maxime Ripard
  -1 siblings, 0 replies; 24+ messages in thread
From: Maxime Ripard @ 2015-10-12 20:43 UTC (permalink / raw)
  To: Mike Turquette, Stephen Boyd, Emilio Lopez
  Cc: linux-arm-kernel, Chen-Yu Tsai, Hans de Goede, linux-clk,
	linux-sunxi, Maxime Ripard

The PLL2 on the A10 and later SoCs is the clock used for all the audio
related operations.

This clock has a somewhat complex output tree, with three outputs (2X, 4X
and 8X) with a fixed divider from the base clock, and an output (1X) with a
post divider.

However, we can simplify things since the 1X divider can be fixed, and we
end up by having a base clock not exposed to any device (or at least
directly, since the 4X output doesn't have any divider), and 4 fixed
divider clocks that will be exposed.

This clock seems to have been introduced, at least in this form, in the
revision B of the A10, but we don't have any information on the clock used
on the revision A.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 drivers/clk/sunxi/Makefile                 |   1 +
 drivers/clk/sunxi/clk-a10-pll2.c           | 188 +++++++++++++++++++++++++++++
 include/dt-bindings/clock/sun4i-a10-pll2.h |  53 ++++++++
 3 files changed, 242 insertions(+)
 create mode 100644 drivers/clk/sunxi/clk-a10-pll2.c
 create mode 100644 include/dt-bindings/clock/sun4i-a10-pll2.h

diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile
index f5a35b82cc1a..c658a18ba7cb 100644
--- a/drivers/clk/sunxi/Makefile
+++ b/drivers/clk/sunxi/Makefile
@@ -4,6 +4,7 @@
 
 obj-y += clk-sunxi.o clk-factors.o
 obj-y += clk-a10-hosc.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-pll2.c b/drivers/clk/sunxi/clk-a10-pll2.c
new file mode 100644
index 000000000000..b877cc6c3622
--- /dev/null
+++ b/drivers/clk/sunxi/clk-a10-pll2.c
@@ -0,0 +1,188 @@
+/*
+ * 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, 0)
+
+#define SUN4I_PLL2_N_SHIFT		8
+#define SUN4I_PLL2_N_WIDTH		7
+#define SUN4I_PLL2_N_MASK		GENMASK(SUN4I_PLL2_N_WIDTH, 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, 0)
+
+#define SUN4I_PLL2_POST_DIV_VALUE	4
+
+#define SUN4I_PLL2_OUTPUTS		4
+
+static DEFINE_SPINLOCK(sun4i_a10_pll2_lock);
+
+static void __init sun4i_pll2_setup(struct device_node *node)
+{
+	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,
+					  CLK_DIVIDER_ONE_BASED |
+					  CLK_DIVIDER_ALLOW_ZERO,
+					  &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 << 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);
+}
+CLK_OF_DECLARE(sun4i_pll2, "allwinner,sun4i-a10-pll2-clk", sun4i_pll2_setup);
diff --git a/include/dt-bindings/clock/sun4i-a10-pll2.h b/include/dt-bindings/clock/sun4i-a10-pll2.h
new file mode 100644
index 000000000000..071c8112d531
--- /dev/null
+++ b/include/dt-bindings/clock/sun4i-a10-pll2.h
@@ -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_ */
-- 
2.5.3

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v4 2/5] clk: sunxi: Add a driver for the PLL2
@ 2015-10-12 20:43   ` Maxime Ripard
  0 siblings, 0 replies; 24+ messages in thread
From: Maxime Ripard @ 2015-10-12 20:43 UTC (permalink / raw)
  To: linux-arm-kernel

The PLL2 on the A10 and later SoCs is the clock used for all the audio
related operations.

This clock has a somewhat complex output tree, with three outputs (2X, 4X
and 8X) with a fixed divider from the base clock, and an output (1X) with a
post divider.

However, we can simplify things since the 1X divider can be fixed, and we
end up by having a base clock not exposed to any device (or at least
directly, since the 4X output doesn't have any divider), and 4 fixed
divider clocks that will be exposed.

This clock seems to have been introduced, at least in this form, in the
revision B of the A10, but we don't have any information on the clock used
on the revision A.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 drivers/clk/sunxi/Makefile                 |   1 +
 drivers/clk/sunxi/clk-a10-pll2.c           | 188 +++++++++++++++++++++++++++++
 include/dt-bindings/clock/sun4i-a10-pll2.h |  53 ++++++++
 3 files changed, 242 insertions(+)
 create mode 100644 drivers/clk/sunxi/clk-a10-pll2.c
 create mode 100644 include/dt-bindings/clock/sun4i-a10-pll2.h

diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile
index f5a35b82cc1a..c658a18ba7cb 100644
--- a/drivers/clk/sunxi/Makefile
+++ b/drivers/clk/sunxi/Makefile
@@ -4,6 +4,7 @@
 
 obj-y += clk-sunxi.o clk-factors.o
 obj-y += clk-a10-hosc.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-pll2.c b/drivers/clk/sunxi/clk-a10-pll2.c
new file mode 100644
index 000000000000..b877cc6c3622
--- /dev/null
+++ b/drivers/clk/sunxi/clk-a10-pll2.c
@@ -0,0 +1,188 @@
+/*
+ * 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, 0)
+
+#define SUN4I_PLL2_N_SHIFT		8
+#define SUN4I_PLL2_N_WIDTH		7
+#define SUN4I_PLL2_N_MASK		GENMASK(SUN4I_PLL2_N_WIDTH, 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, 0)
+
+#define SUN4I_PLL2_POST_DIV_VALUE	4
+
+#define SUN4I_PLL2_OUTPUTS		4
+
+static DEFINE_SPINLOCK(sun4i_a10_pll2_lock);
+
+static void __init sun4i_pll2_setup(struct device_node *node)
+{
+	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,
+					  CLK_DIVIDER_ONE_BASED |
+					  CLK_DIVIDER_ALLOW_ZERO,
+					  &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 << 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);
+}
+CLK_OF_DECLARE(sun4i_pll2, "allwinner,sun4i-a10-pll2-clk", sun4i_pll2_setup);
diff --git a/include/dt-bindings/clock/sun4i-a10-pll2.h b/include/dt-bindings/clock/sun4i-a10-pll2.h
new file mode 100644
index 000000000000..071c8112d531
--- /dev/null
+++ b/include/dt-bindings/clock/sun4i-a10-pll2.h
@@ -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_ */
-- 
2.5.3

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v4 3/5] clk: sunxi: pll2: Add A13 support
  2015-10-12 20:43 ` Maxime Ripard
@ 2015-10-12 20:43   ` Maxime Ripard
  -1 siblings, 0 replies; 24+ messages in thread
From: Maxime Ripard @ 2015-10-12 20:43 UTC (permalink / raw)
  To: Mike Turquette, Stephen Boyd, Emilio Lopez
  Cc: linux-arm-kernel, Chen-Yu Tsai, Hans de Goede, linux-clk,
	linux-sunxi, Maxime Ripard

The A13, unlike the A10 and A20, doesn't use a pass-through exception for
the 0 value in the pre and post dividers, but increments all the values
written in the register by one.

Add an exception for both these cases to handle them nicely.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 drivers/clk/sunxi/clk-a10-pll2.c | 38 +++++++++++++++++++++++++++++++++-----
 1 file changed, 33 insertions(+), 5 deletions(-)

diff --git a/drivers/clk/sunxi/clk-a10-pll2.c b/drivers/clk/sunxi/clk-a10-pll2.c
index b877cc6c3622..bae9235f7528 100644
--- a/drivers/clk/sunxi/clk-a10-pll2.c
+++ b/drivers/clk/sunxi/clk-a10-pll2.c
@@ -41,9 +41,15 @@
 
 #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)
+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;
@@ -70,8 +76,7 @@ static void __init sun4i_pll2_setup(struct device_node *node)
 					  parent, 0, reg,
 					  SUN4I_PLL2_PRE_DIV_SHIFT,
 					  SUN4I_PLL2_PRE_DIV_WIDTH,
-					  CLK_DIVIDER_ONE_BASED |
-					  CLK_DIVIDER_ALLOW_ZERO,
+					  data->pre_div_flags,
 					  &sun4i_a10_pll2_lock);
 	if (!prediv_clk) {
 		pr_err("Couldn't register the prediv clock\n");
@@ -122,7 +127,7 @@ static void __init sun4i_pll2_setup(struct device_node *node)
 	 */
 	val = readl(reg);
 	val &= ~(SUN4I_PLL2_POST_DIV_MASK << SUN4I_PLL2_POST_DIV_SHIFT);
-	val |= SUN4I_PLL2_POST_DIV_VALUE << 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",
@@ -185,4 +190,27 @@ err_free_data:
 err_unmap:
 	iounmap(reg);
 }
-CLK_OF_DECLARE(sun4i_pll2, "allwinner,sun4i-a10-pll2-clk", sun4i_pll2_setup);
+
+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);
-- 
2.5.3

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v4 3/5] clk: sunxi: pll2: Add A13 support
@ 2015-10-12 20:43   ` Maxime Ripard
  0 siblings, 0 replies; 24+ messages in thread
From: Maxime Ripard @ 2015-10-12 20:43 UTC (permalink / raw)
  To: linux-arm-kernel

The A13, unlike the A10 and A20, doesn't use a pass-through exception for
the 0 value in the pre and post dividers, but increments all the values
written in the register by one.

Add an exception for both these cases to handle them nicely.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 drivers/clk/sunxi/clk-a10-pll2.c | 38 +++++++++++++++++++++++++++++++++-----
 1 file changed, 33 insertions(+), 5 deletions(-)

diff --git a/drivers/clk/sunxi/clk-a10-pll2.c b/drivers/clk/sunxi/clk-a10-pll2.c
index b877cc6c3622..bae9235f7528 100644
--- a/drivers/clk/sunxi/clk-a10-pll2.c
+++ b/drivers/clk/sunxi/clk-a10-pll2.c
@@ -41,9 +41,15 @@
 
 #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)
+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;
@@ -70,8 +76,7 @@ static void __init sun4i_pll2_setup(struct device_node *node)
 					  parent, 0, reg,
 					  SUN4I_PLL2_PRE_DIV_SHIFT,
 					  SUN4I_PLL2_PRE_DIV_WIDTH,
-					  CLK_DIVIDER_ONE_BASED |
-					  CLK_DIVIDER_ALLOW_ZERO,
+					  data->pre_div_flags,
 					  &sun4i_a10_pll2_lock);
 	if (!prediv_clk) {
 		pr_err("Couldn't register the prediv clock\n");
@@ -122,7 +127,7 @@ static void __init sun4i_pll2_setup(struct device_node *node)
 	 */
 	val = readl(reg);
 	val &= ~(SUN4I_PLL2_POST_DIV_MASK << SUN4I_PLL2_POST_DIV_SHIFT);
-	val |= SUN4I_PLL2_POST_DIV_VALUE << 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",
@@ -185,4 +190,27 @@ err_free_data:
 err_unmap:
 	iounmap(reg);
 }
-CLK_OF_DECLARE(sun4i_pll2, "allwinner,sun4i-a10-pll2-clk", sun4i_pll2_setup);
+
+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);
-- 
2.5.3

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v4 4/5] clk: sunxi: codec clock support
  2015-10-12 20:43 ` Maxime Ripard
@ 2015-10-12 20:43   ` Maxime Ripard
  -1 siblings, 0 replies; 24+ messages in thread
From: Maxime Ripard @ 2015-10-12 20:43 UTC (permalink / raw)
  To: Mike Turquette, Stephen Boyd, Emilio Lopez
  Cc: linux-arm-kernel, Chen-Yu Tsai, Hans de Goede, linux-clk,
	linux-sunxi, Maxime Ripard

From: Emilio López <emilio@elopez.com.ar>

The codec clock on sun4i, sun5i and sun7i is a simple gate with PLL2 as
parent. Add a driver for such a clock.

Signed-off-by: Emilio López <emilio@elopez.com.ar>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Reviewed-by: Chen-Yu Tsai <wens@csie.org>
---
 drivers/clk/sunxi/Makefile        |  1 +
 drivers/clk/sunxi/clk-a10-codec.c | 44 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 45 insertions(+)
 create mode 100644 drivers/clk/sunxi/clk-a10-codec.c

diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile
index c658a18ba7cb..70a449a419e6 100644
--- a/drivers/clk/sunxi/Makefile
+++ b/drivers/clk/sunxi/Makefile
@@ -3,6 +3,7 @@
 #
 
 obj-y += clk-sunxi.o clk-factors.o
+obj-y += clk-a10-codec.o
 obj-y += clk-a10-hosc.o
 obj-y += clk-a10-pll2.o
 obj-y += clk-a20-gmac.o
diff --git a/drivers/clk/sunxi/clk-a10-codec.c b/drivers/clk/sunxi/clk-a10-codec.c
new file mode 100644
index 000000000000..ac321d6a0df5
--- /dev/null
+++ b/drivers/clk/sunxi/clk-a10-codec.c
@@ -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);
-- 
2.5.3

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v4 4/5] clk: sunxi: codec clock support
@ 2015-10-12 20:43   ` Maxime Ripard
  0 siblings, 0 replies; 24+ messages in thread
From: Maxime Ripard @ 2015-10-12 20:43 UTC (permalink / raw)
  To: linux-arm-kernel

From: Emilio L?pez <emilio@elopez.com.ar>

The codec clock on sun4i, sun5i and sun7i is a simple gate with PLL2 as
parent. Add a driver for such a clock.

Signed-off-by: Emilio L?pez <emilio@elopez.com.ar>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Reviewed-by: Chen-Yu Tsai <wens@csie.org>
---
 drivers/clk/sunxi/Makefile        |  1 +
 drivers/clk/sunxi/clk-a10-codec.c | 44 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 45 insertions(+)
 create mode 100644 drivers/clk/sunxi/clk-a10-codec.c

diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile
index c658a18ba7cb..70a449a419e6 100644
--- a/drivers/clk/sunxi/Makefile
+++ b/drivers/clk/sunxi/Makefile
@@ -3,6 +3,7 @@
 #
 
 obj-y += clk-sunxi.o clk-factors.o
+obj-y += clk-a10-codec.o
 obj-y += clk-a10-hosc.o
 obj-y += clk-a10-pll2.o
 obj-y += clk-a20-gmac.o
diff --git a/drivers/clk/sunxi/clk-a10-codec.c b/drivers/clk/sunxi/clk-a10-codec.c
new file mode 100644
index 000000000000..ac321d6a0df5
--- /dev/null
+++ b/drivers/clk/sunxi/clk-a10-codec.c
@@ -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);
-- 
2.5.3

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v4 5/5] clk: sunxi: mod1 clock support
  2015-10-12 20:43 ` Maxime Ripard
@ 2015-10-12 20:43   ` Maxime Ripard
  -1 siblings, 0 replies; 24+ messages in thread
From: Maxime Ripard @ 2015-10-12 20:43 UTC (permalink / raw)
  To: Mike Turquette, Stephen Boyd, Emilio Lopez
  Cc: linux-arm-kernel, Chen-Yu Tsai, Hans de Goede, linux-clk,
	linux-sunxi, Maxime Ripard

From: Emilio López <emilio@elopez.com.ar>

The module 1 type of clocks consist of a gate and a mux and are used on
the audio blocks to mux and gate the PLL2 outputs for AC97, IIS or
SPDIF. This commit adds support for them on the sunxi clock driver.

Signed-off-by: Emilio López <emilio@elopez.com.ar>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 drivers/clk/sunxi/Makefile       |  1 +
 drivers/clk/sunxi/clk-a10-mod1.c | 81 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 82 insertions(+)
 create mode 100644 drivers/clk/sunxi/clk-a10-mod1.c

diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile
index 70a449a419e6..cb4c299214ce 100644
--- a/drivers/clk/sunxi/Makefile
+++ b/drivers/clk/sunxi/Makefile
@@ -5,6 +5,7 @@
 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
diff --git a/drivers/clk/sunxi/clk-a10-mod1.c b/drivers/clk/sunxi/clk-a10-mod1.c
new file mode 100644
index 000000000000..099dbdab7afe
--- /dev/null
+++ b/drivers/clk/sunxi/clk-a10-mod1.c
@@ -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 = 0;
+
+	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);
+	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);
-- 
2.5.3

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v4 5/5] clk: sunxi: mod1 clock support
@ 2015-10-12 20:43   ` Maxime Ripard
  0 siblings, 0 replies; 24+ messages in thread
From: Maxime Ripard @ 2015-10-12 20:43 UTC (permalink / raw)
  To: linux-arm-kernel

From: Emilio L?pez <emilio@elopez.com.ar>

The module 1 type of clocks consist of a gate and a mux and are used on
the audio blocks to mux and gate the PLL2 outputs for AC97, IIS or
SPDIF. This commit adds support for them on the sunxi clock driver.

Signed-off-by: Emilio L?pez <emilio@elopez.com.ar>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 drivers/clk/sunxi/Makefile       |  1 +
 drivers/clk/sunxi/clk-a10-mod1.c | 81 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 82 insertions(+)
 create mode 100644 drivers/clk/sunxi/clk-a10-mod1.c

diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile
index 70a449a419e6..cb4c299214ce 100644
--- a/drivers/clk/sunxi/Makefile
+++ b/drivers/clk/sunxi/Makefile
@@ -5,6 +5,7 @@
 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
diff --git a/drivers/clk/sunxi/clk-a10-mod1.c b/drivers/clk/sunxi/clk-a10-mod1.c
new file mode 100644
index 000000000000..099dbdab7afe
--- /dev/null
+++ b/drivers/clk/sunxi/clk-a10-mod1.c
@@ -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 = 0;
+
+	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);
+	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);
-- 
2.5.3

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* Re: [PATCH v4 2/5] clk: sunxi: Add a driver for the PLL2
  2015-10-12 20:43   ` Maxime Ripard
@ 2015-10-14  9:10     ` Chen-Yu Tsai
  -1 siblings, 0 replies; 24+ messages in thread
From: Chen-Yu Tsai @ 2015-10-14  9:10 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Mike Turquette, Stephen Boyd, Emilio Lopez, linux-arm-kernel,
	Chen-Yu Tsai, Hans de Goede, linux-clk, linux-sunxi

On Tue, Oct 13, 2015 at 4:43 AM, Maxime Ripard
<maxime.ripard@free-electrons.com> wrote:
> The PLL2 on the A10 and later SoCs is the clock used for all the audio
> related operations.
>
> This clock has a somewhat complex output tree, with three outputs (2X, 4X
> and 8X) with a fixed divider from the base clock, and an output (1X) with=
 a
> post divider.
>
> However, we can simplify things since the 1X divider can be fixed, and we
> end up by having a base clock not exposed to any device (or at least
> directly, since the 4X output doesn't have any divider), and 4 fixed
> divider clocks that will be exposed.
>
> This clock seems to have been introduced, at least in this form, in the
> revision B of the A10, but we don't have any information on the clock use=
d
> on the revision A.
>
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> ---
>  drivers/clk/sunxi/Makefile                 |   1 +
>  drivers/clk/sunxi/clk-a10-pll2.c           | 188 +++++++++++++++++++++++=
++++++
>  include/dt-bindings/clock/sun4i-a10-pll2.h |  53 ++++++++
>  3 files changed, 242 insertions(+)
>  create mode 100644 drivers/clk/sunxi/clk-a10-pll2.c
>  create mode 100644 include/dt-bindings/clock/sun4i-a10-pll2.h
>
> diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile
> index f5a35b82cc1a..c658a18ba7cb 100644
> --- a/drivers/clk/sunxi/Makefile
> +++ b/drivers/clk/sunxi/Makefile
> @@ -4,6 +4,7 @@
>
>  obj-y +=3D clk-sunxi.o clk-factors.o
>  obj-y +=3D clk-a10-hosc.o
> +obj-y +=3D clk-a10-pll2.o
>  obj-y +=3D clk-a20-gmac.o
>  obj-y +=3D clk-mod0.o
>  obj-y +=3D clk-simple-gates.o
> diff --git a/drivers/clk/sunxi/clk-a10-pll2.c b/drivers/clk/sunxi/clk-a10=
-pll2.c
> new file mode 100644
> index 000000000000..b877cc6c3622
> --- /dev/null
> +++ b/drivers/clk/sunxi/clk-a10-pll2.c
> @@ -0,0 +1,188 @@
> +/*
> + * Copyright 2013 Emilio L=C3=B3pez
> + * Emilio L=C3=B3pez <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_DI=
V_WIDTH, 0)
> +
> +#define SUN4I_PLL2_N_SHIFT             8
> +#define SUN4I_PLL2_N_WIDTH             7
> +#define SUN4I_PLL2_N_MASK              GENMASK(SUN4I_PLL2_N_WIDTH, 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=
, 0)
> +
> +#define SUN4I_PLL2_POST_DIV_VALUE      4
> +
> +#define SUN4I_PLL2_OUTPUTS             4
> +
> +static DEFINE_SPINLOCK(sun4i_a10_pll2_lock);
> +
> +static void __init sun4i_pll2_setup(struct device_node *node)
> +{
> +       const char *clk_name =3D 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 =3D of_io_request_and_map(node, 0, of_node_full_name(node));
> +       if (IS_ERR(reg))
> +               return;
> +
> +       clk_data =3D kzalloc(sizeof(*clk_data), GFP_KERNEL);
> +       if (!clk_data)
> +               goto err_unmap;
> +
> +       clks =3D kcalloc(SUN4I_PLL2_OUTPUTS, sizeof(struct clk *), GFP_KE=
RNEL);
> +       if (!clks)
> +               goto err_free_data;
> +
> +       parent =3D of_clk_get_parent_name(node, 0);
> +       prediv_clk =3D clk_register_divider(NULL, "pll2-prediv",
> +                                         parent, 0, reg,
> +                                         SUN4I_PLL2_PRE_DIV_SHIFT,
> +                                         SUN4I_PLL2_PRE_DIV_WIDTH,
> +                                         CLK_DIVIDER_ONE_BASED |
> +                                         CLK_DIVIDER_ALLOW_ZERO,

According to the A10 manual, the prediv is zero based, but allows zero.
So you should just have CLK_DIVIDER_ALLOW_ZERO here.

The rest looks good.

ChenYu

> +                                         &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 =3D kzalloc(sizeof(struct clk_gate), GFP_KERNEL);
> +       if (!gate)
> +               goto err_unregister_prediv;
> +
> +       gate->reg =3D reg;
> +       gate->bit_idx =3D SUN4I_PLL2_ENABLE;
> +       gate->lock =3D &sun4i_a10_pll2_lock;
> +
> +       /* Setup the multiplier part of the PLL2 */
> +       mult =3D kzalloc(sizeof(struct clk_multiplier), GFP_KERNEL);
> +       if (!mult)
> +               goto err_free_gate;
> +
> +       mult->reg =3D reg;
> +       mult->shift =3D SUN4I_PLL2_N_SHIFT;
> +       mult->width =3D 7;
> +       mult->flags =3D CLK_MULTIPLIER_ZERO_BYPASS |
> +                       CLK_MULTIPLIER_ROUND_CLOSEST;
> +       mult->lock =3D &sun4i_a10_pll2_lock;
> +
> +       parent =3D __clk_get_name(prediv_clk);
> +       base_clk =3D 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 =3D __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 =3D readl(reg);
> +       val &=3D ~(SUN4I_PLL2_POST_DIV_MASK << SUN4I_PLL2_POST_DIV_SHIFT)=
;
> +       val |=3D SUN4I_PLL2_POST_DIV_VALUE << 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] =3D clk_register_fixed_factor(NULL, clk_n=
ame,
> +                                                           parent,
> +                                                           CLK_SET_RATE_=
PARENT,
> +                                                           1,
> +                                                           SUN4I_PLL2_PO=
ST_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] =3D clk_register_fixed_factor(NULL, clk_n=
ame,
> +                                                           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] =3D clk_register_fixed_factor(NULL, clk_n=
ame,
> +                                                           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] =3D clk_register_fixed_factor(NULL, clk_n=
ame,
> +                                                           parent,
> +                                                           CLK_SET_RATE_=
PARENT,
> +                                                           2, 1);
> +       WARN_ON(IS_ERR(clks[SUN4I_A10_PLL2_8X]));
> +
> +       clk_data->clks =3D clks;
> +       clk_data->clk_num =3D 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);
> +}
> +CLK_OF_DECLARE(sun4i_pll2, "allwinner,sun4i-a10-pll2-clk", sun4i_pll2_se=
tup);
> diff --git a/include/dt-bindings/clock/sun4i-a10-pll2.h b/include/dt-bind=
ings/clock/sun4i-a10-pll2.h
> new file mode 100644
> index 000000000000..071c8112d531
> --- /dev/null
> +++ b/include/dt-bindings/clock/sun4i-a10-pll2.h
> @@ -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 th=
e
> + *     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_ */
> --
> 2.5.3
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-clk" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH v4 2/5] clk: sunxi: Add a driver for the PLL2
@ 2015-10-14  9:10     ` Chen-Yu Tsai
  0 siblings, 0 replies; 24+ messages in thread
From: Chen-Yu Tsai @ 2015-10-14  9:10 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Oct 13, 2015 at 4:43 AM, Maxime Ripard
<maxime.ripard@free-electrons.com> wrote:
> The PLL2 on the A10 and later SoCs is the clock used for all the audio
> related operations.
>
> This clock has a somewhat complex output tree, with three outputs (2X, 4X
> and 8X) with a fixed divider from the base clock, and an output (1X) with a
> post divider.
>
> However, we can simplify things since the 1X divider can be fixed, and we
> end up by having a base clock not exposed to any device (or at least
> directly, since the 4X output doesn't have any divider), and 4 fixed
> divider clocks that will be exposed.
>
> This clock seems to have been introduced, at least in this form, in the
> revision B of the A10, but we don't have any information on the clock used
> on the revision A.
>
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> ---
>  drivers/clk/sunxi/Makefile                 |   1 +
>  drivers/clk/sunxi/clk-a10-pll2.c           | 188 +++++++++++++++++++++++++++++
>  include/dt-bindings/clock/sun4i-a10-pll2.h |  53 ++++++++
>  3 files changed, 242 insertions(+)
>  create mode 100644 drivers/clk/sunxi/clk-a10-pll2.c
>  create mode 100644 include/dt-bindings/clock/sun4i-a10-pll2.h
>
> diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile
> index f5a35b82cc1a..c658a18ba7cb 100644
> --- a/drivers/clk/sunxi/Makefile
> +++ b/drivers/clk/sunxi/Makefile
> @@ -4,6 +4,7 @@
>
>  obj-y += clk-sunxi.o clk-factors.o
>  obj-y += clk-a10-hosc.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-pll2.c b/drivers/clk/sunxi/clk-a10-pll2.c
> new file mode 100644
> index 000000000000..b877cc6c3622
> --- /dev/null
> +++ b/drivers/clk/sunxi/clk-a10-pll2.c
> @@ -0,0 +1,188 @@
> +/*
> + * 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, 0)
> +
> +#define SUN4I_PLL2_N_SHIFT             8
> +#define SUN4I_PLL2_N_WIDTH             7
> +#define SUN4I_PLL2_N_MASK              GENMASK(SUN4I_PLL2_N_WIDTH, 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, 0)
> +
> +#define SUN4I_PLL2_POST_DIV_VALUE      4
> +
> +#define SUN4I_PLL2_OUTPUTS             4
> +
> +static DEFINE_SPINLOCK(sun4i_a10_pll2_lock);
> +
> +static void __init sun4i_pll2_setup(struct device_node *node)
> +{
> +       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,
> +                                         CLK_DIVIDER_ONE_BASED |
> +                                         CLK_DIVIDER_ALLOW_ZERO,

According to the A10 manual, the prediv is zero based, but allows zero.
So you should just have CLK_DIVIDER_ALLOW_ZERO here.

The rest looks good.

ChenYu

> +                                         &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 << 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);
> +}
> +CLK_OF_DECLARE(sun4i_pll2, "allwinner,sun4i-a10-pll2-clk", sun4i_pll2_setup);
> diff --git a/include/dt-bindings/clock/sun4i-a10-pll2.h b/include/dt-bindings/clock/sun4i-a10-pll2.h
> new file mode 100644
> index 000000000000..071c8112d531
> --- /dev/null
> +++ b/include/dt-bindings/clock/sun4i-a10-pll2.h
> @@ -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_ */
> --
> 2.5.3
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-clk" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v4 2/5] clk: sunxi: Add a driver for the PLL2
  2015-10-14  9:10     ` Chen-Yu Tsai
@ 2015-10-14  9:14       ` Chen-Yu Tsai
  -1 siblings, 0 replies; 24+ messages in thread
From: Chen-Yu Tsai @ 2015-10-14  9:14 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Chen-Yu Tsai, Mike Turquette, Stephen Boyd, Emilio Lopez,
	linux-arm-kernel, Hans de Goede, linux-clk, linux-sunxi

On Wed, Oct 14, 2015 at 5:10 PM, Chen-Yu Tsai <wens@csie.org> wrote:
> On Tue, Oct 13, 2015 at 4:43 AM, Maxime Ripard
> <maxime.ripard@free-electrons.com> wrote:
>> The PLL2 on the A10 and later SoCs is the clock used for all the audio
>> related operations.
>>
>> This clock has a somewhat complex output tree, with three outputs (2X, 4=
X
>> and 8X) with a fixed divider from the base clock, and an output (1X) wit=
h a
>> post divider.
>>
>> However, we can simplify things since the 1X divider can be fixed, and w=
e
>> end up by having a base clock not exposed to any device (or at least
>> directly, since the 4X output doesn't have any divider), and 4 fixed
>> divider clocks that will be exposed.
>>
>> This clock seems to have been introduced, at least in this form, in the
>> revision B of the A10, but we don't have any information on the clock us=
ed
>> on the revision A.
>>
>> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
>> ---
>>  drivers/clk/sunxi/Makefile                 |   1 +
>>  drivers/clk/sunxi/clk-a10-pll2.c           | 188 ++++++++++++++++++++++=
+++++++
>>  include/dt-bindings/clock/sun4i-a10-pll2.h |  53 ++++++++
>>  3 files changed, 242 insertions(+)
>>  create mode 100644 drivers/clk/sunxi/clk-a10-pll2.c
>>  create mode 100644 include/dt-bindings/clock/sun4i-a10-pll2.h
>>
>> diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile
>> index f5a35b82cc1a..c658a18ba7cb 100644
>> --- a/drivers/clk/sunxi/Makefile
>> +++ b/drivers/clk/sunxi/Makefile
>> @@ -4,6 +4,7 @@
>>
>>  obj-y +=3D clk-sunxi.o clk-factors.o
>>  obj-y +=3D clk-a10-hosc.o
>> +obj-y +=3D clk-a10-pll2.o
>>  obj-y +=3D clk-a20-gmac.o
>>  obj-y +=3D clk-mod0.o
>>  obj-y +=3D clk-simple-gates.o
>> diff --git a/drivers/clk/sunxi/clk-a10-pll2.c b/drivers/clk/sunxi/clk-a1=
0-pll2.c
>> new file mode 100644
>> index 000000000000..b877cc6c3622
>> --- /dev/null
>> +++ b/drivers/clk/sunxi/clk-a10-pll2.c
>> @@ -0,0 +1,188 @@
>> +/*
>> + * Copyright 2013 Emilio L=C3=B3pez
>> + * Emilio L=C3=B3pez <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_D=
IV_WIDTH, 0)
>> +
>> +#define SUN4I_PLL2_N_SHIFT             8
>> +#define SUN4I_PLL2_N_WIDTH             7
>> +#define SUN4I_PLL2_N_MASK              GENMASK(SUN4I_PLL2_N_WIDTH, 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_WIDT=
H, 0)
>> +
>> +#define SUN4I_PLL2_POST_DIV_VALUE      4
>> +
>> +#define SUN4I_PLL2_OUTPUTS             4
>> +
>> +static DEFINE_SPINLOCK(sun4i_a10_pll2_lock);
>> +
>> +static void __init sun4i_pll2_setup(struct device_node *node)
>> +{
>> +       const char *clk_name =3D 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 =3D of_io_request_and_map(node, 0, of_node_full_name(node));
>> +       if (IS_ERR(reg))
>> +               return;
>> +
>> +       clk_data =3D kzalloc(sizeof(*clk_data), GFP_KERNEL);
>> +       if (!clk_data)
>> +               goto err_unmap;
>> +
>> +       clks =3D kcalloc(SUN4I_PLL2_OUTPUTS, sizeof(struct clk *), GFP_K=
ERNEL);
>> +       if (!clks)
>> +               goto err_free_data;
>> +
>> +       parent =3D of_clk_get_parent_name(node, 0);
>> +       prediv_clk =3D clk_register_divider(NULL, "pll2-prediv",
>> +                                         parent, 0, reg,
>> +                                         SUN4I_PLL2_PRE_DIV_SHIFT,
>> +                                         SUN4I_PLL2_PRE_DIV_WIDTH,
>> +                                         CLK_DIVIDER_ONE_BASED |
>> +                                         CLK_DIVIDER_ALLOW_ZERO,
>
> According to the A10 manual, the prediv is zero based, but allows zero.
> So you should just have CLK_DIVIDER_ALLOW_ZERO here.

My bad, I misread the description in clk-provider.h. Your code is indeed
correct.

Reviewed-by: Chen-Yu Tsai <wens@csie.org>

> The rest looks good.

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH v4 2/5] clk: sunxi: Add a driver for the PLL2
@ 2015-10-14  9:14       ` Chen-Yu Tsai
  0 siblings, 0 replies; 24+ messages in thread
From: Chen-Yu Tsai @ 2015-10-14  9:14 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Oct 14, 2015 at 5:10 PM, Chen-Yu Tsai <wens@csie.org> wrote:
> On Tue, Oct 13, 2015 at 4:43 AM, Maxime Ripard
> <maxime.ripard@free-electrons.com> wrote:
>> The PLL2 on the A10 and later SoCs is the clock used for all the audio
>> related operations.
>>
>> This clock has a somewhat complex output tree, with three outputs (2X, 4X
>> and 8X) with a fixed divider from the base clock, and an output (1X) with a
>> post divider.
>>
>> However, we can simplify things since the 1X divider can be fixed, and we
>> end up by having a base clock not exposed to any device (or at least
>> directly, since the 4X output doesn't have any divider), and 4 fixed
>> divider clocks that will be exposed.
>>
>> This clock seems to have been introduced, at least in this form, in the
>> revision B of the A10, but we don't have any information on the clock used
>> on the revision A.
>>
>> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
>> ---
>>  drivers/clk/sunxi/Makefile                 |   1 +
>>  drivers/clk/sunxi/clk-a10-pll2.c           | 188 +++++++++++++++++++++++++++++
>>  include/dt-bindings/clock/sun4i-a10-pll2.h |  53 ++++++++
>>  3 files changed, 242 insertions(+)
>>  create mode 100644 drivers/clk/sunxi/clk-a10-pll2.c
>>  create mode 100644 include/dt-bindings/clock/sun4i-a10-pll2.h
>>
>> diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile
>> index f5a35b82cc1a..c658a18ba7cb 100644
>> --- a/drivers/clk/sunxi/Makefile
>> +++ b/drivers/clk/sunxi/Makefile
>> @@ -4,6 +4,7 @@
>>
>>  obj-y += clk-sunxi.o clk-factors.o
>>  obj-y += clk-a10-hosc.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-pll2.c b/drivers/clk/sunxi/clk-a10-pll2.c
>> new file mode 100644
>> index 000000000000..b877cc6c3622
>> --- /dev/null
>> +++ b/drivers/clk/sunxi/clk-a10-pll2.c
>> @@ -0,0 +1,188 @@
>> +/*
>> + * 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, 0)
>> +
>> +#define SUN4I_PLL2_N_SHIFT             8
>> +#define SUN4I_PLL2_N_WIDTH             7
>> +#define SUN4I_PLL2_N_MASK              GENMASK(SUN4I_PLL2_N_WIDTH, 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, 0)
>> +
>> +#define SUN4I_PLL2_POST_DIV_VALUE      4
>> +
>> +#define SUN4I_PLL2_OUTPUTS             4
>> +
>> +static DEFINE_SPINLOCK(sun4i_a10_pll2_lock);
>> +
>> +static void __init sun4i_pll2_setup(struct device_node *node)
>> +{
>> +       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,
>> +                                         CLK_DIVIDER_ONE_BASED |
>> +                                         CLK_DIVIDER_ALLOW_ZERO,
>
> According to the A10 manual, the prediv is zero based, but allows zero.
> So you should just have CLK_DIVIDER_ALLOW_ZERO here.

My bad, I misread the description in clk-provider.h. Your code is indeed
correct.

Reviewed-by: Chen-Yu Tsai <wens@csie.org>

> The rest looks good.

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v4 3/5] clk: sunxi: pll2: Add A13 support
  2015-10-12 20:43   ` Maxime Ripard
@ 2015-10-14  9:20     ` Chen-Yu Tsai
  -1 siblings, 0 replies; 24+ messages in thread
From: Chen-Yu Tsai @ 2015-10-14  9:20 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Mike Turquette, Stephen Boyd, Emilio Lopez, linux-arm-kernel,
	Chen-Yu Tsai, Hans de Goede, linux-clk, linux-sunxi

On Tue, Oct 13, 2015 at 4:43 AM, Maxime Ripard
<maxime.ripard@free-electrons.com> wrote:
> The A13, unlike the A10 and A20, doesn't use a pass-through exception for
> the 0 value in the pre and post dividers, but increments all the values
> written in the register by one.
>
> Add an exception for both these cases to handle them nicely.
>
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>

Reviewed-by: Chen-Yu Tsai <wens@csie.org>

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH v4 3/5] clk: sunxi: pll2: Add A13 support
@ 2015-10-14  9:20     ` Chen-Yu Tsai
  0 siblings, 0 replies; 24+ messages in thread
From: Chen-Yu Tsai @ 2015-10-14  9:20 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Oct 13, 2015 at 4:43 AM, Maxime Ripard
<maxime.ripard@free-electrons.com> wrote:
> The A13, unlike the A10 and A20, doesn't use a pass-through exception for
> the 0 value in the pre and post dividers, but increments all the values
> written in the register by one.
>
> Add an exception for both these cases to handle them nicely.
>
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>

Reviewed-by: Chen-Yu Tsai <wens@csie.org>

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v4 5/5] clk: sunxi: mod1 clock support
  2015-10-12 20:43   ` Maxime Ripard
@ 2015-10-14  9:25     ` Chen-Yu Tsai
  -1 siblings, 0 replies; 24+ messages in thread
From: Chen-Yu Tsai @ 2015-10-14  9:25 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Mike Turquette, Stephen Boyd, Emilio Lopez, linux-sunxi,
	Hans de Goede, Chen-Yu Tsai, linux-clk, linux-arm-kernel

On Tue, Oct 13, 2015 at 4:43 AM, Maxime Ripard
<maxime.ripard@free-electrons.com> wrote:
> From: Emilio L=C3=B3pez <emilio@elopez.com.ar>
>
> The module 1 type of clocks consist of a gate and a mux and are used on
> the audio blocks to mux and gate the PLL2 outputs for AC97, IIS or
> SPDIF. This commit adds support for them on the sunxi clock driver.
>
> Signed-off-by: Emilio L=C3=B3pez <emilio@elopez.com.ar>
> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> ---
>  drivers/clk/sunxi/Makefile       |  1 +
>  drivers/clk/sunxi/clk-a10-mod1.c | 81 ++++++++++++++++++++++++++++++++++=
++++++
>  2 files changed, 82 insertions(+)
>  create mode 100644 drivers/clk/sunxi/clk-a10-mod1.c
>
> diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile
> index 70a449a419e6..cb4c299214ce 100644
> --- a/drivers/clk/sunxi/Makefile
> +++ b/drivers/clk/sunxi/Makefile
> @@ -5,6 +5,7 @@
>  obj-y +=3D clk-sunxi.o clk-factors.o
>  obj-y +=3D clk-a10-codec.o
>  obj-y +=3D clk-a10-hosc.o
> +obj-y +=3D clk-a10-mod1.o
>  obj-y +=3D clk-a10-pll2.o
>  obj-y +=3D clk-a20-gmac.o
>  obj-y +=3D clk-mod0.o
> diff --git a/drivers/clk/sunxi/clk-a10-mod1.c b/drivers/clk/sunxi/clk-a10=
-mod1.c
> new file mode 100644
> index 000000000000..099dbdab7afe
> --- /dev/null
> +++ b/drivers/clk/sunxi/clk-a10-mod1.c
> @@ -0,0 +1,81 @@
> +/*
> + * Copyright 2013 Emilio L=C3=B3pez
> + *
> + * Emilio L=C3=B3pez <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 =3D node->name;
> +       void __iomem *reg;
> +       int i =3D 0;
> +
> +       reg =3D of_io_request_and_map(node, 0, of_node_full_name(node));
> +       if (IS_ERR(reg))
> +               return;
> +
> +       mux =3D kzalloc(sizeof(*mux), GFP_KERNEL);
> +       if (!mux)
> +               goto err_unmap;
> +
> +       gate =3D kzalloc(sizeof(*gate), GFP_KERNEL);
> +       if (!gate)
> +               goto err_free_mux;
> +
> +       of_property_read_string(node, "clock-output-names", &clk_name);
> +       of_clk_parent_fill(node, parents, SUN4I_MOD1_MAX_PARENTS);

          i =3D of_clk_parent_fill(node, parents, SUN4I_MOD1_MAX_PARENTS); =
?

Otherwise,

Reviewed-by: Chen-Yu Tsai <wens@csie.org>

> +
> +       gate->reg =3D reg;
> +       gate->bit_idx =3D SUN4I_MOD1_ENABLE;
> +       gate->lock =3D &mod1_lock;
> +       mux->reg =3D reg;
> +       mux->shift =3D SUN4I_MOD1_MUX;
> +       mux->mask =3D BIT(SUN4I_MOD1_MUX_WIDTH) - 1;
> +       mux->lock =3D &mod1_lock;
> +
> +       clk =3D 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);
> --
> 2.5.3
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH v4 5/5] clk: sunxi: mod1 clock support
@ 2015-10-14  9:25     ` Chen-Yu Tsai
  0 siblings, 0 replies; 24+ messages in thread
From: Chen-Yu Tsai @ 2015-10-14  9:25 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Oct 13, 2015 at 4:43 AM, Maxime Ripard
<maxime.ripard@free-electrons.com> wrote:
> From: Emilio L?pez <emilio@elopez.com.ar>
>
> The module 1 type of clocks consist of a gate and a mux and are used on
> the audio blocks to mux and gate the PLL2 outputs for AC97, IIS or
> SPDIF. This commit adds support for them on the sunxi clock driver.
>
> Signed-off-by: Emilio L?pez <emilio@elopez.com.ar>
> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> ---
>  drivers/clk/sunxi/Makefile       |  1 +
>  drivers/clk/sunxi/clk-a10-mod1.c | 81 ++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 82 insertions(+)
>  create mode 100644 drivers/clk/sunxi/clk-a10-mod1.c
>
> diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile
> index 70a449a419e6..cb4c299214ce 100644
> --- a/drivers/clk/sunxi/Makefile
> +++ b/drivers/clk/sunxi/Makefile
> @@ -5,6 +5,7 @@
>  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
> diff --git a/drivers/clk/sunxi/clk-a10-mod1.c b/drivers/clk/sunxi/clk-a10-mod1.c
> new file mode 100644
> index 000000000000..099dbdab7afe
> --- /dev/null
> +++ b/drivers/clk/sunxi/clk-a10-mod1.c
> @@ -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 = 0;
> +
> +       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);
> +       of_clk_parent_fill(node, parents, SUN4I_MOD1_MAX_PARENTS);

          i = of_clk_parent_fill(node, parents, SUN4I_MOD1_MAX_PARENTS); ?

Otherwise,

Reviewed-by: Chen-Yu Tsai <wens@csie.org>

> +
> +       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);
> --
> 2.5.3
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v4 1/5] clk: Add a basic multiplier clock
  2015-10-12 20:43   ` Maxime Ripard
@ 2015-10-14  9:44     ` Chen-Yu Tsai
  -1 siblings, 0 replies; 24+ messages in thread
From: Chen-Yu Tsai @ 2015-10-14  9:44 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Mike Turquette, Stephen Boyd, Emilio Lopez, linux-arm-kernel,
	Chen-Yu Tsai, Hans de Goede, linux-clk, linux-sunxi

On Tue, Oct 13, 2015 at 4:43 AM, Maxime Ripard
<maxime.ripard@free-electrons.com> wrote:
> Some clocks are using a multiplier component, however, unlike their mux,
> gate or divider counterpart, these factors don't have a basic clock
> implementation.
>
> This leads to code duplication across platforms that want to use that kind
> of clocks, and the impossibility to use the composite clocks with such a
> clock without defining your own rate operations.
>
> Create such a driver in order to remove these issues, and hopefully factor
> the implementations, reducing code size across platforms and consolidating
> the various implementations.
>
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> ---
>  drivers/clk/Makefile         |   1 +
>  drivers/clk/clk-multiplier.c | 181 +++++++++++++++++++++++++++++++++++++++++++
>  include/linux/clk-provider.h |  42 ++++++++++
>  3 files changed, 224 insertions(+)
>  create mode 100644 drivers/clk/clk-multiplier.c
>
> diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
> index d08b3e5985be..0b2101039508 100644
> --- a/drivers/clk/Makefile
> +++ b/drivers/clk/Makefile
> @@ -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
> index 000000000000..75fee96ea1ee
> --- /dev/null
> +++ b/drivers/clk/clk-multiplier.c
> @@ -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, 0);

GENMASK is inclusive on both ends, so you probably want

    GENMASK(multi->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 * i == orig_parent_rate) {

This seems backwards. Doesn't rate = i * parent_rate?

> +                       /*
> +                        * 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, mult->shift);

Same here.

Also, git complains about trailing whitespaces:

.git/rebase-apply/patch:59: trailing whitespace.
.git/rebase-apply/patch:65: trailing whitespace.
.git/rebase-apply/patch:183: trailing whitespace.


Regards
ChenYu

> +       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);
> diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
> index 3ecc07d0da77..6a7dfe33a317 100644
> --- a/include/linux/clk-provider.h
> +++ b/include/linux/clk-provider.h
> @@ -518,6 +518,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
>   *
> --
> 2.5.3
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-clk" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH v4 1/5] clk: Add a basic multiplier clock
@ 2015-10-14  9:44     ` Chen-Yu Tsai
  0 siblings, 0 replies; 24+ messages in thread
From: Chen-Yu Tsai @ 2015-10-14  9:44 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Oct 13, 2015 at 4:43 AM, Maxime Ripard
<maxime.ripard@free-electrons.com> wrote:
> Some clocks are using a multiplier component, however, unlike their mux,
> gate or divider counterpart, these factors don't have a basic clock
> implementation.
>
> This leads to code duplication across platforms that want to use that kind
> of clocks, and the impossibility to use the composite clocks with such a
> clock without defining your own rate operations.
>
> Create such a driver in order to remove these issues, and hopefully factor
> the implementations, reducing code size across platforms and consolidating
> the various implementations.
>
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> ---
>  drivers/clk/Makefile         |   1 +
>  drivers/clk/clk-multiplier.c | 181 +++++++++++++++++++++++++++++++++++++++++++
>  include/linux/clk-provider.h |  42 ++++++++++
>  3 files changed, 224 insertions(+)
>  create mode 100644 drivers/clk/clk-multiplier.c
>
> diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
> index d08b3e5985be..0b2101039508 100644
> --- a/drivers/clk/Makefile
> +++ b/drivers/clk/Makefile
> @@ -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
> index 000000000000..75fee96ea1ee
> --- /dev/null
> +++ b/drivers/clk/clk-multiplier.c
> @@ -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, 0);

GENMASK is inclusive on both ends, so you probably want

    GENMASK(multi->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 * i == orig_parent_rate) {

This seems backwards. Doesn't rate = i * parent_rate?

> +                       /*
> +                        * 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, mult->shift);

Same here.

Also, git complains about trailing whitespaces:

.git/rebase-apply/patch:59: trailing whitespace.
.git/rebase-apply/patch:65: trailing whitespace.
.git/rebase-apply/patch:183: trailing whitespace.


Regards
ChenYu

> +       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);
> diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
> index 3ecc07d0da77..6a7dfe33a317 100644
> --- a/include/linux/clk-provider.h
> +++ b/include/linux/clk-provider.h
> @@ -518,6 +518,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
>   *
> --
> 2.5.3
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-clk" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v4 2/5] clk: sunxi: Add a driver for the PLL2
  2015-10-12 20:43   ` Maxime Ripard
@ 2015-10-14  9:47     ` Chen-Yu Tsai
  -1 siblings, 0 replies; 24+ messages in thread
From: Chen-Yu Tsai @ 2015-10-14  9:47 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Mike Turquette, Stephen Boyd, Emilio Lopez, linux-arm-kernel,
	Chen-Yu Tsai, Hans de Goede, linux-clk, linux-sunxi

On Tue, Oct 13, 2015 at 4:43 AM, Maxime Ripard
<maxime.ripard@free-electrons.com> wrote:
> The PLL2 on the A10 and later SoCs is the clock used for all the audio
> related operations.
>
> This clock has a somewhat complex output tree, with three outputs (2X, 4X
> and 8X) with a fixed divider from the base clock, and an output (1X) with=
 a
> post divider.
>
> However, we can simplify things since the 1X divider can be fixed, and we
> end up by having a base clock not exposed to any device (or at least
> directly, since the 4X output doesn't have any divider), and 4 fixed
> divider clocks that will be exposed.
>
> This clock seems to have been introduced, at least in this form, in the
> revision B of the A10, but we don't have any information on the clock use=
d
> on the revision A.
>
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> ---
>  drivers/clk/sunxi/Makefile                 |   1 +
>  drivers/clk/sunxi/clk-a10-pll2.c           | 188 +++++++++++++++++++++++=
++++++
>  include/dt-bindings/clock/sun4i-a10-pll2.h |  53 ++++++++
>  3 files changed, 242 insertions(+)
>  create mode 100644 drivers/clk/sunxi/clk-a10-pll2.c
>  create mode 100644 include/dt-bindings/clock/sun4i-a10-pll2.h
>
> diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile
> index f5a35b82cc1a..c658a18ba7cb 100644
> --- a/drivers/clk/sunxi/Makefile
> +++ b/drivers/clk/sunxi/Makefile
> @@ -4,6 +4,7 @@
>
>  obj-y +=3D clk-sunxi.o clk-factors.o
>  obj-y +=3D clk-a10-hosc.o
> +obj-y +=3D clk-a10-pll2.o
>  obj-y +=3D clk-a20-gmac.o
>  obj-y +=3D clk-mod0.o
>  obj-y +=3D clk-simple-gates.o
> diff --git a/drivers/clk/sunxi/clk-a10-pll2.c b/drivers/clk/sunxi/clk-a10=
-pll2.c
> new file mode 100644
> index 000000000000..b877cc6c3622
> --- /dev/null
> +++ b/drivers/clk/sunxi/clk-a10-pll2.c
> @@ -0,0 +1,188 @@
> +/*
> + * Copyright 2013 Emilio L=C3=B3pez
> + * Emilio L=C3=B3pez <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_DI=
V_WIDTH, 0)
> +
> +#define SUN4I_PLL2_N_SHIFT             8
> +#define SUN4I_PLL2_N_WIDTH             7
> +#define SUN4I_PLL2_N_MASK              GENMASK(SUN4I_PLL2_N_WIDTH, 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=
, 0)

GENMASK, like in patch 1, should be GENMASK(width - 1, 0);

Sorry about the multiple replies.

ChenYu

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH v4 2/5] clk: sunxi: Add a driver for the PLL2
@ 2015-10-14  9:47     ` Chen-Yu Tsai
  0 siblings, 0 replies; 24+ messages in thread
From: Chen-Yu Tsai @ 2015-10-14  9:47 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Oct 13, 2015 at 4:43 AM, Maxime Ripard
<maxime.ripard@free-electrons.com> wrote:
> The PLL2 on the A10 and later SoCs is the clock used for all the audio
> related operations.
>
> This clock has a somewhat complex output tree, with three outputs (2X, 4X
> and 8X) with a fixed divider from the base clock, and an output (1X) with a
> post divider.
>
> However, we can simplify things since the 1X divider can be fixed, and we
> end up by having a base clock not exposed to any device (or at least
> directly, since the 4X output doesn't have any divider), and 4 fixed
> divider clocks that will be exposed.
>
> This clock seems to have been introduced, at least in this form, in the
> revision B of the A10, but we don't have any information on the clock used
> on the revision A.
>
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> ---
>  drivers/clk/sunxi/Makefile                 |   1 +
>  drivers/clk/sunxi/clk-a10-pll2.c           | 188 +++++++++++++++++++++++++++++
>  include/dt-bindings/clock/sun4i-a10-pll2.h |  53 ++++++++
>  3 files changed, 242 insertions(+)
>  create mode 100644 drivers/clk/sunxi/clk-a10-pll2.c
>  create mode 100644 include/dt-bindings/clock/sun4i-a10-pll2.h
>
> diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile
> index f5a35b82cc1a..c658a18ba7cb 100644
> --- a/drivers/clk/sunxi/Makefile
> +++ b/drivers/clk/sunxi/Makefile
> @@ -4,6 +4,7 @@
>
>  obj-y += clk-sunxi.o clk-factors.o
>  obj-y += clk-a10-hosc.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-pll2.c b/drivers/clk/sunxi/clk-a10-pll2.c
> new file mode 100644
> index 000000000000..b877cc6c3622
> --- /dev/null
> +++ b/drivers/clk/sunxi/clk-a10-pll2.c
> @@ -0,0 +1,188 @@
> +/*
> + * 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, 0)
> +
> +#define SUN4I_PLL2_N_SHIFT             8
> +#define SUN4I_PLL2_N_WIDTH             7
> +#define SUN4I_PLL2_N_MASK              GENMASK(SUN4I_PLL2_N_WIDTH, 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, 0)

GENMASK, like in patch 1, should be GENMASK(width - 1, 0);

Sorry about the multiple replies.

ChenYu

^ permalink raw reply	[flat|nested] 24+ messages in thread

end of thread, other threads:[~2015-10-14  9:47 UTC | newest]

Thread overview: 24+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-10-12 20:43 [PATCH v4 0/5] clk: sunxi: Add support for the Audio PLL Maxime Ripard
2015-10-12 20:43 ` Maxime Ripard
2015-10-12 20:43 ` [PATCH v4 1/5] clk: Add a basic multiplier clock Maxime Ripard
2015-10-12 20:43   ` Maxime Ripard
2015-10-14  9:44   ` Chen-Yu Tsai
2015-10-14  9:44     ` Chen-Yu Tsai
2015-10-12 20:43 ` [PATCH v4 2/5] clk: sunxi: Add a driver for the PLL2 Maxime Ripard
2015-10-12 20:43   ` Maxime Ripard
2015-10-14  9:10   ` Chen-Yu Tsai
2015-10-14  9:10     ` Chen-Yu Tsai
2015-10-14  9:14     ` Chen-Yu Tsai
2015-10-14  9:14       ` Chen-Yu Tsai
2015-10-14  9:47   ` Chen-Yu Tsai
2015-10-14  9:47     ` Chen-Yu Tsai
2015-10-12 20:43 ` [PATCH v4 3/5] clk: sunxi: pll2: Add A13 support Maxime Ripard
2015-10-12 20:43   ` Maxime Ripard
2015-10-14  9:20   ` Chen-Yu Tsai
2015-10-14  9:20     ` Chen-Yu Tsai
2015-10-12 20:43 ` [PATCH v4 4/5] clk: sunxi: codec clock support Maxime Ripard
2015-10-12 20:43   ` Maxime Ripard
2015-10-12 20:43 ` [PATCH v4 5/5] clk: sunxi: mod1 " Maxime Ripard
2015-10-12 20:43   ` Maxime Ripard
2015-10-14  9:25   ` Chen-Yu Tsai
2015-10-14  9:25     ` Chen-Yu Tsai

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.