ARM: imx: shared gate support for i.MX clk_gate2 clock
Subsystems (6)
cover ARM: imx: shared gate support for i.MX clk_gate2 clock
We're constantly running into the situation on i.MX that multiple clocks share the single gate. Since we already have i.MX specific gate clock implementation due to 2-bits gate, the series takes Gerhard's approach to add shared gate support for i.MX clk_gate2 clock.
Shawn Guo (4):
ARM: imx: define struct clk_gate2 on our own
ARM: imx: lock is always valid for clk_gate2
ARM: imx: add shared gate clock support
ARM: imx6q: add the missing esai_ahb clock
.../devicetree/bindings/clock/imx6q-clock.txt | 1 + arch/arm/mach-imx/clk-gate2.c | 47 ++++++++++++++-------- arch/arm/mach-imx/clk-imx6q.c | 7 +++- arch/arm/mach-imx/clk.h | 13 +++++- 4 files changed, 48 insertions(+), 20 deletions(-)
Signature · 1 line
1.8.3.2
PATCH 1/4 ARM: imx: define struct clk_gate2 on our own
The imx clk-gate2 driver implements an i.MX specific gate clock, which has two bits controlling the gate states. While this is a completely separate gate driver from the common clk-gate one, it reuses the common clk_gate structure. Such reusing makes the extending of clk_gate2 clumsy. Let's define struct clk_gate2 on our own to make the driver independent of the common clk-gate one, and ease the clk_gate2 extending at a later time.
--- arch/arm/mach-imx/clk-gate2.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-)
arch/arm/mach-imx/clk-gate2.c +15 -7
@@ -27,11 +27,19 @@* parent - fixed parent. No clk_set_parent support */ -#define to_clk_gate (_hw) container_of(_hw, struct clk_gate, hw) +struct clk_gate2 { +struct clk_hw hw; +void __iomem *reg; +u8 bit_idx; +u8 flags; +spinlock_t *lock; +}; + +#define to_clk_gate2 (_hw) container_of(_hw, struct clk_gate2, hw)static int clk_gate2_enable (struct clk_hw *hw) { -struct clk_gate *gate =to_clk_gate (hw); +struct clk_gate2 *gate =to_clk_gate2 (hw);u32 reg;unsigned long flags =0 ;
@@ -50,7 +58,7 @@ static int clk_gate2_enable(struct clk_hw *hw)static void clk_gate2_disable (struct clk_hw *hw) { -struct clk_gate *gate =to_clk_gate (hw); +struct clk_gate2 *gate =to_clk_gate2 (hw);u32 reg;unsigned long flags =0 ;
@@ -68,7 +76,7 @@ static void clk_gate2_disable(struct clk_hw *hw)static int clk_gate2_is_enabled (struct clk_hw *hw) {u32 reg; -struct clk_gate *gate =to_clk_gate (hw); +struct clk_gate2 *gate =to_clk_gate2 (hw); reg =readl (gate->reg);
@@ -89,15 +97,15 @@ struct clk *clk_register_gate2(struct device *dev, const char *name, void __iomem *reg, u8 bit_idx, u8 clk_gate2_flags, spinlock_t *lock) { - struct clk_gate *gate; + struct clk_gate2 *gate;struct clk *clk;struct clk_init_data init; - gate =kzalloc (sizeof (struct clk_gate ),GFP_KERNEL ); + gate =kzalloc (sizeof (struct clk_gate2 ),GFP_KERNEL );if (!gate)return ERR_PTR (-ENOMEM ); -/* struct clk_gate assignments */ +/* struct clk_gate2 assignments */ gate->reg = reg; gate->bit_idx = bit_idx; gate->flags = clk_gate2_flags;
Signature · 1 line
1.8.3.2
PATCH 2/4 ARM: imx: lock is always valid for clk_gate2
In reply to Shawn GuoThe imx specific clk_gate2 always has a valid lock with the clock. So the validation on gate->lock is not really needed. Remove it.
--- arch/arm/mach-imx/clk-gate2.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-)
arch/arm/mach-imx/clk-gate2.c +4 -8
@@ -43,15 +43,13 @@ static int clk_gate2_enable(struct clk_hw *hw)u32 reg;unsigned long flags =0 ; -if (gate->lock) -spin_lock_irqsave (gate->lock, flags); +spin_lock_irqsave (gate->lock, flags); reg =readl (gate->reg); reg |=3 << gate->bit_idx;writel (reg, gate->reg); -if (gate->lock) -spin_unlock_irqrestore (gate->lock, flags); +spin_unlock_irqrestore (gate->lock, flags);return 0 ; }
@@ -62,15 +60,13 @@ static void clk_gate2_disable(struct clk_hw *hw)u32 reg;unsigned long flags =0 ; -if (gate->lock) -spin_lock_irqsave (gate->lock, flags); +spin_lock_irqsave (gate->lock, flags); reg =readl (gate->reg); reg &= ~(3 << gate->bit_idx);writel (reg, gate->reg); -if (gate->lock) -spin_unlock_irqrestore (gate->lock, flags); +spin_unlock_irqrestore (gate->lock, flags); }static int clk_gate2_is_enabled (struct clk_hw *hw)
Signature · 1 line
1.8.3.2
PATCH 3/4 ARM: imx: add shared gate clock support
In reply to Shawn GuoIt's quite common on i.MX that one gate bit controls the gating of multiple clocks, i.e. this is a shared gate. The patch adds the function imx_clk_gate2_shared() for such case. The clocks controlled by the same gate bits should call this function with a pointer to a single share count variable, so that the gate bits will only be operated on the first enabling and the last disabling of these shared gate clocks.
Thanks to Gerhard Sittig <[email protected]> for this idea.
--- arch/arm/mach-imx/clk-gate2.c | 13 ++++++++++++- arch/arm/mach-imx/clk.h | 13 +++++++++++-- 2 files changed, 23 insertions(+), 3 deletions(-)
arch/arm/mach-imx/clk-gate2.c +12 -1
@@ -33,6 +33,7 @@ struct clk_gate2 {u8 bit_idx;u8 flags;spinlock_t *lock; +unsigned int *share_count; };#define to_clk_gate2 (_hw) container_of(_hw, struct clk_gate2, hw)
@@ -45,10 +46,14 @@ static int clk_gate2_enable(struct clk_hw *hw)spin_lock_irqsave (gate->lock, flags); +if (gate->share_count && (*gate->share_count)++ >0 ) + goto out; + reg =readl (gate->reg); reg |=3 << gate->bit_idx;writel (reg, gate->reg); +out:spin_unlock_irqrestore (gate->lock, flags);return 0 ;
@@ -62,10 +67,14 @@ static void clk_gate2_disable(struct clk_hw *hw)spin_lock_irqsave (gate->lock, flags); +if (gate->share_count && --(*gate->share_count) >0 ) + goto out; + reg =readl (gate->reg); reg &= ~(3 << gate->bit_idx);writel (reg, gate->reg); +out:spin_unlock_irqrestore (gate->lock, flags); }
@@ -91,7 +100,8 @@ static struct clk_ops clk_gate2_ops = {struct clk *clk_register_gate2 (struct device *dev,const char *name,const char *parent_name,unsigned long flags,void __iomem *reg,u8 bit_idx, -u8 clk_gate2_flags,spinlock_t *lock) +u8 clk_gate2_flags,spinlock_t *lock, +unsigned int *share_count) {struct clk_gate2 *gate;struct clk *clk;
@@ -106,6 +116,7 @@ struct clk *clk_register_gate2(struct device *dev, const char *name, gate->bit_idx = bit_idx; gate->flags = clk_gate2_flags; gate->lock = lock; + gate->share_count = share_count; init.name = name; init.ops = &clk_gate2_ops;
arch/arm/mach-imx/clk.h +11 -2
@@ -28,7 +28,8 @@ struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name,struct clk *clk_register_gate2 (struct device *dev,const char *name,const char *parent_name,unsigned long flags,void __iomem *reg,u8 bit_idx, -u8 clk_gate_flags,spinlock_t *lock); +u8 clk_gate_flags,spinlock_t *lock, +unsigned int *share_count);struct clk *imx_obtain_fixed_clock (const char *name,unsigned long rate);
@@ -37,7 +38,15 @@ static inline struct clk *imx_clk_gate2(const char *name, const char *parent, void __iomem *reg, u8 shift) {return clk_register_gate2 (NULL , name, parent,CLK_SET_RATE_PARENT , reg, - shift,0 , &imx_ccm_lock); + shift,0 , &imx_ccm_lock,NULL ); +} + +static inline struct clk *imx_clk_gate2_shared (const char *name, +const char *parent,void __iomem *reg,u8 shift, +unsigned int *share_count) +{ +return clk_register_gate2 (NULL , name, parent,CLK_SET_RATE_PARENT , reg, + shift,0 , &imx_ccm_lock, share_count); } struct clk *imx_clk_pfd(const char *name, const char *parent_name,
Signature · 1 line
1.8.3.2
PATCH 3/4 ARM: imx: add shared gate clock support
Quoted 11 lines · Shawn Guo
It's quite common on i.MX that one gate bit controls the gating of multiple clocks, i.e. this is a shared gate. The patch adds the function
imx_clk_gate2_shared()for such case. The clocks controlled by the same gate bits should call this function with a pointer to a single share count variable, so that the gate bits will only be operated on the first enabling and the last disabling of these shared gate clocks.Thanks to Gerhard Sittig <[email protected]> for this idea.
Signed-off-by Shawn Guo <[email protected]>
Marvelous! It's seemly also applicable to our 3.10.y branch. It'll be superbly helpful to both upstream and internal release.
Thank you Shawn!
Nicolin
Quoted 100 lines
--- arch/arm/mach-imx/clk-gate2.c | 13 ++++++++++++- arch/arm/mach-imx/clk.h | 13 +++++++++++-- 2 files changed, 23 insertions(+), 3 deletions(-)arch/arm/mach-imx/clk-gate2.c +12
-1@@ -33,6 +33,7 @@ struct clk_gate2 {u8 bit_idx;u8 flags;spinlock_t *lock; +unsigned int *share_count; };#define to_clk_gate2 (_hw) container_of(_hw, struct clk_gate2, hw)@@ -45,10 +46,14 @@ static int clk_gate2_enable(struct clk_hw *hw)spin_lock_irqsave (gate->lock, flags); +if (gate->share_count && (*gate->share_count)++ >0 ) + goto out; + reg =readl (gate->reg); reg |=3 << gate->bit_idx;writel (reg, gate->reg); +out:spin_unlock_irqrestore (gate->lock, flags);return 0 ;@@ -62,10 +67,14 @@ static void clk_gate2_disable(struct clk_hw *hw)spin_lock_irqsave (gate->lock, flags); +if (gate->share_count && --(*gate->share_count) >0 ) + goto out; + reg =readl (gate->reg); reg &= ~(3 << gate->bit_idx);writel (reg, gate->reg); +out:spin_unlock_irqrestore (gate->lock, flags); }@@ -91,7 +100,8 @@ static struct clk_ops clk_gate2_ops = {struct clk *clk_register_gate2 (struct device *dev,const char *name,const char *parent_name,unsigned long flags,void __iomem *reg,u8 bit_idx, -u8 clk_gate2_flags,spinlock_t *lock) +u8 clk_gate2_flags,spinlock_t *lock, +unsigned int *share_count) {struct clk_gate2 *gate;struct clk *clk;@@ -106,6 +116,7 @@ struct clk *clk_register_gate2(struct device *dev, const char *name, gate->bit_idx = bit_idx; gate->flags = clk_gate2_flags; gate->lock = lock; + gate->share_count = share_count; init.name = name; init.ops = &clk_gate2_ops;arch/arm/mach-imx/clk.h +11
-2@@ -28,7 +28,8 @@ struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name,struct clk *clk_register_gate2 (struct device *dev,const char *name,const char *parent_name,unsigned long flags,void __iomem *reg,u8 bit_idx, -u8 clk_gate_flags,spinlock_t *lock); +u8 clk_gate_flags,spinlock_t *lock, +unsigned int *share_count);struct clk *imx_obtain_fixed_clock (const char *name,unsigned long rate);@@ -37,7 +38,15 @@ static inline struct clk *imx_clk_gate2(const char *name, const char *parent, void __iomem *reg, u8 shift) {return clk_register_gate2 (NULL , name, parent,CLK_SET_RATE_PARENT , reg, - shift,0 , &imx_ccm_lock); + shift,0 , &imx_ccm_lock,NULL ); +} + +static inline struct clk *imx_clk_gate2_shared (const char *name, +const char *parent,void __iomem *reg,u8 shift, +unsigned int *share_count) +{ +return clk_register_gate2 (NULL , name, parent,CLK_SET_RATE_PARENT , reg, + shift,0 , &imx_ccm_lock, share_count); } struct clk *imx_clk_pfd(const char *name, const char *parent_name,Signature · 1 line
1.8.3.2
PATCH 4/4 ARM: imx6q: add the missing esai_ahb clock
In reply to Shawn GuoThe esai_ahb clock is derived from ahb and used to provide ESAI the capability of register accessing and FSYS clock source for I2S clocks dividing. The gate bits of this esai_ahb clock are shared with the esai clock -- the baud clock, so we need to call imx_clk_gate2_shared() for these two clocks.
--- Documentation/devicetree/bindings/clock/imx6q-clock.txt | 1 + arch/arm/mach-imx/clk-imx6q.c | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-)
Documentation/devicetree/bindings/clock/imx6q-clock.txt +1 -0
@@ -220,6 +220,7 @@ clocks and IDs. lvds2_sel 205 lvds1_gate 206 lvds2_gate 207 + esai_ahb 208 Examples:
arch/arm/mach-imx/clk-imx6q.c +5 -2
@@ -107,7 +107,7 @@ enum mx6q_clks { sata_ref, sata_ref_100m, pcie_ref, pcie_ref_125m, enet_ref, usbphy1_gate, usbphy2_gate, pll4_post_div, pll5_post_div, pll5_video_div, eim_slow, spdif, cko2_sel, cko2_podf, cko2, cko, vdoa, pll4_audio_div, - lvds1_sel, lvds2_sel, lvds1_gate, lvds2_gate, clk_max + lvds1_sel, lvds2_sel, lvds1_gate, lvds2_gate, esai_ahb, clk_max };static struct clk *clk[clk_max];
@@ -140,6 +140,8 @@ static struct clk_div_table video_div_table[] = { {/* sentinel */ } }; +static unsigned int share_count_esai; +static void __init imx6q_clocks_init (struct device_node *ccm_node) {struct device_node *np;
@@ -358,7 +360,8 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node)else clk[ecspi5] =imx_clk_gate2 ("ecspi5" ,"ecspi_root" , base +0x6c ,8 ); clk[enet] =imx_clk_gate2 ("enet" ,"ipg" , base +0x6c ,10 ); - clk[esai] =imx_clk_gate2 ("esai" ,"esai_podf" , base +0x6c ,16 ); + clk[esai] =imx_clk_gate2_shared ("esai" ,"esai_podf" , base +0x6c ,16 , &share_count_esai); + clk[esai_ahb] =imx_clk_gate2_shared ("esai_ahb" ,"ahb" , base +0x6c ,16 , &share_count_esai); clk[gpt_ipg] =imx_clk_gate2 ("gpt_ipg" ,"ipg" , base +0x6c ,20 ); clk[gpt_ipg_per] =imx_clk_gate2 ("gpt_ipg_per" ,"ipg_per" , base +0x6c ,22 );if (cpu_is_imx6dl ())
Signature · 1 line
1.8.3.2
cover ARM: imx: shared gate support for i.MX clk_gate2 clock
In reply to Shawn GuoHi all,
Quoted 10 lines · Shawn Guo
We're constantly running into the situation on i.MX that multiple clocks share the single gate. Since we already have i.MX specific gate clock implementation due to 2-bits gate, the series takes Gerhard's approach to add shared gate support for i.MX
clk_gate2clock.Shawn Guo (4):
ARM: imx: define structclk_gate2on our own
ARM: imx: lock is always valid forclk_gate2
ARM: imx: add shared gate clock support
ARM: imx6q: add the missingesai_ahbclock
I've simply tested these patches on both for-next and 3.10.y baseline and it works fine. Hope we can apply them soon.
Thank you,
Nicolin
Quoted 11 lines
.../devicetree/bindings/clock/imx6q-clock.txt | 1 + arch/arm/mach-imx/clk-gate2.c | 47 ++++++++++++++-------- arch/arm/mach-imx/clk-imx6q.c | 7 +++- arch/arm/mach-imx/clk.h | 13 +++++- 4 files changed, 48 insertions(+), 20 deletions(-)Signature · 1 line
1.8.3.2
cover ARM: imx: shared gate support for i.MX clk_gate2 clock
Quoted 8 lines · Nicolin Chen
Hi all,
Quoted 5 lines · Shawn Guo
We're constantly running into the situation on i.MX that multiple clocks share the single gate. Since we already have i.MX specific gate clock implementation due to 2-bits gate, the series takes Gerhard's approach to add shared gate support for i.MX
clk_gate2clock.
Quoted 8 lines
Quoted 5 lines
Shawn Guo (4):
ARM: imx: define structclk_gate2on our own
ARM: imx: lock is always valid forclk_gate2
ARM: imx: add shared gate clock support
ARM: imx6q: add the missingesai_ahbclockI've simply tested these patches on both for-next and 3.10.y baseline and it works fine. Hope we can apply them soon.
Thanks for testing, Nicolin. I just applied the series, and will get it into linux-next for wider testing soon.
Shawn