X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=drivers%2Fclk%2Fclk_rk3288.c;h=2a85e93a6cc3f534ac165e17343de18344c43619;hb=75cc93fad73897896511f08c1529233484ff063c;hp=ed39b934b1012b352c9ddcaebfc405e05a362283;hpb=009741fbaead2daf003ac8487ac671d32477206e;p=u-boot diff --git a/drivers/clk/clk_rk3288.c b/drivers/clk/clk_rk3288.c index ed39b934b1..2a85e93a6c 100644 --- a/drivers/clk/clk_rk3288.c +++ b/drivers/clk/clk_rk3288.c @@ -59,6 +59,16 @@ enum { /* PLL CON3 */ PLL_RESET_SHIFT = 5, + /* CLKSEL0 */ + CORE_SEL_PLL_MASK = 1, + CORE_SEL_PLL_SHIFT = 15, + A17_DIV_MASK = 0x1f, + A17_DIV_SHIFT = 8, + MP_DIV_MASK = 0xf, + MP_DIV_SHIFT = 4, + M0_DIV_MASK = 0xf, + M0_DIV_SHIFT = 0, + /* CLKSEL1: pd bus clk pll sel: codec or general */ PD_BUS_SEL_PLL_MASK = 15, PD_BUS_SEL_CPLL = 0, @@ -83,8 +93,13 @@ enum { * peripheral bus pclk div: * aclk_bus: pclk_bus = 1:1 or 2:1 or 4:1 or 8:1 */ + PERI_SEL_PLL_MASK = 1, + PERI_SEL_PLL_SHIFT = 15, + PERI_SEL_CPLL = 0, + PERI_SEL_GPLL, + PERI_PCLK_DIV_SHIFT = 12, - PERI_PCLK_DIV_MASK = 7, + PERI_PCLK_DIV_MASK = 3, /* peripheral bus hclk div: aclk_bus: hclk_bus = 1:1 or 2:1 or 4:1 */ PERI_HCLK_DIV_SHIFT = 8, @@ -160,13 +175,13 @@ static int rkclk_set_pll(struct rk3288_cru *cru, enum rk_clk_id clk_id, uint vco_hz = OSC_HZ / 1000 * div->nf / div->nr * 1000; uint output_hz = vco_hz / div->no; - debug("PLL at %p: nf=%d, nr=%d, no=%d, vco=%u Hz, output=%u Hz\n", - pll, div->nf, div->nr, div->no, vco_hz, output_hz); + debug("PLL at %x: nf=%d, nr=%d, no=%d, vco=%u Hz, output=%u Hz\n", + (uint)pll, div->nf, div->nr, div->no, 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 && (div->no == 1 || !(div->no % 2))); - /* enter rest */ + /* enter reset */ rk_setreg(&pll->con3, 1 << PLL_RESET_SHIFT); rk_clrsetreg(&pll->con0, @@ -177,7 +192,7 @@ static int rkclk_set_pll(struct rk3288_cru *cru, enum rk_clk_id clk_id, udelay(10); - /* return form rest */ + /* return from reset */ rk_clrreg(&pll->con3, 1 << PLL_RESET_SHIFT); return 0; @@ -199,7 +214,6 @@ static int rkclk_configure_ddr(struct rk3288_cru *cru, struct rk3288_grf *grf, }; int cfg; - debug("%s: cru=%p, grf=%p, hz=%u\n", __func__, cru, grf, hz); switch (hz) { case 300000000: cfg = 0; @@ -214,7 +228,7 @@ static int rkclk_configure_ddr(struct rk3288_cru *cru, struct rk3288_grf *grf, cfg = 3; break; default: - debug("Unsupported SDRAM frequency, add to clock.c!"); + debug("Unsupported SDRAM frequency"); return -EINVAL; } @@ -235,6 +249,124 @@ static int rkclk_configure_ddr(struct rk3288_cru *cru, struct rk3288_grf *grf, return 0; } +#ifndef CONFIG_SPL_BUILD +#define VCO_MAX_KHZ 2200000 +#define VCO_MIN_KHZ 440000 +#define FREF_MAX_KHZ 2200000 +#define FREF_MIN_KHZ 269 + +static int pll_para_config(ulong freq_hz, struct pll_div *div, uint *ext_div) +{ + 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(no, max_no); + no = DIV_ROUND_UP(no, *ext_div); + } + + /* 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 a supported 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: Failed to match output frequency %lu, difference is %u Hz, exceed 4MHZ\n", + __func__, freq_hz, best_diff_khz * 1000); + return -EINVAL; + } + + return 0; +} + +static int rockchip_vop_set_clk(struct rk3288_cru *cru, struct rk3288_grf *grf, + int periph, unsigned int rate_hz) +{ + struct pll_div npll_config = {0}; + u32 lcdc_div; + int ret; + + ret = pll_para_config(rate_hz, &npll_config, &lcdc_div); + if (ret) + return ret; + + rk_clrsetreg(&cru->cru_mode_con, NPLL_MODE_MASK << NPLL_MODE_SHIFT, + NPLL_MODE_SLOW << NPLL_MODE_SHIFT); + rkclk_set_pll(cru, CLK_NEW, &npll_config); + + /* waiting for pll lock */ + while (1) { + if (readl(&grf->soc_status[1]) & SOCSTS_NPLL_LOCK) + break; + udelay(1); + } + + rk_clrsetreg(&cru->cru_mode_con, NPLL_MODE_MASK << NPLL_MODE_SHIFT, + NPLL_MODE_NORMAL << NPLL_MODE_SHIFT); + + /* vop dclk source clk: npll,dclk_div: 1 */ + switch (periph) { + case DCLK_VOP0: + rk_clrsetreg(&cru->cru_clksel_con[27], 0xff << 8 | 3 << 0, + (lcdc_div - 1) << 8 | 2 << 0); + break; + case DCLK_VOP1: + rk_clrsetreg(&cru->cru_clksel_con[29], 0xff << 8 | 3 << 6, + (lcdc_div - 1) << 8 | 2 << 6); + break; + } + + return 0; +} +#endif + #ifdef CONFIG_SPL_BUILD static void rkclk_init(struct rk3288_cru *cru, struct rk3288_grf *grf) { @@ -302,6 +434,7 @@ static void rkclk_init(struct rk3288_cru *cru, struct rk3288_grf *grf) PERI_PCLK_DIV_MASK << PERI_PCLK_DIV_SHIFT | PERI_HCLK_DIV_MASK << PERI_HCLK_DIV_SHIFT | PERI_ACLK_DIV_MASK << PERI_ACLK_DIV_SHIFT, + PERI_SEL_GPLL << PERI_SEL_PLL_SHIFT | pclk_div << PERI_PCLK_DIV_SHIFT | hclk_div << PERI_HCLK_DIV_SHIFT | aclk_div << PERI_ACLK_DIV_SHIFT); @@ -315,6 +448,52 @@ static void rkclk_init(struct rk3288_cru *cru, struct rk3288_grf *grf) } #endif +void rkclk_configure_cpu(struct rk3288_cru *cru, struct rk3288_grf *grf) +{ + /* pll enter slow-mode */ + rk_clrsetreg(&cru->cru_mode_con, + APLL_MODE_MASK << APLL_MODE_SHIFT, + APLL_MODE_SLOW << APLL_MODE_SHIFT); + + rkclk_set_pll(cru, CLK_ARM, &apll_init_cfg); + + /* waiting for pll lock */ + while (!(readl(&grf->soc_status[1]) & SOCSTS_APLL_LOCK)) + udelay(1); + + /* + * core clock pll source selection and + * set up dependent divisors for MPAXI/M0AXI and ARM clocks. + * core clock select apll, apll clk = 1800MHz + * arm clk = 1800MHz, mpclk = 450MHz, m0clk = 900MHz + */ + rk_clrsetreg(&cru->cru_clksel_con[0], + CORE_SEL_PLL_MASK << CORE_SEL_PLL_SHIFT | + A17_DIV_MASK << A17_DIV_SHIFT | + MP_DIV_MASK << MP_DIV_SHIFT | + M0_DIV_MASK << M0_DIV_SHIFT, + 0 << A17_DIV_SHIFT | + 3 << MP_DIV_SHIFT | + 1 << M0_DIV_SHIFT); + + /* + * set up dependent divisors for L2RAM/ATCLK and PCLK clocks. + * l2ramclk = 900MHz, atclk = 450MHz, pclk_dbg = 450MHz + */ + rk_clrsetreg(&cru->cru_clksel_con[37], + CLK_L2RAM_DIV_MASK << CLK_L2RAM_DIV_SHIFT | + ATCLK_CORE_DIV_CON_MASK << ATCLK_CORE_DIV_CON_SHIFT | + PCLK_CORE_DBG_DIV_MASK >> PCLK_CORE_DBG_DIV_SHIFT, + 1 << CLK_L2RAM_DIV_SHIFT | + 3 << ATCLK_CORE_DIV_CON_SHIFT | + 3 << PCLK_CORE_DBG_DIV_SHIFT); + + /* PLL enter normal-mode */ + rk_clrsetreg(&cru->cru_mode_con, + APLL_MODE_MASK << APLL_MODE_SHIFT, + APLL_MODE_NORMAL << APLL_MODE_SHIFT); +} + /* Get pll rate by id */ static uint32_t rkclk_pll_get_rate(struct rk3288_cru *cru, enum rk_clk_id clk_id) @@ -559,6 +738,7 @@ static ulong rk3288_get_periph_rate(struct udevice *dev, int periph) static ulong rk3288_set_periph_rate(struct udevice *dev, int periph, ulong rate) { struct rk3288_clk_priv *priv = dev_get_priv(dev); + struct rk3288_cru *cru = priv->cru; struct udevice *gclk; ulong new_rate, gclk_rate; int ret; @@ -571,15 +751,62 @@ static ulong rk3288_set_periph_rate(struct udevice *dev, int periph, ulong rate) case HCLK_EMMC: case HCLK_SDMMC: case HCLK_SDIO0: - new_rate = rockchip_mmc_set_clk(priv->cru, gclk_rate, periph, - rate); + new_rate = rockchip_mmc_set_clk(cru, gclk_rate, periph, rate); break; case SCLK_SPI0: case SCLK_SPI1: case SCLK_SPI2: - new_rate = rockchip_spi_set_clk(priv->cru, gclk_rate, periph, - rate); + new_rate = rockchip_spi_set_clk(cru, gclk_rate, periph, rate); + break; +#ifndef CONFIG_SPL_BUILD + case DCLK_VOP0: + case DCLK_VOP1: + new_rate = rockchip_vop_set_clk(cru, priv->grf, periph, rate); + break; + case SCLK_EDP_24M: + /* clk_edp_24M source: 24M */ + rk_setreg(&cru->cru_clksel_con[28], 1 << 15); + + /* rst edp */ + rk_setreg(&cru->cru_clksel_con[6], 1 << 15); + udelay(1); + rk_clrreg(&cru->cru_clksel_con[6], 1 << 15); + new_rate = rate; break; + case ACLK_VOP0: + case ACLK_VOP1: { + u32 div; + + /* vop aclk source clk: cpll */ + div = CPLL_HZ / rate; + assert((div - 1 < 64) && (div * rate == CPLL_HZ)); + + switch (periph) { + case ACLK_VOP0: + rk_clrsetreg(&cru->cru_clksel_con[31], + 3 << 6 | 0x1f << 0, + 0 << 6 | (div - 1) << 0); + break; + case ACLK_VOP1: + rk_clrsetreg(&cru->cru_clksel_con[31], + 3 << 14 | 0x1f << 8, + 0 << 14 | (div - 1) << 8); + break; + } + new_rate = rate; + break; + } + case PCLK_HDMI_CTRL: + /* enable pclk hdmi ctrl */ + rk_clrreg(&cru->cru_clkgate_con[16], 1 << 9); + + /* software reset hdmi */ + rk_setreg(&cru->cru_clkgate_con[7], 1 << 9); + udelay(1); + rk_clrreg(&cru->cru_clkgate_con[7], 1 << 9); + new_rate = rate; + break; +#endif default: return -ENOENT; } @@ -621,7 +848,7 @@ static const char *const clk_name[CLK_COUNT] = { "dpll", "cpll", "gpll", - "mpll", + "npll", }; static int rk3288_clk_bind(struct udevice *dev)