summaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
authorMark Kettenis <kettenis@cvs.openbsd.org>2018-12-31 13:19:25 +0000
committerMark Kettenis <kettenis@cvs.openbsd.org>2018-12-31 13:19:25 +0000
commitc678f30dfdfb712905a929741e3b6f1f9f68943c (patch)
tree9e59578c679e9914a2fed5e4e724a98f7b151ee1 /sys/dev
parentba4272df063a21cb8963bde2b64b89e7823ff178 (diff)
Revamp the way we handle clocks on the RK3328. Instead of having code for
each and every clock, use a data structure for each clock and generic code that handles muxing and clock division based on that data structure. This should reduce the amount of code and hopefully make the process of adding clocks less error prone as it is easy to check the data structures against the SoC documentation. This approach will be used for the other Rockchip SoCs in the future as well.
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/fdt/rkclock.c422
-rw-r--r--sys/dev/fdt/rkclock_clocks.h12
2 files changed, 256 insertions, 178 deletions
diff --git a/sys/dev/fdt/rkclock.c b/sys/dev/fdt/rkclock.c
index 339a37d7e3d..21439ae55ce 100644
--- a/sys/dev/fdt/rkclock.c
+++ b/sys/dev/fdt/rkclock.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: rkclock.c,v 1.31 2018/12/30 23:54:27 kettenis Exp $ */
+/* $OpenBSD: rkclock.c,v 1.32 2018/12/31 13:19:24 kettenis Exp $ */
/*
* Copyright (c) 2017, 2018 Mark Kettenis <kettenis@openbsd.org>
*
@@ -66,8 +66,6 @@
#define RK3328_CRU_CRU_MODE_SLOW 0x0
#define RK3328_CRU_CRU_MODE_NORMAL 0x1
#define RK3328_CRU_CLKSEL_CON(i) (0x0100 + (i) * 4)
-#define RK3328_CRU_BUS_ACLK_DIV_CON_MASK (0x3 << 8)
-#define RK3328_CRU_BUS_ACLK_DIV_CON_SHIFT 8
#define RK3328_CRU_CORE_CLK_PLL_SEL_MASK (0x3 << 6)
#define RK3328_CRU_CORE_CLK_PLL_SEL_SHIFT 6
#define RK3328_CRU_CLK_CORE_DIV_CON_MASK (0x1f << 0)
@@ -76,10 +74,6 @@
#define RK3328_CRU_ACLK_CORE_DIV_CON_SHIFT 4
#define RK3328_CRU_CLK_CORE_DBG_DIV_CON_MASK (0xf << 0)
#define RK3328_CRU_CLK_CORE_DBG_DIV_CON_SHIFT 0
-#define RK3328_CRU_UART_CLK_SEL_MASK (0x3 << 8)
-#define RK3328_CRU_UART_CLK_SEL_24MHZ (0x2 << 8)
-#define RK3328_CRU_PERIPH_ACLK_DIV_CON_MASK (0x3 << 8)
-#define RK3328_CRU_PERIPH_ACLK_DIV_CON_SHIFT 8
#define RK3328_CRU_CLKGATE_CON(i) (0x0200 + (i) * 4)
#define RK3328_CRU_SOFTRST_CON(i) (0x0300 + (i) * 4)
@@ -132,6 +126,17 @@
#include "rkclock_clocks.h"
+struct rkclock {
+ uint16_t idx;
+ uint16_t reg;
+ uint16_t sel_mask;
+ uint16_t div_mask;
+ uint16_t parents[4];
+};
+
+#define SEL(l, f) (((1 << (l - f + 1)) - 1) << f)
+#define DIV(l, f) SEL(l, f)
+
#define HREAD4(sc, reg) \
(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
#define HWRITE4(sc, reg, val) \
@@ -147,6 +152,7 @@ struct rkclock_softc {
bus_space_handle_t sc_ioh;
uint32_t sc_phandle;
+ struct rkclock *sc_clocks;
struct clock_device sc_cd;
struct reset_device sc_rd;
@@ -290,6 +296,120 @@ rkclock_attach(struct device *parent, struct device *self, void *aux)
clock_set_assigned(faa->fa_node);
}
+struct rkclock *
+rkclock_lookup(struct rkclock_softc *sc, uint32_t idx)
+{
+ struct rkclock *clk;
+
+ for (clk = sc->sc_clocks; clk->idx; clk++) {
+ if (clk->idx == idx)
+ return clk;
+ }
+
+ return NULL;
+}
+
+uint32_t
+rkclock_div_con(struct rkclock_softc *sc, int idx, uint32_t freq)
+{
+ uint32_t parent_freq, div;
+
+ parent_freq = sc->sc_cd.cd_get_frequency(sc, &idx);
+ div = (parent_freq + freq - 1) / freq;
+ return (div > 0 ? div - 1 : 0);
+}
+
+uint32_t
+rkclock_get_frequency(struct rkclock_softc *sc, uint32_t idx)
+{
+ struct rkclock *clk;
+ uint32_t reg, mux, div_con;
+ int shift;
+
+ clk = rkclock_lookup(sc, idx);
+ if (clk == NULL) {
+ printf("%s: 0x%08x\n", __func__, idx);
+ return 0;
+ }
+
+ reg = HREAD4(sc, clk->reg);
+ shift = ffs(clk->sel_mask) - 1;
+ if (shift == -1)
+ mux = 0;
+ else
+ mux = (reg & clk->sel_mask) >> shift;
+ shift = ffs(clk->div_mask) - 1;
+ if (shift == -1)
+ div_con = 0;
+ else
+ div_con = (reg & clk->div_mask) >> shift;
+
+ if (clk->parents[mux] == 0) {
+ printf("%s: parent 0x%08x\n", __func__, idx);
+ return 0;
+ }
+ idx = clk->parents[mux];
+ return sc->sc_cd.cd_get_frequency(sc, &idx) / (div_con + 1);
+}
+
+int
+rkclock_set_frequency(struct rkclock_softc *sc, uint32_t idx, uint32_t freq)
+{
+ struct rkclock *clk;
+ uint32_t reg, mux, div_con;
+ int shift;
+
+ clk = rkclock_lookup(sc, idx);
+ if (clk == NULL || clk->div_mask == 0) {
+ printf("%s: 0x%08x\n", __func__, idx);
+ return -1;
+ }
+
+ reg = HREAD4(sc, clk->reg);
+ shift = ffs(clk->sel_mask) - 1;
+ if (shift == -1)
+ mux = 0;
+ else
+ mux = (reg & clk->sel_mask) >> shift;
+
+ if (clk->parents[mux] == 0) {
+ printf("%s: parent 0x%08x\n", __func__, idx);
+ return 0;
+ }
+
+ div_con = rkclock_div_con(sc, clk->parents[mux], freq);
+ shift = ffs(clk->div_mask) - 1;
+ HWRITE4(sc, clk->reg, clk->div_mask << 16 | div_con << shift);
+ return 0;
+}
+
+int
+rkclock_set_parent(struct rkclock_softc *sc, uint32_t idx, uint32_t parent)
+{
+ struct rkclock *clk;
+ uint32_t mux;
+ int shift;
+
+ clk = rkclock_lookup(sc, idx);
+ if (clk == NULL || clk->sel_mask == 0) {
+ printf("%s: 0x%08x\n", __func__, idx);
+ return -1;
+ }
+
+ for (mux = 0; mux < nitems(clk->parents); mux++) {
+ if (clk->parents[mux] == parent)
+ break;
+ }
+ if (mux == nitems(clk->parents) || parent == 0) {
+ printf("%s: 0x%08x parent 0x%08x\n", __func__, idx, parent);
+ return -1;
+ }
+
+ shift = ffs(clk->sel_mask) - 1;
+ HWRITE4(sc, clk->reg, clk->sel_mask << 16 | mux << shift);
+ return 0;
+}
+
/*
* Rockchip RK3288
*/
@@ -599,6 +719,100 @@ rk3288_reset(void *cookie, uint32_t *cells, int on)
* Rockchip RK3328
*/
+struct rkclock rk3328_clocks[] = {
+ {
+ RK3328_CLK_SDMMC, RK3328_CRU_CLKSEL_CON(30),
+ SEL(9, 8), DIV(7, 0),
+ { RK3328_PLL_CPLL, RK3328_PLL_GPLL, RK3328_OSC,
+ RK3328_USB480M }
+ },
+ {
+ RK3328_CLK_SDIO, RK3328_CRU_CLKSEL_CON(31),
+ SEL(9, 8), DIV(7, 0),
+ { RK3328_PLL_CPLL, RK3328_PLL_GPLL, RK3328_OSC,
+ RK3328_USB480M }
+ },
+ {
+ RK3328_CLK_EMMC, RK3328_CRU_CLKSEL_CON(32),
+ SEL(9, 8), DIV(7, 0),
+ { RK3328_PLL_CPLL, RK3328_PLL_GPLL, RK3328_OSC,
+ RK3328_USB480M }
+ },
+ {
+ RK3328_CLK_UART0, RK3328_CRU_CLKSEL_CON(14),
+ SEL(9, 8), 0,
+ { 0, 0, RK3328_OSC, RK3328_OSC }
+ },
+ {
+ RK3328_CLK_UART1, RK3328_CRU_CLKSEL_CON(16),
+ SEL(9, 8), 0,
+ { 0, 0, RK3328_OSC, RK3328_OSC }
+ },
+ {
+ RK3328_CLK_UART2, RK3328_CRU_CLKSEL_CON(18),
+ SEL(9, 8), 0,
+ { 0, 0, RK3328_OSC, RK3328_OSC }
+ },
+ {
+ RK3328_CLK_I2C0, RK3328_CRU_CLKSEL_CON(34),
+ SEL(7, 7), DIV(6, 0),
+ { RK3328_PLL_CPLL, RK3328_PLL_GPLL }
+ },
+ {
+ RK3328_CLK_I2C1, RK3328_CRU_CLKSEL_CON(34),
+ SEL(15, 15), DIV(14, 8),
+ { RK3328_PLL_CPLL, RK3328_PLL_GPLL }
+ },
+ {
+ RK3328_CLK_I2C2, RK3328_CRU_CLKSEL_CON(35),
+ SEL(7, 7), DIV(6, 0),
+ { RK3328_PLL_CPLL, RK3328_PLL_GPLL }
+ },
+ {
+ RK3328_CLK_I2C3, RK3328_CRU_CLKSEL_CON(35),
+ SEL(15, 15), DIV(14, 8),
+ { RK3328_PLL_CPLL, RK3328_PLL_GPLL }
+ },
+ {
+ RK3328_CLK_PDM, RK3328_CRU_CLKSEL_CON(20),
+ SEL(15, 14), DIV(12, 8),
+ { RK3328_PLL_CPLL, RK3328_PLL_GPLL, RK3328_PLL_APLL }
+ },
+ {
+ RK3328_ACLK_BUS_PRE, RK3328_CRU_CLKSEL_CON(0),
+ SEL(14, 13), DIV(12, 8),
+ { RK3328_PLL_CPLL, RK3328_PLL_GPLL, RK3328_HDMIPHY }
+ },
+ {
+ RK3328_ACLK_PERI_PRE, RK3328_CRU_CLKSEL_CON(28),
+ SEL(7, 6), DIV(4, 0),
+ { RK3328_PLL_CPLL, RK3328_PLL_GPLL, RK3328_HDMIPHY }
+ },
+ {
+ RK3328_PCLK_BUS_PRE, RK3328_CRU_CLKSEL_CON(1),
+ 0, DIV(14, 12),
+ { RK3328_ACLK_BUS_PRE }
+ },
+ {
+ RK3328_HCLK_BUS_PRE, RK3328_CRU_CLKSEL_CON(1),
+ 0, DIV(9, 8),
+ { RK3328_ACLK_BUS_PRE }
+ },
+ {
+ RK3328_PCLK_PERI, RK3328_CRU_CLKSEL_CON(29),
+ 0, DIV(6, 4),
+ { RK3328_ACLK_PERI_PRE }
+ },
+ {
+ RK3328_HCLK_PERI, RK3328_CRU_CLKSEL_CON(29),
+ 0, DIV(1, 0),
+ { RK3328_ACLK_PERI_PRE }
+ },
+ {
+ /* Sentinel */
+ }
+};
+
void
rk3328_init(struct rkclock_softc *sc)
{
@@ -611,6 +825,8 @@ rk3328_init(struct rkclock_softc *sc)
HREAD4(sc, RK3328_CRU_CLKGATE_CON(i)));
}
}
+
+ sc->sc_clocks = rk3328_clocks;
}
uint32_t
@@ -823,87 +1039,10 @@ rk3328_set_pll(struct rkclock_softc *sc, bus_size_t base, uint32_t freq)
}
uint32_t
-rk3328_get_sdmmc(struct rkclock_softc *sc, bus_size_t base)
-{
- uint32_t reg, mux, div_con;
- uint32_t idx;
-
- reg = HREAD4(sc, base);
- mux = (reg >> 8) & 0x3;
- div_con = reg & 0xff;
- switch (mux) {
- case 0:
- idx = RK3328_PLL_CPLL;
- break;
- case 1:
- idx = RK3328_PLL_GPLL;
- break;
- case 2:
- return 24000000 / (div_con + 1);
-#ifdef notyet
- case 3:
- idx = RK3328_USB_480M;
- break;
-#endif
- default:
- return 0;
- }
- return rk3328_get_frequency(sc, &idx) / (div_con + 1);
-}
-
-uint32_t
-rk3328_get_i2c(struct rkclock_softc *sc, size_t base, int shift)
-{
- uint32_t reg, mux, div_con;
- uint32_t idx;
-
- reg = HREAD4(sc, base);
- mux = (reg >> (7 + shift)) & 0x1;
- div_con = (reg >> shift) & 0x7f;
- idx = (mux == 0) ? RK3328_PLL_CPLL : RK3328_PLL_GPLL;
- return rk3328_get_frequency(sc, &idx) / (div_con + 1);
-}
-
-uint32_t
-rk3328_div_con(struct rkclock_softc *sc, int idx, uint32_t freq)
-{
- uint32_t parent_freq, div;
-
- parent_freq = rk3328_get_frequency(sc, &idx);
- div = (parent_freq + freq - 1) / freq;
- return (div > 0 ? div - 1 : 0);
-}
-
-int
-rk3328_set_uart_parent(struct rkclock_softc *sc, bus_size_t clksel,
- uint32_t *pcells)
-{
- char name[32];
- int node;
-
- /* We only support switching to the 24 MHz clock. */
- node = OF_getnodebyphandle(pcells[0]);
- if (node == 0)
- return -1;
- name[0] = 0;
- OF_getprop(node, "clock-output-names", name, sizeof(name));
- name[sizeof(name) - 1] = 0;
- if (strcmp(name, "xin24m") != 0)
- return -1;
-
- HWRITE4(sc, clksel,
- RK3328_CRU_UART_CLK_SEL_MASK << 16 |
- RK3328_CRU_UART_CLK_SEL_24MHZ);
-
- return 0;
-}
-
-uint32_t
rk3328_get_frequency(void *cookie, uint32_t *cells)
{
struct rkclock_softc *sc = cookie;
uint32_t idx = cells[0];
- uint32_t reg, mux;
switch (idx) {
case RK3328_PLL_APLL:
@@ -923,42 +1062,11 @@ rk3328_get_frequency(void *cookie, uint32_t *cells)
break;
case RK3328_ARMCLK:
return rk3328_get_armclk(sc);
- case RK3328_CLK_SDMMC:
- return rk3328_get_sdmmc(sc, RK3328_CRU_CLKSEL_CON(30));
- case RK3328_CLK_SDIO:
- return rk3328_get_sdmmc(sc, RK3328_CRU_CLKSEL_CON(31));
- case RK3328_CLK_EMMC:
- return rk3328_get_sdmmc(sc, RK3328_CRU_CLKSEL_CON(32));
- case RK3328_CLK_UART0:
- reg = HREAD4(sc, RK3328_CRU_CLKSEL_CON(14));
- mux = (reg >> 8) & 0x3;
- if (mux == 2)
- return 24000000;
- break;
- case RK3328_CLK_UART1:
- reg = HREAD4(sc, RK3328_CRU_CLKSEL_CON(16));
- mux = (reg >> 8) & 0x3;
- if (mux == 2)
- return 24000000;
- break;
- case RK3328_CLK_UART2:
- reg = HREAD4(sc, RK3328_CRU_CLKSEL_CON(18));
- mux = (reg >> 8) & 0x3;
- if (mux == 2)
- return 24000000;
- break;
- case RK3328_CLK_I2C0:
- return rk3328_get_i2c(sc, RK3328_CRU_CLKSEL_CON(34), 0);
- case RK3328_CLK_I2C1:
- return rk3328_get_i2c(sc, RK3328_CRU_CLKSEL_CON(34), 8);
- case RK3328_CLK_I2C2:
- return rk3328_get_i2c(sc, RK3328_CRU_CLKSEL_CON(35), 0);
- case RK3328_CLK_I2C3:
- return rk3328_get_i2c(sc, RK3328_CRU_CLKSEL_CON(35), 8);
+ case RK3328_OSC:
+ return 24000000;
}
- printf("%s: 0x%08x\n", __func__, idx);
- return 0;
+ return rkclock_get_frequency(sc, idx);
}
int
@@ -966,7 +1074,6 @@ rk3328_set_frequency(void *cookie, uint32_t *cells, uint32_t freq)
{
struct rkclock_softc *sc = cookie;
uint32_t idx = cells[0];
- uint32_t reg, mux, div_con;
switch (idx) {
case RK3328_PLL_APLL:
@@ -987,53 +1094,9 @@ rk3328_set_frequency(void *cookie, uint32_t *cells, uint32_t freq)
if (freq == rk3328_get_frequency(sc, &idx))
return 0;
break;
- case RK3328_ACLK_BUS_PRE:
- reg = HREAD4(sc, RK3328_CRU_CLKSEL_CON(0));
- mux = (reg >> 13) & 0x3;
- switch (mux) {
- case 0:
- idx = RK3328_PLL_CPLL;
- break;
- case 1:
- idx = RK3328_PLL_GPLL;
- break;
- case 2:
- idx = RK3328_HDMIPHY;
- break;
- default:
- goto fail;
- }
- div_con = rk3328_div_con(sc, idx, freq);
- HWRITE4(sc, RK3328_CRU_CLKSEL_CON(0),
- RK3328_CRU_BUS_ACLK_DIV_CON_MASK << 16 |
- div_con << RK3328_CRU_BUS_ACLK_DIV_CON_SHIFT);
- return 0;
- case RK3328_ACLK_PERI_PRE:
- reg = HREAD4(sc, RK3328_CRU_CLKSEL_CON(28));
- mux = (reg >> 6) & 0x3;
- switch (mux) {
- case 0:
- idx = RK3328_PLL_CPLL;
- break;
- case 1:
- idx = RK3328_PLL_GPLL;
- break;
- case 2:
- idx = RK3328_HDMIPHY;
- break;
- default:
- goto fail;
- }
- div_con = rk3328_div_con(sc, idx, freq);
- HWRITE4(sc, RK3328_CRU_CLKSEL_CON(28),
- RK3328_CRU_PERIPH_ACLK_DIV_CON_MASK << 16 |
- div_con << RK3328_CRU_PERIPH_ACLK_DIV_CON_SHIFT);
- return 0;
}
-fail:
- printf("%s: 0x%08x\n", __func__, idx);
- return -1;
+ return rkclock_set_frequency(sc, idx, freq);
}
int
@@ -1041,22 +1104,27 @@ rk3328_set_parent(void *cookie, uint32_t *cells, uint32_t *pcells)
{
struct rkclock_softc *sc = cookie;
uint32_t idx = cells[0];
+ uint32_t parent;
- switch (idx) {
- case RK3328_CLK_UART0:
- return rk3328_set_uart_parent(sc,
- RK3328_CRU_CLKSEL_CON(14), pcells);
- case RK3328_CLK_UART1:
- return rk3328_set_uart_parent(sc,
- RK3328_CRU_CLKSEL_CON(16), pcells);
- case RK3328_CLK_UART2:
- return rk3328_set_uart_parent(sc,
- RK3328_CRU_CLKSEL_CON(18), pcells);
+ if (pcells[0] == sc->sc_phandle)
+ parent = pcells[1];
+ else {
+ char name[32];
+ int node;
+
+ node = OF_getnodebyphandle(pcells[0]);
+ if (node == 0)
+ return -1;
+ name[0] = 0;
+ OF_getprop(node, "clock-output-names", name, sizeof(name));
+ name[sizeof(name) - 1] = 0;
+ if (strcmp(name, "xin24m") != 0)
+ return -1;
+
+ parent = RK3328_OSC;
}
- printf("%s: 0x%08x 0x%08x 0x%08x\n", __func__, idx, pcells[0],
- pcells[0] == sc->sc_phandle ? pcells[1] : -1);
- return -1;
+ return rkclock_set_parent(sc, idx, parent);
}
void
diff --git a/sys/dev/fdt/rkclock_clocks.h b/sys/dev/fdt/rkclock_clocks.h
index 4f8c491f248..699a1df01e3 100644
--- a/sys/dev/fdt/rkclock_clocks.h
+++ b/sys/dev/fdt/rkclock_clocks.h
@@ -57,11 +57,21 @@
#define RK3328_CLK_I2C1 56
#define RK3328_CLK_I2C2 57
#define RK3328_CLK_I2C3 58
+#define RK3328_CLK_PDM 61
+
+#define RK3328_HDMIPHY 122
+#define RK3328_USB480M 123
#define RK3328_ACLK_BUS_PRE 136
#define RK3328_ACLK_PERI_PRE 137
-#define RK3328_HDMIPHY 122
+#define RK3328_PCLK_BUS_PRE 216
+#define RK3328_PCLK_PERI 230
+
+#define RK3328_HCLK_PERI 308
+#define RK3328_HCLK_BUS_PRE 328
+
+#define RK3328_OSC 1023
/*
* RK3399 clocks.