Skip to contentSkip to conversation

clk: tegra: divider: Add missing check for enable-bit on rate's recalculation

PATCH Superseded Dmitry Osipenko 2 participants 8 messages Jul 23, 2019 – Oct 29, 2019

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

PATCH v2 1/2 clk: tegra: divider: Add missing check for enable-bit on rate's recalculation

Unset "enable" bit means that divider is in bypass mode, hence it doesn't have any effect in that case. Please note that there are no known bugs caused by the missing check.

---

Changelog:

v2: Changed the commit's description from 'Fix' to 'Add' in response to the
Stephen's Boyd question about the need to backport the patch into stable kernels. The backporting is not really needed.

 drivers/clk/tegra/clk-divider.c | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)
drivers/clk/tegra/clk-divider.c +7 -2
index e76731fb7d69..f33c19045386 100644
@@ -40,8 +40,13 @@ static unsigned long clk_frac_div_recalc_rate(struct clk_hw *hw,
 	int div, mul;
 	u64 rate = parent_rate;
 
-	reg = readl_relaxed(divider->reg) >> divider->shift;
-	div = reg & div_mask(divider);
+	reg = readl_relaxed(divider->reg);
+
+	if ((divider->flags & TEGRA_DIVIDER_UART) &&
+	    !(reg & PERIPH_CLK_UART_DIV_ENB))
+		return rate;
+
+	div = (reg >> divider->shift) & div_mask(divider);
 
 	mul = get_mul(divider);
 	div += mul;
Signature · 1 line
2.22.0
#1 lore raw

PATCH v2 2/2 clk: tegra: divider: Support enable-bit for Super clocks

All Super clocks have a divider that has the enable bit.

---

Changelog:

v2: Improved commit's message.

 drivers/clk/tegra/clk-divider.c | 12 ++++++++++++
 drivers/clk/tegra/clk-super.c   |  1 +
 drivers/clk/tegra/clk.h         |  4 ++++
 3 files changed, 17 insertions(+)
drivers/clk/tegra/clk-divider.c +12 -0
index f33c19045386..a980b9bddecd 100644
@@ -17,6 +17,7 @@
 #define get_max_div(d) div_mask(d)
 
 #define PERIPH_CLK_UART_DIV_ENB BIT(24)
+#define SUPER_CLK_DIV_ENB BIT(31)
 
 static int get_div(struct tegra_clk_frac_div *divider, unsigned long rate,
 		   unsigned long parent_rate)
@@ -46,6 +47,10 @@ static unsigned long clk_frac_div_recalc_rate(struct clk_hw *hw,
 	    !(reg & PERIPH_CLK_UART_DIV_ENB))
 		return rate;
 
+	if ((divider->flags & TEGRA_DIVIDER_SUPER) &&
+	    !(reg & SUPER_CLK_DIV_ENB))
+		return rate;
+
 	div = (reg >> divider->shift) & div_mask(divider);
 
 	mul = get_mul(divider);
@@ -96,6 +101,13 @@ static int clk_frac_div_set_rate(struct clk_hw *hw, unsigned long rate,
 	val &= ~(div_mask(divider) << divider->shift);
 	val |= div << divider->shift;
 
+	if (divider->flags & TEGRA_DIVIDER_SUPER) {
+		if (div)
+			val |= SUPER_CLK_DIV_ENB;
+		else
+			val &= ~SUPER_CLK_DIV_ENB;
+	}
+
 	if (divider->flags & TEGRA_DIVIDER_UART) {
 		if (div)
 			val |= PERIPH_CLK_UART_DIV_ENB;
drivers/clk/tegra/clk-super.c +1 -0
index 39ef31b46df5..4d8e36b04f03 100644
@@ -220,6 +220,7 @@ struct clk *tegra_clk_register_super_clk(const char *name,
 	super->frac_div.width = 8;
 	super->frac_div.frac_width = 1;
 	super->frac_div.lock = lock;
+	super->frac_div.flags = TEGRA_DIVIDER_SUPER;
 	super->div_ops = &tegra_clk_frac_div_ops;
 
 	/* Data in .init is copied by clk_register(), so stack variable OK */
drivers/clk/tegra/clk.h +4 -0
index 905bf1096558..a4fbf55930aa 100644
@@ -53,6 +53,9 @@ struct clk *tegra_clk_register_sync_source(const char *name,
  * TEGRA_DIVIDER_UART - UART module divider has additional enable bit which is
  *      set when divider value is not 0. This flags indicates that the divider
  *      is for UART module.
+ * TEGRA_DIVIDER_SUPER - Super clock divider has additional enable bit which
+ *      is set when divider value is not 0. This flags indicates that the
+ *      divider is for super clock.
  */
 struct tegra_clk_frac_div {
 	struct clk_hw	hw;
@@ -70,6 +73,7 @@ struct tegra_clk_frac_div {
 #define TEGRA_DIVIDER_FIXED BIT(1)
 #define TEGRA_DIVIDER_INT BIT(2)
 #define TEGRA_DIVIDER_UART BIT(3)
+#define TEGRA_DIVIDER_SUPER BIT(4)
 
 extern const struct clk_ops tegra_clk_frac_div_ops;
 struct clk *tegra_clk_register_divider(const char *name,
Signature · 1 line
2.22.0
#2 lore raw

Re: [PATCH v2 2/2] clk: tegra: divider: Support enable-bit for Super clocks

Quoted 2 lines · Dmitry Osipenko

All Super clocks have a divider that has the enable bit.

This is broken to begin with. The only clock of this type in upstream is SCLK I think. However, this clock is not a normal divider, it's a skipper, so the normal divider logic doesn't work for it. In practice this clock is only used when scaling SCLK, which is not (yet) done in the upstream kernel due to the complex DVFS relationship between sclk, hclk and pclk. A driver for it can be found here:
https://nv-tegra.nvidia.com/gitweb/?p=linux-4.9.git;a=blob;f=drivers/clk/tegra/clk-skipper.c;h=f5da4f6ca44fe194c87f66be70c708e9791db74d;hb=eb8dd21affa2be45fc29be8c082194ac4032393a
As you can see in that tree, we eventually splitted sclk into three clocks:

sclk_mux (controls SCLK_BURST_POLICY register)
sclk (controls SOURCE_SYS register which is like a normal peripheral clock but without the mux)
sclk_skipper (controls SCLK_DIVIDER)

Peter.

Quoted 86 lines

---

Changelog:

v2: Improved commit's message.

 drivers/clk/tegra/clk-divider.c | 12 ++++++++++++
 drivers/clk/tegra/clk-super.c   |  1 +
 drivers/clk/tegra/clk.h         |  4 ++++
 3 files changed, 17 insertions(+)
drivers/clk/tegra/clk-divider.c +12 -0
index f33c19045386..a980b9bddecd 100644
@@ -17,6 +17,7 @@
 #define get_max_div(d) div_mask(d)
 
 #define PERIPH_CLK_UART_DIV_ENB BIT(24)
+#define SUPER_CLK_DIV_ENB BIT(31)
 
 static int get_div(struct tegra_clk_frac_div *divider, unsigned long rate,
 		   unsigned long parent_rate)
@@ -46,6 +47,10 @@ static unsigned long clk_frac_div_recalc_rate(struct clk_hw *hw,
 	    !(reg & PERIPH_CLK_UART_DIV_ENB))
 		return rate;
 
+	if ((divider->flags & TEGRA_DIVIDER_SUPER) &&
+	    !(reg & SUPER_CLK_DIV_ENB))
+		return rate;
+
 	div = (reg >> divider->shift) & div_mask(divider);
 
 	mul = get_mul(divider);
@@ -96,6 +101,13 @@ static int clk_frac_div_set_rate(struct clk_hw *hw, unsigned long rate,
 	val &= ~(div_mask(divider) << divider->shift);
 	val |= div << divider->shift;
 
+	if (divider->flags & TEGRA_DIVIDER_SUPER) {
+		if (div)
+			val |= SUPER_CLK_DIV_ENB;
+		else
+			val &= ~SUPER_CLK_DIV_ENB;
+	}
+
 	if (divider->flags & TEGRA_DIVIDER_UART) {
 		if (div)
 			val |= PERIPH_CLK_UART_DIV_ENB;
drivers/clk/tegra/clk-super.c +1 -0
index 39ef31b46df5..4d8e36b04f03 100644
@@ -220,6 +220,7 @@ struct clk *tegra_clk_register_super_clk(const char *name,
 	super->frac_div.width = 8;
 	super->frac_div.frac_width = 1;
 	super->frac_div.lock = lock;
+	super->frac_div.flags = TEGRA_DIVIDER_SUPER;
 	super->div_ops = &tegra_clk_frac_div_ops;
 
 	/* Data in .init is copied by clk_register(), so stack variable OK */
drivers/clk/tegra/clk.h +4 -0
index 905bf1096558..a4fbf55930aa 100644
@@ -53,6 +53,9 @@ struct clk *tegra_clk_register_sync_source(const char *name,
  * TEGRA_DIVIDER_UART - UART module divider has additional enable bit which is
  *      set when divider value is not 0. This flags indicates that the divider
  *      is for UART module.
+ * TEGRA_DIVIDER_SUPER - Super clock divider has additional enable bit which
+ *      is set when divider value is not 0. This flags indicates that the
+ *      divider is for super clock.
  */
 struct tegra_clk_frac_div {
 	struct clk_hw	hw;
@@ -70,6 +73,7 @@ struct tegra_clk_frac_div {
 #define TEGRA_DIVIDER_FIXED BIT(1)
 #define TEGRA_DIVIDER_INT BIT(2)
 #define TEGRA_DIVIDER_UART BIT(3)
+#define TEGRA_DIVIDER_SUPER BIT(4)
 
 extern const struct clk_ops tegra_clk_frac_div_ops;
 struct clk *tegra_clk_register_divider(const char *name,
Signature · 1 line
2.22.0
#3 lore raw

Re: [PATCH v2 2/2] clk: tegra: divider: Support enable-bit for Super clocks

28.10.2019 17:41, Peter De Schrijver пишет:

Quoted 14 lines · Dmitry Osipenko
Quoted 2 lines

All Super clocks have a divider that has the enable bit.

This is broken to begin with. The only clock of this type in upstream is SCLK I think. However, this clock is not a normal divider, it's a skipper, so the normal divider logic doesn't work for it. In practice this clock is only used when scaling SCLK, which is not (yet) done in the upstream kernel due to the complex DVFS relationship between sclk, hclk and pclk. A driver for it can be found here:
https://nv-tegra.nvidia.com/gitweb/?p=linux-4.9.git;a=blob;f=drivers/clk/tegra/clk-skipper.c;h=f5da4f6ca44fe194c87f66be70c708e9791db74d;hb=eb8dd21affa2be45fc29be8c082194ac4032393a
As you can see in that tree, we eventually splitted sclk into three clocks:

Quoted 4 lines

sclk_mux (controls SCLK_BURST_POLICY register)
sclk (controls SOURCE_SYS register which is like a normal peripheral clock but without the mux)
sclk_skipper (controls SCLK_DIVIDER)

I'll drop this patch, thanks again for the clarification.

#4 lore raw
In reply to Dmitry Osipenko

23.07.2019 5:52, Dmitry Osipenko пишет:

Quoted 27 lines

Unset "enable" bit means that divider is in bypass mode, hence it doesn't have any effect in that case. Please note that there are no known bugs caused by the missing check.

---

Changelog:

v2: Changed the commit's description from 'Fix' to 'Add' in response to the
Stephen's Boyd question about the need to backport the patch into stable kernels. The backporting is not really needed.

 drivers/clk/tegra/clk-divider.c | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)
drivers/clk/tegra/clk-divider.c +0 -2
index e76731fb7d69..f33c19045386 100644
@@ -40,8 +40,13 @@ static unsigned long clk_frac_div_recalc_rate(struct clk_hw *hw,
 	int div, mul;
 	u64 rate = parent_rate;
 
-	reg = readl_relaxed(divider->reg) >> divider->shift;
-	div = reg & div_mask(divider);

drivers/clk/tegra/clk-divider.c static unsigned long clk_frac_div_recalc_rate(struct clk_hw *hw, · line 52

Quoted 10 lines
drivers/clk/tegra/clk-divider.c +7 -0
+	reg = readl_relaxed(divider->reg);
+
+	if ((divider->flags & TEGRA_DIVIDER_UART) &&
+	    !(reg & PERIPH_CLK_UART_DIV_ENB))
+		return rate;
+
+	div = (reg >> divider->shift) & div_mask(divider);
 
 	mul = get_mul(divider);
 	div += mul;

Hello Peter,

ACK?

Acked-by on v2 1/2
#5 lore raw
In reply to Dmitry Osipenko
Quoted 4 lines · Dmitry Osipenko

Unset "enable" bit means that divider is in bypass mode, hence it doesn't have any effect in that case. Please note that there are no known bugs caused by the missing check.

Technically this is not quite true, but for the purposes of CCF you can treat it that way. This bits defines if the value in the lower 16 bits of the divider register is used to configure the divider or if the contents of the UART DLM/DLL registers is used. So the divider isn't actually bypassed, it's just configured differently.
In practice this bit is only set when the divider is non-zero when doing set rate. So the extra test isn't strictly needed as long as the sw running before the kernel also ensures the bit is only set when the divider is non-zero.

Quoted 35 lines

---

Changelog:

v2: Changed the commit's description from 'Fix' to 'Add' in response to the
Stephen's Boyd question about the need to backport the patch into stable kernels. The backporting is not really needed.

 drivers/clk/tegra/clk-divider.c | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)
drivers/clk/tegra/clk-divider.c +7 -2
index e76731fb7d69..f33c19045386 100644
@@ -40,8 +40,13 @@ static unsigned long clk_frac_div_recalc_rate(struct clk_hw *hw,
 	int div, mul;
 	u64 rate = parent_rate;
 
-	reg = readl_relaxed(divider->reg) >> divider->shift;
-	div = reg & div_mask(divider);
+	reg = readl_relaxed(divider->reg);
+
+	if ((divider->flags & TEGRA_DIVIDER_UART) &&
+	    !(reg & PERIPH_CLK_UART_DIV_ENB))
+		return rate;
+
+	div = (reg >> divider->shift) & div_mask(divider);
 
 	mul = get_mul(divider);
 	div += mul;
Signature · 1 line
2.22.0
#6 lore raw

28.10.2019 17:27, Peter De Schrijver пишет:

Quoted 16 lines · Dmitry Osipenko
Quoted 4 lines

Unset "enable" bit means that divider is in bypass mode, hence it doesn't have any effect in that case. Please note that there are no known bugs caused by the missing check.

Technically this is not quite true, but for the purposes of CCF you can treat it that way. This bits defines if the value in the lower 16 bits of the divider register is used to configure the divider or if the contents of the UART DLM/DLL registers is used. So the divider isn't actually bypassed, it's just configured differently.
In practice this bit is only set when the divider is non-zero when doing set rate. So the extra test isn't strictly needed as long as the sw running before the kernel also ensures the bit is only set when the divider is non-zero.

Quoted 1 line

Thank you for the clarification. I hope that bootloader doesn't enable the divider because it looks like standard 8250 driver won't be ready for that. But serial-tegra driver seems should be good.

#7 lore raw

29.10.2019 03:14, Dmitry Osipenko пишет:

Quoted 17 lines

28.10.2019 17:27, Peter De Schrijver пишет:

Quoted 16 lines · Dmitry Osipenko
Quoted 4 lines

Unset "enable" bit means that divider is in bypass mode, hence it doesn't have any effect in that case. Please note that there are no known bugs caused by the missing check.

Technically this is not quite true, but for the purposes of CCF you can treat it that way. This bits defines if the value in the lower 16 bits of the divider register is used to configure the divider or if the contents of the UART DLM/DLL registers is used. So the divider isn't actually bypassed, it's just configured differently.
In practice this bit is only set when the divider is non-zero when doing set rate. So the extra test isn't strictly needed as long as the sw running before the kernel also ensures the bit is only set when the divider is non-zero.

Quoted 5 lines
Quoted 1 line

Thank you for the clarification. I hope that bootloader doesn't enable the divider because it looks like standard 8250 driver won't be ready for that. But serial-tegra driver seems should be good.

Actually, it should be good because I missed that UART clocks are per-initialized in the clk driver init table.

I'll update commit's message and send a new version of this patch.