> 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 = <&reg_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
> 
> 

Reply via email to