clk: sunxi: Improve MMC clocks support
Subsystems (6)
v3 cover clk: sunxi: Improve MMC clocks support
Hi everyone,
Here is an attempt at improving the MMC clock support in the Allwinner SoCs.
Until now, the MMC clocks were having a custom phase function that was directly setting an obscure value in the right register, because we were not really having any idea of what these values were.
Now that we have more informations, we can introduce a common function call to get and set the phase of a particular clock, and use this in both our provider and our client.
Thanks,
Maxime
Changes from v2:
- Reworked the get_phase operation handling to call it from _clk_init instead of the ugly hack
- Added the clocks to the A23
- Modified the output and sample clock names to have a consistent naming
Changes from v1:
- Dropped the patches to enforce rate boundaries on the clocks. This is partly covered by the Tomeu, and should be based on this whenever it will be merged.
- Fixed a few typos here and there
- Added a patch to include of.h from clk-provider.h
Maxime Ripard (11):
clk: Include of.h in clock-provider.h
clk: Add a function to retrieve phase
clk: sunxi: factors: Invert the probing logic
clk: sunxi: Introduce mbus compatible
ARM: sunxi: dt: Switch to the new mbus compatible
clk: sunxi: Move mod0 clock to a file of its own
clk: sunxi: Move mbus to mod0 file
ARM: sunxi: dt: Add sample and output mmc clocks
clk: sunxi: mod0: Introduce MMC proper phase handling
mmc: sunxi: Convert MMC driver to the standard clock phase API
clk: sunxi: Remove custom phase function
Mike Turquette (1):
clk: introduce clk_set_phase function & callback
Documentation/devicetree/bindings/clock/sunxi.txt | 3 + .../devicetree/bindings/mmc/sunxi-mmc.txt | 8 +- arch/arm/boot/dts/sun4i-a10.dtsi | 104 +++++++- arch/arm/boot/dts/sun5i-a10s.dtsi | 81 +++++- arch/arm/boot/dts/sun5i-a13.dtsi | 82 +++++- arch/arm/boot/dts/sun6i-a31.dtsi | 104 +++++++- arch/arm/boot/dts/sun7i-a20.dtsi | 106 +++++++- arch/arm/boot/dts/sun8i-a23.dtsi | 78 +++++- drivers/clk/clk.c | 95 ++++++- drivers/clk/sunxi/Makefile | 1 + drivers/clk/sunxi/clk-factors.c | 101 +++++++- drivers/clk/sunxi/clk-factors.h | 16 +- drivers/clk/sunxi/clk-mod0.c | 283 +++++++++++++++++++++ drivers/clk/sunxi/clk-sunxi.c | 189 +------------- drivers/mmc/host/sunxi-mmc.c | 72 ++++-- include/linux/clk-private.h | 1 + include/linux/clk-provider.h | 11 + include/linux/clk.h | 29 +++ include/linux/clk/sunxi.h | 22 -- 19 files changed, 1090 insertions(+), 296 deletions(-) create mode 100644 drivers/clk/sunxi/clk-mod0.c delete mode 100644 include/linux/clk/sunxi.h
Signature · 1 line
2.1.0
PATCH v3 1/12 clk: introduce clk_set_phase function & callback
From: Mike Turquette <[email protected]>
A common operation for a clock signal generator is to shift the phase of that signal. This patch introduces a new function to the clk.h API to dynamically adjust the phase of a clock signal. Additionally this patch introduces support for the new function in the common clock framework via the .set_phase call back in struct clk_ops.
--- drivers/clk/clk.c | 85 +++++++++++++++++++++++++++++++++++++++++--- include/linux/clk-private.h | 1 + include/linux/clk-provider.h | 5 +++ include/linux/clk.h | 29 +++++++++++++++ 4 files changed, 116 insertions(+), 4 deletions(-)
drivers/clk/clk.c +81 -4
@@ -117,11 +117,11 @@ static void clk_summary_show_one(struct seq_file *s, struct clk *c, int level)if (!c)return ; -seq_printf (s,"%*s%-*s %11d %12d %11lu %10lu\n" , +seq_printf (s,"%*s%-*s %11d %12d %11lu %10lu %-3d\n" , level *3 +1 ,"" ,30 - level *3 , c->name, c->enable_count, c->prepare_count,clk_get_rate (c), -clk_get_accuracy (c)); +clk_get_accuracy (c),clk_get_phase (c)); } static void clk_summary_show_subtree(struct seq_file *s, struct clk *c,
@@ -143,8 +143,8 @@ static int clk_summary_show(struct seq_file *s, void *data)struct clk *c;struct hlist_head **lists = (struct hlist_head **)s->private; -seq_puts (s," clock enable_cnt prepare_cnt rate accuracy\n" ); -seq_puts (s,"--------------------------------------------------------------------------------\n" ); +seq_puts (s," clock enable_cnt prepare_cnt rate accuracy phase\n" ); +seq_puts (s,"----------------------------------------------------------------------------------------\n" );clk_prepare_lock ();
@@ -180,6 +180,7 @@ static void clk_dump_one(struct seq_file *s, struct clk *c, int level)seq_printf (s,"\"prepare_count\": %d," , c->prepare_count);seq_printf (s,"\"rate\": %lu" ,clk_get_rate (c));seq_printf (s,"\"accuracy\": %lu" ,clk_get_accuracy (c)); +seq_printf (s,"\"phase\": %d" ,clk_get_phase (c)); }static void clk_dump_subtree (struct seq_file *s,struct clk *c,int level)
@@ -264,6 +265,11 @@ static int clk_debug_create_one(struct clk *clk, struct dentry *pdentry)if (!d) goto err_out; + d =debugfs_create_u32 ("clk_phase" ,S_IRUGO , clk->dentry, + (u32 *)&clk->phase); +if (!d) + goto err_out; + d =debugfs_create_x32 ("clk_flags" ,S_IRUGO , clk->dentry, (u32 *)&clk->flags);if (!d)
@@ -1739,6 +1745,77 @@ out:EXPORT_SYMBOL_GPL (clk_set_parent);/** +* clk_set_phase - adjust the phase shift of a clock signal +* @clk: clock signal source +* @degrees: number of degrees the signal is shifted +* +* Shifts the phase of a clock signal by the specified +* degrees. Returns 0 on success, -EERROR otherwise. +* +* This function makes no distinction about the input or reference +* signal that we adjust the clock signal phase against. For example +* phase locked-loop clock signal generators we may shift phase with +* respect to feedback clock signal input, but for other cases the +* clock phase may be shifted with respect to some other, unspecified +* signal. +* +* Additionally the concept of phase shift does not propagate through +* the clock tree hierarchy, which sets it apart from clock rates and +* clock accuracy. A parent clock phase attribute does not have an +* impact on the phase attribute of a child clock. +*/ +int clk_set_phase (struct clk *clk,int degrees) +{ +int ret =0 ; + +if (!clk) + goto out; + +/* sanity check degrees */ + degrees %=360 ; +if (degrees <0 ) + degrees +=360 ; + +clk_prepare_lock (); + +if (!clk->ops->set_phase) + goto out_unlock; + + ret = clk->ops->set_phase (clk->hw, degrees); + +if (!ret) + clk->phase = degrees; + +out_unlock: +clk_prepare_unlock (); + +out: +return ret; +} + +/** +* clk_get_phase - return the phase shift of a clock signal +* @clk: clock signal source +* +* Returns the phase shift of a clock node in degrees, otherwise returns +* -EERROR. +*/ +int clk_get_phase (struct clk *clk) +{ +int ret =0 ; + +if (!clk) + goto out; + +clk_prepare_lock (); + ret = clk->phase; +clk_prepare_unlock (); + +out: +return ret; +} + +/** * __clk_init - initialize the data structures in a struct clk * @dev: device initializing this clk, placeholder for now * @clk: clk being initialized
include/linux/clk-private.h +1 -0
@@ -46,6 +46,7 @@ struct clk {unsigned int enable_count;unsigned int prepare_count;unsigned long accuracy; +int phase;struct hlist_head children;struct hlist_node child_node;unsigned int notifier_count;
include/linux/clk-provider.h +5 -0
@@ -129,6 +129,10 @@ struct dentry;* set then clock accuracy will be initialized to parent accuracy * or 0 (perfect clock) if clock has no parent. * +* @set_phase: Shift the phase this clock signal in degrees specified +* by the second argument. Valid values for degrees are +* 0-359. Return 0 on success, otherwise -EERROR. +* * @init: Perform platform-specific initialization magic. * This is not not used by any of the basic clock types. * Please consider other ways of solving initialization problems
@@ -177,6 +181,7 @@ struct clk_ops { unsigned long parent_rate, u8 index);unsigned long (*recalc_accuracy)(struct clk_hw *hw,unsigned long parent_accuracy); +int (*set_phase)(struct clk_hw *hw,int degrees);void (*init)(struct clk_hw *hw);int (*debug_init)(struct clk_hw *hw,struct dentry *dentry); };
include/linux/clk.h +29 -0
@@ -106,6 +106,25 @@ int clk_notifier_unregister(struct clk *clk, struct notifier_block *nb);*/ long clk_get_accuracy (struct clk *clk); +/** +* clk_set_phase - adjust the phase shift of a clock signal +* @clk: clock signal source +* @degrees: number of degrees the signal is shifted +* +* Shifts the phase of a clock signal by the specified degrees. Returns 0 on +* success, -EERROR otherwise. +*/ +int clk_set_phase (struct clk *clk,int degrees); + +/** +* clk_get_phase - return the phase shift of a clock signal +* @clk: clock signal source +* +* Returns the phase shift of a clock node in degrees, otherwise returns +* -EERROR. +*/ +int clk_get_phase (struct clk *clk); +#else static inline long clk_get_accuracy (struct clk *clk)
@@ -113,6 +132,16 @@ static inline long clk_get_accuracy(struct clk *clk) return -ENOTSUPP; } +static inline long clk_set_phase (struct clk *clk,int phase) +{ +return -ENOTSUPP ; +} + +static inline long clk_get_phase (struct clk *clk) +{ +return -ENOTSUPP ; +} +#endif /**
Signature · 1 line
2.1.0
PATCH v3 1/12 clk: introduce clk_set_phase function & callback
Am Donnerstag, 11. September 2014, 22:18:15 schrieb Maxime Ripard:
Quoted 10 lines
From: Mike Turquette <[email protected]>
A common operation for a clock signal generator is to shift the phase of that signal. This patch introduces a new function to the
clk.hAPI to dynamically adjust the phase of a clock signal. Additionally this patch introduces support for the new function in the common clock framework via the .set_phase call back in structclk_ops.Signed-off-by Mike Turquette <[email protected]>Signed-off-by Maxime Ripard <[email protected]>
This looks nice and would also be very useful for Rockchip SoCs I'm working on. And the implementation looks very straightforward.
Quoted 220 lines
--- drivers/clk/clk.c | 85 +++++++++++++++++++++++++++++++++++++++++--- include/linux/clk-private.h | 1 + include/linux/clk-provider.h | 5 +++ include/linux/clk.h | 29 +++++++++++++++ 4 files changed, 116 insertions(+), 4 deletions(-)drivers/clk/clk.c +2
-3@@ -117,11 +117,11 @@ static void clk_summary_show_one(struct seq_file *s, struct clk *c, int level) if (!c) return; -seq_printf (s,"%*s%-*s %11d %12d %11lu %10lu\n" , +seq_printf (s,"%*s%-*s %11d %12d %11lu %10lu %-3d\n" , level *3 +1 ,"" ,30 - level *3 , c->name, c->enable_count, c->prepare_count,clk_get_rate (c), -clk_get_accuracy (c)); +clk_get_accuracy (c),clk_get_phase (c)); } static void clk_summary_show_subtree(struct seq_file *s, struct clk *c,@@ -143,8 +143,8 @@ static int clk_summary_show(struct seq_file *s, void *data) struct clk *c;struct hlist_head **lists = (struct hlist_head **)s->private; - seq_puts(s, " clock enable_cnt prepare_cnt rate accuracy\n"); - seq_puts(s,"--------------------------------------------------------------------------
------\n"); + seq_puts(s, " clockenable_cntprepare_cntrate accuracy phase\n"); + seq_puts(s,
"-------------------------------------------------------------------------- --------------\n");drivers/clk/clk.c +77
-0clk_prepare_lock ();@@ -180,6 +180,7 @@ static void clk_dump_one(struct seq_file *s, struct clk *c, int level) seq_printf(s, "\"prepare_count\": %d,", c->prepare_count);seq_printf (s,"\"rate\": %lu" ,clk_get_rate (c));seq_printf (s,"\"accuracy\": %lu" ,clk_get_accuracy (c)); +seq_printf (s,"\"phase\": %d" ,clk_get_phase (c)); }static void clk_dump_subtree (struct seq_file *s,struct clk *c,int level)@@ -264,6 +265,11 @@ static int clk_debug_create_one(struct clk *clk, struct dentry *pdentry) if (!d) goto err_out; + d =debugfs_create_u32 ("clk_phase" ,S_IRUGO , clk->dentry, + (u32 *)&clk->phase); +if (!d) + goto err_out; + d =debugfs_create_x32 ("clk_flags" ,S_IRUGO , clk->dentry, (u32 *)&clk->flags);if (!d)@@ -1739,6 +1745,77 @@ out:EXPORT_SYMBOL_GPL (clk_set_parent);/** +* clk_set_phase - adjust the phase shift of a clock signal +* @clk: clock signal source +* @degrees: number of degrees the signal is shifted +* +* Shifts the phase of a clock signal by the specified +* degrees. Returns 0 on success, -EERROR otherwise. +* +* This function makes no distinction about the input or reference +* signal that we adjust the clock signal phase against. For example +* phase locked-loop clock signal generators we may shift phase with +* respect to feedback clock signal input, but for other cases the +* clock phase may be shifted with respect to some other, unspecified +* signal. +* +* Additionally the concept of phase shift does not propagate through +* the clock tree hierarchy, which sets it apart from clock rates and +* clock accuracy. A parent clock phase attribute does not have an +* impact on the phase attribute of a child clock. +*/ +int clk_set_phase (struct clk *clk,int degrees) +{ +int ret =0 ; + +if (!clk) + goto out; + +/* sanity check degrees */ + degrees %=360 ; +if (degrees <0 ) + degrees +=360 ; + +clk_prepare_lock (); + +if (!clk->ops->set_phase) + goto out_unlock; + + ret = clk->ops->set_phase (clk->hw, degrees); + +if (!ret) + clk->phase = degrees; + +out_unlock: +clk_prepare_unlock (); + +out: +return ret; +} + +/** +* clk_get_phase - return the phase shift of a clock signal +* @clk: clock signal source +* +* Returns the phase shift of a clock node in degrees, otherwise returns +* -EERROR. +*/ +int clk_get_phase (struct clk *clk) +{ +int ret =0 ; + +if (!clk) + goto out; + +clk_prepare_lock (); + ret = clk->phase; +clk_prepare_unlock (); + +out: +return ret; +} + +/** * __clk_init - initialize the data structures in a struct clk * @dev: device initializing this clk, placeholder for now * @clk: clk being initialized include/linux/clk-private.h +1
-0@@ -46,6 +46,7 @@ struct clk {unsigned int enable_count;unsigned int prepare_count;unsigned long accuracy; +int phase;struct hlist_head children;struct hlist_node child_node;unsigned int notifier_count;include/linux/clk-provider.h +5
-0@@ -129,6 +129,10 @@ struct dentry;* set then clock accuracy will be initialized to parent accuracy * or 0 (perfect clock) if clock has no parent. * +* @set_phase: Shift the phase this clock signal in degrees specified +* by the second argument. Valid values for degrees are +* 0-359. Return 0 on success, otherwise -EERROR. +* * @init: Perform platform-specific initialization magic. * This is not not used by any of the basic clock types. * Please consider other ways of solving initialization problems @@ -177,6 +181,7 @@ struct clk_ops { unsigned long parent_rate, u8 index);unsigned long (*recalc_accuracy)(struct clk_hw *hw,unsigned long parent_accuracy); +int (*set_phase)(struct clk_hw *hw,int degrees);void (*init)(struct clk_hw *hw);int (*debug_init)(struct clk_hw *hw,struct dentry *dentry); };include/linux/clk.h +0
-0@@ -106,6 +106,25 @@ int clk_notifier_unregister(struct clk *clk, struct
notifier_block*nb); */
longclk_get_accuracy(struct clk *clk);+/**
+ *clk_set_phase- adjust the phase shift of a clock signal
+ * @clk: clock signal source
+ * @degrees: number of degrees the signal is shifted
+ *
+ * Shifts the phase of a clock signal by the specified degrees. Returns 0 on + * success,-EERRORotherwise.
+ */include/linux/clk.h +21
-0+int clk_set_phase (struct clk *clk,int degrees); + +/** +* clk_get_phase - return the phase shift of a clock signal +* @clk: clock signal source +* +* Returns the phase shift of a clock node in degrees, otherwise returns +* -EERROR. +*/ +int clk_get_phase (struct clk *clk); +#else static inline long clk_get_accuracy (struct clk *clk)@@ -113,6 +132,16 @@ static inline long clk_get_accuracy(struct clk *clk) return -ENOTSUPP; } +static inline long clk_set_phase (struct clk *clk,int phase) +{ +return -ENOTSUPP ; +} + +static inline long clk_get_phase (struct clk *clk) +{ +return -ENOTSUPP ; +} +#endif /**
PATCH v3 2/12 clk: Include of.h in clock-provider.h
In reply to Maxime RipardCLK_OF_DECLARE relies on OF_DECLARE_1 that is defined in of.h. Fixes build errors when one use CLK_OF_DECLARE but doesn't include of.h
--- include/linux/clk-provider.h | 1 + 1 file changed, 1 insertion(+)
include/linux/clk-provider.h +1 -0
@@ -13,6 +13,7 @@#include <linux/clk.h> #include <linux/io.h> +#include <linux/of.h> #ifdef CONFIG_COMMON_CLK
Signature · 1 line
2.1.0
PATCH v3 3/12 clk: Add a function to retrieve phase
In reply to Maxime RipardThe current phase API doesn't look into the actual hardware to get the phase value, but will rather get it from a variable only set by the set_phase function.
This will cause issue when the client driver will never call the set_phase function, where we can end up having a reported phase that will not match what the hardware has been programmed to by the bootloader or what phase is programmed out of reset.
Add a new get_phase function for the drivers to implement so that we can get this value.
--- drivers/clk/clk.c | 10 ++++++++++ include/linux/clk-provider.h | 5 +++++ 2 files changed, 15 insertions(+)
drivers/clk/clk.c +10 -0
@@ -1934,6 +1934,16 @@ int __clk_init(struct device *dev, struct clk *clk) clk->accuracy =0 ;/* +* Set clk's phase. +* Since a phase is by definition relative to its parent, just +* query the current clock phase, or just assume it's in phase. +*/ +if (clk->ops->get_phase) + clk->phase = clk->ops->get_phase (clk->hw); +else + clk->phase =0 ; + +/* * Set clk's rate. The preferred method is to use .recalc_rate. For * simple clocks and lazy developers the default fallback is to use the * parent's rate. If a clock doesn't have a parent (or is orphaned)
include/linux/clk-provider.h +5 -0
@@ -130,6 +130,10 @@ struct dentry;* set then clock accuracy will be initialized to parent accuracy * or 0 (perfect clock) if clock has no parent. * +* @get_phase: Queries the hardware to get the current phase of a clock. +* Returned values are 0-359 degrees on success, negative +* error codes on failure. +* * @set_phase: Shift the phase this clock signal in degrees specified * by the second argument. Valid values for degrees are * 0-359. Return 0 on success, otherwise -EERROR.
@@ -182,6 +186,7 @@ struct clk_ops { unsigned long parent_rate, u8 index);unsigned long (*recalc_accuracy)(struct clk_hw *hw,unsigned long parent_accuracy); +int (*get_phase)(struct clk_hw *hw);int (*set_phase)(struct clk_hw *hw,int degrees);void (*init)(struct clk_hw *hw);int (*debug_init)(struct clk_hw *hw,struct dentry *dentry);
Signature · 1 line
2.1.0
PATCH v3 3/12 clk: Add a function to retrieve phase
Am Donnerstag, 11. September 2014, 22:18:17 schrieb Maxime Ripard:
Quoted 9 lines
The current phase API doesn't look into the actual hardware to get the phase value, but will rather get it from a variable only set by the
set_phasefunction.This will cause issue when the client driver will never call the
set_phasefunction, where we can end up having a reported phase that will not match what the hardware has been programmed to by the bootloader or what phase is programmed out of reset.
Quoted 4 lines
Add a new
get_phasefunction for the drivers to implement so that we can get this value.Signed-off-by Maxime Ripard <[email protected]>
Quoted 49 lines
--- drivers/clk/clk.c | 10 ++++++++++ include/linux/clk-provider.h | 5 +++++ 2 files changed, 15 insertions(+)drivers/clk/clk.c +10
-0@@ -1934,6 +1934,16 @@ int __clk_init(struct device *dev, struct clk *clk) clk->accuracy =0 ;/* +* Set clk's phase. +* Since a phase is by definition relative to its parent, just +* query the current clock phase, or just assume it's in phase. +*/ +if (clk->ops->get_phase) + clk->phase = clk->ops->get_phase (clk->hw); +else + clk->phase =0 ; + +/* * Set clk's rate. The preferred method is to use .recalc_rate. For * simple clocks and lazy developers the default fallback is to use the * parent's rate. If a clock doesn't have a parent (or is orphaned) include/linux/clk-provider.h +5
-0@@ -130,6 +130,10 @@ struct dentry;* set then clock accuracy will be initialized to parent accuracy * or 0 (perfect clock) if clock has no parent. * +* @get_phase: Queries the hardware to get the current phase of a clock. +* Returned values are 0-359 degrees on success, negative +* error codes on failure. +* * @set_phase: Shift the phase this clock signal in degrees specified * by the second argument. Valid values for degrees are * 0-359. Return 0 on success, otherwise -EERROR. @@ -182,6 +186,7 @@ struct clk_ops { unsigned long parent_rate, u8 index);unsigned long (*recalc_accuracy)(struct clk_hw *hw,unsigned long parent_accuracy); +int (*get_phase)(struct clk_hw *hw);int (*set_phase)(struct clk_hw *hw,int degrees);void (*init)(struct clk_hw *hw);int (*debug_init)(struct clk_hw *hw,struct dentry *dentry);
PATCH v3 3/12 clk: Add a function to retrieve phase
In reply to Maxime RipardQuoting Maxime Ripard (2014-09-11 13:18:17)
Quoted 24 lines
The current phase API doesn't look into the actual hardware to get the phase value, but will rather get it from a variable only set by the
set_phasefunction.This will cause issue when the client driver will never call the
set_phasefunction, where we can end up having a reported phase that will not match what the hardware has been programmed to by the bootloader or what phase is programmed out of reset.Add a new
get_phasefunction for the drivers to implement so that we can get this value.Signed-off-by Maxime Ripard <[email protected]>--- drivers/clk/clk.c | 10 ++++++++++ include/linux/clk-provider.h | 5 +++++ 2 files changed, 15 insertions(+)drivers/clk/clk.c +0
-0@@ -1934,6 +1934,16 @@ int __clk_init(struct device *dev, struct clk *clk) clk->accuracy =0 ;
drivers/clk/clk.c int __clk_init(struct device *dev, struct clk *clk) · line 1944
Quoted 10 lines
drivers/clk/clk.c +8
-0/* +* Set clk's phase. +* Since a phase is by definition relative to its parent, just +* query the current clock phase, or just assume it's in phase. +*/ +if (clk->ops->get_phase) + clk->phase = clk->ops->get_phase (clk->hw); +else + clk->phase =0 ;
I probably wasn't paying enough attention to this. Based on the coverletter I thought that v3 would introduce something like CLK_GET_PHASE_NOCACHE and stuff that logic into a __clk_get_phase function? Looks like this version of the series doesn't handle phases that can be changed by hardware (e.g. coming out of idle).
Correct me if I am wrong, but it looks like we retrieve the phase only once at init and then never again? The rest of the time we rely on clk->phase to always be accurate...
Regards,
Mike
Quoted 31 lines
drivers/clk/clk.c +2
-0+ +/* * Set clk's rate. The preferred method is to use .recalc_rate. For * simple clocks and lazy developers the default fallback is to use the * parent's rate. If a clock doesn't have a parent (or is orphaned) include/linux/clk-provider.h +5
-0@@ -130,6 +130,10 @@ struct dentry;* set then clock accuracy will be initialized to parent accuracy * or 0 (perfect clock) if clock has no parent. * +* @get_phase: Queries the hardware to get the current phase of a clock. +* Returned values are 0-359 degrees on success, negative +* error codes on failure. +* * @set_phase: Shift the phase this clock signal in degrees specified * by the second argument. Valid values for degrees are * 0-359. Return 0 on success, otherwise -EERROR. @@ -182,6 +186,7 @@ struct clk_ops { unsigned long parent_rate, u8 index);unsigned long (*recalc_accuracy)(struct clk_hw *hw,unsigned long parent_accuracy); +int (*get_phase)(struct clk_hw *hw);int (*set_phase)(struct clk_hw *hw,int degrees);void (*init)(struct clk_hw *hw);int (*debug_init)(struct clk_hw *hw,struct dentry *dentry);Signature · 1 line
2.1.0
PATCH v3 3/12 clk: Add a function to retrieve phase
Hi Mike,
Quoted 42 lines · Mike Turquette
Quoting Maxime Ripard (2014-09-11 13:18:17)
Quoted 24 lines
The current phase API doesn't look into the actual hardware to get the phase value, but will rather get it from a variable only set by the
set_phasefunction.This will cause issue when the client driver will never call the
set_phasefunction, where we can end up having a reported phase that will not match what the hardware has been programmed to by the bootloader or what phase is programmed out of reset.Add a new
get_phasefunction for the drivers to implement so that we can get this value.Signed-off-by Maxime Ripard <[email protected]>--- drivers/clk/clk.c | 10 ++++++++++ include/linux/clk-provider.h | 5 +++++ 2 files changed, 15 insertions(+)drivers/clk/clk.c +0
-0@@ -1934,6 +1934,16 @@ int __clk_init(struct device *dev, struct clk *clk) clk->accuracy =0 ;drivers/clk/clk.c int __clk_init(struct device *dev, struct clk *clk) · line 1944
Quoted 10 lines
drivers/clk/clk.c +8
-0/* +* Set clk's phase. +* Since a phase is by definition relative to its parent, just +* query the current clock phase, or just assume it's in phase. +*/ +if (clk->ops->get_phase) + clk->phase = clk->ops->get_phase (clk->hw); +else + clk->phase =0 ;I probably wasn't paying enough attention to this. Based on the coverletter I thought that v3 would introduce something like
CLK_GET_PHASE_NOCACHEand stuff that logic into a__clk_get_phasefunction? Looks like this version of the series doesn't handle phases that can be changed by hardware (e.g. coming out of idle).
Quoted 3 lines
Correct me if I am wrong, but it looks like we retrieve the phase only once at init and then never again? The rest of the time we rely on
clk->phaseto always be accurate...
Yeah, I didn't have any use for it at the moment, and thought that if someone ever needed it, it would be trivial to add, so I left it out.
If you want me to still do it, I can send an additional patch.
Thanks!
Maxime
Signature · 10 lines
Maxime Ripard, Free Electrons Embedded Linux, Kernel and Android engineering http://free-electrons.com -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 819 bytes Desc: Digital signature URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140929/fa5f33c9/attachment.sig>
PATCH v3 3/12 clk: Add a function to retrieve phase
Quoting Maxime Ripard (2014-09-29 01:24:21)
Quoted 45 lines
Hi Mike,
Quoted 42 lines · Mike Turquette
Quoting Maxime Ripard (2014-09-11 13:18:17)
Quoted 24 lines
The current phase API doesn't look into the actual hardware to get the phase value, but will rather get it from a variable only set by the
set_phasefunction.This will cause issue when the client driver will never call the
set_phasefunction, where we can end up having a reported phase that will not match what the hardware has been programmed to by the bootloader or what phase is programmed out of reset.Add a new
get_phasefunction for the drivers to implement so that we can get this value.Signed-off-by Maxime Ripard <[email protected]>--- drivers/clk/clk.c | 10 ++++++++++ include/linux/clk-provider.h | 5 +++++ 2 files changed, 15 insertions(+)drivers/clk/clk.c +0
-0@@ -1934,6 +1934,16 @@ int __clk_init(struct device *dev, struct clk *clk) clk->accuracy =0 ;drivers/clk/clk.c int __clk_init(struct device *dev, struct clk *clk) · line 1944
Quoted 10 lines
drivers/clk/clk.c +8
-0/* +* Set clk's phase. +* Since a phase is by definition relative to its parent, just +* query the current clock phase, or just assume it's in phase. +*/ +if (clk->ops->get_phase) + clk->phase = clk->ops->get_phase (clk->hw); +else + clk->phase =0 ;I probably wasn't paying enough attention to this. Based on the coverletter I thought that v3 would introduce something like
CLK_GET_PHASE_NOCACHEand stuff that logic into a__clk_get_phasefunction? Looks like this version of the series doesn't handle phases that can be changed by hardware (e.g. coming out of idle).
Quoted 8 lines
Quoted 3 lines
Correct me if I am wrong, but it looks like we retrieve the phase only once at init and then never again? The rest of the time we rely on
clk->phaseto always be accurate...Yeah, I didn't have any use for it at the moment, and thought that if someone ever needed it, it would be trivial to add, so I left it out.
If you want me to still do it, I can send an additional patch.
No it is fine. A bit asymmetrical for my brain but it is dead code if no one is using it (yet).
Regards,
Mike
Quoted 8 lines
Thanks!
MaximeSignature · 3 lines
Maxime Ripard, Free Electrons Embedded Linux, Kernel and Android engineering http://free-electrons.com
PATCH v3 4/12 clk: sunxi: factors: Invert the probing logic
In reply to Maxime RipardUntil now, the factors clock probing was done directly by sunxi_init_clocks, with the factors registration being called directly with the clocks data passed as an argument.
This approch has shown its limits when we added more clocks, since we couldn't really split code with such a logic in smaller files, and led to a huge file having all the clocks.
Introduce an intermediate probing function, so that factor clocks will be able to directly be called by CLK_OF_DECLARE, which will in turn ease the split into several files.
--- drivers/clk/sunxi/clk-factors.c | 101 +++++++++++++++++++++++++++++++++++++--- drivers/clk/sunxi/clk-factors.h | 16 ++++++- drivers/clk/sunxi/clk-sunxi.c | 95 ++----------------------------------- 3 files changed, 113 insertions(+), 99 deletions(-)
drivers/clk/sunxi/clk-factors.c +95 -6
@@ -9,18 +9,18 @@*/ #include <linux/clk-provider.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/io.h> #include <linux/module.h> +#include <linux/of_address.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 +* DOC: basic adjustable factor-based clock * * Traits of this clock: * prepare - clk_prepare only ensures that parents are prepared
@@ -32,6 +32,8 @@#define to_clk_factors (_hw) container_of(_hw, struct clk_factors, hw) +#define FACTORS_MAX_PARENTS 5 +#define SETMASK (len, pos) (((1U << (len)) - 1) << (pos))#define CLRMASK (len, pos) (~(SETMASK(len, pos)))#define FACTOR_GET (bit, len, reg) (((reg) & SETMASK(len, bit)) >> (bit))
@@ -147,9 +149,96 @@ static int clk_factors_set_rate(struct clk_hw *hw, unsigned long rate,return 0 ; } -const struct clk_ops clk_factors_ops = { +static const struct clk_ops clk_factors_ops = { .determine_rate = clk_factors_determine_rate, .recalc_rate = clk_factors_recalc_rate, .round_rate = clk_factors_round_rate, .set_rate = clk_factors_set_rate, }; + +struct clk *__init sunxi_factors_register (struct device_node *node, +const struct factors_data *data, +spinlock_t *lock) +{ +struct clk *clk; +struct clk_factors *factors; +struct clk_gate *gate =NULL ; +struct clk_mux *mux =NULL ; +struct clk_hw *gate_hw =NULL ; +struct clk_hw *mux_hw =NULL ; +const char *clk_name = node->name; +const char *parents[FACTORS_MAX_PARENTS ]; +void __iomem *reg; +int i =0 ; + + reg =of_iomap (node,0 ); + +/* if we have a mux, we will have >1 parents */ +while (i <FACTORS_MAX_PARENTS && + (parents[i] =of_clk_get_parent_name (node, i)) !=NULL ) + i++; + +/* +* some factor clocks, such as pll5 and pll6, may have multiple +* outputs, and have their name designated in factors_data +*/ +if (data->name) + clk_name = data->name; +else +of_property_read_string (node,"clock-output-names" , &clk_name); + + factors =kzalloc (sizeof (struct clk_factors ),GFP_KERNEL ); +if (!factors) +return NULL ; + +/* set up factors properties */ + factors->reg = reg; + factors->config = data->table; + factors->get_factors = data->getter; + factors->lock = lock; + +/* Add a gate if this factor clock can be gated */ +if (data->enable) { + gate =kzalloc (sizeof (struct clk_gate ),GFP_KERNEL ); +if (!gate) { +kfree (factors); +return NULL ; + } + +/* set up gate properties */ + gate->reg = reg; + gate->bit_idx = data->enable; + gate->lock = factors->lock; + gate_hw = &gate->hw; + } + +/* Add a mux if this factor clock can be muxed */ +if (data->mux) { + mux =kzalloc (sizeof (struct clk_mux ),GFP_KERNEL ); +if (!mux) { +kfree (factors); +kfree (gate); +return NULL ; + } + +/* set up gate properties */ + mux->reg = reg; + mux->shift = data->mux; + mux->mask =SUNXI_FACTORS_MUX_MASK ; + mux->lock = factors->lock; + mux_hw = &mux->hw; + } + + clk =clk_register_composite (NULL , clk_name, + parents, i, + mux_hw, &clk_mux_ops, + &factors->hw, &clk_factors_ops, + gate_hw, &clk_gate_ops,0 ); + +if (!IS_ERR (clk)) { +of_clk_add_provider (node, of_clk_src_simple_get, clk); +clk_register_clkdev (clk, clk_name,NULL ); + } + +return clk; +}
drivers/clk/sunxi/clk-factors.h +15 -1
@@ -3,9 +3,12 @@#include <linux/clk-provider.h> #include <linux/clkdev.h> +#include <linux/spinlock.h> #define SUNXI_FACTORS_NOT_APPLICABLE (0) +#define SUNXI_FACTORS_MUX_MASK 0x3 +struct clk_factors_config {u8 nshift;u8 nwidth;
@@ -18,6 +21,14 @@ struct clk_factors_config {u8 n_start; }; +struct factors_data { +int enable; +int mux; +struct clk_factors_config *table; +void (*getter) (u32 *rate,u32 parent_rate,u8 *n,u8 *k,u8 *m,u8 *p); +const char *name; +}; +struct clk_factors {struct clk_hw hw;void __iomem *reg;
@@ -26,5 +37,8 @@ struct clk_factors { spinlock_t *lock; }; -extern const struct clk_ops clk_factors_ops; +struct clk *__init sunxi_factors_register (struct device_node *node, +const struct factors_data *data, +spinlock_t *lock); +#endif
drivers/clk/sunxi/clk-sunxi.c +3 -92
114 lines not on this page · raw message
Signature · 1 line
2.1.0
PATCH v3 5/12 clk: sunxi: Introduce mbus compatible
In reply to Maxime RipardEven though the mbus clock is a regular module clock, given its nature, it needs to be enabled all the time.
Introduce a new compatible, to differentiate it from the other module clocks.
--- Documentation/devicetree/bindings/clock/sunxi.txt | 1 + drivers/clk/sunxi/clk-sunxi.c | 1 + 2 files changed, 2 insertions(+)
Documentation/devicetree/bindings/clock/sunxi.txt +1 -0
@@ -46,6 +46,7 @@ Required properties: "allwinner,sun6i-a31-apb2-div-clk" - for the APB2 gates on A31 "allwinner,sun6i-a31-apb2-gates-clk" - for the APB2 gates on A31 "allwinner,sun8i-a23-apb2-gates-clk" - for the APB2 gates on A23 + "allwinner,sun5i-a13-mbus-clk" - for the MBUS clock on A13 "allwinner,sun4i-a10-mod0-clk" - for the module 0 family of clocks "allwinner,sun7i-a20-out-clk" - for the external output clocks "allwinner,sun7i-a20-gmac-clk" - for the GMAC clock module on A20/A31
drivers/clk/sunxi/clk-sunxi.c +1 -0
@@ -1110,6 +1110,7 @@ static const struct of_device_id clk_factors_match[] __initconst = { {.compatible = "allwinner,sun7i-a20-pll4-clk", .data = &sun7i_a20_pll4_data,}, {.compatible = "allwinner,sun6i-a31-pll6-clk", .data = &sun6i_a31_pll6_data,}, {.compatible = "allwinner,sun4i-a10-apb1-clk", .data = &sun4i_apb1_data,}, + {.compatible = "allwinner,sun5i-a13-mbus-clk", .data = &sun4i_mod0_data,}, {.compatible = "allwinner,sun4i-a10-mod0-clk", .data = &sun4i_mod0_data,}, {.compatible = "allwinner,sun7i-a20-out-clk", .data = &sun7i_a20_out_data,}, {}
Signature · 1 line
2.1.0
PATCH v3 6/12 ARM: sunxi: dt: Switch to the new mbus compatible
In reply to Maxime RipardNow that we have a compatible of its own for the mbus clock, switch to it.
--- arch/arm/boot/dts/sun5i-a10s.dtsi | 2 +- arch/arm/boot/dts/sun5i-a13.dtsi | 2 +- arch/arm/boot/dts/sun7i-a20.dtsi | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-)
arch/arm/boot/dts/sun5i-a10s.dtsi +1 -1
@@ -287,7 +287,7 @@ mbus_clk: clk at 01c2015c { #clock-cells = <0>; - compatible = "allwinner,sun4i-a10-mod0-clk"; + compatible = "allwinner,sun5i-a13-mbus-clk"; reg = <0x01c2015c 0x4>; clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; clock-output-names = "mbus";
arch/arm/boot/dts/sun5i-a13.dtsi +1 -1
@@ -285,7 +285,7 @@ mbus_clk: clk at 01c2015c { #clock-cells = <0>; - compatible = "allwinner,sun4i-a10-mod0-clk"; + compatible = "allwinner,sun5i-a13-mbus-clk"; reg = <0x01c2015c 0x4>; clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; clock-output-names = "mbus";
arch/arm/boot/dts/sun7i-a20.dtsi +1 -1
@@ -382,7 +382,7 @@ mbus_clk: clk at 01c2015c { #clock-cells = <0>; - compatible = "allwinner,sun4i-a10-mod0-clk"; + compatible = "allwinner,sun5i-a13-mbus-clk"; reg = <0x01c2015c 0x4>; clocks = <&osc24M>, <&pll6 2>, <&pll5 1>; clock-output-names = "mbus";
Signature · 1 line
2.1.0
PATCH v3 7/12 clk: sunxi: Move mod0 clock to a file of its own
In reply to Maxime RipardSince we know have the ability to declare factors clock outside of clk-sunxi, create a new mod0 driver to deal with the mod0 clocks.
--- drivers/clk/sunxi/Makefile | 1 + drivers/clk/sunxi/clk-mod0.c | 82 +++++++++++++++++++++++++++++++++++++++++++ drivers/clk/sunxi/clk-sunxi.c | 1 - 3 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 drivers/clk/sunxi/clk-mod0.c
drivers/clk/sunxi/Makefile +1 -0
@@ -5,6 +5,7 @@ obj-y += clk-sunxi.o clk-factors.o obj-y += clk-a10-hosc.o obj-y += clk-a20-gmac.o +obj-y += clk-mod0.o obj-$(CONFIG_MFD_SUN6I_PRCM) += \ clk-sun6i-ar100.o clk-sun6i-apb0.o clk-sun6i-apb0-gates.o \
drivers/clk/sunxi/clk-mod0.c new file +82 -0
82 lines not on this page · raw message
drivers/clk/sunxi/clk-sunxi.c +0 -1
@@ -1111,7 +1111,6 @@ static const struct of_device_id clk_factors_match[] __initconst = { {.compatible = "allwinner,sun6i-a31-pll6-clk", .data = &sun6i_a31_pll6_data,}, {.compatible = "allwinner,sun4i-a10-apb1-clk", .data = &sun4i_apb1_data,}, {.compatible = "allwinner,sun5i-a13-mbus-clk", .data = &sun4i_mod0_data,}, - {.compatible = "allwinner,sun4i-a10-mod0-clk", .data = &sun4i_mod0_data,}, {.compatible = "allwinner,sun7i-a20-out-clk", .data = &sun7i_a20_out_data,}, {} };
Signature · 1 line
2.1.0
PATCH v3 8/12 clk: sunxi: Move mbus to mod0 file
In reply to Maxime RipardMove the MBUS clock to the module clocks file. It's pretty trivial, but still requires to enable the clocks to make sure it won't get disabled.
--- drivers/clk/sunxi/clk-mod0.c | 12 +++++++++ drivers/clk/sunxi/clk-sunxi.c | 57 ------------------------------------------- 2 files changed, 12 insertions(+), 57 deletions(-)
drivers/clk/sunxi/clk-mod0.c +12 -0
@@ -80,3 +80,15 @@ static void __init sun4i_a10_mod0_setup(struct device_node *node)sunxi_factors_register (node, &sun4i_a10_mod0_data, &sun4i_a10_mod0_lock); }CLK_OF_DECLARE (sun4i_a10_mod0,"allwinner,sun4i-a10-mod0-clk" , sun4i_a10_mod0_setup); + +static DEFINE_SPINLOCK (sun5i_a13_mbus_lock); + +static void __init sun5i_a13_mbus_setup (struct device_node *node) +{ +struct clk *mbus =sunxi_factors_register (node, &sun4i_a10_mod0_data, &sun5i_a13_mbus_lock); + +/* The MBUS clocks needs to be always enabled */ +__clk_get (mbus); +clk_prepare_enable (mbus); +} +CLK_OF_DECLARE (sun5i_a13_mbus,"allwinner,sun5i-a13-mbus-clk" , sun5i_a13_mbus_setup);
drivers/clk/sunxi/clk-sunxi.c +0 -57
87 lines not on this page · raw message
Signature · 1 line
2.1.0
PATCH v3 9/12 ARM: sunxi: dt: Add sample and output mmc clocks
In reply to Maxime RipardAdd the sample and output clocks for the MMC phase support.
--- arch/arm/boot/dts/sun4i-a10.dtsi | 104 +++++++++++++++++++++++++++++++++++--- arch/arm/boot/dts/sun5i-a10s.dtsi | 79 ++++++++++++++++++++++++++--- arch/arm/boot/dts/sun5i-a13.dtsi | 80 +++++++++++++++++++++++++---- arch/arm/boot/dts/sun6i-a31.dtsi | 104 +++++++++++++++++++++++++++++++++++--- arch/arm/boot/dts/sun7i-a20.dtsi | 104 +++++++++++++++++++++++++++++++++++--- arch/arm/boot/dts/sun8i-a23.dtsi | 78 +++++++++++++++++++++++++--- 6 files changed, 502 insertions(+), 47 deletions(-)
arch/arm/boot/dts/sun4i-a10.dtsi +96 -8
@@ -227,6 +227,22 @@ clock-output-names = "mmc0"; }; + mmc0_output_clk: mmc_output_clk at 01c20088 { + #clock-cells = <0>; + compatible = "allwinner,sun4i-a10-mmc-output-clk"; + reg = <0x01c20088 0x4>; + clocks = <&mmc0_clk>; + clock-output-names = "mmc0_output"; + }; + + mmc0_sample_clk: mmc_sample_clk at 01c20088 { + #clock-cells = <0>; + compatible = "allwinner,sun4i-a10-mmc-sample-clk"; + reg = <0x01c20088 0x4>; + clocks = <&mmc0_clk>; + clock-output-names = "mmc0_sample"; + }; + mmc1_clk: clk at 01c2008c { #clock-cells = <0>; compatible = "allwinner,sun4i-a10-mod0-clk";
@@ -235,6 +251,22 @@ clock-output-names = "mmc1"; }; + mmc1_output_clk: mmc_output_clk at 01c2008c { + #clock-cells = <0>; + compatible = "allwinner,sun4i-a10-mmc-output-clk"; + reg = <0x01c2008c 0x4>; + clocks = <&mmc1_clk>; + clock-output-names = "mmc1_output"; + }; + + mmc1_sample_clk: mmc_sample_clk at 01c2008c { + #clock-cells = <0>; + compatible = "allwinner,sun4i-a10-mmc-sample-clk"; + reg = <0x01c2008c 0x4>; + clocks = <&mmc1_clk>; + clock-output-names = "mmc1_sample"; + }; + mmc2_clk: clk at 01c20090 { #clock-cells = <0>; compatible = "allwinner,sun4i-a10-mod0-clk";
@@ -243,6 +275,22 @@ clock-output-names = "mmc2"; }; + mmc2_output_clk: mmc_output_clk at 01c20090 { + #clock-cells = <0>; + compatible = "allwinner,sun4i-a10-mmc-output-clk"; + reg = <0x01c20090 0x4>; + clocks = <&mmc2_clk>; + clock-output-names = "mmc2_output"; + }; + + mmc2_sample_clk: mmc_sample_clk at 01c20090 { + #clock-cells = <0>; + compatible = "allwinner,sun4i-a10-mmc-sample-clk"; + reg = <0x01c20090 0x4>; + clocks = <&mmc2_clk>; + clock-output-names = "mmc2_sample"; + }; + mmc3_clk: clk at 01c20094 { #clock-cells = <0>; compatible = "allwinner,sun4i-a10-mod0-clk";
@@ -251,6 +299,22 @@ clock-output-names = "mmc3"; }; + mmc3_output_clk: mmc_output_clk at 01c20094 { + #clock-cells = <0>; + compatible = "allwinner,sun4i-a10-mmc-output-clk"; + reg = <0x01c20094 0x4>; + clocks = <&mmc3_clk>; + clock-output-names = "mmc3_output"; + }; + + mmc3_sample_clk: mmc_sample_clk at 01c20094 { + #clock-cells = <0>; + compatible = "allwinner,sun4i-a10-mmc-sample-clk"; + reg = <0x01c20094 0x4>; + clocks = <&mmc3_clk>; + clock-output-names = "mmc3_sample"; + }; + ts_clk: clk at 01c20098 { #clock-cells = <0>; compatible = "allwinner,sun4i-a10-mod0-clk";
@@ -392,8 +456,14 @@ mmc0: mmc at 01c0f000 { compatible = "allwinner,sun4i-a10-mmc"; reg = <0x01c0f000 0x1000>; - clocks = <&ahb_gates 8>, <&mmc0_clk>; - clock-names = "ahb", "mmc"; + clocks = <&ahb_gates 8>, + <&mmc0_clk>, + <&mmc0_sample_clk>, + <&mmc0_output_clk>; + clock-names = "ahb", + "mmc", + "sample", + "output"; interrupts = <32>; status = "disabled"; };
@@ -401,8 +471,14 @@ mmc1: mmc at 01c10000 { compatible = "allwinner,sun4i-a10-mmc"; reg = <0x01c10000 0x1000>; - clocks = <&ahb_gates 9>, <&mmc1_clk>; - clock-names = "ahb", "mmc"; + clocks = <&ahb_gates 9>, + <&mmc1_clk>, + <&mmc1_sample_clk>, + <&mmc1_output_clk>; + clock-names = "ahb", + "mmc", + "sample", + "output"; interrupts = <33>; status = "disabled"; };
@@ -410,8 +486,14 @@ mmc2: mmc at 01c11000 { compatible = "allwinner,sun4i-a10-mmc"; reg = <0x01c11000 0x1000>; - clocks = <&ahb_gates 10>, <&mmc2_clk>; - clock-names = "ahb", "mmc"; + clocks = <&ahb_gates 10>, + <&mmc2_clk>, + <&mmc2_sample_clk>, + <&mmc2_output_clk>; + clock-names = "ahb", + "mmc", + "sample", + "output"; interrupts = <34>; status = "disabled"; };
@@ -419,8 +501,14 @@ mmc3: mmc at 01c12000 { compatible = "allwinner,sun4i-a10-mmc"; reg = <0x01c12000 0x1000>; - clocks = <&ahb_gates 11>, <&mmc3_clk>; - clock-names = "ahb", "mmc"; + clocks = <&ahb_gates 11>, + <&mmc3_clk>, + <&mmc3_sample_clk>, + <&mmc3_output_clk>; + clock-names = "ahb", + "mmc", + "sample", + "output"; interrupts = <35>; status = "disabled"; };
arch/arm/boot/dts/sun5i-a10s.dtsi +72 -7
118 lines not on this page · raw message
arch/arm/boot/dts/sun5i-a13.dtsi +70 -10
116 lines not on this page · raw message
arch/arm/boot/dts/sun6i-a31.dtsi +96 -8
152 lines not on this page · raw message
arch/arm/boot/dts/sun7i-a20.dtsi +96 -8
152 lines not on this page · raw message
arch/arm/boot/dts/sun8i-a23.dtsi +72 -6
114 lines not on this page · raw message
Signature · 1 line
2.1.0
PATCH v3 9/12 ARM: sunxi: dt: Add sample and output mmc clocks
Quoted 10 lines · Maxime Ripard
Add the sample and output clocks for the MMC phase support.
Signed-off-by Maxime Ripard <[email protected]>--- arch/arm/boot/dts/sun4i-a10.dtsi | 104 +++++++++++++++++++++++++++++++++++--- arch/arm/boot/dts/sun5i-a10s.dtsi | 79 ++++++++++++++++++++++++++--- arch/arm/boot/dts/sun5i-a13.dtsi | 80 +++++++++++++++++++++++++---- arch/arm/boot/dts/sun6i-a31.dtsi | 104 +++++++++++++++++++++++++++++++++++--- arch/arm/boot/dts/sun7i-a20.dtsi | 104 +++++++++++++++++++++++++++++++++++--- arch/arm/boot/dts/sun8i-a23.dtsi | 78 +++++++++++++++++++++++++---
Tested on my a23 tablet, looks good.
Quoted 102 lines
6 files changed, 502 insertions(+), 47 deletions(-)arch/arm/boot/dts/sun4i-a10.dtsi +64
-0@@ -227,6 +227,22 @@ clock-output-names = "mmc0"; }; + mmc0_output_clk: mmc_output_clk at 01c20088 { + #clock-cells = <0>; + compatible = "allwinner,sun4i-a10-mmc-output-clk"; + reg = <0x01c20088 0x4>; + clocks = <&mmc0_clk>; + clock-output-names = "mmc0_output"; + }; + + mmc0_sample_clk: mmc_sample_clk at 01c20088 { + #clock-cells = <0>; + compatible = "allwinner,sun4i-a10-mmc-sample-clk"; + reg = <0x01c20088 0x4>; + clocks = <&mmc0_clk>; + clock-output-names = "mmc0_sample"; + }; + mmc1_clk: clk at 01c2008c { #clock-cells = <0>; compatible = "allwinner,sun4i-a10-mod0-clk";@@ -235,6 +251,22 @@ clock-output-names = "mmc1"; }; + mmc1_output_clk: mmc_output_clk at 01c2008c { + #clock-cells = <0>; + compatible = "allwinner,sun4i-a10-mmc-output-clk"; + reg = <0x01c2008c 0x4>; + clocks = <&mmc1_clk>; + clock-output-names = "mmc1_output"; + }; + + mmc1_sample_clk: mmc_sample_clk at 01c2008c { + #clock-cells = <0>; + compatible = "allwinner,sun4i-a10-mmc-sample-clk"; + reg = <0x01c2008c 0x4>; + clocks = <&mmc1_clk>; + clock-output-names = "mmc1_sample"; + }; + mmc2_clk: clk at 01c20090 { #clock-cells = <0>; compatible = "allwinner,sun4i-a10-mod0-clk";@@ -243,6 +275,22 @@ clock-output-names = "mmc2"; }; + mmc2_output_clk: mmc_output_clk at 01c20090 { + #clock-cells = <0>; + compatible = "allwinner,sun4i-a10-mmc-output-clk"; + reg = <0x01c20090 0x4>; + clocks = <&mmc2_clk>; + clock-output-names = "mmc2_output"; + }; + + mmc2_sample_clk: mmc_sample_clk at 01c20090 { + #clock-cells = <0>; + compatible = "allwinner,sun4i-a10-mmc-sample-clk"; + reg = <0x01c20090 0x4>; + clocks = <&mmc2_clk>; + clock-output-names = "mmc2_sample"; + }; + mmc3_clk: clk at 01c20094 { #clock-cells = <0>; compatible = "allwinner,sun4i-a10-mod0-clk";@@ -251,6 +299,22 @@ clock-output-names = "mmc3"; }; + mmc3_output_clk: mmc_output_clk at 01c20094 { + #clock-cells = <0>; + compatible = "allwinner,sun4i-a10-mmc-output-clk"; + reg = <0x01c20094 0x4>; + clocks = <&mmc3_clk>; + clock-output-names = "mmc3_output"; + }; + + mmc3_sample_clk: mmc_sample_clk at 01c20094 { + #clock-cells = <0>; + compatible = "allwinner,sun4i-a10-mmc-sample-clk"; + reg = <0x01c20094 0x4>; + clocks = <&mmc3_clk>; + clock-output-names = "mmc3_sample"; + }; + ts_clk: clk at 01c20098 { #clock-cells = <0>; compatible = "allwinner,sun4i-a10-mod0-clk";@@ -392,8 +456,14 @@ mmc0: mmc at 01c0f000 { compatible = "allwinner,sun4i-a10-mmc"; reg = <0x01c0f000 0x1000>;
arch/arm/boot/dts/sun4i-a10.dtsi line 466
Quoted 10 lines
arch/arm/boot/dts/sun4i-a10.dtsi +8
-2- clocks = <&ahb_gates 8>, <&mmc0_clk>; - clock-names = "ahb", "mmc"; + clocks = <&ahb_gates 8>, + <&mmc0_clk>, + <&mmc0_sample_clk>, + <&mmc0_output_clk>; + clock-names = "ahb", + "mmc", + "sample", + "output";
Just a stylish nitpick. All the other resources in these files are listed inline if possible, not one phandle/value per line.
But otherwise, it looks good.
ChenYu
Quoted 760 lines
arch/arm/boot/dts/sun4i-a10.dtsi +24
-6interrupts = <32>; status = "disabled"; };@@ -401,8 +471,14 @@ mmc1: mmc at 01c10000 { compatible = "allwinner,sun4i-a10-mmc"; reg = <0x01c10000 0x1000>; - clocks = <&ahb_gates 9>, <&mmc1_clk>; - clock-names = "ahb", "mmc"; + clocks = <&ahb_gates 9>, + <&mmc1_clk>, + <&mmc1_sample_clk>, + <&mmc1_output_clk>; + clock-names = "ahb", + "mmc", + "sample", + "output"; interrupts = <33>; status = "disabled"; };@@ -410,8 +486,14 @@ mmc2: mmc at 01c11000 { compatible = "allwinner,sun4i-a10-mmc"; reg = <0x01c11000 0x1000>; - clocks = <&ahb_gates 10>, <&mmc2_clk>; - clock-names = "ahb", "mmc"; + clocks = <&ahb_gates 10>, + <&mmc2_clk>, + <&mmc2_sample_clk>, + <&mmc2_output_clk>; + clock-names = "ahb", + "mmc", + "sample", + "output"; interrupts = <34>; status = "disabled"; };@@ -419,8 +501,14 @@ mmc3: mmc at 01c12000 { compatible = "allwinner,sun4i-a10-mmc"; reg = <0x01c12000 0x1000>; - clocks = <&ahb_gates 11>, <&mmc3_clk>; - clock-names = "ahb", "mmc"; + clocks = <&ahb_gates 11>, + <&mmc3_clk>, + <&mmc3_sample_clk>, + <&mmc3_output_clk>; + clock-names = "ahb", + "mmc", + "sample", + "output"; interrupts = <35>; status = "disabled"; };arch/arm/boot/dts/sun5i-a10s.dtsi +72
-7@@ -212,6 +212,22 @@ clock-output-names = "mmc0"; }; + mmc0_output_clk: mmc_output_clk at 01c20088 { + #clock-cells = <0>; + compatible = "allwinner,sun4i-a10-mmc-output-clk"; + reg = <0x01c20088 0x4>; + clocks = <&mmc0_clk>; + clock-output-names = "mmc0_output"; + }; + + mmc0_sample_clk: mmc_sample_clk at 01c20088 { + #clock-cells = <0>; + compatible = "allwinner,sun4i-a10-mmc-sample-clk"; + reg = <0x01c20088 0x4>; + clocks = <&mmc0_clk>; + clock-output-names = "mmc0_sample"; + }; + mmc1_clk: clk at 01c2008c { #clock-cells = <0>; compatible = "allwinner,sun4i-a10-mod0-clk";@@ -220,6 +236,22 @@ clock-output-names = "mmc1"; }; + mmc1_output_clk: mmc_output_clk at 01c2008c { + #clock-cells = <0>; + compatible = "allwinner,sun4i-a10-mmc-output-clk"; + reg = <0x01c2008c 0x4>; + clocks = <&mmc1_clk>; + clock-output-names = "mmc1_output"; + }; + + mmc1_sample_clk: mmc_sample_clk at 01c2008c { + #clock-cells = <0>; + compatible = "allwinner,sun4i-a10-mmc-sample-clk"; + reg = <0x01c2008c 0x4>; + clocks = <&mmc1_clk>; + clock-output-names = "mmc1_sample"; + }; + mmc2_clk: clk at 01c20090 { #clock-cells = <0>; compatible = "allwinner,sun4i-a10-mod0-clk";@@ -228,6 +260,22 @@ clock-output-names = "mmc2"; }; + mmc2_output_clk: mmc_output_clk at 01c20090 { + #clock-cells = <0>; + compatible = "allwinner,sun4i-a10-mmc-output-clk"; + reg = <0x01c20090 0x4>; + clocks = <&mmc2_clk>; + clock-output-names = "mmc2_output"; + }; + + mmc2_sample_clk: mmc_sample_clk at 01c20090 { + #clock-cells = <0>; + compatible = "allwinner,sun4i-a10-mmc-sample-clk"; + reg = <0x01c20090 0x4>; + clocks = <&mmc2_clk>; + clock-output-names = "mmc2_sample"; + }; + ts_clk: clk at 01c20098 { #clock-cells = <0>; compatible = "allwinner,sun4i-a10-mod0-clk";@@ -353,8 +401,14 @@ mmc0: mmc at 01c0f000 { compatible = "allwinner,sun5i-a13-mmc"; reg = <0x01c0f000 0x1000>; - clocks = <&ahb_gates 8>, <&mmc0_clk>; - clock-names = "ahb", "mmc"; + clocks = <&ahb_gates 8>, + <&mmc0_clk>, + <&mmc0_sample_clk>, + <&mmc0_output_clk>; + clock-names = "ahb", + "mmc", + "sample", + "output"; interrupts = <32>; status = "disabled"; };@@ -362,8 +416,14 @@ mmc1: mmc at 01c10000 { compatible = "allwinner,sun5i-a13-mmc"; reg = <0x01c10000 0x1000>; - clocks = <&ahb_gates 9>, <&mmc1_clk>; - clock-names = "ahb", "mmc"; + clocks = <&ahb_gates 9>, + <&mmc1_clk>, + <&mmc1_sample_clk>, + <&mmc1_output_clk>; + clock-names = "ahb", + "mmc", + "sample", + "output"; interrupts = <33>; status = "disabled"; };@@ -371,12 +431,17 @@ mmc2: mmc at 01c11000 { compatible = "allwinner,sun5i-a13-mmc"; reg = <0x01c11000 0x1000>; - clocks = <&ahb_gates 10>, <&mmc2_clk>; - clock-names = "ahb", "mmc"; + clocks = <&ahb_gates 10>, + <&mmc2_clk>, + <&mmc2_sample_clk>, + <&mmc2_output_clk>; + clock-names = "ahb", + "mmc", + "sample", + "output"; interrupts = <34>; status = "disabled"; }; - usbphy: phy at 01c13400 { #phy-cells = <1>; compatible = "allwinner,sun5i-a13-usb-phy";arch/arm/boot/dts/sun5i-a13.dtsi +70
-10@@ -202,30 +202,78 @@ clock-output-names = "ms"; }; - mmc0_clk: clk at 01c20088 { + mmc0_clk: clk_mmc at 01c20088 { #clock-cells = <0>; - compatible = "allwinner,sun4i-a10-mod0-clk"; + compatible = "allwinner,sun4i-a10-mmc-clk"; reg = <0x01c20088 0x4>; clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; clock-output-names = "mmc0"; }; - mmc1_clk: clk at 01c2008c { + mmc0_output_clk: mmc_output_clk at 01c20088 { #clock-cells = <0>; - compatible = "allwinner,sun4i-a10-mod0-clk"; + compatible = "allwinner,sun4i-a10-mmc-output-clk"; + reg = <0x01c20088 0x4>; + clocks = <&mmc0_clk>; + clock-output-names = "mmc0_output"; + }; + + mmc0_sample_clk: mmc_sample_clk at 01c20088 { + #clock-cells = <0>; + compatible = "allwinner,sun4i-a10-mmc-sample-clk"; + reg = <0x01c20088 0x4>; + clocks = <&mmc0_clk>; + clock-output-names = "mmc0_sample"; + }; + + mmc1_clk: clk_mmc at 01c2008c { + #clock-cells = <0>; + compatible = "allwinner,sun4i-a10-mmc-clk"; reg = <0x01c2008c 0x4>; clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; clock-output-names = "mmc1"; }; - mmc2_clk: clk at 01c20090 { + mmc1_output_clk: mmc_output_clk at 01c2008c { #clock-cells = <0>; - compatible = "allwinner,sun4i-a10-mod0-clk"; + compatible = "allwinner,sun4i-a10-mmc-output-clk"; + reg = <0x01c2008c 0x4>; + clocks = <&mmc1_clk>; + clock-output-names = "mmc1_output"; + }; + + mmc1_sample_clk: mmc_sample_clk at 01c2008c { + #clock-cells = <0>; + compatible = "allwinner,sun4i-a10-mmc-sample-clk"; + reg = <0x01c2008c 0x4>; + clocks = <&mmc1_clk>; + clock-output-names = "mmc1_sample"; + }; + + mmc2_clk: clk_mmc at 01c20090 { + #clock-cells = <0>; + compatible = "allwinner,sun4i-a10-mmc-clk"; reg = <0x01c20090 0x4>; clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; clock-output-names = "mmc2"; }; + mmc2_output_clk: mmc_output_clk at 01c20090 { + #clock-cells = <0>; + compatible = "allwinner,sun4i-a10-mmc-output-clk"; + reg = <0x01c20090 0x4>; + clocks = <&mmc2_clk>; + clock-output-names = "mmc2_output"; + }; + + mmc2_sample_clk: mmc_sample_clk at 01c20090 { + #clock-cells = <0>; + compatible = "allwinner,sun4i-a10-mmc-sample-clk"; + reg = <0x01c20090 0x4>; + clocks = <&mmc2_clk>; + clock-output-names = "mmc2_sample"; + }; + ts_clk: clk at 01c20098 { #clock-cells = <0>; compatible = "allwinner,sun4i-a10-mod0-clk";@@ -335,8 +383,14 @@ mmc0: mmc at 01c0f000 { compatible = "allwinner,sun5i-a13-mmc"; reg = <0x01c0f000 0x1000>; - clocks = <&ahb_gates 8>, <&mmc0_clk>; - clock-names = "ahb", "mmc"; + clocks = <&ahb_gates 8>, + <&mmc0_clk>, + <&mmc0_sample_clk>, + <&mmc0_output_clk>; + clock-names = "ahb", + "mmc", + "sample", + "output"; interrupts = <32>; status = "disabled"; };@@ -344,8 +398,14 @@ mmc2: mmc at 01c11000 { compatible = "allwinner,sun5i-a13-mmc"; reg = <0x01c11000 0x1000>; - clocks = <&ahb_gates 10>, <&mmc2_clk>; - clock-names = "ahb", "mmc"; + clocks = <&ahb_gates 10>, + <&mmc2_clk>, + <&mmc2_sample_clk>, + <&mmc2_output_clk>; + clock-names = "ahb", + "mmc", + "sample", + "output"; interrupts = <34>; status = "disabled"; };arch/arm/boot/dts/sun6i-a31.dtsi +96
-8@@ -252,6 +252,22 @@ clock-output-names = "mmc0"; }; + mmc0_output_clk: mmc_output_clk at 01c20088 { + #clock-cells = <0>; + compatible = "allwinner,sun4i-a10-mmc-output-clk"; + reg = <0x01c20088 0x4>; + clocks = <&mmc0_clk>; + clock-output-names = "mmc0_output"; + }; + + mmc0_sample_clk: mmc_sample_clk at 01c20088 { + #clock-cells = <0>; + compatible = "allwinner,sun4i-a10-mmc-sample-clk"; + reg = <0x01c20088 0x4>; + clocks = <&mmc0_clk>; + clock-output-names = "mmc0_sample"; + }; + mmc1_clk: clk at 01c2008c { #clock-cells = <0>; compatible = "allwinner,sun4i-a10-mod0-clk";@@ -260,6 +276,22 @@ clock-output-names = "mmc1"; }; + mmc1_output_clk: mmc_output_clk at 01c2008c { + #clock-cells = <0>; + compatible = "allwinner,sun4i-a10-mmc-output-clk"; + reg = <0x01c2008c 0x4>; + clocks = <&mmc1_clk>; + clock-output-names = "mmc1_output"; + }; + + mmc1_sample_clk: mmc_sample_clk at 01c2008c { + #clock-cells = <0>; + compatible = "allwinner,sun4i-a10-mmc-sample-clk"; + reg = <0x01c2008c 0x4>; + clocks = <&mmc1_clk>; + clock-output-names = "mmc1_sample"; + }; + mmc2_clk: clk at 01c20090 { #clock-cells = <0>; compatible = "allwinner,sun4i-a10-mod0-clk";@@ -268,6 +300,22 @@ clock-output-names = "mmc2"; }; + mmc2_output_clk: mmc_output_clk at 01c20090 { + #clock-cells = <0>; + compatible = "allwinner,sun4i-a10-mmc-output-clk"; + reg = <0x01c20090 0x4>; + clocks = <&mmc2_clk>; + clock-output-names = "mmc2_output"; + }; + + mmc2_sample_clk: mmc_sample_clk at 01c20090 { + #clock-cells = <0>; + compatible = "allwinner,sun4i-a10-mmc-sample-clk"; + reg = <0x01c20090 0x4>; + clocks = <&mmc2_clk>; + clock-output-names = "mmc2_sample"; + }; + mmc3_clk: clk at 01c20094 { #clock-cells = <0>; compatible = "allwinner,sun4i-a10-mod0-clk";@@ -276,6 +324,22 @@ clock-output-names = "mmc3"; }; + mmc3_output_clk: mmc_output_clk at 01c20094 { + #clock-cells = <0>; + compatible = "allwinner,sun4i-a10-mmc-output-clk"; + reg = <0x01c20094 0x4>; + clocks = <&mmc3_clk>; + clock-output-names = "mmc3_output"; + }; + + mmc3_sample_clk: mmc_sample_clk at 01c20094 { + #clock-cells = <0>; + compatible = "allwinner,sun4i-a10-mmc-sample-clk"; + reg = <0x01c20094 0x4>; + clocks = <&mmc3_clk>; + clock-output-names = "mmc3_sample"; + }; + spi0_clk: clk at 01c200a0 { #clock-cells = <0>; compatible = "allwinner,sun4i-a10-mod0-clk";@@ -366,8 +430,14 @@ mmc0: mmc at 01c0f000 { compatible = "allwinner,sun5i-a13-mmc"; reg = <0x01c0f000 0x1000>; - clocks = <&ahb1_gates 8>, <&mmc0_clk>; - clock-names = "ahb", "mmc"; + clocks = <&ahb1_gates 8>, + <&mmc0_clk>, + <&mmc0_sample_clk>, + <&mmc0_output_clk>; + clock-names = "ahb", + "mmc", + "sample", + "output"; resets = <&ahb1_rst 8>; reset-names = "ahb"; interrupts = <0 60 4>;@@ -377,8 +447,14 @@ mmc1: mmc at 01c10000 { compatible = "allwinner,sun5i-a13-mmc"; reg = <0x01c10000 0x1000>; - clocks = <&ahb1_gates 9>, <&mmc1_clk>; - clock-names = "ahb", "mmc"; + clocks = <&ahb1_gates 9>, + <&mmc1_clk>, + <&mmc1_sample_clk>, + <&mmc1_output_clk>; + clock-names = "ahb", + "mmc", + "sample", + "output"; resets = <&ahb1_rst 9>; reset-names = "ahb"; interrupts = <0 61 4>;@@ -388,8 +464,14 @@ mmc2: mmc at 01c11000 { compatible = "allwinner,sun5i-a13-mmc"; reg = <0x01c11000 0x1000>; - clocks = <&ahb1_gates 10>, <&mmc2_clk>; - clock-names = "ahb", "mmc"; + clocks = <&ahb1_gates 10>, + <&mmc2_clk>, + <&mmc2_sample_clk>, + <&mmc2_output_clk>; + clock-names = "ahb", + "mmc", + "sample", + "output"; resets = <&ahb1_rst 10>; reset-names = "ahb"; interrupts = <0 62 4>;@@ -399,8 +481,14 @@ mmc3: mmc at 01c12000 { compatible = "allwinner,sun5i-a13-mmc"; reg = <0x01c12000 0x1000>; - clocks = <&ahb1_gates 11>, <&mmc3_clk>; - clock-names = "ahb", "mmc"; + clocks = <&ahb1_gates 11>, + <&mmc3_clk>, + <&mmc3_sample_clk>, + <&mmc3_output_clk>; + clock-names = "ahb", + "mmc", + "sample", + "output"; resets = <&ahb1_rst 11>; reset-names = "ahb"; interrupts = <0 63 4>;arch/arm/boot/dts/sun7i-a20.dtsi +96
-8@@ -275,6 +275,22 @@ clock-output-names = "mmc0"; }; + mmc0_output_clk: mmc_output_clk at 01c20088 { + #clock-cells = <0>; + compatible = "allwinner,sun4i-a10-mmc-output-clk"; + reg = <0x01c20088 0x4>; + clocks = <&mmc0_clk>; + clock-output-names = "mmc0_output"; + }; + + mmc0_sample_clk: mmc_sample_clk at 01c20088 { + #clock-cells = <0>; + compatible = "allwinner,sun4i-a10-mmc-sample-clk"; + reg = <0x01c20088 0x4>; + clocks = <&mmc0_clk>; + clock-output-names = "mmc0_sample"; + }; + mmc1_clk: clk at 01c2008c { #clock-cells = <0>; compatible = "allwinner,sun4i-a10-mod0-clk";@@ -283,6 +299,22 @@ clock-output-names = "mmc1"; }; + mmc1_output_clk: mmc_output_clk at 01c2008c { + #clock-cells = <0>; + compatible = "allwinner,sun4i-a10-mmc-output-clk"; + reg = <0x01c2008c 0x4>; + clocks = <&mmc1_clk>; + clock-output-names = "mmc1_output"; + }; + + mmc1_sample_clk: mmc_sample_clk at 01c2008c { + #clock-cells = <0>; + compatible = "allwinner,sun4i-a10-mmc-sample-clk"; + reg = <0x01c2008c 0x4>; + clocks = <&mmc1_clk>; + clock-output-names = "mmc1_sample"; + }; + mmc2_clk: clk at 01c20090 { #clock-cells = <0>; compatible = "allwinner,sun4i-a10-mod0-clk";@@ -291,6 +323,22 @@ clock-output-names = "mmc2"; }; + mmc2_output_clk: mmc_output_clk at 01c20090 { + #clock-cells = <0>; + compatible = "allwinner,sun4i-a10-mmc-output-clk"; + reg = <0x01c20090 0x4>; + clocks = <&mmc2_clk>; + clock-output-names = "mmc2_output"; + }; + + mmc2_sample_clk: mmc_sample_clk at 01c20090 { + #clock-cells = <0>; + compatible = "allwinner,sun4i-a10-mmc-sample-clk"; + reg = <0x01c20090 0x4>; + clocks = <&mmc2_clk>; + clock-output-names = "mmc2_sample"; + }; + mmc3_clk: clk at 01c20094 { #clock-cells = <0>; compatible = "allwinner,sun4i-a10-mod0-clk";@@ -299,6 +347,22 @@ clock-output-names = "mmc3"; }; + mmc3_output_clk: mmc_output_clk at 01c20094 { + #clock-cells = <0>; + compatible = "allwinner,sun4i-a10-mmc-output-clk"; + reg = <0x01c20094 0x4>; + clocks = <&mmc3_clk>; + clock-output-names = "mmc3_output"; + }; + + mmc3_sample_clk: mmc_sample_clk at 01c20094 { + #clock-cells = <0>; + compatible = "allwinner,sun4i-a10-mmc-sample-clk"; + reg = <0x01c20094 0x4>; + clocks = <&mmc3_clk>; + clock-output-names = "mmc3_sample"; + }; + ts_clk: clk at 01c20098 { #clock-cells = <0>; compatible = "allwinner,sun4i-a10-mod0-clk";@@ -512,8 +576,14 @@ mmc0: mmc at 01c0f000 { compatible = "allwinner,sun5i-a13-mmc"; reg = <0x01c0f000 0x1000>; - clocks = <&ahb_gates 8>, <&mmc0_clk>; - clock-names = "ahb", "mmc"; + clocks = <&ahb_gates 8>, + <&mmc0_clk>, + <&mmc0_sample_clk>, + <&mmc0_output_clk>; + clock-names = "ahb", + "mmc", + "sample", + "output"; interrupts = <0 32 4>; status = "disabled"; };@@ -521,8 +591,14 @@ mmc1: mmc at 01c10000 { compatible = "allwinner,sun5i-a13-mmc"; reg = <0x01c10000 0x1000>; - clocks = <&ahb_gates 9>, <&mmc1_clk>; - clock-names = "ahb", "mmc"; + clocks = <&ahb_gates 9>, + <&mmc1_clk>, + <&mmc1_sample_clk>, + <&mmc1_output_clk>; + clock-names = "ahb", + "mmc", + "sample", + "output"; interrupts = <0 33 4>; status = "disabled"; };@@ -530,8 +606,14 @@ mmc2: mmc at 01c11000 { compatible = "allwinner,sun5i-a13-mmc"; reg = <0x01c11000 0x1000>; - clocks = <&ahb_gates 10>, <&mmc2_clk>; - clock-names = "ahb", "mmc"; + clocks = <&ahb_gates 10>, + <&mmc2_clk>, + <&mmc2_sample_clk>, + <&mmc2_output_clk>; + clock-names = "ahb", + "mmc", + "sample", + "output"; interrupts = <0 34 4>; status = "disabled"; };@@ -539,8 +621,14 @@ mmc3: mmc at 01c12000 { compatible = "allwinner,sun5i-a13-mmc"; reg = <0x01c12000 0x1000>; - clocks = <&ahb_gates 11>, <&mmc3_clk>; - clock-names = "ahb", "mmc"; + clocks = <&ahb_gates 11>, + <&mmc3_clk>, + <&mmc3_sample_clk>, + <&mmc3_output_clk>; + clock-names = "ahb", + "mmc", + "sample", + "output"; interrupts = <0 35 4>; status = "disabled"; };arch/arm/boot/dts/sun8i-a23.dtsi +72
-7@@ -224,6 +224,22 @@ clock-output-names = "mmc0"; }; + mmc0_output_clk: mmc_output_clk at 01c20088 { + #clock-cells = <0>; + compatible = "allwinner,sun4i-a10-mmc-output-clk"; + reg = <0x01c20088 0x4>; + clocks = <&mmc0_clk>; + clock-output-names = "mmc0_output"; + }; + + mmc0_sample_clk: mmc_sample_clk at 01c20088 { + #clock-cells = <0>; + compatible = "allwinner,sun4i-a10-mmc-sample-clk"; + reg = <0x01c20088 0x4>; + clocks = <&mmc0_clk>; + clock-output-names = "mmc0_sample"; + }; + mmc1_clk: clk at 01c2008c { #clock-cells = <0>; compatible = "allwinner,sun4i-a10-mod0-clk";@@ -232,6 +248,22 @@ clock-output-names = "mmc1"; }; + mmc1_output_clk: mmc_output_clk at 01c2008c { + #clock-cells = <0>; + compatible = "allwinner,sun4i-a10-mmc-output-clk"; + reg = <0x01c2008c 0x4>; + clocks = <&mmc1_clk>; + clock-output-names = "mmc1_output"; + }; + + mmc1_sample_clk: mmc_sample_clk at 01c2008c { + #clock-cells = <0>; + compatible = "allwinner,sun4i-a10-mmc-sample-clk"; + reg = <0x01c2008c 0x4>; + clocks = <&mmc1_clk>; + clock-output-names = "mmc1_sample"; + }; + mmc2_clk: clk at 01c20090 { #clock-cells = <0>; compatible = "allwinner,sun4i-a10-mod0-clk";@@ -239,6 +271,22 @@ clocks = <&osc24M>, <&pll6>; clock-output-names = "mmc2"; }; + + mmc2_output_clk: mmc_output_clk at 01c20090 { + #clock-cells = <0>; + compatible = "allwinner,sun4i-a10-mmc-output-clk"; + reg = <0x01c20090 0x4>; + clocks = <&mmc2_clk>; + clock-output-names = "mmc2_output"; + }; + + mmc2_sample_clk: mmc_sample_clk at 01c20090 { + #clock-cells = <0>; + compatible = "allwinner,sun4i-a10-mmc-sample-clk"; + reg = <0x01c20090 0x4>; + clocks = <&mmc2_clk>; + clock-output-names = "mmc2_sample"; + }; }; soc at 01c00000 {@@ -250,8 +298,14 @@ mmc0: mmc at 01c0f000 { compatible = "allwinner,sun5i-a13-mmc"; reg = <0x01c0f000 0x1000>; - clocks = <&ahb1_gates 8>, <&mmc0_clk>; - clock-names = "ahb", "mmc"; + clocks = <&ahb1_gates 8>, + <&mmc0_clk>, + <&mmc0_sample_clk>, + <&mmc0_output_clk>; + clock-names = "ahb", + "mmc", + "sample", + "output"; resets = <&ahb1_rst 8>; reset-names = "ahb"; interrupts = <0 60 4>;@@ -261,8 +315,14 @@ mmc1: mmc at 01c10000 { compatible = "allwinner,sun5i-a13-mmc"; reg = <0x01c10000 0x1000>; - clocks = <&ahb1_gates 9>, <&mmc1_clk>; - clock-names = "ahb", "mmc"; + clocks = <&ahb1_gates 9>, + <&mmc1_clk>, + <&mmc1_sample_clk>, + <&mmc1_output_clk>; + clock-names = "ahb", + "mmc", + "sample", + "output"; resets = <&ahb1_rst 9>; reset-names = "ahb"; interrupts = <0 61 4>;@@ -272,8 +332,14 @@ mmc2: mmc at 01c11000 { compatible = "allwinner,sun5i-a13-mmc"; reg = <0x01c11000 0x1000>; - clocks = <&ahb1_gates 10>, <&mmc2_clk>; - clock-names = "ahb", "mmc"; + clocks = <&ahb1_gates 10>, + <&mmc2_clk>, + <&mmc2_sample_clk>, + <&mmc2_output_clk>; + clock-names = "ahb", + "mmc", + "sample", + "output"; resets = <&ahb1_rst 10>; reset-names = "ahb"; interrupts = <0 62 4>; --2.1.0
PATCH v3 9/12 ARM: sunxi: dt: Add sample and output mmc clocks
Quoted 14 lines · Chen-Yu Tsai
arch/arm/boot/dts/sun4i-a10.dtsi line 466
Quoted 13 lines
mmc0: mmc at 01c0f000 { compatible = "allwinner,sun4i-a10-mmc"; reg = <0x01c0f000 0x1000>;arch/arm/boot/dts/sun4i-a10.dtsi +8
-2- clocks = <&ahb_gates 8>, <&mmc0_clk>; - clock-names = "ahb", "mmc"; + clocks = <&ahb_gates 8>, + <&mmc0_clk>, + <&mmc0_sample_clk>, + <&mmc0_output_clk>; + clock-names = "ahb", + "mmc", + "sample", + "output";
Quoted 2 lines
Just a stylish nitpick. All the other resources in these files are listed inline if possible, not one phandle/value per line.
Look at the PIO interrupts, every USB PHY resources, etc. ;)
Maxime
Signature · 10 lines
Maxime Ripard, Free Electrons Embedded Linux, Kernel and Android engineering http://free-electrons.com -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 819 bytes Desc: Digital signature URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140913/3cf98b29/attachment.sig>
PATCH v3 9/12 ARM: sunxi: dt: Add sample and output mmc clocks
Quoted 15 lines · Maxime Ripard
Quoted 14 lines · Chen-Yu Tsai
Quoted 13 lines
mmc0: mmc at 01c0f000 { compatible = "allwinner,sun4i-a10-mmc"; reg = <0x01c0f000 0x1000>;arch/arm/boot/dts/sun4i-a10.dtsi +8
-2- clocks = <&ahb_gates 8>, <&mmc0_clk>; - clock-names = "ahb", "mmc"; + clocks = <&ahb_gates 8>, + <&mmc0_clk>, + <&mmc0_sample_clk>, + <&mmc0_output_clk>; + clock-names = "ahb", + "mmc", + "sample", + "output";
Quoted 4 lines
Quoted 2 lines
Just a stylish nitpick. All the other resources in these files are listed inline if possible, not one phandle/value per line.
Look at the PIO interrupts, every USB PHY resources, etc. ;)
I guess we need some styling cleanups. :)
ChenYu
PATCH v3 10/12 clk: sunxi: mod0: Introduce MMC proper phase handling
In reply to Maxime RipardThe MMC clock we thought we had until now are actually not one but three different clocks.
The main one is unchanged, and will have three outputs:
- The clock fed into the MMC
- a sample and output clocks, to deal with when should we output/sample data to/from the MMC bus
The phase control we had are actually controlling the two latter clocks, but the main MMC one is unchanged.
We can adjust the phase with a 3 bits value, from 0 to 7, 0 meaning a 180 phase shift, and the other values being the number of periods from the MMC parent clock to outphase the clock of.
--- Documentation/devicetree/bindings/clock/sunxi.txt | 2 + drivers/clk/sunxi/clk-mod0.c | 189 ++++++++++++++++++++++ 2 files changed, 191 insertions(+)
Documentation/devicetree/bindings/clock/sunxi.txt +2 -0
@@ -47,6 +47,8 @@ Required properties: "allwinner,sun6i-a31-apb2-gates-clk" - for the APB2 gates on A31 "allwinner,sun8i-a23-apb2-gates-clk" - for the APB2 gates on A23 "allwinner,sun5i-a13-mbus-clk" - for the MBUS clock on A13 + "allwinner,sun4i-a10-mmc-output-clk" - for the MMC output clock on A10 + "allwinner,sun4i-a10-mmc-sample-clk" - for the MMC sample clock on A10 "allwinner,sun4i-a10-mod0-clk" - for the module 0 family of clocks "allwinner,sun7i-a20-out-clk" - for the external output clocks "allwinner,sun7i-a20-gmac-clk" - for the GMAC clock module on A20/A31
drivers/clk/sunxi/clk-mod0.c +189 -0
198 lines not on this page · raw message
Signature · 1 line
2.1.0
PATCH v3 11/12 mmc: sunxi: Convert MMC driver to the standard clock phase API
In reply to Maxime RipardNow that we have proper support to use the generic phase API in our clock driver, switch the MMC driver to use it.
--- .../devicetree/bindings/mmc/sunxi-mmc.txt | 8 +-- drivers/mmc/host/sunxi-mmc.c | 72 +++++++++++++++------- 2 files changed, 53 insertions(+), 27 deletions(-)
Documentation/devicetree/bindings/mmc/sunxi-mmc.txt +4 -4
@@ -10,8 +10,8 @@ Absolute maximum transfer rate is 200MB/s Required properties: - compatible : "allwinner,sun4i-a10-mmc" or "allwinner,sun5i-a13-mmc" - reg : mmc controller base registers - - clocks : a list with 2 phandle + clock specifier pairs - - clock-names : must contain "ahb" and "mmc" + - clocks : a list with 4 phandle + clock specifier pairs + - clock-names : must contain "ahb", "mmc", "output" and "sample" - interrupts : mmc controller interrupt Optional properties:
@@ -25,8 +25,8 @@ Examples: mmc0: mmc at 01c0f000 { compatible = "allwinner,sun5i-a13-mmc"; reg = <0x01c0f000 0x1000>; - clocks = <&ahb_gates 8>, <&mmc0_clk>; - clock-names = "ahb", "mod"; + clocks = <&ahb_gates 8>, <&mmc0_clk>, <&mmc0_output_clk>, <&mmc0_sample_clk>; + clock-names = "ahb", "mod", "output", "sample"; interrupts = <0 32 4>; status = "disabled"; };
drivers/mmc/host/sunxi-mmc.c +49 -23
126 lines not on this page · raw message
Signature · 1 line
2.1.0
PATCH v3 11/12 mmc: sunxi: Convert MMC driver to the standard clock phase API
Hello
drivers/mmc/host/sunxi-mmc.c static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host, · line 648
Quoted 10 lines
Now that we have proper support to use the generic phase API in our clock driver, switch the MMC driver to use it.
[...]drivers/mmc/host/sunxi-mmc.c +2
-2/* determine delays */ if (rate <=400000 ) { - oclk_dly =0 ; - sclk_dly =7 ; + oclk_dly =180 ; + sclk_dly =42 ;
How did you calculate the actual phase values from the original parameters?
greatings
Signature · 11 lines
David Lanzend?rfer OpenSourceSupport GmbH System engineer and supporter http://www.o2s.ch/ -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 198 bytes Desc: This is a digitally signed message part. URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140926/8bc39436/attachment.sig>
PATCH v3 11/12 mmc: sunxi: Convert MMC driver to the standard clock phase API
Quoted 12 lines · David Lanzend?rfer
Hello
drivers/mmc/host/sunxi-mmc.c static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host, · line 648
Quoted 10 lines
Now that we have proper support to use the generic phase API in our clock driver, switch the MMC driver to use it.
[...]drivers/mmc/host/sunxi-mmc.c +2
-2/* determine delays */ if (rate <=400000 ) { - oclk_dly =0 ; - sclk_dly =7 ; + oclk_dly =180 ; + sclk_dly =42 ;How did you calculate the actual phase values from the original parameters?
Like shown in the clock driver. 0 is always a 180 deg phase shift, and any other value is the number of the parent clock cycle to outphase the clock of.
the clock out phase is calculating using:
phase = 360 * interval / period.
with period = 1 / (parent_freq / divider)
and interval = reg / parent_freq
which makes
phase = 360 * (reg / parent_freq) * (parent_freq / divider)
If we simplify by removing the parent_freq, we end up with
phase = 360 * reg / divider, which makes it rather trivial to calculate.
Maxime
Signature · 10 lines
Maxime Ripard, Free Electrons Embedded Linux, Kernel and Android engineering http://free-electrons.com -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 819 bytes Desc: Digital signature URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140926/6476fba8/attachment.sig>
PATCH v3 11/12 mmc: sunxi: Convert MMC driver to the standard clock phase API
Hi
Great! Since I didn't see any mistakes from my side it shouldn't be a problem. Because someone has borrowed my UART2USB(FT232)-cable I unfortunately can't test the code for now.
If you can verfiy that all the adaptions are working properly on A10/A20 and all the others you my assume my:
chears and all the best
david
Quoted 25 lines
Quoted 3 lines
Quoted 1 line
[...]
How did you calculate the actual phase values from the original parameters?
Like shown in the clock driver. 0 is always a 180 deg phase shift, and any other value is the number of the parent clock cycle to outphase the clock of.
the clock out phase is calculating using:
phase = 360 * interval / period.
with period = 1 / (
parent_freq/ divider)
and interval = reg /parent_freqwhich makes
phase = 360 * (reg /
parent_freq) * (parent_freq/ divider)If we simplify by removing the
parent_freq, we end up withphase = 360 * reg / divider, which makes it rather trivial to calculate.
Maxime
Signature · 11 lines
David Lanzend?rfer OpenSourceSupport GmbH System engineer and supporter http://www.o2s.ch/ -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 198 bytes Desc: This is a digitally signed message part. URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140926/074b17e2/attachment.sig>
PATCH v3 12/12 clk: sunxi: Remove custom phase function
In reply to Maxime RipardNow that we don't have any user left for our custom phase function, we can safely remove this hack from the code.
--- drivers/clk/sunxi/clk-sunxi.c | 37 ------------------------------------- include/linux/clk/sunxi.h | 22 ---------------------- 2 files changed, 59 deletions(-) delete mode 100644 include/linux/clk/sunxi.h
drivers/clk/sunxi/clk-sunxi.c +0 -37
@@ -361,43 +361,6 @@ static void sun7i_a20_get_out_factors(u32 *freq, u32 parent_rate, }/** -* clk_sunxi_mmc_phase_control() - configures MMC clock phase control -*/ - -void clk_sunxi_mmc_phase_control (struct clk *clk,u8 sample,u8 output) -{ -#define to_clk_composite (_hw) container_of(_hw, struct clk_composite, hw) -#define to_clk_factors (_hw) container_of(_hw, struct clk_factors, hw) - -struct clk_hw *hw =__clk_get_hw (clk); -struct clk_composite *composite =to_clk_composite (hw); -struct clk_hw *rate_hw = composite->rate_hw; -struct clk_factors *factors =to_clk_factors (rate_hw); -unsigned long flags =0 ; -u32 reg; - -if (factors->lock) -spin_lock_irqsave (factors->lock, flags); - - reg =readl (factors->reg); - -/* set sample clock phase control */ - reg &= ~(0x7 <<20 ); - reg |= ((sample &0x7 ) <<20 ); - -/* set output clock phase control */ - reg &= ~(0x7 <<8 ); - reg |= ((output &0x7 ) <<8 ); - -writel (reg, factors->reg); - -if (factors->lock) -spin_unlock_irqrestore (factors->lock, flags); -} -EXPORT_SYMBOL (clk_sunxi_mmc_phase_control); - - -/** * sunxi_factors_clk_setup() - Setup function for factor clocks */
include/linux/clk/sunxi.h deleted +0 -22
@@ -1,22 +0,0 @@ -/* -* Copyright 2013 - Hans de Goede <[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_ - -#include <linux/clk.h> - -void clk_sunxi_mmc_phase_control (struct clk *clk,u8 sample,u8 output); - -#endif
Signature · 1 line
2.1.0
v3 cover clk: sunxi: Improve MMC clocks support
In reply to Maxime RipardHi,
Quoted 9 lines · Maxime Ripard
Hi everyone,
Here is an attempt at improving the MMC clock support in the Allwinner SoCs.
Until now, the MMC clocks were having a custom phase function that was directly setting an obscure value in the right register, because we were not really having any idea of what these values were.
Quoted 6 lines
Now that we have more informations, we can introduce a common function call to get and set the phase of a particular clock, and use this in both our provider and our client.
Thanks,
Maxime
I've not done a full review this time around (-ENOTIME), but I've taken a look at the changed bits, and those look good to me.
Regards,
Hans
v3 cover clk: sunxi: Improve MMC clocks support
In reply to Maxime RipardMike,
Quoted 5 lines · Maxime Ripard
Hi everyone,
Here is an attempt at improving the MMC clock support in the Allwinner SoCs.
Quoted 7 lines
Until now, the MMC clocks were having a custom phase function that was directly setting an obscure value in the right register, because we were not really having any idea of what these values were.
Now that we have more informations, we can introduce a common function call to get and set the phase of a particular clock, and use this in both our provider and our client.
It would be great that we have that custom phase API out of the kernel for 3.18, and you were one of the greatest supporters to that idea, could we please have your view on this?
Thanks,
Maxime
Signature · 10 lines
Maxime Ripard, Free Electrons Embedded Linux, Kernel and Android engineering http://free-electrons.com -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 819 bytes Desc: Digital signature URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140922/b6ddffc8/attachment.sig>
v3 cover clk: sunxi: Improve MMC clocks support
Quoting Maxime Ripard (2014-09-22 04:31:33)
Quoted 12 lines
Mike,
Quoted 9 lines · Maxime Ripard
Hi everyone,
Here is an attempt at improving the MMC clock support in the Allwinner SoCs.
Until now, the MMC clocks were having a custom phase function that was directly setting an obscure value in the right register, because we were not really having any idea of what these values were.
Quoted 7 lines
Quoted 3 lines
Now that we have more informations, we can introduce a common function call to get and set the phase of a particular clock, and use this in both our provider and our client.
It would be great that we have that custom phase API out of the kernel for 3.18, and you were one of the greatest supporters to that idea, could we please have your view on this?
Patches all look good to me. Did you want the whole series to go through the clk tree? If so we'll need an Ack for the MMC patch and for the ARM DTS patches as well.
Regards,
Mike
Quoted 8 lines
Thanks,
MaximeSignature · 3 lines
Maxime Ripard, Free Electrons Embedded Linux, Kernel and Android engineering http://free-electrons.com
v3 cover clk: sunxi: Improve MMC clocks support
Hi Mike,
Quoted 17 lines · Mike Turquette
Quoting Maxime Ripard (2014-09-22 04:31:33)
Quoted 16 lines
Mike,
Quoted 12 lines · Maxime Ripard
Hi everyone,
Here is an attempt at improving the MMC clock support in the Allwinner SoCs.
Until now, the MMC clocks were having a custom phase function that was directly setting an obscure value in the right register, because we were not really having any idea of what these values were.
Now that we have more informations, we can introduce a common function call to get and set the phase of a particular clock, and use this in both our provider and our client.
Quoted 6 lines
Quoted 3 lines
It would be great that we have that custom phase API out of the kernel for 3.18, and you were one of the greatest supporters to that idea, could we please have your view on this?
Patches all look good to me. Did you want the whole series to go through the clk tree?
I guess it would be where it most make sense yes.
Quoted 2 lines
If so we'll need an Ack for the MMC patch and for the ARM DTS patches as well.
We already have an Ack from Ulf on the MMC patch, and I'm the maintainer of the DTS involved, so we should be ok :)
Thanks!
Maxime
Signature · 10 lines
Maxime Ripard, Free Electrons Embedded Linux, Kernel and Android engineering http://free-electrons.com -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 819 bytes Desc: Digital signature URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140926/c3dd532a/attachment.sig>
v3 cover clk: sunxi: Improve MMC clocks support
Quoting Maxime Ripard (2014-09-25 23:55:07)
Quoted 27 lines
Hi Mike,
Quoted 17 lines · Mike Turquette
Quoting Maxime Ripard (2014-09-22 04:31:33)
Quoted 16 lines
Mike,
Quoted 12 lines · Maxime Ripard
Hi everyone,
Here is an attempt at improving the MMC clock support in the Allwinner SoCs.
Until now, the MMC clocks were having a custom phase function that was directly setting an obscure value in the right register, because we were not really having any idea of what these values were.
Now that we have more informations, we can introduce a common function call to get and set the phase of a particular clock, and use this in both our provider and our client.
Quoted 6 lines
Quoted 3 lines
It would be great that we have that custom phase API out of the kernel for 3.18, and you were one of the greatest supporters to that idea, could we please have your view on this?
Patches all look good to me. Did you want the whole series to go through the clk tree?
Quoted 7 lines
I guess it would be where it most make sense yes.
Quoted 2 lines
If so we'll need an Ack for the MMC patch and for the ARM DTS patches as well.
We already have an Ack from Ulf on the MMC patch, and I'm the maintainer of the DTS involved, so we should be ok :)
Oops, I didn't notice Ulf's Ack. Will either your or Emilio be sending a pull request for the sunxi clock changes?
Regards,
Mike
Quoted 8 lines
Thanks!
MaximeSignature · 3 lines
Maxime Ripard, Free Electrons Embedded Linux, Kernel and Android engineering http://free-electrons.com