Skip to contentSkip to conversation

ARM: imx: shared gate support for i.MX clk_gate2 clock

PATCH New Shawn Guo 2 participants 8 messages Apr 19, 2014 – Apr 23, 2014

Subsystems (6)
lore
Share & tools
lore

Shawn Guo

author
#0 lore raw

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
author
#1 lore raw

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
index a2ecc00..7e98ccb 100644
@@ -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
author
#2 lore raw

PATCH 2/4 ARM: imx: lock is always valid for clk_gate2

In reply to Shawn Guo

The 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
index 7e98ccb..0803df9 100644
@@ -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
author
#3 lore raw

PATCH 3/4 ARM: imx: add shared gate clock support

In reply to 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.

---
 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
index 0803df9..4ba587d 100644
@@ -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
index 048c5ad8..e29f6eb 100644
@@ -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
#4 lore raw

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.

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
index 0803df9..4ba587d 100644
@@ -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
index 048c5ad8..e29f6eb 100644
@@ -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
author
#5 lore raw

PATCH 4/4 ARM: imx6q: add the missing esai_ahb clock

In reply to Shawn Guo

The 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
index 6aab72b..90ec91f 100644
@@ -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
index 8440878..8e795de 100644
@@ -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
#6 lore raw

cover ARM: imx: shared gate support for i.MX clk_gate2 clock

In reply to Shawn Guo

Hi 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_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

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
author
#7 lore raw

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_gate2 clock.

Quoted 8 lines
Quoted 5 lines

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

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.

Thanks for testing, Nicolin. I just applied the series, and will get it into linux-next for wider testing soon.

Shawn