[PATCH v2 09/12] clk: qcom: ipq-cmn-pll: Add clock gate support for fixed clocks
Luo Jie <[email protected]> Thu, 06 Aug 2026 23:53:13 -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]> |
Some CMN PLL output clocks require a hardware gate in addition to a fixed rate. The gate control bits reside in the register CMN_PLL_OUTPUT_RELATED_1 (0x79c). Each such clock is registered via a new struct clk_fixed_gate and clk_fixed_gate_ops, combining a fixed rate with enable/disable/is_enabled gate control through the shared regmap for CMN_PLL_OUTPUT_RELATED_1. The enable_bit field is added to struct cmn_pll_fixed_output_clk (set to -1 for non-gated clocks). The CLK_PLL_OUTPUT_RAW() and CLK_PLL_GATE() helper macros are introduced alongside the existing CLK_PLL_OUTPUT(). Signed-off-by: Luo Jie <[email protected]> --- drivers/clk/qcom/ipq-cmn-pll.c | 141 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 136 insertions(+), 5 deletions(-) diff --git a/drivers/clk/qcom/ipq-cmn-pll.c b/drivers/clk/qcom/ipq-cmn-pll.c index 586f9c620e7e..2b05896ff00f 100644 --- a/drivers/clk/qcom/ipq-cmn-pll.c +++ b/drivers/clk/qcom/ipq-cmn-pll.c @@ -93,9 +93,19 @@ #define CMN_PLL_DIVIDER_CTRL 0x794 #define CMN_PLL_DIVIDER_CTRL_FACTOR GENMASK(9, 0) +/* Clock gate enable bits. */ +#define CMN_PLL_OUTPUT_RELATED_1 0x79c +#define CLK25M_EN_BIT 15 +#define CLK50M_EN_BIT3_BIT 14 +#define CLK250M_EN_BIT 13 +#define CLK31P25M_EN_BIT 12 +#define CLK50M_EN_BIT 11 +#define CLK50M_EN_BIT2_BIT 10 + /** * enum cmn_pll_clk_type - CMN PLL output clock registration type * @CMN_PLL_CLK_FIXED_RATE: plain fixed rate clock + * @CMN_PLL_CLK_FIXED_GATE: fixed rate clock with a hardware gate * @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 @@ -103,6 +113,7 @@ */ enum cmn_pll_clk_type { CMN_PLL_CLK_FIXED_RATE, + CMN_PLL_CLK_FIXED_GATE, CMN_PLL_CLK_NSS, CMN_PLL_CLK_PPE, CMN_PLL_CLK_PON, @@ -115,33 +126,66 @@ enum cmn_pll_clk_type { * @name: Clock name to be registered * @type: Clock registration type * @rate: Clock rate + * @enable_bit: Enable bit in CMN_PLL_OUTPUT_RELATED_1 for gate clock, + * -1 for non-gated clocks. */ struct cmn_pll_fixed_output_clk { unsigned int id; const char *name; enum cmn_pll_clk_type type; unsigned long rate; + int enable_bit; }; /** * struct clk_cmn_pll - CMN PLL hardware specific data * @regmap: hardware regmap. * @hw: handle between common and hardware-specific interfaces + * + * This structure is used for all CMN PLL-derived clocks including + * the main PLL, NSS clock, PPE clock, PON reference clock, and + * EPHY-RAW clock. */ struct clk_cmn_pll { struct regmap *regmap; struct clk_hw hw; }; -#define CLK_PLL_OUTPUT(_id, _name, _rate) { \ - .id = _id, \ - .name = _name, \ - .type = CMN_PLL_CLK_FIXED_RATE, \ - .rate = _rate, \ +/** + * struct clk_fixed_gate - fixed rate clock with a hardware gate + * @regmap: hardware regmap. + * @hw: handle between common and hardware-specific interfaces + * @rate: fixed clock rate. + * @enable_bit: enable bit in CMN_PLL_OUTPUT_RELATED_1, which is shared + * across multiple gate clocks, but regmap already serializes + * read-modify-write access to a given register, so no + * additional locking is needed here. + */ +struct clk_fixed_gate { + struct regmap *regmap; + struct clk_hw hw; + unsigned long rate; + int enable_bit; +}; + +#define CLK_PLL_OUTPUT_RAW(_id, _name, _type, _rate, _bit) { \ + .id = _id, \ + .name = _name, \ + .type = _type, \ + .rate = _rate, \ + .enable_bit = _bit, \ } +#define CLK_PLL_OUTPUT(_id, _name, _rate) \ + CLK_PLL_OUTPUT_RAW(_id, _name, CMN_PLL_CLK_FIXED_RATE, _rate, -1) + +#define CLK_PLL_GATE(_id, _name, _rate, _bit) \ + CLK_PLL_OUTPUT_RAW(_id, _name, CMN_PLL_CLK_FIXED_GATE, _rate, _bit) + #define to_clk_cmn_pll(_hw) container_of(_hw, struct clk_cmn_pll, hw) +#define to_clk_fixed_gate(_hw) container_of(_hw, struct clk_fixed_gate, hw) + static const struct regmap_config ipq_cmn_pll_regmap_config = { .reg_bits = 32, .reg_stride = 4, @@ -670,6 +714,85 @@ static struct clk_hw *ipq_cmn_pll_ephy_raw_register(struct platform_device *pdev return &ephy_raw_clk->hw; } +static int clk_fixed_gate_enable(struct clk_hw *hw) +{ + struct clk_fixed_gate *gate_clk = to_clk_fixed_gate(hw); + + return regmap_set_bits(gate_clk->regmap, CMN_PLL_OUTPUT_RELATED_1, + BIT(gate_clk->enable_bit)); +} + +static void clk_fixed_gate_disable(struct clk_hw *hw) +{ + struct clk_fixed_gate *gate_clk = to_clk_fixed_gate(hw); + + regmap_clear_bits(gate_clk->regmap, CMN_PLL_OUTPUT_RELATED_1, + BIT(gate_clk->enable_bit)); +} + +static int clk_fixed_gate_is_enabled(struct clk_hw *hw) +{ + struct clk_fixed_gate *gate_clk = to_clk_fixed_gate(hw); + + return regmap_test_bits(gate_clk->regmap, CMN_PLL_OUTPUT_RELATED_1, + BIT(gate_clk->enable_bit)); +} + +static unsigned long clk_fixed_gate_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_fixed_gate *gate_clk = to_clk_fixed_gate(hw); + + return gate_clk->rate; +} + +static const struct clk_ops clk_fixed_gate_ops = { + .enable = clk_fixed_gate_enable, + .disable = clk_fixed_gate_disable, + .is_enabled = clk_fixed_gate_is_enabled, + .recalc_rate = clk_fixed_gate_recalc_rate, +}; + +static struct clk_hw *ipq_cmn_pll_register_fixed_gate(struct device *dev, + const char *name, + struct clk_hw *parent_hw, + struct regmap *regmap, + unsigned long rate, + int enable_bit) +{ + struct clk_parent_data pdata = { .hw = parent_hw }; + struct clk_fixed_gate *gate_clk; + struct clk_init_data init = {}; + int ret; + + gate_clk = devm_kzalloc(dev, sizeof(*gate_clk), GFP_KERNEL); + if (!gate_clk) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.parent_data = &pdata; + init.num_parents = 1; + init.ops = &clk_fixed_gate_ops; + /* + * These gated clocks may be relied on by external hardware or + * bootloader-enabled paths without an in-kernel client driver. + * Add CLK_IGNORE_UNUSED so the clock framework does not disable + * them when no consumer has claimed them. + */ + init.flags = CLK_IGNORE_UNUSED; + + gate_clk->hw.init = &init; + gate_clk->regmap = regmap; + gate_clk->rate = rate; + gate_clk->enable_bit = enable_bit; + + ret = devm_clk_hw_register(dev, &gate_clk->hw); + if (ret) + return ERR_PTR(ret); + + return &gate_clk->hw; +} + static int ipq_cmn_pll_register_clks(struct platform_device *pdev) { const struct cmn_pll_fixed_output_clk *p, *fixed_clk; @@ -709,6 +832,14 @@ static int ipq_cmn_pll_register_clks(struct platform_device *pdev) hw = ERR_PTR(-EINVAL); switch (fixed_clk[i].type) { + case CMN_PLL_CLK_FIXED_GATE: + hw = ipq_cmn_pll_register_fixed_gate(dev, + fixed_clk[i].name, + cmn_pll_hw, + cmn_pll->regmap, + fixed_clk[i].rate, + fixed_clk[i].enable_bit); + break; case CMN_PLL_CLK_FIXED_RATE: { struct clk_parent_data pdata = { .hw = cmn_pll_hw }; -- 2.43.0