[PATCH v2 05/12] clk: qcom: ipq-cmn-pll: Add NSS clock support
Luo Jie <[email protected]> Thu, 06 Aug 2026 23:53:09 -0700
| Newsgroups | org.kernel.vger.linux-clk,org.kernel.vger.linux-arm-msm,org.kernel.vger.linux-devicetree,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
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 <[email protected]> --- 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