Skip to contentSkip to conversation

clk: sunxi-ng: h616: Reparent GPU clock during frequency changes

PATCH Superseded Philippe Simons 2 participants 3 messages Feb 9, 2025 – Feb 10, 2025

Subsystems (2)

v1 of 3 Superseded Latest is v3 →

lore
Share & tools
lore
#0 lore raw

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
index 190816c35..e88eefa24 100644
@@ -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
#1 lore raw

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_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 +0 -0
index 190816c35..e88eefa24 100644

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 -0
 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;

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;
 }
 
#2 lore raw
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_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 +0 -0
index 190816c35..e88eefa24 100644

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 26 lines
drivers/clk/sunxi-ng/ccu-sun50i-h616.c +13 -0
 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);
 
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,
Andre

Quoted 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