> Date: Thu, 29 Aug 2019 00:50:02 +0200
> From: Krystian Lewandowski <[email protected]>
>
> 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.
Are you sure a delay of 200us is needed? Linux seems to use 100us.
It is somewhat important to keep the delay as short as possible since
this is a busy loop and we don't want to waste cycles when scaling the
frequency up and down.
> / {
> 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
>
>