diff --git a/arch/arm/include/asm/arch-rockchip/cru_rv1108.h b/arch/arm/include/asm/arch-rockchip/cru_rv1108.h index f7b8c13d0f..b57c07e55e 100644 --- a/arch/arm/include/asm/arch-rockchip/cru_rv1108.h +++ b/arch/arm/include/asm/arch-rockchip/cru_rv1108.h @@ -12,7 +12,11 @@ #define OSC_HZ (24 * 1000 * 1000) #define APLL_HZ (600 * 1000000) -#define GPLL_HZ (594 * 1000000) +#define GPLL_HZ (1188 * 1000000) +#define ACLK_PERI_HZ (148500000) +#define HCLK_PERI_HZ (148500000) +#define PCLK_PERI_HZ (74250000) +#define ACLK_BUS_HZ (148500000) struct rv1108_clk_priv { struct rv1108_cru *cru; @@ -110,6 +114,7 @@ enum { ACLK_BUS_PLL_SEL_DPLL = 2, ACLK_BUS_DIV_CON_SHIFT = 0, ACLK_BUS_DIV_CON_MASK = 0x1f << ACLK_BUS_DIV_CON_SHIFT, + ACLK_BUS_DIV_CON_WIDTH = 5, /* CLKSEL_CON3 */ PCLK_BUS_DIV_CON_SHIFT = 8, @@ -139,6 +144,7 @@ enum { HCLK_PERI_DIV_CON_MASK = 0x1f << HCLK_PERI_DIV_CON_SHIFT, ACLK_PERI_DIV_CON_SHIFT = 0, ACLK_PERI_DIV_CON_MASK = 0x1f, + PERI_DIV_CON_WIDTH = 5, /* CLKSEL24_CON */ MAC_PLL_SEL_SHIFT = 12, @@ -158,6 +164,38 @@ enum { SFC_CLK_DIV_SHIFT = 0, SFC_CLK_DIV_MASK = 0x3f << SFC_CLK_DIV_SHIFT, + /* CLKSEL28_CON */ + ACLK_VIO1_PLL_SEL_SHIFT = 14, + ACLK_VIO1_PLL_SEL_MASK = 3 << ACLK_VIO1_PLL_SEL_SHIFT, + VIO_PLL_SEL_DPLL = 0, + VIO_PLL_SEL_GPLL = 1, + ACLK_VIO1_CLK_DIV_SHIFT = 8, + ACLK_VIO1_CLK_DIV_MASK = 0x1f << ACLK_VIO1_CLK_DIV_SHIFT, + CLK_VIO_DIV_CON_WIDTH = 5, + ACLK_VIO0_PLL_SEL_SHIFT = 6, + ACLK_VIO0_PLL_SEL_MASK = 3 << ACLK_VIO0_PLL_SEL_SHIFT, + ACLK_VIO0_CLK_DIV_SHIFT = 0, + ACLK_VIO0_CLK_DIV_MASK = 0x1f << ACLK_VIO0_CLK_DIV_SHIFT, + + /* CLKSEL29_CON */ + PCLK_VIO_CLK_DIV_SHIFT = 8, + PCLK_VIO_CLK_DIV_MASK = 0x1f << PCLK_VIO_CLK_DIV_SHIFT, + HCLK_VIO_CLK_DIV_SHIFT = 0, + HCLK_VIO_CLK_DIV_MASK = 0x1f << HCLK_VIO_CLK_DIV_SHIFT, + + /* CLKSEL32_CON */ + DCLK_VOP_SEL_SHIFT = 7, + DCLK_VOP_SEL_MASK = 1 << DCLK_VOP_SEL_SHIFT, + DCLK_VOP_SEL_HDMI = 0, + DCLK_VOP_SEL_PLL = 1, + DCLK_VOP_PLL_SEL_SHIFT = 6, + DCLK_VOP_PLL_SEL_MASK = 1 << DCLK_VOP_PLL_SEL_SHIFT, + DCLK_VOP_PLL_SEL_GPLL = 0, + DCLK_VOP_PLL_SEL_DPLL = 1, + DCLK_VOP_CLK_DIV_SHIFT = 0, + DCLK_VOP_CLK_DIV_MASK = 0x3f << DCLK_VOP_CLK_DIV_SHIFT, + DCLK_VOP_DIV_CON_WIDTH = 6, + /* SOFTRST1_CON*/ DDRPHY_SRSTN_CLKDIV_REQ_SHIFT = 0, DDRPHY_SRSTN_CLKDIV_REQ = 1, diff --git a/drivers/clk/rockchip/clk_rv1108.c b/drivers/clk/rockchip/clk_rv1108.c index a6c5c47ad8..cbaf0b2913 100644 --- a/drivers/clk/rockchip/clk_rv1108.c +++ b/drivers/clk/rockchip/clk_rv1108.c @@ -37,6 +37,9 @@ enum { #hz "Hz cannot be hit with PLL "\ "divisors on line " __stringify(__LINE__)); +static const struct pll_div apll_init_cfg = PLL_DIVISORS(APLL_HZ, 1, 3, 1); +static const struct pll_div gpll_init_cfg = PLL_DIVISORS(GPLL_HZ, 2, 2, 1); + /* use integer mode */ static inline int rv1108_pll_id(enum rk_clk_id clk_id) { @@ -59,6 +62,58 @@ static inline int rv1108_pll_id(enum rk_clk_id clk_id) return id; } +static int rkclk_set_pll(struct rv1108_cru *cru, enum rk_clk_id clk_id, + const struct pll_div *div) +{ + int pll_id = rv1108_pll_id(clk_id); + struct rv1108_pll *pll = &cru->pll[pll_id]; + + /* All PLLs have same VCO and output frequency range restrictions. */ + uint vco_hz = OSC_HZ / 1000 * div->fbdiv / div->refdiv * 1000; + uint output_hz = vco_hz / div->postdiv1 / div->postdiv2; + + debug("PLL at %p: fb=%d, ref=%d, pst1=%d, pst2=%d, vco=%u Hz, output=%u Hz\n", + pll, div->fbdiv, div->refdiv, div->postdiv1, + div->postdiv2, vco_hz, output_hz); + assert(vco_hz >= VCO_MIN_HZ && vco_hz <= VCO_MAX_HZ && + output_hz >= OUTPUT_MIN_HZ && output_hz <= OUTPUT_MAX_HZ); + + /* + * When power on or changing PLL setting, + * we must force PLL into slow mode to ensure output stable clock. + */ + rk_clrsetreg(&pll->con3, WORK_MODE_MASK, + WORK_MODE_SLOW << WORK_MODE_SHIFT); + + /* use integer mode */ + rk_setreg(&pll->con3, 1 << DSMPD_SHIFT); + /* Power down */ + rk_setreg(&pll->con3, 1 << GLOBAL_POWER_DOWN_SHIFT); + + rk_clrsetreg(&pll->con0, FBDIV_MASK, div->fbdiv << FBDIV_SHIFT); + rk_clrsetreg(&pll->con1, POSTDIV1_MASK | POSTDIV2_MASK | REFDIV_MASK, + (div->postdiv1 << POSTDIV1_SHIFT | + div->postdiv2 << POSTDIV2_SHIFT | + div->refdiv << REFDIV_SHIFT)); + rk_clrsetreg(&pll->con2, FRACDIV_MASK, + (div->refdiv << REFDIV_SHIFT)); + + /* Power Up */ + rk_clrreg(&pll->con3, 1 << GLOBAL_POWER_DOWN_SHIFT); + + /* waiting for pll lock */ + while (readl(&pll->con2) & (1 << LOCK_STA_SHIFT)) + udelay(1); + + /* + * set PLL into normal mode. + */ + rk_clrsetreg(&pll->con3, WORK_MODE_MASK, + WORK_MODE_NORMAL << WORK_MODE_SHIFT); + + return 0; +} + static uint32_t rkclk_pll_get_rate(struct rv1108_cru *cru, enum rk_clk_id clk_id) { @@ -76,7 +131,7 @@ static uint32_t rkclk_pll_get_rate(struct rv1108_cru *cru, fbdiv = (con0 >> FBDIV_SHIFT) & FBDIV_MASK; postdiv1 = (con1 & POSTDIV1_MASK) >> POSTDIV1_SHIFT; postdiv2 = (con1 & POSTDIV2_MASK) >> POSTDIV2_SHIFT; - refdiv = (con1 & REFDIV_MASK) >> REFDIV_SHIFT; + refdiv = (con1 >> REFDIV_SHIFT) & REFDIV_MASK; freq = (24 * fbdiv / (refdiv * postdiv1 * postdiv2)) * 1000000; } else { freq = OSC_HZ; @@ -156,6 +211,205 @@ static ulong rv1108_saradc_set_clk(struct rv1108_cru *cru, uint hz) return rv1108_saradc_get_clk(cru); } +static ulong rv1108_aclk_vio1_get_clk(struct rv1108_cru *cru) +{ + u32 div, val; + + val = readl(&cru->clksel_con[28]); + div = bitfield_extract(val, ACLK_VIO1_CLK_DIV_SHIFT, + CLK_VIO_DIV_CON_WIDTH); + + return DIV_TO_RATE(GPLL_HZ, div); +} + +static ulong rv1108_aclk_vio1_set_clk(struct rv1108_cru *cru, uint hz) +{ + int src_clk_div; + + src_clk_div = DIV_ROUND_UP(GPLL_HZ, hz) - 1; + assert(src_clk_div < 32); + + rk_clrsetreg(&cru->clksel_con[28], + ACLK_VIO1_CLK_DIV_MASK | ACLK_VIO1_PLL_SEL_MASK, + (src_clk_div << ACLK_VIO1_CLK_DIV_SHIFT) | + (VIO_PLL_SEL_GPLL << ACLK_VIO1_PLL_SEL_SHIFT)); + + return rv1108_aclk_vio1_get_clk(cru); +} + +static ulong rv1108_aclk_vio0_get_clk(struct rv1108_cru *cru) +{ + u32 div, val; + + val = readl(&cru->clksel_con[28]); + div = bitfield_extract(val, ACLK_VIO0_CLK_DIV_SHIFT, + CLK_VIO_DIV_CON_WIDTH); + + return DIV_TO_RATE(GPLL_HZ, div); +} + +static ulong rv1108_aclk_vio0_set_clk(struct rv1108_cru *cru, uint hz) +{ + int src_clk_div; + + src_clk_div = DIV_ROUND_UP(GPLL_HZ, hz) - 1; + assert(src_clk_div < 32); + + rk_clrsetreg(&cru->clksel_con[28], + ACLK_VIO0_CLK_DIV_MASK | ACLK_VIO0_PLL_SEL_MASK, + (src_clk_div << ACLK_VIO0_CLK_DIV_SHIFT) | + (VIO_PLL_SEL_GPLL << ACLK_VIO0_PLL_SEL_SHIFT)); + + /*HCLK_VIO default div = 4*/ + rk_clrsetreg(&cru->clksel_con[29], + HCLK_VIO_CLK_DIV_MASK, + 3 << HCLK_VIO_CLK_DIV_SHIFT); + /*PCLK_VIO default div = 4*/ + rk_clrsetreg(&cru->clksel_con[29], + PCLK_VIO_CLK_DIV_MASK, + 3 << PCLK_VIO_CLK_DIV_SHIFT); + + return rv1108_aclk_vio0_get_clk(cru); +} + +static ulong rv1108_dclk_vop_get_clk(struct rv1108_cru *cru) +{ + u32 div, val; + + val = readl(&cru->clksel_con[32]); + div = bitfield_extract(val, DCLK_VOP_CLK_DIV_SHIFT, + DCLK_VOP_DIV_CON_WIDTH); + + return DIV_TO_RATE(GPLL_HZ, div); +} + +static ulong rv1108_dclk_vop_set_clk(struct rv1108_cru *cru, uint hz) +{ + int src_clk_div; + + src_clk_div = DIV_ROUND_UP(GPLL_HZ, hz) - 1; + assert(src_clk_div < 64); + + rk_clrsetreg(&cru->clksel_con[32], + DCLK_VOP_CLK_DIV_MASK | DCLK_VOP_PLL_SEL_MASK | + DCLK_VOP_SEL_SHIFT, + (src_clk_div << DCLK_VOP_CLK_DIV_SHIFT) | + (DCLK_VOP_PLL_SEL_GPLL << DCLK_VOP_PLL_SEL_SHIFT) | + (DCLK_VOP_SEL_PLL << DCLK_VOP_SEL_SHIFT)); + + return rv1108_dclk_vop_get_clk(cru); +} + +static ulong rv1108_aclk_bus_get_clk(struct rv1108_cru *cru) +{ + u32 div, val; + ulong parent_rate = rkclk_pll_get_rate(cru, CLK_GENERAL); + + val = readl(&cru->clksel_con[2]); + div = bitfield_extract(val, ACLK_BUS_DIV_CON_SHIFT, + ACLK_BUS_DIV_CON_WIDTH); + + return DIV_TO_RATE(parent_rate, div); +} + +static ulong rv1108_aclk_bus_set_clk(struct rv1108_cru *cru, uint hz) +{ + int src_clk_div; + ulong parent_rate = rkclk_pll_get_rate(cru, CLK_GENERAL); + + src_clk_div = DIV_ROUND_UP(parent_rate, hz) - 1; + assert(src_clk_div < 32); + + rk_clrsetreg(&cru->clksel_con[2], + ACLK_BUS_DIV_CON_MASK | ACLK_BUS_PLL_SEL_MASK, + (src_clk_div << ACLK_BUS_DIV_CON_SHIFT) | + (ACLK_BUS_PLL_SEL_GPLL << ACLK_BUS_PLL_SEL_SHIFT)); + + return rv1108_aclk_bus_get_clk(cru); +} + +static ulong rv1108_aclk_peri_get_clk(struct rv1108_cru *cru) +{ + u32 div, val; + ulong parent_rate = rkclk_pll_get_rate(cru, CLK_GENERAL); + + val = readl(&cru->clksel_con[23]); + div = bitfield_extract(val, ACLK_PERI_DIV_CON_SHIFT, + PERI_DIV_CON_WIDTH); + + return DIV_TO_RATE(parent_rate, div); +} + +static ulong rv1108_hclk_peri_get_clk(struct rv1108_cru *cru) +{ + u32 div, val; + ulong parent_rate = rkclk_pll_get_rate(cru, CLK_GENERAL); + + val = readl(&cru->clksel_con[23]); + div = bitfield_extract(val, HCLK_PERI_DIV_CON_SHIFT, + PERI_DIV_CON_WIDTH); + + return DIV_TO_RATE(parent_rate, div); +} + +static ulong rv1108_pclk_peri_get_clk(struct rv1108_cru *cru) +{ + u32 div, val; + ulong parent_rate = rkclk_pll_get_rate(cru, CLK_GENERAL); + + val = readl(&cru->clksel_con[23]); + div = bitfield_extract(val, PCLK_PERI_DIV_CON_SHIFT, + PERI_DIV_CON_WIDTH); + + return DIV_TO_RATE(parent_rate, div); +} + +static ulong rv1108_aclk_peri_set_clk(struct rv1108_cru *cru, uint hz) +{ + int src_clk_div; + ulong parent_rate = rkclk_pll_get_rate(cru, CLK_GENERAL); + + src_clk_div = DIV_ROUND_UP(parent_rate, hz) - 1; + assert(src_clk_div < 32); + + rk_clrsetreg(&cru->clksel_con[23], + ACLK_PERI_DIV_CON_MASK | ACLK_PERI_PLL_SEL_MASK, + (src_clk_div << ACLK_PERI_DIV_CON_SHIFT) | + (ACLK_PERI_PLL_SEL_GPLL << ACLK_PERI_PLL_SEL_SHIFT)); + + return rv1108_aclk_peri_get_clk(cru); +} + +static ulong rv1108_hclk_peri_set_clk(struct rv1108_cru *cru, uint hz) +{ + int src_clk_div; + ulong parent_rate = rkclk_pll_get_rate(cru, CLK_GENERAL); + + src_clk_div = DIV_ROUND_UP(parent_rate, hz) - 1; + assert(src_clk_div < 32); + + rk_clrsetreg(&cru->clksel_con[23], + HCLK_PERI_DIV_CON_MASK, + (src_clk_div << HCLK_PERI_DIV_CON_SHIFT)); + + return rv1108_hclk_peri_get_clk(cru); +} + +static ulong rv1108_pclk_peri_set_clk(struct rv1108_cru *cru, uint hz) +{ + int src_clk_div; + ulong parent_rate = rkclk_pll_get_rate(cru, CLK_GENERAL); + + src_clk_div = DIV_ROUND_UP(parent_rate, hz) - 1; + assert(src_clk_div < 32); + + rk_clrsetreg(&cru->clksel_con[23], + PCLK_PERI_DIV_CON_MASK, + (src_clk_div << PCLK_PERI_DIV_CON_SHIFT)); + + return rv1108_pclk_peri_get_clk(cru); +} + static ulong rv1108_clk_get_rate(struct clk *clk) { struct rv1108_clk_priv *priv = dev_get_priv(clk->dev); @@ -165,6 +419,20 @@ static ulong rv1108_clk_get_rate(struct clk *clk) return rkclk_pll_get_rate(priv->cru, clk->id); case SCLK_SARADC: return rv1108_saradc_get_clk(priv->cru); + case ACLK_VIO0: + return rv1108_aclk_vio0_get_clk(priv->cru); + case ACLK_VIO1: + return rv1108_aclk_vio1_get_clk(priv->cru); + case DCLK_VOP: + return rv1108_dclk_vop_get_clk(priv->cru); + case ACLK_PRE: + return rv1108_aclk_bus_get_clk(priv->cru); + case ACLK_PERI: + return rv1108_aclk_peri_get_clk(priv->cru); + case HCLK_PERI: + return rv1108_hclk_peri_get_clk(priv->cru); + case PCLK_PERI: + return rv1108_pclk_peri_get_clk(priv->cru); default: return -ENOENT; } @@ -185,6 +453,27 @@ static ulong rv1108_clk_set_rate(struct clk *clk, ulong rate) case SCLK_SARADC: new_rate = rv1108_saradc_set_clk(priv->cru, rate); break; + case ACLK_VIO0: + new_rate = rv1108_aclk_vio0_set_clk(priv->cru, rate); + break; + case ACLK_VIO1: + new_rate = rv1108_aclk_vio1_set_clk(priv->cru, rate); + break; + case DCLK_VOP: + new_rate = rv1108_dclk_vop_set_clk(priv->cru, rate); + break; + case ACLK_PRE: + new_rate = rv1108_aclk_bus_set_clk(priv->cru, rate); + break; + case ACLK_PERI: + new_rate = rv1108_aclk_peri_set_clk(priv->cru, rate); + break; + case HCLK_PERI: + new_rate = rv1108_hclk_peri_set_clk(priv->cru, rate); + break; + case PCLK_PERI: + new_rate = rv1108_pclk_peri_set_clk(priv->cru, rate); + break; default: return -ENOENT; } @@ -199,14 +488,34 @@ static const struct clk_ops rv1108_clk_ops = { static void rkclk_init(struct rv1108_cru *cru) { - unsigned int apll = rkclk_pll_get_rate(cru, CLK_ARM); - unsigned int dpll = rkclk_pll_get_rate(cru, CLK_DDR); - unsigned int gpll = rkclk_pll_get_rate(cru, CLK_GENERAL); + unsigned int apll, dpll, gpll; + unsigned int aclk_bus, aclk_peri, hclk_peri, pclk_peri; + + aclk_bus = rv1108_aclk_bus_set_clk(cru, ACLK_BUS_HZ / 2); + aclk_peri = rv1108_aclk_peri_set_clk(cru, ACLK_PERI_HZ / 2); + hclk_peri = rv1108_hclk_peri_set_clk(cru, HCLK_PERI_HZ / 2); + pclk_peri = rv1108_pclk_peri_set_clk(cru, PCLK_PERI_HZ / 2); + rv1108_aclk_vio0_set_clk(cru, 297000000); + rv1108_aclk_vio1_set_clk(cru, 297000000); + + /* configure apll */ + rkclk_set_pll(cru, CLK_ARM, &apll_init_cfg); + rkclk_set_pll(cru, CLK_GENERAL, &gpll_init_cfg); + aclk_bus = rv1108_aclk_bus_set_clk(cru, ACLK_BUS_HZ); + aclk_peri = rv1108_aclk_peri_set_clk(cru, ACLK_PERI_HZ); + hclk_peri = rv1108_hclk_peri_set_clk(cru, HCLK_PERI_HZ); + pclk_peri = rv1108_pclk_peri_set_clk(cru, PCLK_PERI_HZ); + + apll = rkclk_pll_get_rate(cru, CLK_ARM); + dpll = rkclk_pll_get_rate(cru, CLK_DDR); + gpll = rkclk_pll_get_rate(cru, CLK_GENERAL); rk_clrsetreg(&cru->clksel_con[0], CORE_CLK_DIV_MASK, 0 << MAC_CLK_DIV_SHIFT); printf("APLL: %d DPLL:%d GPLL:%d\n", apll, dpll, gpll); + printf("ACLK_BUS: %d ACLK_PERI:%d HCLK_PERI:%d PCLK_PERI:%d\n", + aclk_bus, aclk_peri, hclk_peri, pclk_peri); } static int rv1108_clk_probe(struct udevice *dev) diff --git a/include/dt-bindings/clock/rv1108-cru.h b/include/dt-bindings/clock/rv1108-cru.h index 7defc6b282..dcc6e42463 100644 --- a/include/dt-bindings/clock/rv1108-cru.h +++ b/include/dt-bindings/clock/rv1108-cru.h @@ -41,6 +41,7 @@ #define SCLK_MACREF_OUT 90 #define SCLK_SARADC 91 +#define DCLK_VOP 187 /* aclk gates */ #define ACLK_DMAC 192 @@ -48,7 +49,9 @@ #define ACLK_CORE 194 #define ACLK_ENMCORE 195 #define ACLK_GMAC 196 - +#define ACLK_VIO0 200 +#define ACLK_VIO1 201 +#define ACLK_PERI 209 /* pclk gates */ #define PCLK_GPIO1 256 @@ -69,6 +72,7 @@ #define PCLK_PERI 271 #define PCLK_GMAC 272 #define PCLK_SARADC 273 +#define PCLK_VIO 276 /* hclk gates */ #define HCLK_I2S0_8CH 320 @@ -80,8 +84,9 @@ #define HCLK_EMMC 326 #define HCLK_PERI 327 #define HCLK_SFC 328 +#define HCLK_VIO 332 -#define CLK_NR_CLKS (HCLK_SFC + 1) +#define CLK_NR_CLKS (HCLK_VIO + 1) /* reset id */ #define SRST_CORE_PO_AD 0