diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/clk/rockchip/clk_rk3399.c | 79 | ||||
-rw-r--r-- | drivers/clk/rockchip/clk_rv1108.c | 475 | ||||
-rw-r--r-- | drivers/rtc/Kconfig | 10 | ||||
-rw-r--r-- | drivers/rtc/rv3029.c | 580 | ||||
-rw-r--r-- | drivers/video/rockchip/rk_mipi.c | 4 |
5 files changed, 994 insertions, 154 deletions
diff --git a/drivers/clk/rockchip/clk_rk3399.c b/drivers/clk/rockchip/clk_rk3399.c index 26faf88116..198914b067 100644 --- a/drivers/clk/rockchip/clk_rk3399.c +++ b/drivers/clk/rockchip/clk_rk3399.c @@ -61,6 +61,11 @@ static const struct pll_div *apll_l_cfgs[] = { [APLL_L_600_MHZ] = &apll_l_600_cfg, }; +static const struct pll_div apll_b_600_cfg = PLL_DIVISORS(600*MHz, 1, 2, 1); +static const struct pll_div *apll_b_cfgs[] = { + [APLL_B_600_MHZ] = &apll_b_600_cfg, +}; + enum { /* PLL_CON0 */ PLL_FBDIV_MASK = 0xfff, @@ -128,6 +133,24 @@ enum { ATCLK_CORE_L_DIV_SHIFT = 0, ATCLK_CORE_L_DIV_MASK = 0x1f << ATCLK_CORE_L_DIV_SHIFT, + /* CLKSEL_CON2 */ + ACLKM_CORE_B_DIV_CON_SHIFT = 8, + ACLKM_CORE_B_DIV_CON_MASK = 0x1f << ACLKM_CORE_B_DIV_CON_SHIFT, + CLK_CORE_B_PLL_SEL_SHIFT = 6, + CLK_CORE_B_PLL_SEL_MASK = 3 << CLK_CORE_B_PLL_SEL_SHIFT, + CLK_CORE_B_PLL_SEL_ALPLL = 0x0, + CLK_CORE_B_PLL_SEL_ABPLL = 0x1, + CLK_CORE_B_PLL_SEL_DPLL = 0x10, + CLK_CORE_B_PLL_SEL_GPLL = 0x11, + CLK_CORE_B_DIV_MASK = 0x1f, + CLK_CORE_B_DIV_SHIFT = 0, + + /* CLKSEL_CON3 */ + PCLK_DBG_B_DIV_SHIFT = 0x8, + PCLK_DBG_B_DIV_MASK = 0x1f << PCLK_DBG_B_DIV_SHIFT, + ATCLK_CORE_B_DIV_SHIFT = 0, + ATCLK_CORE_B_DIV_MASK = 0x1f << ATCLK_CORE_B_DIV_SHIFT, + /* CLKSEL_CON14 */ PCLK_PERIHP_DIV_CON_SHIFT = 12, PCLK_PERIHP_DIV_CON_MASK = 0x7 << PCLK_PERIHP_DIV_CON_SHIFT, @@ -395,25 +418,26 @@ static int pll_para_config(u32 freq_hz, struct pll_div *div) return 0; } -void rk3399_configure_cpu(struct rk3399_cru *cru, - enum apll_l_frequencies apll_l_freq) +void rk3399_configure_cpu_l(struct rk3399_cru *cru, + enum apll_l_frequencies apll_l_freq) { u32 aclkm_div; u32 pclk_dbg_div; u32 atclk_div; + /* Setup cluster L */ rkclk_set_pll(&cru->apll_l_con[0], apll_l_cfgs[apll_l_freq]); - aclkm_div = APLL_HZ / ACLKM_CORE_HZ - 1; - assert((aclkm_div + 1) * ACLKM_CORE_HZ == APLL_HZ && + aclkm_div = LPLL_HZ / ACLKM_CORE_L_HZ - 1; + assert((aclkm_div + 1) * ACLKM_CORE_L_HZ == LPLL_HZ && aclkm_div < 0x1f); - pclk_dbg_div = APLL_HZ / PCLK_DBG_HZ - 1; - assert((pclk_dbg_div + 1) * PCLK_DBG_HZ == APLL_HZ && + pclk_dbg_div = LPLL_HZ / PCLK_DBG_L_HZ - 1; + assert((pclk_dbg_div + 1) * PCLK_DBG_L_HZ == LPLL_HZ && pclk_dbg_div < 0x1f); - atclk_div = APLL_HZ / ATCLK_CORE_HZ - 1; - assert((atclk_div + 1) * ATCLK_CORE_HZ == APLL_HZ && + atclk_div = LPLL_HZ / ATCLK_CORE_L_HZ - 1; + assert((atclk_div + 1) * ATCLK_CORE_L_HZ == LPLL_HZ && atclk_div < 0x1f); rk_clrsetreg(&cru->clksel_con[0], @@ -428,6 +452,42 @@ void rk3399_configure_cpu(struct rk3399_cru *cru, pclk_dbg_div << PCLK_DBG_L_DIV_SHIFT | atclk_div << ATCLK_CORE_L_DIV_SHIFT); } + +void rk3399_configure_cpu_b(struct rk3399_cru *cru, + enum apll_b_frequencies apll_b_freq) +{ + u32 aclkm_div; + u32 pclk_dbg_div; + u32 atclk_div; + + /* Setup cluster B */ + rkclk_set_pll(&cru->apll_b_con[0], apll_b_cfgs[apll_b_freq]); + + aclkm_div = BPLL_HZ / ACLKM_CORE_B_HZ - 1; + assert((aclkm_div + 1) * ACLKM_CORE_B_HZ == BPLL_HZ && + aclkm_div < 0x1f); + + pclk_dbg_div = BPLL_HZ / PCLK_DBG_B_HZ - 1; + assert((pclk_dbg_div + 1) * PCLK_DBG_B_HZ == BPLL_HZ && + pclk_dbg_div < 0x1f); + + atclk_div = BPLL_HZ / ATCLK_CORE_B_HZ - 1; + assert((atclk_div + 1) * ATCLK_CORE_B_HZ == BPLL_HZ && + atclk_div < 0x1f); + + rk_clrsetreg(&cru->clksel_con[2], + ACLKM_CORE_B_DIV_CON_MASK | CLK_CORE_B_PLL_SEL_MASK | + CLK_CORE_B_DIV_MASK, + aclkm_div << ACLKM_CORE_B_DIV_CON_SHIFT | + CLK_CORE_B_PLL_SEL_ABPLL << CLK_CORE_B_PLL_SEL_SHIFT | + 0 << CLK_CORE_B_DIV_SHIFT); + + rk_clrsetreg(&cru->clksel_con[3], + PCLK_DBG_B_DIV_MASK | ATCLK_CORE_B_DIV_MASK, + pclk_dbg_div << PCLK_DBG_B_DIV_SHIFT | + atclk_div << ATCLK_CORE_B_DIV_SHIFT); +} + #define I2C_CLK_REG_MASK(bus) \ (I2C_DIV_CON_MASK << \ CLK_I2C ##bus## _DIV_CON_SHIFT | \ @@ -1026,7 +1086,8 @@ static void rkclk_init(struct rk3399_cru *cru) u32 hclk_div; u32 pclk_div; - rk3399_configure_cpu(cru, APLL_L_600_MHZ); + rk3399_configure_cpu_l(cru, APLL_L_600_MHZ); + rk3399_configure_cpu_b(cru, APLL_B_600_MHZ); /* * some cru registers changed by bootrom, we'd better reset them to * reset/default values described in TRM to avoid confusion in kernel. diff --git a/drivers/clk/rockchip/clk_rv1108.c b/drivers/clk/rockchip/clk_rv1108.c index 1f9f534b28..914e2f4b21 100644 --- a/drivers/clk/rockchip/clk_rv1108.c +++ b/drivers/clk/rockchip/clk_rv1108.c @@ -17,6 +17,8 @@ #include <dm/lists.h> #include <dt-bindings/clock/rv1108-cru.h> +DECLARE_GLOBAL_DATA_PTR; + enum { VCO_MAX_HZ = 2400U * 1000000, VCO_MIN_HZ = 600 * 1000000, @@ -35,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) { @@ -57,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) { @@ -74,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; @@ -154,6 +211,326 @@ 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_i2c_get_clk(struct rv1108_cru *cru, ulong clk_id) +{ + u32 div, con; + + switch (clk_id) { + case SCLK_I2C0_PMU: + con = readl(&cru->clksel_con[19]); + div = bitfield_extract(con, CLK_I2C0_DIV_CON_SHIFT, + I2C_DIV_CON_WIDTH); + break; + case SCLK_I2C1: + con = readl(&cru->clksel_con[19]); + div = bitfield_extract(con, CLK_I2C1_DIV_CON_SHIFT, + I2C_DIV_CON_WIDTH); + break; + case SCLK_I2C2: + con = readl(&cru->clksel_con[20]); + div = bitfield_extract(con, CLK_I2C2_DIV_CON_SHIFT, + I2C_DIV_CON_WIDTH); + break; + case SCLK_I2C3: + con = readl(&cru->clksel_con[20]); + div = bitfield_extract(con, CLK_I2C3_DIV_CON_SHIFT, + I2C_DIV_CON_WIDTH); + break; + default: + printf("do not support this i2c bus\n"); + return -EINVAL; + } + + return DIV_TO_RATE(GPLL_HZ, div); +} + +static ulong rv1108_i2c_set_clk(struct rv1108_cru *cru, ulong clk_id, uint hz) +{ + int src_clk_div; + + /* i2c0,4,8 src clock from ppll, i2c1,2,3,5,6,7 src clock from gpll*/ + src_clk_div = GPLL_HZ / hz; + assert(src_clk_div - 1 <= 127); + + switch (clk_id) { + case SCLK_I2C0_PMU: + rk_clrsetreg(&cru->clksel_con[19], + CLK_I2C0_DIV_CON_MASK | CLK_I2C1_PLL_SEL_MASK, + (src_clk_div << CLK_I2C0_DIV_CON_SHIFT) | + (CLK_I2C1_PLL_SEL_GPLL << CLK_I2C1_PLL_SEL_SHIFT)); + break; + case SCLK_I2C1: + rk_clrsetreg(&cru->clksel_con[19], + CLK_I2C1_DIV_CON_MASK | CLK_I2C1_PLL_SEL_MASK, + (src_clk_div << CLK_I2C1_DIV_CON_SHIFT) | + (CLK_I2C1_PLL_SEL_GPLL << CLK_I2C1_PLL_SEL_SHIFT)); + break; + case SCLK_I2C2: + rk_clrsetreg(&cru->clksel_con[20], + CLK_I2C2_DIV_CON_MASK | CLK_I2C3_PLL_SEL_MASK, + (src_clk_div << CLK_I2C2_DIV_CON_SHIFT) | + (CLK_I2C3_PLL_SEL_GPLL << CLK_I2C3_PLL_SEL_SHIFT)); + break; + case SCLK_I2C3: + rk_clrsetreg(&cru->clksel_con[20], + CLK_I2C3_DIV_CON_MASK | CLK_I2C3_PLL_SEL_MASK, + (src_clk_div << CLK_I2C3_DIV_CON_SHIFT) | + (CLK_I2C3_PLL_SEL_GPLL << CLK_I2C3_PLL_SEL_SHIFT)); + break; + default: + printf("do not support this i2c bus\n"); + return -EINVAL; + } + + return rv1108_i2c_get_clk(cru, clk_id); +} + +static ulong rv1108_mmc_get_clk(struct rv1108_cru *cru) +{ + u32 div, con; + ulong mmc_clk; + + con = readl(&cru->clksel_con[26]); + div = bitfield_extract(con, EMMC_CLK_DIV_SHIFT, 8); + + con = readl(&cru->clksel_con[25]); + + if ((con & EMMC_PLL_SEL_MASK) >> EMMC_PLL_SEL_SHIFT == EMMC_PLL_SEL_OSC) + mmc_clk = DIV_TO_RATE(OSC_HZ, div) / 2; + else + mmc_clk = DIV_TO_RATE(GPLL_HZ, div) / 2; + + debug("%s div %d get_clk %ld\n", __func__, div, mmc_clk); + return mmc_clk; +} + +static ulong rv1108_mmc_set_clk(struct rv1108_cru *cru, ulong rate) +{ + int div; + u32 pll_rate; + + div = DIV_ROUND_UP(rkclk_pll_get_rate(cru, CLK_GENERAL), rate); + + if (div < 127) { + debug("%s source gpll\n", __func__); + rk_clrsetreg(&cru->clksel_con[25], EMMC_PLL_SEL_MASK, + (EMMC_PLL_SEL_GPLL << EMMC_PLL_SEL_SHIFT)); + pll_rate = rkclk_pll_get_rate(cru, CLK_GENERAL); + } else { + debug("%s source 24m\n", __func__); + rk_clrsetreg(&cru->clksel_con[25], EMMC_PLL_SEL_MASK, + (EMMC_PLL_SEL_OSC << EMMC_PLL_SEL_SHIFT)); + pll_rate = OSC_HZ; + } + + div = DIV_ROUND_UP(pll_rate / 2, rate); + rk_clrsetreg(&cru->clksel_con[26], EMMC_CLK_DIV_MASK, + ((div - 1) << EMMC_CLK_DIV_SHIFT)); + + debug("%s set_rate %ld div %d\n", __func__, rate, div); + + return DIV_TO_RATE(pll_rate, div); +} + static ulong rv1108_clk_get_rate(struct clk *clk) { struct rv1108_clk_priv *priv = dev_get_priv(clk->dev); @@ -163,6 +540,29 @@ 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); + case SCLK_I2C0_PMU: + case SCLK_I2C1: + case SCLK_I2C2: + case SCLK_I2C3: + return rv1108_i2c_get_clk(priv->cru, clk->id); + case HCLK_EMMC: + case SCLK_EMMC: + case SCLK_EMMC_SAMPLE: + return rv1108_mmc_get_clk(priv->cru); default: return -ENOENT; } @@ -183,6 +583,37 @@ 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; + case SCLK_I2C0_PMU: + case SCLK_I2C1: + case SCLK_I2C2: + case SCLK_I2C3: + new_rate = rv1108_i2c_set_clk(priv->cru, clk->id, rate); + break; + case HCLK_EMMC: + case SCLK_EMMC: + new_rate = rv1108_mmc_set_clk(priv->cru, rate); + break; default: return -ENOENT; } @@ -197,14 +628,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_ofdata_to_platdata(struct udevice *dev) @@ -228,8 +679,9 @@ static int rv1108_clk_probe(struct udevice *dev) static int rv1108_clk_bind(struct udevice *dev) { int ret; - struct udevice *sys_child; + struct udevice *sys_child, *sf_child; struct sysreset_reg *priv; + struct softreset_reg *sf_priv; /* The reset driver does not have a device node, so bind it here */ ret = device_bind_driver(dev, "rockchip_sysreset", "sysreset", @@ -251,6 +703,17 @@ static int rv1108_clk_bind(struct udevice *dev) if (ret) debug("Warning: software reset driver bind faile\n"); #endif + ret = device_bind_driver_to_node(dev, "rockchip_reset", "reset", + dev_ofnode(dev), &sf_child); + if (ret) { + debug("Warning: No rockchip reset driver: ret=%d\n", ret); + } else { + sf_priv = malloc(sizeof(struct softreset_reg)); + sf_priv->sf_reset_offset = offsetof(struct rv1108_cru, + softrst_con[0]); + sf_priv->sf_reset_num = 13; + sf_child->priv = sf_priv; + } return 0; } @@ -265,8 +728,8 @@ U_BOOT_DRIVER(clk_rv1108) = { .id = UCLASS_CLK, .of_match = rv1108_clk_ids, .priv_auto_alloc_size = sizeof(struct rv1108_clk_priv), - .ofdata_to_platdata = rv1108_clk_ofdata_to_platdata, .ops = &rv1108_clk_ops, .bind = rv1108_clk_bind, + .ofdata_to_platdata = rv1108_clk_ofdata_to_platdata, .probe = rv1108_clk_probe, }; diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index bcc01b135e..6038b43230 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -60,6 +60,16 @@ config RTC_ISL1208 This driver supports reading and writing the RTC/calendar and detects total power failures. +config RTC_RV3029 + bool "Enable RV3029 driver" + depends on DM_RTC + help + The MicroCrystal RV3029 is a I2C Real Time Clock (RTC) with 8-byte + battery-backed SRAM. + + This driver supports reading and writing the RTC/calendar and the + battery-baced SRAM section. + config RTC_RX8010SJ bool "Enable RX8010SJ driver" depends on DM_RTC diff --git a/drivers/rtc/rv3029.c b/drivers/rtc/rv3029.c index dd4d0607a8..38acb9c992 100644 --- a/drivers/rtc/rv3029.c +++ b/drivers/rtc/rv3029.c @@ -1,189 +1,495 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * (C) Copyright 2010 - * Heiko Schocher, DENX Software Engineering, hs@denx.de + * (C) Copyright 2018 Theobroma Systems Design und Consulting GmbH + * + * Based on a the Linux rtc-rv3029c2.c driver written by: + * Gregory Hermant <gregory.hermant@calao-systems.com> + * Michael Buesch <m@bues.ch> */ + #include <common.h> #include <command.h> +#include <dm.h> #include <i2c.h> #include <rtc.h> -#define RTC_RV3029_CTRL1 0x00 -#define RTC_RV3029_CTRL1_EERE (1 << 3) - -#define RTC_RV3029_CTRL_STATUS 0x03 -#define RTC_RV3029_CTRLS_EEBUSY (1 << 7) +#define RTC_RV3029_PAGE_LEN 7 -#define RTC_RV3029_CTRL_RESET 0x04 -#define RTC_RV3029_CTRL_SYS_R (1 << 4) +/* control section */ +#define RV3029_ONOFF_CTRL 0x00 +#define RV3029_ONOFF_CTRL_WE BIT(0) +#define RV3029_ONOFF_CTRL_TE BIT(1) +#define RV3029_ONOFF_CTRL_TAR BIT(2) +#define RV3029_ONOFF_CTRL_EERE BIT(3) +#define RV3029_ONOFF_CTRL_SRON BIT(4) +#define RV3029_ONOFF_CTRL_TD0 BIT(5) +#define RV3029_ONOFF_CTRL_TD1 BIT(6) +#define RV3029_ONOFF_CTRL_CLKINT BIT(7) +#define RV3029_IRQ_CTRL 0x01 +#define RV3029_IRQ_CTRL_AIE BIT(0) +#define RV3029_IRQ_CTRL_TIE BIT(1) +#define RV3029_IRQ_CTRL_V1IE BIT(2) +#define RV3029_IRQ_CTRL_V2IE BIT(3) +#define RV3029_IRQ_CTRL_SRIE BIT(4) +#define RV3029_IRQ_FLAGS 0x02 +#define RV3029_IRQ_FLAGS_AF BIT(0) +#define RV3029_IRQ_FLAGS_TF BIT(1) +#define RV3029_IRQ_FLAGS_V1IF BIT(2) +#define RV3029_IRQ_FLAGS_V2IF BIT(3) +#define RV3029_IRQ_FLAGS_SRF BIT(4) +#define RV3029_STATUS 0x03 +#define RV3029_STATUS_VLOW1 BIT(2) +#define RV3029_STATUS_VLOW2 BIT(3) +#define RV3029_STATUS_SR BIT(4) +#define RV3029_STATUS_PON BIT(5) +#define RV3029_STATUS_EEBUSY BIT(7) +#define RV3029_RST_CTRL 0x04 +#define RV3029_RST_CTRL_SYSR BIT(4) +#define RV3029_CONTROL_SECTION_LEN 0x05 -#define RTC_RV3029_CLOCK_PAGE 0x08 -#define RTC_RV3029_PAGE_LEN 7 +/* watch section */ +#define RV3029_W_SEC 0x08 +#define RV3029_W_MINUTES 0x09 +#define RV3029_W_HOURS 0x0A +#define RV3029_REG_HR_12_24 BIT(6) /* 24h/12h mode */ +#define RV3029_REG_HR_PM BIT(5) /* PM/AM bit in 12h mode */ +#define RV3029_W_DATE 0x0B +#define RV3029_W_DAYS 0x0C +#define RV3029_W_MONTHS 0x0D +#define RV3029_W_YEARS 0x0E -#define RV3029C2_W_SECONDS 0x00 -#define RV3029C2_W_MINUTES 0x01 -#define RV3029C2_W_HOURS 0x02 -#define RV3029C2_W_DATE 0x03 -#define RV3029C2_W_DAYS 0x04 -#define RV3029C2_W_MONTHS 0x05 -#define RV3029C2_W_YEARS 0x06 +/* eeprom control section */ +#define RV3029_CONTROL_E2P_EECTRL 0x30 +#define RV3029_TRICKLE_1K BIT(4) /* 1.5K resistance */ +#define RV3029_TRICKLE_5K BIT(5) /* 5K resistance */ +#define RV3029_TRICKLE_20K BIT(6) /* 20K resistance */ +#define RV3029_TRICKLE_80K BIT(7) /* 80K resistance */ +#define RV3029_TRICKLE_MASK (RV3029_TRICKLE_1K |\ + RV3029_TRICKLE_5K |\ + RV3029_TRICKLE_20K |\ + RV3029_TRICKLE_80K) +#define RV3029_TRICKLE_SHIFT 4 -#define RV3029C2_REG_HR_12_24 (1 << 6) /* 24h/12h mode */ -#define RV3029C2_REG_HR_PM (1 << 5) /* PM/AM bit in 12h mode */ -#define RTC_RV3029_EEPROM_CTRL 0x30 -#define RTC_RV3029_TRICKLE_1K (1 << 4) -#define RTC_RV3029_TRICKLE_5K (1 << 5) -#define RTC_RV3029_TRICKLE_20K (1 << 6) -#define RTC_RV3029_TRICKLE_80K (1 << 7) - -int rtc_get( struct rtc_time *tmp ) +static int rv3029_rtc_get(struct udevice *dev, struct rtc_time *tm) { - int ret; - unsigned char buf[RTC_RV3029_PAGE_LEN]; + u8 regs[RTC_RV3029_PAGE_LEN]; + int ret; - ret = i2c_read(CONFIG_SYS_I2C_RTC_ADDR, RTC_RV3029_CLOCK_PAGE, 1, buf, \ - RTC_RV3029_PAGE_LEN); - if (ret) { + ret = dm_i2c_read(dev, RV3029_W_SEC, regs, sizeof(regs)); + if (ret < 0) { printf("%s: error reading RTC: %x\n", __func__, ret); - return -1; + return -EIO; + } + + tm->tm_sec = bcd2bin(regs[RV3029_W_SEC - RV3029_W_SEC]); + tm->tm_min = bcd2bin(regs[RV3029_W_MINUTES - RV3029_W_SEC]); + + /* HR field has a more complex interpretation */ + { + const u8 _hr = regs[RV3029_W_HOURS - RV3029_W_SEC]; + + if (_hr & RV3029_REG_HR_12_24) { + /* 12h format */ + tm->tm_hour = bcd2bin(_hr & 0x1f); + if (_hr & RV3029_REG_HR_PM) /* PM flag set */ + tm->tm_hour += 12; + } else { + /* 24h format */ + tm->tm_hour = bcd2bin(_hr & 0x3f); + } } - tmp->tm_sec = bcd2bin( buf[RV3029C2_W_SECONDS] & 0x7f); - tmp->tm_min = bcd2bin( buf[RV3029C2_W_MINUTES] & 0x7f); - if (buf[RV3029C2_W_HOURS] & RV3029C2_REG_HR_12_24) { - /* 12h format */ - tmp->tm_hour = bcd2bin(buf[RV3029C2_W_HOURS] & 0x1f); - if (buf[RV3029C2_W_HOURS] & RV3029C2_REG_HR_PM) - /* PM flag set */ - tmp->tm_hour += 12; - } else - tmp->tm_hour = bcd2bin(buf[RV3029C2_W_HOURS] & 0x3f); - - tmp->tm_mday = bcd2bin( buf[RV3029C2_W_DATE] & 0x3F ); - tmp->tm_mon = bcd2bin( buf[RV3029C2_W_MONTHS] & 0x1F ); - tmp->tm_wday = bcd2bin( buf[RV3029C2_W_DAYS] & 0x07 ); + + tm->tm_mday = bcd2bin(regs[RV3029_W_DATE - RV3029_W_SEC]); + tm->tm_mon = bcd2bin(regs[RV3029_W_MONTHS - RV3029_W_SEC]) - 1; /* RTC supports only years > 1999 */ - tmp->tm_year = bcd2bin( buf[RV3029C2_W_YEARS]) + 2000; - tmp->tm_yday = 0; - tmp->tm_isdst = 0; + tm->tm_year = bcd2bin(regs[RV3029_W_YEARS - RV3029_W_SEC]) + 2000; + tm->tm_wday = bcd2bin(regs[RV3029_W_DAYS - RV3029_W_SEC]) - 1; + + tm->tm_yday = 0; + tm->tm_isdst = 0; - debug( "Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", - tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, - tmp->tm_hour, tmp->tm_min, tmp->tm_sec ); + debug("%s: %4d-%02d-%02d (wday=%d) %2d:%02d:%02d\n", + __func__, tm->tm_year, tm->tm_mon, tm->tm_mday, + tm->tm_wday, tm->tm_hour, tm->tm_min, tm->tm_sec); return 0; } -int rtc_set( struct rtc_time *tmp ) +static int rv3029_rtc_set(struct udevice *dev, const struct rtc_time *tm) { - int ret; - unsigned char buf[RTC_RV3029_PAGE_LEN]; + u8 regs[RTC_RV3029_PAGE_LEN]; + + debug("%s: %4d-%02d-%02d (wday=%d( %2d:%02d:%02d\n", + __func__, tm->tm_year, tm->tm_mon, tm->tm_mday, + tm->tm_wday, tm->tm_hour, tm->tm_min, tm->tm_sec); - debug( "Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", - tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, - tmp->tm_hour, tmp->tm_min, tmp->tm_sec); - if (tmp->tm_year < 2000) { - printf("RTC: year %d < 2000 not possible\n", tmp->tm_year); - return -1; + if (tm->tm_year < 2000) { + printf("%s: year %d (before 2000) not supported\n", + __func__, tm->tm_year); + return -EINVAL; } - buf[RV3029C2_W_SECONDS] = bin2bcd(tmp->tm_sec); - buf[RV3029C2_W_MINUTES] = bin2bcd(tmp->tm_min); - buf[RV3029C2_W_HOURS] = bin2bcd(tmp->tm_hour); - /* set 24h format */ - buf[RV3029C2_W_HOURS] &= ~RV3029C2_REG_HR_12_24; - buf[RV3029C2_W_DATE] = bin2bcd(tmp->tm_mday); - buf[RV3029C2_W_DAYS] = bin2bcd(tmp->tm_wday); - buf[RV3029C2_W_MONTHS] = bin2bcd(tmp->tm_mon); - tmp->tm_year -= 2000; - buf[RV3029C2_W_YEARS] = bin2bcd(tmp->tm_year); - ret = i2c_write(CONFIG_SYS_I2C_RTC_ADDR, RTC_RV3029_CLOCK_PAGE, 1, - buf, RTC_RV3029_PAGE_LEN); - - /* give the RTC some time to update */ - udelay(1000); - return ret; + + regs[RV3029_W_SEC - RV3029_W_SEC] = bin2bcd(tm->tm_sec); + regs[RV3029_W_MINUTES - RV3029_W_SEC] = bin2bcd(tm->tm_min); + regs[RV3029_W_HOURS - RV3029_W_SEC] = bin2bcd(tm->tm_hour); + regs[RV3029_W_DATE - RV3029_W_SEC] = bin2bcd(tm->tm_mday); + regs[RV3029_W_MONTHS - RV3029_W_SEC] = bin2bcd(tm->tm_mon + 1); + regs[RV3029_W_DAYS - RV3029_W_SEC] = bin2bcd(tm->tm_wday + 1) & 0x7; + regs[RV3029_W_YEARS - RV3029_W_SEC] = bin2bcd(tm->tm_year - 2000); + + return dm_i2c_write(dev, RV3029_W_SEC, regs, sizeof(regs)); } -/* sets EERE-Bit (automatic EEPROM refresh) */ -static void set_eere_bit(int state) +static int rv3029_rtc_reset(struct udevice *dev) { - unsigned char reg_ctrl1; + u8 ctrl = RV3029_RST_CTRL_SYSR; + unsigned long start; + const unsigned long timeout_ms = 10000; + int ret; - (void)i2c_read(CONFIG_SYS_I2C_RTC_ADDR, RTC_RV3029_CTRL1, 1, - ®_ctrl1, 1); + /* trigger the system-reset */ + ret = dm_i2c_write(dev, RV3029_RST_CTRL, &ctrl, 1); + if (ret < 0) + return -EIO; - if (state) - reg_ctrl1 |= RTC_RV3029_CTRL1_EERE; - else - reg_ctrl1 &= (~RTC_RV3029_CTRL1_EERE); + /* wait for the system-reset to complete */ + start = get_timer(0); + do { + if (get_timer(start) > timeout_ms) + return -ETIMEDOUT; - (void)i2c_write(CONFIG_SYS_I2C_RTC_ADDR, RTC_RV3029_CTRL1, 1, - ®_ctrl1, 1); + ret = dm_i2c_read(dev, RV3029_RST_CTRL, &ctrl, 1); + if (ret < 0) + return -EIO; + } while (ctrl & RV3029_RST_CTRL_SYSR); + + return 0; } -/* waits until EEPROM page is no longer busy (times out after 10ms*loops) */ -static int wait_eebusy(int loops) +static int rv3029_rtc_read8(struct udevice *dev, unsigned int reg) { - int i; - unsigned char ctrl_status; + u8 data; + int ret; + + ret = dm_i2c_read(dev, reg, &data, sizeof(data)); + return ret < 0 ? ret : data; +} - for (i = 0; i < loops; i++) { - (void)i2c_read(CONFIG_SYS_I2C_RTC_ADDR, RTC_RV3029_CTRL_STATUS, - 1, &ctrl_status, 1); +static int rv3029_rtc_write8(struct udevice *dev, unsigned int reg, int val) +{ + u8 data = val; + + return dm_i2c_write(dev, reg, &data, 1); +} + +#if defined(OF_CONTROL) +static int rv3029_get_sr(struct udevice *dev, u8 *buf) +{ + int ret = dm_i2c_read(dev, RV3029_STATUS, buf, 1); + + if (ret < 0) + return -EIO; + + dev_dbg(dev, "status = 0x%.2x (%d)\n", buf[0], buf[0]); + return 0; +} - if ((ctrl_status & RTC_RV3029_CTRLS_EEBUSY) == 0) +static int rv3029_set_sr(struct udevice *dev, u8 val) +{ + int ret; + + ret = dm_i2c_read(dev, RV3029_STATUS, &val, 1); + if (ret < 0) + return -EIO; + + dev_dbg(dev, "status = 0x%.2x (%d)\n", val, val); + return 0; +} + +static int rv3029_eeprom_busywait(struct udevice *dev) +{ + int i, ret; + u8 sr; + + for (i = 100; i > 0; i--) { + ret = rv3029_get_sr(dev, &sr); + if (ret < 0) + break; + if (!(sr & RV3029_STATUS_EEBUSY)) break; udelay(10000); } - return i; + if (i <= 0) { + dev_err(dev, "EEPROM busy wait timeout.\n"); + return -ETIMEDOUT; + } + + return ret; } -void rtc_reset (void) +static int rv3029_update_bits(struct udevice *dev, u8 reg, u8 mask, u8 set) { - unsigned char buf[RTC_RV3029_PAGE_LEN]; + u8 buf; + int ret; - buf[0] = RTC_RV3029_CTRL_SYS_R; - (void)i2c_write(CONFIG_SYS_I2C_RTC_ADDR, RTC_RV3029_CTRL_RESET, 1, - buf, 1); + ret = dm_i2c_read(dev, reg, &buf, 1); + if (ret < 0) + return ret; -#if defined(CONFIG_SYS_RV3029_TCR) - /* - * because EEPROM_CTRL register is in EEPROM page it is necessary to - * disable automatic EEPROM refresh and check if EEPROM is busy - * before EEPORM_CTRL register may be accessed - */ - set_eere_bit(0); - wait_eebusy(100); - /* read current trickle charger setting */ - (void)i2c_read(CONFIG_SYS_I2C_RTC_ADDR, RTC_RV3029_EEPROM_CTRL, - 1, buf, 1); - /* enable automatic EEPROM refresh again */ - set_eere_bit(1); + if ((buf & mask) == (set && mask)) + return 0; - /* - * to minimize EEPROM access write trickle charger setting only if it - * differs from current value - */ - if ((buf[0] & 0xF0) != CONFIG_SYS_RV3029_TCR) { - buf[0] = (buf[0] & 0x0F) | CONFIG_SYS_RV3029_TCR; - /* - * write trickle charger setting (disable autom. EEPROM - * refresh and wait until EEPROM is idle) - */ - set_eere_bit(0); - wait_eebusy(100); - (void)i2c_write(CONFIG_SYS_I2C_RTC_ADDR, - RTC_RV3029_EEPROM_CTRL, 1, buf, 1); - /* - * it is necessary to wait 10ms before EEBUSY-Bit may be read - * (this is not documented in the data sheet yet, but the - * manufacturer recommends it) + buf = (buf & ~mask) | (set & mask); + ret = dm_i2c_read(dev, reg, &buf, 1); + if (ret < 0) + return ret; + + return 0; +} + +static int rv3029_eeprom_exit(struct udevice *dev) +{ + /* Re-enable eeprom refresh */ + return rv3029_update_bits(dev, RV3029_ONOFF_CTRL, + RV3029_ONOFF_CTRL_EERE, + RV3029_ONOFF_CTRL_EERE); +} + +static int rv3029_eeprom_enter(struct udevice *dev) +{ + int ret; + u8 sr; + + /* Check whether we are in the allowed voltage range. */ + ret = rv3029_get_sr(dev, &sr); + if (ret < 0) + return ret; + if (sr & (RV3029_STATUS_VLOW1 | RV3029_STATUS_VLOW2)) { + /* We clear the bits and retry once just in case + * we had a brown out in early startup. */ + sr &= ~RV3029_STATUS_VLOW1; + sr &= ~RV3029_STATUS_VLOW2; + ret = rv3029_set_sr(dev, sr); + if (ret < 0) + return ret; udelay(10000); - /* wait until EEPROM write access is finished */ - wait_eebusy(100); - set_eere_bit(1); + ret = rv3029_get_sr(dev, &sr); + if (ret < 0) + return ret; + if (sr & (RV3029_STATUS_VLOW1 | RV3029_STATUS_VLOW2)) { + dev_err(dev, "Supply voltage is too low to safely access the EEPROM.\n"); + return -ENODEV; + } + } + + /* Disable eeprom refresh. */ + ret = rv3029_update_bits(dev, + RV3029_ONOFF_CTRL, RV3029_ONOFF_CTRL_EERE, 0); + if (ret < 0) + return ret; + + /* Wait for any previous eeprom accesses to finish. */ + ret = rv3029_eeprom_busywait(dev); + if (ret < 0) + rv3029_eeprom_exit(dev); + + return ret; +} + +static int rv3029_eeprom_read(struct udevice *dev, u8 reg, + u8 buf[], size_t len) +{ + int ret, err; + + err = rv3029_eeprom_enter(dev); + if (err < 0) + return err; + + ret = dm_i2c_read(dev, reg, buf, len); + + err = rv3029_eeprom_exit(dev); + if (err < 0) + return err; + + return ret; +} + +static int rv3029_eeprom_write(struct udevice *dev, u8 reg, + u8 const buf[], size_t len) +{ + int ret; + size_t i; + u8 tmp; + + ret = rv3029_eeprom_enter(dev); + if (ret < 0) + return ret; + + for (i = 0; i < len; i++, reg++) { + ret = dm_i2c_read(dev, reg, &tmp, 1); + if (ret < 0) + break; + if (tmp != buf[i]) { + ret = dm_i2c_write(dev, reg, &buf[i], 1); + if (ret < 0) + break; + } + ret = rv3029_eeprom_busywait(dev); + if (ret < 0) + break; } + + ret = rv3029_eeprom_exit(dev); + if (ret < 0) + return ret; + + return 0; +} + +static int rv3029_eeprom_update_bits(struct udevice *dev, + u8 reg, u8 mask, u8 set) +{ + u8 buf; + int ret; + + ret = rv3029_eeprom_read(dev, reg, &buf, 1); + if (ret < 0) + return ret; + + /* + * If the EEPROM already reads the correct bitpattern, we don't need + * to update it. + */ + if ((buf & mask) == (set & mask)) + return 0; + + buf = (buf & ~mask) | (set & mask); + ret = rv3029_eeprom_write(dev, reg, &buf, 1); + if (ret < 0) + return ret; + + return 0; +} + +static void rv3029_trickle_config(struct udevice *dev) +{ + static const struct rv3029_trickle_tab_elem { + u32 r; /* resistance in ohms */ + u8 conf; /* trickle config bits */ + } rv3029_trickle_tab[] = { + { + .r = 1076, + .conf = RV3029_TRICKLE_1K | RV3029_TRICKLE_5K | + RV3029_TRICKLE_20K | RV3029_TRICKLE_80K, + }, { + .r = 1091, + .conf = RV3029_TRICKLE_1K | RV3029_TRICKLE_5K | + RV3029_TRICKLE_20K, + }, { + .r = 1137, + .conf = RV3029_TRICKLE_1K | RV3029_TRICKLE_5K | + RV3029_TRICKLE_80K, + }, { + .r = 1154, + .conf = RV3029_TRICKLE_1K | RV3029_TRICKLE_5K, + }, { + .r = 1371, + .conf = RV3029_TRICKLE_1K | RV3029_TRICKLE_20K | + RV3029_TRICKLE_80K, + }, { + .r = 1395, + .conf = RV3029_TRICKLE_1K | RV3029_TRICKLE_20K, + }, { + .r = 1472, + .conf = RV3029_TRICKLE_1K | RV3029_TRICKLE_80K, + }, { + .r = 1500, + .conf = RV3029_TRICKLE_1K, + }, { + .r = 3810, + .conf = RV3029_TRICKLE_5K | RV3029_TRICKLE_20K | + RV3029_TRICKLE_80K, + }, { + .r = 4000, + .conf = RV3029_TRICKLE_5K | RV3029_TRICKLE_20K, + }, { + .r = 4706, + .conf = RV3029_TRICKLE_5K | RV3029_TRICKLE_80K, + }, { + .r = 5000, + .conf = RV3029_TRICKLE_5K, + }, { + .r = 16000, + .conf = RV3029_TRICKLE_20K | RV3029_TRICKLE_80K, + }, { + .r = 20000, + .conf = RV3029_TRICKLE_20K, + }, { + .r = 80000, + .conf = RV3029_TRICKLE_80K, + }, + }; + int err; + u32 ohms; + u8 trickle_set_bits = 0; + + /* Configure the trickle charger. */ + err = dev_read_u32(dev, "trickle-resistor-ohms", &ohms); + + if (!err) { + /* Find trickle-charger config */ + for (int i = 0; i < ARRAY_SIZE(rv3029_trickle_tab); i++) + if (rv3029_trickle_tab[i].r >= ohms) { + dev_dbg(dev, "trickle charger at %d ohms\n", + rv3029_trickle_tab[i].r); + trickle_set_bits = rv3029_trickle_tab[i].conf; + break; + } + } + + dev_dbg(dev, "trickle charger config 0x%x\n", trickle_set_bits); + err = rv3029_eeprom_update_bits(dev, RV3029_CONTROL_E2P_EECTRL, + RV3029_TRICKLE_MASK, + trickle_set_bits); + if (err < 0) + dev_dbg(dev, "failed to update trickle charger\n"); +} +#else +static inline void rv3029_trickle_config(struct udevice *dev) +{ +} #endif + +static int rv3029_probe(struct udevice *dev) +{ + i2c_set_chip_flags(dev, DM_I2C_CHIP_RD_ADDRESS | + DM_I2C_CHIP_WR_ADDRESS); + + rv3029_trickle_config(dev); + return 0; } + +static const struct rtc_ops rv3029_rtc_ops = { + .get = rv3029_rtc_get, + .set = rv3029_rtc_set, + .read8 = rv3029_rtc_read8, + .write8 = rv3029_rtc_write8, + .reset = rv3029_rtc_reset, +}; + +static const struct udevice_id rv3029_rtc_ids[] = { + { .compatible = "mc,rv3029" }, + { .compatible = "mc,rv3029c2" }, + { } +}; + +U_BOOT_DRIVER(rtc_rv3029) = { + .name = "rtc-rv3029", + .id = UCLASS_RTC, + .probe = rv3029_probe, + .of_match = rv3029_rtc_ids, + .ops = &rv3029_rtc_ops, +}; diff --git a/drivers/video/rockchip/rk_mipi.c b/drivers/video/rockchip/rk_mipi.c index 4fe8f47441..4f1a0f3a5f 100644 --- a/drivers/video/rockchip/rk_mipi.c +++ b/drivers/video/rockchip/rk_mipi.c @@ -106,7 +106,7 @@ int rk_mipi_dsi_enable(struct udevice *dev, rk_mipi_dsi_write(regs, VSYNC_ACTIVE_LOW, val); val = (timing->flags & DISPLAY_FLAGS_DE_LOW) ? 1 : 0; - rk_mipi_dsi_write(regs, DISPLAY_FLAGS_DE_LOW, val); + rk_mipi_dsi_write(regs, DATAEN_ACTIVE_LOW, val); val = (timing->flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE) ? 1 : 0; rk_mipi_dsi_write(regs, COLORM_ACTIVE_LOW, val); @@ -241,7 +241,7 @@ int rk_mipi_phy_enable(struct udevice *dev) /* select the suitable value for fsfreqrang reg */ for (i = 0; i < ARRAY_SIZE(freq_rang); i++) { - if (ddr_clk / (MHz) >= freq_rang[i][0]) + if (ddr_clk / (MHz) <= freq_rang[i][0]) break; } if (i == ARRAY_SIZE(freq_rang)) { |