[PATCH v2 05/12] clk: qcom: ipq-cmn-pll: Add NSS clock support

From: Luo Jie

Date: Fri Aug 07 2026 - 02:57:21 EST


The NSS (network subsystem) clock is derived from the CMN PLL output
divided by 2 and then further divided by a configurable 6-bit divider.

This is functionally identical to clk_regmap_div_ops, aside from the
implicit divide-by-2, which the new CLK_DIVIDER_EVEN_INTEGERS_NO_OFFSET
flag now expresses. 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,
and drop the [8, 63] range clamp that the hand-rolled implementation
enforced, since the full 6-bit register range is now allowed, matching
clk_regmap_div_ops's own behavior. 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 | 80 +++++++++++++++++++++++++++++++++++++++---
1 file changed, 75 insertions(+), 5 deletions(-)

diff --git a/drivers/clk/qcom/ipq-cmn-pll.c b/drivers/clk/qcom/ipq-cmn-pll.c
index 4fdf78f6829e..c1b00699eb72 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;
};

@@ -105,6 +123,7 @@ struct clk_cmn_pll {
#define CLK_PLL_OUTPUT(_id, _name, _rate) { \
.id = _id, \
.name = _name, \
+ .type = CMN_PLL_CLK_FIXED_RATE, \
.rate = _rate, \
}

@@ -357,11 +376,46 @@ 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 *cmn_pll_hw,
+ const char *name,
+ u32 field_mask)
+{
+ struct clk_parent_data pdata = { .hw = cmn_pll_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);
+ div_clk->flags = CLK_DIVIDER_EVEN_INTEGERS_NO_OFFSET;
+ 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;
struct clk_hw_onecell_data *hw_data;
struct device *dev = &pdev->dev;
+ struct clk_cmn_pll *cmn_pll;
struct clk_hw *cmn_pll_hw;
unsigned int num_clks;
struct clk_hw *hw;
@@ -388,14 +442,30 @@ static int ipq_cmn_pll_register_clks(struct platform_device *pdev)
if (IS_ERR(cmn_pll_hw))
return PTR_ERR(cmn_pll_hw);

+ cmn_pll = to_clk_cmn_pll(cmn_pll_hw);
+
/* 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_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