diff options
-rw-r--r-- | arch/arm/include/asm/arch-rockchip/cru_px30.h | 7 | ||||
-rw-r--r-- | drivers/clk/rockchip/clk_px30.c | 105 |
2 files changed, 112 insertions, 0 deletions
diff --git a/arch/arm/include/asm/arch-rockchip/cru_px30.h b/arch/arm/include/asm/arch-rockchip/cru_px30.h index b66277fc7f3..504459bd93d 100644 --- a/arch/arm/include/asm/arch-rockchip/cru_px30.h +++ b/arch/arm/include/asm/arch-rockchip/cru_px30.h @@ -464,5 +464,12 @@ enum { UART0_CLK_SEL_UART0_FRAC, UART0_DIVNP5_SHIFT = 0, UART0_DIVNP5_MASK = 0x1f << UART0_DIVNP5_SHIFT, + + /* CRU_PMU_CLKSEL5_CON */ + CLK_UART_FRAC_NUMERATOR_SHIFT = 16, + CLK_UART_FRAC_NUMERATOR_MASK = 0xffff << CLK_UART_FRAC_NUMERATOR_SHIFT, + CLK_UART_FRAC_DENOMINATOR_SHIFT = 0, + CLK_UART_FRAC_DENOMINATOR_MASK = + 0xffff << CLK_UART_FRAC_DENOMINATOR_SHIFT, }; #endif diff --git a/drivers/clk/rockchip/clk_px30.c b/drivers/clk/rockchip/clk_px30.c index 22ede1c38a8..ad7e1c0f246 100644 --- a/drivers/clk/rockchip/clk_px30.c +++ b/drivers/clk/rockchip/clk_px30.c @@ -1588,6 +1588,105 @@ static ulong px30_pmuclk_set_gpll_rate(struct px30_pmuclk_priv *priv, ulong hz) return priv->gpll_hz; } +static ulong px30_pmu_uart0_get_clk(struct px30_pmuclk_priv *priv) +{ + struct px30_pmucru *pmucru = priv->pmucru; + u32 clk_div_con; + u32 clk_pll_sel; + ulong pll_rate; + u32 clk_sel; + ulong clk; + u32 con; + + con = readl(&pmucru->pmu_clksel_con[3]); + clk_div_con = bitfield_extract_by_mask(con, UART0_DIV_CON_MASK); + clk_pll_sel = bitfield_extract_by_mask(con, UART0_PLL_SEL_MASK); + + switch (clk_pll_sel) { + case UART0_PLL_SEL_GPLL: + pll_rate = px30_pmuclk_get_gpll_rate(priv); + break; + case UART0_PLL_SEL_24M: + pll_rate = OSC_HZ; + break; + case UART0_PLL_SEL_480M: + case UART0_PLL_SEL_NPLL: + /* usbphy480M and NPLL clocks, generated by CRU, are not supported yet */ + default: + return -ENOENT; + } + + clk = DIV_TO_RATE(pll_rate, clk_div_con); + con = readl(&pmucru->pmu_clksel_con[4]); + clk_sel = bitfield_extract_by_mask(con, UART0_CLK_SEL_MASK); + + switch (clk_sel) { + case UART0_CLK_SEL_UART0: + return clk; + case UART0_CLK_SEL_UART0_NP5:{ + u32 clk_divnp5_div_con; + + clk_divnp5_div_con = + bitfield_extract_by_mask(con, UART0_DIVNP5_MASK); + return 2 * (u64) clk / (2 * clk_divnp5_div_con + 3); + } + case UART0_CLK_SEL_UART0_FRAC:{ + u32 fracdiv, n, m; + + fracdiv = readl(&pmucru->pmu_clksel_con[5]); + n = bitfield_extract_by_mask(fracdiv, + CLK_UART_FRAC_NUMERATOR_MASK); + m = bitfield_extract_by_mask(fracdiv, + CLK_UART_FRAC_DENOMINATOR_MASK); + return (u64) clk * n / m; + } + default: + return -ENOENT; + } +} + +static ulong px30_pmu_uart0_set_clk(struct px30_pmuclk_priv *priv, ulong rate) +{ + struct px30_pmucru *pmucru = priv->pmucru; + ulong m = 0, n = 0; + ulong gpll_rate; + u32 clk_div_con; + u32 clk_pll_sel; + u32 clk_sel; + + gpll_rate = px30_pmuclk_get_gpll_rate(priv); + if (gpll_rate % rate == 0) { + clk_pll_sel = UART0_PLL_SEL_GPLL; + clk_sel = UART0_CLK_SEL_UART0; + clk_div_con = DIV_ROUND_UP(priv->gpll_hz, rate); + } else if (rate == OSC_HZ) { + clk_pll_sel = UART0_PLL_SEL_24M; + clk_sel = UART0_CLK_SEL_UART0; + clk_div_con = 1; + } else { + clk_pll_sel = UART0_PLL_SEL_GPLL; + clk_sel = UART0_CLK_SEL_UART0_FRAC; + clk_div_con = 1; + rational_best_approximation(rate, priv->gpll_hz, + GENMASK(16 - 1, 0), + GENMASK(16 - 1, 0), &m, &n); + } + + rk_clrsetreg(&pmucru->pmu_clksel_con[3], + UART0_PLL_SEL_MASK | UART0_DIV_CON_MASK, + clk_pll_sel << UART0_PLL_SEL_SHIFT | (clk_div_con - 1)); + rk_clrsetreg(&pmucru->pmu_clksel_con[4], UART0_CLK_SEL_MASK, + clk_sel << UART0_CLK_SEL_SHIFT); + if (m && n) { + u32 fracdiv; + + fracdiv = m << CLK_UART_FRAC_NUMERATOR_SHIFT | n; + writel(fracdiv, &pmucru->pmu_clksel_con[5]); + } + + return px30_pmu_uart0_get_clk(priv); +} + static ulong px30_pmuclk_get_rate(struct clk *clk) { struct px30_pmuclk_priv *priv = dev_get_priv(clk->dev); @@ -1601,6 +1700,9 @@ static ulong px30_pmuclk_get_rate(struct clk *clk) case PCLK_PMU_PRE: rate = px30_pclk_pmu_get_pmuclk(priv); break; + case SCLK_UART0_PMU: + rate = px30_pmu_uart0_get_clk(priv); + break; default: return -ENOENT; } @@ -1621,6 +1723,9 @@ static ulong px30_pmuclk_set_rate(struct clk *clk, ulong rate) case PCLK_PMU_PRE: ret = px30_pclk_pmu_set_pmuclk(priv, rate); break; + case SCLK_UART0_PMU: + ret = px30_pmu_uart0_set_clk(priv, rate); + break; default: return -ENOENT; } |