Skip to contentSkip to conversation

clock driver for sunxi

PATCH Superseded Emilio López 4 participants 26 messages Jan 22, 2013 – Mar 27, 2013

Subsystems (6)
lore
Share & tools
lore

Emilio López

author maintainer
#0 lore raw

cover clock driver for sunxi

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
Signature · 1 line
1.8.1.1
author maintainer
#1 lore raw

PATCH 1/3 clk: arm: sunxi: Add a new clock driver for sunxi SOCs

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
---
 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
Documentation/devicetree/bindings/clock/sunxi.txt new file +47 -0
new file mode 100644
index 0000000..446c5ca
@@ -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>;
+};
drivers/clk/Makefile +1 -1
index ee90e87..129afed 100644
@@ -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
drivers/clk/clk-sunxi.c deleted +0 -30
deleted file mode 100644
index 0e831b5..0000000
@@ -1,30 +0,0 @@
-/*
- * Copyright 2012 Maxime Ripard
- *
- * Maxime Ripard <[email protected]>
- *
- * 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);
-}
drivers/clk/sunxi/Makefile new file +5 -0
new file mode 100644
index 0000000..8e773be
@@ -0,0 +1,5 @@
+#
+# Makefile for sunxi specific clk
+#
+
+obj-y += clk-sunxi.o clk-factors.o clk-fixed-gate.o
drivers/clk/sunxi/clk-factors.c new file +175 -0

175 lines not on this page · raw message

drivers/clk/sunxi/clk-factors.h new file +25 -0
new file mode 100644
index 0000000..a24c889
@@ -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
drivers/clk/sunxi/clk-fixed-gate.c new file +152 -0

152 lines not on this page · raw message

drivers/clk/sunxi/clk-fixed-gate.h new file +13 -0
new file mode 100644
index 0000000..29d9ed3
@@ -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
drivers/clk/sunxi/clk-sunxi.c new file +265 -0

265 lines not on this page · raw message

Signature · 1 line
1.8.1.1
#2 lore raw

PATCH 1/3 clk: arm: sunxi: Add a new clock driver for sunxi SOCs

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.

Quoted 417 lines · Emilio L?pez

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
---
 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
Documentation/devicetree/bindings/clock/sunxi.txt new file +47 -0
new file mode 100644
index 0000000..446c5ca
@@ -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>;
+};
drivers/clk/Makefile +1 -1
index ee90e87..129afed 100644
@@ -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
drivers/clk/clk-sunxi.c deleted +0 -30
deleted file mode 100644
index 0e831b5..0000000
@@ -1,30 +0,0 @@
-/*
- * Copyright 2012 Maxime Ripard
- *
- * Maxime Ripard <[email protected]>
- *
- * 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);
-}
drivers/clk/sunxi/Makefile new file +5 -0
new file mode 100644
index 0000000..8e773be
@@ -0,0 +1,5 @@
+#
+# Makefile for sunxi specific clk
+#
+
+obj-y += clk-sunxi.o clk-factors.o clk-fixed-gate.o
drivers/clk/sunxi/clk-factors.c new file +175 -0
new file mode 100644
index 0000000..428b47d
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2013 Emilio L?pez <[email protected]>
+ *
+ * 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;
+}
drivers/clk/sunxi/clk-factors.h new file +25 -0
new file mode 100644
index 0000000..a24c889
@@ -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
drivers/clk/sunxi/clk-fixed-gate.c new file +24 -0
new file mode 100644
index 0000000..b16eda5
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2013 Emilio L?pez <[email protected]>
+ *
+ * Based on drivers/clk/clk-gate.c,
+ *
+ * Copyright (C) 2010-2011 Canonical Ltd <[email protected]>
+ * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <[email protected]>
+ *
+ * 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"
+

drivers/clk/sunxi/clk-fixed-gate.c line 34

Quoted 10 lines
drivers/clk/sunxi/clk-fixed-gate.c new file +10 -0
+/**
+ * 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

[...]

drivers/clk/sunxi/clk-fixed-gate.c line 43

Quoted 9 lines
drivers/clk/sunxi/clk-fixed-gate.c new file +9 -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);

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

drivers/clk/sunxi/clk-fixed-gate.c line 47

Quoted 4 lines
drivers/clk/sunxi/clk-fixed-gate.c new file +4 -0
+
+	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:

drivers/clk/sunxi/clk-fixed-gate.c line 51

Quoted 4 lines
drivers/clk/sunxi/clk-fixed-gate.c new file +4 -0
+	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);
Quoted 207 lines
drivers/clk/sunxi/clk-fixed-gate.c new file +206 -0
+	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);
+	}

drivers/clk/sunxi/clk-fixed-gate.c

Quoted 10 lines
drivers/clk/sunxi/clk-fixed-gate.c new file +10 -0
+}
+
+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,

Signature · 4 lines
Gregory Clement, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com
author maintainer
#3 lore raw

PATCH 1/3 clk: arm: sunxi: Add a new clock driver for sunxi SOCs

Hello Gregory,

El 05/02/13 08:18, Gregory CLEMENT escribi?:

Quoted 430 lines

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.

Quoted 423 lines · Emilio L?pez

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
---
 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
Documentation/devicetree/bindings/clock/sunxi.txt new file +47 -0
new file mode 100644
index 0000000..446c5ca
@@ -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>;
+};
drivers/clk/Makefile +1 -1
index ee90e87..129afed 100644
@@ -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
drivers/clk/clk-sunxi.c deleted +0 -30
deleted file mode 100644
index 0e831b5..0000000
@@ -1,30 +0,0 @@
-/*
- * Copyright 2012 Maxime Ripard
- *
- * Maxime Ripard <[email protected]>
- *
- * 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);
-}
drivers/clk/sunxi/Makefile new file +5 -0
new file mode 100644
index 0000000..8e773be
@@ -0,0 +1,5 @@
+#
+# Makefile for sunxi specific clk
+#
+
+obj-y += clk-sunxi.o clk-factors.o clk-fixed-gate.o
drivers/clk/sunxi/clk-factors.c new file +175 -0
new file mode 100644
index 0000000..428b47d
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2013 Emilio L?pez <[email protected]>
+ *
+ * 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;
+}
drivers/clk/sunxi/clk-factors.h new file +25 -0
new file mode 100644
index 0000000..a24c889
@@ -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
drivers/clk/sunxi/clk-fixed-gate.c new file +30 -0
new file mode 100644
index 0000000..b16eda5
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2013 Emilio L?pez <[email protected]>
+ *
+ * Based on drivers/clk/clk-gate.c,
+ *
+ * Copyright (C) 2010-2011 Canonical Ltd <[email protected]>
+ * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <[email protected]>
+ *
+ * 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
Quoted 8 lines

drivers/clk/sunxi/clk-fixed-gate.c line 34

Quoted 4 lines
drivers/clk/sunxi/clk-fixed-gate.c new file +4 -0
+ * 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 :)

Quoted 2 lines

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.

Quoted 3 lines

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)

Quoted 265 lines

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

[...]

drivers/clk/sunxi/clk-fixed-gate.c line 43

Quoted 9 lines
drivers/clk/sunxi/clk-fixed-gate.c new file +9 -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);

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

drivers/clk/sunxi/clk-fixed-gate.c line 47

Quoted 4 lines
drivers/clk/sunxi/clk-fixed-gate.c new file +4 -0
+
+	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:

drivers/clk/sunxi/clk-fixed-gate.c line 51

Quoted 4 lines
drivers/clk/sunxi/clk-fixed-gate.c new file +4 -0
+	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);
Quoted 211 lines
drivers/clk/sunxi/clk-fixed-gate.c new file +211 -0
+	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 */
Quoted 9 lines

drivers/clk/sunxi/clk-fixed-gate.c

Quoted 6 lines
drivers/clk/sunxi/clk-fixed-gate.c new file +5 -0
+	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

#4 lore raw

PATCH 1/3 clk: arm: sunxi: Add a new clock driver for sunxi SOCs

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

Quoted 6 lines
Quoted 5 lines
Quoted 1 line

+

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! :)

Quoted 7 lines

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

Quoted 2 lines

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.

Quoted 7 lines
Quoted 4 lines

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.

Quoted 8 lines

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.

Quoted 276 lines
Quoted 266 lines

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

[...]

drivers/clk/sunxi/clk-sunxi.c line 41

Quoted 9 lines
drivers/clk/sunxi/clk-sunxi.c new file +9 -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);

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

drivers/clk/sunxi/clk-sunxi.c line 45

Quoted 4 lines
drivers/clk/sunxi/clk-sunxi.c new file +4 -0
+
+	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:

drivers/clk/sunxi/clk-sunxi.c line 49

Quoted 4 lines
drivers/clk/sunxi/clk-sunxi.c new file +4 -0
+	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);
Quoted 211 lines
drivers/clk/sunxi/clk-sunxi.c new file +211 -0
+	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 */
Quoted 9 lines

drivers/clk/sunxi/clk-sunxi.c line 265

Quoted 6 lines
drivers/clk/sunxi/clk-sunxi.c new file +5 -0
+	of_clk_init(clk_match);
+
+	/* Register divider clocks */
+	of_sunxi_divider_clock_setup();
+}

Regards,

Quoted 2 lines

@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

#5 lore raw

PATCH 1/3 clk: arm: sunxi: Add a new clock driver for sunxi SOCs

In reply to Emilio López

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

Quoted 306 lines · Emilio L?pez

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
---
 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
Documentation/devicetree/bindings/clock/sunxi.txt new file +47 -0
new file mode 100644
index 0000000..446c5ca
@@ -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>;
+};
drivers/clk/Makefile +1 -1
index ee90e87..129afed 100644
@@ -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
drivers/clk/clk-sunxi.c deleted +0 -30
deleted file mode 100644
index 0e831b5..0000000
@@ -1,30 +0,0 @@
-/*
- * Copyright 2012 Maxime Ripard
- *
- * Maxime Ripard <[email protected]>
- *
- * 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);
-}
drivers/clk/sunxi/Makefile new file +5 -0
new file mode 100644
index 0000000..8e773be
@@ -0,0 +1,5 @@
+#
+# Makefile for sunxi specific clk
+#
+
+obj-y += clk-sunxi.o clk-factors.o clk-fixed-gate.o
drivers/clk/sunxi/clk-factors.c new file +125 -0
new file mode 100644
index 0000000..428b47d
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2013 Emilio L?pez <[email protected]>
+ *
+ * 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

drivers/clk/sunxi/clk-factors.c line 135

Quoted 10 lines
drivers/clk/sunxi/clk-factors.c new file +10 -0
+ * 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.

Quoted 447 lines
drivers/clk/sunxi/clk-factors.c new file +40 -0
+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;
+}
drivers/clk/sunxi/clk-factors.h new file +25 -0
new file mode 100644
index 0000000..a24c889
@@ -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
drivers/clk/sunxi/clk-fixed-gate.c new file +152 -0
new file mode 100644
index 0000000..b16eda5
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2013 Emilio L?pez <[email protected]>
+ *
+ * Based on drivers/clk/clk-gate.c,
+ *
+ * Copyright (C) 2010-2011 Canonical Ltd <[email protected]>
+ * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <[email protected]>
+ *
+ * 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;
+}
drivers/clk/sunxi/clk-fixed-gate.h new file +13 -0
new file mode 100644
index 0000000..29d9ed3
@@ -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
drivers/clk/sunxi/clk-sunxi.c new file +193 -0
new file mode 100644
index 0000000..cb587a0
@@ -0,0 +1,265 @@
+/*
+ * Copyright 2013 Emilio L?pez
+ *
+ * Emilio L?pez <[email protected]>
+ *
+ * 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,
+};

drivers/clk/sunxi/clk-sunxi.c line 203

Quoted 10 lines
drivers/clk/sunxi/clk-sunxi.c new file +10 -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.

Quoted 63 lines
drivers/clk/sunxi/clk-sunxi.c new file +62 -0
+
+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();
+}
Signature · 4 lines
Gregory Clement, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com
author maintainer
#6 lore raw

PATCH 1/3 clk: arm: sunxi: Add a new clock driver for sunxi SOCs

Hello Gregory,

El 08/02/13 08:33, Gregory CLEMENT escribi?:

Quoted 320 lines

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

Quoted 313 lines · Emilio L?pez

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
---
 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
Documentation/devicetree/bindings/clock/sunxi.txt new file +47 -0
new file mode 100644
index 0000000..446c5ca
@@ -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>;
+};
drivers/clk/Makefile +1 -1
index ee90e87..129afed 100644
@@ -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
drivers/clk/clk-sunxi.c deleted +0 -30
deleted file mode 100644
index 0e831b5..0000000
@@ -1,30 +0,0 @@
-/*
- * Copyright 2012 Maxime Ripard
- *
- * Maxime Ripard <[email protected]>
- *
- * 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);
-}
drivers/clk/sunxi/Makefile new file +5 -0
new file mode 100644
index 0000000..8e773be
@@ -0,0 +1,5 @@
+#
+# Makefile for sunxi specific clk
+#
+
+obj-y += clk-sunxi.o clk-factors.o clk-fixed-gate.o
drivers/clk/sunxi/clk-factors.c new file +132 -0
new file mode 100644
index 0000000..428b47d
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2013 Emilio L?pez <[email protected]>
+ *
+ * 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
Quoted 8 lines

drivers/clk/sunxi/clk-factors.c line 135

Quoted 3 lines
drivers/clk/sunxi/clk-factors.c new file +3 -0
+ * @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.

Quoted 452 lines
Quoted 452 lines
drivers/clk/sunxi/clk-factors.c new file +40 -0
+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;
+}
drivers/clk/sunxi/clk-factors.h new file +25 -0
new file mode 100644
index 0000000..a24c889
@@ -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
drivers/clk/sunxi/clk-fixed-gate.c new file +152 -0
new file mode 100644
index 0000000..b16eda5
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2013 Emilio L?pez <[email protected]>
+ *
+ * Based on drivers/clk/clk-gate.c,
+ *
+ * Copyright (C) 2010-2011 Canonical Ltd <[email protected]>
+ * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <[email protected]>
+ *
+ * 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;
+}
drivers/clk/sunxi/clk-fixed-gate.h new file +13 -0
new file mode 100644
index 0000000..29d9ed3
@@ -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
drivers/clk/sunxi/clk-sunxi.c new file +197 -0
new file mode 100644
index 0000000..cb587a0
@@ -0,0 +1,265 @@
+/*
+ * Copyright 2013 Emilio L?pez
+ *
+ * Emilio L?pez <[email protected]>
+ *
+ * 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,
Quoted 8 lines

drivers/clk/sunxi/clk-sunxi.c line 203

Quoted 6 lines
drivers/clk/sunxi/clk-sunxi.c new file +6 -0
+};
+
+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.

Quoted 4 lines

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.

Quoted 10 lines

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.

drivers/clk/sunxi/clk-sunxi.c line 206

Quoted 3 lines
drivers/clk/sunxi/clk-sunxi.c new file +3 -0
+
+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

Quoted 52 lines
Quoted 52 lines
drivers/clk/sunxi/clk-sunxi.c new file +52 -0
+{
+	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)
Quoted 8 lines

drivers/clk/sunxi/clk-sunxi.c line 265

Quoted 8 lines
drivers/clk/sunxi/clk-sunxi.c new file +7 -0
+{
+	/* 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

#7 lore raw

PATCH 1/3 clk: arm: sunxi: Add a new clock driver for sunxi SOCs

Quoted 332 lines · Emilio L?pez

Hello Gregory,

El 08/02/13 08:33, Gregory CLEMENT escribi?:

Quoted 320 lines

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

Quoted 313 lines · Emilio L?pez

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
---
 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
Documentation/devicetree/bindings/clock/sunxi.txt new file +47 -0
new file mode 100644
index 0000000..446c5ca
@@ -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>;
+};
drivers/clk/Makefile +1 -1
index ee90e87..129afed 100644
@@ -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
drivers/clk/clk-sunxi.c deleted +0 -30
deleted file mode 100644
index 0e831b5..0000000
@@ -1,30 +0,0 @@
-/*
- * Copyright 2012 Maxime Ripard
- *
- * Maxime Ripard <[email protected]>
- *
- * 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);
-}
drivers/clk/sunxi/Makefile new file +5 -0
new file mode 100644
index 0000000..8e773be
@@ -0,0 +1,5 @@
+#
+# Makefile for sunxi specific clk
+#
+
+obj-y += clk-sunxi.o clk-factors.o clk-fixed-gate.o
drivers/clk/sunxi/clk-factors.c new file +132 -0
new file mode 100644
index 0000000..428b47d
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2013 Emilio L?pez <[email protected]>
+ *
+ * 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
Quoted 8 lines

drivers/clk/sunxi/clk-factors.c line 135

Quoted 3 lines
drivers/clk/sunxi/clk-factors.c new file +3 -0
+ * @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.

Quoted 3 lines

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.

Quoted 462 lines
Quoted 453 lines
Quoted 453 lines
drivers/clk/sunxi/clk-factors.c new file +40 -0
+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;
+}
drivers/clk/sunxi/clk-factors.h new file +25 -0
new file mode 100644
index 0000000..a24c889
@@ -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
drivers/clk/sunxi/clk-fixed-gate.c new file +152 -0
new file mode 100644
index 0000000..b16eda5
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2013 Emilio L?pez <[email protected]>
+ *
+ * Based on drivers/clk/clk-gate.c,
+ *
+ * Copyright (C) 2010-2011 Canonical Ltd <[email protected]>
+ * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <[email protected]>
+ *
+ * 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;
+}
drivers/clk/sunxi/clk-fixed-gate.h new file +13 -0
new file mode 100644
index 0000000..29d9ed3
@@ -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
drivers/clk/sunxi/clk-sunxi.c new file +197 -0
new file mode 100644
index 0000000..cb587a0
@@ -0,0 +1,265 @@
+/*
+ * Copyright 2013 Emilio L?pez
+ *
+ * Emilio L?pez <[email protected]>
+ *
+ * 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,
Quoted 8 lines

drivers/clk/sunxi/clk-sunxi.c line 203

Quoted 6 lines
drivers/clk/sunxi/clk-sunxi.c new file +6 -0
+};
+
+static const __initconst struct div_data apb0_data = {
+	.div = 8,
+	.pow = 1,
+};

Can't you read this from a register?

Quoted 3 lines

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.

Quoted 76 lines
Quoted 5 lines

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.

Quoted 10 lines

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.

drivers/clk/sunxi/clk-sunxi.c line 206

Quoted 3 lines
drivers/clk/sunxi/clk-sunxi.c new file +3 -0
+
+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

Quoted 54 lines
Quoted 54 lines
drivers/clk/sunxi/clk-sunxi.c new file +54 -0
+{
+	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 */
Quoted 8 lines
Quoted 6 lines

drivers/clk/sunxi/clk-sunxi.c line 265

Quoted 6 lines
drivers/clk/sunxi/clk-sunxi.c new file +5 -0
+	of_clk_init(clk_match);
+
+	/* Register divider clocks */
+	of_sunxi_divider_clock_setup();
+}

Thanks for the review :)

You're welcome :)

Gregory

author maintainer
#8 lore raw

PATCH 2/3 arm: sunxi: Add clock definitions for the new clock driver

In reply to Emilio López

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

---
 arch/arm/boot/dts/sunxi.dtsi | 55 +++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 49 insertions(+), 6 deletions(-)
arch/arm/boot/dts/sunxi.dtsi +49 -6
index 8bbc2bf..e2cef13 100644
@@ -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";
 		};
 	};
Signature · 1 line
1.8.1.1
#9 lore raw

PATCH 2/3 arm: sunxi: Add clock definitions for the new clock driver

Hi Emilio,

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

arch/arm/boot/dts/sunxi.dtsi line 55

Quoted 6 lines
arch/arm/boot/dts/sunxi.dtsi +6 -0
+		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

Signature · 4 lines
Maxime Ripard, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com
author maintainer
#10 lore raw

PATCH 2/3 arm: sunxi: Add clock definitions for the new clock driver

Hi Maxime,

El 30/01/13 05:24, Maxime Ripard escribi?:

Quoted 4 lines

Hi Emilio,

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

Quoted 10 lines

arch/arm/boot/dts/sunxi.dtsi line 55

Quoted 6 lines
arch/arm/boot/dts/sunxi.dtsi +6 -0
+		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

author maintainer
#11 lore raw

PATCH 3/3 arm: sunxi: Add useful information about sunxi clocks

In reply to Emilio López

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

---
 Documentation/arm/sunxi/clocks.txt | 56 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 56 insertions(+)
 create mode 100644 Documentation/arm/sunxi/clocks.txt
Documentation/arm/sunxi/clocks.txt new file +56 -0
new file mode 100644
index 0000000..e09a88a
@@ -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
Signature · 1 line
1.8.1.1
#12 lore raw

cover clock driver for sunxi

In reply to Emilio López

Hi Emilio,

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

Quoted 10 lines

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

Signature · 4 lines
Maxime Ripard, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com
#13 lore raw

cover clock driver for sunxi

In reply to Emilio López

Hi Emilio,

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

Quoted 8 lines

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

Signature · 4 lines
Maxime Ripard, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com
author maintainer
#14 lore raw

v2 cover clock driver for sunxi

In reply to Emilio López

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
Signature · 1 line
1.8.2.rc0
author maintainer
#15 lore raw

PATCH v2 1/3 clk: arm: sunxi: Add a new clock driver for sunxi SOCs

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

---
 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
Documentation/devicetree/bindings/clock/sunxi.txt new file +44 -0
new file mode 100644
index 0000000..b23cfbd
@@ -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>;
+};
drivers/clk/Makefile +1 -0
index 300d477..db8b11b 100644
@@ -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
drivers/clk/sunxi/Makefile new file +5 -0
new file mode 100644
index 0000000..b5bac91
@@ -0,0 +1,5 @@
+#
+# Makefile for sunxi specific clk
+#
+
+obj-y += clk-sunxi.o clk-factors.o
drivers/clk/sunxi/clk-factors.c new file +180 -0

180 lines not on this page · raw message

drivers/clk/sunxi/clk-factors.h new file +27 -0
new file mode 100644
index 0000000..f49851c
@@ -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
drivers/clk/sunxi/clk-sunxi.c new file +362 -0

362 lines not on this page · raw message

drivers/clocksource/sunxi_timer.c +2 -2
index 4086b91..0ce85e2 100644
@@ -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))
include/linux/clk/sunxi.h new file +22 -0
new file mode 100644
index 0000000..e074fdd
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2012 Maxime Ripard
+ *
+ * Maxime Ripard <[email protected]>
+ *
+ * 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
Signature · 1 line
1.8.2.rc0
author maintainer
#16 lore raw

PATCH v2 2/3 arm: sunxi: Add clock definitions for the new clock driver

In reply to Emilio López

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

---
 arch/arm/boot/dts/sunxi.dtsi | 83 ++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 80 insertions(+), 3 deletions(-)
arch/arm/boot/dts/sunxi.dtsi +80 -3
index 8b36abe..acf7777 100644
@@ -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 {
Signature · 1 line
1.8.2.rc0
author maintainer
#17 lore raw

PATCH v2 3/3 arm: sunxi: Add useful information about sunxi clocks

In reply to Emilio López

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

---
 Documentation/arm/sunxi/clocks.txt | 56 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 56 insertions(+)
 create mode 100644 Documentation/arm/sunxi/clocks.txt
Documentation/arm/sunxi/clocks.txt new file +56 -0
new file mode 100644
index 0000000..e09a88a
@@ -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
Signature · 1 line
1.8.2.rc0
author maintainer
#18 lore raw

v2 cover clock driver for sunxi

In reply to Emilio López

El 25/02/13 11:44, Emilio L?pez escribi?:

Quoted 40 lines

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 ++
Quoted 9 lines
 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

#19 lore raw

v2 cover clock driver for sunxi

In reply to Emilio López

Hi Mike,

Le 25/02/2013 15:44, Emilio L?pez a ?crit :

Quoted 9 lines

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).

Quoted 3 lines

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

Signature · 4 lines
Maxime Ripard, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com
#20 lore raw

v2 cover clock driver for sunxi

In reply to Emilio López

Quoting Emilio L?pez (2013-02-25 06:44:25)

Quoted 9 lines

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).

Quoted 6 lines

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

Quoted 36 lines

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
Signature · 1 line
1.8.2.rc0
#21 lore raw

v2 cover clock driver for sunxi

Hi Mike,

Le 21/03/2013 22:54, Mike Turquette a ?crit :

Quoted 29 lines

Quoting Emilio L?pez (2013-02-25 06:44:25)

Quoted 9 lines

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).

Quoted 6 lines

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.

Quoted 2 lines

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

Signature · 3 lines
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com
#22 lore raw

v2 cover clock driver for sunxi

Quoting Maxime Ripard (2013-03-22 03:22:25)

Quoted 32 lines

Hi Mike,

Le 21/03/2013 22:54, Mike Turquette a ?crit :

Quoted 29 lines

Quoting Emilio L?pez (2013-02-25 06:44:25)

Quoted 9 lines

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).

Quoted 6 lines

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.

Quoted 8 lines
Quoted 2 lines

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.

Quoted 6 lines

Maxime

Signature · 3 lines
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com
#23 lore raw

v2 cover clock driver for sunxi

Le 22/03/2013 17:40, Mike Turquette a ?crit :

Quoted 1 line

Thanks Mike,

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

Maxime

Signature · 3 lines
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com
#24 lore raw

v2 cover clock driver for sunxi

In reply to Mike Turquette

Hi Mike,

Le 22/03/2013 17:40, Mike Turquette a ?crit :

Quoted 36 lines

Quoting Maxime Ripard (2013-03-22 03:22:25)

Quoted 35 lines

Hi Mike,

Le 21/03/2013 22:54, Mike Turquette a ?crit :

Quoted 31 lines

Quoting Emilio L?pez (2013-02-25 06:44:25)

Quoted 9 lines

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).

Quoted 6 lines

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?

Quoted 7 lines
Quoted 5 lines

Thanks for your feedback!

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

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

Signature · 3 lines
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com
#25 lore raw

v2 cover clock driver for sunxi

Quoting Maxime Ripard (2013-03-26 02:20:21)

Quoted 41 lines

Hi Mike,

Le 22/03/2013 17:40, Mike Turquette a ?crit :

Quoted 38 lines

Quoting Maxime Ripard (2013-03-22 03:22:25)

Quoted 37 lines

Hi Mike,

Le 21/03/2013 22:54, Mike Turquette a ?crit :

Quoted 29 lines

Quoting Emilio L?pez (2013-02-25 06:44:25)

Quoted 9 lines

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).

Quoted 6 lines

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.

Quoted 2 lines

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!

Quoted 9 lines
Quoted 5 lines
Quoted 3 lines

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

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

Quoted 7 lines

Thanks,
Maxime

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