[PATCH v3 05/12] clk: qcom: ipq-cmn-pll: Add NSS clock support
From: Luo Jie
Date: Fri Aug 14 2026 - 08:49:32 EST
The NSS (network subsystem) clock's parent is cmn_pll_div2 (CMN PLL
rate / 2); it is then further divided by a configurable 6-bit divider.
Register the NSS clock through a new ipq_cmn_pll_regmap_div_register()
helper, built as a plain struct clk_regmap_div instance instead of
hand-rolling recalc_rate/set_rate. The helper takes the register field
mask and clock name as parameters so that the upcoming PPE clock, which
shares the same register with a different field, can reuse it.
Signed-off-by: Luo Jie <jie.luo@xxxxxxxxxxxxxxxx>
---
drivers/clk/qcom/ipq-cmn-pll.c | 84 +++++++++++++++++++++++++++++++++++++++---
1 file changed, 79 insertions(+), 5 deletions(-)
diff --git a/drivers/clk/qcom/ipq-cmn-pll.c b/drivers/clk/qcom/ipq-cmn-pll.c
index bcb51a5f559a..59978789f493 100644
--- a/drivers/clk/qcom/ipq-cmn-pll.c
+++ b/drivers/clk/qcom/ipq-cmn-pll.c
@@ -60,12 +60,18 @@
#include <dt-bindings/clock/qcom,ipq6018-cmn-pll.h>
#include <dt-bindings/clock/qcom,ipq8074-cmn-pll.h>
+#include "clk-regmap.h"
+#include "clk-regmap-divider.h"
+
#define CMN_PLL_REFCLK_SRC_SELECTION 0x28
#define CMN_PLL_REFCLK_SRC_DIV GENMASK(9, 8)
#define CMN_PLL_LOCKED 0x64
#define CMN_PLL_CLKS_LOCKED BIT(8)
+#define CMN_PLL_NSS_PPE_FREQ_CTRL 0x98
+#define CMN_PLL_NSS_CLK_SEL GENMASK(13, 8)
+
#define CMN_PLL_POWER_ON_AND_RESET 0x780
#define CMN_ANA_EN_SW_RSTN BIT(6)
@@ -80,15 +86,27 @@
#define CMN_PLL_DIVIDER_CTRL 0x794
#define CMN_PLL_DIVIDER_CTRL_FACTOR GENMASK(9, 0)
+/**
+ * enum cmn_pll_clk_type - CMN PLL output clock registration type
+ * @CMN_PLL_CLK_FIXED_RATE: plain fixed rate clock
+ * @CMN_PLL_CLK_NSS: NSS clock with configurable divider
+ */
+enum cmn_pll_clk_type {
+ CMN_PLL_CLK_FIXED_RATE,
+ CMN_PLL_CLK_NSS,
+};
+
/**
* struct cmn_pll_fixed_output_clk - CMN PLL output clocks information
* @id: Clock specifier to be supplied
* @name: Clock name to be registered
+ * @type: Clock registration type
* @rate: Clock rate
*/
struct cmn_pll_fixed_output_clk {
unsigned int id;
const char *name;
+ enum cmn_pll_clk_type type;
unsigned long rate;
};
@@ -112,6 +130,7 @@ struct clk_cmn_pll {
#define CLK_PLL_OUTPUT(_id, _name, _rate) { \
.id = _id, \
.name = _name, \
+ .type = CMN_PLL_CLK_FIXED_RATE, \
.rate = _rate, \
}
@@ -364,6 +383,47 @@ static struct clk_hw *ipq_cmn_pll_clk_hw_register(struct platform_device *pdev)
return &cmn_pll->hw;
}
+static struct clk_hw *ipq_cmn_pll_regmap_div_register(struct platform_device *pdev,
+ struct regmap *regmap,
+ struct clk_hw *parent_hw,
+ const char *name,
+ u32 field_mask)
+{
+ struct clk_parent_data pdata = { .hw = parent_hw };
+ struct device *dev = &pdev->dev;
+ struct clk_regmap_div *div_clk;
+ int ret;
+
+ div_clk = devm_kzalloc(dev, sizeof(*div_clk), GFP_KERNEL);
+ if (!div_clk)
+ return ERR_PTR(-ENOMEM);
+
+ div_clk->reg = CMN_PLL_NSS_PPE_FREQ_CTRL;
+ div_clk->shift = __ffs(field_mask);
+ div_clk->width = hweight32(field_mask);
+ /*
+ * CMN_PLL_NSS_CLK_SEL / CMN_PLL_PPE_CLK_SEL reset to a valid, non-zero
+ * divider in hardware. CLK_DIVIDER_ALLOW_ZERO is deliberately not set:
+ * a divider field read back as 0 is genuinely invalid and should trip
+ * the core clk-divider's zero-divisor WARN rather than be silently
+ * tolerated.
+ */
+ div_clk->flags = CLK_DIVIDER_ONE_BASED;
+ div_clk->clkr.regmap = regmap;
+ div_clk->clkr.hw.init = &(struct clk_init_data){
+ .name = name,
+ .parent_data = &pdata,
+ .num_parents = 1,
+ .ops = &clk_regmap_div_ops,
+ };
+
+ ret = devm_clk_register_regmap(dev, &div_clk->clkr);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return &div_clk->clkr.hw;
+}
+
static int ipq_cmn_pll_register_clks(struct platform_device *pdev)
{
const struct cmn_pll_fixed_output_clk *p, *fixed_clk;
@@ -412,12 +472,26 @@ static int ipq_cmn_pll_register_clks(struct platform_device *pdev)
/* Register the fixed rate output clocks. */
for (i = 0; i < num_clks; i++) {
- struct clk_parent_data pdata = { .hw = cmn_pll_hw };
+ hw = ERR_PTR(-EINVAL);
+
+ switch (fixed_clk[i].type) {
+ case CMN_PLL_CLK_FIXED_RATE: {
+ struct clk_parent_data pdata = { .hw = cmn_pll_hw };
+
+ hw = devm_clk_hw_register_fixed_rate_parent_data(dev,
+ fixed_clk[i].name,
+ &pdata, 0,
+ fixed_clk[i].rate);
+ break;
+ }
+ case CMN_PLL_CLK_NSS:
+ hw = ipq_cmn_pll_regmap_div_register(pdev, cmn_pll->regmap,
+ cmn_pll->div2_hw,
+ fixed_clk[i].name,
+ CMN_PLL_NSS_CLK_SEL);
+ break;
+ }
- hw = devm_clk_hw_register_fixed_rate_parent_data(dev,
- fixed_clk[i].name,
- &pdata, 0,
- fixed_clk[i].rate);
if (IS_ERR(hw))
return PTR_ERR(hw);
--
2.43.0