[PATCH v3] rockchip: px30: clk: add UART0 clock getter/setter

Lukasz Czechowski lukasz.czechowski at thaumatec.com
Thu Aug 22 12:33:15 CEST 2024


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 <lukasz.czechowski at thaumatec.com>

Reviewed-by: Quentin Schulz <quentin.schulz at cherry.de>
---
 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 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 2875c152b20..e5c004cfcc8 100644
--- a/drivers/clk/rockchip/clk_px30.c
+++ b/drivers/clk/rockchip/clk_px30.c
@@ -1589,6 +1589,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);
@@ -1602,6 +1701,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;
 	}
@@ -1622,6 +1724,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.43.0



More information about the U-Boot mailing list