Clock driver need provide VOP related clocks support if we need display support, this patch add VOP clock support for rk3368.
This patch get form Rockchip downstream uboot repository. Link: https://github.com/rockchip-linux/u-boot Signed-off-by: WeiHao Li <cn.liwei...@gmail.com> --- .../include/asm/arch-rockchip/cru_rk3368.h | 22 ++ drivers/clk/rockchip/clk_rk3368.c | 240 +++++++++++++++++- 2 files changed, 257 insertions(+), 5 deletions(-) diff --git a/arch/arm/include/asm/arch-rockchip/cru_rk3368.h b/arch/arm/include/asm/arch-rockchip/cru_rk3368.h index 845113f13..4e2def4dd 100644 --- a/arch/arm/include/asm/arch-rockchip/cru_rk3368.h +++ b/arch/arm/include/asm/arch-rockchip/cru_rk3368.h @@ -102,6 +102,28 @@ enum { MCU_CLK_DIV_SHIFT = 0, MCU_CLK_DIV_MASK = GENMASK(4, 0), + /* CLKSEL19_CON */ + ACLK_VOP_PLL_SEL_SHIFT = 6, + ACLK_VOP_PLL_SEL_MASK = GENMASK(7, 6), + ACLK_VOP_PLL_SEL_CPLL = 0, + ACLK_VOP_PLL_SEL_GPLL = 1, + ACLK_VOP_DIV_SHIFT = 0, + ACLK_VOP_DIV_MASK = GENMASK(4, 0), + + /* CLKSEL20_CON */ + DCLK_VOP_PLL_SEL_SHIFT = 8, + DCLK_VOP_PLL_SEL_MASK = GENMASK(9, 8), + DCLK_VOP_PLL_SEL_CPLL = 0, + DCLK_VOP_PLL_SEL_GPLL = 1, + DCLK_VOP_PLL_SEL_NPLL = 2, + DCLK_VOP_DIV_SHIFT = 0, + DCLK_VOP_DIV_MASK = GENMASK(7, 0), + + /* CLKSEL21_CON */ + HCLK_VOP_DIV_SHIFT = 0, + HCLK_VOP_DIV_MASK = GENMASK(5, 0), + HCLK_VOP_DIV_WIDTH = 5, + /* CLKSEL_CON25 */ CLK_SARADC_DIV_CON_SHIFT = 8, CLK_SARADC_DIV_CON_MASK = GENMASK(15, 8), diff --git a/drivers/clk/rockchip/clk_rk3368.c b/drivers/clk/rockchip/clk_rk3368.c index f9936fb40..b260391ad 100644 --- a/drivers/clk/rockchip/clk_rk3368.c +++ b/drivers/clk/rockchip/clk_rk3368.c @@ -31,9 +31,27 @@ struct rk3368_clk_plat { #endif struct pll_div { + ulong rate; u32 nr; u32 nf; u32 no; + u32 nb; +}; + +#define RK3368_PLL_RATE(_rate, _nr, _nf, _no, _nb) \ +{ \ + .rate = _rate##U, \ + .nr = _nr, \ + .nf = _nf, \ + .no = _no, \ + .nb = _nb, \ +} + +static struct pll_div rk3368_pll_rates[] = { + /* _mhz, _nr, _nf, _no, _nb */ + RK3368_PLL_RATE(594000000, 1, 99, 4, 16), + RK3368_PLL_RATE(424200000, 5, 707, 8, 0), + RK3368_PLL_RATE(410000000, 3, 205, 4, 16), }; #define OSC_HZ (24 * 1000 * 1000) @@ -41,6 +59,7 @@ struct pll_div { #define APLL_B_HZ (816 * 1000 * 1000) #define GPLL_HZ (576 * 1000 * 1000) #define CPLL_HZ (400 * 1000 * 1000) +#define NPLL_HZ (594 * 1000 * 1000) #define DIV_TO_RATE(input_rate, div) ((input_rate) / ((div) + 1)) @@ -61,6 +80,105 @@ static const struct pll_div cpll_init_cfg = PLL_DIVISORS(CPLL_HZ, 1, 6); static ulong rk3368_clk_get_rate(struct clk *clk); +#define VCO_MAX_KHZ 2200000 +#define VCO_MIN_KHZ 440000 +#define FREF_MAX_KHZ 2200000 +#define FREF_MIN_KHZ 269 +#define PLL_LIMIT_FREQ 400000000 + +struct pll_div *rkclk_get_pll_config(ulong freq_hz) +{ + unsigned int rate_count = ARRAY_SIZE(rk3368_pll_rates); + int i; + + for (i = 0; i < rate_count; i++) { + if (freq_hz == rk3368_pll_rates[i].rate) + return &rk3368_pll_rates[i]; + } + return NULL; +} + +static int pll_para_config(ulong freq_hz, struct pll_div *div, uint *ext_div) +{ + struct pll_div *best_div = NULL; + uint ref_khz = OSC_HZ / 1000, nr, nf = 0; + uint fref_khz; + uint diff_khz, best_diff_khz; + const uint max_nr = 1 << 6, max_nf = 1 << 12, max_no = 1 << 4; + uint vco_khz; + uint no = 1; + uint freq_khz = freq_hz / 1000; + + if (!freq_hz) { + printf("%s: the frequency can not be 0 Hz\n", __func__); + return -EINVAL; + } + + no = DIV_ROUND_UP(VCO_MIN_KHZ, freq_khz); + if (ext_div) { + *ext_div = DIV_ROUND_UP(PLL_LIMIT_FREQ, freq_hz); + no = DIV_ROUND_UP(no, *ext_div); + } + + best_div = rkclk_get_pll_config(freq_hz * (*ext_div)); + if (best_div) { + div->nr = best_div->nr; + div->nf = best_div->nf; + div->no = best_div->no; + div->nb = best_div->nb; + return 0; + } + + /* only even divisors (and 1) are supported */ + if (no > 1) + no = DIV_ROUND_UP(no, 2) * 2; + + vco_khz = freq_khz * no; + if (ext_div) + vco_khz *= *ext_div; + + if (vco_khz < VCO_MIN_KHZ || vco_khz > VCO_MAX_KHZ || no > max_no) { + printf("%s: Cannot find out VCO for Frequency (%luHz).\n", + __func__, freq_hz); + return -1; + } + + div->no = no; + + best_diff_khz = vco_khz; + for (nr = 1; nr < max_nr && best_diff_khz; nr++) { + fref_khz = ref_khz / nr; + if (fref_khz < FREF_MIN_KHZ) + break; + if (fref_khz > FREF_MAX_KHZ) + continue; + + nf = vco_khz / fref_khz; + if (nf >= max_nf) + continue; + diff_khz = vco_khz - nf * fref_khz; + if (nf + 1 < max_nf && diff_khz > fref_khz / 2) { + nf++; + diff_khz = fref_khz - diff_khz; + } + + if (diff_khz >= best_diff_khz) + continue; + + best_diff_khz = diff_khz; + div->nr = nr; + div->nf = nf; + } + + if (best_diff_khz > 4 * 1000) { + printf("%s:Fail to match output freq %lu,best_is %u Hz\n", + __func__, freq_hz, best_diff_khz * 1000); + return -EINVAL; + } + + return 0; +} + /* Get pll rate by id */ static uint32_t rkclk_pll_get_rate(struct rk3368_cru *cru, enum rk3368_pll_id pll_id) @@ -88,7 +206,6 @@ static uint32_t rkclk_pll_get_rate(struct rk3368_cru *cru, } } -#if IS_ENABLED(CONFIG_XPL_BUILD) || IS_ENABLED(CONFIG_TPL_BUILD) static int rkclk_set_pll(struct rk3368_cru *cru, enum rk3368_pll_id pll_id, const struct pll_div *div) { @@ -128,7 +245,6 @@ static int rkclk_set_pll(struct rk3368_cru *cru, enum rk3368_pll_id pll_id, return 0; } -#endif #if IS_ENABLED(CONFIG_XPL_BUILD) || IS_ENABLED(CONFIG_TPL_BUILD) static void rkclk_init(struct rk3368_cru *cru) @@ -531,6 +647,104 @@ static ulong rk3368_bus_set_clk(struct rk3368_cru *cru, } return rk3368_bus_get_clk(cru, clk_id); } + +static ulong rk3368_vop_get_clk(struct rk3368_cru *cru, int clk_id) +{ + u32 div, con, parent, sel; + + switch (clk_id) { + case DCLK_VOP: + con = readl(&cru->clksel_con[20]); + div = con & DCLK_VOP_DIV_MASK; + parent = rkclk_pll_get_rate(cru, NPLL); + break; + case ACLK_VOP: + con = readl(&cru->clksel_con[19]); + div = con & ACLK_VOP_DIV_MASK; + sel = (con & (ACLK_VOP_PLL_SEL_MASK << + ACLK_VOP_PLL_SEL_SHIFT)) >> + ACLK_VOP_PLL_SEL_SHIFT; + if (sel == ACLK_VOP_PLL_SEL_CPLL) + parent = rkclk_pll_get_rate(cru, CPLL); + else if (ACLK_VOP_PLL_SEL_GPLL) + parent = rkclk_pll_get_rate(cru, GPLL); + else + parent = 480000000; + break; + case HCLK_VOP: + parent = rk3368_vop_get_clk(cru, ACLK_VOP); + con = readl(&cru->clksel_con[21]); + div = con & HCLK_VOP_DIV_MASK; + break; + default: + return -EINVAL; + } + + return DIV_TO_RATE(parent, div); +} + +static ulong rk3368_vop_set_clk(struct rk3368_cru *cru, int clk_id, uint hz) +{ + struct pll_div npll_config = {0}; + u32 lcdc_div; + int ret; + + switch (clk_id) { + case DCLK_VOP: + if (!(NPLL_HZ % hz)) { + rkclk_set_pll(cru, NPLL, rkclk_get_pll_config(NPLL_HZ)); + lcdc_div = NPLL_HZ / hz; + } else { + ret = pll_para_config(hz, &npll_config, &lcdc_div); + if (ret) + return ret; + + rkclk_set_pll(cru, NPLL, &npll_config); + } + /* vop dclk source clk: npll,dclk_div: 1 */ + rk_clrsetreg(&cru->clksel_con[20], + (DCLK_VOP_PLL_SEL_MASK << DCLK_VOP_PLL_SEL_SHIFT) | + (DCLK_VOP_DIV_MASK << DCLK_VOP_DIV_SHIFT), + (DCLK_VOP_PLL_SEL_NPLL << DCLK_VOP_PLL_SEL_SHIFT) | + (lcdc_div - 1) << DCLK_VOP_DIV_SHIFT); + break; + case ACLK_VOP: + if ((rkclk_pll_get_rate(cru, CPLL) % hz) == 0) { + lcdc_div = rkclk_pll_get_rate(cru, CPLL) / hz; + rk_clrsetreg(&cru->clksel_con[19], + (ACLK_VOP_PLL_SEL_MASK << + ACLK_VOP_PLL_SEL_SHIFT) | + (ACLK_VOP_DIV_MASK << + ACLK_VOP_DIV_SHIFT), + (ACLK_VOP_PLL_SEL_CPLL << + ACLK_VOP_PLL_SEL_SHIFT) | + (lcdc_div - 1) << + ACLK_VOP_DIV_SHIFT); + } else { + lcdc_div = rkclk_pll_get_rate(cru, GPLL) / hz; + rk_clrsetreg(&cru->clksel_con[19], + (ACLK_VOP_PLL_SEL_MASK << + ACLK_VOP_PLL_SEL_SHIFT) | + (ACLK_VOP_DIV_MASK << + ACLK_VOP_DIV_SHIFT), + (ACLK_VOP_PLL_SEL_GPLL << + ACLK_VOP_PLL_SEL_SHIFT) | + (lcdc_div - 1) << + ACLK_VOP_DIV_SHIFT); + } + break; + case HCLK_VOP: + lcdc_div = rk3368_vop_get_clk(cru, ACLK_VOP) / hz; + rk_clrsetreg(&cru->clksel_con[21], + HCLK_VOP_DIV_MASK, + (lcdc_div - 1) << HCLK_VOP_DIV_SHIFT); + break; + default: + return -EINVAL; + } + + return rk3368_vop_get_clk(cru, clk_id); +} #endif static ulong rk3368_clk_get_rate(struct clk *clk) @@ -540,11 +754,13 @@ static ulong rk3368_clk_get_rate(struct clk *clk) debug("%s: id %ld\n", __func__, clk->id); switch (clk->id) { + case PLL_APLLB: + case PLL_APLLL: + case PLL_DPLL: case PLL_CPLL: - rate = rkclk_pll_get_rate(priv->cru, CPLL); - break; case PLL_GPLL: - rate = rkclk_pll_get_rate(priv->cru, GPLL); + case PLL_NPLL: + rate = rkclk_pll_get_rate(priv->cru, clk->id - 1); break; case SCLK_SPI0 ... SCLK_SPI2: rate = rk3368_spi_get_clk(priv->cru, clk->id); @@ -571,6 +787,13 @@ static ulong rk3368_clk_get_rate(struct clk *clk) case SCLK_SARADC: rate = rk3368_saradc_get_clk(priv->cru); break; +#if !IS_ENABLED(CONFIG_XPL_BUILD) + case ACLK_VOP: + case DCLK_VOP: + case HCLK_VOP: + rate = rk3368_vop_get_clk(priv->cru, clk->id); + break; +#endif default: return -ENOENT; } @@ -617,6 +840,13 @@ static ulong rk3368_clk_set_rate(struct clk *clk, ulong rate) case SCLK_SARADC: ret = rk3368_saradc_set_clk(priv->cru, rate); break; +#if !defined(CONFIG_XPL_BUILD) + case ACLK_VOP: + case DCLK_VOP: + case HCLK_VOP: + ret = rk3368_vop_set_clk(priv->cru, clk->id, rate); + break; +#endif default: return -ENOENT; } -- 2.39.5