[PATCH v3 08/12] clk: qcom: ipq-cmn-pll: Add EPHY-RAW clock support
From: Luo Jie
Date: Fri Aug 14 2026 - 08:51:08 EST
The EPHY-RAW clock is an IPQ5210-specific output from the CMN PLL that
provides the raw reference clock to the SoC-integrated Ethernet PHY
(EPHY), which supports up to 2.5G link speed. The output frequency is
selected via bit 10 (CMN_PLL_GEPHY_312P5M_125M_SEL) of the
CMN_PLL_PON_CONFIG register:
- 0: 125 MHz - for 1G/100M/10M link speed
- 1: 312.5 MHz - for 2.5G link speed
Parent it on cmn_pll_div2, like the rest of IPQ5210's output clocks,
for clock-tree consistency. This is functionally identical: the
output rate is purely register-bit-selected and never depends on the
parent rate.
Signed-off-by: Luo Jie <jie.luo@xxxxxxxxxxxxxxxx>
---
drivers/clk/qcom/ipq-cmn-pll.c | 114 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 114 insertions(+)
diff --git a/drivers/clk/qcom/ipq-cmn-pll.c b/drivers/clk/qcom/ipq-cmn-pll.c
index 8987e0ff7dea..eadbf6482aa4 100644
--- a/drivers/clk/qcom/ipq-cmn-pll.c
+++ b/drivers/clk/qcom/ipq-cmn-pll.c
@@ -77,6 +77,7 @@
#define CMN_PLL_PON_MODE_SEL BIT(9)
#define CMN_PLL_PON_EN BIT(8)
#define CMN_PLL_PON_DIV_CTRL GENMASK(7, 0)
+#define CMN_PLL_GEPHY_312P5M_125M_SEL BIT(10)
#define CMN_PLL_POWER_ON_AND_RESET 0x780
#define CMN_ANA_EN_SW_RSTN BIT(6)
@@ -98,12 +99,14 @@
* @CMN_PLL_CLK_NSS: NSS clock with configurable divider
* @CMN_PLL_CLK_PPE: PPE clock with configurable divider
* @CMN_PLL_CLK_PON: PON reference clock
+ * @CMN_PLL_CLK_EPHY_RAW: EPHY-RAW clock
*/
enum cmn_pll_clk_type {
CMN_PLL_CLK_FIXED_RATE,
CMN_PLL_CLK_NSS,
CMN_PLL_CLK_PPE,
CMN_PLL_CLK_PON,
+ CMN_PLL_CLK_EPHY_RAW,
};
/**
@@ -603,6 +606,111 @@ static struct clk_hw *ipq_cmn_pll_pon_clk_register(struct platform_device *pdev,
return &pon_clk->hw;
}
+/*
+ * EPHY-RAW clock operations for IPQ5210.
+ * The output clock rate is selected via bit 10 of CMN_PLL_PON_CONFIG:
+ * 0: 125 MHz (for 1G link speed)
+ * 1: 312.5 MHz (for 2.5G link speed)
+ */
+static unsigned long clk_ephy_raw_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_cmn_pll *ephy_raw_clk = to_clk_cmn_pll(hw);
+ u32 val;
+ int ret;
+
+ ret = regmap_read(ephy_raw_clk->regmap, CMN_PLL_PON_CONFIG, &val);
+ if (WARN_ON_ONCE(ret))
+ return 0;
+
+ if (val & CMN_PLL_GEPHY_312P5M_125M_SEL)
+ return 312500000UL;
+
+ return 125000000UL;
+}
+
+static int clk_ephy_raw_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ unsigned long rate_125m = 125000000UL, rate_312p5m = 312500000UL;
+ bool rate_125m_valid, rate_312p5m_valid;
+
+ rate_125m_valid = rate_125m >= req->min_rate &&
+ rate_125m <= req->max_rate;
+ rate_312p5m_valid = rate_312p5m >= req->min_rate &&
+ rate_312p5m <= req->max_rate;
+
+ if (!rate_125m_valid && !rate_312p5m_valid)
+ return -EINVAL;
+
+ /* Pick whichever of the two supported rates is closer to the request */
+ if (rate_125m_valid && rate_312p5m_valid) {
+ unsigned long diff_125m, diff_312p5m;
+
+ diff_125m = abs_diff(req->rate, rate_125m);
+ diff_312p5m = abs_diff(req->rate, rate_312p5m);
+ req->rate = diff_125m < diff_312p5m ? rate_125m : rate_312p5m;
+ } else {
+ req->rate = rate_125m_valid ? rate_125m : rate_312p5m;
+ }
+
+ return 0;
+}
+
+static int clk_ephy_raw_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_cmn_pll *ephy_raw_clk = to_clk_cmn_pll(hw);
+
+ if (rate == 125000000UL)
+ return regmap_clear_bits(ephy_raw_clk->regmap,
+ CMN_PLL_PON_CONFIG,
+ CMN_PLL_GEPHY_312P5M_125M_SEL);
+
+ if (rate == 312500000UL)
+ return regmap_set_bits(ephy_raw_clk->regmap,
+ CMN_PLL_PON_CONFIG,
+ CMN_PLL_GEPHY_312P5M_125M_SEL);
+
+ return -EINVAL;
+}
+
+static const struct clk_ops clk_ephy_raw_ops = {
+ .recalc_rate = clk_ephy_raw_recalc_rate,
+ .determine_rate = clk_ephy_raw_determine_rate,
+ .set_rate = clk_ephy_raw_set_rate,
+};
+
+static struct clk_hw *ipq_cmn_pll_ephy_raw_register(struct platform_device *pdev,
+ struct regmap *regmap,
+ struct clk_hw *parent_hw,
+ const char *name)
+{
+ struct clk_parent_data pdata = { .hw = parent_hw };
+ struct clk_cmn_pll *ephy_raw_clk;
+ struct device *dev = &pdev->dev;
+ struct clk_init_data init = {};
+ int ret;
+
+ ephy_raw_clk = devm_kzalloc(dev, sizeof(*ephy_raw_clk), GFP_KERNEL);
+ if (!ephy_raw_clk)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.parent_data = &pdata;
+ init.num_parents = 1;
+ init.ops = &clk_ephy_raw_ops;
+
+ ephy_raw_clk->hw.init = &init;
+ ephy_raw_clk->regmap = regmap;
+
+ ret = devm_clk_hw_register(dev, &ephy_raw_clk->hw);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return &ephy_raw_clk->hw;
+}
+
static int ipq_cmn_pll_register_clks(struct platform_device *pdev)
{
const struct cmn_pll_fixed_output_clk *p, *fixed_clk;
@@ -681,6 +789,12 @@ static int ipq_cmn_pll_register_clks(struct platform_device *pdev)
cmn_pll->div2_hw,
fixed_clk[i].name);
break;
+ case CMN_PLL_CLK_EPHY_RAW:
+ hw = ipq_cmn_pll_ephy_raw_register(pdev,
+ cmn_pll->regmap,
+ cmn_pll->div2_hw,
+ fixed_clk[i].name);
+ break;
}
if (IS_ERR(hw))
--
2.43.0