rockchip: add audio-related clocks to the CRU driver

- Add I2S and CODEC clocks to CRU driver
- Add support for gate selection to frac clock
- Add setfreq support to mux clock

Reviewed by:	manu
Differential Revision:	https://reviews.freebsd.org/D27831
This commit is contained in:
Oleksandr Tymoshenko 2020-12-29 16:25:52 -08:00
parent 52981a1694
commit d03fd8ede2
6 changed files with 379 additions and 3 deletions

View File

@ -56,6 +56,11 @@ __FBSDID("$FreeBSD$");
/* 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 @@ __FBSDID("$FreeBSD$");
#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 @@ static struct rk_cru_gate rk3328_gates[] = {
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 @@ static struct rk_cru_gate rk3328_gates[] = {
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 @@ static struct rk_cru_gate rk3328_gates[] = {
/* 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 @@ static struct rk_clk_composite_def ref_usb3otg_src = {
.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 @@ static struct rk_clk rk3328_clks[] = {
.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

View File

@ -964,19 +964,19 @@ static struct rk_clk rk3399_clks[] = {
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),

View File

@ -49,21 +49,27 @@ __FBSDID("$FreeBSD$");
#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 @@ rk_clk_fract_init(struct clknode *clk, device_t dev)
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 @@ rk_clk_fract_register(struct clkdom *clkdom, struct rk_clk_fract_def *clkdef)
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);

View File

@ -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);

View File

@ -55,8 +55,17 @@ __FBSDID("$FreeBSD$");
#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 @@ static clknode_method_t rk_clk_mux_methods[] = {
/* 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 @@ rk_clk_mux_set_mux(struct clknode *clk, int idx)
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)
{

View File

@ -41,6 +41,7 @@ struct rk_clk_mux_def {
};
#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);