From 609e71e68e46a0183978352a330198e570a89a86 Mon Sep 17 00:00:00 2001 From: Lukasz Czechowski Date: Thu, 22 Aug 2024 12:33:15 +0200 Subject: [PATCH] rockchip: px30: clk: add UART0 clock getter/setter Add dedicated getter and setter for SCLK_UART0_PMU. This allows the driver to correctly handle UART0 clocks, and thus it fixes the issues with UART0 not working in case DEBUG_UART is disabled. Unlike other Rockchip SoCs, i.e. rk3399, in the PX30 the default clock source for UART is GPLL, instead of external oscillator. If the DEBUG_UART is enabled, the clock source is changed in board_debug_uart_init function to 24Mhz oscillator, which also matches the fallback value obtained from DT node. In case the DEBUG_UART is disabled, the UART clock source remains default, and the DM serial driver wrongly configures the baud rate, resulting in broken communication. By implementing the UART clock getter/setter, the serial driver can probe the actual configuration and corectly configure itself. The DEBUG_UART settings now should not affect it. The driver supports GPLL and 24M oscillator. NPLL and USBPHY480M sources, that are managed by CRU, are not yet handled, as likely they won't be used in real scenarios. Signed-off-by: Lukasz Czechowski Reviewed-by: Quentin Schulz Reviewed-by: Kever Yang --- arch/arm/include/asm/arch-rockchip/cru_px30.h | 7 ++ drivers/clk/rockchip/clk_px30.c | 105 ++++++++++++++++++ 2 files changed, 112 insertions(+) diff --git a/arch/arm/include/asm/arch-rockchip/cru_px30.h b/arch/arm/include/asm/arch-rockchip/cru_px30.h index b66277fc7f..504459bd93 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 22ede1c38a..ad7e1c0f24 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; } -- 2.39.5