Fix various RK3399 clocks and add support for getting the clock frequency
authorkettenis <kettenis@openbsd.org>
Wed, 1 Aug 2018 15:55:50 +0000 (15:55 +0000)
committerkettenis <kettenis@openbsd.org>
Wed, 1 Aug 2018 15:55:50 +0000 (15:55 +0000)
of the clocks that we can set.

Assign clock rates (and parents) based on the "assigned-clocks" device tree
property, but only on RK3399 for now as the code for the other Rockchip SoCs
isn't quite ready yet.

Last but not least, fixup a mistake on the firmware for the Theobroma
Systems RK3399-Q7 module such that the "big" cluster uses BPLL as intended.

sys/dev/fdt/rkclock.c

index c486830..28bf509 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: rkclock.c,v 1.26 2018/07/31 10:08:24 kettenis Exp $   */
+/*     $OpenBSD: rkclock.c,v 1.27 2018/08/01 15:55:50 kettenis Exp $   */
 /*
  * Copyright (c) 2017, 2018 Mark Kettenis <kettenis@openbsd.org>
  *
 #define  RK3399_CRU_ACLKM_CORE_DIV_CON_MASK    (0x1f << 8)
 #define  RK3399_CRU_ACLKM_CORE_DIV_CON_SHIFT   8
 #define  RK3399_CRU_CORE_PLL_SEL_MASK          (0x3 << 6)
+#define  RK3399_CRU_CORE_PLL_SEL_APLL          (0x0 << 6)
+#define  RK3399_CRU_CORE_PLL_SEL_BPLL          (0x1 << 6)
+#define  RK3399_CRU_CORE_PLL_SEL_DPLL          (0x2 << 6)
+#define  RK3399_CRU_CORE_PLL_SEL_GPLL          (0x3 << 6)
 #define  RK3399_CRU_CORE_PLL_SEL_SHIFT         6
 #define  RK3399_CRU_CLK_CORE_DIV_CON_MASK      (0x1f << 0)
 #define  RK3399_CRU_CLK_CORE_DIV_CON_SHIFT     0
@@ -163,6 +167,7 @@ void        rk3399_pmu_reset(void *, uint32_t *, int);
 
 struct rkclock_compat {
        const char *compat;
+       int     assign;
        void    (*init)(struct rkclock_softc *);
        void    (*enable)(void *, uint32_t *, int);
        uint32_t (*get_frequency)(void *, uint32_t *);
@@ -172,22 +177,22 @@ struct rkclock_compat {
 
 struct rkclock_compat rkclock_compat[] = {
        {
-               "rockchip,rk3288-cru", rk3288_init,
+               "rockchip,rk3288-cru", 0, rk3288_init,
                rk3288_enable, rk3288_get_frequency,
                rk3288_set_frequency, rk3288_reset
        },
        {
-               "rockchip,rk3328-cru", rk3328_init,
+               "rockchip,rk3328-cru", 0, rk3328_init,
                rk3328_enable, rk3328_get_frequency,
                rk3328_set_frequency, rk3328_reset
        },
        {
-               "rockchip,rk3399-cru", rk3399_init,
+               "rockchip,rk3399-cru", 1, rk3399_init,
                rk3399_enable, rk3399_get_frequency,
                rk3399_set_frequency, rk3399_reset,
        },
        {
-               "rockchip,rk3399-pmucru", rk3399_pmu_init,
+               "rockchip,rk3399-pmucru", 1, rk3399_pmu_init,
                rk3399_pmu_enable, rk3399_pmu_get_frequency,
                rk3399_pmu_set_frequency, rk3399_pmu_reset
        }
@@ -250,6 +255,9 @@ rkclock_attach(struct device *parent, struct device *self, void *aux)
        sc->sc_rd.rd_cookie = sc;
        sc->sc_rd.rd_reset = rkclock_compat[i].reset;
        reset_register(&sc->sc_rd);
+
+       if (rkclock_compat[i].assign)
+               clock_set_assigned(faa->fa_node);
 }
 
 /*
@@ -785,6 +793,16 @@ rk3399_init(struct rkclock_softc *sc)
 #endif
        }
 
+       /*
+        * The U-Boot shipped on the Theobroma Systems RK3399-Q7
+        * module is buggy and sets the parent of the clock for the
+        * "big" cluster to LPLL.  Undo that mistake here such that
+        * the clocks of both clusters are independent.
+        */
+       HWRITE4(sc, RK3399_CRU_CLKSEL_CON(2),
+           RK3399_CRU_CORE_PLL_SEL_MASK << 16 |
+           RK3399_CRU_CORE_PLL_SEL_BPLL);
+
        /* The code below assumes all clocks are enabled.  Check this!. */
        for (i = 0; i <= 34; i++) {
                if (HREAD4(sc, RK3399_CRU_CLKGATE_CON(i)) != 0x00000000) {
@@ -998,7 +1016,7 @@ rk3399_set_armclk(struct rkclock_softc *sc, bus_size_t clksel, uint32_t freq)
                    RK3399_CRU_ATCLK_CORE_DIV_CON_MASK << 16 |
                    div << RK3399_CRU_ATCLK_CORE_DIV_CON_SHIFT);
        }
-       
+
        return 0;
 }
 
@@ -1174,26 +1192,90 @@ rk3399_get_frequency(void *cookie, uint32_t *cells)
        case RK3399_ACLK_PERIPH:
                reg = HREAD4(sc, RK3399_CRU_CLKSEL_CON(14));
                mux = (reg >> 7) & 0x1;
-               idx = mux ? RK3399_PLL_CPLL : RK3399_PLL_GPLL;
+               idx = mux ? RK3399_PLL_GPLL : RK3399_PLL_CPLL;
                div_con = reg & 0x1f;
                return rk3399_get_frequency(sc, &idx) / (div_con + 1);
        case RK3399_ACLK_PERILP0:
                reg = HREAD4(sc, RK3399_CRU_CLKSEL_CON(23));
                mux = (reg >> 7) & 0x1;
-               idx = mux ? RK3399_PLL_CPLL : RK3399_PLL_GPLL;
+               idx = mux ? RK3399_PLL_GPLL : RK3399_PLL_CPLL;
+               div_con = reg & 0x1f;
+               return rk3399_get_frequency(sc, &idx) / (div_con + 1);
+       case RK3399_ACLK_VIO:
+               reg = HREAD4(sc, RK3399_CRU_CLKSEL_CON(42));
+               mux = (reg >> 6) & 0x3;
+               switch (mux) {
+               case 0:
+                       idx = RK3399_PLL_CPLL;
+                       break;
+               case 1:
+                       idx = RK3399_PLL_GPLL;
+                       break;
+#ifdef notyet
+               case 2:
+                       idx = RK3399_PLL_PPLL;
+                       break;
+#endif
+               default:
+                       return -1;
+               }
+               div_con = reg & 0x1f;
+               return rk3399_get_frequency(sc, &idx) / (div_con + 1);
+       case RK3399_ACLK_CCI:
+               reg = HREAD4(sc, RK3399_CRU_CLKSEL_CON(5));
+               mux = (reg >> 6) & 0x3;
+               switch (mux) {
+               case 0:
+                       idx = RK3399_PLL_CPLL;
+                       break;
+               case 1:
+                       idx = RK3399_PLL_GPLL;
+                       break;
+               case 2:
+                       idx = RK3399_PLL_NPLL;
+                       break;
+               case 3:
+                       idx = RK3399_PLL_VPLL;
+                       break;
+               }
                div_con = reg & 0x1f;
                return rk3399_get_frequency(sc, &idx) / (div_con + 1);
+       case RK3399_PCLK_PERIPH:
+               reg = HREAD4(sc, RK3399_CRU_CLKSEL_CON(14));
+               idx = RK3399_ACLK_PERIPH;
+               div_con = (reg >> 12) & 0x7;
+               return rk3399_get_frequency(sc, &idx) / (div_con + 1);
+       case RK3399_PCLK_PERILP0:
+               reg = HREAD4(sc, RK3399_CRU_CLKSEL_CON(23));
+               idx = RK3399_ACLK_PERILP0;
+               div_con = (reg >> 12) & 0x7;
+               return rk3399_get_frequency(sc, &idx) / (div_con + 1);
+       case RK3399_PCLK_PERILP1:
+               reg = HREAD4(sc, RK3399_CRU_CLKSEL_CON(25));
+               idx = RK3399_HCLK_PERILP1;
+               div_con = (reg >> 8) & 0x7;
+               return rk3399_get_frequency(sc, &idx) / (div_con + 1);
+       case RK3399_HCLK_PERIPH:
+               reg = HREAD4(sc, RK3399_CRU_CLKSEL_CON(14));
+               idx = RK3399_ACLK_PERIPH;
+               div_con = (reg >> 8) & 0x3;
+               return rk3399_get_frequency(sc, &idx) / (div_con + 1);
+       case RK3399_HCLK_PERILP0:
+               reg = HREAD4(sc, RK3399_CRU_CLKSEL_CON(23));
+               idx = RK3399_ACLK_PERILP0;
+               div_con = (reg >> 8) & 0x3;
+               return rk3399_get_frequency(sc, &idx) / (div_con + 1);
        case RK3399_HCLK_PERILP1:
                reg = HREAD4(sc, RK3399_CRU_CLKSEL_CON(25));
                mux = (reg >> 7) & 0x1;
-               idx = mux ? RK3399_PLL_CPLL : RK3399_PLL_GPLL;
+               idx = mux ? RK3399_PLL_GPLL : RK3399_PLL_CPLL;
                div_con = reg & 0x1f;
                return rk3399_get_frequency(sc, &idx) / (div_con + 1);
        case RK3399_HCLK_SDMMC:
                reg = HREAD4(sc, RK3399_CRU_CLKSEL_CON(13));
                mux = (reg >> 15) & 0x1;
                div_con = (reg >> 8) & 0x1f;
-               idx = mux ? RK3399_PLL_CPLL : RK3399_PLL_GPLL;
+               idx = mux ? RK3399_PLL_GPLL : RK3399_PLL_CPLL;
                return rk3399_get_frequency(sc, &idx) / (div_con + 1);
        default:
                break;
@@ -1232,14 +1314,14 @@ rk3399_set_frequency(void *cookie, uint32_t *cells, uint32_t freq)
        case RK3399_ACLK_PERIPH:
                reg = HREAD4(sc, RK3399_CRU_CLKSEL_CON(14));
                mux = (reg >> 7) & 0x1;
-               idx = mux ? RK3399_PLL_CPLL : RK3399_PLL_GPLL;
+               idx = mux ? RK3399_PLL_GPLL : RK3399_PLL_CPLL;
                HWRITE4(sc, RK3399_CRU_CLKSEL_CON(14),
                    0x1f << 16 | rk3399_div_con(sc, idx, freq));
                return 0;
        case RK3399_ACLK_PERILP0:
                reg = HREAD4(sc, RK3399_CRU_CLKSEL_CON(23));
                mux = (reg >> 7) & 0x1;
-               idx = mux ? RK3399_PLL_CPLL : RK3399_PLL_GPLL;
+               idx = mux ? RK3399_PLL_GPLL : RK3399_PLL_CPLL;
                HWRITE4(sc, RK3399_CRU_CLKSEL_CON(23),
                    0x1f << 16 | rk3399_div_con(sc, idx, freq));
                return 0;
@@ -1285,39 +1367,34 @@ rk3399_set_frequency(void *cookie, uint32_t *cells, uint32_t freq)
                    0x1f << 16 | rk3399_div_con(sc, idx, freq));
                return 0;
        case RK3399_PCLK_PERIPH:
-               reg = HREAD4(sc, RK3399_CRU_CLKSEL_CON(14));
                idx = RK3399_ACLK_PERIPH;
                HWRITE4(sc, RK3399_CRU_CLKSEL_CON(14),
-                   (0x7 << 28 | rk3399_div_con(sc, idx, freq)) << 12);
+                   (0x7 << 16 | rk3399_div_con(sc, idx, freq)) << 12);
                return 0;
        case RK3399_PCLK_PERILP0:
-               reg = HREAD4(sc, RK3399_CRU_CLKSEL_CON(23));
                idx = RK3399_ACLK_PERILP0;
                HWRITE4(sc, RK3399_CRU_CLKSEL_CON(23),
-                   (0x7 << 28 | rk3399_div_con(sc, idx, freq)) << 12);
+                   (0x7 << 16 | rk3399_div_con(sc, idx, freq)) << 12);
                return 0;
        case RK3399_PCLK_PERILP1:
-               reg = HREAD4(sc, RK3399_CRU_CLKSEL_CON(25));
                idx = RK3399_HCLK_PERILP1;
                HWRITE4(sc, RK3399_CRU_CLKSEL_CON(25),
-                   (0x3 << 24 | rk3399_div_con(sc, idx, freq)) << 8);
+                   (0x7 << 16 | rk3399_div_con(sc, idx, freq)) << 8);
                return 0;
        case RK3399_HCLK_PERIPH:
-               reg = HREAD4(sc, RK3399_CRU_CLKSEL_CON(14));
                idx = RK3399_ACLK_PERIPH;
                HWRITE4(sc, RK3399_CRU_CLKSEL_CON(14),
-                   (0x3 << 24 | rk3399_div_con(sc, idx, freq)) << 8);
+                   (0x3 << 16 | rk3399_div_con(sc, idx, freq)) << 8);
                return 0;
        case RK3399_HCLK_PERILP0:
-               reg = HREAD4(sc, RK3399_CRU_CLKSEL_CON(23));
                idx = RK3399_ACLK_PERILP0;
                HWRITE4(sc, RK3399_CRU_CLKSEL_CON(23),
-                   (0x3 << 24 | rk3399_div_con(sc, idx, freq)) << 8);
+                   (0x3 << 16 | rk3399_div_con(sc, idx, freq)) << 8);
                return 0;
        case RK3399_HCLK_PERILP1:
                reg = HREAD4(sc, RK3399_CRU_CLKSEL_CON(25));
                mux = (reg >> 7) & 0x1;
-               idx = mux ? RK3399_PLL_CPLL : RK3399_PLL_GPLL;
+               idx = mux ? RK3399_PLL_GPLL : RK3399_PLL_CPLL;
                HWRITE4(sc, RK3399_CRU_CLKSEL_CON(25),
                    0x1f << 16 | rk3399_div_con(sc, idx, freq));
                return 0;