clk: tegra: divider: Add missing check for enable-bit on rate's recalculation
Subsystems (3)
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
@@ -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
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
@@ -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
@@ -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
@@ -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
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
Signed-off-by Dmitry Osipenko <[email protected]>---
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@@ -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@@ -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@@ -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
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(controlsSCLK_BURST_POLICYregister)
sclk (controlsSOURCE_SYSregister which is like a normal peripheral clock but without the mux)sclk_skipper(controlsSCLK_DIVIDER)
I'll drop this patch, thanks again for the clarification.
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.
Signed-off-by Dmitry Osipenko <[email protected]>---
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@@ -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?
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
Signed-off-by Dmitry Osipenko <[email protected]>---
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@@ -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
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.
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.