[PATCH v2 09/12] clk: qcom: ipq-cmn-pll: Add clock gate support for fixed clocks
From: Luo Jie
Date: Fri Aug 07 2026 - 02:59:11 EST
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 <jie.luo@xxxxxxxxxxxxxxxx>
---
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