-/* $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 */
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);
struct rkclock_compat rkclock_compat[] = {
{
- "rockchip,rk3288-cru", NULL,
+ "rockchip,rk3288-cru", rk3288_init,
rk3288_enable, rk3288_get_frequency,
rk3288_set_frequency, rk3288_reset
},
* 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)
{
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)
{
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));
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;