clock driver for sunxi
Subsystems (6)
Emilio López
maintainercover 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
Emilio López
maintainerPATCH 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
32768After "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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
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 32768After "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 32768Signed-off-by Emilio L?pez <[email protected]>--- 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.cDocumentation/devicetree/bindings/clock/sunxi.txt new file +47
-0@@ -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@@ -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 specificdrivers/clk/clk-sunxi.c deleted +0
-30@@ -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@@ -0,0 +1,5 @@ +# +# Makefile for sunxi specific clk +# + +obj-y += clk-sunxi.o clk-factors.o clk-fixed-gate.odrivers/clk/sunxi/clk-factors.c new file +175
-0@@ -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@@ -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@@ -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
Emilio López
maintainerPATCH 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 32768After "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 32768Signed-off-by Emilio L?pez <[email protected]>--- 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.cDocumentation/devicetree/bindings/clock/sunxi.txt new file +47
-0@@ -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@@ -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 specificdrivers/clk/clk-sunxi.c deleted +0
-30@@ -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@@ -0,0 +1,5 @@ +# +# Makefile for sunxi specific clk +# + +obj-y += clk-sunxi.o clk-factors.o clk-fixed-gate.odrivers/clk/sunxi/clk-factors.c new file +175
-0@@ -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@@ -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@@ -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.candclk-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_gateyou can use theclk_register_gateas 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
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.candclk-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_gateyou can use theclk_register_gateas 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
PATCH 1/3 clk: arm: sunxi: Add a new clock driver for sunxi SOCs
In reply to Emilio LópezHi 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 32768After "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 32768Signed-off-by Emilio L?pez <[email protected]>--- 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.cDocumentation/devicetree/bindings/clock/sunxi.txt new file +47
-0@@ -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@@ -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 specificdrivers/clk/clk-sunxi.c deleted +0
-30@@ -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@@ -0,0 +1,5 @@ +# +# Makefile for sunxi specific clk +# + +obj-y += clk-sunxi.o clk-factors.o clk-fixed-gate.odrivers/clk/sunxi/clk-factors.c new file +125
-0@@ -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@@ -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@@ -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@@ -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@@ -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
Emilio López
maintainerPATCH 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 32768After "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 32768Signed-off-by Emilio L?pez <[email protected]>--- 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.cDocumentation/devicetree/bindings/clock/sunxi.txt new file +47
-0@@ -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@@ -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 specificdrivers/clk/clk-sunxi.c deleted +0
-30@@ -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@@ -0,0 +1,5 @@ +# +# Makefile for sunxi specific clk +# + +obj-y += clk-sunxi.o clk-factors.o clk-fixed-gate.odrivers/clk/sunxi/clk-factors.c new file +132
-0@@ -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@@ -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@@ -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@@ -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@@ -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
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 32768After "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 32768Signed-off-by Emilio L?pez <[email protected]>--- 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.cDocumentation/devicetree/bindings/clock/sunxi.txt new file +47
-0@@ -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@@ -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 specificdrivers/clk/clk-sunxi.c deleted +0
-30@@ -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@@ -0,0 +1,5 @@ +# +# Makefile for sunxi specific clk +# + +obj-y += clk-sunxi.o clk-factors.o clk-fixed-gate.odrivers/clk/sunxi/clk-factors.c new file +132
-0@@ -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@@ -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@@ -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@@ -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@@ -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.htmlThese 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_twoQuoted 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
Emilio López
maintainerPATCH 2/3 arm: sunxi: Add clock definitions for the new clock driver
In reply to Emilio LópezThis 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
@@ -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
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
Emilio López
maintainerPATCH 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:
Thanks for the review,
Emilio
Emilio López
maintainerPATCH 3/3 arm: sunxi: Add useful information about sunxi clocks
In reply to Emilio LópezThis 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
@@ -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
cover clock driver for sunxi
In reply to Emilio LópezHi 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
cover clock driver for sunxi
In reply to Emilio LópezHi 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
Emilio López
maintainerv2 cover clock driver for sunxi
In reply to Emilio LópezHello 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
Emilio López
maintainerPATCH 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
Emilio López
maintainerPATCH v2 2/3 arm: sunxi: Add clock definitions for the new clock driver
In reply to Emilio LópezThis 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
@@ -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
Emilio López
maintainerPATCH v2 3/3 arm: sunxi: Add useful information about sunxi clocks
In reply to Emilio LópezThis 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
@@ -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
Emilio López
maintainerv2 cover clock driver for sunxi
In reply to Emilio LópezEl 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 GregoryEmilio 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 clocksDocumentation/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
v2 cover clock driver for sunxi
In reply to Emilio LópezHi 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
v2 cover clock driver for sunxi
In reply to Emilio LópezQuoting 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 GregoryEmilio 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 clocksDocumentation/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.hSignature · 1 line
1.8.2.rc0
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
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
v2 cover clock driver for sunxi
Le 22/03/2013 17:40, Mike Turquette a ?crit :
Quoted 1 line
Acked-by Mike Turquette <[email protected]>
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
v2 cover clock driver for sunxi
In reply to Mike TurquetteHi 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.
Acked-by Mike Turquette <[email protected]>
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
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.
Acked-by Mike Turquette <[email protected]>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,
MaximeSignature · 3 lines
Maxime Ripard, Free Electrons Embedded Linux, Kernel and Android engineering http://free-electrons.com