Skip to content

Commit 66d58ad

Browse files
committed
clk: sunxi-ng: h616: switch CPU to backup clock source when changing frequency
H616 CPU, as in the most of Allwinner chips, is clocked from PLL called PLL_CPUX. The PLL can temporary become unstable when adjusting divider and factors to adjust its output frequency. So in order to safely change CPU frequency we need first switch the CPU to another clock source, for instance to PLL_PERIPH0. We then need to gate/ungate PLL_CPUX and wait for the corresponding status bit to verify it's locked. This procedure is mandated by T5 User Manual, section 3.3.3.1 and it is implemented in vendor BSP in the same way. Tested extensively on dozens of custom T507 boards (Wiren Board 8 PLC). In our test this fix significantly improved the stability, especially at low core voltages. From my understanding, all Allwinner SoCs need to follow this kind of procedure, however it's only implemented in mainline for a handful of chips.
1 parent e46c8f3 commit 66d58ad

File tree

1 file changed

+27
-1
lines changed

1 file changed

+27
-1
lines changed

drivers/clk/sunxi-ng/ccu-sun50i-h616.c

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -729,6 +729,21 @@ static CLK_FIXED_FACTOR_HW(pll_video2_4x_clk, "pll-video2-4x",
729729
&pll_video2_clk.common.hw,
730730
1, 4, CLK_SET_RATE_PARENT);
731731

732+
733+
static struct ccu_pll_nb sun50i_h616_pll_cpu_nb = {
734+
.common = &pll_cpux_clk.common,
735+
.enable = BIT(29), /* gate/ungate LOCK_ENABLE, as per user manual */
736+
.lock = BIT(28), /* indicates that the PLL has been stable */
737+
};
738+
739+
static struct ccu_mux_nb sun50i_h616_cpu_nb = {
740+
.common = &cpux_clk.common,
741+
.cm = &cpux_clk.mux,
742+
.delay_us = 1,
743+
.bypass_index = 4, /* index of pll periph0 */
744+
};
745+
746+
732747
static struct ccu_common *sun50i_h616_ccu_clks[] = {
733748
&pll_cpux_clk.common,
734749
&pll_ddr0_clk.common,
@@ -1093,6 +1108,7 @@ static const u32 usb2_clk_regs[] = {
10931108
static int sun50i_h616_ccu_probe(struct platform_device *pdev)
10941109
{
10951110
void __iomem *reg;
1111+
int ret;
10961112
u32 val;
10971113
int i;
10981114

@@ -1147,7 +1163,17 @@ static int sun50i_h616_ccu_probe(struct platform_device *pdev)
11471163
val |= BIT(24);
11481164
writel(val, reg + SUN50I_H616_HDMI_CEC_CLK_REG);
11491165

1150-
return devm_sunxi_ccu_probe(&pdev->dev, reg, &sun50i_h616_ccu_desc);
1166+
ret = devm_sunxi_ccu_probe(&pdev->dev, reg, &sun50i_h616_ccu_desc);
1167+
if (ret)
1168+
return ret;
1169+
1170+
/* Gate then ungate PLL CPU after any rate changes */
1171+
ccu_pll_notifier_register(&sun50i_h616_pll_cpu_nb);
1172+
1173+
/* Reparent CPU during PLL CPU rate changes */
1174+
ccu_mux_notifier_register(pll_cpux_clk.common.hw.clk,
1175+
&sun50i_h616_cpu_nb);
1176+
return 0;
11511177
}
11521178

11531179
static const struct of_device_id sun50i_h616_ccu_ids[] = {

0 commit comments

Comments
 (0)