X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=drivers%2Fclk%2Fclk_rk3288.c;h=2a85e93a6cc3f534ac165e17343de18344c43619;hb=75cc93fad73897896511f08c1529233484ff063c;hp=e410e7d171056d1582b234086f45394f60a61a24;hpb=898d64395c6f8889fc9fd09a0a4dc6a6f018598f;p=u-boot diff --git a/drivers/clk/clk_rk3288.c b/drivers/clk/clk_rk3288.c index e410e7d171..2a85e93a6c 100644 --- a/drivers/clk/clk_rk3288.c +++ b/drivers/clk/clk_rk3288.c @@ -15,7 +15,9 @@ #include #include #include +#include #include +#include DECLARE_GLOBAL_DATA_PTR; @@ -57,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, @@ -81,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, @@ -95,27 +112,6 @@ enum { PERI_ACLK_DIV_SHIFT = 0, PERI_ACLK_DIV_MASK = 0x1f, - /* CLKSEL37 */ - DPLL_MODE_MASK = 0x3, - DPLL_MODE_SHIFT = 4, - DPLL_MODE_SLOW = 0, - DPLL_MODE_NORM, - - CPLL_MODE_MASK = 3, - CPLL_MODE_SHIFT = 8, - CPLL_MODE_SLOW = 0, - CPLL_MODE_NORM, - - GPLL_MODE_MASK = 3, - GPLL_MODE_SHIFT = 12, - GPLL_MODE_SLOW = 0, - GPLL_MODE_NORM, - - NPLL_MODE_MASK = 3, - NPLL_MODE_SHIFT = 14, - NPLL_MODE_SLOW = 0, - NPLL_MODE_NORM, - SOCSTS_DPLL_LOCK = 1 << 5, SOCSTS_APLL_LOCK = 1 << 6, SOCSTS_CPLL_LOCK = 1 << 7, @@ -139,6 +135,37 @@ static const struct pll_div apll_init_cfg = PLL_DIVISORS(APLL_HZ, 1, 1); static const struct pll_div gpll_init_cfg = PLL_DIVISORS(GPLL_HZ, 2, 2); static const struct pll_div cpll_init_cfg = PLL_DIVISORS(CPLL_HZ, 1, 2); +int rkclk_get_clk(enum rk_clk_id clk_id, struct udevice **devp) +{ + struct udevice *dev; + + for (uclass_find_first_device(UCLASS_CLK, &dev); + dev; + uclass_find_next_device(&dev)) { + struct rk3288_clk_plat *plat = dev_get_platdata(dev); + + if (plat->clk_id == clk_id) { + *devp = dev; + return device_probe(dev); + } + } + + return -ENODEV; +} + +void *rockchip_get_cru(void) +{ + struct rk3288_clk_priv *priv; + struct udevice *dev; + int ret; + + ret = rkclk_get_clk(CLK_GENERAL, &dev); + if (ret) + return ERR_PTR(ret); + priv = dev_get_priv(dev); + return priv->cru; +} + static int rkclk_set_pll(struct rk3288_cru *cru, enum rk_clk_id clk_id, const struct pll_div *div) { @@ -148,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, @@ -165,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; @@ -187,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; @@ -202,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; } @@ -218,10 +244,128 @@ static int rkclk_configure_ddr(struct rk3288_cru *cru, struct rk3288_grf *grf, /* PLL enter normal-mode */ rk_clrsetreg(&cru->cru_mode_con, DPLL_MODE_MASK << DPLL_MODE_SHIFT, - DPLL_MODE_NORM << DPLL_MODE_SHIFT); + DPLL_MODE_NORMAL << DPLL_MODE_SHIFT); + + 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) @@ -290,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); @@ -298,11 +443,57 @@ static void rkclk_init(struct rk3288_cru *cru, struct rk3288_grf *grf) rk_clrsetreg(&cru->cru_mode_con, GPLL_MODE_MASK << GPLL_MODE_SHIFT | CPLL_MODE_MASK << CPLL_MODE_SHIFT, - GPLL_MODE_NORM << GPLL_MODE_SHIFT | - GPLL_MODE_NORM << CPLL_MODE_SHIFT); + GPLL_MODE_NORMAL << GPLL_MODE_SHIFT | + CPLL_MODE_NORMAL << CPLL_MODE_SHIFT); } #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) @@ -312,17 +503,17 @@ static uint32_t rkclk_pll_get_rate(struct rk3288_cru *cru, int pll_id = rk_pll_id(clk_id); struct rk3288_pll *pll = &cru->pll[pll_id]; static u8 clk_shift[CLK_COUNT] = { - 0xff, APLL_WORK_SHIFT, DPLL_WORK_SHIFT, CPLL_WORK_SHIFT, - GPLL_WORK_SHIFT, NPLL_WORK_SHIFT + 0xff, APLL_MODE_SHIFT, DPLL_MODE_SHIFT, CPLL_MODE_SHIFT, + GPLL_MODE_SHIFT, NPLL_MODE_SHIFT }; uint shift; con = readl(&cru->cru_mode_con); shift = clk_shift[clk_id]; - switch ((con >> shift) & APLL_WORK_MASK) { - case APLL_WORK_SLOW: + switch ((con >> shift) & APLL_MODE_MASK) { + case APLL_MODE_SLOW: return OSC_HZ; - case APLL_WORK_NORMAL: + case APLL_MODE_NORMAL: /* normal mode */ con = readl(&pll->con0); no = ((con >> CLKOD_SHIFT) & CLKOD_MASK) + 1; @@ -331,7 +522,7 @@ static uint32_t rkclk_pll_get_rate(struct rk3288_cru *cru, nf = ((con >> CLKF_SHIFT) & CLKF_MASK) + 1; return (24 * nf / (nr * no)) * 1000000; - case APLL_WORK_DEEP: + case APLL_MODE_DEEP: default: return 32768; } @@ -363,7 +554,7 @@ static ulong rk3288_clk_set_rate(struct udevice *dev, ulong rate) return 0; } -static ulong rockchip_mmc_get_clk(struct rk3288_cru *cru, uint clk_general_rate, +static ulong rockchip_mmc_get_clk(struct rk3288_cru *cru, uint gclk_rate, int periph) { uint src_rate; @@ -390,18 +581,18 @@ static ulong rockchip_mmc_get_clk(struct rk3288_cru *cru, uint clk_general_rate, return -EINVAL; } - src_rate = mux == EMMC_PLL_SELECT_24MHZ ? OSC_HZ : clk_general_rate; + src_rate = mux == EMMC_PLL_SELECT_24MHZ ? OSC_HZ : gclk_rate; return DIV_TO_RATE(src_rate, div); } -static ulong rockchip_mmc_set_clk(struct rk3288_cru *cru, uint clk_general_rate, +static ulong rockchip_mmc_set_clk(struct rk3288_cru *cru, uint gclk_rate, int periph, uint freq) { int src_clk_div; int mux; - debug("%s: clk_general_rate=%u\n", __func__, clk_general_rate); - src_clk_div = RATE_TO_DIV(clk_general_rate, freq); + debug("%s: gclk_rate=%u\n", __func__, gclk_rate); + src_clk_div = RATE_TO_DIV(gclk_rate, freq); if (src_clk_div > 0x3f) { src_clk_div = RATE_TO_DIV(OSC_HZ, freq); @@ -439,10 +630,10 @@ static ulong rockchip_mmc_set_clk(struct rk3288_cru *cru, uint clk_general_rate, return -EINVAL; } - return rockchip_mmc_get_clk(cru, clk_general_rate, periph); + return rockchip_mmc_get_clk(cru, gclk_rate, periph); } -static ulong rockchip_spi_get_clk(struct rk3288_cru *cru, uint clk_general_rate, +static ulong rockchip_spi_get_clk(struct rk3288_cru *cru, uint gclk_rate, int periph) { uint div, mux; @@ -469,16 +660,16 @@ static ulong rockchip_spi_get_clk(struct rk3288_cru *cru, uint clk_general_rate, } assert(mux == SPI0_PLL_SELECT_GENERAL); - return DIV_TO_RATE(clk_general_rate, div); + return DIV_TO_RATE(gclk_rate, div); } -static ulong rockchip_spi_set_clk(struct rk3288_cru *cru, uint clk_general_rate, +static ulong rockchip_spi_set_clk(struct rk3288_cru *cru, uint gclk_rate, int periph, uint freq) { int src_clk_div; - debug("%s: clk_general_rate=%u\n", __func__, clk_general_rate); - src_clk_div = RATE_TO_DIV(clk_general_rate, freq); + debug("%s: clk_general_rate=%u\n", __func__, gclk_rate); + src_clk_div = RATE_TO_DIV(gclk_rate, freq); switch (periph) { case SCLK_SPI0: rk_clrsetreg(&cru->cru_clksel_con[25], @@ -505,17 +696,54 @@ static ulong rockchip_spi_set_clk(struct rk3288_cru *cru, uint clk_general_rate, return -EINVAL; } - return rockchip_spi_get_clk(cru, clk_general_rate, periph); + return rockchip_spi_get_clk(cru, gclk_rate, periph); +} + +static ulong rk3288_get_periph_rate(struct udevice *dev, int periph) +{ + struct rk3288_clk_priv *priv = dev_get_priv(dev); + struct udevice *gclk; + ulong new_rate, gclk_rate; + int ret; + + ret = rkclk_get_clk(CLK_GENERAL, &gclk); + if (ret) + return ret; + gclk_rate = clk_get_rate(gclk); + switch (periph) { + case HCLK_EMMC: + case HCLK_SDMMC: + case HCLK_SDIO0: + new_rate = rockchip_mmc_get_clk(priv->cru, gclk_rate, periph); + break; + case SCLK_SPI0: + case SCLK_SPI1: + case SCLK_SPI2: + new_rate = rockchip_spi_get_clk(priv->cru, gclk_rate, periph); + break; + case PCLK_I2C0: + case PCLK_I2C1: + case PCLK_I2C2: + case PCLK_I2C3: + case PCLK_I2C4: + case PCLK_I2C5: + return gclk_rate; + default: + return -ENOENT; + } + + return new_rate; } 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; - ret = uclass_get_device(UCLASS_CLK, CLK_GENERAL, &gclk); + ret = rkclk_get_clk(CLK_GENERAL, &gclk); if (ret) return ret; gclk_rate = clk_get_rate(gclk); @@ -523,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; } @@ -543,6 +818,7 @@ static struct clk_ops rk3288_clk_ops = { .get_rate = rk3288_clk_get_rate, .set_rate = rk3288_clk_set_rate, .set_periph_rate = rk3288_set_periph_rate, + .get_periph_rate = rk3288_get_periph_rate, }; static int rk3288_clk_probe(struct udevice *dev) @@ -572,7 +848,7 @@ static const char *const clk_name[CLK_COUNT] = { "dpll", "cpll", "gpll", - "mpll", + "npll", }; static int rk3288_clk_bind(struct udevice *dev)