All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/3] clock driver for sunxi
@ 2013-01-22  6:12 Emilio López
  2013-01-22  6:12 ` [PATCH 1/3] clk: arm: sunxi: Add a new clock driver for sunxi SOCs Emilio López
                   ` (5 more replies)
  0 siblings, 6 replies; 26+ messages in thread
From: Emilio López @ 2013-01-22  6:12 UTC (permalink / raw)
  To: linux-arm-kernel

Hello everyone,

This patchset adds basic clock support for sunxi devices. Currently, it
implements support for the two oscillators, the main PLL, the CPU mux,
and its three divisor clocks. With this in place, it is possible to
write a cpufreq driver and have it work.

I have tested this driver successfully on a Cubieboard (A10, sun4i)
using the cpufreq driver from the linux-sunxi tree after minor
modifications (the clock names are not the same).

Any feedback will be highly appreciated

Thanks,

--
Emilio

Emilio L?pez (3):
  clk: arm: sunxi: Add a new clock driver for sunxi SOCs
  arm: sunxi: Add clock definitions for the new clock driver
  arm: sunxi: Add useful information about sunxi clocks

 Documentation/arm/sunxi/clocks.txt                |  56 +++++
 Documentation/devicetree/bindings/clock/sunxi.txt |  47 ++++
 arch/arm/boot/dts/sunxi.dtsi                      |  55 ++++-
 drivers/clk/Makefile                              |   2 +-
 drivers/clk/clk-sunxi.c                           |  30 ---
 drivers/clk/sunxi/Makefile                        |   5 +
 drivers/clk/sunxi/clk-factors.c                   | 175 ++++++++++++++
 drivers/clk/sunxi/clk-factors.h                   |  25 ++
 drivers/clk/sunxi/clk-fixed-gate.c                | 152 +++++++++++++
 drivers/clk/sunxi/clk-fixed-gate.h                |  13 ++
 drivers/clk/sunxi/clk-sunxi.c                     | 265 ++++++++++++++++++++++
 11 files changed, 788 insertions(+), 37 deletions(-)
 create mode 100644 Documentation/arm/sunxi/clocks.txt
 create mode 100644 Documentation/devicetree/bindings/clock/sunxi.txt
 delete mode 100644 drivers/clk/clk-sunxi.c
 create mode 100644 drivers/clk/sunxi/Makefile
 create mode 100644 drivers/clk/sunxi/clk-factors.c
 create mode 100644 drivers/clk/sunxi/clk-factors.h
 create mode 100644 drivers/clk/sunxi/clk-fixed-gate.c
 create mode 100644 drivers/clk/sunxi/clk-fixed-gate.h
 create mode 100644 drivers/clk/sunxi/clk-sunxi.c

-- 
1.8.1.1

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

* [PATCH 1/3] clk: arm: sunxi: Add a new clock driver for sunxi SOCs
  2013-01-22  6:12 [PATCH 0/3] clock driver for sunxi Emilio López
@ 2013-01-22  6:12 ` Emilio López
  2013-02-05 11:18   ` Gregory CLEMENT
  2013-02-08 11:33   ` Gregory CLEMENT
  2013-01-22  6:12 ` [PATCH 2/3] arm: sunxi: Add clock definitions for the new clock driver Emilio López
                   ` (4 subsequent siblings)
  5 siblings, 2 replies; 26+ messages in thread
From: Emilio López @ 2013-01-22  6:12 UTC (permalink / raw)
  To: linux-arm-kernel

This commit implements the base CPU clocks for sunxi devices. It has
been tested using a slightly modified cpufreq driver from the
linux-sunxi 3.0 tree.

Additionally, document the new bindings introduced by this patch, and drop
the (now unused) old sunxi clock driver.

Idling:
    / # find /sys/kernel/debug/clk -name clk_rate -print -exec cat {} \;
    /sys/kernel/debug/clk/osc24M/pll1/cpu/axi/ahb/apb0/clk_rate
    30000000
    /sys/kernel/debug/clk/osc24M/pll1/cpu/axi/ahb/clk_rate
    60000000
    /sys/kernel/debug/clk/osc24M/pll1/cpu/axi/clk_rate
    60000000
    /sys/kernel/debug/clk/osc24M/pll1/cpu/clk_rate
    60000000
    /sys/kernel/debug/clk/osc24M/pll1/clk_rate
    60000000
    /sys/kernel/debug/clk/osc24M/clk_rate
    24000000
    /sys/kernel/debug/clk/osc32k/clk_rate
    32768

After "yes >/dev/null &":
    / # find /sys/kernel/debug/clk -name clk_rate -print -exec cat {} \;
    /sys/kernel/debug/clk/osc24M/pll1/cpu/axi/ahb/apb0/clk_rate
    84000000
    /sys/kernel/debug/clk/osc24M/pll1/cpu/axi/ahb/clk_rate
    168000000
    /sys/kernel/debug/clk/osc24M/pll1/cpu/axi/clk_rate
    336000000
    /sys/kernel/debug/clk/osc24M/pll1/cpu/clk_rate
    1008000000
    /sys/kernel/debug/clk/osc24M/pll1/clk_rate
    1008000000
    /sys/kernel/debug/clk/osc24M/clk_rate
    24000000
    /sys/kernel/debug/clk/osc32k/clk_rate
    32768

Signed-off-by: Emilio L?pez <emilio@elopez.com.ar>
---
 Documentation/devicetree/bindings/clock/sunxi.txt |  47 ++++
 drivers/clk/Makefile                              |   2 +-
 drivers/clk/clk-sunxi.c                           |  30 ---
 drivers/clk/sunxi/Makefile                        |   5 +
 drivers/clk/sunxi/clk-factors.c                   | 175 ++++++++++++++
 drivers/clk/sunxi/clk-factors.h                   |  25 ++
 drivers/clk/sunxi/clk-fixed-gate.c                | 152 +++++++++++++
 drivers/clk/sunxi/clk-fixed-gate.h                |  13 ++
 drivers/clk/sunxi/clk-sunxi.c                     | 265 ++++++++++++++++++++++
 9 files changed, 683 insertions(+), 31 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/clock/sunxi.txt
 delete mode 100644 drivers/clk/clk-sunxi.c
 create mode 100644 drivers/clk/sunxi/Makefile
 create mode 100644 drivers/clk/sunxi/clk-factors.c
 create mode 100644 drivers/clk/sunxi/clk-factors.h
 create mode 100644 drivers/clk/sunxi/clk-fixed-gate.c
 create mode 100644 drivers/clk/sunxi/clk-fixed-gate.h
 create mode 100644 drivers/clk/sunxi/clk-sunxi.c

diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt
new file mode 100644
index 0000000..446c5ca
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/sunxi.txt
@@ -0,0 +1,47 @@
+Device Tree Clock bindings for arch-sunxi
+
+This binding uses the common clock binding[1].
+
+[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
+
+Required properties:
+- compatible : shall be one of the following:
+	"allwinner,sunxi-osc-clk" - for a gatable oscillator
+	"allwinner,sunxi-pll1-clk" - for the main PLL clock
+	"allwinner,sunxi-cpu-clk" - for the CPU multiplexer clock
+	"allwinner,sunxi-axi-clk" - for the sunxi AXI clock
+	"allwinner,sunxi-ahb-clk" - for the sunxi AHB clock
+	"allwinner,sunxi-apb0-clk" - for the sunxi APB0 clock
+
+Required properties for all clocks:
+- reg : shall be the control register address for the clock.
+- clocks : shall be the input parent clock(s) phandle for the clock, except for
+	the root gatable oscillator clock where it is not present
+- #clock-cells : from common clock binding; shall be set to 0.
+
+Additionally, the gatable oscillator clock requires:
+- clock-frequency : shall be the frequency of the oscillator.
+
+
+For example:
+
+osc24M: osc24M {
+	#clock-cells = <0>;
+	compatible = "allwinner,sunxi-osc-clk";
+	reg = <0x01c20050 0x4>;
+	clock-frequency = <24000000>;
+};
+
+pll1: pll1 at 01c20000 {
+	#clock-cells = <0>;
+	compatible = "allwinner,sunxi-pll1-clk";
+	reg = <0x01c20000 0x4>;
+	clocks = <&osc24M>;
+};
+
+cpu: cpu at 01c20054 {
+	#clock-cells = <0>;
+	compatible = "allwinner,sunxi-cpu-clk";
+	reg = <0x01c20054 0x4>;
+	clocks = <&osc32k>, <&osc24M>, <&pll1>;
+};
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index ee90e87..129afed 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -20,7 +20,7 @@ endif
 obj-$(CONFIG_MACH_LOONGSON1)	+= clk-ls1x.o
 obj-$(CONFIG_ARCH_U8500)	+= ux500/
 obj-$(CONFIG_ARCH_VT8500)	+= clk-vt8500.o
-obj-$(CONFIG_ARCH_SUNXI)	+= clk-sunxi.o
+obj-$(CONFIG_ARCH_SUNXI)	+= sunxi/
 obj-$(CONFIG_ARCH_ZYNQ)		+= clk-zynq.o
 
 # Chip specific
diff --git a/drivers/clk/clk-sunxi.c b/drivers/clk/clk-sunxi.c
deleted file mode 100644
index 0e831b5..0000000
--- a/drivers/clk/clk-sunxi.c
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright 2012 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/clkdev.h>
-#include <linux/clk/sunxi.h>
-#include <linux/of.h>
-
-static const __initconst struct of_device_id clk_match[] = {
-	{ .compatible = "fixed-clock", .data = of_fixed_clk_setup, },
-	{}
-};
-
-void __init sunxi_init_clocks(void)
-{
-	of_clk_init(clk_match);
-}
diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile
new file mode 100644
index 0000000..8e773be
--- /dev/null
+++ b/drivers/clk/sunxi/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for sunxi specific clk
+#
+
+obj-y += clk-sunxi.o clk-factors.o clk-fixed-gate.o
diff --git a/drivers/clk/sunxi/clk-factors.c b/drivers/clk/sunxi/clk-factors.c
new file mode 100644
index 0000000..428b47d
--- /dev/null
+++ b/drivers/clk/sunxi/clk-factors.c
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2013 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 version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Adjustable factor-based clock implementation
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/string.h>
+
+#include <linux/delay.h>
+
+#include "clk-factors.h"
+
+/*
+ * DOC: basic adjustable factor-based clock that cannot gate
+ *
+ * Traits of this clock:
+ * prepare - clk_prepare only ensures that parents are prepared
+ * enable - clk_enable only ensures that parents are enabled
+ * rate - rate is adjustable.
+ *        clk->rate = (parent->rate * N * (K + 1) >> P) / (M + 1)
+ * parent - fixed parent.  No clk_set_parent support
+ */
+
+struct clk_factors {
+	struct clk_hw hw;
+	void __iomem *reg;
+	struct clk_factors_config *config;
+	void (*get_factors) (u32 *rate, u8 *n, u8 *k, u8 *m, u8 *p);
+	spinlock_t *lock;
+};
+
+#define to_clk_factors(_hw) container_of(_hw, struct clk_factors, hw)
+
+#define SETMASK(len, pos)		(((-1U) >> (31-len))  << (pos))
+#define CLRMASK(len, pos)		(~(SETMASK(len, pos)))
+#define FACTOR_GET(bit, len, reg)	(((reg) & SETMASK(len, bit)) >> (bit))
+
+#define FACTOR_SET(bit, len, reg, val) \
+	(((reg) & CLRMASK(len, bit)) | (val << (bit)))
+
+static unsigned long clk_factors_recalc_rate(struct clk_hw *hw,
+					     unsigned long parent_rate)
+{
+	u8 n, k, p, m;
+	u32 reg;
+	unsigned long rate;
+	struct clk_factors *factors = to_clk_factors(hw);
+	struct clk_factors_config *config = factors->config;
+
+	/* Fetch the register value */
+	reg = readl(factors->reg);
+
+	/* Get each individual factor */
+	n = FACTOR_GET(config->nshift, config->nwidth, reg);
+	k = FACTOR_GET(config->kshift, config->kwidth, reg);
+	m = FACTOR_GET(config->mshift, config->mwidth, reg);
+	p = FACTOR_GET(config->pshift, config->pwidth, reg);
+
+	/* Calculate the rate */
+	rate = (parent_rate * n * (k + 1) >> p) / (m + 1);
+
+	return rate;
+}
+
+static long clk_factors_round_rate(struct clk_hw *hw, unsigned long rate,
+				   unsigned long *parent_rate)
+{
+	struct clk_factors *factors = to_clk_factors(hw);
+	factors->get_factors((u32 *)&rate, NULL, NULL, NULL, NULL);
+
+	return rate;
+}
+
+static int clk_factors_set_rate(struct clk_hw *hw, unsigned long rate,
+				unsigned long parent_rate)
+{
+	u8 n, k, m, p;
+	u32 reg;
+	struct clk_factors *factors = to_clk_factors(hw);
+	struct clk_factors_config *config = factors->config;
+	unsigned long flags = 0;
+
+	factors->get_factors((u32 *)&rate, &n, &k, &m, &p);
+
+	if (factors->lock)
+		spin_lock_irqsave(factors->lock, flags);
+
+	/* Fetch the register value */
+	reg = readl(factors->reg);
+
+	/* Set up the new factors */
+	reg = FACTOR_SET(config->nshift, config->nwidth, reg, n);
+	reg = FACTOR_SET(config->kshift, config->kwidth, reg, k);
+	reg = FACTOR_SET(config->mshift, config->mwidth, reg, m);
+	reg = FACTOR_SET(config->pshift, config->pwidth, reg, p);
+
+	/* Apply them now */
+	writel(reg, factors->reg);
+
+	/* delay 500us so pll stabilizes */
+	__delay((rate >> 20) * 500 / 2);
+
+	if (factors->lock)
+		spin_unlock_irqrestore(factors->lock, flags);
+
+	return 0;
+}
+
+static const struct clk_ops clk_factors_ops = {
+	.recalc_rate = clk_factors_recalc_rate,
+	.round_rate = clk_factors_round_rate,
+	.set_rate = clk_factors_set_rate,
+};
+
+/**
+ * clk_register_factors - register a factors clock with
+ * the clock framework
+ * @dev: device registering this clock
+ * @name: name of this clock
+ * @parent_name: name of clock's parent
+ * @flags: framework-specific flags
+ * @reg: register address to adjust factors
+ * @config: shift and width of factors n, k, m and p
+ * @get_factors: function to calculate the factors for a given frequency
+ * @lock: shared register lock for this clock
+ */
+struct clk *clk_register_factors(struct device *dev, const char *name,
+				 const char *parent_name,
+				 unsigned long flags, void __iomem *reg,
+				 struct clk_factors_config *config,
+				 void (*get_factors) (u32 *rate, u8 *n, u8 *k,
+						      u8 *m, u8 *p),
+				 spinlock_t *lock)
+{
+	struct clk_factors *factors;
+	struct clk *clk;
+	struct clk_init_data init;
+
+	/* allocate the factors */
+	factors = kzalloc(sizeof(struct clk_factors), GFP_KERNEL);
+	if (!factors) {
+		pr_err("%s: could not allocate factors clk\n", __func__);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	init.name = name;
+	init.ops = &clk_factors_ops;
+	init.flags = flags;
+	init.parent_names = (parent_name ? &parent_name : NULL);
+	init.num_parents = (parent_name ? 1 : 0);
+
+	/* struct clk_factors assignments */
+	factors->reg = reg;
+	factors->config = config;
+	factors->lock = lock;
+	factors->hw.init = &init;
+	factors->get_factors = get_factors;
+
+	/* register the clock */
+	clk = clk_register(dev, &factors->hw);
+
+	if (IS_ERR(clk))
+		kfree(factors);
+
+	return clk;
+}
diff --git a/drivers/clk/sunxi/clk-factors.h b/drivers/clk/sunxi/clk-factors.h
new file mode 100644
index 0000000..a24c889
--- /dev/null
+++ b/drivers/clk/sunxi/clk-factors.h
@@ -0,0 +1,25 @@
+#ifndef __MACH_SUNXI_CLK_FACTORS_H
+#define __MACH_SUNXI_CLK_FACTORS_H
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+
+struct clk_factors_config {
+	u8 nshift;
+	u8 nwidth;
+	u8 kshift;
+	u8 kwidth;
+	u8 mshift;
+	u8 mwidth;
+	u8 pshift;
+	u8 pwidth;
+};
+
+struct clk *clk_register_factors(struct device *dev, const char *name,
+				 const char *parent_name,
+				 unsigned long flags, void __iomem *reg,
+				 struct clk_factors_config *config,
+				 void (*get_factors) (u32 *rate, u8 *n, u8 *k,
+						      u8 *m, u8 *p),
+				 spinlock_t *lock);
+#endif
diff --git a/drivers/clk/sunxi/clk-fixed-gate.c b/drivers/clk/sunxi/clk-fixed-gate.c
new file mode 100644
index 0000000..b16eda5
--- /dev/null
+++ b/drivers/clk/sunxi/clk-fixed-gate.c
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2013 Emilio L?pez <emilio@elopez.com.ar>
+ *
+ * Based on drivers/clk/clk-gate.c,
+ *
+ * Copyright (C) 2010-2011 Canonical Ltd <jeremy.kerr@canonical.com>
+ * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org>
+ *
+ * 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.
+ *
+ * Fixed rate, gated clock implementation
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/string.h>
+
+#include "clk-fixed-gate.h"
+
+/**
+ * DOC: fixed rate clock which can gate and ungate it's ouput
+ *
+ * Traits of this clock:
+ * prepare - clk_(un)prepare only ensures parent is (un)prepared
+ * enable - clk_enable and clk_disable are functional & control gating
+ * rate - rate is always a fixed value.  No clk_set_rate support
+ * parent - fixed parent.  No clk_set_parent support
+ */
+
+struct clk_fixed_gate {
+	struct clk_hw hw;
+	u8            bit_idx;
+	u8            flags;
+	unsigned long fixed_rate;
+	void __iomem  *reg;
+	spinlock_t    *lock;
+};
+
+#define to_clk_fixed_gate(_hw) container_of(_hw, struct clk_fixed_gate, hw)
+
+static void clk_fixed_gate_endisable(struct clk_hw *hw, int enable)
+{
+	struct clk_fixed_gate *gate = to_clk_fixed_gate(hw);
+	unsigned long flags = 0;
+	u32 reg;
+
+	if (gate->lock)
+		spin_lock_irqsave(gate->lock, flags);
+
+	reg = readl(gate->reg);
+
+	if (enable)
+		reg |= BIT(gate->bit_idx);
+	else
+		reg &= ~BIT(gate->bit_idx);
+
+	writel(reg, gate->reg);
+
+	if (gate->lock)
+		spin_unlock_irqrestore(gate->lock, flags);
+}
+
+static int clk_fixed_gate_enable(struct clk_hw *hw)
+{
+	clk_fixed_gate_endisable(hw, 1);
+
+	return 0;
+}
+
+static void clk_fixed_gate_disable(struct clk_hw *hw)
+{
+	clk_fixed_gate_endisable(hw, 0);
+}
+
+static int clk_fixed_gate_is_enabled(struct clk_hw *hw)
+{
+	u32 reg;
+	struct clk_fixed_gate *gate = to_clk_fixed_gate(hw);
+
+	reg = readl(gate->reg);
+
+	reg &= BIT(gate->bit_idx);
+
+	return reg ? 1 : 0;
+}
+
+static unsigned long clk_fixed_gate_recalc_rate(struct clk_hw *hw,
+						unsigned long parent_rate)
+{
+	return to_clk_fixed_gate(hw)->fixed_rate;
+}
+
+static const struct clk_ops clk_fixed_gate_ops = {
+	.enable = clk_fixed_gate_enable,
+	.disable = clk_fixed_gate_disable,
+	.is_enabled = clk_fixed_gate_is_enabled,
+	.recalc_rate = clk_fixed_gate_recalc_rate,
+};
+
+/**
+ * clk_register_fixed_gate - register a fixed rate,
+ * gate clock with the clock framework
+ * @dev: device that is registering this clock
+ * @name: name of this clock
+ * @parent_name: name of this clock's parent
+ * @flags: framework-specific flags for this clock
+ * @reg: register address to control gating of this clock
+ * @bit_idx: which bit in the register controls gating of this clock
+ * @lock: shared register lock for this clock
+ */
+struct clk *clk_register_fixed_gate(struct device *dev, const char *name,
+				    const char *parent_name,
+				    unsigned long flags, void __iomem *reg,
+				    u8 bit_idx, unsigned long fixed_rate,
+				    spinlock_t *lock)
+{
+	struct clk_fixed_gate *gate;
+	struct clk *clk;
+	struct clk_init_data init;
+
+	/* allocate the gate */
+	gate = kzalloc(sizeof(struct clk_fixed_gate), GFP_KERNEL);
+	if (!gate) {
+		pr_err("%s: could not allocate fixed gated clk\n", __func__);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	init.name = name;
+	init.ops = &clk_fixed_gate_ops;
+	init.flags = flags | CLK_IS_BASIC;
+	init.parent_names = (parent_name ? &parent_name : NULL);
+	init.num_parents = (parent_name ? 1 : 0);
+
+	/* struct clk_fixed_gate assignments */
+	gate->fixed_rate = fixed_rate;
+	gate->reg = reg;
+	gate->bit_idx = bit_idx;
+	gate->lock = lock;
+	gate->hw.init = &init;
+
+	clk = clk_register(dev, &gate->hw);
+
+	if (IS_ERR(clk))
+		kfree(gate);
+
+	return clk;
+}
diff --git a/drivers/clk/sunxi/clk-fixed-gate.h b/drivers/clk/sunxi/clk-fixed-gate.h
new file mode 100644
index 0000000..29d9ed3
--- /dev/null
+++ b/drivers/clk/sunxi/clk-fixed-gate.h
@@ -0,0 +1,13 @@
+#ifndef __MACH_SUNXI_CLK_FIXED_GATE_H
+#define __MACH_SUNXI_CLK_FIXED_GATE_H
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+
+struct clk *clk_register_fixed_gate(struct device *dev, const char *name,
+				    const char *parent_name,
+				    unsigned long flags, void __iomem *reg,
+				    u8 bit_idx, unsigned long fixed_rate,
+				    spinlock_t *lock);
+
+#endif
diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c
new file mode 100644
index 0000000..cb587a0
--- /dev/null
+++ b/drivers/clk/sunxi/clk-sunxi.c
@@ -0,0 +1,265 @@
+/*
+ * 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/clkdev.h>
+#include <linux/clk/sunxi.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+
+#include "clk-factors.h"
+#include "clk-fixed-gate.h"
+
+static DEFINE_SPINLOCK(clk_lock);
+
+/**
+ * sunxi_osc_clk_setup() - Setup function for gatable oscillator
+ */
+
+#define SUNXI_OSC24M_GATE	0
+
+static void __init sunxi_osc_clk_setup(struct device_node *node)
+{
+	struct clk *clk;
+	const char *clk_name = node->name;
+	void *reg;
+	u32 rate;
+
+	reg = of_iomap(node, 0);
+
+	if (of_property_read_u32(node, "clock-frequency", &rate))
+		return;
+
+	clk = clk_register_fixed_gate(NULL, clk_name, NULL,
+				      CLK_IS_ROOT | CLK_IGNORE_UNUSED,
+				      reg, SUNXI_OSC24M_GATE, rate, &clk_lock);
+
+	if (clk) {
+		of_clk_add_provider(node, of_clk_src_simple_get, clk);
+		clk_register_clkdev(clk, clk_name, NULL);
+	}
+}
+
+
+
+/**
+ * sunxi_get_pll1_factors() - calculates n, k, m, p factors for PLL1
+ * PLL1 rate is calculated as follows
+ * rate = (parent_rate * n * (k + 1) >> p) / (m + 1);
+ * parent_rate is always 24Mhz
+ */
+
+static void sunxi_get_pll1_factors(u32 *freq, u8 *n, u8 *k, u8 *m, u8 *p)
+{
+	u8 div;
+
+	/* Normalize value to a 6M multiple */
+	div = *freq / 6000000;
+	*freq = 6000000 * div;
+
+	/* we were called to round the frequency, we can now return */
+	if (n == NULL)
+		return;
+
+	/* m is always zero for pll1 */
+	*m = 0;
+
+	/* k is 1 only on these cases */
+	if (*freq >= 768000000 || *freq == 42000000 || *freq == 54000000)
+		*k = 1;
+	else
+		*k = 0;
+
+	/* p will be 3 for divs under 10 */
+	if (div < 10)
+		*p = 3;
+
+	/* p will be 2 for divs between 10 - 20 and odd divs under 32 */
+	else if (div < 20 || (div < 32 && (div & 1)))
+		*p = 2;
+
+	/* p will be 1 for even divs under 32, divs under 40 and odd pairs
+	 * of divs between 40-62 */
+	else if (div < 40 || (div < 64 && (div & 2)))
+		*p = 1;
+
+	/* any other entries have p = 0 */
+	else
+		*p = 0;
+
+	/* calculate a suitable n based on k and p */
+	div <<= *p;
+	div /= (*k + 1);
+	*n = div / 4;
+}
+
+/**
+ * sunxi_pll1_clk_setup() - Setup function for PLL1 clock
+ */
+
+struct clk_factors_config pll1_config = {
+	.nshift = 8,
+	.nwidth = 5,
+	.kshift = 4,
+	.kwidth = 2,
+	.mshift = 0,
+	.mwidth = 2,
+	.pshift = 16,
+	.pwidth = 2,
+};
+
+static void __init sunxi_pll1_clk_setup(struct device_node *node)
+{
+	struct clk *clk;
+	const char *clk_name = node->name;
+	const char *parent;
+	void *reg;
+
+	reg = of_iomap(node, 0);
+
+	parent = of_clk_get_parent_name(node, 0);
+
+	clk = clk_register_factors(NULL, clk_name, parent, CLK_IGNORE_UNUSED,
+				   reg, &pll1_config, sunxi_get_pll1_factors,
+				   &clk_lock);
+
+	if (clk) {
+		of_clk_add_provider(node, of_clk_src_simple_get, clk);
+		clk_register_clkdev(clk, clk_name, NULL);
+	}
+}
+
+
+
+/**
+ * sunxi_cpu_clk_setup() - Setup function for CPU mux
+ */
+
+#define SUNXI_CPU_GATE		16
+#define SUNXI_CPU_GATE_WIDTH	2
+
+static void __init sunxi_cpu_clk_setup(struct device_node *node)
+{
+	struct clk *clk;
+	const char *clk_name = node->name;
+	const char **parents = kmalloc(sizeof(char *) * 5, GFP_KERNEL);
+	void *reg;
+	int i = 0;
+
+	reg = of_iomap(node, 0);
+
+	while (i < 5 && (parents[i] = of_clk_get_parent_name(node, i)) != NULL)
+		i++;
+
+	clk = clk_register_mux(NULL, clk_name, parents, i, 0, reg,
+			       SUNXI_CPU_GATE, SUNXI_CPU_GATE_WIDTH,
+			       0, &clk_lock);
+
+	if (clk) {
+		of_clk_add_provider(node, of_clk_src_simple_get, clk);
+		clk_register_clkdev(clk, clk_name, NULL);
+	}
+}
+
+
+
+/**
+ * sunxi_divider_clk_setup() - Setup function for simple divider clocks
+ */
+
+#define SUNXI_DIVISOR_WIDTH	2
+
+struct div_data {
+	u8 div;
+	u8 pow;
+};
+
+static const __initconst struct div_data axi_data = {
+	.div = 0,
+	.pow = 0,
+};
+
+static const __initconst struct div_data ahb_data = {
+	.div = 4,
+	.pow = 1,
+};
+
+static const __initconst struct div_data apb0_data = {
+	.div = 8,
+	.pow = 1,
+};
+
+static void __init sunxi_divider_clk_setup(struct device_node *node, u8 shift,
+					   u8 power_of_two)
+{
+	struct clk *clk;
+	const char *clk_name = node->name;
+	const char *clk_parent;
+	void *reg;
+
+	reg = of_iomap(node, 0);
+
+	clk_parent = of_clk_get_parent_name(node, 0);
+
+	clk = clk_register_divider(NULL, clk_name, clk_parent, 0,
+				   reg, shift, SUNXI_DIVISOR_WIDTH,
+				   power_of_two ? CLK_DIVIDER_POWER_OF_TWO : 0,
+				   &clk_lock);
+	if (clk) {
+		of_clk_add_provider(node, of_clk_src_simple_get, clk);
+		clk_register_clkdev(clk, clk_name, NULL);
+	}
+}
+
+
+/* Matches for of_clk_init */
+static const __initconst struct of_device_id clk_match[] = {
+	{.compatible = "fixed-clock", .data = of_fixed_clk_setup,},
+	{.compatible = "allwinner,sunxi-osc-clk", .data = sunxi_osc_clk_setup,},
+	{.compatible = "allwinner,sunxi-pll1-clk", .data = sunxi_pll1_clk_setup,},
+	{.compatible = "allwinner,sunxi-cpu-clk", .data = sunxi_cpu_clk_setup,},
+	{}
+};
+
+/* Matches for divider clocks */
+static const __initconst struct of_device_id clk_div_match[] = {
+	{.compatible = "allwinner,sunxi-axi-clk", .data = &axi_data,},
+	{.compatible = "allwinner,sunxi-ahb-clk", .data = &ahb_data,},
+	{.compatible = "allwinner,sunxi-apb0-clk", .data = &apb0_data,},
+	{}
+};
+
+static void __init of_sunxi_divider_clock_setup(void)
+{
+	struct device_node *np;
+	const struct div_data *data;
+	const struct of_device_id *match;
+
+	for_each_matching_node(np, clk_div_match) {
+		match = of_match_node(clk_div_match, np);
+		data = match->data;
+		sunxi_divider_clk_setup(np, data->div, data->pow);
+	}
+}
+
+void __init sunxi_init_clocks(void)
+{
+	/* Register all the simple sunxi clocks on DT */
+	of_clk_init(clk_match);
+
+	/* Register divider clocks */
+	of_sunxi_divider_clock_setup();
+}
-- 
1.8.1.1

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

* [PATCH 2/3] arm: sunxi: Add clock definitions for the new clock driver
  2013-01-22  6:12 [PATCH 0/3] clock driver for sunxi Emilio López
  2013-01-22  6:12 ` [PATCH 1/3] clk: arm: sunxi: Add a new clock driver for sunxi SOCs Emilio López
@ 2013-01-22  6:12 ` Emilio López
  2013-01-30  8:24   ` Maxime Ripard
  2013-01-22  6:12 ` [PATCH 3/3] arm: sunxi: Add useful information about sunxi clocks Emilio López
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 26+ messages in thread
From: Emilio López @ 2013-01-22  6:12 UTC (permalink / raw)
  To: linux-arm-kernel

This introduces proper clock definitions on sunxi.dtsi, to be used
with the new clock driver for sunxi.

Signed-off-by: Emilio L?pez <emilio@elopez.com.ar>
---
 arch/arm/boot/dts/sunxi.dtsi | 55 +++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 49 insertions(+), 6 deletions(-)

diff --git a/arch/arm/boot/dts/sunxi.dtsi b/arch/arm/boot/dts/sunxi.dtsi
index 8bbc2bf..e2cef13 100644
--- a/arch/arm/boot/dts/sunxi.dtsi
+++ b/arch/arm/boot/dts/sunxi.dtsi
@@ -24,13 +24,56 @@
 
 	clocks {
 		#address-cells = <1>;
-		#size-cells = <0>;
+		#size-cells = <1>;
+		ranges;
 
-		osc: oscillator {
+		osc24M: osc24M at 01c20050 {
 			#clock-cells = <0>;
-			compatible = "fixed-clock";
+			compatible = "allwinner,sunxi-osc-clk";
+			reg = <0x01c20050 0x4>;
 			clock-frequency = <24000000>;
 		};
+
+		osc32k: osc32k {
+			#clock-cells = <0>;
+			compatible = "fixed-clock";
+			clock-frequency = <32768>;
+		};
+
+		pll1: pll1 at 01c20000 {
+			#clock-cells = <0>;
+			compatible = "allwinner,sunxi-pll1-clk";
+			reg = <0x01c20000 0x4>;
+			clocks = <&osc24M>;
+		};
+
+		cpu: cpu at 01c20054 {
+			#clock-cells = <0>;
+			compatible = "allwinner,sunxi-cpu-clk";
+			reg = <0x01c20054 0x4>;
+			clocks = <&osc32k>, <&osc24M>, <&pll1>;
+		};
+
+		axi: axi at 01c20054 {
+			#clock-cells = <0>;
+			compatible = "allwinner,sunxi-axi-clk";
+			reg = <0x01c20054 0x4>;
+			clocks = <&cpu>;
+		};
+
+		ahb: ahb at 01c20054 {
+			#clock-cells = <0>;
+			compatible = "allwinner,sunxi-ahb-clk";
+			reg = <0x01c20054 0x4>;
+			clocks = <&axi>;
+		};
+
+		apb0: apb0 at 01c20054 {
+			#clock-cells = <0>;
+			compatible = "allwinner,sunxi-apb0-clk";
+			reg = <0x01c20054 0x4>;
+			clocks = <&ahb>;
+		};
 	};
 
 	soc {
@@ -44,7 +87,7 @@
 			compatible = "allwinner,sunxi-timer";
 			reg = <0x01c20c00 0x90>;
 			interrupts = <22>;
-			clocks = <&osc>;
+			clocks = <&osc24M>;
 		};
 
 		wdt: watchdog at 01c20c90 {
@@ -64,7 +107,7 @@
 			reg = <0x01c28000 0x400>;
 			interrupts = <1>;
 			reg-shift = <2>;
-			clock-frequency = <24000000>;
+			clocks = <&osc24M>;
 			status = "disabled";
 		};
 
@@ -73,7 +116,7 @@
 			reg = <0x01c28400 0x400>;
 			interrupts = <2>;
 			reg-shift = <2>;
-			clock-frequency = <24000000>;
+			clocks = <&osc24M>;
 			status = "disabled";
 		};
 	};
-- 
1.8.1.1

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

* [PATCH 3/3] arm: sunxi: Add useful information about sunxi clocks
  2013-01-22  6:12 [PATCH 0/3] clock driver for sunxi Emilio López
  2013-01-22  6:12 ` [PATCH 1/3] clk: arm: sunxi: Add a new clock driver for sunxi SOCs Emilio López
  2013-01-22  6:12 ` [PATCH 2/3] arm: sunxi: Add clock definitions for the new clock driver Emilio López
@ 2013-01-22  6:12 ` Emilio López
  2013-01-30  8:17 ` [PATCH 0/3] clock driver for sunxi Maxime Ripard
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 26+ messages in thread
From: Emilio López @ 2013-01-22  6:12 UTC (permalink / raw)
  To: linux-arm-kernel

This patch contains useful bits of information about the sunxi clocks
that may help and/or be interesting for current and future developers.

Signed-off-by: Emilio L?pez <emilio@elopez.com.ar>
---
 Documentation/arm/sunxi/clocks.txt | 56 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 56 insertions(+)
 create mode 100644 Documentation/arm/sunxi/clocks.txt

diff --git a/Documentation/arm/sunxi/clocks.txt b/Documentation/arm/sunxi/clocks.txt
new file mode 100644
index 0000000..e09a88a
--- /dev/null
+++ b/Documentation/arm/sunxi/clocks.txt
@@ -0,0 +1,56 @@
+Frequently asked questions about the sunxi clock system
+=======================================================
+
+This document contains useful bits of information that people tend to ask
+about the sunxi clock system, as well as accompanying ASCII art when adequate.
+
+Q: Why is the main 24MHz oscillator gatable? Wouldn't that break the
+   system?
+
+A: The 24MHz oscillator allows gating to save power. Indeed, if gated
+   carelessly the system would stop functioning, but with the right
+   steps, one can gate it and keep the system running. Consider this
+   simplified suspend example:
+
+   While the system is operational, you would see something like
+
+      24MHz         32kHz
+       |
+      PLL1
+       \
+        \_ CPU Mux
+             |
+           [CPU]
+
+   When you are about to suspend, you switch the CPU Mux to the 32kHz
+   oscillator:
+
+      24Mhz         32kHz
+       |              |
+      PLL1            |
+                     /
+           CPU Mux _/
+             |
+           [CPU]
+
+    Finally you can gate the main oscillator
+
+                    32kHz
+                      |
+                      |
+                     /
+           CPU Mux _/
+             |
+           [CPU]
+
+Q: Were can I learn more about the sunxi clocks?
+
+A: The linux-sunxi wiki contains a page documenting the clock registers,
+   you can find it at
+
+        http://linux-sunxi.org/A10/CCM
+
+   The authoritative source for information at this time is the ccmu driver
+   released by Allwinner, you can find it at
+
+        https://github.com/linux-sunxi/linux-sunxi/tree/sunxi-3.0/arch/arm/mach-sun4i/clock/ccmu
-- 
1.8.1.1

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

* [PATCH 0/3] clock driver for sunxi
  2013-01-22  6:12 [PATCH 0/3] clock driver for sunxi Emilio López
                   ` (2 preceding siblings ...)
  2013-01-22  6:12 ` [PATCH 3/3] arm: sunxi: Add useful information about sunxi clocks Emilio López
@ 2013-01-30  8:17 ` Maxime Ripard
  2013-02-04 22:03 ` Maxime Ripard
  2013-02-25 14:44 ` [PATCH v2 " Emilio López
  5 siblings, 0 replies; 26+ messages in thread
From: Maxime Ripard @ 2013-01-30  8:17 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Emilio,

Le 22/01/2013 07:12, Emilio L?pez a ?crit :
> Hello everyone,
> 
> This patchset adds basic clock support for sunxi devices. Currently, it
> implements support for the two oscillators, the main PLL, the CPU mux,
> and its three divisor clocks. With this in place, it is possible to
> write a cpufreq driver and have it work.
> 
> I have tested this driver successfully on a Cubieboard (A10, sun4i)
> using the cpufreq driver from the linux-sunxi tree after minor
> modifications (the clock names are not the same).

Thanks for doing this. I'll try to test it on my boards this week.

I don't have a lot of comments, but Mike will probably have some.

Maxime

-- 
Maxime Ripard, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com

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

* [PATCH 2/3] arm: sunxi: Add clock definitions for the new clock driver
  2013-01-22  6:12 ` [PATCH 2/3] arm: sunxi: Add clock definitions for the new clock driver Emilio López
@ 2013-01-30  8:24   ` Maxime Ripard
  2013-01-30 11:57     ` Emilio López
  0 siblings, 1 reply; 26+ messages in thread
From: Maxime Ripard @ 2013-01-30  8:24 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Emilio,

Le 22/01/2013 07:12, Emilio L?pez a ?crit :

> +		cpu: cpu at 01c20054 {
> +			#clock-cells = <0>;
> +			compatible = "allwinner,sunxi-cpu-clk";
> +			reg = <0x01c20054 0x4>;
> +			clocks = <&osc32k>, <&osc24M>, <&pll1>;
> +		};

Why do you need these three clocks here ? From what you said in patch 3,
it seems like the 24M oscillator is not a direct parent, but only the
pll1 and the 32k oscillator, right?

Thanks,
Maxime

-- 
Maxime Ripard, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com

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

* [PATCH 2/3] arm: sunxi: Add clock definitions for the new clock driver
  2013-01-30  8:24   ` Maxime Ripard
@ 2013-01-30 11:57     ` Emilio López
  0 siblings, 0 replies; 26+ messages in thread
From: Emilio López @ 2013-01-30 11:57 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Maxime,

El 30/01/13 05:24, Maxime Ripard escribi?:
> Hi Emilio,
> 
> Le 22/01/2013 07:12, Emilio L?pez a ?crit :
> 
>> +		cpu: cpu at 01c20054 {
>> +			#clock-cells = <0>;
>> +			compatible = "allwinner,sunxi-cpu-clk";
>> +			reg = <0x01c20054 0x4>;
>> +			clocks = <&osc32k>, <&osc24M>, <&pll1>;
>> +		};
> 
> Why do you need these three clocks here ? From what you said in patch 3,
> it seems like the 24M oscillator is not a direct parent, but only the
> pll1 and the 32k oscillator, right?

There are two reasons:
 * Hardware wise it actually is a parent, as in, it is an usable source
you can choose on the mux. There is also PLL6 as an option, but it has
no representation yet so I have not listed it.

  Quoting the linux-sunxi wiki:

    0x00 = 32 KHz internal RC clock
    0x01 = 24 MHz external Oscillator
    0x02 = PLL1
    0x03 = 200 MHz sourced from PPL6

 * The example in patch 3 is simplified, you may want to switch to the
24M oscillator first. My intention was to just present the concept/idea,
the actual clock handling on standby contains some more steps; see:

https://github.com/linux-sunxi/linux-sunxi/blob/sunxi-3.0/arch/arm/mach-sun4i/pm/standby/standby.c#L179

Thanks for the review,

Emilio

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

* [PATCH 0/3] clock driver for sunxi
  2013-01-22  6:12 [PATCH 0/3] clock driver for sunxi Emilio López
                   ` (3 preceding siblings ...)
  2013-01-30  8:17 ` [PATCH 0/3] clock driver for sunxi Maxime Ripard
@ 2013-02-04 22:03 ` Maxime Ripard
  2013-02-25 14:44 ` [PATCH v2 " Emilio López
  5 siblings, 0 replies; 26+ messages in thread
From: Maxime Ripard @ 2013-02-04 22:03 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Emilio,

Le 22/01/2013 07:12, Emilio L?pez a ?crit :
> This patchset adds basic clock support for sunxi devices. Currently, it
> implements support for the two oscillators, the main PLL, the CPU mux,
> and its three divisor clocks. With this in place, it is possible to
> write a cpufreq driver and have it work.
> 
> I have tested this driver successfully on a Cubieboard (A10, sun4i)
> using the cpufreq driver from the linux-sunxi tree after minor
> modifications (the clock names are not the same).

I just tested this patchset on a A13-Olinuxino, and it looks to be
working fine. I tested without the cpufreq patch though.

Mike, could you give us your feedback on this serie? It would be really
great if we could get this merged by 3.9, if you're ok with it of course.

Thanks,
Maxime
-- 
Maxime Ripard, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com

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

* [PATCH 1/3] clk: arm: sunxi: Add a new clock driver for sunxi SOCs
  2013-01-22  6:12 ` [PATCH 1/3] clk: arm: sunxi: Add a new clock driver for sunxi SOCs Emilio López
@ 2013-02-05 11:18   ` Gregory CLEMENT
  2013-02-08 10:38     ` Emilio López
  2013-02-08 11:33   ` Gregory CLEMENT
  1 sibling, 1 reply; 26+ messages in thread
From: Gregory CLEMENT @ 2013-02-05 11:18 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Emilio,

I don't have this hardware by I have a few remarks regarding the way
you use the CCF, see my comments inline.
Mike, don't hesite to correct me if I am wrong.

On 01/22/2013 07:12 AM, Emilio L?pez wrote:
> This commit implements the base CPU clocks for sunxi devices. It has
> been tested using a slightly modified cpufreq driver from the
> linux-sunxi 3.0 tree.
> 
> Additionally, document the new bindings introduced by this patch, and drop
> the (now unused) old sunxi clock driver.
> 
> Idling:
>     / # find /sys/kernel/debug/clk -name clk_rate -print -exec cat {} \;
>     /sys/kernel/debug/clk/osc24M/pll1/cpu/axi/ahb/apb0/clk_rate
>     30000000
>     /sys/kernel/debug/clk/osc24M/pll1/cpu/axi/ahb/clk_rate
>     60000000
>     /sys/kernel/debug/clk/osc24M/pll1/cpu/axi/clk_rate
>     60000000
>     /sys/kernel/debug/clk/osc24M/pll1/cpu/clk_rate
>     60000000
>     /sys/kernel/debug/clk/osc24M/pll1/clk_rate
>     60000000
>     /sys/kernel/debug/clk/osc24M/clk_rate
>     24000000
>     /sys/kernel/debug/clk/osc32k/clk_rate
>     32768
> 
> After "yes >/dev/null &":
>     / # find /sys/kernel/debug/clk -name clk_rate -print -exec cat {} \;
>     /sys/kernel/debug/clk/osc24M/pll1/cpu/axi/ahb/apb0/clk_rate
>     84000000
>     /sys/kernel/debug/clk/osc24M/pll1/cpu/axi/ahb/clk_rate
>     168000000
>     /sys/kernel/debug/clk/osc24M/pll1/cpu/axi/clk_rate
>     336000000
>     /sys/kernel/debug/clk/osc24M/pll1/cpu/clk_rate
>     1008000000
>     /sys/kernel/debug/clk/osc24M/pll1/clk_rate
>     1008000000
>     /sys/kernel/debug/clk/osc24M/clk_rate
>     24000000
>     /sys/kernel/debug/clk/osc32k/clk_rate
>     32768
> 
> Signed-off-by: Emilio L?pez <emilio@elopez.com.ar>
> ---
>  Documentation/devicetree/bindings/clock/sunxi.txt |  47 ++++
>  drivers/clk/Makefile                              |   2 +-
>  drivers/clk/clk-sunxi.c                           |  30 ---
>  drivers/clk/sunxi/Makefile                        |   5 +
>  drivers/clk/sunxi/clk-factors.c                   | 175 ++++++++++++++
>  drivers/clk/sunxi/clk-factors.h                   |  25 ++
>  drivers/clk/sunxi/clk-fixed-gate.c                | 152 +++++++++++++
>  drivers/clk/sunxi/clk-fixed-gate.h                |  13 ++
>  drivers/clk/sunxi/clk-sunxi.c                     | 265 ++++++++++++++++++++++
>  9 files changed, 683 insertions(+), 31 deletions(-)
>  create mode 100644 Documentation/devicetree/bindings/clock/sunxi.txt
>  delete mode 100644 drivers/clk/clk-sunxi.c
>  create mode 100644 drivers/clk/sunxi/Makefile
>  create mode 100644 drivers/clk/sunxi/clk-factors.c
>  create mode 100644 drivers/clk/sunxi/clk-factors.h
>  create mode 100644 drivers/clk/sunxi/clk-fixed-gate.c
>  create mode 100644 drivers/clk/sunxi/clk-fixed-gate.h
>  create mode 100644 drivers/clk/sunxi/clk-sunxi.c
> 
> diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt
> new file mode 100644
> index 0000000..446c5ca
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/clock/sunxi.txt
> @@ -0,0 +1,47 @@
> +Device Tree Clock bindings for arch-sunxi
> +
> +This binding uses the common clock binding[1].
> +
> +[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
> +
> +Required properties:
> +- compatible : shall be one of the following:
> +	"allwinner,sunxi-osc-clk" - for a gatable oscillator
> +	"allwinner,sunxi-pll1-clk" - for the main PLL clock
> +	"allwinner,sunxi-cpu-clk" - for the CPU multiplexer clock
> +	"allwinner,sunxi-axi-clk" - for the sunxi AXI clock
> +	"allwinner,sunxi-ahb-clk" - for the sunxi AHB clock
> +	"allwinner,sunxi-apb0-clk" - for the sunxi APB0 clock
> +
> +Required properties for all clocks:
> +- reg : shall be the control register address for the clock.
> +- clocks : shall be the input parent clock(s) phandle for the clock, except for
> +	the root gatable oscillator clock where it is not present
> +- #clock-cells : from common clock binding; shall be set to 0.
> +
> +Additionally, the gatable oscillator clock requires:
> +- clock-frequency : shall be the frequency of the oscillator.
> +
> +
> +For example:
> +
> +osc24M: osc24M {
> +	#clock-cells = <0>;
> +	compatible = "allwinner,sunxi-osc-clk";
> +	reg = <0x01c20050 0x4>;
> +	clock-frequency = <24000000>;
> +};
> +
> +pll1: pll1 at 01c20000 {
> +	#clock-cells = <0>;
> +	compatible = "allwinner,sunxi-pll1-clk";
> +	reg = <0x01c20000 0x4>;
> +	clocks = <&osc24M>;
> +};
> +
> +cpu: cpu at 01c20054 {
> +	#clock-cells = <0>;
> +	compatible = "allwinner,sunxi-cpu-clk";
> +	reg = <0x01c20054 0x4>;
> +	clocks = <&osc32k>, <&osc24M>, <&pll1>;
> +};
> diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
> index ee90e87..129afed 100644
> --- a/drivers/clk/Makefile
> +++ b/drivers/clk/Makefile
> @@ -20,7 +20,7 @@ endif
>  obj-$(CONFIG_MACH_LOONGSON1)	+= clk-ls1x.o
>  obj-$(CONFIG_ARCH_U8500)	+= ux500/
>  obj-$(CONFIG_ARCH_VT8500)	+= clk-vt8500.o
> -obj-$(CONFIG_ARCH_SUNXI)	+= clk-sunxi.o
> +obj-$(CONFIG_ARCH_SUNXI)	+= sunxi/
>  obj-$(CONFIG_ARCH_ZYNQ)		+= clk-zynq.o
>  
>  # Chip specific
> diff --git a/drivers/clk/clk-sunxi.c b/drivers/clk/clk-sunxi.c
> deleted file mode 100644
> index 0e831b5..0000000
> --- a/drivers/clk/clk-sunxi.c
> +++ /dev/null
> @@ -1,30 +0,0 @@
> -/*
> - * Copyright 2012 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/clkdev.h>
> -#include <linux/clk/sunxi.h>
> -#include <linux/of.h>
> -
> -static const __initconst struct of_device_id clk_match[] = {
> -	{ .compatible = "fixed-clock", .data = of_fixed_clk_setup, },
> -	{}
> -};
> -
> -void __init sunxi_init_clocks(void)
> -{
> -	of_clk_init(clk_match);
> -}
> diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile
> new file mode 100644
> index 0000000..8e773be
> --- /dev/null
> +++ b/drivers/clk/sunxi/Makefile
> @@ -0,0 +1,5 @@
> +#
> +# Makefile for sunxi specific clk
> +#
> +
> +obj-y += clk-sunxi.o clk-factors.o clk-fixed-gate.o
> diff --git a/drivers/clk/sunxi/clk-factors.c b/drivers/clk/sunxi/clk-factors.c
> new file mode 100644
> index 0000000..428b47d
> --- /dev/null
> +++ b/drivers/clk/sunxi/clk-factors.c
> @@ -0,0 +1,175 @@
> +/*
> + * Copyright (C) 2013 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 version 2 as
> + * published by the Free Software Foundation.
> + *
> + * Adjustable factor-based clock implementation
> + */
> +
> +#include <linux/clk-provider.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/io.h>
> +#include <linux/err.h>
> +#include <linux/string.h>
> +
> +#include <linux/delay.h>
> +
> +#include "clk-factors.h"
> +
> +/*
> + * DOC: basic adjustable factor-based clock that cannot gate
> + *
> + * Traits of this clock:
> + * prepare - clk_prepare only ensures that parents are prepared
> + * enable - clk_enable only ensures that parents are enabled
> + * rate - rate is adjustable.
> + *        clk->rate = (parent->rate * N * (K + 1) >> P) / (M + 1)
> + * parent - fixed parent.  No clk_set_parent support
> + */
> +
> +struct clk_factors {
> +	struct clk_hw hw;
> +	void __iomem *reg;
> +	struct clk_factors_config *config;
> +	void (*get_factors) (u32 *rate, u8 *n, u8 *k, u8 *m, u8 *p);
> +	spinlock_t *lock;
> +};
> +
> +#define to_clk_factors(_hw) container_of(_hw, struct clk_factors, hw)
> +
> +#define SETMASK(len, pos)		(((-1U) >> (31-len))  << (pos))
> +#define CLRMASK(len, pos)		(~(SETMASK(len, pos)))
> +#define FACTOR_GET(bit, len, reg)	(((reg) & SETMASK(len, bit)) >> (bit))
> +
> +#define FACTOR_SET(bit, len, reg, val) \
> +	(((reg) & CLRMASK(len, bit)) | (val << (bit)))
> +
> +static unsigned long clk_factors_recalc_rate(struct clk_hw *hw,
> +					     unsigned long parent_rate)
> +{
> +	u8 n, k, p, m;
> +	u32 reg;
> +	unsigned long rate;
> +	struct clk_factors *factors = to_clk_factors(hw);
> +	struct clk_factors_config *config = factors->config;
> +
> +	/* Fetch the register value */
> +	reg = readl(factors->reg);
> +
> +	/* Get each individual factor */
> +	n = FACTOR_GET(config->nshift, config->nwidth, reg);
> +	k = FACTOR_GET(config->kshift, config->kwidth, reg);
> +	m = FACTOR_GET(config->mshift, config->mwidth, reg);
> +	p = FACTOR_GET(config->pshift, config->pwidth, reg);
> +
> +	/* Calculate the rate */
> +	rate = (parent_rate * n * (k + 1) >> p) / (m + 1);
> +
> +	return rate;
> +}
> +
> +static long clk_factors_round_rate(struct clk_hw *hw, unsigned long rate,
> +				   unsigned long *parent_rate)
> +{
> +	struct clk_factors *factors = to_clk_factors(hw);
> +	factors->get_factors((u32 *)&rate, NULL, NULL, NULL, NULL);
> +
> +	return rate;
> +}
> +
> +static int clk_factors_set_rate(struct clk_hw *hw, unsigned long rate,
> +				unsigned long parent_rate)
> +{
> +	u8 n, k, m, p;
> +	u32 reg;
> +	struct clk_factors *factors = to_clk_factors(hw);
> +	struct clk_factors_config *config = factors->config;
> +	unsigned long flags = 0;
> +
> +	factors->get_factors((u32 *)&rate, &n, &k, &m, &p);
> +
> +	if (factors->lock)
> +		spin_lock_irqsave(factors->lock, flags);
> +
> +	/* Fetch the register value */
> +	reg = readl(factors->reg);
> +
> +	/* Set up the new factors */
> +	reg = FACTOR_SET(config->nshift, config->nwidth, reg, n);
> +	reg = FACTOR_SET(config->kshift, config->kwidth, reg, k);
> +	reg = FACTOR_SET(config->mshift, config->mwidth, reg, m);
> +	reg = FACTOR_SET(config->pshift, config->pwidth, reg, p);
> +
> +	/* Apply them now */
> +	writel(reg, factors->reg);
> +
> +	/* delay 500us so pll stabilizes */
> +	__delay((rate >> 20) * 500 / 2);
> +
> +	if (factors->lock)
> +		spin_unlock_irqrestore(factors->lock, flags);
> +
> +	return 0;
> +}
> +
> +static const struct clk_ops clk_factors_ops = {
> +	.recalc_rate = clk_factors_recalc_rate,
> +	.round_rate = clk_factors_round_rate,
> +	.set_rate = clk_factors_set_rate,
> +};
> +
> +/**
> + * clk_register_factors - register a factors clock with
> + * the clock framework
> + * @dev: device registering this clock
> + * @name: name of this clock
> + * @parent_name: name of clock's parent
> + * @flags: framework-specific flags
> + * @reg: register address to adjust factors
> + * @config: shift and width of factors n, k, m and p
> + * @get_factors: function to calculate the factors for a given frequency
> + * @lock: shared register lock for this clock
> + */
> +struct clk *clk_register_factors(struct device *dev, const char *name,
> +				 const char *parent_name,
> +				 unsigned long flags, void __iomem *reg,
> +				 struct clk_factors_config *config,
> +				 void (*get_factors) (u32 *rate, u8 *n, u8 *k,
> +						      u8 *m, u8 *p),
> +				 spinlock_t *lock)
> +{
> +	struct clk_factors *factors;
> +	struct clk *clk;
> +	struct clk_init_data init;
> +
> +	/* allocate the factors */
> +	factors = kzalloc(sizeof(struct clk_factors), GFP_KERNEL);
> +	if (!factors) {
> +		pr_err("%s: could not allocate factors clk\n", __func__);
> +		return ERR_PTR(-ENOMEM);
> +	}
> +
> +	init.name = name;
> +	init.ops = &clk_factors_ops;
> +	init.flags = flags;
> +	init.parent_names = (parent_name ? &parent_name : NULL);
> +	init.num_parents = (parent_name ? 1 : 0);
> +
> +	/* struct clk_factors assignments */
> +	factors->reg = reg;
> +	factors->config = config;
> +	factors->lock = lock;
> +	factors->hw.init = &init;
> +	factors->get_factors = get_factors;
> +
> +	/* register the clock */
> +	clk = clk_register(dev, &factors->hw);
> +
> +	if (IS_ERR(clk))
> +		kfree(factors);
> +
> +	return clk;
> +}
> diff --git a/drivers/clk/sunxi/clk-factors.h b/drivers/clk/sunxi/clk-factors.h
> new file mode 100644
> index 0000000..a24c889
> --- /dev/null
> +++ b/drivers/clk/sunxi/clk-factors.h
> @@ -0,0 +1,25 @@
> +#ifndef __MACH_SUNXI_CLK_FACTORS_H
> +#define __MACH_SUNXI_CLK_FACTORS_H
> +
> +#include <linux/clk-provider.h>
> +#include <linux/clkdev.h>
> +
> +struct clk_factors_config {
> +	u8 nshift;
> +	u8 nwidth;
> +	u8 kshift;
> +	u8 kwidth;
> +	u8 mshift;
> +	u8 mwidth;
> +	u8 pshift;
> +	u8 pwidth;
> +};
> +
> +struct clk *clk_register_factors(struct device *dev, const char *name,
> +				 const char *parent_name,
> +				 unsigned long flags, void __iomem *reg,
> +				 struct clk_factors_config *config,
> +				 void (*get_factors) (u32 *rate, u8 *n, u8 *k,
> +						      u8 *m, u8 *p),
> +				 spinlock_t *lock);
> +#endif
> diff --git a/drivers/clk/sunxi/clk-fixed-gate.c b/drivers/clk/sunxi/clk-fixed-gate.c
> new file mode 100644
> index 0000000..b16eda5
> --- /dev/null
> +++ b/drivers/clk/sunxi/clk-fixed-gate.c
> @@ -0,0 +1,152 @@
> +/*
> + * Copyright (C) 2013 Emilio L?pez <emilio@elopez.com.ar>
> + *
> + * Based on drivers/clk/clk-gate.c,
> + *
> + * Copyright (C) 2010-2011 Canonical Ltd <jeremy.kerr@canonical.com>
> + * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org>
> + *
> + * 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.
> + *
> + * Fixed rate, gated clock implementation
> + */
> +
> +#include <linux/clk-provider.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/io.h>
> +#include <linux/err.h>
> +#include <linux/string.h>
> +
> +#include "clk-fixed-gate.h"
> +
> +/**
> + * DOC: fixed rate clock which can gate and ungate it's ouput
> + *
> + * Traits of this clock:
> + * prepare - clk_(un)prepare only ensures parent is (un)prepared
> + * enable - clk_enable and clk_disable are functional & control gating
> + * rate - rate is always a fixed value.  No clk_set_rate support
> + * parent - fixed parent.  No clk_set_parent support
> + */
> +

This file seems to be a copy and past of clk-fixed-rate.c and
clk-gate.c (as explained in your header). The purpose of the common
clock framework was to reduce code duplication not increase it! :)

The current way to achieve what you want is to declare two clock:
one fixed-rate and one gateable.

In this case your fixed clock will only be use by the gateable
one. Latter thecomposite clk may be a solution, if you really want to
only deal with one clock.

You could remove this file and its header, do a little change in the
dtsi with something like this:

osc24M_fixed: osc24M_fixed {
	#clock-cells = <0>;
	compatible = "fixed-clock";
	clock-frequency = <24000000>;
};

osc24M osc24M {
	#clock-cells = <0>;
	compatible = "allwinner,sunxi-osc-clk"
	reg = <0x01c20050 0x4>;
	clocks = <&osc24M_fixed>;
};

And modify sunxi_osc_clk_setup() as below


[...]

> +
> +static void __init sunxi_osc_clk_setup(struct device_node *node)
> +{
> +	struct clk *clk;
> +	const char *clk_name = node->name;
> +	void *reg;
> +	u32 rate;
> +
> +	reg = of_iomap(node, 0);

here you retrieve the name of the parent clock:

	clk = of_clk_get(np, 0);
	if (!IS_ERR(clk)) {
		parent_name = __clk_get_name(clk);
		clk_put(clk);
	}

We don't anymore to get the clock-frequency
> +
> +	if (of_property_read_u32(node, "clock-frequency", &rate))
> +		return;
> +

And here instead of clk_register_fixed_gate you can use the
clk_register_gate as below:
> +	clk = clk_register_fixed_gate(NULL, clk_name, NULL,
> +				      CLK_IS_ROOT | CLK_IGNORE_UNUSED,
> +				      reg, SUNXI_OSC24M_GATE, rate, &clk_lock);
> +
	clk = clk_register_gate(NULL, clk_name, parent_name,
			CLK_IGNORE_UNUSED, reg, SUNXI_OSC24M_GATE, 0, &clk->lock);

> +	if (clk) {
> +		of_clk_add_provider(node, of_clk_src_simple_get, clk);
> +		clk_register_clkdev(clk, clk_name, NULL);
> +	}
> +}
> +
> +
> +
> +/**
> + * sunxi_get_pll1_factors() - calculates n, k, m, p factors for PLL1
> + * PLL1 rate is calculated as follows
> + * rate = (parent_rate * n * (k + 1) >> p) / (m + 1);
> + * parent_rate is always 24Mhz
> + */
> +
> +static void sunxi_get_pll1_factors(u32 *freq, u8 *n, u8 *k, u8 *m, u8 *p)
> +{
> +	u8 div;
> +
> +	/* Normalize value to a 6M multiple */
> +	div = *freq / 6000000;
> +	*freq = 6000000 * div;
> +
> +	/* we were called to round the frequency, we can now return */
> +	if (n == NULL)
> +		return;
> +
> +	/* m is always zero for pll1 */
> +	*m = 0;
> +
> +	/* k is 1 only on these cases */
> +	if (*freq >= 768000000 || *freq == 42000000 || *freq == 54000000)
> +		*k = 1;
> +	else
> +		*k = 0;
> +
> +	/* p will be 3 for divs under 10 */
> +	if (div < 10)
> +		*p = 3;
> +
> +	/* p will be 2 for divs between 10 - 20 and odd divs under 32 */
> +	else if (div < 20 || (div < 32 && (div & 1)))
> +		*p = 2;
> +
> +	/* p will be 1 for even divs under 32, divs under 40 and odd pairs
> +	 * of divs between 40-62 */
> +	else if (div < 40 || (div < 64 && (div & 2)))
> +		*p = 1;
> +
> +	/* any other entries have p = 0 */
> +	else
> +		*p = 0;
> +
> +	/* calculate a suitable n based on k and p */
> +	div <<= *p;
> +	div /= (*k + 1);
> +	*n = div / 4;
> +}
> +
> +/**
> + * sunxi_pll1_clk_setup() - Setup function for PLL1 clock
> + */
> +
> +struct clk_factors_config pll1_config = {
> +	.nshift = 8,
> +	.nwidth = 5,
> +	.kshift = 4,
> +	.kwidth = 2,
> +	.mshift = 0,
> +	.mwidth = 2,
> +	.pshift = 16,
> +	.pwidth = 2,
> +};
> +
> +static void __init sunxi_pll1_clk_setup(struct device_node *node)
> +{
> +	struct clk *clk;
> +	const char *clk_name = node->name;
> +	const char *parent;
> +	void *reg;
> +
> +	reg = of_iomap(node, 0);
> +
> +	parent = of_clk_get_parent_name(node, 0);
> +
> +	clk = clk_register_factors(NULL, clk_name, parent, CLK_IGNORE_UNUSED,
> +				   reg, &pll1_config, sunxi_get_pll1_factors,
> +				   &clk_lock);
> +
> +	if (clk) {
> +		of_clk_add_provider(node, of_clk_src_simple_get, clk);
> +		clk_register_clkdev(clk, clk_name, NULL);
> +	}
> +}
> +
> +
> +
> +/**
> + * sunxi_cpu_clk_setup() - Setup function for CPU mux
> + */
> +
> +#define SUNXI_CPU_GATE		16
> +#define SUNXI_CPU_GATE_WIDTH	2
> +
> +static void __init sunxi_cpu_clk_setup(struct device_node *node)
> +{
> +	struct clk *clk;
> +	const char *clk_name = node->name;
> +	const char **parents = kmalloc(sizeof(char *) * 5, GFP_KERNEL);
> +	void *reg;
> +	int i = 0;
> +
> +	reg = of_iomap(node, 0);
> +
> +	while (i < 5 && (parents[i] = of_clk_get_parent_name(node, i)) != NULL)
> +		i++;
> +
> +	clk = clk_register_mux(NULL, clk_name, parents, i, 0, reg,
> +			       SUNXI_CPU_GATE, SUNXI_CPU_GATE_WIDTH,
> +			       0, &clk_lock);
> +
> +	if (clk) {
> +		of_clk_add_provider(node, of_clk_src_simple_get, clk);
> +		clk_register_clkdev(clk, clk_name, NULL);
> +	}
> +}
> +
> +
> +
> +/**
> + * sunxi_divider_clk_setup() - Setup function for simple divider clocks
> + */
> +
> +#define SUNXI_DIVISOR_WIDTH	2
> +
> +struct div_data {
> +	u8 div;
> +	u8 pow;
> +};
> +
> +static const __initconst struct div_data axi_data = {
> +	.div = 0,
> +	.pow = 0,
> +};
> +
> +static const __initconst struct div_data ahb_data = {
> +	.div = 4,
> +	.pow = 1,
> +};
> +
> +static const __initconst struct div_data apb0_data = {
> +	.div = 8,
> +	.pow = 1,
> +};
> +
> +static void __init sunxi_divider_clk_setup(struct device_node *node, u8 shift,
> +					   u8 power_of_two)
> +{
> +	struct clk *clk;
> +	const char *clk_name = node->name;
> +	const char *clk_parent;
> +	void *reg;
> +
> +	reg = of_iomap(node, 0);
> +
> +	clk_parent = of_clk_get_parent_name(node, 0);
> +
> +	clk = clk_register_divider(NULL, clk_name, clk_parent, 0,
> +				   reg, shift, SUNXI_DIVISOR_WIDTH,
> +				   power_of_two ? CLK_DIVIDER_POWER_OF_TWO : 0,
> +				   &clk_lock);
> +	if (clk) {
> +		of_clk_add_provider(node, of_clk_src_simple_get, clk);
> +		clk_register_clkdev(clk, clk_name, NULL);
> +	}
> +}
> +
> +
> +/* Matches for of_clk_init */
> +static const __initconst struct of_device_id clk_match[] = {
> +	{.compatible = "fixed-clock", .data = of_fixed_clk_setup,},
> +	{.compatible = "allwinner,sunxi-osc-clk", .data = sunxi_osc_clk_setup,},
> +	{.compatible = "allwinner,sunxi-pll1-clk", .data = sunxi_pll1_clk_setup,},
> +	{.compatible = "allwinner,sunxi-cpu-clk", .data = sunxi_cpu_clk_setup,},
> +	{}
> +};
> +
> +/* Matches for divider clocks */
> +static const __initconst struct of_device_id clk_div_match[] = {
> +	{.compatible = "allwinner,sunxi-axi-clk", .data = &axi_data,},
> +	{.compatible = "allwinner,sunxi-ahb-clk", .data = &ahb_data,},
> +	{.compatible = "allwinner,sunxi-apb0-clk", .data = &apb0_data,},
> +	{}
> +};
> +
> +static void __init of_sunxi_divider_clock_setup(void)
> +{
> +	struct device_node *np;
> +	const struct div_data *data;
> +	const struct of_device_id *match;
> +
> +	for_each_matching_node(np, clk_div_match) {
> +		match = of_match_node(clk_div_match, np);
> +		data = match->data;
> +		sunxi_divider_clk_setup(np, data->div, data->pow);
> +	}
> +}
> +
> +void __init sunxi_init_clocks(void)
> +{
> +	/* Register all the simple sunxi clocks on DT */
> +	of_clk_init(clk_match);
> +
> +	/* Register divider clocks */
> +	of_sunxi_divider_clock_setup();
> +}
> 

Regards,
-- 
Gregory Clement, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com

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

* [PATCH 1/3] clk: arm: sunxi: Add a new clock driver for sunxi SOCs
  2013-02-05 11:18   ` Gregory CLEMENT
@ 2013-02-08 10:38     ` Emilio López
  2013-02-08 11:43       ` Gregory CLEMENT
  0 siblings, 1 reply; 26+ messages in thread
From: Emilio López @ 2013-02-08 10:38 UTC (permalink / raw)
  To: linux-arm-kernel

Hello Gregory,

El 05/02/13 08:18, Gregory CLEMENT escribi?:
> Hi Emilio,
> 
> I don't have this hardware by I have a few remarks regarding the way
> you use the CCF, see my comments inline.
> Mike, don't hesite to correct me if I am wrong.
> 
> On 01/22/2013 07:12 AM, Emilio L?pez wrote:
>> This commit implements the base CPU clocks for sunxi devices. It has
>> been tested using a slightly modified cpufreq driver from the
>> linux-sunxi 3.0 tree.
>>
>> Additionally, document the new bindings introduced by this patch, and drop
>> the (now unused) old sunxi clock driver.
>>
>> Idling:
>>     / # find /sys/kernel/debug/clk -name clk_rate -print -exec cat {} \;
>>     /sys/kernel/debug/clk/osc24M/pll1/cpu/axi/ahb/apb0/clk_rate
>>     30000000
>>     /sys/kernel/debug/clk/osc24M/pll1/cpu/axi/ahb/clk_rate
>>     60000000
>>     /sys/kernel/debug/clk/osc24M/pll1/cpu/axi/clk_rate
>>     60000000
>>     /sys/kernel/debug/clk/osc24M/pll1/cpu/clk_rate
>>     60000000
>>     /sys/kernel/debug/clk/osc24M/pll1/clk_rate
>>     60000000
>>     /sys/kernel/debug/clk/osc24M/clk_rate
>>     24000000
>>     /sys/kernel/debug/clk/osc32k/clk_rate
>>     32768
>>
>> After "yes >/dev/null &":
>>     / # find /sys/kernel/debug/clk -name clk_rate -print -exec cat {} \;
>>     /sys/kernel/debug/clk/osc24M/pll1/cpu/axi/ahb/apb0/clk_rate
>>     84000000
>>     /sys/kernel/debug/clk/osc24M/pll1/cpu/axi/ahb/clk_rate
>>     168000000
>>     /sys/kernel/debug/clk/osc24M/pll1/cpu/axi/clk_rate
>>     336000000
>>     /sys/kernel/debug/clk/osc24M/pll1/cpu/clk_rate
>>     1008000000
>>     /sys/kernel/debug/clk/osc24M/pll1/clk_rate
>>     1008000000
>>     /sys/kernel/debug/clk/osc24M/clk_rate
>>     24000000
>>     /sys/kernel/debug/clk/osc32k/clk_rate
>>     32768
>>
>> Signed-off-by: Emilio L?pez <emilio@elopez.com.ar>
>> ---
>>  Documentation/devicetree/bindings/clock/sunxi.txt |  47 ++++
>>  drivers/clk/Makefile                              |   2 +-
>>  drivers/clk/clk-sunxi.c                           |  30 ---
>>  drivers/clk/sunxi/Makefile                        |   5 +
>>  drivers/clk/sunxi/clk-factors.c                   | 175 ++++++++++++++
>>  drivers/clk/sunxi/clk-factors.h                   |  25 ++
>>  drivers/clk/sunxi/clk-fixed-gate.c                | 152 +++++++++++++
>>  drivers/clk/sunxi/clk-fixed-gate.h                |  13 ++
>>  drivers/clk/sunxi/clk-sunxi.c                     | 265 ++++++++++++++++++++++
>>  9 files changed, 683 insertions(+), 31 deletions(-)
>>  create mode 100644 Documentation/devicetree/bindings/clock/sunxi.txt
>>  delete mode 100644 drivers/clk/clk-sunxi.c
>>  create mode 100644 drivers/clk/sunxi/Makefile
>>  create mode 100644 drivers/clk/sunxi/clk-factors.c
>>  create mode 100644 drivers/clk/sunxi/clk-factors.h
>>  create mode 100644 drivers/clk/sunxi/clk-fixed-gate.c
>>  create mode 100644 drivers/clk/sunxi/clk-fixed-gate.h
>>  create mode 100644 drivers/clk/sunxi/clk-sunxi.c
>>
>> diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt
>> new file mode 100644
>> index 0000000..446c5ca
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/clock/sunxi.txt
>> @@ -0,0 +1,47 @@
>> +Device Tree Clock bindings for arch-sunxi
>> +
>> +This binding uses the common clock binding[1].
>> +
>> +[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
>> +
>> +Required properties:
>> +- compatible : shall be one of the following:
>> +	"allwinner,sunxi-osc-clk" - for a gatable oscillator
>> +	"allwinner,sunxi-pll1-clk" - for the main PLL clock
>> +	"allwinner,sunxi-cpu-clk" - for the CPU multiplexer clock
>> +	"allwinner,sunxi-axi-clk" - for the sunxi AXI clock
>> +	"allwinner,sunxi-ahb-clk" - for the sunxi AHB clock
>> +	"allwinner,sunxi-apb0-clk" - for the sunxi APB0 clock
>> +
>> +Required properties for all clocks:
>> +- reg : shall be the control register address for the clock.
>> +- clocks : shall be the input parent clock(s) phandle for the clock, except for
>> +	the root gatable oscillator clock where it is not present
>> +- #clock-cells : from common clock binding; shall be set to 0.
>> +
>> +Additionally, the gatable oscillator clock requires:
>> +- clock-frequency : shall be the frequency of the oscillator.
>> +
>> +
>> +For example:
>> +
>> +osc24M: osc24M {
>> +	#clock-cells = <0>;
>> +	compatible = "allwinner,sunxi-osc-clk";
>> +	reg = <0x01c20050 0x4>;
>> +	clock-frequency = <24000000>;
>> +};
>> +
>> +pll1: pll1 at 01c20000 {
>> +	#clock-cells = <0>;
>> +	compatible = "allwinner,sunxi-pll1-clk";
>> +	reg = <0x01c20000 0x4>;
>> +	clocks = <&osc24M>;
>> +};
>> +
>> +cpu: cpu at 01c20054 {
>> +	#clock-cells = <0>;
>> +	compatible = "allwinner,sunxi-cpu-clk";
>> +	reg = <0x01c20054 0x4>;
>> +	clocks = <&osc32k>, <&osc24M>, <&pll1>;
>> +};
>> diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
>> index ee90e87..129afed 100644
>> --- a/drivers/clk/Makefile
>> +++ b/drivers/clk/Makefile
>> @@ -20,7 +20,7 @@ endif
>>  obj-$(CONFIG_MACH_LOONGSON1)	+= clk-ls1x.o
>>  obj-$(CONFIG_ARCH_U8500)	+= ux500/
>>  obj-$(CONFIG_ARCH_VT8500)	+= clk-vt8500.o
>> -obj-$(CONFIG_ARCH_SUNXI)	+= clk-sunxi.o
>> +obj-$(CONFIG_ARCH_SUNXI)	+= sunxi/
>>  obj-$(CONFIG_ARCH_ZYNQ)		+= clk-zynq.o
>>  
>>  # Chip specific
>> diff --git a/drivers/clk/clk-sunxi.c b/drivers/clk/clk-sunxi.c
>> deleted file mode 100644
>> index 0e831b5..0000000
>> --- a/drivers/clk/clk-sunxi.c
>> +++ /dev/null
>> @@ -1,30 +0,0 @@
>> -/*
>> - * Copyright 2012 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/clkdev.h>
>> -#include <linux/clk/sunxi.h>
>> -#include <linux/of.h>
>> -
>> -static const __initconst struct of_device_id clk_match[] = {
>> -	{ .compatible = "fixed-clock", .data = of_fixed_clk_setup, },
>> -	{}
>> -};
>> -
>> -void __init sunxi_init_clocks(void)
>> -{
>> -	of_clk_init(clk_match);
>> -}
>> diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile
>> new file mode 100644
>> index 0000000..8e773be
>> --- /dev/null
>> +++ b/drivers/clk/sunxi/Makefile
>> @@ -0,0 +1,5 @@
>> +#
>> +# Makefile for sunxi specific clk
>> +#
>> +
>> +obj-y += clk-sunxi.o clk-factors.o clk-fixed-gate.o
>> diff --git a/drivers/clk/sunxi/clk-factors.c b/drivers/clk/sunxi/clk-factors.c
>> new file mode 100644
>> index 0000000..428b47d
>> --- /dev/null
>> +++ b/drivers/clk/sunxi/clk-factors.c
>> @@ -0,0 +1,175 @@
>> +/*
>> + * Copyright (C) 2013 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 version 2 as
>> + * published by the Free Software Foundation.
>> + *
>> + * Adjustable factor-based clock implementation
>> + */
>> +
>> +#include <linux/clk-provider.h>
>> +#include <linux/module.h>
>> +#include <linux/slab.h>
>> +#include <linux/io.h>
>> +#include <linux/err.h>
>> +#include <linux/string.h>
>> +
>> +#include <linux/delay.h>
>> +
>> +#include "clk-factors.h"
>> +
>> +/*
>> + * DOC: basic adjustable factor-based clock that cannot gate
>> + *
>> + * Traits of this clock:
>> + * prepare - clk_prepare only ensures that parents are prepared
>> + * enable - clk_enable only ensures that parents are enabled
>> + * rate - rate is adjustable.
>> + *        clk->rate = (parent->rate * N * (K + 1) >> P) / (M + 1)
>> + * parent - fixed parent.  No clk_set_parent support
>> + */
>> +
>> +struct clk_factors {
>> +	struct clk_hw hw;
>> +	void __iomem *reg;
>> +	struct clk_factors_config *config;
>> +	void (*get_factors) (u32 *rate, u8 *n, u8 *k, u8 *m, u8 *p);
>> +	spinlock_t *lock;
>> +};
>> +
>> +#define to_clk_factors(_hw) container_of(_hw, struct clk_factors, hw)
>> +
>> +#define SETMASK(len, pos)		(((-1U) >> (31-len))  << (pos))
>> +#define CLRMASK(len, pos)		(~(SETMASK(len, pos)))
>> +#define FACTOR_GET(bit, len, reg)	(((reg) & SETMASK(len, bit)) >> (bit))
>> +
>> +#define FACTOR_SET(bit, len, reg, val) \
>> +	(((reg) & CLRMASK(len, bit)) | (val << (bit)))
>> +
>> +static unsigned long clk_factors_recalc_rate(struct clk_hw *hw,
>> +					     unsigned long parent_rate)
>> +{
>> +	u8 n, k, p, m;
>> +	u32 reg;
>> +	unsigned long rate;
>> +	struct clk_factors *factors = to_clk_factors(hw);
>> +	struct clk_factors_config *config = factors->config;
>> +
>> +	/* Fetch the register value */
>> +	reg = readl(factors->reg);
>> +
>> +	/* Get each individual factor */
>> +	n = FACTOR_GET(config->nshift, config->nwidth, reg);
>> +	k = FACTOR_GET(config->kshift, config->kwidth, reg);
>> +	m = FACTOR_GET(config->mshift, config->mwidth, reg);
>> +	p = FACTOR_GET(config->pshift, config->pwidth, reg);
>> +
>> +	/* Calculate the rate */
>> +	rate = (parent_rate * n * (k + 1) >> p) / (m + 1);
>> +
>> +	return rate;
>> +}
>> +
>> +static long clk_factors_round_rate(struct clk_hw *hw, unsigned long rate,
>> +				   unsigned long *parent_rate)
>> +{
>> +	struct clk_factors *factors = to_clk_factors(hw);
>> +	factors->get_factors((u32 *)&rate, NULL, NULL, NULL, NULL);
>> +
>> +	return rate;
>> +}
>> +
>> +static int clk_factors_set_rate(struct clk_hw *hw, unsigned long rate,
>> +				unsigned long parent_rate)
>> +{
>> +	u8 n, k, m, p;
>> +	u32 reg;
>> +	struct clk_factors *factors = to_clk_factors(hw);
>> +	struct clk_factors_config *config = factors->config;
>> +	unsigned long flags = 0;
>> +
>> +	factors->get_factors((u32 *)&rate, &n, &k, &m, &p);
>> +
>> +	if (factors->lock)
>> +		spin_lock_irqsave(factors->lock, flags);
>> +
>> +	/* Fetch the register value */
>> +	reg = readl(factors->reg);
>> +
>> +	/* Set up the new factors */
>> +	reg = FACTOR_SET(config->nshift, config->nwidth, reg, n);
>> +	reg = FACTOR_SET(config->kshift, config->kwidth, reg, k);
>> +	reg = FACTOR_SET(config->mshift, config->mwidth, reg, m);
>> +	reg = FACTOR_SET(config->pshift, config->pwidth, reg, p);
>> +
>> +	/* Apply them now */
>> +	writel(reg, factors->reg);
>> +
>> +	/* delay 500us so pll stabilizes */
>> +	__delay((rate >> 20) * 500 / 2);
>> +
>> +	if (factors->lock)
>> +		spin_unlock_irqrestore(factors->lock, flags);
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct clk_ops clk_factors_ops = {
>> +	.recalc_rate = clk_factors_recalc_rate,
>> +	.round_rate = clk_factors_round_rate,
>> +	.set_rate = clk_factors_set_rate,
>> +};
>> +
>> +/**
>> + * clk_register_factors - register a factors clock with
>> + * the clock framework
>> + * @dev: device registering this clock
>> + * @name: name of this clock
>> + * @parent_name: name of clock's parent
>> + * @flags: framework-specific flags
>> + * @reg: register address to adjust factors
>> + * @config: shift and width of factors n, k, m and p
>> + * @get_factors: function to calculate the factors for a given frequency
>> + * @lock: shared register lock for this clock
>> + */
>> +struct clk *clk_register_factors(struct device *dev, const char *name,
>> +				 const char *parent_name,
>> +				 unsigned long flags, void __iomem *reg,
>> +				 struct clk_factors_config *config,
>> +				 void (*get_factors) (u32 *rate, u8 *n, u8 *k,
>> +						      u8 *m, u8 *p),
>> +				 spinlock_t *lock)
>> +{
>> +	struct clk_factors *factors;
>> +	struct clk *clk;
>> +	struct clk_init_data init;
>> +
>> +	/* allocate the factors */
>> +	factors = kzalloc(sizeof(struct clk_factors), GFP_KERNEL);
>> +	if (!factors) {
>> +		pr_err("%s: could not allocate factors clk\n", __func__);
>> +		return ERR_PTR(-ENOMEM);
>> +	}
>> +
>> +	init.name = name;
>> +	init.ops = &clk_factors_ops;
>> +	init.flags = flags;
>> +	init.parent_names = (parent_name ? &parent_name : NULL);
>> +	init.num_parents = (parent_name ? 1 : 0);
>> +
>> +	/* struct clk_factors assignments */
>> +	factors->reg = reg;
>> +	factors->config = config;
>> +	factors->lock = lock;
>> +	factors->hw.init = &init;
>> +	factors->get_factors = get_factors;
>> +
>> +	/* register the clock */
>> +	clk = clk_register(dev, &factors->hw);
>> +
>> +	if (IS_ERR(clk))
>> +		kfree(factors);
>> +
>> +	return clk;
>> +}
>> diff --git a/drivers/clk/sunxi/clk-factors.h b/drivers/clk/sunxi/clk-factors.h
>> new file mode 100644
>> index 0000000..a24c889
>> --- /dev/null
>> +++ b/drivers/clk/sunxi/clk-factors.h
>> @@ -0,0 +1,25 @@
>> +#ifndef __MACH_SUNXI_CLK_FACTORS_H
>> +#define __MACH_SUNXI_CLK_FACTORS_H
>> +
>> +#include <linux/clk-provider.h>
>> +#include <linux/clkdev.h>
>> +
>> +struct clk_factors_config {
>> +	u8 nshift;
>> +	u8 nwidth;
>> +	u8 kshift;
>> +	u8 kwidth;
>> +	u8 mshift;
>> +	u8 mwidth;
>> +	u8 pshift;
>> +	u8 pwidth;
>> +};
>> +
>> +struct clk *clk_register_factors(struct device *dev, const char *name,
>> +				 const char *parent_name,
>> +				 unsigned long flags, void __iomem *reg,
>> +				 struct clk_factors_config *config,
>> +				 void (*get_factors) (u32 *rate, u8 *n, u8 *k,
>> +						      u8 *m, u8 *p),
>> +				 spinlock_t *lock);
>> +#endif
>> diff --git a/drivers/clk/sunxi/clk-fixed-gate.c b/drivers/clk/sunxi/clk-fixed-gate.c
>> new file mode 100644
>> index 0000000..b16eda5
>> --- /dev/null
>> +++ b/drivers/clk/sunxi/clk-fixed-gate.c
>> @@ -0,0 +1,152 @@
>> +/*
>> + * Copyright (C) 2013 Emilio L?pez <emilio@elopez.com.ar>
>> + *
>> + * Based on drivers/clk/clk-gate.c,
>> + *
>> + * Copyright (C) 2010-2011 Canonical Ltd <jeremy.kerr@canonical.com>
>> + * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org>
>> + *
>> + * 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.
>> + *
>> + * Fixed rate, gated clock implementation
>> + */
>> +
>> +#include <linux/clk-provider.h>
>> +#include <linux/module.h>
>> +#include <linux/slab.h>
>> +#include <linux/io.h>
>> +#include <linux/err.h>
>> +#include <linux/string.h>
>> +
>> +#include "clk-fixed-gate.h"
>> +
>> +/**
>> + * DOC: fixed rate clock which can gate and ungate it's ouput
>> + *
>> + * Traits of this clock:
>> + * prepare - clk_(un)prepare only ensures parent is (un)prepared
>> + * enable - clk_enable and clk_disable are functional & control gating
>> + * rate - rate is always a fixed value.  No clk_set_rate support
>> + * parent - fixed parent.  No clk_set_parent support
>> + */
>> +
> 
> This file seems to be a copy and past of clk-fixed-rate.c and
> clk-gate.c (as explained in your header). The purpose of the common
> clock framework was to reduce code duplication not increase it! :)

I agree, I am not much of a fan of code duplication :)

> The current way to achieve what you want is to declare two clock:
> one fixed-rate and one gateable.

I don't like this solution much however, because the software
representation does not accurately match (1:1) the hardware then.

> In this case your fixed clock will only be use by the gateable
> one. Latter thecomposite clk may be a solution, if you really want to
> only deal with one clock.

The composite clock lacks fixed rate support from what I saw on the patches.

So, right now I can think of three possibilities:
1) Use two separate clocks as Gregory suggests, and ignore the
non-accurate representation.
2) Extend composite to support fixed rate and use that.
3) Extend gate to support fixed rate and use that.

I think going with 1) will be best for now, and we can then decide and
work on getting it improved with 2) or 3)

> You could remove this file and its header, do a little change in the
> dtsi with something like this:
> 
> osc24M_fixed: osc24M_fixed {
> 	#clock-cells = <0>;
> 	compatible = "fixed-clock";
> 	clock-frequency = <24000000>;
> };
> 
> osc24M osc24M {
> 	#clock-cells = <0>;
> 	compatible = "allwinner,sunxi-osc-clk"
> 	reg = <0x01c20050 0x4>;
> 	clocks = <&osc24M_fixed>;
> };
> 
> And modify sunxi_osc_clk_setup() as below
> 
> 
> [...]
> 
>> +
>> +static void __init sunxi_osc_clk_setup(struct device_node *node)
>> +{
>> +	struct clk *clk;
>> +	const char *clk_name = node->name;
>> +	void *reg;
>> +	u32 rate;
>> +
>> +	reg = of_iomap(node, 0);
> 
> here you retrieve the name of the parent clock:
> 
> 	clk = of_clk_get(np, 0);
> 	if (!IS_ERR(clk)) {
> 		parent_name = __clk_get_name(clk);
> 		clk_put(clk);
> 	}
> 
> We don't anymore to get the clock-frequency
>> +
>> +	if (of_property_read_u32(node, "clock-frequency", &rate))
>> +		return;
>> +
> 
> And here instead of clk_register_fixed_gate you can use the
> clk_register_gate as below:
>> +	clk = clk_register_fixed_gate(NULL, clk_name, NULL,
>> +				      CLK_IS_ROOT | CLK_IGNORE_UNUSED,
>> +				      reg, SUNXI_OSC24M_GATE, rate, &clk_lock);
>> +
> 	clk = clk_register_gate(NULL, clk_name, parent_name,
> 			CLK_IGNORE_UNUSED, reg, SUNXI_OSC24M_GATE, 0, &clk->lock);
> 
>> +	if (clk) {
>> +		of_clk_add_provider(node, of_clk_src_simple_get, clk);
>> +		clk_register_clkdev(clk, clk_name, NULL);
>> +	}
>> +}
>> +
>> +
>> +
>> +/**
>> + * sunxi_get_pll1_factors() - calculates n, k, m, p factors for PLL1
>> + * PLL1 rate is calculated as follows
>> + * rate = (parent_rate * n * (k + 1) >> p) / (m + 1);
>> + * parent_rate is always 24Mhz
>> + */
>> +
>> +static void sunxi_get_pll1_factors(u32 *freq, u8 *n, u8 *k, u8 *m, u8 *p)
>> +{
>> +	u8 div;
>> +
>> +	/* Normalize value to a 6M multiple */
>> +	div = *freq / 6000000;
>> +	*freq = 6000000 * div;
>> +
>> +	/* we were called to round the frequency, we can now return */
>> +	if (n == NULL)
>> +		return;
>> +
>> +	/* m is always zero for pll1 */
>> +	*m = 0;
>> +
>> +	/* k is 1 only on these cases */
>> +	if (*freq >= 768000000 || *freq == 42000000 || *freq == 54000000)
>> +		*k = 1;
>> +	else
>> +		*k = 0;
>> +
>> +	/* p will be 3 for divs under 10 */
>> +	if (div < 10)
>> +		*p = 3;
>> +
>> +	/* p will be 2 for divs between 10 - 20 and odd divs under 32 */
>> +	else if (div < 20 || (div < 32 && (div & 1)))
>> +		*p = 2;
>> +
>> +	/* p will be 1 for even divs under 32, divs under 40 and odd pairs
>> +	 * of divs between 40-62 */
>> +	else if (div < 40 || (div < 64 && (div & 2)))
>> +		*p = 1;
>> +
>> +	/* any other entries have p = 0 */
>> +	else
>> +		*p = 0;
>> +
>> +	/* calculate a suitable n based on k and p */
>> +	div <<= *p;
>> +	div /= (*k + 1);
>> +	*n = div / 4;
>> +}
>> +
>> +/**
>> + * sunxi_pll1_clk_setup() - Setup function for PLL1 clock
>> + */
>> +
>> +struct clk_factors_config pll1_config = {
>> +	.nshift = 8,
>> +	.nwidth = 5,
>> +	.kshift = 4,
>> +	.kwidth = 2,
>> +	.mshift = 0,
>> +	.mwidth = 2,
>> +	.pshift = 16,
>> +	.pwidth = 2,
>> +};
>> +
>> +static void __init sunxi_pll1_clk_setup(struct device_node *node)
>> +{
>> +	struct clk *clk;
>> +	const char *clk_name = node->name;
>> +	const char *parent;
>> +	void *reg;
>> +
>> +	reg = of_iomap(node, 0);
>> +
>> +	parent = of_clk_get_parent_name(node, 0);
>> +
>> +	clk = clk_register_factors(NULL, clk_name, parent, CLK_IGNORE_UNUSED,
>> +				   reg, &pll1_config, sunxi_get_pll1_factors,
>> +				   &clk_lock);
>> +
>> +	if (clk) {
>> +		of_clk_add_provider(node, of_clk_src_simple_get, clk);
>> +		clk_register_clkdev(clk, clk_name, NULL);
>> +	}
>> +}
>> +
>> +
>> +
>> +/**
>> + * sunxi_cpu_clk_setup() - Setup function for CPU mux
>> + */
>> +
>> +#define SUNXI_CPU_GATE		16
>> +#define SUNXI_CPU_GATE_WIDTH	2
>> +
>> +static void __init sunxi_cpu_clk_setup(struct device_node *node)
>> +{
>> +	struct clk *clk;
>> +	const char *clk_name = node->name;
>> +	const char **parents = kmalloc(sizeof(char *) * 5, GFP_KERNEL);
>> +	void *reg;
>> +	int i = 0;
>> +
>> +	reg = of_iomap(node, 0);
>> +
>> +	while (i < 5 && (parents[i] = of_clk_get_parent_name(node, i)) != NULL)
>> +		i++;
>> +
>> +	clk = clk_register_mux(NULL, clk_name, parents, i, 0, reg,
>> +			       SUNXI_CPU_GATE, SUNXI_CPU_GATE_WIDTH,
>> +			       0, &clk_lock);
>> +
>> +	if (clk) {
>> +		of_clk_add_provider(node, of_clk_src_simple_get, clk);
>> +		clk_register_clkdev(clk, clk_name, NULL);
>> +	}
>> +}
>> +
>> +
>> +
>> +/**
>> + * sunxi_divider_clk_setup() - Setup function for simple divider clocks
>> + */
>> +
>> +#define SUNXI_DIVISOR_WIDTH	2
>> +
>> +struct div_data {
>> +	u8 div;
>> +	u8 pow;
>> +};
>> +
>> +static const __initconst struct div_data axi_data = {
>> +	.div = 0,
>> +	.pow = 0,
>> +};
>> +
>> +static const __initconst struct div_data ahb_data = {
>> +	.div = 4,
>> +	.pow = 1,
>> +};
>> +
>> +static const __initconst struct div_data apb0_data = {
>> +	.div = 8,
>> +	.pow = 1,
>> +};
>> +
>> +static void __init sunxi_divider_clk_setup(struct device_node *node, u8 shift,
>> +					   u8 power_of_two)
>> +{
>> +	struct clk *clk;
>> +	const char *clk_name = node->name;
>> +	const char *clk_parent;
>> +	void *reg;
>> +
>> +	reg = of_iomap(node, 0);
>> +
>> +	clk_parent = of_clk_get_parent_name(node, 0);
>> +
>> +	clk = clk_register_divider(NULL, clk_name, clk_parent, 0,
>> +				   reg, shift, SUNXI_DIVISOR_WIDTH,
>> +				   power_of_two ? CLK_DIVIDER_POWER_OF_TWO : 0,
>> +				   &clk_lock);
>> +	if (clk) {
>> +		of_clk_add_provider(node, of_clk_src_simple_get, clk);
>> +		clk_register_clkdev(clk, clk_name, NULL);
>> +	}
>> +}
>> +
>> +
>> +/* Matches for of_clk_init */
>> +static const __initconst struct of_device_id clk_match[] = {
>> +	{.compatible = "fixed-clock", .data = of_fixed_clk_setup,},
>> +	{.compatible = "allwinner,sunxi-osc-clk", .data = sunxi_osc_clk_setup,},
>> +	{.compatible = "allwinner,sunxi-pll1-clk", .data = sunxi_pll1_clk_setup,},
>> +	{.compatible = "allwinner,sunxi-cpu-clk", .data = sunxi_cpu_clk_setup,},
>> +	{}
>> +};
>> +
>> +/* Matches for divider clocks */
>> +static const __initconst struct of_device_id clk_div_match[] = {
>> +	{.compatible = "allwinner,sunxi-axi-clk", .data = &axi_data,},
>> +	{.compatible = "allwinner,sunxi-ahb-clk", .data = &ahb_data,},
>> +	{.compatible = "allwinner,sunxi-apb0-clk", .data = &apb0_data,},
>> +	{}
>> +};
>> +
>> +static void __init of_sunxi_divider_clock_setup(void)
>> +{
>> +	struct device_node *np;
>> +	const struct div_data *data;
>> +	const struct of_device_id *match;
>> +
>> +	for_each_matching_node(np, clk_div_match) {
>> +		match = of_match_node(clk_div_match, np);
>> +		data = match->data;
>> +		sunxi_divider_clk_setup(np, data->div, data->pow);
>> +	}
>> +}
>> +
>> +void __init sunxi_init_clocks(void)
>> +{
>> +	/* Register all the simple sunxi clocks on DT */
>> +	of_clk_init(clk_match);
>> +
>> +	/* Register divider clocks */
>> +	of_sunxi_divider_clock_setup();
>> +}
>>
> 
> Regards,
> 

@Mike and anyone else reading: do you have any other comments before I
send a v2 series?


Thanks for the review,

Emilio

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

* [PATCH 1/3] clk: arm: sunxi: Add a new clock driver for sunxi SOCs
  2013-01-22  6:12 ` [PATCH 1/3] clk: arm: sunxi: Add a new clock driver for sunxi SOCs Emilio López
  2013-02-05 11:18   ` Gregory CLEMENT
@ 2013-02-08 11:33   ` Gregory CLEMENT
  2013-02-08 17:41     ` Emilio López
  1 sibling, 1 reply; 26+ messages in thread
From: Gregory CLEMENT @ 2013-02-08 11:33 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Emilio,

This is the last part of my review, I didn't takt time to review the
whole patch the last time. As usual, my comments are inline

On 01/22/2013 07:12 AM, Emilio L?pez wrote:

> This commit implements the base CPU clocks for sunxi devices. It has
> been tested using a slightly modified cpufreq driver from the
> linux-sunxi 3.0 tree.
> 
> Additionally, document the new bindings introduced by this patch, and drop
> the (now unused) old sunxi clock driver.
> 
> Idling:
>     / # find /sys/kernel/debug/clk -name clk_rate -print -exec cat {} \;
>     /sys/kernel/debug/clk/osc24M/pll1/cpu/axi/ahb/apb0/clk_rate
>     30000000
>     /sys/kernel/debug/clk/osc24M/pll1/cpu/axi/ahb/clk_rate
>     60000000
>     /sys/kernel/debug/clk/osc24M/pll1/cpu/axi/clk_rate
>     60000000
>     /sys/kernel/debug/clk/osc24M/pll1/cpu/clk_rate
>     60000000
>     /sys/kernel/debug/clk/osc24M/pll1/clk_rate
>     60000000
>     /sys/kernel/debug/clk/osc24M/clk_rate
>     24000000
>     /sys/kernel/debug/clk/osc32k/clk_rate
>     32768
> 
> After "yes >/dev/null &":
>     / # find /sys/kernel/debug/clk -name clk_rate -print -exec cat {} \;
>     /sys/kernel/debug/clk/osc24M/pll1/cpu/axi/ahb/apb0/clk_rate
>     84000000
>     /sys/kernel/debug/clk/osc24M/pll1/cpu/axi/ahb/clk_rate
>     168000000
>     /sys/kernel/debug/clk/osc24M/pll1/cpu/axi/clk_rate
>     336000000
>     /sys/kernel/debug/clk/osc24M/pll1/cpu/clk_rate
>     1008000000
>     /sys/kernel/debug/clk/osc24M/pll1/clk_rate
>     1008000000
>     /sys/kernel/debug/clk/osc24M/clk_rate
>     24000000
>     /sys/kernel/debug/clk/osc32k/clk_rate
>     32768
> 
> Signed-off-by: Emilio L?pez <emilio@elopez.com.ar>
> ---
>  Documentation/devicetree/bindings/clock/sunxi.txt |  47 ++++
>  drivers/clk/Makefile                              |   2 +-
>  drivers/clk/clk-sunxi.c                           |  30 ---
>  drivers/clk/sunxi/Makefile                        |   5 +
>  drivers/clk/sunxi/clk-factors.c                   | 175 ++++++++++++++
>  drivers/clk/sunxi/clk-factors.h                   |  25 ++
>  drivers/clk/sunxi/clk-fixed-gate.c                | 152 +++++++++++++
>  drivers/clk/sunxi/clk-fixed-gate.h                |  13 ++
>  drivers/clk/sunxi/clk-sunxi.c                     | 265 ++++++++++++++++++++++
>  9 files changed, 683 insertions(+), 31 deletions(-)
>  create mode 100644 Documentation/devicetree/bindings/clock/sunxi.txt
>  delete mode 100644 drivers/clk/clk-sunxi.c
>  create mode 100644 drivers/clk/sunxi/Makefile
>  create mode 100644 drivers/clk/sunxi/clk-factors.c
>  create mode 100644 drivers/clk/sunxi/clk-factors.h
>  create mode 100644 drivers/clk/sunxi/clk-fixed-gate.c
>  create mode 100644 drivers/clk/sunxi/clk-fixed-gate.h
>  create mode 100644 drivers/clk/sunxi/clk-sunxi.c
> 
> diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt
> new file mode 100644
> index 0000000..446c5ca
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/clock/sunxi.txt
> @@ -0,0 +1,47 @@
> +Device Tree Clock bindings for arch-sunxi
> +
> +This binding uses the common clock binding[1].
> +
> +[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
> +
> +Required properties:
> +- compatible : shall be one of the following:
> +	"allwinner,sunxi-osc-clk" - for a gatable oscillator
> +	"allwinner,sunxi-pll1-clk" - for the main PLL clock
> +	"allwinner,sunxi-cpu-clk" - for the CPU multiplexer clock
> +	"allwinner,sunxi-axi-clk" - for the sunxi AXI clock
> +	"allwinner,sunxi-ahb-clk" - for the sunxi AHB clock
> +	"allwinner,sunxi-apb0-clk" - for the sunxi APB0 clock
> +
> +Required properties for all clocks:
> +- reg : shall be the control register address for the clock.
> +- clocks : shall be the input parent clock(s) phandle for the clock, except for
> +	the root gatable oscillator clock where it is not present
> +- #clock-cells : from common clock binding; shall be set to 0.
> +
> +Additionally, the gatable oscillator clock requires:
> +- clock-frequency : shall be the frequency of the oscillator.
> +
> +
> +For example:
> +
> +osc24M: osc24M {
> +	#clock-cells = <0>;
> +	compatible = "allwinner,sunxi-osc-clk";
> +	reg = <0x01c20050 0x4>;
> +	clock-frequency = <24000000>;
> +};
> +
> +pll1: pll1 at 01c20000 {
> +	#clock-cells = <0>;
> +	compatible = "allwinner,sunxi-pll1-clk";
> +	reg = <0x01c20000 0x4>;
> +	clocks = <&osc24M>;
> +};
> +
> +cpu: cpu at 01c20054 {
> +	#clock-cells = <0>;
> +	compatible = "allwinner,sunxi-cpu-clk";
> +	reg = <0x01c20054 0x4>;
> +	clocks = <&osc32k>, <&osc24M>, <&pll1>;
> +};
> diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
> index ee90e87..129afed 100644
> --- a/drivers/clk/Makefile
> +++ b/drivers/clk/Makefile
> @@ -20,7 +20,7 @@ endif
>  obj-$(CONFIG_MACH_LOONGSON1)	+= clk-ls1x.o
>  obj-$(CONFIG_ARCH_U8500)	+= ux500/
>  obj-$(CONFIG_ARCH_VT8500)	+= clk-vt8500.o
> -obj-$(CONFIG_ARCH_SUNXI)	+= clk-sunxi.o
> +obj-$(CONFIG_ARCH_SUNXI)	+= sunxi/
>  obj-$(CONFIG_ARCH_ZYNQ)		+= clk-zynq.o
>  
>  # Chip specific
> diff --git a/drivers/clk/clk-sunxi.c b/drivers/clk/clk-sunxi.c
> deleted file mode 100644
> index 0e831b5..0000000
> --- a/drivers/clk/clk-sunxi.c
> +++ /dev/null
> @@ -1,30 +0,0 @@
> -/*
> - * Copyright 2012 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/clkdev.h>
> -#include <linux/clk/sunxi.h>
> -#include <linux/of.h>
> -
> -static const __initconst struct of_device_id clk_match[] = {
> -	{ .compatible = "fixed-clock", .data = of_fixed_clk_setup, },
> -	{}
> -};
> -
> -void __init sunxi_init_clocks(void)
> -{
> -	of_clk_init(clk_match);
> -}
> diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile
> new file mode 100644
> index 0000000..8e773be
> --- /dev/null
> +++ b/drivers/clk/sunxi/Makefile
> @@ -0,0 +1,5 @@
> +#
> +# Makefile for sunxi specific clk
> +#
> +
> +obj-y += clk-sunxi.o clk-factors.o clk-fixed-gate.o
> diff --git a/drivers/clk/sunxi/clk-factors.c b/drivers/clk/sunxi/clk-factors.c
> new file mode 100644
> index 0000000..428b47d
> --- /dev/null
> +++ b/drivers/clk/sunxi/clk-factors.c
> @@ -0,0 +1,175 @@
> +/*
> + * Copyright (C) 2013 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 version 2 as
> + * published by the Free Software Foundation.
> + *
> + * Adjustable factor-based clock implementation
> + */
> +
> +#include <linux/clk-provider.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/io.h>
> +#include <linux/err.h>
> +#include <linux/string.h>
> +
> +#include <linux/delay.h>
> +
> +#include "clk-factors.h"
> +
> +/*
> + * DOC: basic adjustable factor-based clock that cannot gate
> + *
> + * Traits of this clock:
> + * prepare - clk_prepare only ensures that parents are prepared
> + * enable - clk_enable only ensures that parents are enabled
> + * rate - rate is adjustable.
> + *        clk->rate = (parent->rate * N * (K + 1) >> P) / (M + 1)
> + * parent - fixed parent.  No clk_set_parent support
> + */
> +
> +struct clk_factors {
> +	struct clk_hw hw;
> +	void __iomem *reg;
> +	struct clk_factors_config *config;
> +	void (*get_factors) (u32 *rate, u8 *n, u8 *k, u8 *m, u8 *p);
> +	spinlock_t *lock;
> +};
> +
> +#define to_clk_factors(_hw) container_of(_hw, struct clk_factors, hw)
> +
> +#define SETMASK(len, pos)		(((-1U) >> (31-len))  << (pos))
> +#define CLRMASK(len, pos)		(~(SETMASK(len, pos)))
> +#define FACTOR_GET(bit, len, reg)	(((reg) & SETMASK(len, bit)) >> (bit))
> +
> +#define FACTOR_SET(bit, len, reg, val) \
> +	(((reg) & CLRMASK(len, bit)) | (val << (bit)))
> +
> +static unsigned long clk_factors_recalc_rate(struct clk_hw *hw,
> +					     unsigned long parent_rate)
> +{
> +	u8 n, k, p, m;
> +	u32 reg;
> +	unsigned long rate;
> +	struct clk_factors *factors = to_clk_factors(hw);
> +	struct clk_factors_config *config = factors->config;
> +
> +	/* Fetch the register value */
> +	reg = readl(factors->reg);
> +
> +	/* Get each individual factor */
> +	n = FACTOR_GET(config->nshift, config->nwidth, reg);
> +	k = FACTOR_GET(config->kshift, config->kwidth, reg);
> +	m = FACTOR_GET(config->mshift, config->mwidth, reg);
> +	p = FACTOR_GET(config->pshift, config->pwidth, reg);
> +
> +	/* Calculate the rate */
> +	rate = (parent_rate * n * (k + 1) >> p) / (m + 1);
> +
> +	return rate;
> +}
> +
> +static long clk_factors_round_rate(struct clk_hw *hw, unsigned long rate,
> +				   unsigned long *parent_rate)
> +{
> +	struct clk_factors *factors = to_clk_factors(hw);
> +	factors->get_factors((u32 *)&rate, NULL, NULL, NULL, NULL);
> +
> +	return rate;
> +}
> +
> +static int clk_factors_set_rate(struct clk_hw *hw, unsigned long rate,
> +				unsigned long parent_rate)
> +{
> +	u8 n, k, m, p;
> +	u32 reg;
> +	struct clk_factors *factors = to_clk_factors(hw);
> +	struct clk_factors_config *config = factors->config;
> +	unsigned long flags = 0;
> +
> +	factors->get_factors((u32 *)&rate, &n, &k, &m, &p);
> +
> +	if (factors->lock)
> +		spin_lock_irqsave(factors->lock, flags);
> +
> +	/* Fetch the register value */
> +	reg = readl(factors->reg);
> +
> +	/* Set up the new factors */
> +	reg = FACTOR_SET(config->nshift, config->nwidth, reg, n);
> +	reg = FACTOR_SET(config->kshift, config->kwidth, reg, k);
> +	reg = FACTOR_SET(config->mshift, config->mwidth, reg, m);
> +	reg = FACTOR_SET(config->pshift, config->pwidth, reg, p);
> +
> +	/* Apply them now */
> +	writel(reg, factors->reg);
> +
> +	/* delay 500us so pll stabilizes */
> +	__delay((rate >> 20) * 500 / 2);
> +
> +	if (factors->lock)
> +		spin_unlock_irqrestore(factors->lock, flags);
> +
> +	return 0;
> +}
> +
> +static const struct clk_ops clk_factors_ops = {
> +	.recalc_rate = clk_factors_recalc_rate,
> +	.round_rate = clk_factors_round_rate,
> +	.set_rate = clk_factors_set_rate,
> +};
> +
> +/**
> + * clk_register_factors - register a factors clock with
> + * the clock framework
> + * @dev: device registering this clock
> + * @name: name of this clock
> + * @parent_name: name of clock's parent
> + * @flags: framework-specific flags
> + * @reg: register address to adjust factors
> + * @config: shift and width of factors n, k, m and p
> + * @get_factors: function to calculate the factors for a given frequency
> + * @lock: shared register lock for this clock
> + */

Do you have other "factors" clock than sunxi-pll1-clk?
On your patch I only see this clock using this, and the way
you pass a function  and a the config struct seems to me
overcomplicated for a sinlge clock.

> +struct clk *clk_register_factors(struct device *dev, const char *name,
> +				 const char *parent_name,
> +				 unsigned long flags, void __iomem *reg,
> +				 struct clk_factors_config *config,
> +				 void (*get_factors) (u32 *rate, u8 *n, u8 *k,
> +						      u8 *m, u8 *p),
> +				 spinlock_t *lock)
> +{
> +	struct clk_factors *factors;
> +	struct clk *clk;
> +	struct clk_init_data init;
> +
> +	/* allocate the factors */
> +	factors = kzalloc(sizeof(struct clk_factors), GFP_KERNEL);
> +	if (!factors) {
> +		pr_err("%s: could not allocate factors clk\n", __func__);
> +		return ERR_PTR(-ENOMEM);
> +	}
> +
> +	init.name = name;
> +	init.ops = &clk_factors_ops;
> +	init.flags = flags;
> +	init.parent_names = (parent_name ? &parent_name : NULL);
> +	init.num_parents = (parent_name ? 1 : 0);
> +
> +	/* struct clk_factors assignments */
> +	factors->reg = reg;
> +	factors->config = config;
> +	factors->lock = lock;
> +	factors->hw.init = &init;
> +	factors->get_factors = get_factors;
> +
> +	/* register the clock */
> +	clk = clk_register(dev, &factors->hw);
> +
> +	if (IS_ERR(clk))
> +		kfree(factors);
> +
> +	return clk;
> +}
> diff --git a/drivers/clk/sunxi/clk-factors.h b/drivers/clk/sunxi/clk-factors.h
> new file mode 100644
> index 0000000..a24c889
> --- /dev/null
> +++ b/drivers/clk/sunxi/clk-factors.h
> @@ -0,0 +1,25 @@
> +#ifndef __MACH_SUNXI_CLK_FACTORS_H
> +#define __MACH_SUNXI_CLK_FACTORS_H
> +
> +#include <linux/clk-provider.h>
> +#include <linux/clkdev.h>
> +
> +struct clk_factors_config {
> +	u8 nshift;
> +	u8 nwidth;
> +	u8 kshift;
> +	u8 kwidth;
> +	u8 mshift;
> +	u8 mwidth;
> +	u8 pshift;
> +	u8 pwidth;
> +};
> +
> +struct clk *clk_register_factors(struct device *dev, const char *name,
> +				 const char *parent_name,
> +				 unsigned long flags, void __iomem *reg,
> +				 struct clk_factors_config *config,
> +				 void (*get_factors) (u32 *rate, u8 *n, u8 *k,
> +						      u8 *m, u8 *p),
> +				 spinlock_t *lock);
> +#endif
> diff --git a/drivers/clk/sunxi/clk-fixed-gate.c b/drivers/clk/sunxi/clk-fixed-gate.c
> new file mode 100644
> index 0000000..b16eda5
> --- /dev/null
> +++ b/drivers/clk/sunxi/clk-fixed-gate.c
> @@ -0,0 +1,152 @@
> +/*
> + * Copyright (C) 2013 Emilio L?pez <emilio@elopez.com.ar>
> + *
> + * Based on drivers/clk/clk-gate.c,
> + *
> + * Copyright (C) 2010-2011 Canonical Ltd <jeremy.kerr@canonical.com>
> + * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org>
> + *
> + * 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.
> + *
> + * Fixed rate, gated clock implementation
> + */
> +
> +#include <linux/clk-provider.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/io.h>
> +#include <linux/err.h>
> +#include <linux/string.h>
> +
> +#include "clk-fixed-gate.h"
> +
> +/**
> + * DOC: fixed rate clock which can gate and ungate it's ouput
> + *
> + * Traits of this clock:
> + * prepare - clk_(un)prepare only ensures parent is (un)prepared
> + * enable - clk_enable and clk_disable are functional & control gating
> + * rate - rate is always a fixed value.  No clk_set_rate support
> + * parent - fixed parent.  No clk_set_parent support
> + */
> +
> +struct clk_fixed_gate {
> +	struct clk_hw hw;
> +	u8            bit_idx;
> +	u8            flags;
> +	unsigned long fixed_rate;
> +	void __iomem  *reg;
> +	spinlock_t    *lock;
> +};
> +
> +#define to_clk_fixed_gate(_hw) container_of(_hw, struct clk_fixed_gate, hw)
> +
> +static void clk_fixed_gate_endisable(struct clk_hw *hw, int enable)
> +{
> +	struct clk_fixed_gate *gate = to_clk_fixed_gate(hw);
> +	unsigned long flags = 0;
> +	u32 reg;
> +
> +	if (gate->lock)
> +		spin_lock_irqsave(gate->lock, flags);
> +
> +	reg = readl(gate->reg);
> +
> +	if (enable)
> +		reg |= BIT(gate->bit_idx);
> +	else
> +		reg &= ~BIT(gate->bit_idx);
> +
> +	writel(reg, gate->reg);
> +
> +	if (gate->lock)
> +		spin_unlock_irqrestore(gate->lock, flags);
> +}
> +
> +static int clk_fixed_gate_enable(struct clk_hw *hw)
> +{
> +	clk_fixed_gate_endisable(hw, 1);
> +
> +	return 0;
> +}
> +
> +static void clk_fixed_gate_disable(struct clk_hw *hw)
> +{
> +	clk_fixed_gate_endisable(hw, 0);
> +}
> +
> +static int clk_fixed_gate_is_enabled(struct clk_hw *hw)
> +{
> +	u32 reg;
> +	struct clk_fixed_gate *gate = to_clk_fixed_gate(hw);
> +
> +	reg = readl(gate->reg);
> +
> +	reg &= BIT(gate->bit_idx);
> +
> +	return reg ? 1 : 0;
> +}
> +
> +static unsigned long clk_fixed_gate_recalc_rate(struct clk_hw *hw,
> +						unsigned long parent_rate)
> +{
> +	return to_clk_fixed_gate(hw)->fixed_rate;
> +}
> +
> +static const struct clk_ops clk_fixed_gate_ops = {
> +	.enable = clk_fixed_gate_enable,
> +	.disable = clk_fixed_gate_disable,
> +	.is_enabled = clk_fixed_gate_is_enabled,
> +	.recalc_rate = clk_fixed_gate_recalc_rate,
> +};
> +
> +/**
> + * clk_register_fixed_gate - register a fixed rate,
> + * gate clock with the clock framework
> + * @dev: device that is registering this clock
> + * @name: name of this clock
> + * @parent_name: name of this clock's parent
> + * @flags: framework-specific flags for this clock
> + * @reg: register address to control gating of this clock
> + * @bit_idx: which bit in the register controls gating of this clock
> + * @lock: shared register lock for this clock
> + */
> +struct clk *clk_register_fixed_gate(struct device *dev, const char *name,
> +				    const char *parent_name,
> +				    unsigned long flags, void __iomem *reg,
> +				    u8 bit_idx, unsigned long fixed_rate,
> +				    spinlock_t *lock)
> +{
> +	struct clk_fixed_gate *gate;
> +	struct clk *clk;
> +	struct clk_init_data init;
> +
> +	/* allocate the gate */
> +	gate = kzalloc(sizeof(struct clk_fixed_gate), GFP_KERNEL);
> +	if (!gate) {
> +		pr_err("%s: could not allocate fixed gated clk\n", __func__);
> +		return ERR_PTR(-ENOMEM);
> +	}
> +
> +	init.name = name;
> +	init.ops = &clk_fixed_gate_ops;
> +	init.flags = flags | CLK_IS_BASIC;
> +	init.parent_names = (parent_name ? &parent_name : NULL);
> +	init.num_parents = (parent_name ? 1 : 0);
> +
> +	/* struct clk_fixed_gate assignments */
> +	gate->fixed_rate = fixed_rate;
> +	gate->reg = reg;
> +	gate->bit_idx = bit_idx;
> +	gate->lock = lock;
> +	gate->hw.init = &init;
> +
> +	clk = clk_register(dev, &gate->hw);
> +
> +	if (IS_ERR(clk))
> +		kfree(gate);
> +
> +	return clk;
> +}
> diff --git a/drivers/clk/sunxi/clk-fixed-gate.h b/drivers/clk/sunxi/clk-fixed-gate.h
> new file mode 100644
> index 0000000..29d9ed3
> --- /dev/null
> +++ b/drivers/clk/sunxi/clk-fixed-gate.h
> @@ -0,0 +1,13 @@
> +#ifndef __MACH_SUNXI_CLK_FIXED_GATE_H
> +#define __MACH_SUNXI_CLK_FIXED_GATE_H
> +
> +#include <linux/clk-provider.h>
> +#include <linux/clkdev.h>
> +
> +struct clk *clk_register_fixed_gate(struct device *dev, const char *name,
> +				    const char *parent_name,
> +				    unsigned long flags, void __iomem *reg,
> +				    u8 bit_idx, unsigned long fixed_rate,
> +				    spinlock_t *lock);
> +
> +#endif
> diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c
> new file mode 100644
> index 0000000..cb587a0
> --- /dev/null
> +++ b/drivers/clk/sunxi/clk-sunxi.c
> @@ -0,0 +1,265 @@
> +/*
> + * 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/clkdev.h>
> +#include <linux/clk/sunxi.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +
> +#include "clk-factors.h"
> +#include "clk-fixed-gate.h"
> +
> +static DEFINE_SPINLOCK(clk_lock);
> +
> +/**
> + * sunxi_osc_clk_setup() - Setup function for gatable oscillator
> + */
> +
> +#define SUNXI_OSC24M_GATE	0
> +
> +static void __init sunxi_osc_clk_setup(struct device_node *node)
> +{
> +	struct clk *clk;
> +	const char *clk_name = node->name;
> +	void *reg;
> +	u32 rate;
> +
> +	reg = of_iomap(node, 0);
> +
> +	if (of_property_read_u32(node, "clock-frequency", &rate))
> +		return;
> +
> +	clk = clk_register_fixed_gate(NULL, clk_name, NULL,
> +				      CLK_IS_ROOT | CLK_IGNORE_UNUSED,
> +				      reg, SUNXI_OSC24M_GATE, rate, &clk_lock);
> +
> +	if (clk) {
> +		of_clk_add_provider(node, of_clk_src_simple_get, clk);
> +		clk_register_clkdev(clk, clk_name, NULL);
> +	}
> +}
> +
> +
> +
> +/**
> + * sunxi_get_pll1_factors() - calculates n, k, m, p factors for PLL1
> + * PLL1 rate is calculated as follows
> + * rate = (parent_rate * n * (k + 1) >> p) / (m + 1);
> + * parent_rate is always 24Mhz
> + */
> +
> +static void sunxi_get_pll1_factors(u32 *freq, u8 *n, u8 *k, u8 *m, u8 *p)
> +{
> +	u8 div;
> +
> +	/* Normalize value to a 6M multiple */
> +	div = *freq / 6000000;
> +	*freq = 6000000 * div;
> +
> +	/* we were called to round the frequency, we can now return */
> +	if (n == NULL)
> +		return;
> +
> +	/* m is always zero for pll1 */
> +	*m = 0;
> +
> +	/* k is 1 only on these cases */
> +	if (*freq >= 768000000 || *freq == 42000000 || *freq == 54000000)
> +		*k = 1;
> +	else
> +		*k = 0;
> +
> +	/* p will be 3 for divs under 10 */
> +	if (div < 10)
> +		*p = 3;
> +
> +	/* p will be 2 for divs between 10 - 20 and odd divs under 32 */
> +	else if (div < 20 || (div < 32 && (div & 1)))
> +		*p = 2;
> +
> +	/* p will be 1 for even divs under 32, divs under 40 and odd pairs
> +	 * of divs between 40-62 */
> +	else if (div < 40 || (div < 64 && (div & 2)))
> +		*p = 1;
> +
> +	/* any other entries have p = 0 */
> +	else
> +		*p = 0;
> +
> +	/* calculate a suitable n based on k and p */
> +	div <<= *p;
> +	div /= (*k + 1);
> +	*n = div / 4;
> +}
> +
> +/**
> + * sunxi_pll1_clk_setup() - Setup function for PLL1 clock
> + */
> +
> +struct clk_factors_config pll1_config = {
> +	.nshift = 8,
> +	.nwidth = 5,
> +	.kshift = 4,
> +	.kwidth = 2,
> +	.mshift = 0,
> +	.mwidth = 2,
> +	.pshift = 16,
> +	.pwidth = 2,
> +};
> +
> +static void __init sunxi_pll1_clk_setup(struct device_node *node)
> +{
> +	struct clk *clk;
> +	const char *clk_name = node->name;
> +	const char *parent;
> +	void *reg;
> +
> +	reg = of_iomap(node, 0);
> +
> +	parent = of_clk_get_parent_name(node, 0);
> +
> +	clk = clk_register_factors(NULL, clk_name, parent, CLK_IGNORE_UNUSED,
> +				   reg, &pll1_config, sunxi_get_pll1_factors,
> +				   &clk_lock);
> +
> +	if (clk) {
> +		of_clk_add_provider(node, of_clk_src_simple_get, clk);
> +		clk_register_clkdev(clk, clk_name, NULL);
> +	}
> +}
> +
> +
> +
> +/**
> + * sunxi_cpu_clk_setup() - Setup function for CPU mux
> + */
> +
> +#define SUNXI_CPU_GATE		16
> +#define SUNXI_CPU_GATE_WIDTH	2
> +
> +static void __init sunxi_cpu_clk_setup(struct device_node *node)
> +{
> +	struct clk *clk;
> +	const char *clk_name = node->name;
> +	const char **parents = kmalloc(sizeof(char *) * 5, GFP_KERNEL);
> +	void *reg;
> +	int i = 0;
> +
> +	reg = of_iomap(node, 0);
> +
> +	while (i < 5 && (parents[i] = of_clk_get_parent_name(node, i)) != NULL)
> +		i++;
> +
> +	clk = clk_register_mux(NULL, clk_name, parents, i, 0, reg,
> +			       SUNXI_CPU_GATE, SUNXI_CPU_GATE_WIDTH,
> +			       0, &clk_lock);
> +
> +	if (clk) {
> +		of_clk_add_provider(node, of_clk_src_simple_get, clk);
> +		clk_register_clkdev(clk, clk_name, NULL);
> +	}
> +}
> +
> +
> +
> +/**
> + * sunxi_divider_clk_setup() - Setup function for simple divider clocks
> + */
> +
> +#define SUNXI_DIVISOR_WIDTH	2
> +
> +struct div_data {
> +	u8 div;
> +	u8 pow;
> +};
> +
> +static const __initconst struct div_data axi_data = {
> +	.div = 0,
> +	.pow = 0,
> +};
> +
> +static const __initconst struct div_data ahb_data = {
> +	.div = 4,
> +	.pow = 1,
> +};
> +
> +static const __initconst struct div_data apb0_data = {
> +	.div = 8,
> +	.pow = 1,
> +};

Can't you read this from a register?

If not then this 3 clocks are good candidates for my patch:
"clk: add DT fixed-factor-clock binding support"
http://www.spinics.net/lists/arm-kernel/msg191522.html

It was not merged by lack of user, but it will be pretty simple to me
to rebase it on top of 3.8-rc6

So if it is really an informatilon you can only get from the datasheet,
you will be able to just declare it in the DTS.

> +
> +static void __init sunxi_divider_clk_setup(struct device_node *node, u8 shift,
> +					   u8 power_of_two)
> +{
> +	struct clk *clk;
> +	const char *clk_name = node->name;
> +	const char *clk_parent;
> +	void *reg;
> +
> +	reg = of_iomap(node, 0);
> +
> +	clk_parent = of_clk_get_parent_name(node, 0);
> +
> +	clk = clk_register_divider(NULL, clk_name, clk_parent, 0,
> +				   reg, shift, SUNXI_DIVISOR_WIDTH,
> +				   power_of_two ? CLK_DIVIDER_POWER_OF_TWO : 0,
> +				   &clk_lock);
> +	if (clk) {
> +		of_clk_add_provider(node, of_clk_src_simple_get, clk);
> +		clk_register_clkdev(clk, clk_name, NULL);
> +	}
> +}
> +
> +
> +/* Matches for of_clk_init */
> +static const __initconst struct of_device_id clk_match[] = {
> +	{.compatible = "fixed-clock", .data = of_fixed_clk_setup,},
> +	{.compatible = "allwinner,sunxi-osc-clk", .data = sunxi_osc_clk_setup,},
> +	{.compatible = "allwinner,sunxi-pll1-clk", .data = sunxi_pll1_clk_setup,},
> +	{.compatible = "allwinner,sunxi-cpu-clk", .data = sunxi_cpu_clk_setup,},
> +	{}
> +};
> +
> +/* Matches for divider clocks */
> +static const __initconst struct of_device_id clk_div_match[] = {
> +	{.compatible = "allwinner,sunxi-axi-clk", .data = &axi_data,},
> +	{.compatible = "allwinner,sunxi-ahb-clk", .data = &ahb_data,},
> +	{.compatible = "allwinner,sunxi-apb0-clk", .data = &apb0_data,},
> +	{}
> +};
> +
> +static void __init of_sunxi_divider_clock_setup(void)
> +{
> +	struct device_node *np;
> +	const struct div_data *data;
> +	const struct of_device_id *match;
> +
> +	for_each_matching_node(np, clk_div_match) {
> +		match = of_match_node(clk_div_match, np);
> +		data = match->data;
> +		sunxi_divider_clk_setup(np, data->div, data->pow);
> +	}
> +}
> +
> +void __init sunxi_init_clocks(void)
> +{
> +	/* Register all the simple sunxi clocks on DT */
> +	of_clk_init(clk_match);
> +
> +	/* Register divider clocks */
> +	of_sunxi_divider_clock_setup();
> +}
> 


-- 
Gregory Clement, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com

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

* [PATCH 1/3] clk: arm: sunxi: Add a new clock driver for sunxi SOCs
  2013-02-08 10:38     ` Emilio López
@ 2013-02-08 11:43       ` Gregory CLEMENT
  0 siblings, 0 replies; 26+ messages in thread
From: Gregory CLEMENT @ 2013-02-08 11:43 UTC (permalink / raw)
  To: linux-arm-kernel

On 02/08/2013 11:38 AM, Emilio L?pez wrote:
[Snip]

>>> +
>>
>> This file seems to be a copy and past of clk-fixed-rate.c and
>> clk-gate.c (as explained in your header). The purpose of the common
>> clock framework was to reduce code duplication not increase it! :)
> 
> I agree, I am not much of a fan of code duplication :)
> 
>> The current way to achieve what you want is to declare two clock:
>> one fixed-rate and one gateable.
> 
> I don't like this solution much however, because the software
> representation does not accurately match (1:1) the hardware then.

I don't see as a problem: we just split this clock in logical
components. It allows a better reusability of the components.

> 
>> In this case your fixed clock will only be use by the gateable
>> one. Latter thecomposite clk may be a solution, if you really want to
>> only deal with one clock.
> 
> The composite clock lacks fixed rate support from what I saw on the patches.
> 
> So, right now I can think of three possibilities:
> 1) Use two separate clocks as Gregory suggests, and ignore the
> non-accurate representation.
> 2) Extend composite to support fixed rate and use that.
> 3) Extend gate to support fixed rate and use that.
> 
> I think going with 1) will be best for now, and we can then decide and
> work on getting it improved with 2) or 3)

I agree.

> 
>> You could remove this file and its header, do a little change in the
>> dtsi with something like this:
>>
>> osc24M_fixed: osc24M_fixed {
>> 	#clock-cells = <0>;
>> 	compatible = "fixed-clock";
>> 	clock-frequency = <24000000>;
>> };
>>
>> osc24M osc24M {
>> 	#clock-cells = <0>;
>> 	compatible = "allwinner,sunxi-osc-clk"
>> 	reg = <0x01c20050 0x4>;
>> 	clocks = <&osc24M_fixed>;
>> };
>>
>> And modify sunxi_osc_clk_setup() as below
>>
>>
>> [...]
>>
>>> +
>>> +static void __init sunxi_osc_clk_setup(struct device_node *node)
>>> +{
>>> +	struct clk *clk;
>>> +	const char *clk_name = node->name;
>>> +	void *reg;
>>> +	u32 rate;
>>> +
>>> +	reg = of_iomap(node, 0);
>>
>> here you retrieve the name of the parent clock:
>>
>> 	clk = of_clk_get(np, 0);
>> 	if (!IS_ERR(clk)) {
>> 		parent_name = __clk_get_name(clk);
>> 		clk_put(clk);
>> 	}
>>
>> We don't anymore to get the clock-frequency
>>> +
>>> +	if (of_property_read_u32(node, "clock-frequency", &rate))
>>> +		return;
>>> +
>>
>> And here instead of clk_register_fixed_gate you can use the
>> clk_register_gate as below:
>>> +	clk = clk_register_fixed_gate(NULL, clk_name, NULL,
>>> +				      CLK_IS_ROOT | CLK_IGNORE_UNUSED,
>>> +				      reg, SUNXI_OSC24M_GATE, rate, &clk_lock);
>>> +
>> 	clk = clk_register_gate(NULL, clk_name, parent_name,
>> 			CLK_IGNORE_UNUSED, reg, SUNXI_OSC24M_GATE, 0, &clk->lock);
>>
>>> +	if (clk) {
>>> +		of_clk_add_provider(node, of_clk_src_simple_get, clk);
>>> +		clk_register_clkdev(clk, clk_name, NULL);
>>> +	}
>>> +}
>>> +
>>> +
>>> +
>>> +/**
>>> + * sunxi_get_pll1_factors() - calculates n, k, m, p factors for PLL1
>>> + * PLL1 rate is calculated as follows
>>> + * rate = (parent_rate * n * (k + 1) >> p) / (m + 1);
>>> + * parent_rate is always 24Mhz
>>> + */
>>> +
>>> +static void sunxi_get_pll1_factors(u32 *freq, u8 *n, u8 *k, u8 *m, u8 *p)
>>> +{
>>> +	u8 div;
>>> +
>>> +	/* Normalize value to a 6M multiple */
>>> +	div = *freq / 6000000;
>>> +	*freq = 6000000 * div;
>>> +
>>> +	/* we were called to round the frequency, we can now return */
>>> +	if (n == NULL)
>>> +		return;
>>> +
>>> +	/* m is always zero for pll1 */
>>> +	*m = 0;
>>> +
>>> +	/* k is 1 only on these cases */
>>> +	if (*freq >= 768000000 || *freq == 42000000 || *freq == 54000000)
>>> +		*k = 1;
>>> +	else
>>> +		*k = 0;
>>> +
>>> +	/* p will be 3 for divs under 10 */
>>> +	if (div < 10)
>>> +		*p = 3;
>>> +
>>> +	/* p will be 2 for divs between 10 - 20 and odd divs under 32 */
>>> +	else if (div < 20 || (div < 32 && (div & 1)))
>>> +		*p = 2;
>>> +
>>> +	/* p will be 1 for even divs under 32, divs under 40 and odd pairs
>>> +	 * of divs between 40-62 */
>>> +	else if (div < 40 || (div < 64 && (div & 2)))
>>> +		*p = 1;
>>> +
>>> +	/* any other entries have p = 0 */
>>> +	else
>>> +		*p = 0;
>>> +
>>> +	/* calculate a suitable n based on k and p */
>>> +	div <<= *p;
>>> +	div /= (*k + 1);
>>> +	*n = div / 4;
>>> +}
>>> +
>>> +/**
>>> + * sunxi_pll1_clk_setup() - Setup function for PLL1 clock
>>> + */
>>> +
>>> +struct clk_factors_config pll1_config = {
>>> +	.nshift = 8,
>>> +	.nwidth = 5,
>>> +	.kshift = 4,
>>> +	.kwidth = 2,
>>> +	.mshift = 0,
>>> +	.mwidth = 2,
>>> +	.pshift = 16,
>>> +	.pwidth = 2,
>>> +};
>>> +
>>> +static void __init sunxi_pll1_clk_setup(struct device_node *node)
>>> +{
>>> +	struct clk *clk;
>>> +	const char *clk_name = node->name;
>>> +	const char *parent;
>>> +	void *reg;
>>> +
>>> +	reg = of_iomap(node, 0);
>>> +
>>> +	parent = of_clk_get_parent_name(node, 0);
>>> +
>>> +	clk = clk_register_factors(NULL, clk_name, parent, CLK_IGNORE_UNUSED,
>>> +				   reg, &pll1_config, sunxi_get_pll1_factors,
>>> +				   &clk_lock);
>>> +
>>> +	if (clk) {
>>> +		of_clk_add_provider(node, of_clk_src_simple_get, clk);
>>> +		clk_register_clkdev(clk, clk_name, NULL);
>>> +	}
>>> +}
>>> +
>>> +
>>> +
>>> +/**
>>> + * sunxi_cpu_clk_setup() - Setup function for CPU mux
>>> + */
>>> +
>>> +#define SUNXI_CPU_GATE		16
>>> +#define SUNXI_CPU_GATE_WIDTH	2
>>> +
>>> +static void __init sunxi_cpu_clk_setup(struct device_node *node)
>>> +{
>>> +	struct clk *clk;
>>> +	const char *clk_name = node->name;
>>> +	const char **parents = kmalloc(sizeof(char *) * 5, GFP_KERNEL);
>>> +	void *reg;
>>> +	int i = 0;
>>> +
>>> +	reg = of_iomap(node, 0);
>>> +
>>> +	while (i < 5 && (parents[i] = of_clk_get_parent_name(node, i)) != NULL)
>>> +		i++;
>>> +
>>> +	clk = clk_register_mux(NULL, clk_name, parents, i, 0, reg,
>>> +			       SUNXI_CPU_GATE, SUNXI_CPU_GATE_WIDTH,
>>> +			       0, &clk_lock);
>>> +
>>> +	if (clk) {
>>> +		of_clk_add_provider(node, of_clk_src_simple_get, clk);
>>> +		clk_register_clkdev(clk, clk_name, NULL);
>>> +	}
>>> +}
>>> +
>>> +
>>> +
>>> +/**
>>> + * sunxi_divider_clk_setup() - Setup function for simple divider clocks
>>> + */
>>> +
>>> +#define SUNXI_DIVISOR_WIDTH	2
>>> +
>>> +struct div_data {
>>> +	u8 div;
>>> +	u8 pow;
>>> +};
>>> +
>>> +static const __initconst struct div_data axi_data = {
>>> +	.div = 0,
>>> +	.pow = 0,
>>> +};
>>> +
>>> +static const __initconst struct div_data ahb_data = {
>>> +	.div = 4,
>>> +	.pow = 1,
>>> +};
>>> +
>>> +static const __initconst struct div_data apb0_data = {
>>> +	.div = 8,
>>> +	.pow = 1,
>>> +};
>>> +
>>> +static void __init sunxi_divider_clk_setup(struct device_node *node, u8 shift,
>>> +					   u8 power_of_two)
>>> +{
>>> +	struct clk *clk;
>>> +	const char *clk_name = node->name;
>>> +	const char *clk_parent;
>>> +	void *reg;
>>> +
>>> +	reg = of_iomap(node, 0);
>>> +
>>> +	clk_parent = of_clk_get_parent_name(node, 0);
>>> +
>>> +	clk = clk_register_divider(NULL, clk_name, clk_parent, 0,
>>> +				   reg, shift, SUNXI_DIVISOR_WIDTH,
>>> +				   power_of_two ? CLK_DIVIDER_POWER_OF_TWO : 0,
>>> +				   &clk_lock);
>>> +	if (clk) {
>>> +		of_clk_add_provider(node, of_clk_src_simple_get, clk);
>>> +		clk_register_clkdev(clk, clk_name, NULL);
>>> +	}
>>> +}
>>> +
>>> +
>>> +/* Matches for of_clk_init */
>>> +static const __initconst struct of_device_id clk_match[] = {
>>> +	{.compatible = "fixed-clock", .data = of_fixed_clk_setup,},
>>> +	{.compatible = "allwinner,sunxi-osc-clk", .data = sunxi_osc_clk_setup,},
>>> +	{.compatible = "allwinner,sunxi-pll1-clk", .data = sunxi_pll1_clk_setup,},
>>> +	{.compatible = "allwinner,sunxi-cpu-clk", .data = sunxi_cpu_clk_setup,},
>>> +	{}
>>> +};
>>> +
>>> +/* Matches for divider clocks */
>>> +static const __initconst struct of_device_id clk_div_match[] = {
>>> +	{.compatible = "allwinner,sunxi-axi-clk", .data = &axi_data,},
>>> +	{.compatible = "allwinner,sunxi-ahb-clk", .data = &ahb_data,},
>>> +	{.compatible = "allwinner,sunxi-apb0-clk", .data = &apb0_data,},
>>> +	{}
>>> +};
>>> +
>>> +static void __init of_sunxi_divider_clock_setup(void)
>>> +{
>>> +	struct device_node *np;
>>> +	const struct div_data *data;
>>> +	const struct of_device_id *match;
>>> +
>>> +	for_each_matching_node(np, clk_div_match) {
>>> +		match = of_match_node(clk_div_match, np);
>>> +		data = match->data;
>>> +		sunxi_divider_clk_setup(np, data->div, data->pow);
>>> +	}
>>> +}
>>> +
>>> +void __init sunxi_init_clocks(void)
>>> +{
>>> +	/* Register all the simple sunxi clocks on DT */
>>> +	of_clk_init(clk_match);
>>> +
>>> +	/* Register divider clocks */
>>> +	of_sunxi_divider_clock_setup();
>>> +}
>>>
>>
>> Regards,
>>
> 
> @Mike and anyone else reading: do you have any other comments before I
> send a v2 series?

I've just sent new comments about the other clocks.

Regards,

Gregory

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

* [PATCH 1/3] clk: arm: sunxi: Add a new clock driver for sunxi SOCs
  2013-02-08 11:33   ` Gregory CLEMENT
@ 2013-02-08 17:41     ` Emilio López
  2013-02-08 17:51       ` Gregory CLEMENT
  0 siblings, 1 reply; 26+ messages in thread
From: Emilio López @ 2013-02-08 17:41 UTC (permalink / raw)
  To: linux-arm-kernel

Hello Gregory,

El 08/02/13 08:33, Gregory CLEMENT escribi?:
> Hi Emilio,
> 
> This is the last part of my review, I didn't takt time to review the
> whole patch the last time. As usual, my comments are inline
> 
> On 01/22/2013 07:12 AM, Emilio L?pez wrote:
> 
>> This commit implements the base CPU clocks for sunxi devices. It has
>> been tested using a slightly modified cpufreq driver from the
>> linux-sunxi 3.0 tree.
>>
>> Additionally, document the new bindings introduced by this patch, and drop
>> the (now unused) old sunxi clock driver.
>>
>> Idling:
>>     / # find /sys/kernel/debug/clk -name clk_rate -print -exec cat {} \;
>>     /sys/kernel/debug/clk/osc24M/pll1/cpu/axi/ahb/apb0/clk_rate
>>     30000000
>>     /sys/kernel/debug/clk/osc24M/pll1/cpu/axi/ahb/clk_rate
>>     60000000
>>     /sys/kernel/debug/clk/osc24M/pll1/cpu/axi/clk_rate
>>     60000000
>>     /sys/kernel/debug/clk/osc24M/pll1/cpu/clk_rate
>>     60000000
>>     /sys/kernel/debug/clk/osc24M/pll1/clk_rate
>>     60000000
>>     /sys/kernel/debug/clk/osc24M/clk_rate
>>     24000000
>>     /sys/kernel/debug/clk/osc32k/clk_rate
>>     32768
>>
>> After "yes >/dev/null &":
>>     / # find /sys/kernel/debug/clk -name clk_rate -print -exec cat {} \;
>>     /sys/kernel/debug/clk/osc24M/pll1/cpu/axi/ahb/apb0/clk_rate
>>     84000000
>>     /sys/kernel/debug/clk/osc24M/pll1/cpu/axi/ahb/clk_rate
>>     168000000
>>     /sys/kernel/debug/clk/osc24M/pll1/cpu/axi/clk_rate
>>     336000000
>>     /sys/kernel/debug/clk/osc24M/pll1/cpu/clk_rate
>>     1008000000
>>     /sys/kernel/debug/clk/osc24M/pll1/clk_rate
>>     1008000000
>>     /sys/kernel/debug/clk/osc24M/clk_rate
>>     24000000
>>     /sys/kernel/debug/clk/osc32k/clk_rate
>>     32768
>>
>> Signed-off-by: Emilio L?pez <emilio@elopez.com.ar>
>> ---
>>  Documentation/devicetree/bindings/clock/sunxi.txt |  47 ++++
>>  drivers/clk/Makefile                              |   2 +-
>>  drivers/clk/clk-sunxi.c                           |  30 ---
>>  drivers/clk/sunxi/Makefile                        |   5 +
>>  drivers/clk/sunxi/clk-factors.c                   | 175 ++++++++++++++
>>  drivers/clk/sunxi/clk-factors.h                   |  25 ++
>>  drivers/clk/sunxi/clk-fixed-gate.c                | 152 +++++++++++++
>>  drivers/clk/sunxi/clk-fixed-gate.h                |  13 ++
>>  drivers/clk/sunxi/clk-sunxi.c                     | 265 ++++++++++++++++++++++
>>  9 files changed, 683 insertions(+), 31 deletions(-)
>>  create mode 100644 Documentation/devicetree/bindings/clock/sunxi.txt
>>  delete mode 100644 drivers/clk/clk-sunxi.c
>>  create mode 100644 drivers/clk/sunxi/Makefile
>>  create mode 100644 drivers/clk/sunxi/clk-factors.c
>>  create mode 100644 drivers/clk/sunxi/clk-factors.h
>>  create mode 100644 drivers/clk/sunxi/clk-fixed-gate.c
>>  create mode 100644 drivers/clk/sunxi/clk-fixed-gate.h
>>  create mode 100644 drivers/clk/sunxi/clk-sunxi.c
>>
>> diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt
>> new file mode 100644
>> index 0000000..446c5ca
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/clock/sunxi.txt
>> @@ -0,0 +1,47 @@
>> +Device Tree Clock bindings for arch-sunxi
>> +
>> +This binding uses the common clock binding[1].
>> +
>> +[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
>> +
>> +Required properties:
>> +- compatible : shall be one of the following:
>> +	"allwinner,sunxi-osc-clk" - for a gatable oscillator
>> +	"allwinner,sunxi-pll1-clk" - for the main PLL clock
>> +	"allwinner,sunxi-cpu-clk" - for the CPU multiplexer clock
>> +	"allwinner,sunxi-axi-clk" - for the sunxi AXI clock
>> +	"allwinner,sunxi-ahb-clk" - for the sunxi AHB clock
>> +	"allwinner,sunxi-apb0-clk" - for the sunxi APB0 clock
>> +
>> +Required properties for all clocks:
>> +- reg : shall be the control register address for the clock.
>> +- clocks : shall be the input parent clock(s) phandle for the clock, except for
>> +	the root gatable oscillator clock where it is not present
>> +- #clock-cells : from common clock binding; shall be set to 0.
>> +
>> +Additionally, the gatable oscillator clock requires:
>> +- clock-frequency : shall be the frequency of the oscillator.
>> +
>> +
>> +For example:
>> +
>> +osc24M: osc24M {
>> +	#clock-cells = <0>;
>> +	compatible = "allwinner,sunxi-osc-clk";
>> +	reg = <0x01c20050 0x4>;
>> +	clock-frequency = <24000000>;
>> +};
>> +
>> +pll1: pll1 at 01c20000 {
>> +	#clock-cells = <0>;
>> +	compatible = "allwinner,sunxi-pll1-clk";
>> +	reg = <0x01c20000 0x4>;
>> +	clocks = <&osc24M>;
>> +};
>> +
>> +cpu: cpu at 01c20054 {
>> +	#clock-cells = <0>;
>> +	compatible = "allwinner,sunxi-cpu-clk";
>> +	reg = <0x01c20054 0x4>;
>> +	clocks = <&osc32k>, <&osc24M>, <&pll1>;
>> +};
>> diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
>> index ee90e87..129afed 100644
>> --- a/drivers/clk/Makefile
>> +++ b/drivers/clk/Makefile
>> @@ -20,7 +20,7 @@ endif
>>  obj-$(CONFIG_MACH_LOONGSON1)	+= clk-ls1x.o
>>  obj-$(CONFIG_ARCH_U8500)	+= ux500/
>>  obj-$(CONFIG_ARCH_VT8500)	+= clk-vt8500.o
>> -obj-$(CONFIG_ARCH_SUNXI)	+= clk-sunxi.o
>> +obj-$(CONFIG_ARCH_SUNXI)	+= sunxi/
>>  obj-$(CONFIG_ARCH_ZYNQ)		+= clk-zynq.o
>>  
>>  # Chip specific
>> diff --git a/drivers/clk/clk-sunxi.c b/drivers/clk/clk-sunxi.c
>> deleted file mode 100644
>> index 0e831b5..0000000
>> --- a/drivers/clk/clk-sunxi.c
>> +++ /dev/null
>> @@ -1,30 +0,0 @@
>> -/*
>> - * Copyright 2012 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/clkdev.h>
>> -#include <linux/clk/sunxi.h>
>> -#include <linux/of.h>
>> -
>> -static const __initconst struct of_device_id clk_match[] = {
>> -	{ .compatible = "fixed-clock", .data = of_fixed_clk_setup, },
>> -	{}
>> -};
>> -
>> -void __init sunxi_init_clocks(void)
>> -{
>> -	of_clk_init(clk_match);
>> -}
>> diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile
>> new file mode 100644
>> index 0000000..8e773be
>> --- /dev/null
>> +++ b/drivers/clk/sunxi/Makefile
>> @@ -0,0 +1,5 @@
>> +#
>> +# Makefile for sunxi specific clk
>> +#
>> +
>> +obj-y += clk-sunxi.o clk-factors.o clk-fixed-gate.o
>> diff --git a/drivers/clk/sunxi/clk-factors.c b/drivers/clk/sunxi/clk-factors.c
>> new file mode 100644
>> index 0000000..428b47d
>> --- /dev/null
>> +++ b/drivers/clk/sunxi/clk-factors.c
>> @@ -0,0 +1,175 @@
>> +/*
>> + * Copyright (C) 2013 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 version 2 as
>> + * published by the Free Software Foundation.
>> + *
>> + * Adjustable factor-based clock implementation
>> + */
>> +
>> +#include <linux/clk-provider.h>
>> +#include <linux/module.h>
>> +#include <linux/slab.h>
>> +#include <linux/io.h>
>> +#include <linux/err.h>
>> +#include <linux/string.h>
>> +
>> +#include <linux/delay.h>
>> +
>> +#include "clk-factors.h"
>> +
>> +/*
>> + * DOC: basic adjustable factor-based clock that cannot gate
>> + *
>> + * Traits of this clock:
>> + * prepare - clk_prepare only ensures that parents are prepared
>> + * enable - clk_enable only ensures that parents are enabled
>> + * rate - rate is adjustable.
>> + *        clk->rate = (parent->rate * N * (K + 1) >> P) / (M + 1)
>> + * parent - fixed parent.  No clk_set_parent support
>> + */
>> +
>> +struct clk_factors {
>> +	struct clk_hw hw;
>> +	void __iomem *reg;
>> +	struct clk_factors_config *config;
>> +	void (*get_factors) (u32 *rate, u8 *n, u8 *k, u8 *m, u8 *p);
>> +	spinlock_t *lock;
>> +};
>> +
>> +#define to_clk_factors(_hw) container_of(_hw, struct clk_factors, hw)
>> +
>> +#define SETMASK(len, pos)		(((-1U) >> (31-len))  << (pos))
>> +#define CLRMASK(len, pos)		(~(SETMASK(len, pos)))
>> +#define FACTOR_GET(bit, len, reg)	(((reg) & SETMASK(len, bit)) >> (bit))
>> +
>> +#define FACTOR_SET(bit, len, reg, val) \
>> +	(((reg) & CLRMASK(len, bit)) | (val << (bit)))
>> +
>> +static unsigned long clk_factors_recalc_rate(struct clk_hw *hw,
>> +					     unsigned long parent_rate)
>> +{
>> +	u8 n, k, p, m;
>> +	u32 reg;
>> +	unsigned long rate;
>> +	struct clk_factors *factors = to_clk_factors(hw);
>> +	struct clk_factors_config *config = factors->config;
>> +
>> +	/* Fetch the register value */
>> +	reg = readl(factors->reg);
>> +
>> +	/* Get each individual factor */
>> +	n = FACTOR_GET(config->nshift, config->nwidth, reg);
>> +	k = FACTOR_GET(config->kshift, config->kwidth, reg);
>> +	m = FACTOR_GET(config->mshift, config->mwidth, reg);
>> +	p = FACTOR_GET(config->pshift, config->pwidth, reg);
>> +
>> +	/* Calculate the rate */
>> +	rate = (parent_rate * n * (k + 1) >> p) / (m + 1);
>> +
>> +	return rate;
>> +}
>> +
>> +static long clk_factors_round_rate(struct clk_hw *hw, unsigned long rate,
>> +				   unsigned long *parent_rate)
>> +{
>> +	struct clk_factors *factors = to_clk_factors(hw);
>> +	factors->get_factors((u32 *)&rate, NULL, NULL, NULL, NULL);
>> +
>> +	return rate;
>> +}
>> +
>> +static int clk_factors_set_rate(struct clk_hw *hw, unsigned long rate,
>> +				unsigned long parent_rate)
>> +{
>> +	u8 n, k, m, p;
>> +	u32 reg;
>> +	struct clk_factors *factors = to_clk_factors(hw);
>> +	struct clk_factors_config *config = factors->config;
>> +	unsigned long flags = 0;
>> +
>> +	factors->get_factors((u32 *)&rate, &n, &k, &m, &p);
>> +
>> +	if (factors->lock)
>> +		spin_lock_irqsave(factors->lock, flags);
>> +
>> +	/* Fetch the register value */
>> +	reg = readl(factors->reg);
>> +
>> +	/* Set up the new factors */
>> +	reg = FACTOR_SET(config->nshift, config->nwidth, reg, n);
>> +	reg = FACTOR_SET(config->kshift, config->kwidth, reg, k);
>> +	reg = FACTOR_SET(config->mshift, config->mwidth, reg, m);
>> +	reg = FACTOR_SET(config->pshift, config->pwidth, reg, p);
>> +
>> +	/* Apply them now */
>> +	writel(reg, factors->reg);
>> +
>> +	/* delay 500us so pll stabilizes */
>> +	__delay((rate >> 20) * 500 / 2);
>> +
>> +	if (factors->lock)
>> +		spin_unlock_irqrestore(factors->lock, flags);
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct clk_ops clk_factors_ops = {
>> +	.recalc_rate = clk_factors_recalc_rate,
>> +	.round_rate = clk_factors_round_rate,
>> +	.set_rate = clk_factors_set_rate,
>> +};
>> +
>> +/**
>> + * clk_register_factors - register a factors clock with
>> + * the clock framework
>> + * @dev: device registering this clock
>> + * @name: name of this clock
>> + * @parent_name: name of clock's parent
>> + * @flags: framework-specific flags
>> + * @reg: register address to adjust factors
>> + * @config: shift and width of factors n, k, m and p
>> + * @get_factors: function to calculate the factors for a given frequency
>> + * @lock: shared register lock for this clock
>> + */
> 
> Do you have other "factors" clock than sunxi-pll1-clk?
> On your patch I only see this clock using this, and the way
> you pass a function  and a the config struct seems to me
> overcomplicated for a sinlge clock.

Yes, there are other "factors" clocks on sunxi hardware; I have not
added them to the driver yet, but having the factors implementation
there makes it trivial to represent them on a future patchset.

> 
>> +struct clk *clk_register_factors(struct device *dev, const char *name,
>> +				 const char *parent_name,
>> +				 unsigned long flags, void __iomem *reg,
>> +				 struct clk_factors_config *config,
>> +				 void (*get_factors) (u32 *rate, u8 *n, u8 *k,
>> +						      u8 *m, u8 *p),
>> +				 spinlock_t *lock)
>> +{
>> +	struct clk_factors *factors;
>> +	struct clk *clk;
>> +	struct clk_init_data init;
>> +
>> +	/* allocate the factors */
>> +	factors = kzalloc(sizeof(struct clk_factors), GFP_KERNEL);
>> +	if (!factors) {
>> +		pr_err("%s: could not allocate factors clk\n", __func__);
>> +		return ERR_PTR(-ENOMEM);
>> +	}
>> +
>> +	init.name = name;
>> +	init.ops = &clk_factors_ops;
>> +	init.flags = flags;
>> +	init.parent_names = (parent_name ? &parent_name : NULL);
>> +	init.num_parents = (parent_name ? 1 : 0);
>> +
>> +	/* struct clk_factors assignments */
>> +	factors->reg = reg;
>> +	factors->config = config;
>> +	factors->lock = lock;
>> +	factors->hw.init = &init;
>> +	factors->get_factors = get_factors;
>> +
>> +	/* register the clock */
>> +	clk = clk_register(dev, &factors->hw);
>> +
>> +	if (IS_ERR(clk))
>> +		kfree(factors);
>> +
>> +	return clk;
>> +}
>> diff --git a/drivers/clk/sunxi/clk-factors.h b/drivers/clk/sunxi/clk-factors.h
>> new file mode 100644
>> index 0000000..a24c889
>> --- /dev/null
>> +++ b/drivers/clk/sunxi/clk-factors.h
>> @@ -0,0 +1,25 @@
>> +#ifndef __MACH_SUNXI_CLK_FACTORS_H
>> +#define __MACH_SUNXI_CLK_FACTORS_H
>> +
>> +#include <linux/clk-provider.h>
>> +#include <linux/clkdev.h>
>> +
>> +struct clk_factors_config {
>> +	u8 nshift;
>> +	u8 nwidth;
>> +	u8 kshift;
>> +	u8 kwidth;
>> +	u8 mshift;
>> +	u8 mwidth;
>> +	u8 pshift;
>> +	u8 pwidth;
>> +};
>> +
>> +struct clk *clk_register_factors(struct device *dev, const char *name,
>> +				 const char *parent_name,
>> +				 unsigned long flags, void __iomem *reg,
>> +				 struct clk_factors_config *config,
>> +				 void (*get_factors) (u32 *rate, u8 *n, u8 *k,
>> +						      u8 *m, u8 *p),
>> +				 spinlock_t *lock);
>> +#endif
>> diff --git a/drivers/clk/sunxi/clk-fixed-gate.c b/drivers/clk/sunxi/clk-fixed-gate.c
>> new file mode 100644
>> index 0000000..b16eda5
>> --- /dev/null
>> +++ b/drivers/clk/sunxi/clk-fixed-gate.c
>> @@ -0,0 +1,152 @@
>> +/*
>> + * Copyright (C) 2013 Emilio L?pez <emilio@elopez.com.ar>
>> + *
>> + * Based on drivers/clk/clk-gate.c,
>> + *
>> + * Copyright (C) 2010-2011 Canonical Ltd <jeremy.kerr@canonical.com>
>> + * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org>
>> + *
>> + * 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.
>> + *
>> + * Fixed rate, gated clock implementation
>> + */
>> +
>> +#include <linux/clk-provider.h>
>> +#include <linux/module.h>
>> +#include <linux/slab.h>
>> +#include <linux/io.h>
>> +#include <linux/err.h>
>> +#include <linux/string.h>
>> +
>> +#include "clk-fixed-gate.h"
>> +
>> +/**
>> + * DOC: fixed rate clock which can gate and ungate it's ouput
>> + *
>> + * Traits of this clock:
>> + * prepare - clk_(un)prepare only ensures parent is (un)prepared
>> + * enable - clk_enable and clk_disable are functional & control gating
>> + * rate - rate is always a fixed value.  No clk_set_rate support
>> + * parent - fixed parent.  No clk_set_parent support
>> + */
>> +
>> +struct clk_fixed_gate {
>> +	struct clk_hw hw;
>> +	u8            bit_idx;
>> +	u8            flags;
>> +	unsigned long fixed_rate;
>> +	void __iomem  *reg;
>> +	spinlock_t    *lock;
>> +};
>> +
>> +#define to_clk_fixed_gate(_hw) container_of(_hw, struct clk_fixed_gate, hw)
>> +
>> +static void clk_fixed_gate_endisable(struct clk_hw *hw, int enable)
>> +{
>> +	struct clk_fixed_gate *gate = to_clk_fixed_gate(hw);
>> +	unsigned long flags = 0;
>> +	u32 reg;
>> +
>> +	if (gate->lock)
>> +		spin_lock_irqsave(gate->lock, flags);
>> +
>> +	reg = readl(gate->reg);
>> +
>> +	if (enable)
>> +		reg |= BIT(gate->bit_idx);
>> +	else
>> +		reg &= ~BIT(gate->bit_idx);
>> +
>> +	writel(reg, gate->reg);
>> +
>> +	if (gate->lock)
>> +		spin_unlock_irqrestore(gate->lock, flags);
>> +}
>> +
>> +static int clk_fixed_gate_enable(struct clk_hw *hw)
>> +{
>> +	clk_fixed_gate_endisable(hw, 1);
>> +
>> +	return 0;
>> +}
>> +
>> +static void clk_fixed_gate_disable(struct clk_hw *hw)
>> +{
>> +	clk_fixed_gate_endisable(hw, 0);
>> +}
>> +
>> +static int clk_fixed_gate_is_enabled(struct clk_hw *hw)
>> +{
>> +	u32 reg;
>> +	struct clk_fixed_gate *gate = to_clk_fixed_gate(hw);
>> +
>> +	reg = readl(gate->reg);
>> +
>> +	reg &= BIT(gate->bit_idx);
>> +
>> +	return reg ? 1 : 0;
>> +}
>> +
>> +static unsigned long clk_fixed_gate_recalc_rate(struct clk_hw *hw,
>> +						unsigned long parent_rate)
>> +{
>> +	return to_clk_fixed_gate(hw)->fixed_rate;
>> +}
>> +
>> +static const struct clk_ops clk_fixed_gate_ops = {
>> +	.enable = clk_fixed_gate_enable,
>> +	.disable = clk_fixed_gate_disable,
>> +	.is_enabled = clk_fixed_gate_is_enabled,
>> +	.recalc_rate = clk_fixed_gate_recalc_rate,
>> +};
>> +
>> +/**
>> + * clk_register_fixed_gate - register a fixed rate,
>> + * gate clock with the clock framework
>> + * @dev: device that is registering this clock
>> + * @name: name of this clock
>> + * @parent_name: name of this clock's parent
>> + * @flags: framework-specific flags for this clock
>> + * @reg: register address to control gating of this clock
>> + * @bit_idx: which bit in the register controls gating of this clock
>> + * @lock: shared register lock for this clock
>> + */
>> +struct clk *clk_register_fixed_gate(struct device *dev, const char *name,
>> +				    const char *parent_name,
>> +				    unsigned long flags, void __iomem *reg,
>> +				    u8 bit_idx, unsigned long fixed_rate,
>> +				    spinlock_t *lock)
>> +{
>> +	struct clk_fixed_gate *gate;
>> +	struct clk *clk;
>> +	struct clk_init_data init;
>> +
>> +	/* allocate the gate */
>> +	gate = kzalloc(sizeof(struct clk_fixed_gate), GFP_KERNEL);
>> +	if (!gate) {
>> +		pr_err("%s: could not allocate fixed gated clk\n", __func__);
>> +		return ERR_PTR(-ENOMEM);
>> +	}
>> +
>> +	init.name = name;
>> +	init.ops = &clk_fixed_gate_ops;
>> +	init.flags = flags | CLK_IS_BASIC;
>> +	init.parent_names = (parent_name ? &parent_name : NULL);
>> +	init.num_parents = (parent_name ? 1 : 0);
>> +
>> +	/* struct clk_fixed_gate assignments */
>> +	gate->fixed_rate = fixed_rate;
>> +	gate->reg = reg;
>> +	gate->bit_idx = bit_idx;
>> +	gate->lock = lock;
>> +	gate->hw.init = &init;
>> +
>> +	clk = clk_register(dev, &gate->hw);
>> +
>> +	if (IS_ERR(clk))
>> +		kfree(gate);
>> +
>> +	return clk;
>> +}
>> diff --git a/drivers/clk/sunxi/clk-fixed-gate.h b/drivers/clk/sunxi/clk-fixed-gate.h
>> new file mode 100644
>> index 0000000..29d9ed3
>> --- /dev/null
>> +++ b/drivers/clk/sunxi/clk-fixed-gate.h
>> @@ -0,0 +1,13 @@
>> +#ifndef __MACH_SUNXI_CLK_FIXED_GATE_H
>> +#define __MACH_SUNXI_CLK_FIXED_GATE_H
>> +
>> +#include <linux/clk-provider.h>
>> +#include <linux/clkdev.h>
>> +
>> +struct clk *clk_register_fixed_gate(struct device *dev, const char *name,
>> +				    const char *parent_name,
>> +				    unsigned long flags, void __iomem *reg,
>> +				    u8 bit_idx, unsigned long fixed_rate,
>> +				    spinlock_t *lock);
>> +
>> +#endif
>> diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c
>> new file mode 100644
>> index 0000000..cb587a0
>> --- /dev/null
>> +++ b/drivers/clk/sunxi/clk-sunxi.c
>> @@ -0,0 +1,265 @@
>> +/*
>> + * 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/clkdev.h>
>> +#include <linux/clk/sunxi.h>
>> +#include <linux/of.h>
>> +#include <linux/of_address.h>
>> +
>> +#include "clk-factors.h"
>> +#include "clk-fixed-gate.h"
>> +
>> +static DEFINE_SPINLOCK(clk_lock);
>> +
>> +/**
>> + * sunxi_osc_clk_setup() - Setup function for gatable oscillator
>> + */
>> +
>> +#define SUNXI_OSC24M_GATE	0
>> +
>> +static void __init sunxi_osc_clk_setup(struct device_node *node)
>> +{
>> +	struct clk *clk;
>> +	const char *clk_name = node->name;
>> +	void *reg;
>> +	u32 rate;
>> +
>> +	reg = of_iomap(node, 0);
>> +
>> +	if (of_property_read_u32(node, "clock-frequency", &rate))
>> +		return;
>> +
>> +	clk = clk_register_fixed_gate(NULL, clk_name, NULL,
>> +				      CLK_IS_ROOT | CLK_IGNORE_UNUSED,
>> +				      reg, SUNXI_OSC24M_GATE, rate, &clk_lock);
>> +
>> +	if (clk) {
>> +		of_clk_add_provider(node, of_clk_src_simple_get, clk);
>> +		clk_register_clkdev(clk, clk_name, NULL);
>> +	}
>> +}
>> +
>> +
>> +
>> +/**
>> + * sunxi_get_pll1_factors() - calculates n, k, m, p factors for PLL1
>> + * PLL1 rate is calculated as follows
>> + * rate = (parent_rate * n * (k + 1) >> p) / (m + 1);
>> + * parent_rate is always 24Mhz
>> + */
>> +
>> +static void sunxi_get_pll1_factors(u32 *freq, u8 *n, u8 *k, u8 *m, u8 *p)
>> +{
>> +	u8 div;
>> +
>> +	/* Normalize value to a 6M multiple */
>> +	div = *freq / 6000000;
>> +	*freq = 6000000 * div;
>> +
>> +	/* we were called to round the frequency, we can now return */
>> +	if (n == NULL)
>> +		return;
>> +
>> +	/* m is always zero for pll1 */
>> +	*m = 0;
>> +
>> +	/* k is 1 only on these cases */
>> +	if (*freq >= 768000000 || *freq == 42000000 || *freq == 54000000)
>> +		*k = 1;
>> +	else
>> +		*k = 0;
>> +
>> +	/* p will be 3 for divs under 10 */
>> +	if (div < 10)
>> +		*p = 3;
>> +
>> +	/* p will be 2 for divs between 10 - 20 and odd divs under 32 */
>> +	else if (div < 20 || (div < 32 && (div & 1)))
>> +		*p = 2;
>> +
>> +	/* p will be 1 for even divs under 32, divs under 40 and odd pairs
>> +	 * of divs between 40-62 */
>> +	else if (div < 40 || (div < 64 && (div & 2)))
>> +		*p = 1;
>> +
>> +	/* any other entries have p = 0 */
>> +	else
>> +		*p = 0;
>> +
>> +	/* calculate a suitable n based on k and p */
>> +	div <<= *p;
>> +	div /= (*k + 1);
>> +	*n = div / 4;
>> +}
>> +
>> +/**
>> + * sunxi_pll1_clk_setup() - Setup function for PLL1 clock
>> + */
>> +
>> +struct clk_factors_config pll1_config = {
>> +	.nshift = 8,
>> +	.nwidth = 5,
>> +	.kshift = 4,
>> +	.kwidth = 2,
>> +	.mshift = 0,
>> +	.mwidth = 2,
>> +	.pshift = 16,
>> +	.pwidth = 2,
>> +};
>> +
>> +static void __init sunxi_pll1_clk_setup(struct device_node *node)
>> +{
>> +	struct clk *clk;
>> +	const char *clk_name = node->name;
>> +	const char *parent;
>> +	void *reg;
>> +
>> +	reg = of_iomap(node, 0);
>> +
>> +	parent = of_clk_get_parent_name(node, 0);
>> +
>> +	clk = clk_register_factors(NULL, clk_name, parent, CLK_IGNORE_UNUSED,
>> +				   reg, &pll1_config, sunxi_get_pll1_factors,
>> +				   &clk_lock);
>> +
>> +	if (clk) {
>> +		of_clk_add_provider(node, of_clk_src_simple_get, clk);
>> +		clk_register_clkdev(clk, clk_name, NULL);
>> +	}
>> +}
>> +
>> +
>> +
>> +/**
>> + * sunxi_cpu_clk_setup() - Setup function for CPU mux
>> + */
>> +
>> +#define SUNXI_CPU_GATE		16
>> +#define SUNXI_CPU_GATE_WIDTH	2
>> +
>> +static void __init sunxi_cpu_clk_setup(struct device_node *node)
>> +{
>> +	struct clk *clk;
>> +	const char *clk_name = node->name;
>> +	const char **parents = kmalloc(sizeof(char *) * 5, GFP_KERNEL);
>> +	void *reg;
>> +	int i = 0;
>> +
>> +	reg = of_iomap(node, 0);
>> +
>> +	while (i < 5 && (parents[i] = of_clk_get_parent_name(node, i)) != NULL)
>> +		i++;
>> +
>> +	clk = clk_register_mux(NULL, clk_name, parents, i, 0, reg,
>> +			       SUNXI_CPU_GATE, SUNXI_CPU_GATE_WIDTH,
>> +			       0, &clk_lock);
>> +
>> +	if (clk) {
>> +		of_clk_add_provider(node, of_clk_src_simple_get, clk);
>> +		clk_register_clkdev(clk, clk_name, NULL);
>> +	}
>> +}
>> +
>> +
>> +
>> +/**
>> + * sunxi_divider_clk_setup() - Setup function for simple divider clocks
>> + */
>> +
>> +#define SUNXI_DIVISOR_WIDTH	2
>> +
>> +struct div_data {
>> +	u8 div;
>> +	u8 pow;
>> +};
>> +
>> +static const __initconst struct div_data axi_data = {
>> +	.div = 0,
>> +	.pow = 0,
>> +};
>> +
>> +static const __initconst struct div_data ahb_data = {
>> +	.div = 4,
>> +	.pow = 1,
>> +};
>> +
>> +static const __initconst struct div_data apb0_data = {
>> +	.div = 8,
>> +	.pow = 1,
>> +};
> 
> Can't you read this from a register?

I don't understand your question. "div" is the shift in the register
where the divisor gets configured. width is always 2 as indicated by the
compiler macro above.

> 
> If not then this 3 clocks are good candidates for my patch:
> "clk: add DT fixed-factor-clock binding support"
> http://www.spinics.net/lists/arm-kernel/msg191522.html

These are configurable dividers; fixed-factor appears to be for clocks
where div and mult are fixed values.

> 
> It was not merged by lack of user, but it will be pretty simple to me
> to rebase it on top of 3.8-rc6
> 
> So if it is really an informatilon you can only get from the datasheet,
> you will be able to just declare it in the DTS.
> 
>> +
>> +static void __init sunxi_divider_clk_setup(struct device_node *node, u8 shift,
>> +					   u8 power_of_two)

See here, .div is shift and .pow is power_of_two

>> +{
>> +	struct clk *clk;
>> +	const char *clk_name = node->name;
>> +	const char *clk_parent;
>> +	void *reg;
>> +
>> +	reg = of_iomap(node, 0);
>> +
>> +	clk_parent = of_clk_get_parent_name(node, 0);
>> +
>> +	clk = clk_register_divider(NULL, clk_name, clk_parent, 0,
>> +				   reg, shift, SUNXI_DIVISOR_WIDTH,
>> +				   power_of_two ? CLK_DIVIDER_POWER_OF_TWO : 0,
>> +				   &clk_lock);
>> +	if (clk) {
>> +		of_clk_add_provider(node, of_clk_src_simple_get, clk);
>> +		clk_register_clkdev(clk, clk_name, NULL);
>> +	}
>> +}
>> +
>> +
>> +/* Matches for of_clk_init */
>> +static const __initconst struct of_device_id clk_match[] = {
>> +	{.compatible = "fixed-clock", .data = of_fixed_clk_setup,},
>> +	{.compatible = "allwinner,sunxi-osc-clk", .data = sunxi_osc_clk_setup,},
>> +	{.compatible = "allwinner,sunxi-pll1-clk", .data = sunxi_pll1_clk_setup,},
>> +	{.compatible = "allwinner,sunxi-cpu-clk", .data = sunxi_cpu_clk_setup,},
>> +	{}
>> +};
>> +
>> +/* Matches for divider clocks */
>> +static const __initconst struct of_device_id clk_div_match[] = {
>> +	{.compatible = "allwinner,sunxi-axi-clk", .data = &axi_data,},
>> +	{.compatible = "allwinner,sunxi-ahb-clk", .data = &ahb_data,},
>> +	{.compatible = "allwinner,sunxi-apb0-clk", .data = &apb0_data,},
>> +	{}
>> +};
>> +
>> +static void __init of_sunxi_divider_clock_setup(void)
>> +{
>> +	struct device_node *np;
>> +	const struct div_data *data;
>> +	const struct of_device_id *match;
>> +
>> +	for_each_matching_node(np, clk_div_match) {
>> +		match = of_match_node(clk_div_match, np);
>> +		data = match->data;
>> +		sunxi_divider_clk_setup(np, data->div, data->pow);
>> +	}
>> +}
>> +
>> +void __init sunxi_init_clocks(void)
>> +{
>> +	/* Register all the simple sunxi clocks on DT */
>> +	of_clk_init(clk_match);
>> +
>> +	/* Register divider clocks */
>> +	of_sunxi_divider_clock_setup();
>> +}
>>

Thanks for the review :)

Emilio

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

* [PATCH 1/3] clk: arm: sunxi: Add a new clock driver for sunxi SOCs
  2013-02-08 17:41     ` Emilio López
@ 2013-02-08 17:51       ` Gregory CLEMENT
  0 siblings, 0 replies; 26+ messages in thread
From: Gregory CLEMENT @ 2013-02-08 17:51 UTC (permalink / raw)
  To: linux-arm-kernel

On 02/08/2013 06:41 PM, Emilio L?pez wrote:
> Hello Gregory,
> 
> El 08/02/13 08:33, Gregory CLEMENT escribi?:
>> Hi Emilio,
>>
>> This is the last part of my review, I didn't takt time to review the
>> whole patch the last time. As usual, my comments are inline
>>
>> On 01/22/2013 07:12 AM, Emilio L?pez wrote:
>>
>>> This commit implements the base CPU clocks for sunxi devices. It has
>>> been tested using a slightly modified cpufreq driver from the
>>> linux-sunxi 3.0 tree.
>>>
>>> Additionally, document the new bindings introduced by this patch, and drop
>>> the (now unused) old sunxi clock driver.
>>>
>>> Idling:
>>>     / # find /sys/kernel/debug/clk -name clk_rate -print -exec cat {} \;
>>>     /sys/kernel/debug/clk/osc24M/pll1/cpu/axi/ahb/apb0/clk_rate
>>>     30000000
>>>     /sys/kernel/debug/clk/osc24M/pll1/cpu/axi/ahb/clk_rate
>>>     60000000
>>>     /sys/kernel/debug/clk/osc24M/pll1/cpu/axi/clk_rate
>>>     60000000
>>>     /sys/kernel/debug/clk/osc24M/pll1/cpu/clk_rate
>>>     60000000
>>>     /sys/kernel/debug/clk/osc24M/pll1/clk_rate
>>>     60000000
>>>     /sys/kernel/debug/clk/osc24M/clk_rate
>>>     24000000
>>>     /sys/kernel/debug/clk/osc32k/clk_rate
>>>     32768
>>>
>>> After "yes >/dev/null &":
>>>     / # find /sys/kernel/debug/clk -name clk_rate -print -exec cat {} \;
>>>     /sys/kernel/debug/clk/osc24M/pll1/cpu/axi/ahb/apb0/clk_rate
>>>     84000000
>>>     /sys/kernel/debug/clk/osc24M/pll1/cpu/axi/ahb/clk_rate
>>>     168000000
>>>     /sys/kernel/debug/clk/osc24M/pll1/cpu/axi/clk_rate
>>>     336000000
>>>     /sys/kernel/debug/clk/osc24M/pll1/cpu/clk_rate
>>>     1008000000
>>>     /sys/kernel/debug/clk/osc24M/pll1/clk_rate
>>>     1008000000
>>>     /sys/kernel/debug/clk/osc24M/clk_rate
>>>     24000000
>>>     /sys/kernel/debug/clk/osc32k/clk_rate
>>>     32768
>>>
>>> Signed-off-by: Emilio L?pez <emilio@elopez.com.ar>
>>> ---
>>>  Documentation/devicetree/bindings/clock/sunxi.txt |  47 ++++
>>>  drivers/clk/Makefile                              |   2 +-
>>>  drivers/clk/clk-sunxi.c                           |  30 ---
>>>  drivers/clk/sunxi/Makefile                        |   5 +
>>>  drivers/clk/sunxi/clk-factors.c                   | 175 ++++++++++++++
>>>  drivers/clk/sunxi/clk-factors.h                   |  25 ++
>>>  drivers/clk/sunxi/clk-fixed-gate.c                | 152 +++++++++++++
>>>  drivers/clk/sunxi/clk-fixed-gate.h                |  13 ++
>>>  drivers/clk/sunxi/clk-sunxi.c                     | 265 ++++++++++++++++++++++
>>>  9 files changed, 683 insertions(+), 31 deletions(-)
>>>  create mode 100644 Documentation/devicetree/bindings/clock/sunxi.txt
>>>  delete mode 100644 drivers/clk/clk-sunxi.c
>>>  create mode 100644 drivers/clk/sunxi/Makefile
>>>  create mode 100644 drivers/clk/sunxi/clk-factors.c
>>>  create mode 100644 drivers/clk/sunxi/clk-factors.h
>>>  create mode 100644 drivers/clk/sunxi/clk-fixed-gate.c
>>>  create mode 100644 drivers/clk/sunxi/clk-fixed-gate.h
>>>  create mode 100644 drivers/clk/sunxi/clk-sunxi.c
>>>
>>> diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt
>>> new file mode 100644
>>> index 0000000..446c5ca
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/clock/sunxi.txt
>>> @@ -0,0 +1,47 @@
>>> +Device Tree Clock bindings for arch-sunxi
>>> +
>>> +This binding uses the common clock binding[1].
>>> +
>>> +[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
>>> +
>>> +Required properties:
>>> +- compatible : shall be one of the following:
>>> +	"allwinner,sunxi-osc-clk" - for a gatable oscillator
>>> +	"allwinner,sunxi-pll1-clk" - for the main PLL clock
>>> +	"allwinner,sunxi-cpu-clk" - for the CPU multiplexer clock
>>> +	"allwinner,sunxi-axi-clk" - for the sunxi AXI clock
>>> +	"allwinner,sunxi-ahb-clk" - for the sunxi AHB clock
>>> +	"allwinner,sunxi-apb0-clk" - for the sunxi APB0 clock
>>> +
>>> +Required properties for all clocks:
>>> +- reg : shall be the control register address for the clock.
>>> +- clocks : shall be the input parent clock(s) phandle for the clock, except for
>>> +	the root gatable oscillator clock where it is not present
>>> +- #clock-cells : from common clock binding; shall be set to 0.
>>> +
>>> +Additionally, the gatable oscillator clock requires:
>>> +- clock-frequency : shall be the frequency of the oscillator.
>>> +
>>> +
>>> +For example:
>>> +
>>> +osc24M: osc24M {
>>> +	#clock-cells = <0>;
>>> +	compatible = "allwinner,sunxi-osc-clk";
>>> +	reg = <0x01c20050 0x4>;
>>> +	clock-frequency = <24000000>;
>>> +};
>>> +
>>> +pll1: pll1 at 01c20000 {
>>> +	#clock-cells = <0>;
>>> +	compatible = "allwinner,sunxi-pll1-clk";
>>> +	reg = <0x01c20000 0x4>;
>>> +	clocks = <&osc24M>;
>>> +};
>>> +
>>> +cpu: cpu at 01c20054 {
>>> +	#clock-cells = <0>;
>>> +	compatible = "allwinner,sunxi-cpu-clk";
>>> +	reg = <0x01c20054 0x4>;
>>> +	clocks = <&osc32k>, <&osc24M>, <&pll1>;
>>> +};
>>> diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
>>> index ee90e87..129afed 100644
>>> --- a/drivers/clk/Makefile
>>> +++ b/drivers/clk/Makefile
>>> @@ -20,7 +20,7 @@ endif
>>>  obj-$(CONFIG_MACH_LOONGSON1)	+= clk-ls1x.o
>>>  obj-$(CONFIG_ARCH_U8500)	+= ux500/
>>>  obj-$(CONFIG_ARCH_VT8500)	+= clk-vt8500.o
>>> -obj-$(CONFIG_ARCH_SUNXI)	+= clk-sunxi.o
>>> +obj-$(CONFIG_ARCH_SUNXI)	+= sunxi/
>>>  obj-$(CONFIG_ARCH_ZYNQ)		+= clk-zynq.o
>>>  
>>>  # Chip specific
>>> diff --git a/drivers/clk/clk-sunxi.c b/drivers/clk/clk-sunxi.c
>>> deleted file mode 100644
>>> index 0e831b5..0000000
>>> --- a/drivers/clk/clk-sunxi.c
>>> +++ /dev/null
>>> @@ -1,30 +0,0 @@
>>> -/*
>>> - * Copyright 2012 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/clkdev.h>
>>> -#include <linux/clk/sunxi.h>
>>> -#include <linux/of.h>
>>> -
>>> -static const __initconst struct of_device_id clk_match[] = {
>>> -	{ .compatible = "fixed-clock", .data = of_fixed_clk_setup, },
>>> -	{}
>>> -};
>>> -
>>> -void __init sunxi_init_clocks(void)
>>> -{
>>> -	of_clk_init(clk_match);
>>> -}
>>> diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile
>>> new file mode 100644
>>> index 0000000..8e773be
>>> --- /dev/null
>>> +++ b/drivers/clk/sunxi/Makefile
>>> @@ -0,0 +1,5 @@
>>> +#
>>> +# Makefile for sunxi specific clk
>>> +#
>>> +
>>> +obj-y += clk-sunxi.o clk-factors.o clk-fixed-gate.o
>>> diff --git a/drivers/clk/sunxi/clk-factors.c b/drivers/clk/sunxi/clk-factors.c
>>> new file mode 100644
>>> index 0000000..428b47d
>>> --- /dev/null
>>> +++ b/drivers/clk/sunxi/clk-factors.c
>>> @@ -0,0 +1,175 @@
>>> +/*
>>> + * Copyright (C) 2013 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 version 2 as
>>> + * published by the Free Software Foundation.
>>> + *
>>> + * Adjustable factor-based clock implementation
>>> + */
>>> +
>>> +#include <linux/clk-provider.h>
>>> +#include <linux/module.h>
>>> +#include <linux/slab.h>
>>> +#include <linux/io.h>
>>> +#include <linux/err.h>
>>> +#include <linux/string.h>
>>> +
>>> +#include <linux/delay.h>
>>> +
>>> +#include "clk-factors.h"
>>> +
>>> +/*
>>> + * DOC: basic adjustable factor-based clock that cannot gate
>>> + *
>>> + * Traits of this clock:
>>> + * prepare - clk_prepare only ensures that parents are prepared
>>> + * enable - clk_enable only ensures that parents are enabled
>>> + * rate - rate is adjustable.
>>> + *        clk->rate = (parent->rate * N * (K + 1) >> P) / (M + 1)
>>> + * parent - fixed parent.  No clk_set_parent support
>>> + */
>>> +
>>> +struct clk_factors {
>>> +	struct clk_hw hw;
>>> +	void __iomem *reg;
>>> +	struct clk_factors_config *config;
>>> +	void (*get_factors) (u32 *rate, u8 *n, u8 *k, u8 *m, u8 *p);
>>> +	spinlock_t *lock;
>>> +};
>>> +
>>> +#define to_clk_factors(_hw) container_of(_hw, struct clk_factors, hw)
>>> +
>>> +#define SETMASK(len, pos)		(((-1U) >> (31-len))  << (pos))
>>> +#define CLRMASK(len, pos)		(~(SETMASK(len, pos)))
>>> +#define FACTOR_GET(bit, len, reg)	(((reg) & SETMASK(len, bit)) >> (bit))
>>> +
>>> +#define FACTOR_SET(bit, len, reg, val) \
>>> +	(((reg) & CLRMASK(len, bit)) | (val << (bit)))
>>> +
>>> +static unsigned long clk_factors_recalc_rate(struct clk_hw *hw,
>>> +					     unsigned long parent_rate)
>>> +{
>>> +	u8 n, k, p, m;
>>> +	u32 reg;
>>> +	unsigned long rate;
>>> +	struct clk_factors *factors = to_clk_factors(hw);
>>> +	struct clk_factors_config *config = factors->config;
>>> +
>>> +	/* Fetch the register value */
>>> +	reg = readl(factors->reg);
>>> +
>>> +	/* Get each individual factor */
>>> +	n = FACTOR_GET(config->nshift, config->nwidth, reg);
>>> +	k = FACTOR_GET(config->kshift, config->kwidth, reg);
>>> +	m = FACTOR_GET(config->mshift, config->mwidth, reg);
>>> +	p = FACTOR_GET(config->pshift, config->pwidth, reg);
>>> +
>>> +	/* Calculate the rate */
>>> +	rate = (parent_rate * n * (k + 1) >> p) / (m + 1);
>>> +
>>> +	return rate;
>>> +}
>>> +
>>> +static long clk_factors_round_rate(struct clk_hw *hw, unsigned long rate,
>>> +				   unsigned long *parent_rate)
>>> +{
>>> +	struct clk_factors *factors = to_clk_factors(hw);
>>> +	factors->get_factors((u32 *)&rate, NULL, NULL, NULL, NULL);
>>> +
>>> +	return rate;
>>> +}
>>> +
>>> +static int clk_factors_set_rate(struct clk_hw *hw, unsigned long rate,
>>> +				unsigned long parent_rate)
>>> +{
>>> +	u8 n, k, m, p;
>>> +	u32 reg;
>>> +	struct clk_factors *factors = to_clk_factors(hw);
>>> +	struct clk_factors_config *config = factors->config;
>>> +	unsigned long flags = 0;
>>> +
>>> +	factors->get_factors((u32 *)&rate, &n, &k, &m, &p);
>>> +
>>> +	if (factors->lock)
>>> +		spin_lock_irqsave(factors->lock, flags);
>>> +
>>> +	/* Fetch the register value */
>>> +	reg = readl(factors->reg);
>>> +
>>> +	/* Set up the new factors */
>>> +	reg = FACTOR_SET(config->nshift, config->nwidth, reg, n);
>>> +	reg = FACTOR_SET(config->kshift, config->kwidth, reg, k);
>>> +	reg = FACTOR_SET(config->mshift, config->mwidth, reg, m);
>>> +	reg = FACTOR_SET(config->pshift, config->pwidth, reg, p);
>>> +
>>> +	/* Apply them now */
>>> +	writel(reg, factors->reg);
>>> +
>>> +	/* delay 500us so pll stabilizes */
>>> +	__delay((rate >> 20) * 500 / 2);
>>> +
>>> +	if (factors->lock)
>>> +		spin_unlock_irqrestore(factors->lock, flags);
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static const struct clk_ops clk_factors_ops = {
>>> +	.recalc_rate = clk_factors_recalc_rate,
>>> +	.round_rate = clk_factors_round_rate,
>>> +	.set_rate = clk_factors_set_rate,
>>> +};
>>> +
>>> +/**
>>> + * clk_register_factors - register a factors clock with
>>> + * the clock framework
>>> + * @dev: device registering this clock
>>> + * @name: name of this clock
>>> + * @parent_name: name of clock's parent
>>> + * @flags: framework-specific flags
>>> + * @reg: register address to adjust factors
>>> + * @config: shift and width of factors n, k, m and p
>>> + * @get_factors: function to calculate the factors for a given frequency
>>> + * @lock: shared register lock for this clock
>>> + */
>>
>> Do you have other "factors" clock than sunxi-pll1-clk?
>> On your patch I only see this clock using this, and the way
>> you pass a function  and a the config struct seems to me
>> overcomplicated for a sinlge clock.
> 
> Yes, there are other "factors" clocks on sunxi hardware; I have not
> added them to the driver yet, but having the factors implementation
> there makes it trivial to represent them on a future patchset.

OK, in this case it make sens to have this.

> 
>>
>>> +struct clk *clk_register_factors(struct device *dev, const char *name,
>>> +				 const char *parent_name,
>>> +				 unsigned long flags, void __iomem *reg,
>>> +				 struct clk_factors_config *config,
>>> +				 void (*get_factors) (u32 *rate, u8 *n, u8 *k,
>>> +						      u8 *m, u8 *p),
>>> +				 spinlock_t *lock)
>>> +{
>>> +	struct clk_factors *factors;
>>> +	struct clk *clk;
>>> +	struct clk_init_data init;
>>> +
>>> +	/* allocate the factors */
>>> +	factors = kzalloc(sizeof(struct clk_factors), GFP_KERNEL);
>>> +	if (!factors) {
>>> +		pr_err("%s: could not allocate factors clk\n", __func__);
>>> +		return ERR_PTR(-ENOMEM);
>>> +	}
>>> +
>>> +	init.name = name;
>>> +	init.ops = &clk_factors_ops;
>>> +	init.flags = flags;
>>> +	init.parent_names = (parent_name ? &parent_name : NULL);
>>> +	init.num_parents = (parent_name ? 1 : 0);
>>> +
>>> +	/* struct clk_factors assignments */
>>> +	factors->reg = reg;
>>> +	factors->config = config;
>>> +	factors->lock = lock;
>>> +	factors->hw.init = &init;
>>> +	factors->get_factors = get_factors;
>>> +
>>> +	/* register the clock */
>>> +	clk = clk_register(dev, &factors->hw);
>>> +
>>> +	if (IS_ERR(clk))
>>> +		kfree(factors);
>>> +
>>> +	return clk;
>>> +}
>>> diff --git a/drivers/clk/sunxi/clk-factors.h b/drivers/clk/sunxi/clk-factors.h
>>> new file mode 100644
>>> index 0000000..a24c889
>>> --- /dev/null
>>> +++ b/drivers/clk/sunxi/clk-factors.h
>>> @@ -0,0 +1,25 @@
>>> +#ifndef __MACH_SUNXI_CLK_FACTORS_H
>>> +#define __MACH_SUNXI_CLK_FACTORS_H
>>> +
>>> +#include <linux/clk-provider.h>
>>> +#include <linux/clkdev.h>
>>> +
>>> +struct clk_factors_config {
>>> +	u8 nshift;
>>> +	u8 nwidth;
>>> +	u8 kshift;
>>> +	u8 kwidth;
>>> +	u8 mshift;
>>> +	u8 mwidth;
>>> +	u8 pshift;
>>> +	u8 pwidth;
>>> +};
>>> +
>>> +struct clk *clk_register_factors(struct device *dev, const char *name,
>>> +				 const char *parent_name,
>>> +				 unsigned long flags, void __iomem *reg,
>>> +				 struct clk_factors_config *config,
>>> +				 void (*get_factors) (u32 *rate, u8 *n, u8 *k,
>>> +						      u8 *m, u8 *p),
>>> +				 spinlock_t *lock);
>>> +#endif
>>> diff --git a/drivers/clk/sunxi/clk-fixed-gate.c b/drivers/clk/sunxi/clk-fixed-gate.c
>>> new file mode 100644
>>> index 0000000..b16eda5
>>> --- /dev/null
>>> +++ b/drivers/clk/sunxi/clk-fixed-gate.c
>>> @@ -0,0 +1,152 @@
>>> +/*
>>> + * Copyright (C) 2013 Emilio L?pez <emilio@elopez.com.ar>
>>> + *
>>> + * Based on drivers/clk/clk-gate.c,
>>> + *
>>> + * Copyright (C) 2010-2011 Canonical Ltd <jeremy.kerr@canonical.com>
>>> + * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org>
>>> + *
>>> + * 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.
>>> + *
>>> + * Fixed rate, gated clock implementation
>>> + */
>>> +
>>> +#include <linux/clk-provider.h>
>>> +#include <linux/module.h>
>>> +#include <linux/slab.h>
>>> +#include <linux/io.h>
>>> +#include <linux/err.h>
>>> +#include <linux/string.h>
>>> +
>>> +#include "clk-fixed-gate.h"
>>> +
>>> +/**
>>> + * DOC: fixed rate clock which can gate and ungate it's ouput
>>> + *
>>> + * Traits of this clock:
>>> + * prepare - clk_(un)prepare only ensures parent is (un)prepared
>>> + * enable - clk_enable and clk_disable are functional & control gating
>>> + * rate - rate is always a fixed value.  No clk_set_rate support
>>> + * parent - fixed parent.  No clk_set_parent support
>>> + */
>>> +
>>> +struct clk_fixed_gate {
>>> +	struct clk_hw hw;
>>> +	u8            bit_idx;
>>> +	u8            flags;
>>> +	unsigned long fixed_rate;
>>> +	void __iomem  *reg;
>>> +	spinlock_t    *lock;
>>> +};
>>> +
>>> +#define to_clk_fixed_gate(_hw) container_of(_hw, struct clk_fixed_gate, hw)
>>> +
>>> +static void clk_fixed_gate_endisable(struct clk_hw *hw, int enable)
>>> +{
>>> +	struct clk_fixed_gate *gate = to_clk_fixed_gate(hw);
>>> +	unsigned long flags = 0;
>>> +	u32 reg;
>>> +
>>> +	if (gate->lock)
>>> +		spin_lock_irqsave(gate->lock, flags);
>>> +
>>> +	reg = readl(gate->reg);
>>> +
>>> +	if (enable)
>>> +		reg |= BIT(gate->bit_idx);
>>> +	else
>>> +		reg &= ~BIT(gate->bit_idx);
>>> +
>>> +	writel(reg, gate->reg);
>>> +
>>> +	if (gate->lock)
>>> +		spin_unlock_irqrestore(gate->lock, flags);
>>> +}
>>> +
>>> +static int clk_fixed_gate_enable(struct clk_hw *hw)
>>> +{
>>> +	clk_fixed_gate_endisable(hw, 1);
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static void clk_fixed_gate_disable(struct clk_hw *hw)
>>> +{
>>> +	clk_fixed_gate_endisable(hw, 0);
>>> +}
>>> +
>>> +static int clk_fixed_gate_is_enabled(struct clk_hw *hw)
>>> +{
>>> +	u32 reg;
>>> +	struct clk_fixed_gate *gate = to_clk_fixed_gate(hw);
>>> +
>>> +	reg = readl(gate->reg);
>>> +
>>> +	reg &= BIT(gate->bit_idx);
>>> +
>>> +	return reg ? 1 : 0;
>>> +}
>>> +
>>> +static unsigned long clk_fixed_gate_recalc_rate(struct clk_hw *hw,
>>> +						unsigned long parent_rate)
>>> +{
>>> +	return to_clk_fixed_gate(hw)->fixed_rate;
>>> +}
>>> +
>>> +static const struct clk_ops clk_fixed_gate_ops = {
>>> +	.enable = clk_fixed_gate_enable,
>>> +	.disable = clk_fixed_gate_disable,
>>> +	.is_enabled = clk_fixed_gate_is_enabled,
>>> +	.recalc_rate = clk_fixed_gate_recalc_rate,
>>> +};
>>> +
>>> +/**
>>> + * clk_register_fixed_gate - register a fixed rate,
>>> + * gate clock with the clock framework
>>> + * @dev: device that is registering this clock
>>> + * @name: name of this clock
>>> + * @parent_name: name of this clock's parent
>>> + * @flags: framework-specific flags for this clock
>>> + * @reg: register address to control gating of this clock
>>> + * @bit_idx: which bit in the register controls gating of this clock
>>> + * @lock: shared register lock for this clock
>>> + */
>>> +struct clk *clk_register_fixed_gate(struct device *dev, const char *name,
>>> +				    const char *parent_name,
>>> +				    unsigned long flags, void __iomem *reg,
>>> +				    u8 bit_idx, unsigned long fixed_rate,
>>> +				    spinlock_t *lock)
>>> +{
>>> +	struct clk_fixed_gate *gate;
>>> +	struct clk *clk;
>>> +	struct clk_init_data init;
>>> +
>>> +	/* allocate the gate */
>>> +	gate = kzalloc(sizeof(struct clk_fixed_gate), GFP_KERNEL);
>>> +	if (!gate) {
>>> +		pr_err("%s: could not allocate fixed gated clk\n", __func__);
>>> +		return ERR_PTR(-ENOMEM);
>>> +	}
>>> +
>>> +	init.name = name;
>>> +	init.ops = &clk_fixed_gate_ops;
>>> +	init.flags = flags | CLK_IS_BASIC;
>>> +	init.parent_names = (parent_name ? &parent_name : NULL);
>>> +	init.num_parents = (parent_name ? 1 : 0);
>>> +
>>> +	/* struct clk_fixed_gate assignments */
>>> +	gate->fixed_rate = fixed_rate;
>>> +	gate->reg = reg;
>>> +	gate->bit_idx = bit_idx;
>>> +	gate->lock = lock;
>>> +	gate->hw.init = &init;
>>> +
>>> +	clk = clk_register(dev, &gate->hw);
>>> +
>>> +	if (IS_ERR(clk))
>>> +		kfree(gate);
>>> +
>>> +	return clk;
>>> +}
>>> diff --git a/drivers/clk/sunxi/clk-fixed-gate.h b/drivers/clk/sunxi/clk-fixed-gate.h
>>> new file mode 100644
>>> index 0000000..29d9ed3
>>> --- /dev/null
>>> +++ b/drivers/clk/sunxi/clk-fixed-gate.h
>>> @@ -0,0 +1,13 @@
>>> +#ifndef __MACH_SUNXI_CLK_FIXED_GATE_H
>>> +#define __MACH_SUNXI_CLK_FIXED_GATE_H
>>> +
>>> +#include <linux/clk-provider.h>
>>> +#include <linux/clkdev.h>
>>> +
>>> +struct clk *clk_register_fixed_gate(struct device *dev, const char *name,
>>> +				    const char *parent_name,
>>> +				    unsigned long flags, void __iomem *reg,
>>> +				    u8 bit_idx, unsigned long fixed_rate,
>>> +				    spinlock_t *lock);
>>> +
>>> +#endif
>>> diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c
>>> new file mode 100644
>>> index 0000000..cb587a0
>>> --- /dev/null
>>> +++ b/drivers/clk/sunxi/clk-sunxi.c
>>> @@ -0,0 +1,265 @@
>>> +/*
>>> + * 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/clkdev.h>
>>> +#include <linux/clk/sunxi.h>
>>> +#include <linux/of.h>
>>> +#include <linux/of_address.h>
>>> +
>>> +#include "clk-factors.h"
>>> +#include "clk-fixed-gate.h"
>>> +
>>> +static DEFINE_SPINLOCK(clk_lock);
>>> +
>>> +/**
>>> + * sunxi_osc_clk_setup() - Setup function for gatable oscillator
>>> + */
>>> +
>>> +#define SUNXI_OSC24M_GATE	0
>>> +
>>> +static void __init sunxi_osc_clk_setup(struct device_node *node)
>>> +{
>>> +	struct clk *clk;
>>> +	const char *clk_name = node->name;
>>> +	void *reg;
>>> +	u32 rate;
>>> +
>>> +	reg = of_iomap(node, 0);
>>> +
>>> +	if (of_property_read_u32(node, "clock-frequency", &rate))
>>> +		return;
>>> +
>>> +	clk = clk_register_fixed_gate(NULL, clk_name, NULL,
>>> +				      CLK_IS_ROOT | CLK_IGNORE_UNUSED,
>>> +				      reg, SUNXI_OSC24M_GATE, rate, &clk_lock);
>>> +
>>> +	if (clk) {
>>> +		of_clk_add_provider(node, of_clk_src_simple_get, clk);
>>> +		clk_register_clkdev(clk, clk_name, NULL);
>>> +	}
>>> +}
>>> +
>>> +
>>> +
>>> +/**
>>> + * sunxi_get_pll1_factors() - calculates n, k, m, p factors for PLL1
>>> + * PLL1 rate is calculated as follows
>>> + * rate = (parent_rate * n * (k + 1) >> p) / (m + 1);
>>> + * parent_rate is always 24Mhz
>>> + */
>>> +
>>> +static void sunxi_get_pll1_factors(u32 *freq, u8 *n, u8 *k, u8 *m, u8 *p)
>>> +{
>>> +	u8 div;
>>> +
>>> +	/* Normalize value to a 6M multiple */
>>> +	div = *freq / 6000000;
>>> +	*freq = 6000000 * div;
>>> +
>>> +	/* we were called to round the frequency, we can now return */
>>> +	if (n == NULL)
>>> +		return;
>>> +
>>> +	/* m is always zero for pll1 */
>>> +	*m = 0;
>>> +
>>> +	/* k is 1 only on these cases */
>>> +	if (*freq >= 768000000 || *freq == 42000000 || *freq == 54000000)
>>> +		*k = 1;
>>> +	else
>>> +		*k = 0;
>>> +
>>> +	/* p will be 3 for divs under 10 */
>>> +	if (div < 10)
>>> +		*p = 3;
>>> +
>>> +	/* p will be 2 for divs between 10 - 20 and odd divs under 32 */
>>> +	else if (div < 20 || (div < 32 && (div & 1)))
>>> +		*p = 2;
>>> +
>>> +	/* p will be 1 for even divs under 32, divs under 40 and odd pairs
>>> +	 * of divs between 40-62 */
>>> +	else if (div < 40 || (div < 64 && (div & 2)))
>>> +		*p = 1;
>>> +
>>> +	/* any other entries have p = 0 */
>>> +	else
>>> +		*p = 0;
>>> +
>>> +	/* calculate a suitable n based on k and p */
>>> +	div <<= *p;
>>> +	div /= (*k + 1);
>>> +	*n = div / 4;
>>> +}
>>> +
>>> +/**
>>> + * sunxi_pll1_clk_setup() - Setup function for PLL1 clock
>>> + */
>>> +
>>> +struct clk_factors_config pll1_config = {
>>> +	.nshift = 8,
>>> +	.nwidth = 5,
>>> +	.kshift = 4,
>>> +	.kwidth = 2,
>>> +	.mshift = 0,
>>> +	.mwidth = 2,
>>> +	.pshift = 16,
>>> +	.pwidth = 2,
>>> +};
>>> +
>>> +static void __init sunxi_pll1_clk_setup(struct device_node *node)
>>> +{
>>> +	struct clk *clk;
>>> +	const char *clk_name = node->name;
>>> +	const char *parent;
>>> +	void *reg;
>>> +
>>> +	reg = of_iomap(node, 0);
>>> +
>>> +	parent = of_clk_get_parent_name(node, 0);
>>> +
>>> +	clk = clk_register_factors(NULL, clk_name, parent, CLK_IGNORE_UNUSED,
>>> +				   reg, &pll1_config, sunxi_get_pll1_factors,
>>> +				   &clk_lock);
>>> +
>>> +	if (clk) {
>>> +		of_clk_add_provider(node, of_clk_src_simple_get, clk);
>>> +		clk_register_clkdev(clk, clk_name, NULL);
>>> +	}
>>> +}
>>> +
>>> +
>>> +
>>> +/**
>>> + * sunxi_cpu_clk_setup() - Setup function for CPU mux
>>> + */
>>> +
>>> +#define SUNXI_CPU_GATE		16
>>> +#define SUNXI_CPU_GATE_WIDTH	2
>>> +
>>> +static void __init sunxi_cpu_clk_setup(struct device_node *node)
>>> +{
>>> +	struct clk *clk;
>>> +	const char *clk_name = node->name;
>>> +	const char **parents = kmalloc(sizeof(char *) * 5, GFP_KERNEL);
>>> +	void *reg;
>>> +	int i = 0;
>>> +
>>> +	reg = of_iomap(node, 0);
>>> +
>>> +	while (i < 5 && (parents[i] = of_clk_get_parent_name(node, i)) != NULL)
>>> +		i++;
>>> +
>>> +	clk = clk_register_mux(NULL, clk_name, parents, i, 0, reg,
>>> +			       SUNXI_CPU_GATE, SUNXI_CPU_GATE_WIDTH,
>>> +			       0, &clk_lock);
>>> +
>>> +	if (clk) {
>>> +		of_clk_add_provider(node, of_clk_src_simple_get, clk);
>>> +		clk_register_clkdev(clk, clk_name, NULL);
>>> +	}
>>> +}
>>> +
>>> +
>>> +
>>> +/**
>>> + * sunxi_divider_clk_setup() - Setup function for simple divider clocks
>>> + */
>>> +
>>> +#define SUNXI_DIVISOR_WIDTH	2
>>> +
>>> +struct div_data {
>>> +	u8 div;
>>> +	u8 pow;
>>> +};
>>> +
>>> +static const __initconst struct div_data axi_data = {
>>> +	.div = 0,
>>> +	.pow = 0,
>>> +};
>>> +
>>> +static const __initconst struct div_data ahb_data = {
>>> +	.div = 4,
>>> +	.pow = 1,
>>> +};
>>> +
>>> +static const __initconst struct div_data apb0_data = {
>>> +	.div = 8,
>>> +	.pow = 1,
>>> +};
>>
>> Can't you read this from a register?
> 
> I don't understand your question. "div" is the shift in the register
> where the divisor gets configured. width is always 2 as indicated by the
> compiler macro above.

My bad, I went to fast on this one.

> 
>>
>> If not then this 3 clocks are good candidates for my patch:
>> "clk: add DT fixed-factor-clock binding support"
>> http://www.spinics.net/lists/arm-kernel/msg191522.html
> 
> These are configurable dividers; fixed-factor appears to be for clocks
> where div and mult are fixed values.
> 
>>
>> It was not merged by lack of user, but it will be pretty simple to me
>> to rebase it on top of 3.8-rc6
>>
>> So if it is really an informatilon you can only get from the datasheet,
>> you will be able to just declare it in the DTS.
>>
>>> +
>>> +static void __init sunxi_divider_clk_setup(struct device_node *node, u8 shift,
>>> +					   u8 power_of_two)
> 
> See here, .div is shift and .pow is power_of_two
> 
>>> +{
>>> +	struct clk *clk;
>>> +	const char *clk_name = node->name;
>>> +	const char *clk_parent;
>>> +	void *reg;
>>> +
>>> +	reg = of_iomap(node, 0);
>>> +
>>> +	clk_parent = of_clk_get_parent_name(node, 0);
>>> +
>>> +	clk = clk_register_divider(NULL, clk_name, clk_parent, 0,
>>> +				   reg, shift, SUNXI_DIVISOR_WIDTH,
>>> +				   power_of_two ? CLK_DIVIDER_POWER_OF_TWO : 0,
>>> +				   &clk_lock);
>>> +	if (clk) {
>>> +		of_clk_add_provider(node, of_clk_src_simple_get, clk);
>>> +		clk_register_clkdev(clk, clk_name, NULL);
>>> +	}
>>> +}
>>> +
>>> +
>>> +/* Matches for of_clk_init */
>>> +static const __initconst struct of_device_id clk_match[] = {
>>> +	{.compatible = "fixed-clock", .data = of_fixed_clk_setup,},
>>> +	{.compatible = "allwinner,sunxi-osc-clk", .data = sunxi_osc_clk_setup,},
>>> +	{.compatible = "allwinner,sunxi-pll1-clk", .data = sunxi_pll1_clk_setup,},
>>> +	{.compatible = "allwinner,sunxi-cpu-clk", .data = sunxi_cpu_clk_setup,},
>>> +	{}
>>> +};
>>> +
>>> +/* Matches for divider clocks */
>>> +static const __initconst struct of_device_id clk_div_match[] = {
>>> +	{.compatible = "allwinner,sunxi-axi-clk", .data = &axi_data,},
>>> +	{.compatible = "allwinner,sunxi-ahb-clk", .data = &ahb_data,},
>>> +	{.compatible = "allwinner,sunxi-apb0-clk", .data = &apb0_data,},
>>> +	{}
>>> +};
>>> +
>>> +static void __init of_sunxi_divider_clock_setup(void)
>>> +{
>>> +	struct device_node *np;
>>> +	const struct div_data *data;
>>> +	const struct of_device_id *match;
>>> +
>>> +	for_each_matching_node(np, clk_div_match) {
>>> +		match = of_match_node(clk_div_match, np);
>>> +		data = match->data;
>>> +		sunxi_divider_clk_setup(np, data->div, data->pow);
>>> +	}
>>> +}
>>> +
>>> +void __init sunxi_init_clocks(void)
>>> +{
>>> +	/* Register all the simple sunxi clocks on DT */
>>> +	of_clk_init(clk_match);
>>> +
>>> +	/* Register divider clocks */
>>> +	of_sunxi_divider_clock_setup();
>>> +}
>>>
> 
> Thanks for the review :)

You're welcome :)

Gregory

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

* [PATCH v2 0/3] clock driver for sunxi
  2013-01-22  6:12 [PATCH 0/3] clock driver for sunxi Emilio López
                   ` (4 preceding siblings ...)
  2013-02-04 22:03 ` Maxime Ripard
@ 2013-02-25 14:44 ` Emilio López
  2013-02-25 14:44   ` [PATCH v2 1/3] clk: arm: sunxi: Add a new clock driver for sunxi SOCs Emilio López
                     ` (5 more replies)
  5 siblings, 6 replies; 26+ messages in thread
From: Emilio López @ 2013-02-25 14:44 UTC (permalink / raw)
  To: linux-arm-kernel

Hello everyone,

This patchset adds basic clock support for sunxi devices. Currently, it
implements support for the two oscillators, the main PLL, the CPU mux,
its three divisor clocks and APB1. With this in place, it is possible to
write a cpufreq driver and have it work, and with a little more code,
represent the UART clocks correctly (I will be sending a separate
patchset for this).

I have tested this driver successfully on a Cubieboard (A10, sun4i)
using the cpufreq driver from the linux-sunxi tree after minor
modifications (the clock names are not the same).

Any feedback will be highly appreciated

Thanks,

Emilio

Overview of changes from v1:
  * Implement APB1
  * Add missing glue for driver that got dropped on torvalds/master
  * Drop "fixed gate" code and implement it with two clocks as suggested
    by Gregory

Emilio L?pez (3):
  clk: arm: sunxi: Add a new clock driver for sunxi SOCs
  arm: sunxi: Add clock definitions for the new clock driver
  arm: sunxi: Add useful information about sunxi clocks

 Documentation/arm/sunxi/clocks.txt                |  56 ++++
 Documentation/devicetree/bindings/clock/sunxi.txt |  44 +++
 arch/arm/boot/dts/sunxi.dtsi                      |  83 ++++-
 drivers/clk/Makefile                              |   1 +
 drivers/clk/sunxi/Makefile                        |   5 +
 drivers/clk/sunxi/clk-factors.c                   | 180 +++++++++++
 drivers/clk/sunxi/clk-factors.h                   |  27 ++
 drivers/clk/sunxi/clk-sunxi.c                     | 362 ++++++++++++++++++++++
 drivers/clocksource/sunxi_timer.c                 |   4 +-
 include/linux/clk/sunxi.h                         |  22 ++
 10 files changed, 779 insertions(+), 5 deletions(-)
 create mode 100644 Documentation/arm/sunxi/clocks.txt
 create mode 100644 Documentation/devicetree/bindings/clock/sunxi.txt
 create mode 100644 drivers/clk/sunxi/Makefile
 create mode 100644 drivers/clk/sunxi/clk-factors.c
 create mode 100644 drivers/clk/sunxi/clk-factors.h
 create mode 100644 drivers/clk/sunxi/clk-sunxi.c
 create mode 100644 include/linux/clk/sunxi.h

-- 
1.8.2.rc0

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

* [PATCH v2 1/3] clk: arm: sunxi: Add a new clock driver for sunxi SOCs
  2013-02-25 14:44 ` [PATCH v2 " Emilio López
@ 2013-02-25 14:44   ` Emilio López
  2013-02-25 14:44   ` [PATCH v2 2/3] arm: sunxi: Add clock definitions for the new clock driver Emilio López
                     ` (4 subsequent siblings)
  5 siblings, 0 replies; 26+ messages in thread
From: Emilio López @ 2013-02-25 14:44 UTC (permalink / raw)
  To: linux-arm-kernel

This commit implements the base CPU clocks for sunxi devices. It has
been tested using a slightly modified cpufreq driver from the
linux-sunxi 3.0 tree.

Additionally, document the new bindings introduced by this patch.

Idling:
    / # cat /sys/kernel/debug/clk/clk_summary
       clock                        enable_cnt  prepare_cnt  rate
    ---------------------------------------------------------------------
     osc32k                         0           0            32768
     osc24M_fixed                   0           0            24000000
        osc24M                      0           0            24000000
           apb1_mux                 0           0            24000000
              apb1                  0           0            24000000
           pll1                     0           0            60000000
              cpu                   0           0            60000000
                 axi                0           0            60000000
                    ahb             0           0            60000000
                       apb0         0           0            30000000
     dummy                          0           0            0

After "yes >/dev/null &":
    / # cat /sys/kernel/debug/clk/clk_summary
       clock                        enable_cnt  prepare_cnt  rate
    ---------------------------------------------------------------------
     osc32k                         0           0            32768
     osc24M_fixed                   0           0            24000000
        osc24M                      0           0            24000000
           apb1_mux                 0           0            24000000
              apb1                  0           0            24000000
           pll1                     0           0            1008000000
              cpu                   0           0            1008000000
                 axi                0           0            336000000
                    ahb             0           0            168000000
                       apb0         0           0            84000000
     dummy                          0           0            0

Signed-off-by: Emilio L?pez <emilio@elopez.com.ar>
---
 Documentation/devicetree/bindings/clock/sunxi.txt |  44 +++
 drivers/clk/Makefile                              |   1 +
 drivers/clk/sunxi/Makefile                        |   5 +
 drivers/clk/sunxi/clk-factors.c                   | 180 +++++++++++
 drivers/clk/sunxi/clk-factors.h                   |  27 ++
 drivers/clk/sunxi/clk-sunxi.c                     | 362 ++++++++++++++++++++++
 drivers/clocksource/sunxi_timer.c                 |   4 +-
 include/linux/clk/sunxi.h                         |  22 ++
 8 files changed, 643 insertions(+), 2 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/clock/sunxi.txt
 create mode 100644 drivers/clk/sunxi/Makefile
 create mode 100644 drivers/clk/sunxi/clk-factors.c
 create mode 100644 drivers/clk/sunxi/clk-factors.h
 create mode 100644 drivers/clk/sunxi/clk-sunxi.c
 create mode 100644 include/linux/clk/sunxi.h

diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt
new file mode 100644
index 0000000..b23cfbd
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/sunxi.txt
@@ -0,0 +1,44 @@
+Device Tree Clock bindings for arch-sunxi
+
+This binding uses the common clock binding[1].
+
+[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
+
+Required properties:
+- compatible : shall be one of the following:
+	"allwinner,sunxi-osc-clk" - for a gatable oscillator
+	"allwinner,sunxi-pll1-clk" - for the main PLL clock
+	"allwinner,sunxi-cpu-clk" - for the CPU multiplexer clock
+	"allwinner,sunxi-axi-clk" - for the sunxi AXI clock
+	"allwinner,sunxi-ahb-clk" - for the sunxi AHB clock
+	"allwinner,sunxi-apb0-clk" - for the sunxi APB0 clock
+	"allwinner,sunxi-apb1-clk" - for the sunxi APB1 clock
+	"allwinner,sunxi-apb1-mux-clk" - for the sunxi APB1 clock muxing
+
+Required properties for all clocks:
+- reg : shall be the control register address for the clock.
+- clocks : shall be the input parent clock(s) phandle for the clock
+- #clock-cells : from common clock binding; shall be set to 0.
+
+For example:
+
+osc24M: osc24M at 01c20050 {
+	#clock-cells = <0>;
+	compatible = "allwinner,sunxi-osc-clk";
+	reg = <0x01c20050 0x4>;
+	clocks = <&osc24M_fixed>;
+};
+
+pll1: pll1 at 01c20000 {
+	#clock-cells = <0>;
+	compatible = "allwinner,sunxi-pll1-clk";
+	reg = <0x01c20000 0x4>;
+	clocks = <&osc24M>;
+};
+
+cpu: cpu at 01c20054 {
+	#clock-cells = <0>;
+	compatible = "allwinner,sunxi-cpu-clk";
+	reg = <0x01c20054 0x4>;
+	clocks = <&osc32k>, <&osc24M>, <&pll1>;
+};
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 300d477..db8b11b 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -23,6 +23,7 @@ ifeq ($(CONFIG_COMMON_CLK), y)
 obj-$(CONFIG_ARCH_MMP)		+= mmp/
 endif
 obj-$(CONFIG_MACH_LOONGSON1)	+= clk-ls1x.o
+obj-$(CONFIG_ARCH_SUNXI)	+= sunxi/
 obj-$(CONFIG_ARCH_U8500)	+= ux500/
 obj-$(CONFIG_ARCH_VT8500)	+= clk-vt8500.o
 obj-$(CONFIG_ARCH_ZYNQ)		+= clk-zynq.o
diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile
new file mode 100644
index 0000000..b5bac91
--- /dev/null
+++ b/drivers/clk/sunxi/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for sunxi specific clk
+#
+
+obj-y += clk-sunxi.o clk-factors.o
diff --git a/drivers/clk/sunxi/clk-factors.c b/drivers/clk/sunxi/clk-factors.c
new file mode 100644
index 0000000..88523f9
--- /dev/null
+++ b/drivers/clk/sunxi/clk-factors.c
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2013 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 version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Adjustable factor-based clock implementation
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/string.h>
+
+#include <linux/delay.h>
+
+#include "clk-factors.h"
+
+/*
+ * DOC: basic adjustable factor-based clock that cannot gate
+ *
+ * Traits of this clock:
+ * prepare - clk_prepare only ensures that parents are prepared
+ * enable - clk_enable only ensures that parents are enabled
+ * rate - rate is adjustable.
+ *        clk->rate = (parent->rate * N * (K + 1) >> P) / (M + 1)
+ * parent - fixed parent.  No clk_set_parent support
+ */
+
+struct clk_factors {
+	struct clk_hw hw;
+	void __iomem *reg;
+	struct clk_factors_config *config;
+	void (*get_factors) (u32 *rate, u32 parent, u8 *n, u8 *k, u8 *m, u8 *p);
+	spinlock_t *lock;
+};
+
+#define to_clk_factors(_hw) container_of(_hw, struct clk_factors, hw)
+
+#define SETMASK(len, pos)		(((-1U) >> (31-len))  << (pos))
+#define CLRMASK(len, pos)		(~(SETMASK(len, pos)))
+#define FACTOR_GET(bit, len, reg)	(((reg) & SETMASK(len, bit)) >> (bit))
+
+#define FACTOR_SET(bit, len, reg, val) \
+	(((reg) & CLRMASK(len, bit)) | (val << (bit)))
+
+static unsigned long clk_factors_recalc_rate(struct clk_hw *hw,
+					     unsigned long parent_rate)
+{
+	u8 n = 1, k = 0, p = 0, m = 0;
+	u32 reg;
+	unsigned long rate;
+	struct clk_factors *factors = to_clk_factors(hw);
+	struct clk_factors_config *config = factors->config;
+
+	/* Fetch the register value */
+	reg = readl(factors->reg);
+
+	/* Get each individual factor if applicable */
+	if (config->nwidth != SUNXI_FACTORS_NOT_APPLICABLE)
+		n = FACTOR_GET(config->nshift, config->nwidth, reg);
+	if (config->kwidth != SUNXI_FACTORS_NOT_APPLICABLE)
+		k = FACTOR_GET(config->kshift, config->kwidth, reg);
+	if (config->mwidth != SUNXI_FACTORS_NOT_APPLICABLE)
+		m = FACTOR_GET(config->mshift, config->mwidth, reg);
+	if (config->pwidth != SUNXI_FACTORS_NOT_APPLICABLE)
+		p = FACTOR_GET(config->pshift, config->pwidth, reg);
+
+	/* Calculate the rate */
+	rate = (parent_rate * n * (k + 1) >> p) / (m + 1);
+
+	return rate;
+}
+
+static long clk_factors_round_rate(struct clk_hw *hw, unsigned long rate,
+				   unsigned long *parent_rate)
+{
+	struct clk_factors *factors = to_clk_factors(hw);
+	factors->get_factors((u32 *)&rate, (u32)*parent_rate,
+			     NULL, NULL, NULL, NULL);
+
+	return rate;
+}
+
+static int clk_factors_set_rate(struct clk_hw *hw, unsigned long rate,
+				unsigned long parent_rate)
+{
+	u8 n, k, m, p;
+	u32 reg;
+	struct clk_factors *factors = to_clk_factors(hw);
+	struct clk_factors_config *config = factors->config;
+	unsigned long flags = 0;
+
+	factors->get_factors((u32 *)&rate, (u32)parent_rate, &n, &k, &m, &p);
+
+	if (factors->lock)
+		spin_lock_irqsave(factors->lock, flags);
+
+	/* Fetch the register value */
+	reg = readl(factors->reg);
+
+	/* Set up the new factors - macros do not do anything if width is 0 */
+	reg = FACTOR_SET(config->nshift, config->nwidth, reg, n);
+	reg = FACTOR_SET(config->kshift, config->kwidth, reg, k);
+	reg = FACTOR_SET(config->mshift, config->mwidth, reg, m);
+	reg = FACTOR_SET(config->pshift, config->pwidth, reg, p);
+
+	/* Apply them now */
+	writel(reg, factors->reg);
+
+	/* delay 500us so pll stabilizes */
+	__delay((rate >> 20) * 500 / 2);
+
+	if (factors->lock)
+		spin_unlock_irqrestore(factors->lock, flags);
+
+	return 0;
+}
+
+static const struct clk_ops clk_factors_ops = {
+	.recalc_rate = clk_factors_recalc_rate,
+	.round_rate = clk_factors_round_rate,
+	.set_rate = clk_factors_set_rate,
+};
+
+/**
+ * clk_register_factors - register a factors clock with
+ * the clock framework
+ * @dev: device registering this clock
+ * @name: name of this clock
+ * @parent_name: name of clock's parent
+ * @flags: framework-specific flags
+ * @reg: register address to adjust factors
+ * @config: shift and width of factors n, k, m and p
+ * @get_factors: function to calculate the factors for a given frequency
+ * @lock: shared register lock for this clock
+ */
+struct clk *clk_register_factors(struct device *dev, const char *name,
+				 const char *parent_name,
+				 unsigned long flags, void __iomem *reg,
+				 struct clk_factors_config *config,
+				 void (*get_factors)(u32 *rate, u32 parent,
+						     u8 *n, u8 *k, u8 *m, u8 *p),
+				 spinlock_t *lock)
+{
+	struct clk_factors *factors;
+	struct clk *clk;
+	struct clk_init_data init;
+
+	/* allocate the factors */
+	factors = kzalloc(sizeof(struct clk_factors), GFP_KERNEL);
+	if (!factors) {
+		pr_err("%s: could not allocate factors clk\n", __func__);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	init.name = name;
+	init.ops = &clk_factors_ops;
+	init.flags = flags;
+	init.parent_names = (parent_name ? &parent_name : NULL);
+	init.num_parents = (parent_name ? 1 : 0);
+
+	/* struct clk_factors assignments */
+	factors->reg = reg;
+	factors->config = config;
+	factors->lock = lock;
+	factors->hw.init = &init;
+	factors->get_factors = get_factors;
+
+	/* register the clock */
+	clk = clk_register(dev, &factors->hw);
+
+	if (IS_ERR(clk))
+		kfree(factors);
+
+	return clk;
+}
diff --git a/drivers/clk/sunxi/clk-factors.h b/drivers/clk/sunxi/clk-factors.h
new file mode 100644
index 0000000..f49851c
--- /dev/null
+++ b/drivers/clk/sunxi/clk-factors.h
@@ -0,0 +1,27 @@
+#ifndef __MACH_SUNXI_CLK_FACTORS_H
+#define __MACH_SUNXI_CLK_FACTORS_H
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+
+#define SUNXI_FACTORS_NOT_APPLICABLE	(0)
+
+struct clk_factors_config {
+	u8 nshift;
+	u8 nwidth;
+	u8 kshift;
+	u8 kwidth;
+	u8 mshift;
+	u8 mwidth;
+	u8 pshift;
+	u8 pwidth;
+};
+
+struct clk *clk_register_factors(struct device *dev, const char *name,
+				 const char *parent_name,
+				 unsigned long flags, void __iomem *reg,
+				 struct clk_factors_config *config,
+				 void (*get_factors) (u32 *rate, u32 parent_rate,
+						      u8 *n, u8 *k, u8 *m, u8 *p),
+				 spinlock_t *lock);
+#endif
diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c
new file mode 100644
index 0000000..d4ad1c2
--- /dev/null
+++ b/drivers/clk/sunxi/clk-sunxi.c
@@ -0,0 +1,362 @@
+/*
+ * 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/clkdev.h>
+#include <linux/clk/sunxi.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+
+#include "clk-factors.h"
+
+static DEFINE_SPINLOCK(clk_lock);
+
+/**
+ * sunxi_osc_clk_setup() - Setup function for gatable oscillator
+ */
+
+#define SUNXI_OSC24M_GATE	0
+
+static void __init sunxi_osc_clk_setup(struct device_node *node)
+{
+	struct clk *clk;
+	const char *clk_name = node->name;
+	const char *parent;
+	void *reg;
+
+	reg = of_iomap(node, 0);
+
+	parent = of_clk_get_parent_name(node, 0);
+
+	clk = clk_register_gate(NULL, clk_name, parent, CLK_IGNORE_UNUSED,
+				reg, SUNXI_OSC24M_GATE, 0, &clk_lock);
+
+	if (clk) {
+		of_clk_add_provider(node, of_clk_src_simple_get, clk);
+		clk_register_clkdev(clk, clk_name, NULL);
+	}
+}
+
+
+
+/**
+ * sunxi_get_pll1_factors() - calculates n, k, m, p factors for PLL1
+ * PLL1 rate is calculated as follows
+ * rate = (parent_rate * n * (k + 1) >> p) / (m + 1);
+ * parent_rate is always 24Mhz
+ */
+
+static void sunxi_get_pll1_factors(u32 *freq, u32 parent_rate,
+				   u8 *n, u8 *k, u8 *m, u8 *p)
+{
+	u8 div;
+
+	/* Normalize value to a 6M multiple */
+	div = *freq / 6000000;
+	*freq = 6000000 * div;
+
+	/* we were called to round the frequency, we can now return */
+	if (n == NULL)
+		return;
+
+	/* m is always zero for pll1 */
+	*m = 0;
+
+	/* k is 1 only on these cases */
+	if (*freq >= 768000000 || *freq == 42000000 || *freq == 54000000)
+		*k = 1;
+	else
+		*k = 0;
+
+	/* p will be 3 for divs under 10 */
+	if (div < 10)
+		*p = 3;
+
+	/* p will be 2 for divs between 10 - 20 and odd divs under 32 */
+	else if (div < 20 || (div < 32 && (div & 1)))
+		*p = 2;
+
+	/* p will be 1 for even divs under 32, divs under 40 and odd pairs
+	 * of divs between 40-62 */
+	else if (div < 40 || (div < 64 && (div & 2)))
+		*p = 1;
+
+	/* any other entries have p = 0 */
+	else
+		*p = 0;
+
+	/* calculate a suitable n based on k and p */
+	div <<= *p;
+	div /= (*k + 1);
+	*n = div / 4;
+}
+
+
+
+/**
+ * sunxi_get_apb1_factors() - calculates m, p factors for APB1
+ * APB1 rate is calculated as follows
+ * rate = (parent_rate >> p) / (m + 1);
+ */
+
+static void sunxi_get_apb1_factors(u32 *freq, u32 parent_rate,
+				   u8 *n, u8 *k, u8 *m, u8 *p)
+{
+	u8 calcm, calcp;
+
+	if (parent_rate < *freq)
+		*freq = parent_rate;
+
+	parent_rate = (parent_rate + (*freq - 1)) / *freq;
+
+	/* Invalid rate! */
+	if (parent_rate > 32)
+		return;
+
+	if (parent_rate <= 4)
+		calcp = 0;
+	else if (parent_rate <= 8)
+		calcp = 1;
+	else if (parent_rate <= 16)
+		calcp = 2;
+	else
+		calcp = 3;
+
+	calcm = (parent_rate >> calcp) - 1;
+
+	*freq = (parent_rate >> calcp) / (calcm + 1);
+
+	/* we were called to round the frequency, we can now return */
+	if (n == NULL)
+		return;
+
+	*m = calcm;
+	*p = calcp;
+}
+
+
+
+/**
+ * sunxi_factors_clk_setup() - Setup function for factor clocks
+ */
+
+struct factors_data {
+	struct clk_factors_config *table;
+	void (*getter) (u32 *rate, u32 parent_rate, u8 *n, u8 *k, u8 *m, u8 *p);
+};
+
+static struct clk_factors_config pll1_config = {
+	.nshift = 8,
+	.nwidth = 5,
+	.kshift = 4,
+	.kwidth = 2,
+	.mshift = 0,
+	.mwidth = 2,
+	.pshift = 16,
+	.pwidth = 2,
+};
+
+static struct clk_factors_config apb1_config = {
+	.mshift = 0,
+	.mwidth = 5,
+	.pshift = 16,
+	.pwidth = 2,
+};
+
+static const __initconst struct factors_data pll1_data = {
+	.table = &pll1_config,
+	.getter = sunxi_get_pll1_factors,
+};
+
+static const __initconst struct factors_data apb1_data = {
+	.table = &apb1_config,
+	.getter = sunxi_get_apb1_factors,
+};
+
+static void __init sunxi_factors_clk_setup(struct device_node *node,
+					   struct factors_data *data)
+{
+	struct clk *clk;
+	const char *clk_name = node->name;
+	const char *parent;
+	void *reg;
+
+	reg = of_iomap(node, 0);
+
+	parent = of_clk_get_parent_name(node, 0);
+
+	clk = clk_register_factors(NULL, clk_name, parent, CLK_IGNORE_UNUSED,
+				   reg, data->table, data->getter, &clk_lock);
+
+	if (clk) {
+		of_clk_add_provider(node, of_clk_src_simple_get, clk);
+		clk_register_clkdev(clk, clk_name, NULL);
+	}
+}
+
+
+
+/**
+ * sunxi_mux_clk_setup() - Setup function for muxes
+ */
+
+#define SUNXI_MUX_GATE_WIDTH	2
+
+struct mux_data {
+	u8 shift;
+};
+
+static const __initconst struct mux_data cpu_data = {
+	.shift = 16,
+};
+
+static const __initconst struct mux_data apb1_mux_data = {
+	.shift = 24,
+};
+
+static void __init sunxi_mux_clk_setup(struct device_node *node,
+				       struct mux_data *data)
+{
+	struct clk *clk;
+	const char *clk_name = node->name;
+	const char **parents = kmalloc(sizeof(char *) * 5, GFP_KERNEL);
+	void *reg;
+	int i = 0;
+
+	reg = of_iomap(node, 0);
+
+	while (i < 5 && (parents[i] = of_clk_get_parent_name(node, i)) != NULL)
+		i++;
+
+	clk = clk_register_mux(NULL, clk_name, parents, i, 0, reg,
+			       data->shift, SUNXI_MUX_GATE_WIDTH,
+			       0, &clk_lock);
+
+	if (clk) {
+		of_clk_add_provider(node, of_clk_src_simple_get, clk);
+		clk_register_clkdev(clk, clk_name, NULL);
+	}
+}
+
+
+
+/**
+ * sunxi_divider_clk_setup() - Setup function for simple divider clocks
+ */
+
+#define SUNXI_DIVISOR_WIDTH	2
+
+struct div_data {
+	u8 shift;
+	u8 pow;
+};
+
+static const __initconst struct div_data axi_data = {
+	.shift = 0,
+	.pow = 0,
+};
+
+static const __initconst struct div_data ahb_data = {
+	.shift = 4,
+	.pow = 1,
+};
+
+static const __initconst struct div_data apb0_data = {
+	.shift = 8,
+	.pow = 1,
+};
+
+static void __init sunxi_divider_clk_setup(struct device_node *node,
+					   struct div_data *data)
+{
+	struct clk *clk;
+	const char *clk_name = node->name;
+	const char *clk_parent;
+	void *reg;
+
+	reg = of_iomap(node, 0);
+
+	clk_parent = of_clk_get_parent_name(node, 0);
+
+	clk = clk_register_divider(NULL, clk_name, clk_parent, 0,
+				   reg, data->shift, SUNXI_DIVISOR_WIDTH,
+				   data->pow ? CLK_DIVIDER_POWER_OF_TWO : 0,
+				   &clk_lock);
+	if (clk) {
+		of_clk_add_provider(node, of_clk_src_simple_get, clk);
+		clk_register_clkdev(clk, clk_name, NULL);
+	}
+}
+
+
+/* Matches for of_clk_init */
+static const __initconst struct of_device_id clk_match[] = {
+	{.compatible = "fixed-clock", .data = of_fixed_clk_setup,},
+	{.compatible = "allwinner,sunxi-osc-clk", .data = sunxi_osc_clk_setup,},
+	{}
+};
+
+/* Matches for factors clocks */
+static const __initconst struct of_device_id clk_factors_match[] = {
+	{.compatible = "allwinner,sunxi-pll1-clk", .data = &pll1_data,},
+	{.compatible = "allwinner,sunxi-apb1-clk", .data = &apb1_data,},
+	{}
+};
+
+/* Matches for divider clocks */
+static const __initconst struct of_device_id clk_div_match[] = {
+	{.compatible = "allwinner,sunxi-axi-clk", .data = &axi_data,},
+	{.compatible = "allwinner,sunxi-ahb-clk", .data = &ahb_data,},
+	{.compatible = "allwinner,sunxi-apb0-clk", .data = &apb0_data,},
+	{}
+};
+
+/* Matches for mux clocks */
+static const __initconst struct of_device_id clk_mux_match[] = {
+	{.compatible = "allwinner,sunxi-cpu-clk", .data = &cpu_data,},
+	{.compatible = "allwinner,sunxi-apb1-mux-clk", .data = &apb1_mux_data,},
+	{}
+};
+
+static void __init of_sunxi_table_clock_setup(const struct of_device_id *clk_match,
+					      void *function)
+{
+	struct device_node *np;
+	const struct div_data *data;
+	const struct of_device_id *match;
+	void (*setup_function)(struct device_node *, const void *) = function;
+
+	for_each_matching_node(np, clk_match) {
+		match = of_match_node(clk_match, np);
+		data = match->data;
+		setup_function(np, data);
+	}
+}
+
+void __init sunxi_init_clocks(void)
+{
+	/* Register all the simple sunxi clocks on DT */
+	of_clk_init(clk_match);
+
+	/* Register factor clocks */
+	of_sunxi_table_clock_setup(clk_factors_match, sunxi_factors_clk_setup);
+
+	/* Register divider clocks */
+	of_sunxi_table_clock_setup(clk_div_match, sunxi_divider_clk_setup);
+
+	/* Register mux clocks */
+	of_sunxi_table_clock_setup(clk_mux_match, sunxi_mux_clk_setup);
+}
diff --git a/drivers/clocksource/sunxi_timer.c b/drivers/clocksource/sunxi_timer.c
index 4086b91..0ce85e2 100644
--- a/drivers/clocksource/sunxi_timer.c
+++ b/drivers/clocksource/sunxi_timer.c
@@ -23,7 +23,7 @@
 #include <linux/of_address.h>
 #include <linux/of_irq.h>
 #include <linux/sunxi_timer.h>
-#include <linux/clk-provider.h>
+#include <linux/clk/sunxi.h>
 
 #define TIMER_CTL_REG		0x00
 #define TIMER_CTL_ENABLE		(1 << 0)
@@ -123,7 +123,7 @@ void __init sunxi_timer_init(void)
 	if (irq <= 0)
 		panic("Can't parse IRQ");
 
-	of_clk_init(NULL);
+	sunxi_init_clocks();
 
 	clk = of_clk_get(node, 0);
 	if (IS_ERR(clk))
diff --git a/include/linux/clk/sunxi.h b/include/linux/clk/sunxi.h
new file mode 100644
index 0000000..e074fdd
--- /dev/null
+++ b/include/linux/clk/sunxi.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2012 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.
+ */
+
+#ifndef __LINUX_CLK_SUNXI_H_
+#define __LINUX_CLK_SUNXI_H_
+
+void __init sunxi_init_clocks(void);
+
+#endif
-- 
1.8.2.rc0

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

* [PATCH v2 2/3] arm: sunxi: Add clock definitions for the new clock driver
  2013-02-25 14:44 ` [PATCH v2 " Emilio López
  2013-02-25 14:44   ` [PATCH v2 1/3] clk: arm: sunxi: Add a new clock driver for sunxi SOCs Emilio López
@ 2013-02-25 14:44   ` Emilio López
  2013-02-25 14:44   ` [PATCH v2 3/3] arm: sunxi: Add useful information about sunxi clocks Emilio López
                     ` (3 subsequent siblings)
  5 siblings, 0 replies; 26+ messages in thread
From: Emilio López @ 2013-02-25 14:44 UTC (permalink / raw)
  To: linux-arm-kernel

This introduces proper clock definitions on sunxi.dtsi, to be used
with the new clock driver for sunxi.

Signed-off-by: Emilio L?pez <emilio@elopez.com.ar>
---
 arch/arm/boot/dts/sunxi.dtsi | 83 ++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 80 insertions(+), 3 deletions(-)

diff --git a/arch/arm/boot/dts/sunxi.dtsi b/arch/arm/boot/dts/sunxi.dtsi
index 8b36abe..acf7777 100644
--- a/arch/arm/boot/dts/sunxi.dtsi
+++ b/arch/arm/boot/dts/sunxi.dtsi
@@ -24,13 +24,90 @@
 
 	clocks {
 		#address-cells = <1>;
-		#size-cells = <0>;
+		#size-cells = <1>;
+		ranges;
 
-		osc: oscillator {
+		/*
+		 * This is a dummy clock, to be used as placeholder on
+		 * other mux clocks when a specific parent clock is not
+		 * yet implemented. It should be dropped when the driver
+		 * is complete.
+		 */
+		dummy: dummy {
+			#clock-cells = <0>;
+			compatible = "fixed-clock";
+			clock-frequency = <0>;
+		};
+
+		osc24M_fixed: osc24M_fixed {
 			#clock-cells = <0>;
 			compatible = "fixed-clock";
 			clock-frequency = <24000000>;
 		};
+
+		osc24M: osc24M at 01c20050 {
+			#clock-cells = <0>;
+			compatible = "allwinner,sunxi-osc-clk";
+			reg = <0x01c20050 0x4>;
+			clocks = <&osc24M_fixed>;
+		};
+
+		osc32k: osc32k {
+			#clock-cells = <0>;
+			compatible = "fixed-clock";
+			clock-frequency = <32768>;
+		};
+
+		pll1: pll1 at 01c20000 {
+			#clock-cells = <0>;
+			compatible = "allwinner,sunxi-pll1-clk";
+			reg = <0x01c20000 0x4>;
+			clocks = <&osc24M>;
+		};
+
+		/* dummy is 200M */
+		cpu: cpu at 01c20054 {
+			#clock-cells = <0>;
+			compatible = "allwinner,sunxi-cpu-clk";
+			reg = <0x01c20054 0x4>;
+			clocks = <&osc32k>, <&osc24M>, <&pll1>, <&dummy>;
+		};
+
+		axi: axi at 01c20054 {
+			#clock-cells = <0>;
+			compatible = "allwinner,sunxi-axi-clk";
+			reg = <0x01c20054 0x4>;
+			clocks = <&cpu>;
+		};
+
+		ahb: ahb at 01c20054 {
+			#clock-cells = <0>;
+			compatible = "allwinner,sunxi-ahb-clk";
+			reg = <0x01c20054 0x4>;
+			clocks = <&axi>;
+		};
+
+		apb0: apb0 at 01c20054 {
+			#clock-cells = <0>;
+			compatible = "allwinner,sunxi-apb0-clk";
+			reg = <0x01c20054 0x4>;
+			clocks = <&ahb>;
+		};
+
+		/* dummy is pll62 */
+		apb1_mux: apb1_mux at 01c20058 {
+			#clock-cells = <0>;
+			compatible = "allwinner,sunxi-apb1-mux-clk";
+			reg = <0x01c20058 0x4>;
+			clocks = <&osc24M>, <&dummy>, <&osc32k>;
+		};
+
+		apb1: apb1 at 01c20058 {
+			#clock-cells = <0>;
+			compatible = "allwinner,sunxi-apb1-clk";
+			reg = <0x01c20058 0x4>;
+			clocks = <&apb1_mux>;
+		};
 	};
 
 	soc {
@@ -44,7 +121,7 @@
 			compatible = "allwinner,sunxi-timer";
 			reg = <0x01c20c00 0x90>;
 			interrupts = <22>;
-			clocks = <&osc>;
+			clocks = <&osc24M>;
 		};
 
 		wdt: watchdog at 01c20c90 {
-- 
1.8.2.rc0

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

* [PATCH v2 3/3] arm: sunxi: Add useful information about sunxi clocks
  2013-02-25 14:44 ` [PATCH v2 " Emilio López
  2013-02-25 14:44   ` [PATCH v2 1/3] clk: arm: sunxi: Add a new clock driver for sunxi SOCs Emilio López
  2013-02-25 14:44   ` [PATCH v2 2/3] arm: sunxi: Add clock definitions for the new clock driver Emilio López
@ 2013-02-25 14:44   ` Emilio López
  2013-03-04 15:04   ` [PATCH v2 0/3] clock driver for sunxi Emilio López
                     ` (2 subsequent siblings)
  5 siblings, 0 replies; 26+ messages in thread
From: Emilio López @ 2013-02-25 14:44 UTC (permalink / raw)
  To: linux-arm-kernel

This patch contains useful bits of information about the sunxi clocks
that may help and/or be interesting for current and future developers.

Signed-off-by: Emilio L?pez <emilio@elopez.com.ar>
---
 Documentation/arm/sunxi/clocks.txt | 56 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 56 insertions(+)
 create mode 100644 Documentation/arm/sunxi/clocks.txt

diff --git a/Documentation/arm/sunxi/clocks.txt b/Documentation/arm/sunxi/clocks.txt
new file mode 100644
index 0000000..e09a88a
--- /dev/null
+++ b/Documentation/arm/sunxi/clocks.txt
@@ -0,0 +1,56 @@
+Frequently asked questions about the sunxi clock system
+=======================================================
+
+This document contains useful bits of information that people tend to ask
+about the sunxi clock system, as well as accompanying ASCII art when adequate.
+
+Q: Why is the main 24MHz oscillator gatable? Wouldn't that break the
+   system?
+
+A: The 24MHz oscillator allows gating to save power. Indeed, if gated
+   carelessly the system would stop functioning, but with the right
+   steps, one can gate it and keep the system running. Consider this
+   simplified suspend example:
+
+   While the system is operational, you would see something like
+
+      24MHz         32kHz
+       |
+      PLL1
+       \
+        \_ CPU Mux
+             |
+           [CPU]
+
+   When you are about to suspend, you switch the CPU Mux to the 32kHz
+   oscillator:
+
+      24Mhz         32kHz
+       |              |
+      PLL1            |
+                     /
+           CPU Mux _/
+             |
+           [CPU]
+
+    Finally you can gate the main oscillator
+
+                    32kHz
+                      |
+                      |
+                     /
+           CPU Mux _/
+             |
+           [CPU]
+
+Q: Were can I learn more about the sunxi clocks?
+
+A: The linux-sunxi wiki contains a page documenting the clock registers,
+   you can find it at
+
+        http://linux-sunxi.org/A10/CCM
+
+   The authoritative source for information at this time is the ccmu driver
+   released by Allwinner, you can find it at
+
+        https://github.com/linux-sunxi/linux-sunxi/tree/sunxi-3.0/arch/arm/mach-sun4i/clock/ccmu
-- 
1.8.2.rc0

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

* [PATCH v2 0/3] clock driver for sunxi
  2013-02-25 14:44 ` [PATCH v2 " Emilio López
                     ` (2 preceding siblings ...)
  2013-02-25 14:44   ` [PATCH v2 3/3] arm: sunxi: Add useful information about sunxi clocks Emilio López
@ 2013-03-04 15:04   ` Emilio López
  2013-03-10 11:42   ` Maxime Ripard
  2013-03-21 21:54   ` Mike Turquette
  5 siblings, 0 replies; 26+ messages in thread
From: Emilio López @ 2013-03-04 15:04 UTC (permalink / raw)
  To: linux-arm-kernel

El 25/02/13 11:44, Emilio L?pez escribi?:
> Hello everyone,
> 
> This patchset adds basic clock support for sunxi devices. Currently, it
> implements support for the two oscillators, the main PLL, the CPU mux,
> its three divisor clocks and APB1. With this in place, it is possible to
> write a cpufreq driver and have it work, and with a little more code,
> represent the UART clocks correctly (I will be sending a separate
> patchset for this).
> 
> I have tested this driver successfully on a Cubieboard (A10, sun4i)
> using the cpufreq driver from the linux-sunxi tree after minor
> modifications (the clock names are not the same).
> 
> Any feedback will be highly appreciated
> 
> Thanks,
> 
> Emilio
> 
> Overview of changes from v1:
>   * Implement APB1
>   * Add missing glue for driver that got dropped on torvalds/master
>   * Drop "fixed gate" code and implement it with two clocks as suggested
>     by Gregory
> 
> Emilio L?pez (3):
>   clk: arm: sunxi: Add a new clock driver for sunxi SOCs
>   arm: sunxi: Add clock definitions for the new clock driver
>   arm: sunxi: Add useful information about sunxi clocks
> 
>  Documentation/arm/sunxi/clocks.txt                |  56 ++++
>  Documentation/devicetree/bindings/clock/sunxi.txt |  44 +++
>  arch/arm/boot/dts/sunxi.dtsi                      |  83 ++++-
>  drivers/clk/Makefile                              |   1 +
>  drivers/clk/sunxi/Makefile                        |   5 +
>  drivers/clk/sunxi/clk-factors.c                   | 180 +++++++++++
>  drivers/clk/sunxi/clk-factors.h                   |  27 ++
>  drivers/clk/sunxi/clk-sunxi.c                     | 362 ++++++++++++++++++++++
>  drivers/clocksource/sunxi_timer.c                 |   4 +-
>  include/linux/clk/sunxi.h                         |  22 ++
>  10 files changed, 779 insertions(+), 5 deletions(-)
>  create mode 100644 Documentation/arm/sunxi/clocks.txt
>  create mode 100644 Documentation/devicetree/bindings/clock/sunxi.txt
>  create mode 100644 drivers/clk/sunxi/Makefile
>  create mode 100644 drivers/clk/sunxi/clk-factors.c
>  create mode 100644 drivers/clk/sunxi/clk-factors.h
>  create mode 100644 drivers/clk/sunxi/clk-sunxi.c
>  create mode 100644 include/linux/clk/sunxi.h
> 

Hello Mike,

Can I get your review on this patchset? We would like to get the base
driver merged early for 3.10, so I can continue working on new features
we will be needing for other drivers while knowing the foundations are
good to go.

Thanks!

Emilio

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

* [PATCH v2 0/3] clock driver for sunxi
  2013-02-25 14:44 ` [PATCH v2 " Emilio López
                     ` (3 preceding siblings ...)
  2013-03-04 15:04   ` [PATCH v2 0/3] clock driver for sunxi Emilio López
@ 2013-03-10 11:42   ` Maxime Ripard
  2013-03-21 21:54   ` Mike Turquette
  5 siblings, 0 replies; 26+ messages in thread
From: Maxime Ripard @ 2013-03-10 11:42 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Mike,

Le 25/02/2013 15:44, Emilio L?pez a ?crit :
> Hello everyone,
> 
> This patchset adds basic clock support for sunxi devices. Currently, it
> implements support for the two oscillators, the main PLL, the CPU mux,
> its three divisor clocks and APB1. With this in place, it is possible to
> write a cpufreq driver and have it work, and with a little more code,
> represent the UART clocks correctly (I will be sending a separate
> patchset for this).
> 
> I have tested this driver successfully on a Cubieboard (A10, sun4i)
> using the cpufreq driver from the linux-sunxi tree after minor
> modifications (the clock names are not the same).

Could we have your comments or your Acked-by on these patches?

We already missed 3.9 and I'd really want it to have it merged for 3.10,
as it is a real stepping stone for our future work on these SoCs.

Thanks,
Maxime

-- 
Maxime Ripard, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com

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

* [PATCH v2 0/3] clock driver for sunxi
  2013-02-25 14:44 ` [PATCH v2 " Emilio López
                     ` (4 preceding siblings ...)
  2013-03-10 11:42   ` Maxime Ripard
@ 2013-03-21 21:54   ` Mike Turquette
  2013-03-22 10:22     ` Maxime Ripard
  5 siblings, 1 reply; 26+ messages in thread
From: Mike Turquette @ 2013-03-21 21:54 UTC (permalink / raw)
  To: linux-arm-kernel

Quoting Emilio L?pez (2013-02-25 06:44:25)
> Hello everyone,
> 
> This patchset adds basic clock support for sunxi devices. Currently, it
> implements support for the two oscillators, the main PLL, the CPU mux,
> its three divisor clocks and APB1. With this in place, it is possible to
> write a cpufreq driver and have it work, and with a little more code,
> represent the UART clocks correctly (I will be sending a separate
> patchset for this).
> 
> I have tested this driver successfully on a Cubieboard (A10, sun4i)
> using the cpufreq driver from the linux-sunxi tree after minor
> modifications (the clock names are not the same).
> 
> Any feedback will be highly appreciated
> 

Emilio,

This is a nice series.  I appreciate the amount of comments in the code,
the kerneldoc and also the added sunxi clock documentation.

Concerning the discussion from the v1 series, I prefer for the struct
clk instances in the tree to match 1-to-1 with hardware clocks.  As the
number of clocks in a system scales into the hundreds there are
certainly advantages to having fewer clocks in the hierarchy, not the
least of which is more easily associating these objects with hardware
clocks during a deep debug session.

That can be changed at a later time if you wish.  Do you want me to take
the series into clk-next or do you just want my ack?

Thanks,
Mike

> Thanks,
> 
> Emilio
> 
> Overview of changes from v1:
>   * Implement APB1
>   * Add missing glue for driver that got dropped on torvalds/master
>   * Drop "fixed gate" code and implement it with two clocks as suggested
>     by Gregory
> 
> Emilio L?pez (3):
>   clk: arm: sunxi: Add a new clock driver for sunxi SOCs
>   arm: sunxi: Add clock definitions for the new clock driver
>   arm: sunxi: Add useful information about sunxi clocks
> 
>  Documentation/arm/sunxi/clocks.txt                |  56 ++++
>  Documentation/devicetree/bindings/clock/sunxi.txt |  44 +++
>  arch/arm/boot/dts/sunxi.dtsi                      |  83 ++++-
>  drivers/clk/Makefile                              |   1 +
>  drivers/clk/sunxi/Makefile                        |   5 +
>  drivers/clk/sunxi/clk-factors.c                   | 180 +++++++++++
>  drivers/clk/sunxi/clk-factors.h                   |  27 ++
>  drivers/clk/sunxi/clk-sunxi.c                     | 362 ++++++++++++++++++++++
>  drivers/clocksource/sunxi_timer.c                 |   4 +-
>  include/linux/clk/sunxi.h                         |  22 ++
>  10 files changed, 779 insertions(+), 5 deletions(-)
>  create mode 100644 Documentation/arm/sunxi/clocks.txt
>  create mode 100644 Documentation/devicetree/bindings/clock/sunxi.txt
>  create mode 100644 drivers/clk/sunxi/Makefile
>  create mode 100644 drivers/clk/sunxi/clk-factors.c
>  create mode 100644 drivers/clk/sunxi/clk-factors.h
>  create mode 100644 drivers/clk/sunxi/clk-sunxi.c
>  create mode 100644 include/linux/clk/sunxi.h
> 
> -- 
> 1.8.2.rc0

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

* [PATCH v2 0/3] clock driver for sunxi
  2013-03-21 21:54   ` Mike Turquette
@ 2013-03-22 10:22     ` Maxime Ripard
  2013-03-22 16:40       ` Mike Turquette
  0 siblings, 1 reply; 26+ messages in thread
From: Maxime Ripard @ 2013-03-22 10:22 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Mike,

Le 21/03/2013 22:54, Mike Turquette a ?crit :
> Quoting Emilio L?pez (2013-02-25 06:44:25)
>> Hello everyone,
>>
>> This patchset adds basic clock support for sunxi devices. Currently, it
>> implements support for the two oscillators, the main PLL, the CPU mux,
>> its three divisor clocks and APB1. With this in place, it is possible to
>> write a cpufreq driver and have it work, and with a little more code,
>> represent the UART clocks correctly (I will be sending a separate
>> patchset for this).
>>
>> I have tested this driver successfully on a Cubieboard (A10, sun4i)
>> using the cpufreq driver from the linux-sunxi tree after minor
>> modifications (the clock names are not the same).
>>
>> Any feedback will be highly appreciated
>>
> 
> Emilio,
> 
> This is a nice series.  I appreciate the amount of comments in the code,
> the kerneldoc and also the added sunxi clock documentation.
> 
> Concerning the discussion from the v1 series, I prefer for the struct
> clk instances in the tree to match 1-to-1 with hardware clocks.  As the
> number of clocks in a system scales into the hundreds there are
> certainly advantages to having fewer clocks in the hierarchy, not the
> least of which is more easily associating these objects with hardware
> clocks during a deep debug session.
> 
> That can be changed at a later time if you wish.  Do you want me to take
> the series into clk-next or do you just want my ack?

Thanks for your feedback!

Unless you have some objection, I was planning to merge it through my
tree, with your Acked-by, of course.

Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com

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

* [PATCH v2 0/3] clock driver for sunxi
  2013-03-22 10:22     ` Maxime Ripard
@ 2013-03-22 16:40       ` Mike Turquette
  2013-03-23  9:27         ` Maxime Ripard
  2013-03-26  9:20         ` Maxime Ripard
  0 siblings, 2 replies; 26+ messages in thread
From: Mike Turquette @ 2013-03-22 16:40 UTC (permalink / raw)
  To: linux-arm-kernel

Quoting Maxime Ripard (2013-03-22 03:22:25)
> Hi Mike,
> 
> Le 21/03/2013 22:54, Mike Turquette a ?crit :
> > Quoting Emilio L?pez (2013-02-25 06:44:25)
> >> Hello everyone,
> >>
> >> This patchset adds basic clock support for sunxi devices. Currently, it
> >> implements support for the two oscillators, the main PLL, the CPU mux,
> >> its three divisor clocks and APB1. With this in place, it is possible to
> >> write a cpufreq driver and have it work, and with a little more code,
> >> represent the UART clocks correctly (I will be sending a separate
> >> patchset for this).
> >>
> >> I have tested this driver successfully on a Cubieboard (A10, sun4i)
> >> using the cpufreq driver from the linux-sunxi tree after minor
> >> modifications (the clock names are not the same).
> >>
> >> Any feedback will be highly appreciated
> >>
> > 
> > Emilio,
> > 
> > This is a nice series.  I appreciate the amount of comments in the code,
> > the kerneldoc and also the added sunxi clock documentation.
> > 
> > Concerning the discussion from the v1 series, I prefer for the struct
> > clk instances in the tree to match 1-to-1 with hardware clocks.  As the
> > number of clocks in a system scales into the hundreds there are
> > certainly advantages to having fewer clocks in the hierarchy, not the
> > least of which is more easily associating these objects with hardware
> > clocks during a deep debug session.
> > 
> > That can be changed at a later time if you wish.  Do you want me to take
> > the series into clk-next or do you just want my ack?
> 
> Thanks for your feedback!
> 
> Unless you have some objection, I was planning to merge it through my
> tree, with your Acked-by, of course.
> 

Acked-by: Mike Turquette <mturquette@linaro.org>

> Maxime
> 
> -- 
> Maxime Ripard, Free Electrons
> Embedded Linux, Kernel and Android engineering
> http://free-electrons.com

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

* [PATCH v2 0/3] clock driver for sunxi
  2013-03-22 16:40       ` Mike Turquette
@ 2013-03-23  9:27         ` Maxime Ripard
  2013-03-26  9:20         ` Maxime Ripard
  1 sibling, 0 replies; 26+ messages in thread
From: Maxime Ripard @ 2013-03-23  9:27 UTC (permalink / raw)
  To: linux-arm-kernel

Le 22/03/2013 17:40, Mike Turquette a ?crit :
> Acked-by: Mike Turquette <mturquette@linaro.org>

Thanks Mike,

Applied it, and it will be part of the pull request I'll send in a few
minutes.

Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com

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

* [PATCH v2 0/3] clock driver for sunxi
  2013-03-22 16:40       ` Mike Turquette
  2013-03-23  9:27         ` Maxime Ripard
@ 2013-03-26  9:20         ` Maxime Ripard
  2013-03-27  1:55           ` Mike Turquette
  1 sibling, 1 reply; 26+ messages in thread
From: Maxime Ripard @ 2013-03-26  9:20 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Mike,

Le 22/03/2013 17:40, Mike Turquette a ?crit :
> Quoting Maxime Ripard (2013-03-22 03:22:25)
>> Hi Mike,
>>
>> Le 21/03/2013 22:54, Mike Turquette a ?crit :
>>> Quoting Emilio L?pez (2013-02-25 06:44:25)
>>>> Hello everyone,
>>>>
>>>> This patchset adds basic clock support for sunxi devices. Currently, it
>>>> implements support for the two oscillators, the main PLL, the CPU mux,
>>>> its three divisor clocks and APB1. With this in place, it is possible to
>>>> write a cpufreq driver and have it work, and with a little more code,
>>>> represent the UART clocks correctly (I will be sending a separate
>>>> patchset for this).
>>>>
>>>> I have tested this driver successfully on a Cubieboard (A10, sun4i)
>>>> using the cpufreq driver from the linux-sunxi tree after minor
>>>> modifications (the clock names are not the same).
>>>>
>>>> Any feedback will be highly appreciated
>>>>
>>>
>>> Emilio,
>>>
>>> This is a nice series.  I appreciate the amount of comments in the code,
>>> the kerneldoc and also the added sunxi clock documentation.
>>>
>>> Concerning the discussion from the v1 series, I prefer for the struct
>>> clk instances in the tree to match 1-to-1 with hardware clocks.  As the
>>> number of clocks in a system scales into the hundreds there are
>>> certainly advantages to having fewer clocks in the hierarchy, not the
>>> least of which is more easily associating these objects with hardware
>>> clocks during a deep debug session.
>>>
>>> That can be changed at a later time if you wish.  Do you want me to take
>>> the series into clk-next or do you just want my ack?
>>
>> Thanks for your feedback!
>>
>> Unless you have some objection, I was planning to merge it through my
>> tree, with your Acked-by, of course.
>>
> 
> Acked-by: Mike Turquette <mturquette@linaro.org>

Arnd would prefer to get this serie going through your tree, could you
merge the patches 1/3 and 3/3 with my Acked-by?

Thanks,
Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com

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

* [PATCH v2 0/3] clock driver for sunxi
  2013-03-26  9:20         ` Maxime Ripard
@ 2013-03-27  1:55           ` Mike Turquette
  0 siblings, 0 replies; 26+ messages in thread
From: Mike Turquette @ 2013-03-27  1:55 UTC (permalink / raw)
  To: linux-arm-kernel

Quoting Maxime Ripard (2013-03-26 02:20:21)
> Hi Mike,
> 
> Le 22/03/2013 17:40, Mike Turquette a ?crit :
> > Quoting Maxime Ripard (2013-03-22 03:22:25)
> >> Hi Mike,
> >>
> >> Le 21/03/2013 22:54, Mike Turquette a ?crit :
> >>> Quoting Emilio L?pez (2013-02-25 06:44:25)
> >>>> Hello everyone,
> >>>>
> >>>> This patchset adds basic clock support for sunxi devices. Currently, it
> >>>> implements support for the two oscillators, the main PLL, the CPU mux,
> >>>> its three divisor clocks and APB1. With this in place, it is possible to
> >>>> write a cpufreq driver and have it work, and with a little more code,
> >>>> represent the UART clocks correctly (I will be sending a separate
> >>>> patchset for this).
> >>>>
> >>>> I have tested this driver successfully on a Cubieboard (A10, sun4i)
> >>>> using the cpufreq driver from the linux-sunxi tree after minor
> >>>> modifications (the clock names are not the same).
> >>>>
> >>>> Any feedback will be highly appreciated
> >>>>
> >>>
> >>> Emilio,
> >>>
> >>> This is a nice series.  I appreciate the amount of comments in the code,
> >>> the kerneldoc and also the added sunxi clock documentation.
> >>>
> >>> Concerning the discussion from the v1 series, I prefer for the struct
> >>> clk instances in the tree to match 1-to-1 with hardware clocks.  As the
> >>> number of clocks in a system scales into the hundreds there are
> >>> certainly advantages to having fewer clocks in the hierarchy, not the
> >>> least of which is more easily associating these objects with hardware
> >>> clocks during a deep debug session.
> >>>
> >>> That can be changed at a later time if you wish.  Do you want me to take
> >>> the series into clk-next or do you just want my ack?
> >>
> >> Thanks for your feedback!
> >>
> >> Unless you have some objection, I was planning to merge it through my
> >> tree, with your Acked-by, of course.
> >>
> > 
> > Acked-by: Mike Turquette <mturquette@linaro.org>
> 
> Arnd would prefer to get this serie going through your tree, could you
> merge the patches 1/3 and 3/3 with my Acked-by?
> 

Makes sense, as this is how we have merged other platform's DT/CCF
support recently.  Patches #1 & #3 are in clk-next.

Regards,
Mike

> Thanks,
> Maxime
> 
> -- 
> Maxime Ripard, Free Electrons
> Embedded Linux, Kernel and Android engineering
> http://free-electrons.com

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

end of thread, other threads:[~2013-03-27  1:55 UTC | newest]

Thread overview: 26+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-01-22  6:12 [PATCH 0/3] clock driver for sunxi Emilio López
2013-01-22  6:12 ` [PATCH 1/3] clk: arm: sunxi: Add a new clock driver for sunxi SOCs Emilio López
2013-02-05 11:18   ` Gregory CLEMENT
2013-02-08 10:38     ` Emilio López
2013-02-08 11:43       ` Gregory CLEMENT
2013-02-08 11:33   ` Gregory CLEMENT
2013-02-08 17:41     ` Emilio López
2013-02-08 17:51       ` Gregory CLEMENT
2013-01-22  6:12 ` [PATCH 2/3] arm: sunxi: Add clock definitions for the new clock driver Emilio López
2013-01-30  8:24   ` Maxime Ripard
2013-01-30 11:57     ` Emilio López
2013-01-22  6:12 ` [PATCH 3/3] arm: sunxi: Add useful information about sunxi clocks Emilio López
2013-01-30  8:17 ` [PATCH 0/3] clock driver for sunxi Maxime Ripard
2013-02-04 22:03 ` Maxime Ripard
2013-02-25 14:44 ` [PATCH v2 " Emilio López
2013-02-25 14:44   ` [PATCH v2 1/3] clk: arm: sunxi: Add a new clock driver for sunxi SOCs Emilio López
2013-02-25 14:44   ` [PATCH v2 2/3] arm: sunxi: Add clock definitions for the new clock driver Emilio López
2013-02-25 14:44   ` [PATCH v2 3/3] arm: sunxi: Add useful information about sunxi clocks Emilio López
2013-03-04 15:04   ` [PATCH v2 0/3] clock driver for sunxi Emilio López
2013-03-10 11:42   ` Maxime Ripard
2013-03-21 21:54   ` Mike Turquette
2013-03-22 10:22     ` Maxime Ripard
2013-03-22 16:40       ` Mike Turquette
2013-03-23  9:27         ` Maxime Ripard
2013-03-26  9:20         ` Maxime Ripard
2013-03-27  1:55           ` Mike Turquette

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.