From bf9e0ff8e1d3635fcd006a77a11d012d9c034aab Mon Sep 17 00:00:00 2001 From: kettenis Date: Fri, 29 Dec 2017 13:52:52 +0000 Subject: [PATCH] Add support for the CPU clock on the RK3288. Set its frequency to 1.2 GHz on the Tinker-RK3288 such that things run a bit faster. --- sys/dev/fdt/rkclock.c | 146 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 140 insertions(+), 6 deletions(-) diff --git a/sys/dev/fdt/rkclock.c b/sys/dev/fdt/rkclock.c index ecc9453535e..d1bd82aec9b 100644 --- a/sys/dev/fdt/rkclock.c +++ b/sys/dev/fdt/rkclock.c @@ -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 * @@ -32,6 +32,17 @@ #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; -- 2.20.1