Skip to contentSkip to conversation

clk: aspeed: Fix is_enabled and prevent reset if clock enabled

PATCH Applied Eddie James 4 participants 12 messages Mar 8, 2018 – Mar 15, 2018

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

v2 cover clk: aspeed: Fix is_enabled and prevent reset if clock enabled

Here are two fixes for the Aspeed clock driver. The first fixes the is_enabled clock function to account for different clock gates getting disabled with either 0s or 1s. The second patch addresses some issue found with the LPC controller clock if it gets reset while the clock is enabled, which it is by default. Thanks to Lei Yu for tracking down the LPC issue.

Changes since v1:
- Fix is_enabled.
- Check for enabled in the spinlock in the enable function.
- Use is_enabled instead of more code to read the register.

Eddie James (2):
clk: aspeed: Fix is_enabled for certain clocks
clk: aspeed: Prevent reset if clock is enabled

 drivers/clk/clk-aspeed.c | 28 +++++++++++++++++-----------
 1 file changed, 17 insertions(+), 11 deletions(-)
Signature · 1 line
1.8.3.1
#1 lore raw

PATCH v2 1/2 clk: aspeed: Fix is_enabled for certain clocks

Some of the Aspeed clocks are disabled by setting the relevant bit in the "clock stop control" register to one, while others are disabled by setting their bit to zero. The driver already uses a flag per gate to identify this behavior, but doesn't apply it in the clock is_enabled function.

Use the existing gate flag to correctly return whether or not a clock is enabled in the aspeed_clk_is_enabled function.

---
 drivers/clk/clk-aspeed.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/clk/clk-aspeed.c +2 -1
index 9f7f931..1687771 100644
@@ -259,11 +259,12 @@ static int aspeed_clk_is_enabled(struct clk_hw *hw)
 {
 	struct aspeed_clk_gate *gate = to_aspeed_clk_gate(hw);
 	u32 clk = BIT(gate->clock_idx);
+	u32 enval = (gate->flags & CLK_GATE_SET_TO_DISABLE) ? 0 : clk;
 	u32 reg;
 
 	regmap_read(gate->map, ASPEED_CLK_STOP_CTRL, &reg);
 
-	return (reg & clk) ? 0 : 1;
+	return ((reg & clk) == enval) ? 1 : 0;
 }
 
 static const struct clk_ops aspeed_clk_gate_ops = {
Signature · 1 line
1.8.3.1
maintainer Reviewed-by on v2 1/2
#2 lore raw

Re: [PATCH v2 1/2] clk: aspeed: Fix is_enabled for certain clocks

Quoted 10 lines · Eddie James

Some of the Aspeed clocks are disabled by setting the relevant bit in the "clock stop control" register to one, while others are disabled by setting their bit to zero. The driver already uses a flag per gate to identify this behavior, but doesn't apply it in the clock is_enabled function.

Use the existing gate flag to correctly return whether or not a clock is enabled in the aspeed_clk_is_enabled function.

Fixes 6671507f0fbd ("clk: aspeed: Handle inverse polarity of USB port

1 clock gate")

Cheers,

Joel

Quoted 25 lines
---
 drivers/clk/clk-aspeed.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/clk/clk-aspeed.c +2 -2
index 9f7f931..1687771 100644
@@ -259,11 +259,12 @@ static int aspeed_clk_is_enabled(struct clk_hw *hw)
 {
        struct aspeed_clk_gate *gate = to_aspeed_clk_gate(hw);
        u32 clk = BIT(gate->clock_idx);
+       u32 enval = (gate->flags & CLK_GATE_SET_TO_DISABLE) ? 0 : clk;
        u32 reg;
 
        regmap_read(gate->map, ASPEED_CLK_STOP_CTRL, &reg);
 
-       return (reg & clk) ? 0 : 1;
+       return ((reg & clk) == enval) ? 1 : 0;
 }
 
 static const struct clk_ops aspeed_clk_gate_ops = {
--

1.8.3.1

maintainer Applied
#3 lore raw

Re: [PATCH v2 1/2] clk: aspeed: Fix is_enabled for certain clocks

In reply to Eddie James

Quoting Eddie James (2018-03-08 12:57:19)

Quoted 7 lines

Some of the Aspeed clocks are disabled by setting the relevant bit in the "clock stop control" register to one, while others are disabled by setting their bit to zero. The driver already uses a flag per gate to identify this behavior, but doesn't apply it in the clock is_enabled function.
=

Quoted 6 lines

Use the existing gate flag to correctly return whether or not a clock is enabled in the aspeed_clk_is_enabled function.
=

---

Applied to clk-fixes

#4 lore raw

PATCH v2 2/2 clk: aspeed: Prevent reset if clock is enabled

In reply to Eddie James

According to the Aspeed specification, the reset and enable sequence should be done when the clock is stopped. The specification doesn't define behavior if the reset is done while the clock is enabled.

Quoted 1 line

From testing on the AST2500, the LPC Controller has problems if the

clock is reset while enabled.

Therefore, check whether the clock is enabled or not before performing the reset and enable sequence in the Aspeed clock driver.

---
 drivers/clk/clk-aspeed.c | 29 +++++++++++++++++------------
 1 file changed, 17 insertions(+), 12 deletions(-)
drivers/clk/clk-aspeed.c +17 -12
index 1687771..5eb50c3 100644
@@ -205,6 +205,18 @@ struct aspeed_clk_soc_data {
 	.calc_pll = aspeed_ast2400_calc_pll,
 };
 
+static int aspeed_clk_is_enabled(struct clk_hw *hw)
+{
+	struct aspeed_clk_gate *gate = to_aspeed_clk_gate(hw);
+	u32 clk = BIT(gate->clock_idx);
+	u32 enval = (gate->flags & CLK_GATE_SET_TO_DISABLE) ? 0 : clk;
+	u32 reg;
+
+	regmap_read(gate->map, ASPEED_CLK_STOP_CTRL, &reg);
+
+	return ((reg & clk) == enval) ? 1 : 0;
+}
+
 static int aspeed_clk_enable(struct clk_hw *hw)
 {
 	struct aspeed_clk_gate *gate = to_aspeed_clk_gate(hw);
@@ -215,6 +227,11 @@ static int aspeed_clk_enable(struct clk_hw *hw)
 
 	spin_lock_irqsave(gate->lock, flags);
 
+	if (aspeed_clk_is_enabled(hw)) {
+		spin_unlock_irqrestore(gate->lock, flags);
+		return 0;
+	}
+
 	if (gate->reset_idx >= 0) {
 		/* Put IP in reset */
 		regmap_update_bits(gate->map, ASPEED_RESET_CTRL, rst, rst);
@@ -255,18 +272,6 @@ static void aspeed_clk_disable(struct clk_hw *hw)
 	spin_unlock_irqrestore(gate->lock, flags);
 }
 
-static int aspeed_clk_is_enabled(struct clk_hw *hw)
-{
-	struct aspeed_clk_gate *gate = to_aspeed_clk_gate(hw);
-	u32 clk = BIT(gate->clock_idx);
-	u32 enval = (gate->flags & CLK_GATE_SET_TO_DISABLE) ? 0 : clk;
-	u32 reg;
-
-	regmap_read(gate->map, ASPEED_CLK_STOP_CTRL, &reg);
-
-	return ((reg & clk) == enval) ? 1 : 0;
-}
-
 static const struct clk_ops aspeed_clk_gate_ops = {
 	.enable = aspeed_clk_enable,
 	.disable = aspeed_clk_disable,
Signature · 1 line
1.8.3.1
#5 lore raw

Re: [PATCH v2 2/2] clk: aspeed: Prevent reset if clock is enabled

drivers/clk/clk-aspeed.c static int aspeed_clk_enable(struct clk_hw *hw) · line 234

Quoted 12 lines

static int aspeed_clk_enable(struct clk_hw *hw)

 {
        struct aspeed_clk_gate *gate = to_aspeed_clk_gate(hw);
drivers/clk/clk-aspeed.c +5 -0
@@ -215,6 +227,11 @@ static int aspeed_clk_enable(struct clk_hw *hw)
 
        spin_lock_irqsave(gate->lock, flags);
 
+       if (aspeed_clk_is_enabled(hw)) {
+               spin_unlock_irqrestore(gate->lock, flags);
+               return 0;
+       }
+

I think this piece of code can be run before spin_lock_irqsave(), so it is able to just return without spin_unlock_irqrestore()?

maintainer
#6 lore raw

Re: [PATCH v2 2/2] clk: aspeed: Prevent reset if clock is enabled

Hi Lei,

Quoted 10 lines · Lei YU
Quoted 10 lines

static int aspeed_clk_enable(struct clk_hw *hw)

 {
        struct aspeed_clk_gate *gate = to_aspeed_clk_gate(hw);
drivers/clk/clk-aspeed.c +0 -0
@@ -215,6 +227,11 @@ static int aspeed_clk_enable(struct clk_hw *hw)
 
        spin_lock_irqsave(gate->lock, flags);
 
Quoted 8 lines

drivers/clk/clk-aspeed.c static int aspeed_clk_enable(struct clk_hw *hw) · line 234

Quoted 5 lines
drivers/clk/clk-aspeed.c +5 -0
+       if (aspeed_clk_is_enabled(hw)) {
+               spin_unlock_irqrestore(gate->lock, flags);
+               return 0;
+       }
+

I think this piece of code can be run before spin_lock_irqsave(), so it is able to just return without spin_unlock_irqrestore()?

As far as I understand, we are not running under any kind of lock when calling into the clock framework.

Consider two clock consumer (any old driver) attempting an operation to change the state of a clock. The first consumer calls aspeed_clk_enable, and run the aspeed_clk_is_enabled function. This consumer is then preempted, and the second consume calls aspeed_clk_disable, taking the lock, they then disable the clock. They return out of aspeed_clk_disable and the first consumer can run again.

The first consumer has already checked that the clock was disabled, so they execute the 'return 0', without enabling it. However, their information is out of date, so they are now in a state where the clock hardware is disabled, but the clock framework and the consumer think it is enabled.

By doing the check under a lock, the second consumer won't be able to perform it's operation until after the first has finished (and as we disable IRQs, the first bit of code will run to completion without being preempted).

I might have missed something, so if you're confident we don't need to check the value under a lock then please let me know :)

Cheers,

Joel

#7 lore raw

Re: [PATCH v2 2/2] clk: aspeed: Prevent reset if clock is enabled

In reply to Eddie James

drivers/clk/clk-aspeed.c static int aspeed_clk_enable(struct clk_hw *hw) · line 234

Quoted 12 lines

static int aspeed_clk_enable(struct clk_hw *hw)

 {
        struct aspeed_clk_gate *gate = to_aspeed_clk_gate(hw);
drivers/clk/clk-aspeed.c +5 -0
@@ -215,6 +227,11 @@ static int aspeed_clk_enable(struct clk_hw *hw)
 
        spin_lock_irqsave(gate->lock, flags);
 
+       if (aspeed_clk_is_enabled(hw)) {
+               spin_unlock_irqrestore(gate->lock, flags);
+               return 0;
+       }
+

I think this piece of code can be run before spin_lock_irqsave(), so it is able to just return without spin_unlock_irqrestore()?

maintainer Reviewed-by on v2 2/2
#8 lore raw

Re: [PATCH v2 2/2] clk: aspeed: Prevent reset if clock is enabled

In reply to Eddie James
Quoted 4 lines · Eddie James

According to the Aspeed specification, the reset and enable sequence should be done when the clock is stopped. The specification doesn't define behavior if the reset is done while the clock is enabled.

Quoted 8 lines

From testing on the AST2500, the LPC Controller has problems if the clock is reset while enabled.

Therefore, check whether the clock is enabled or not before performing the reset and enable sequence in the Aspeed clock driver.

Fixes 15ed8ce5f84e ("clk: aspeed: Register gated clocks")

Cheers,

Joel

Quoted 61 lines
---
 drivers/clk/clk-aspeed.c | 29 +++++++++++++++++------------
 1 file changed, 17 insertions(+), 12 deletions(-)
drivers/clk/clk-aspeed.c +17 -13
index 1687771..5eb50c3 100644
@@ -205,6 +205,18 @@ struct aspeed_clk_soc_data {
        .calc_pll = aspeed_ast2400_calc_pll,
 };
 
+static int aspeed_clk_is_enabled(struct clk_hw *hw)
+{
+       struct aspeed_clk_gate *gate = to_aspeed_clk_gate(hw);
+       u32 clk = BIT(gate->clock_idx);
+       u32 enval = (gate->flags & CLK_GATE_SET_TO_DISABLE) ? 0 : clk;
+       u32 reg;
+
+       regmap_read(gate->map, ASPEED_CLK_STOP_CTRL, &reg);
+
+       return ((reg & clk) == enval) ? 1 : 0;
+}
+
 static int aspeed_clk_enable(struct clk_hw *hw)
 {
        struct aspeed_clk_gate *gate = to_aspeed_clk_gate(hw);
@@ -215,6 +227,11 @@ static int aspeed_clk_enable(struct clk_hw *hw)
 
        spin_lock_irqsave(gate->lock, flags);
 
+       if (aspeed_clk_is_enabled(hw)) {
+               spin_unlock_irqrestore(gate->lock, flags);
+               return 0;
+       }
+
        if (gate->reset_idx >= 0) {
                /* Put IP in reset */
                regmap_update_bits(gate->map, ASPEED_RESET_CTRL, rst, rst);
@@ -255,18 +272,6 @@ static void aspeed_clk_disable(struct clk_hw *hw)
        spin_unlock_irqrestore(gate->lock, flags);
 }
 
-static int aspeed_clk_is_enabled(struct clk_hw *hw)
-{
-       struct aspeed_clk_gate *gate = to_aspeed_clk_gate(hw);
-       u32 clk = BIT(gate->clock_idx);
-       u32 enval = (gate->flags & CLK_GATE_SET_TO_DISABLE) ? 0 : clk;
-       u32 reg;
-
-       regmap_read(gate->map, ASPEED_CLK_STOP_CTRL, &reg);
-
-       return ((reg & clk) == enval) ? 1 : 0;
-}
-
 static const struct clk_ops aspeed_clk_gate_ops = {
        .enable = aspeed_clk_enable,
        .disable = aspeed_clk_disable,
--

1.8.3.1

maintainer Applied
#9 lore raw

Re: [PATCH v2 2/2] clk: aspeed: Prevent reset if clock is enabled

In reply to Eddie James

Quoting Eddie James (2018-03-08 12:57:20)

Quoted 9 lines

According to the Aspeed specification, the reset and enable sequence should be done when the clock is stopped. The specification doesn't define behavior if the reset is done while the clock is enabled. =

From testing on the AST2500, the LPC Controller has problems if the clock is reset while enabled.
=

Quoted 7 lines

Therefore, check whether the clock is enabled or not before performing the reset and enable sequence in the Aspeed clock driver.
=

---

Applied to clk-fixes

maintainer
#10 lore raw
In reply to Eddie James

Quoting Eddie James (2018-03-08 12:57:18)

Quoted 1 line

Here are two fixes for the Aspeed clock driver. The first fixes the is_en=

abled

Quoted 3 lines

clock function to account for different clock gates getting disabled with either 0s or 1s. The second patch addresses some issue found with the LPC controller clock if it gets reset while the clock is enabled, which it is=

by

Quoted 2 lines

default. Thanks to Lei Yu for tracking down the LPC issue. =

Can you please add some "Fixes:" tags to these patches? Or just indicate the tags and I can pick them up. If it doesn't happen, I'll just apply these early next week.

#11 lore raw
Quoted 10 lines · Stephen Boyd

Quoting Eddie James (2018-03-08 12:57:18)

Quoted 6 lines

Here are two fixes for the Aspeed clock driver. The first fixes the is_enabled clock function to account for different clock gates getting disabled with either 0s or 1s. The second patch addresses some issue found with the LPC controller clock if it gets reset while the clock is enabled, which it is by default. Thanks to Lei Yu for tracking down the LPC issue.

Can you please add some "Fixes:" tags to these patches? Or just indicate the tags and I can pick them up. If it doesn't happen, I'll just apply these early next week.

Sure, sorry for the late response...

Patch 1 fixes 6671507f0fbd582b4003f837ab791d03ade8e0f4 Patch 2 fixes 15ed8ce5f84e2b3718690915dbee12ebd497dc0f

Thanks!
Eddie

Quoted 1 line