diff --git a/sys/arm64/rockchip/clk/rk3328_cru.c b/sys/arm64/rockchip/clk/rk3328_cru.c --- a/sys/arm64/rockchip/clk/rk3328_cru.c +++ b/sys/arm64/rockchip/clk/rk3328_cru.c @@ -56,6 +56,11 @@ /* GATES */ +#define SCLK_I2S0 41 +#define SCLK_I2S1 42 +#define SCLK_I2S2 43 +#define SCLK_I2S1_OUT 44 +#define SCLK_I2S2_OUT 45 #define SCLK_MAC2PHY_RXTX 83 #define SCLK_MAC2PHY_SRC 84 #define SCLK_MAC2PHY_REF 85 @@ -90,6 +95,10 @@ #define PCLK_USB3PHY_OTG 224 #define PCLK_USB3PHY_PIPE 225 #define PCLK_USB3_GRF 226 +#define PCLK_ACODECPHY 235 +#define HCLK_I2S0_8CH 311 +#define HCLK_I2S1_8CH 312 +#define HCLK_I2S2_2CH 313 #define HCLK_SDMMC 317 #define HCLK_SDIO 318 #define HCLK_EMMC 319 @@ -102,6 +111,11 @@ CRU_GATE(0, "gpll_core", "gpll", 0x200, 2) CRU_GATE(0, "npll_core", "npll", 0x200, 12) + /* CRU_CLKGATE_CON1 */ + CRU_GATE(SCLK_I2S0, "clk_i2s0", "clk_i2s0_mux", 0x204, 3) + CRU_GATE(SCLK_I2S1, "clk_i2s1", "clk_i2s1_mux", 0x204, 6) + CRU_GATE(SCLK_I2S1, "clk_i2s2", "clk_i2s2_mux", 0x204, 10) + /* CRU_CLKGATE_CON4 */ CRU_GATE(0, "gpll_peri", "gpll", 0x210, 0) CRU_GATE(0, "cpll_peri", "cpll", 0x210, 1) @@ -123,6 +137,9 @@ CRU_GATE(ACLK_PERI, "aclk_peri", "aclk_peri_pre", 0x228, 0) /* CRU_CLKGATE_CON15*/ + CRU_GATE(HCLK_I2S0_8CH, "hclk_i2s0_8ch", "hclk_bus_pre", 0x23C, 3) + CRU_GATE(HCLK_I2S1_8CH, "hclk_i2s1_8ch", "hclk_bus_pre", 0x23C, 4) + CRU_GATE(HCLK_I2S2_2CH, "hclk_i2s2_2ch", "hclk_bus_pre", 0x23C, 5) CRU_GATE(PCLK_I2C0, "pclk_i2c0", "pclk_bus", 0x23C, 10) /* CRU_CLKGATE_CON16 */ @@ -138,6 +155,7 @@ /* CRU_CLKGATE_CON17 */ CRU_GATE(PCLK_USB3_GRF, "pclk_usb3_grf", "pclk_phy_pre", 0x244, 2) + CRU_GATE(PCLK_ACODECPHY, "pclk_acodecphy", "pclk_phy_pre", 0x244, 5) /* CRU_CLKGATE_CON19 */ CRU_GATE(HCLK_SDMMC, "hclk_sdmmc", "hclk_peri", 0x24C, 0) @@ -1115,6 +1133,215 @@ .flags = RK_CLK_COMPOSITE_HAVE_GATE, }; +/* I2S0 */ +static const char *i2s0_div_parents[] = { "cpll", "gpll" }; +static struct rk_clk_composite_def i2s0_div = { + .clkdef = { + .id = 0, + .name = "clk_i2s0_div", + .parent_names = i2s0_div_parents, + .parent_cnt = nitems(i2s0_div_parents), + }, + /* CRU_CLKSEL_CON6 */ + .muxdiv_offset = 0x118, + + .mux_shift = 15, + .mux_width = 1, + + .div_shift = 0, + .div_width = 7, + + /* CRU_CLKGATE_CON1 */ + .gate_offset = 0x204, + .gate_shift = 1, + + .flags = RK_CLK_COMPOSITE_HAVE_GATE, +}; + +static const char *i2s0_frac_parents[] = { "clk_i2s0_div" }; +static struct rk_clk_fract_def i2s0_frac = { + .clkdef = { + .id = 0, + .name = "clk_i2s0_frac", + .parent_names = i2s0_frac_parents, + .parent_cnt = nitems(i2s0_frac_parents), + }, + /* CRU_CLKSEL_CON7 */ + .offset = 0x11c, + + /* CRU_CLKGATE_CON1 */ + .gate_offset = 0x204, + .gate_shift = 2, + + .flags = RK_CLK_FRACT_HAVE_GATE, +}; + +static const char *i2s0_mux_parents[] = { "clk_i2s0_div", "clk_i2s0_frac", "xin12m", "xin12m" }; +static struct rk_clk_mux_def i2s0_mux = { + .clkdef = { + .id = 0, + .name = "clk_i2s0_mux", + .parent_names = i2s0_mux_parents, + .parent_cnt = nitems(i2s0_mux_parents), + }, + .offset = 0x118, + + .shift = 8, + .width = 2, + + .mux_flags = RK_CLK_MUX_REPARENT, +}; + +/* I2S1 */ +static const char *i2s1_div_parents[] = { "cpll", "gpll" }; +static struct rk_clk_composite_def i2s1_div = { + .clkdef = { + .id = 0, + .name = "clk_i2s1_div", + .parent_names = i2s1_div_parents, + .parent_cnt = nitems(i2s1_div_parents), + }, + /* CRU_CLKSEL_CON8 */ + .muxdiv_offset = 0x120, + + .mux_shift = 15, + .mux_width = 1, + + .div_shift = 0, + .div_width = 7, + + /* CRU_CLKGATE_CON1 */ + .gate_offset = 0x204, + .gate_shift = 4, + + .flags = RK_CLK_COMPOSITE_HAVE_GATE, +}; + +static const char *i2s1_frac_parents[] = { "clk_i2s1_div" }; +static struct rk_clk_fract_def i2s1_frac = { + .clkdef = { + .id = 0, + .name = "clk_i2s1_frac", + .parent_names = i2s1_frac_parents, + .parent_cnt = nitems(i2s1_frac_parents), + }, + /* CRU_CLKSEL_CON9 */ + .offset = 0x124, + + /* CRU_CLKGATE_CON1 */ + .gate_offset = 0x204, + .gate_shift = 5, + + .flags = RK_CLK_FRACT_HAVE_GATE, +}; + +static const char *i2s1_mux_parents[] = { "clk_i2s1_div", "clk_i2s1_frac", "clkin_i2s1", "xin12m" }; +static struct rk_clk_mux_def i2s1_mux = { + .clkdef = { + .id = 0, + .name = "clk_i2s1_mux", + .parent_names = i2s1_mux_parents, + .parent_cnt = nitems(i2s1_mux_parents), + }, + .offset = 0x120, + + .shift = 8, + .width = 2, + .mux_flags = RK_CLK_MUX_REPARENT, +}; + +static struct clk_fixed_def clkin_i2s1 = { + .clkdef = { + .id = 0, + .name = "clkin_i2s1", + .parent_names = NULL, + .parent_cnt = 0 + }, + + .freq = 0, +}; + +/* I2S2 */ +static const char *i2s2_div_parents[] = { "cpll", "gpll" }; +static struct rk_clk_composite_def i2s2_div = { + .clkdef = { + .id = 0, + .name = "clk_i2s2_div", + .parent_names = i2s2_div_parents, + .parent_cnt = nitems(i2s2_div_parents), + }, + /* CRU_CLKSEL_CON10 */ + .muxdiv_offset = 0x128, + + .mux_shift = 15, + .mux_width = 1, + + .div_shift = 0, + .div_width = 7, + + /* CRU_CLKGATE_CON1 */ + .gate_offset = 0x204, + .gate_shift = 8, + + .flags = RK_CLK_COMPOSITE_HAVE_GATE, +}; + +static const char *i2s2_frac_parents[] = { "clk_i2s2_div" }; +static struct rk_clk_fract_def i2s2_frac = { + .clkdef = { + .id = 0, + .name = "clk_i2s2_frac", + .parent_names = i2s2_frac_parents, + .parent_cnt = nitems(i2s2_frac_parents), + }, + /* CRU_CLKSEL_CON11 */ + .offset = 0x12c, + + /* CRU_CLKGATE_CON1 */ + .gate_offset = 0x204, + .gate_shift = 9, + + .flags = RK_CLK_FRACT_HAVE_GATE, +}; + +static const char *i2s2_mux_parents[] = { "clk_i2s2_div", "clk_i2s2_frac", "clkin_i2s2", "xin12m" }; +static struct rk_clk_mux_def i2s2_mux = { + .clkdef = { + .id = 0, + .name = "clk_i2s2_mux", + .parent_names = i2s2_mux_parents, + .parent_cnt = nitems(i2s2_mux_parents), + }, + .offset = 0x128, + + .shift = 8, + .width = 2, + + .mux_flags = RK_CLK_MUX_REPARENT, +}; + +static struct clk_fixed_def clkin_i2s2 = { + .clkdef = { + .id = 0, + .name = "clkin_i2s2", + .parent_names = NULL, + .parent_cnt = 0 + }, + + .freq = 0, +}; + +static struct clk_fixed_def xin12m = { + .clkdef = { + .id = 0, + .name = "xin12m", + .parent_names = NULL, + .parent_cnt = 0 + }, + + .freq = 12000000, +}; + static const char *mac2io_src_parents[] = { "cpll", "gpll" }; static struct rk_clk_composite_def mac2io_src = { @@ -1417,6 +1644,54 @@ .type = RK_CLK_COMPOSITE, .clk.composite = &usb3otg_suspend }, + { + .type = RK_CLK_COMPOSITE, + .clk.composite = &i2s0_div + }, + { + .type = RK_CLK_FRACT, + .clk.fract = &i2s0_frac + }, + { + .type = RK_CLK_MUX, + .clk.mux = &i2s0_mux + }, + { + .type = RK_CLK_COMPOSITE, + .clk.composite = &i2s1_div + }, + { + .type = RK_CLK_FRACT, + .clk.fract = &i2s1_frac + }, + { + .type = RK_CLK_MUX, + .clk.mux = &i2s1_mux + }, + { + .type = RK_CLK_FIXED, + .clk.fixed = &clkin_i2s1 + }, + { + .type = RK_CLK_COMPOSITE, + .clk.composite = &i2s2_div + }, + { + .type = RK_CLK_FRACT, + .clk.fract = &i2s2_frac + }, + { + .type = RK_CLK_MUX, + .clk.mux = &i2s2_mux + }, + { + .type = RK_CLK_FIXED, + .clk.fixed = &clkin_i2s2 + }, + { + .type = RK_CLK_FIXED, + .clk.fixed = &xin12m + }, { .type = RK_CLK_COMPOSITE, .clk.composite = &mac2io_src diff --git a/sys/arm64/rockchip/clk/rk3399_cru.c b/sys/arm64/rockchip/clk/rk3399_cru.c --- a/sys/arm64/rockchip/clk/rk3399_cru.c +++ b/sys/arm64/rockchip/clk/rk3399_cru.c @@ -964,19 +964,19 @@ 27, 0, 10, 15, 1), /* CRU_CLKSEL_CON28 */ - MUX(0, "clk_i2s0_mux", i2s0_p, 0, + MUX(0, "clk_i2s0_mux", i2s0_p, RK_CLK_MUX_REPARENT, 28, 8, 2), COMP(0, "clk_i2s0_div_c", pll_src_cpll_gpll_p, 0, 28, 0, 7, 7, 1), /* CRU_CLKSEL_CON29 */ - MUX(0, "clk_i2s1_mux", i2s1_p, 0, + MUX(0, "clk_i2s1_mux", i2s1_p, RK_CLK_MUX_REPARENT, 29, 8, 2), COMP(0, "clk_i2s1_div_c", pll_src_cpll_gpll_p, 0, 29, 0, 7, 7, 1), /* CRU_CLKSEL_CON30 */ - MUX(0, "clk_i2s2_mux", i2s2_p, 0, + MUX(0, "clk_i2s2_mux", i2s2_p, RK_CLK_MUX_REPARENT, 30, 8, 2), COMP(0, "clk_i2s2_div_c", pll_src_cpll_gpll_p, 0, 30, 0, 7, 7, 1), diff --git a/sys/arm64/rockchip/clk/rk_clk_fract.h b/sys/arm64/rockchip/clk/rk_clk_fract.h --- a/sys/arm64/rockchip/clk/rk_clk_fract.h +++ b/sys/arm64/rockchip/clk/rk_clk_fract.h @@ -35,9 +35,13 @@ struct rk_clk_fract_def { struct clknode_init_def clkdef; uint32_t offset; + uint32_t gate_offset; + uint32_t gate_shift; uint32_t flags; }; +#define RK_CLK_FRACT_HAVE_GATE 0x0001 + int rk_clk_fract_register(struct clkdom *clkdom, struct rk_clk_fract_def *clkdef); diff --git a/sys/arm64/rockchip/clk/rk_clk_fract.c b/sys/arm64/rockchip/clk/rk_clk_fract.c --- a/sys/arm64/rockchip/clk/rk_clk_fract.c +++ b/sys/arm64/rockchip/clk/rk_clk_fract.c @@ -49,21 +49,27 @@ #define DEVICE_UNLOCK(_clk) \ CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk)) +#define RK_CLK_FRACT_MASK_SHIFT 16 + static int rk_clk_fract_init(struct clknode *clk, device_t dev); static int rk_clk_fract_recalc(struct clknode *clk, uint64_t *req); static int rk_clk_fract_set_freq(struct clknode *clknode, uint64_t fin, uint64_t *fout, int flag, int *stop); +static int rk_clk_fract_set_gate(struct clknode *clk, bool enable); struct rk_clk_fract_sc { uint32_t flags; uint32_t offset; uint32_t numerator; uint32_t denominator; + uint32_t gate_offset; + uint32_t gate_shift; }; static clknode_method_t rk_clk_fract_methods[] = { /* Device interface */ CLKNODEMETHOD(clknode_init, rk_clk_fract_init), + CLKNODEMETHOD(clknode_set_gate, rk_clk_fract_set_gate), CLKNODEMETHOD(clknode_recalc_freq, rk_clk_fract_recalc), CLKNODEMETHOD(clknode_set_freq, rk_clk_fract_set_freq), CLKNODEMETHOD_END @@ -149,6 +155,33 @@ return(0); } +static int +rk_clk_fract_set_gate(struct clknode *clk, bool enable) +{ + struct rk_clk_fract_sc *sc; + uint32_t val = 0; + + sc = clknode_get_softc(clk); + + if ((sc->flags & RK_CLK_FRACT_HAVE_GATE) == 0) + return (0); + + RD4(clk, sc->gate_offset, &val); + + val = 0; + if (!enable) + val |= 1 << sc->gate_shift; + val |= (1 << sc->gate_shift) << RK_CLK_FRACT_MASK_SHIFT; + DEVICE_LOCK(clk); + WR4(clk, sc->gate_offset, val); + DEVICE_UNLOCK(clk); + + return (0); +} + +static int +rk_clk_fract_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout, + int flags, int *stop); static int rk_clk_fract_recalc(struct clknode *clk, uint64_t *freq) { @@ -240,6 +273,8 @@ sc = clknode_get_softc(clk); sc->flags = clkdef->flags; sc->offset = clkdef->offset; + sc->gate_offset = clkdef->gate_offset; + sc->gate_shift = clkdef->gate_shift; clknode_register(clkdom, clk); return (0); diff --git a/sys/arm64/rockchip/clk/rk_clk_mux.h b/sys/arm64/rockchip/clk/rk_clk_mux.h --- a/sys/arm64/rockchip/clk/rk_clk_mux.h +++ b/sys/arm64/rockchip/clk/rk_clk_mux.h @@ -41,6 +41,7 @@ }; #define RK_CLK_MUX_MASK 0xFFFF0000 +#define RK_CLK_MUX_REPARENT (1 << 0) int rk_clk_mux_register(struct clkdom *clkdom, struct rk_clk_mux_def *clkdef); diff --git a/sys/arm64/rockchip/clk/rk_clk_mux.c b/sys/arm64/rockchip/clk/rk_clk_mux.c --- a/sys/arm64/rockchip/clk/rk_clk_mux.c +++ b/sys/arm64/rockchip/clk/rk_clk_mux.c @@ -55,8 +55,17 @@ #define DEVICE_UNLOCK(_clk) \ CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk)) +#if 0 +#define dprintf(format, arg...) \ + printf("%s:(%s)" format, __func__, clknode_get_name(clk), arg) +#else +#define dprintf(format, arg...) +#endif + static int rk_clk_mux_init(struct clknode *clk, device_t dev); static int rk_clk_mux_set_mux(struct clknode *clk, int idx); +static int rk_clk_mux_set_freq(struct clknode *clk, uint64_t fparent, + uint64_t *fout, int flags, int *stop); struct rk_clk_mux_sc { uint32_t offset; @@ -69,6 +78,7 @@ /* Device interface */ CLKNODEMETHOD(clknode_init, rk_clk_mux_init), CLKNODEMETHOD(clknode_set_mux, rk_clk_mux_set_mux), + CLKNODEMETHOD(clknode_set_freq, rk_clk_mux_set_freq), CLKNODEMETHOD_END }; DEFINE_CLASS_1(rk_clk_mux, rk_clk_mux_class, rk_clk_mux_methods, @@ -116,6 +126,57 @@ return(0); } +static int +rk_clk_mux_set_freq(struct clknode *clk, uint64_t fparent, uint64_t *fout, + int flags, int *stop) +{ + struct rk_clk_mux_sc *sc; + struct clknode *p_clk, *p_best_clk; + const char **p_names; + int p_idx, best_parent; + int rv; + + sc = clknode_get_softc(clk); + + if ((sc->mux_flags & RK_CLK_MUX_REPARENT) == 0) + return (0); + + dprintf("Finding best parent for target freq of %ju\n", *fout); + p_names = clknode_get_parent_names(clk); + for (p_idx = 0; p_idx != clknode_get_parents_num(clk); p_idx++) { + p_clk = clknode_find_by_name(p_names[p_idx]); + dprintf("Testing with parent %s (%d)\n", + clknode_get_name(p_clk), p_idx); + + rv = clknode_set_freq(p_clk, *fout, flags | CLK_SET_DRYRUN, 0); + dprintf("Testing with parent %s (%d) rv=%d\n", + clknode_get_name(p_clk), p_idx, rv); + if (rv == 0) { + best_parent = p_idx; + p_best_clk = p_clk; + *stop = 1; + } + } + + if (!*stop) + return (0); + + if ((flags & CLK_SET_DRYRUN) != 0) + return (0); + + p_idx = clknode_get_parent_idx(clk); + if (p_idx != best_parent) { + dprintf("Switching parent index from %d to %d\n", p_idx, + best_parent); + clknode_set_parent_by_idx(clk, best_parent); + } + + clknode_set_freq(p_best_clk, *fout, flags, 0); + clknode_get_freq(p_best_clk, fout); + + return (0); +} + int rk_clk_mux_register(struct clkdom *clkdom, struct rk_clk_mux_def *clkdef) {