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

Reply via email to