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..6768ef21b68 100644
--- a/drivers/clk/rockchip/clk_px30.c
+++ b/drivers/clk/rockchip/clk_px30.c
@@ -1589,6 +1589,108 @@ 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_uart0_div_con;
+	u32 clk_uart0_pll_sel;
+	u32 clk_uart0_sel;
+	ulong clk_uart0;
+	ulong pll_rate;
+	u32 con;
+
+	con = readl(&pmucru->pmu_clksel_con[3]);
+	clk_uart0_div_con = bitfield_extract_by_mask(con, UART0_DIV_CON_MASK);
+	clk_uart0_pll_sel = bitfield_extract_by_mask(con, UART0_PLL_SEL_MASK);
+
+	switch (clk_uart0_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_uart0 = DIV_TO_RATE(pll_rate, clk_uart0_div_con);
+	con = readl(&pmucru->pmu_clksel_con[4]);
+	clk_uart0_sel = bitfield_extract_by_mask(con, UART0_CLK_SEL_MASK);
+
+	switch (clk_uart0_sel) {
+	case UART0_CLK_SEL_UART0:
+		return clk_uart0;
+	case UART0_CLK_SEL_UART0_NP5:{
+			u32 clk_uart0_divnp5_div_con;
+
+			clk_uart0_divnp5_div_con =
+			    bitfield_extract_by_mask(con, UART0_DIVNP5_MASK);
+			return 2 * (u64) clk_uart0 / (2 *
+						      clk_uart0_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_uart0 * 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;
+	u32 clk_uart0_div_con;
+	u32 clk_uart0_pll_sel;
+	u32 clk_uart0_sel;
+	ulong gpll_rate;
+	ulong m = 0, n = 0;
+
+	gpll_rate = px30_pmuclk_get_gpll_rate(priv);
+	if (gpll_rate % rate == 0) {
+		clk_uart0_pll_sel = UART0_PLL_SEL_GPLL;
+		clk_uart0_sel = UART0_CLK_SEL_UART0;
+		clk_uart0_div_con = DIV_ROUND_UP(priv->gpll_hz, rate);
+	} else if (rate == OSC_HZ) {
+		clk_uart0_pll_sel = UART0_PLL_SEL_24M;
+		clk_uart0_sel = UART0_CLK_SEL_UART0;
+		clk_uart0_div_con = 1;
+	} else {
+		clk_uart0_pll_sel = UART0_PLL_SEL_GPLL;
+		clk_uart0_sel = UART0_CLK_SEL_UART0_FRAC;
+		clk_uart0_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_uart0_pll_sel << UART0_PLL_SEL_SHIFT |
+		     (clk_uart0_div_con - 1));
+	rk_clrsetreg(&pmucru->pmu_clksel_con[4], UART0_CLK_SEL_MASK,
+		     clk_uart0_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 +1704,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 +1727,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;
 	}
