Hello Mark,
below is the patch I'm using for a while now on Pinebook and A64+.
I thought, if it's good enough maybe it could be accepted to the main
tree.
I used it with device tree entries below, apm was able to set lowest
and highest clock, running stable for more than a week.
It's actually same as H3 implementation but - since A64 already had
its own set of functions I replicated it there.
I had one problem with the implementation - "delay(1)" for PLL just
hung on Pinebook (was fine on A64+). It didn't look like it was
looping, i.e. breaking the loop after some threshold did not resume
the execution as far as I could tell.
Increasing the delay value by trial and error solved the issue for me.
/ {
compatible = "allwinner,sun50i-a64";
};
&{/} {
cpu0_opp_table: opp_table0 {
compatible = "operating-points-v2";
opp-shared;
opp-648000000 {
opp-hz = /bits/ 64 <648000000>;
opp-microvolt = <1040000>;
clock-latency-ns = <244144>; /* 8 32k periods */
};
opp-792000000 {
opp-hz = /bits/ 64 <792000000>;
opp-microvolt = <1100000>;
clock-latency-ns = <244144>; /* 8 32k periods */
};
opp-816000000 {
opp-hz = /bits/ 64 <816000000>;
opp-microvolt = <1100000>;
clock-latency-ns = <244144>; /* 8 32k periods */
};
opp-912000000 {
opp-hz = /bits/ 64 <912000000>;
opp-microvolt = <1120000>;
clock-latency-ns = <244144>; /* 8 32k periods */
};
opp-960000000 {
opp-hz = /bits/ 64 <960000000>;
opp-microvolt = <1160000>;
clock-latency-ns = <244144>; /* 8 32k periods */
};
opp-1008000000 {
opp-hz = /bits/ 64 <1008000000>;
opp-microvolt = <1200000>;
clock-latency-ns = <244144>; /* 8 32k periods */
};
opp-1056000000 {
opp-hz = /bits/ 64 <1056000000>;
opp-microvolt = <1240000>;
clock-latency-ns = <244144>; /* 8 32k periods */
};
opp-1104000000 {
opp-hz = /bits/ 64 <1104000000>;
opp-microvolt = <1260000>;
clock-latency-ns = <244144>; /* 8 32k periods */
};
opp-1152000000 {
opp-hz = /bits/ 64 <1152000000>;
opp-microvolt = <1300000>;
clock-latency-ns = <244144>; /* 8 32k periods */
};
};
};
&{/cpus/cpu@0} {
operating-points-v2 = <&cpu0_opp_table>;
clocks = <&ccu 21>;
clock-names = "cpu";
cpu-supply = <®_dcdc2>;
};
&{/cpus/cpu@1} {
operating-points-v2 = <&cpu0_opp_table>;
};
&{/cpus/cpu@2} {
operating-points-v2 = <&cpu0_opp_table>;
};
&{/cpus/cpu@3} {
operating-points-v2 = <&cpu0_opp_table>;
};
--
Krystian
Index: sys/dev/fdt/sxiccmu.c
===================================================================
RCS file: /cvs/src/sys/dev/fdt/sxiccmu.c,v
retrieving revision 1.22
diff -u -p -r1.22 sxiccmu.c
--- sys/dev/fdt/sxiccmu.c 12 Feb 2019 21:34:11 -0000 1.22
+++ sys/dev/fdt/sxiccmu.c 28 Aug 2019 22:16:15 -0000
@@ -971,13 +971,55 @@ sxiccmu_a23_get_frequency(struct sxiccmu
return 0;
}
+#define A64_PLL_CPUX_CTRL_REG 0x0000
+#define A64_PLL_CPUX_LOCK (1 << 28)
+#define A64_PLL_CPUX_OUT_EXT_DIVP(x) (((x) >> 16) & 0x3)
+#define A64_PLL_CPUX_OUT_EXT_DIVP_MASK (0x3 << 16)
+#define A64_PLL_CPUX_FACTOR_N(x) (((x) >> 8) & 0x1f)
+#define A64_PLL_CPUX_FACTOR_N_MASK (0x1f << 8)
+#define A64_PLL_CPUX_FACTOR_N_SHIFT 8
+#define A64_PLL_CPUX_FACTOR_K(x) (((x) >> 4) & 0x3)
+#define A64_PLL_CPUX_FACTOR_K_MASK (0x3 << 4)
+#define A64_PLL_CPUX_FACTOR_K_SHIFT 4
+#define A64_PLL_CPUX_FACTOR_M(x) (((x) >> 0) & 0x3)
+#define A64_PLL_CPUX_FACTOR_M_MASK (0x3 << 0)
+#define A64_CPUX_AXI_CFG_REG 0x0050
+#define A64_CPUX_CLK_SRC_SEL (0x3 << 16)
+#define A64_CPUX_CLK_SRC_SEL_LOSC (0x0 << 16)
+#define A64_CPUX_CLK_SRC_SEL_OSC24M (0x1 << 16)
+#define A64_CPUX_CLK_SRC_SEL_PLL_CPUX (0x2 << 16)
+
uint32_t
sxiccmu_a64_get_frequency(struct sxiccmu_softc *sc, uint32_t idx)
{
uint32_t parent;
uint32_t reg, div;
+ uint32_t k, m, n, p;
switch (idx) {
+ case A64_CLK_PLL_CPUX:
+ reg = SXIREAD4(sc, A64_PLL_CPUX_CTRL_REG);
+ k = A64_PLL_CPUX_FACTOR_K(reg) + 1;
+ m = A64_PLL_CPUX_FACTOR_M(reg) + 1;
+ n = A64_PLL_CPUX_FACTOR_N(reg) + 1;
+ p = 1 << A64_PLL_CPUX_OUT_EXT_DIVP(reg);
+ return (24000000 * n * k) / (m * p);
+ case A64_CLK_CPUX:
+ reg = SXIREAD4(sc, A64_CPUX_AXI_CFG_REG);
+ switch (reg & A64_CPUX_CLK_SRC_SEL) {
+ case A64_CPUX_CLK_SRC_SEL_LOSC:
+ parent = A64_CLK_LOSC;
+ break;
+ case A64_CPUX_CLK_SRC_SEL_OSC24M:
+ parent = A64_CLK_HOSC;
+ break;
+ case A64_CPUX_CLK_SRC_SEL_PLL_CPUX:
+ parent = A64_CLK_PLL_CPUX;
+ break;
+ default:
+ return 0;
+ }
+ return sxiccmu_ccu_get_frequency(sc, &parent);
case A64_CLK_LOSC:
return clock_get_frequency(sc->sc_node, "losc");
case A64_CLK_HOSC:
@@ -1369,8 +1411,49 @@ sxiccmu_a64_set_frequency(struct sxiccmu
{
struct sxiccmu_clock clock;
uint32_t parent, parent_freq;
+ uint32_t reg;
+ int k, n;
+ int error;
switch (idx) {
+ case A64_CLK_PLL_CPUX:
+ k = 1; n = 32;
+ while (k <= 4 && (24000000 * n * k) < freq)
+ k++;
+ while (n >= 1 && (24000000 * n * k) > freq)
+ n--;
+
+ reg = SXIREAD4(sc, A64_PLL_CPUX_CTRL_REG);
+ reg &= ~A64_PLL_CPUX_OUT_EXT_DIVP_MASK;
+ reg &= ~A64_PLL_CPUX_FACTOR_N_MASK;
+ reg &= ~A64_PLL_CPUX_FACTOR_K_MASK;
+ reg &= ~A64_PLL_CPUX_FACTOR_M_MASK;
+ reg |= ((n - 1) << A64_PLL_CPUX_FACTOR_N_SHIFT);
+ reg |= ((k - 1) << A64_PLL_CPUX_FACTOR_K_SHIFT);
+ SXIWRITE4(sc, A64_PLL_CPUX_CTRL_REG, reg);
+
+ /* Wait for PLL to lock. */
+ while ((SXIREAD4(sc, A64_PLL_CPUX_CTRL_REG) &
+ A64_PLL_CPUX_LOCK) == 0) {
+ delay(200);
+ }
+
+ return 0;
+ case A64_CLK_CPUX:
+ /* Switch to 24 MHz clock. */
+ reg = SXIREAD4(sc, A64_CPUX_AXI_CFG_REG);
+ reg &= ~A64_CPUX_CLK_SRC_SEL;
+ reg |= A64_CPUX_CLK_SRC_SEL_OSC24M;
+ SXIWRITE4(sc, A64_CPUX_AXI_CFG_REG, reg);
+
+ error = sxiccmu_a64_set_frequency(sc, A64_CLK_PLL_CPUX, freq);
+
+ /* Switch back to PLL. */
+ reg = SXIREAD4(sc, A64_CPUX_AXI_CFG_REG);
+ reg &= ~A64_CPUX_CLK_SRC_SEL;
+ reg |= A64_CPUX_CLK_SRC_SEL_PLL_CPUX;
+ SXIWRITE4(sc, A64_CPUX_AXI_CFG_REG, reg);
+ return error;
case A64_CLK_MMC0:
case A64_CLK_MMC1:
case A64_CLK_MMC2:
Index: sys/dev/fdt/sxiccmu_clocks.h
===================================================================
RCS file: /cvs/src/sys/dev/fdt/sxiccmu_clocks.h,v
retrieving revision 1.25
diff -u -p -r1.25 sxiccmu_clocks.h
--- sys/dev/fdt/sxiccmu_clocks.h 19 Aug 2019 06:13:44 -0000 1.25
+++ sys/dev/fdt/sxiccmu_clocks.h 28 Aug 2019 22:16:20 -0000
@@ -140,9 +140,12 @@ struct sxiccmu_ccu_bit sun8i_a23_gates[]
/* A64 */
+#define A64_CLK_PLL_CPUX 1
+
#define A64_CLK_PLL_PERIPH0 11
#define A64_CLK_PLL_PERIPH0_2X 12
+#define A64_CLK_CPUX 21
#define A64_CLK_AXI 22
#define A64_CLK_APB 23
#define A64_CLK_AHB1 24