clk: sunxi-ng: h616: Reparent GPU clock during frequency changes
Subsystems (2)
PATCH 1/1 clk: sunxi-ng: h616: Reparent GPU clock during frequency changes
Re-parent the GPU clock during frequency changes of the PLL. Also it asks to disable and then re-enable the PLL lock bit, after the factor changes have been applied.
Add clock notifiers for the PLL and the GPU mux clock, using the existing notifier callbacks, and tell them to use mux 1 (the GPU_CLK1 source), and bit 29 (the LOCK_ENABLE) bit. The existing code already follows the correct algorithms.
--- drivers/clk/sunxi-ng/ccu-sun50i-h616.c | 33 +++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-)
drivers/clk/sunxi-ng/ccu-sun50i-h616.c +32 -1
@@ -328,10 +328,12 @@ static SUNXI_CCU_M_WITH_MUX_GATE(gpu0_clk, "gpu0", gpu0_parents, 0x670,24 ,1 ,/* mux */ BIT (31 ),/* gate */ CLK_SET_RATE_PARENT); + +#define SUN50I_H616_GPU_CLK1_REG 0x674 static SUNXI_CCU_M_WITH_GATE(gpu1_clk, "gpu1", "pll-periph0-2x", 0x674,0 ,2 ,/* M */ BIT (31 ),/* gate */ - 0); + CLK_IS_CRITICAL); static SUNXI_CCU_GATE(bus_gpu_clk, "bus-gpu", "psi-ahb1-ahb2", 0x67c, BIT(0), 0);
@@ -1120,6 +1122,19 @@ static struct ccu_pll_nb sun50i_h616_pll_cpu_nb = { .lock = BIT(28), }; +static struct ccu_mux_nb sun50i_h616_gpu_nb = { + .common = &gpu0_clk.common, + .cm = &gpu0_clk.mux, + .delay_us =1 ,/* manual doesn't really say */ + .bypass_index =1 ,/* GPU_CLK1 */ +}; + +static struct ccu_pll_nb sun50i_h616_pll_gpu_nb = { + .common = &pll_gpu_clk.common, + .enable =BIT (29 ),/* LOCK_ENABLE */ + .lock =BIT (28 ), +}; +static int sun50i_h616_ccu_probe (struct platform_device *pdev) {void __iomem *reg;
@@ -1170,6 +1185,15 @@ static int sun50i_h616_ccu_probe(struct platform_device *pdev) val |=BIT (0 );writel (val, reg +SUN50I_H616_PLL_AUDIO_REG ); +/* +* Set the input-divider for the gpu1 clock to 3. +* Also enable gpu1 clock. +*/ + val =readl (reg +SUN50I_H616_GPU_CLK1_REG ); + val |=BIT (31 ); + val |=BIT (1 ); +writel (val, reg +SUN50I_H616_GPU_CLK1_REG ); +/* * First clock parent (osc32K) is unusable for CEC. But since there * is no good way to force parent switch (both run with same frequency),
@@ -1190,6 +1214,13 @@ static int sun50i_h616_ccu_probe(struct platform_device *pdev)/* Re-lock the CPU PLL after any rate changes */ ccu_pll_notifier_register (&sun50i_h616_pll_cpu_nb); +/* Reparent GPU during GPU PLL rate changes */ +ccu_mux_notifier_register (pll_gpu_clk.common.hw.clk, + &sun50i_h616_gpu_nb); + +/* Re-lock the GPU PLL after any rate changes */ +ccu_pll_notifier_register (&sun50i_h616_pll_gpu_nb); +return 0 ; }
Signature · 1 line
2.47.1
On Sun, 9 Feb 2025 19:31:42 +0100 Philippe Simons <[email protected]> wrote:
Hi Philippe,
thanks for bringing this patch together!
You should mention here *why* you want this patch, the motivation seems to be missing, but would be crucial - and preventing a sure crash should make this a no-brainer.
Maybe start with: The H616 manual does not state that the GPU PLL supports dynamic frequency configuration, so we must take extra care when changing the frequency. Currently any attempt to do device DVFS on the GPU lead ...
Then mention that the manual describes the algorithm for changing the PLL frequency, which the CPU PLL notifier code already support, so we reuse that.
Quoted 19 lines
Re-parent the GPU clock during frequency changes of the PLL. Also it asks to disable and then re-enable the PLL lock bit, after the factor changes have been applied.
Add clock notifiers for the PLL and the GPU mux clock, using the existing notifier callbacks, and tell them to use mux 1 (the
GPU_CLK1source), and bit 29 (theLOCK_ENABLE) bit. The existing code already follows the correct algorithms.Signed-off-by Philippe Simons <[email protected]>--- drivers/clk/sunxi-ng/ccu-sun50i-h616.c | 33 +++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-)drivers/clk/sunxi-ng/ccu-sun50i-h616.c +0
-0
drivers/clk/sunxi-ng/ccu-sun50i-h616.c static SUNXI_CCU_M_WITH_MUX_GATE(gpu0_clk, "gpu0", gpu0_parents, 0x670, · line 336
Quoted 10 lines
drivers/clk/sunxi-ng/ccu-sun50i-h616.c +3
-1@@ -328,10 +328,12 @@ static SUNXI_CCU_M_WITH_MUX_GATE(gpu0_clk, "gpu0", gpu0_parents, 0x670,24 ,1 ,/* mux */ BIT (31 ),/* gate */ CLK_SET_RATE_PARENT); + +#define SUN50I_H616_GPU_CLK1_REG 0x674 static SUNXI_CCU_M_WITH_GATE(gpu1_clk, "gpu1", "pll-periph0-2x", 0x674,0 ,2 ,/* M */ BIT (31 ),/* gate */ - 0); + CLK_IS_CRITICAL);
The addition of CLK_IS_CRITICAL deserves a comment, something about that this clock is needed as a temporary fallback clock for the PLL frequency changes or so.
Quoted 23 lines
drivers/clk/sunxi-ng/ccu-sun50i-h616.c +13
-0static SUNXI_CCU_GATE(bus_gpu_clk, "bus-gpu", "psi-ahb1-ahb2", 0x67c, BIT(0), 0);@@ -1120,6 +1122,19 @@ static struct ccu_pll_nb sun50i_h616_pll_cpu_nb = { .lock = BIT(28), }; +static struct ccu_mux_nb sun50i_h616_gpu_nb = { + .common = &gpu0_clk.common, + .cm = &gpu0_clk.mux, + .delay_us =1 ,/* manual doesn't really say */ + .bypass_index =1 ,/* GPU_CLK1 */ +}; + +static struct ccu_pll_nb sun50i_h616_pll_gpu_nb = { + .common = &pll_gpu_clk.common, + .enable =BIT (29 ),/* LOCK_ENABLE */ + .lock =BIT (28 ), +}; +static int sun50i_h616_ccu_probe (struct platform_device *pdev) {void __iomem *reg;
drivers/clk/sunxi-ng/ccu-sun50i-h616.c static int sun50i_h616_ccu_probe(struct platform_device *pdev) · line 1193
Quoted 9 lines
drivers/clk/sunxi-ng/ccu-sun50i-h616.c +6
-0@@ -1170,6 +1185,15 @@ static int sun50i_h616_ccu_probe(struct platform_device *pdev) val |=BIT (0 );writel (val, reg +SUN50I_H616_PLL_AUDIO_REG ); +/* +* Set the input-divider for the gpu1 clock to 3. +* Also enable gpu1 clock. +*/ + val =readl (reg +SUN50I_H616_GPU_CLK1_REG ); + val |=BIT (31 );
Do we need this if the clock is marked as critical now?
drivers/clk/sunxi-ng/ccu-sun50i-h616.c static int sun50i_h616_ccu_probe(struct platform_device *pdev) · line 1194
Quoted 1 line
drivers/clk/sunxi-ng/ccu-sun50i-h616.c +1
-0+ val |=BIT (1 );
You probably want to clear the lowest 2 bits first, then set bit 1, otherwise you end up with either 2 or 3, depending on what bit 0 was before.
Cheers,
Andre
Quoted 19 lines
drivers/clk/sunxi-ng/ccu-sun50i-h616.c +9
-0+writel (val, reg +SUN50I_H616_GPU_CLK1_REG ); +/* * First clock parent (osc32K) is unusable for CEC. But since there * is no good way to force parent switch (both run with same frequency), @@ -1190,6 +1214,13 @@ static int sun50i_h616_ccu_probe(struct platform_device *pdev)/* Re-lock the CPU PLL after any rate changes */ ccu_pll_notifier_register (&sun50i_h616_pll_cpu_nb); +/* Reparent GPU during GPU PLL rate changes */ +ccu_mux_notifier_register (pll_gpu_clk.common.hw.clk, + &sun50i_h616_gpu_nb); + +/* Re-lock the GPU PLL after any rate changes */ +ccu_pll_notifier_register (&sun50i_h616_pll_gpu_nb); +return 0 ; }
Quoted 80 lines · Andre Przywara
On Sun, 9 Feb 2025 19:31:42 +0100 Philippe Simons <[email protected]> wrote:
Hi Philippe,
thanks for bringing this patch together!
You should mention here *why* you want this patch, the motivation seems to be missing, but would be crucial - and preventing a sure crash should make this a no-brainer.
Maybe start with: The H616 manual does not state that the GPU PLL supports dynamic frequency configuration, so we must take extra care when changing the frequency. Currently any attempt to do device DVFS on the GPU lead ...
Then mention that the manual describes the algorithm for changing the PLL frequency, which the CPU PLL notifier code already support, so we reuse that.
Quoted 19 lines
Re-parent the GPU clock during frequency changes of the PLL. Also it asks to disable and then re-enable the PLL lock bit, after the factor changes have been applied.
Add clock notifiers for the PLL and the GPU mux clock, using the existing notifier callbacks, and tell them to use mux 1 (the
GPU_CLK1source), and bit 29 (theLOCK_ENABLE) bit. The existing code already follows the correct algorithms.Signed-off-by Philippe Simons <[email protected]>--- drivers/clk/sunxi-ng/ccu-sun50i-h616.c | 33 +++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-)drivers/clk/sunxi-ng/ccu-sun50i-h616.c +0
-0drivers/clk/sunxi-ng/ccu-sun50i-h616.c static SUNXI_CCU_M_WITH_MUX_GATE(gpu0_clk, "gpu0", gpu0_parents, 0x670, · line 336
Quoted 10 lines
drivers/clk/sunxi-ng/ccu-sun50i-h616.c +3
-1@@ -328,10 +328,12 @@ static SUNXI_CCU_M_WITH_MUX_GATE(gpu0_clk, "gpu0", gpu0_parents, 0x670,24 ,1 ,/* mux */ BIT (31 ),/* gate */ CLK_SET_RATE_PARENT); + +#define SUN50I_H616_GPU_CLK1_REG 0x674 static SUNXI_CCU_M_WITH_GATE(gpu1_clk, "gpu1", "pll-periph0-2x", 0x674,0 ,2 ,/* M */ BIT (31 ),/* gate */ - 0); + CLK_IS_CRITICAL);The addition of
CLK_IS_CRITICALdeserves a comment, something about that this clock is needed as a temporary fallback clock for the PLL frequency changes or so.Quoted 26 lines
drivers/clk/sunxi-ng/ccu-sun50i-h616.c +13
-0static SUNXI_CCU_GATE(bus_gpu_clk, "bus-gpu", "psi-ahb1-ahb2", 0x67c, BIT(0), 0);@@ -1120,6 +1122,19 @@ static struct ccu_pll_nb sun50i_h616_pll_cpu_nb = { .lock = BIT(28), }; +static struct ccu_mux_nb sun50i_h616_gpu_nb = { + .common = &gpu0_clk.common, + .cm = &gpu0_clk.mux, + .delay_us =1 ,/* manual doesn't really say */ + .bypass_index =1 ,/* GPU_CLK1 */ +}; + +static struct ccu_pll_nb sun50i_h616_pll_gpu_nb = { + .common = &pll_gpu_clk.common, + .enable =BIT (29 ),/* LOCK_ENABLE */ + .lock =BIT (28 ), +}; +static int sun50i_h616_ccu_probe (struct platform_device *pdev) {void __iomem *reg;@@ -1170,6 +1185,15 @@ static int sun50i_h616_ccu_probe(struct platform_device *pdev) val |=BIT (0 );writel (val, reg +SUN50I_H616_PLL_AUDIO_REG );
Quoted 8 lines
drivers/clk/sunxi-ng/ccu-sun50i-h616.c static int sun50i_h616_ccu_probe(struct platform_device *pdev) · line 1193
Quoted 6 lines
drivers/clk/sunxi-ng/ccu-sun50i-h616.c +6
-0+/* +* Set the input-divider for the gpu1 clock to 3. +* Also enable gpu1 clock. +*/ + val =readl (reg +SUN50I_H616_GPU_CLK1_REG ); + val |=BIT (31 );Do we need this if the clock is marked as critical now?
I'll test that.
Quoted 6 lines
drivers/clk/sunxi-ng/ccu-sun50i-h616.c static int sun50i_h616_ccu_probe(struct platform_device *pdev) · line 1194
Quoted 2 lines
drivers/clk/sunxi-ng/ccu-sun50i-h616.c +1
-0+ val |=BIT (1 );You probably want to clear the lowest 2 bits first, then set bit 1, otherwise you end up with either 2 or 3, depending on what bit 0 was before.
Good catch, I'll address that in V2.
Quoted 13 lines
Cheers,
AndreQuoted 9 lines
drivers/clk/sunxi-ng/ccu-sun50i-h616.c +2
-0+writel (val, reg +SUN50I_H616_GPU_CLK1_REG ); +/* * First clock parent (osc32K) is unusable for CEC. But since there * is no good way to force parent switch (both run with same frequency), @@ -1190,6 +1214,13 @@ static int sun50i_h616_ccu_probe(struct platform_device *pdev)/* Re-lock the CPU PLL after any rate changes */ ccu_pll_notifier_register (&sun50i_h616_pll_cpu_nb);
Quoted 11 lines
drivers/clk/sunxi-ng/ccu-sun50i-h616.c static int sun50i_h616_ccu_probe(struct platform_device *pdev) · line 1226
Quoted 10 lines
drivers/clk/sunxi-ng/ccu-sun50i-h616.c +7
-0+/* Reparent GPU during GPU PLL rate changes */ +ccu_mux_notifier_register (pll_gpu_clk.common.hw.clk, + &sun50i_h616_gpu_nb); + +/* Re-lock the GPU PLL after any rate changes */ +ccu_pll_notifier_register (&sun50i_h616_pll_gpu_nb); +return 0 ; }
Thanks for the review,
Philippe