Add support for the CPU clock on the RK3288. Set its frequency to 1.2 GHz
authorkettenis <kettenis@openbsd.org>
Fri, 29 Dec 2017 13:52:52 +0000 (13:52 +0000)
committerkettenis <kettenis@openbsd.org>
Fri, 29 Dec 2017 13:52:52 +0000 (13:52 +0000)
on the Tinker-RK3288 such that things run a bit faster.

sys/dev/fdt/rkclock.c

index ecc9453..d1bd82a 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: rkclock.c,v 1.17 2017/12/28 15:06:24 kettenis Exp $   */
+/*     $OpenBSD: rkclock.c,v 1.18 2017/12/29 13:52:52 kettenis Exp $   */
 /*
  * Copyright (c) 2017 Mark Kettenis <kettenis@openbsd.org>
  *
 #define RK3288_CRU_APLL_CON(i)         (0x0000 + (i) * 4)
 #define RK3288_CRU_CPLL_CON(i)         (0x0020 + (i) * 4)
 #define RK3288_CRU_GPLL_CON(i)         (0x0030 + (i) * 4)
+#define  RK3288_CRU_PLL_CLKR_MASK              (0x3f << 8)
+#define  RK3288_CRU_PLL_CLKR_SHIFT             8
+#define  RK3288_CRU_PLL_CLKOD_MASK             (0xf << 0)
+#define  RK3288_CRU_PLL_CLKOD_SHIFT            0
+#define  RK3288_CRU_PLL_CLKF_MASK              (0x1fff << 0)
+#define  RK3288_CRU_PLL_CLKF_SHIFT             0
+#define  RK3288_CRU_PLL_RESET                  (1 << 5)
+#define RK3288_CRU_MODE_CON            0x0050
+#define  RK3288_CRU_MODE_PLL_WORK_MODE_MASK    0x3
+#define  RK3288_CRU_MODE_PLL_WORK_MODE_SLOW    0x0
+#define  RK3288_CRU_MODE_PLL_WORK_MODE_NORMAL  0x1
 #define RK3288_CRU_CLKSEL_CON(i)       (0x0060 + (i) * 4)
 
 /* RK3399 registers */
@@ -105,6 +116,7 @@ struct cfdriver rkclock_cd = {
 
 struct rkclock_softc *rkclock_cpuspeed_sc;
 
+void   rk3288_init(struct rkclock_softc *);
 uint32_t rk3288_get_frequency(void *, uint32_t *);
 int    rk3288_set_frequency(void *, uint32_t *, uint32_t);
 void   rk3288_enable(void *, uint32_t *, int);
@@ -134,7 +146,7 @@ struct rkclock_compat {
 
 struct rkclock_compat rkclock_compat[] = {
        {
-               "rockchip,rk3288-cru", NULL,
+               "rockchip,rk3288-cru", rk3288_init,
                rk3288_enable, rk3288_get_frequency,
                rk3288_set_frequency, rk3288_reset
        },
@@ -213,6 +225,30 @@ rkclock_attach(struct device *parent, struct device *self, void *aux)
  * Rockchip RK3288
  */
 
+void
+rk3288_init(struct rkclock_softc *sc)
+{
+       int node;
+
+       /*
+        * Since the hardware comes up with a really conservative CPU
+        * clock frequency, and U-Boot doesn't set it to a more
+        * reasonable default, try to do so here.  These defaults were
+        * chosen assuming that the CPU voltage is at least 1.1 V.
+        * Only do this on the Tinker-RK3288 for now where this is
+        * likely to be true given the default voltages for the
+        * regulators on that board.
+        */
+       node = OF_finddevice("/");
+       if (OF_is_compatible(node, "rockchip,rk3288-tinker")) {
+               uint32_t idx;
+               
+               /* Run at 1.2 GHz. */
+               idx = RK3288_ARMCLK;
+               rk3288_set_frequency(sc, &idx, 1200000000);
+       }
+}
+
 uint32_t
 rk3288_get_pll(struct rkclock_softc *sc, bus_size_t base)
 {
@@ -220,13 +256,96 @@ rk3288_get_pll(struct rkclock_softc *sc, bus_size_t base)
        uint32_t reg;
 
        reg = HREAD4(sc, base);
-       clkod = (reg >> 0) & 0xf;
-       clkr = (reg >> 8) & 0x3f;
+       clkod = (reg & RK3288_CRU_PLL_CLKOD_MASK) >>
+           RK3288_CRU_PLL_CLKOD_SHIFT;
+       clkr = (reg & RK3288_CRU_PLL_CLKR_MASK) >>
+           RK3288_CRU_PLL_CLKR_SHIFT;
        reg = HREAD4(sc, base + 4);
-       clkf = (reg >> 0) & 0x1fff;
+       clkf = (reg & RK3288_CRU_PLL_CLKF_MASK) >>
+           RK3288_CRU_PLL_CLKF_SHIFT;
        return 24000000ULL * (clkf + 1) / (clkr + 1) / (clkod + 1);
 }
 
+int
+rk3288_set_pll(struct rkclock_softc *sc, bus_size_t base, uint32_t freq)
+{
+       int shift = 4 * (base / RK3288_CRU_CPLL_CON(0));
+       uint32_t no, nr, nf;
+
+       /*
+        * It is not clear whether all combinations of the clock
+        * dividers result in a stable clock.  Therefore this function
+        * only supports a limited set of PLL clock rates.  For now
+        * this set covers all the CPU frequencies supported by the
+        * Linux kernel.
+        */
+       switch (freq) {
+       case 1800000000:
+       case 1704000000:
+       case 1608000000:
+       case 1512000000:
+       case 1488000000:
+       case 1416000000:
+       case 1200000000:
+               nr = no = 1;
+               break;
+       case 1008000000:
+       case 816000000:
+       case 696000000:
+       case 600000000:
+               nr = 1; no = 2;
+               break;
+       case 408000000:
+       case 312000000:
+               nr = 1; no = 4;
+               break;
+       case 216000000:
+       case 126000000:
+               nr = 1; no = 8;
+               break;
+       default:
+               printf("%s: %d MHz\n", __func__, freq);
+               return -1;
+       }
+
+       /* Calculate feedback divider. */
+       nf = freq * nr * no / 24000000;
+
+       /*
+        * Select slow mode to guarantee a stable clock while we're
+        * adjusting the PLL.
+        */
+       HWRITE4(sc, RK3288_CRU_MODE_CON,
+           (RK3288_CRU_MODE_PLL_WORK_MODE_MASK << 16 |
+            RK3288_CRU_MODE_PLL_WORK_MODE_SLOW) << shift);
+
+       /* Assert reset. */
+       HWRITE4(sc, base + 0x000c,
+           RK3288_CRU_PLL_RESET << 16 | RK3288_CRU_PLL_RESET);
+
+       /* Set PLL rate. */
+       HWRITE4(sc, base + 0x0000,
+           RK3288_CRU_PLL_CLKR_MASK << 16 |
+           (nr - 1) << RK3288_CRU_PLL_CLKR_SHIFT |
+           RK3288_CRU_PLL_CLKOD_MASK << 16 |
+           (no - 1) << RK3288_CRU_PLL_CLKOD_SHIFT);
+       HWRITE4(sc, base + 0x0004,
+           RK3288_CRU_PLL_CLKF_MASK << 16 |
+           (nf - 1) << RK3288_CRU_PLL_CLKF_SHIFT);
+
+       /* Deassert reset and wait. */
+       HWRITE4(sc, base + 0x000c,
+           RK3288_CRU_PLL_RESET << 16);
+       delay((nr * 500 / 24) + 1);
+
+       /* Switch back to normal mode. */
+       HWRITE4(sc, RK3288_CRU_MODE_CON,
+           (RK3288_CRU_MODE_PLL_WORK_MODE_MASK << 16 |
+            RK3288_CRU_MODE_PLL_WORK_MODE_NORMAL) << shift);
+
+       return 0;
+}
+
 uint32_t
 rk3288_get_frequency(void *cookie, uint32_t *cells)
 {
@@ -245,7 +364,7 @@ rk3288_get_frequency(void *cookie, uint32_t *cells)
                reg = HREAD4(sc, RK3288_CRU_CLKSEL_CON(0));
                mux = (reg >> 15) & 0x1;
                div_con = (reg >> 8) & 0x1f;
-               idx = mux ? RK3288_PLL_APLL : RK3288_PLL_GPLL;
+               idx = (mux == 0) ? RK3288_PLL_APLL : RK3288_PLL_GPLL;
                return rk3288_get_frequency(sc, &idx) / (div_con + 1);
        case RK3288_CLK_SDMMC:
                reg = HREAD4(sc, RK3288_CRU_CLKSEL_CON(11));
@@ -338,7 +457,22 @@ rk3288_get_frequency(void *cookie, uint32_t *cells)
 int
 rk3288_set_frequency(void *cookie, uint32_t *cells, uint32_t freq)
 {
+       struct rkclock_softc *sc = cookie;
        uint32_t idx = cells[0];
+       int error;
+
+       switch (idx) {
+       case RK3288_PLL_APLL:
+               return rk3288_set_pll(sc, RK3288_CRU_APLL_CON(0), freq);
+       case RK3288_ARMCLK:
+               idx = RK3288_PLL_APLL;
+               error = rk3288_set_frequency(sc, &idx, freq);
+               if (error == 0) {
+                       HWRITE4(sc, RK3288_CRU_CLKSEL_CON(0),
+                           ((1 << 15) | (0x1f << 8)) << 16);
+               }
+               return error;
+       }
 
        printf("%s: 0x%08x\n", __func__, idx);
        return -1;