* [PATCH v2 01/12] clk: qcom: ipq-cmn-pll: Use devm_clk_hw_register_fixed_rate_parent_data
2026-08-07 6:53 [PATCH v2 00/12] clk: qcom: ipq-cmn-pll: Add IPQ5210 CMN PLL support Luo Jie
@ 2026-08-07 6:53 ` Luo Jie
2026-08-07 6:53 ` [PATCH v2 02/12] dt-bindings: clock: qcom: Add CMN PLL support for IPQ5210 SoC Luo Jie
` (10 subsequent siblings)
11 siblings, 0 replies; 15+ messages in thread
From: Luo Jie @ 2026-08-07 6:53 UTC (permalink / raw)
To: Bjorn Andersson, Michael Turquette, Stephen Boyd, Brian Masney,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Luo Jie,
Konrad Dybcio
Cc: linux-arm-msm, linux-clk, linux-kernel, devicetree, quic_kkumarcs,
quic_linchen, quic_leiwei, quic_suruchia, Konrad Dybcio
Replace clk_hw_register_fixed_rate_parent_hw() with the managed
devm_clk_hw_register_fixed_rate_parent_data() so the fixed-rate output
clocks are automatically unregistered when the device is unbound.
Since all clocks in the driver are now registered via devm_ variants,
the remove callback 'ipq_cmn_pll_clk_remove()' and the manual
'unregister_fixed_clk' error-path loop are no longer needed and are
removed.
Signed-off-by: Luo Jie <jie.luo@oss.qualcomm.com>
Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
---
drivers/clk/qcom/ipq-cmn-pll.c | 39 +++++++++------------------------------
1 file changed, 9 insertions(+), 30 deletions(-)
diff --git a/drivers/clk/qcom/ipq-cmn-pll.c b/drivers/clk/qcom/ipq-cmn-pll.c
index dafe8c1738df..4fdf78f6829e 100644
--- a/drivers/clk/qcom/ipq-cmn-pll.c
+++ b/drivers/clk/qcom/ipq-cmn-pll.c
@@ -390,13 +390,14 @@ static int ipq_cmn_pll_register_clks(struct platform_device *pdev)
/* Register the fixed rate output clocks. */
for (i = 0; i < num_clks; i++) {
- hw = clk_hw_register_fixed_rate_parent_hw(dev, fixed_clk[i].name,
- cmn_pll_hw, 0,
- fixed_clk[i].rate);
- if (IS_ERR(hw)) {
- ret = PTR_ERR(hw);
- goto unregister_fixed_clk;
- }
+ 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);
+ if (IS_ERR(hw))
+ return PTR_ERR(hw);
hw_data->hws[fixed_clk[i].id] = hw;
}
@@ -410,17 +411,11 @@ static int ipq_cmn_pll_register_clks(struct platform_device *pdev)
ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, hw_data);
if (ret)
- goto unregister_fixed_clk;
+ return ret;
platform_set_drvdata(pdev, hw_data);
return 0;
-
-unregister_fixed_clk:
- while (i > 0)
- clk_hw_unregister(hw_data->hws[fixed_clk[--i].id]);
-
- return ret;
}
static int ipq_cmn_pll_clk_probe(struct platform_device *pdev)
@@ -462,21 +457,6 @@ static int ipq_cmn_pll_clk_probe(struct platform_device *pdev)
return 0;
}
-static void ipq_cmn_pll_clk_remove(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *hw_data = platform_get_drvdata(pdev);
- int i;
-
- /*
- * The clock with index CMN_PLL_CLK is unregistered by
- * device management.
- */
- for (i = 0; i < hw_data->num; i++) {
- if (i != CMN_PLL_CLK)
- clk_hw_unregister(hw_data->hws[i]);
- }
-}
-
static const struct dev_pm_ops ipq_cmn_pll_pm_ops = {
SET_RUNTIME_PM_OPS(pm_clk_suspend, pm_clk_resume, NULL)
};
@@ -494,7 +474,6 @@ MODULE_DEVICE_TABLE(of, ipq_cmn_pll_clk_ids);
static struct platform_driver ipq_cmn_pll_clk_driver = {
.probe = ipq_cmn_pll_clk_probe,
- .remove = ipq_cmn_pll_clk_remove,
.driver = {
.name = "ipq_cmn_pll",
.of_match_table = ipq_cmn_pll_clk_ids,
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PATCH v2 02/12] dt-bindings: clock: qcom: Add CMN PLL support for IPQ5210 SoC
2026-08-07 6:53 [PATCH v2 00/12] clk: qcom: ipq-cmn-pll: Add IPQ5210 CMN PLL support Luo Jie
2026-08-07 6:53 ` [PATCH v2 01/12] clk: qcom: ipq-cmn-pll: Use devm_clk_hw_register_fixed_rate_parent_data Luo Jie
@ 2026-08-07 6:53 ` Luo Jie
2026-08-07 6:53 ` [PATCH v2 03/12] clk: divider: Introduce CLK_DIVIDER_EVEN_INTEGERS_NO_OFFSET flag Luo Jie
` (9 subsequent siblings)
11 siblings, 0 replies; 15+ messages in thread
From: Luo Jie @ 2026-08-07 6:53 UTC (permalink / raw)
To: Bjorn Andersson, Michael Turquette, Stephen Boyd, Brian Masney,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Luo Jie,
Konrad Dybcio
Cc: linux-arm-msm, linux-clk, linux-kernel, devicetree, quic_kkumarcs,
quic_linchen, quic_leiwei, quic_suruchia
Add the compatible string for the IPQ5210 CMN PLL clock controller and
the dt-bindings clock ID header for IPQ5210.
The IPQ5210 CMN PLL supplies configurable clocks to the NSS (network
subsystem), PPE (packet process engine), and PON (passive optical
network) hardware blocks. It also supplies a raw EPHY output clock for
the integrated Ethernet PHY and several gated fixed-rate outputs to
Ethernet PHY and switch devices.
Signed-off-by: Luo Jie <jie.luo@oss.qualcomm.com>
---
.../bindings/clock/qcom,ipq9574-cmn-pll.yaml | 1 +
include/dt-bindings/clock/qcom,ipq5210-cmn-pll.h | 30 ++++++++++++++++++++++
2 files changed, 31 insertions(+)
diff --git a/Documentation/devicetree/bindings/clock/qcom,ipq9574-cmn-pll.yaml b/Documentation/devicetree/bindings/clock/qcom,ipq9574-cmn-pll.yaml
index a4f9af8fa187..91e17f126a9b 100644
--- a/Documentation/devicetree/bindings/clock/qcom,ipq9574-cmn-pll.yaml
+++ b/Documentation/devicetree/bindings/clock/qcom,ipq9574-cmn-pll.yaml
@@ -25,6 +25,7 @@ properties:
compatible:
enum:
- qcom,ipq5018-cmn-pll
+ - qcom,ipq5210-cmn-pll
- qcom,ipq5332-cmn-pll
- qcom,ipq5424-cmn-pll
- qcom,ipq6018-cmn-pll
diff --git a/include/dt-bindings/clock/qcom,ipq5210-cmn-pll.h b/include/dt-bindings/clock/qcom,ipq5210-cmn-pll.h
new file mode 100644
index 000000000000..191589fd5311
--- /dev/null
+++ b/include/dt-bindings/clock/qcom,ipq5210-cmn-pll.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#ifndef _DT_BINDINGS_CLK_QCOM_IPQ5210_CMN_PLL_H
+#define _DT_BINDINGS_CLK_QCOM_IPQ5210_CMN_PLL_H
+
+/* Parent clock */
+#define IPQ5210_CMN_PLL_CLK 0
+
+/* Fixed-rate clocks */
+#define IPQ5210_XO_24MHZ_CLK 1
+#define IPQ5210_SLEEP_32KHZ_CLK 2
+
+/* Configurable divider clocks */
+#define IPQ5210_NSS_CLK 3
+#define IPQ5210_PPE_CLK 4
+#define IPQ5210_PON_REFCLK 5
+#define IPQ5210_EPHY_RAW_CLK 6
+
+/* Gate clocks */
+#define IPQ5210_PCS_31P25MHZ_CLK 7
+#define IPQ5210_ETH0_50MHZ_CLK 8
+#define IPQ5210_ETH1_50MHZ_CLK 9
+#define IPQ5210_ETH2_50MHZ_CLK 10
+#define IPQ5210_EPHY_50MHZ_CLK 11
+#define IPQ5210_ETH_25MHZ_CLK 12
+
+#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PATCH v2 03/12] clk: divider: Introduce CLK_DIVIDER_EVEN_INTEGERS_NO_OFFSET flag
2026-08-07 6:53 [PATCH v2 00/12] clk: qcom: ipq-cmn-pll: Add IPQ5210 CMN PLL support Luo Jie
2026-08-07 6:53 ` [PATCH v2 01/12] clk: qcom: ipq-cmn-pll: Use devm_clk_hw_register_fixed_rate_parent_data Luo Jie
2026-08-07 6:53 ` [PATCH v2 02/12] dt-bindings: clock: qcom: Add CMN PLL support for IPQ5210 SoC Luo Jie
@ 2026-08-07 6:53 ` Luo Jie
2026-08-10 17:17 ` Brian Masney
2026-08-07 6:53 ` [PATCH v2 04/12] clk: qcom: clk-regmap-divider: Support CLK_DIVIDER_* flags Luo Jie
` (8 subsequent siblings)
11 siblings, 1 reply; 15+ messages in thread
From: Luo Jie @ 2026-08-07 6:53 UTC (permalink / raw)
To: Bjorn Andersson, Michael Turquette, Stephen Boyd, Brian Masney,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Luo Jie,
Konrad Dybcio
Cc: linux-arm-msm, linux-clk, linux-kernel, devicetree, quic_kkumarcs,
quic_linchen, quic_leiwei, quic_suruchia
Some hardware dividers derive an even divisor directly from the raw
register value, i.e. divisor = 2 * val, with no +1 offset. This differs
from CLK_DIVIDER_EVEN_INTEGERS, which is one-based (divisor = 2 *
(val + 1)).
Add CLK_DIVIDER_EVEN_INTEGERS_NO_OFFSET for this variant, updating
_get_maxdiv()/_get_div()/_get_val() accordingly. _is_valid_div() is
extended to also reject odd divisors, since _get_val()'s `div >> 1`
would otherwise silently decode them to the wrong even value (e.g. 3
-> 1 -> 2).
To avoid regressing rate requests that previously computed an odd
divisor (which used to succeed with a silently wrong rate), round to
the nearest even divisor in _div_round_up(), _div_round_closest(), and
_next_div(), mirroring how CLK_DIVIDER_POWER_OF_TWO already rounds to
the nearest power of two.
Also guard divider_ro_determine_rate() against a zero divisor, which
can occur from an unprogrammed/reset register, the same way
divider_recalc_rate() already does: WARN unless CLK_DIVIDER_ALLOW_ZERO
is set, and return -EINVAL.
Signed-off-by: Luo Jie <jie.luo@oss.qualcomm.com>
---
drivers/clk/clk-divider.c | 22 ++++++++++++++++++++++
include/linux/clk-provider.h | 3 +++
2 files changed, 25 insertions(+)
diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
index b3b485d23ea8..f062d77ee106 100644
--- a/drivers/clk/clk-divider.c
+++ b/drivers/clk/clk-divider.c
@@ -74,6 +74,8 @@ static unsigned int _get_maxdiv(const struct clk_div_table *table, u8 width,
return 1 << clk_div_mask(width);
if (flags & CLK_DIVIDER_EVEN_INTEGERS)
return 2 * (clk_div_mask(width) + 1);
+ if (flags & CLK_DIVIDER_EVEN_INTEGERS_NO_OFFSET)
+ return 2 * clk_div_mask(width);
if (table)
return _get_table_maxdiv(table, width);
return clk_div_mask(width) + 1;
@@ -101,6 +103,8 @@ static unsigned int _get_div(const struct clk_div_table *table,
return val ? val : clk_div_mask(width) + 1;
if (flags & CLK_DIVIDER_EVEN_INTEGERS)
return 2 * (val + 1);
+ if (flags & CLK_DIVIDER_EVEN_INTEGERS_NO_OFFSET)
+ return 2 * val;
if (table)
return _get_table_div(table, val);
return val + 1;
@@ -128,6 +132,8 @@ static unsigned int _get_val(const struct clk_div_table *table,
return (div == clk_div_mask(width) + 1) ? 0 : div;
if (flags & CLK_DIVIDER_EVEN_INTEGERS)
return (div >> 1) - 1;
+ if (flags & CLK_DIVIDER_EVEN_INTEGERS_NO_OFFSET)
+ return div >> 1;
if (table)
return _get_table_val(table, div);
return div - 1;
@@ -181,6 +187,8 @@ static bool _is_valid_div(const struct clk_div_table *table, unsigned int div,
{
if (flags & CLK_DIVIDER_POWER_OF_TWO)
return is_power_of_2(div);
+ if (flags & CLK_DIVIDER_EVEN_INTEGERS_NO_OFFSET)
+ return div >= 2 && !(div & 1);
if (table)
return _is_valid_table_div(table, div);
return true;
@@ -230,6 +238,8 @@ static int _div_round_up(const struct clk_div_table *table,
if (flags & CLK_DIVIDER_POWER_OF_TWO)
div = __roundup_pow_of_two(div);
+ else if (flags & CLK_DIVIDER_EVEN_INTEGERS_NO_OFFSET)
+ div = max(2, (div + 1) & ~1);
if (table)
div = _round_up_table(table, div);
@@ -249,6 +259,9 @@ static int _div_round_closest(const struct clk_div_table *table,
if (flags & CLK_DIVIDER_POWER_OF_TWO) {
up = __roundup_pow_of_two(up);
down = __rounddown_pow_of_two(down);
+ } else if (flags & CLK_DIVIDER_EVEN_INTEGERS_NO_OFFSET) {
+ up = max(2, (up + 1) & ~1);
+ down = max(2, down & ~1);
} else if (table) {
up = _round_up_table(table, up);
down = _round_down_table(table, down);
@@ -286,6 +299,8 @@ static int _next_div(const struct clk_div_table *table, int div,
if (flags & CLK_DIVIDER_POWER_OF_TWO)
return __roundup_pow_of_two(div);
+ if (flags & CLK_DIVIDER_EVEN_INTEGERS_NO_OFFSET)
+ return div + (div & 1);
if (table)
return _round_up_table(table, div);
@@ -372,6 +387,13 @@ int divider_ro_determine_rate(struct clk_hw *hw, struct clk_rate_request *req,
div = _get_div(table, val, flags, width);
+ if (!div) {
+ WARN(!(flags & CLK_DIVIDER_ALLOW_ZERO),
+ "%s: Zero divisor and CLK_DIVIDER_ALLOW_ZERO not set\n",
+ clk_hw_get_name(hw));
+ return -EINVAL;
+ }
+
/* Even a read-only clock can propagate a rate change */
if (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) {
if (!req->best_parent_hw)
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index b01a38fef8cf..368970d6a12a 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -703,6 +703,8 @@ struct clk_div_table {
* big endian.
* CLK_DIVIDER_EVEN_INTEGERS - clock divisor is 2, 4, 6, 8, 10, etc.
* Formula is 2 * (value read from hardware + 1).
+ * CLK_DIVIDER_EVEN_INTEGERS_NO_OFFSET - clock divisor is 2, 4, 6, 8, etc.
+ * Formula is 2 * (value read from hardware).
*/
struct clk_divider {
struct clk_hw hw;
@@ -726,6 +728,7 @@ struct clk_divider {
#define CLK_DIVIDER_MAX_AT_ZERO BIT(6)
#define CLK_DIVIDER_BIG_ENDIAN BIT(7)
#define CLK_DIVIDER_EVEN_INTEGERS BIT(8)
+#define CLK_DIVIDER_EVEN_INTEGERS_NO_OFFSET BIT(9)
extern const struct clk_ops clk_divider_ops;
extern const struct clk_ops clk_divider_ro_ops;
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* Re: [PATCH v2 03/12] clk: divider: Introduce CLK_DIVIDER_EVEN_INTEGERS_NO_OFFSET flag
2026-08-07 6:53 ` [PATCH v2 03/12] clk: divider: Introduce CLK_DIVIDER_EVEN_INTEGERS_NO_OFFSET flag Luo Jie
@ 2026-08-10 17:17 ` Brian Masney
2026-08-11 13:46 ` Jie Luo
0 siblings, 1 reply; 15+ messages in thread
From: Brian Masney @ 2026-08-10 17:17 UTC (permalink / raw)
To: Luo Jie
Cc: Bjorn Andersson, Michael Turquette, Stephen Boyd, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Konrad Dybcio, linux-arm-msm,
linux-clk, linux-kernel, devicetree, quic_kkumarcs, quic_linchen,
quic_leiwei, quic_suruchia
Hi Luo,
On Thu, Aug 06, 2026 at 11:53:07PM -0700, Luo Jie wrote:
> Some hardware dividers derive an even divisor directly from the raw
> register value, i.e. divisor = 2 * val, with no +1 offset. This differs
> from CLK_DIVIDER_EVEN_INTEGERS, which is one-based (divisor = 2 *
> (val + 1)).
>
> Add CLK_DIVIDER_EVEN_INTEGERS_NO_OFFSET for this variant, updating
> _get_maxdiv()/_get_div()/_get_val() accordingly. _is_valid_div() is
> extended to also reject odd divisors, since _get_val()'s `div >> 1`
> would otherwise silently decode them to the wrong even value (e.g. 3
> -> 1 -> 2).
>
> To avoid regressing rate requests that previously computed an odd
> divisor (which used to succeed with a silently wrong rate), round to
> the nearest even divisor in _div_round_up(), _div_round_closest(), and
> _next_div(), mirroring how CLK_DIVIDER_POWER_OF_TWO already rounds to
> the nearest power of two.
>
> Also guard divider_ro_determine_rate() against a zero divisor, which
> can occur from an unprogrammed/reset register, the same way
> divider_recalc_rate() already does: WARN unless CLK_DIVIDER_ALLOW_ZERO
> is set, and return -EINVAL.
>
> Signed-off-by: Luo Jie <jie.luo@oss.qualcomm.com>
One of the other patches in your series says:
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.
Can you register a fixed-factor-clock parent with /2, then use
CLK_DIVIDER_ONE_BASED? That should yield the same results and it may
more accurately represent the hardware as well.
Brian
^ permalink raw reply [flat|nested] 15+ messages in thread* Re: [PATCH v2 03/12] clk: divider: Introduce CLK_DIVIDER_EVEN_INTEGERS_NO_OFFSET flag
2026-08-10 17:17 ` Brian Masney
@ 2026-08-11 13:46 ` Jie Luo
0 siblings, 0 replies; 15+ messages in thread
From: Jie Luo @ 2026-08-11 13:46 UTC (permalink / raw)
To: Brian Masney
Cc: Bjorn Andersson, Michael Turquette, Stephen Boyd, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Konrad Dybcio, linux-arm-msm,
linux-clk, linux-kernel, devicetree, quic_kkumarcs, quic_linchen,
quic_leiwei, quic_suruchia
On 8/11/2026 1:17 AM, Brian Masney wrote:
> Hi Luo,
>
> On Thu, Aug 06, 2026 at 11:53:07PM -0700, Luo Jie wrote:
>> Some hardware dividers derive an even divisor directly from the raw
>> register value, i.e. divisor = 2 * val, with no +1 offset. This differs
>> from CLK_DIVIDER_EVEN_INTEGERS, which is one-based (divisor = 2 *
>> (val + 1)).
>>
>> Add CLK_DIVIDER_EVEN_INTEGERS_NO_OFFSET for this variant, updating
>> _get_maxdiv()/_get_div()/_get_val() accordingly. _is_valid_div() is
>> extended to also reject odd divisors, since _get_val()'s `div >> 1`
>> would otherwise silently decode them to the wrong even value (e.g. 3
>> -> 1 -> 2).
>>
>> To avoid regressing rate requests that previously computed an odd
>> divisor (which used to succeed with a silently wrong rate), round to
>> the nearest even divisor in _div_round_up(), _div_round_closest(), and
>> _next_div(), mirroring how CLK_DIVIDER_POWER_OF_TWO already rounds to
>> the nearest power of two.
>>
>> Also guard divider_ro_determine_rate() against a zero divisor, which
>> can occur from an unprogrammed/reset register, the same way
>> divider_recalc_rate() already does: WARN unless CLK_DIVIDER_ALLOW_ZERO
>> is set, and return -EINVAL.
>>
>> Signed-off-by: Luo Jie <jie.luo@oss.qualcomm.com>
>
> One of the other patches in your series says:
>
> 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.
>
> Can you register a fixed-factor-clock parent with /2, then use
> CLK_DIVIDER_ONE_BASED? That should yield the same results and it may
> more accurately represent the hardware as well.
>
> Brian
>
Thanks for the suggestion. I will model this by registering a fixed-
factor clock with a /2 factor and using CLK_DIVIDER_ONE_BASED for the
subsequent 6-bit divider. This will preserve the current frequency
calculation while making the clock topology more accurately reflect the
hardware implementation.
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v2 04/12] clk: qcom: clk-regmap-divider: Support CLK_DIVIDER_* flags
2026-08-07 6:53 [PATCH v2 00/12] clk: qcom: ipq-cmn-pll: Add IPQ5210 CMN PLL support Luo Jie
` (2 preceding siblings ...)
2026-08-07 6:53 ` [PATCH v2 03/12] clk: divider: Introduce CLK_DIVIDER_EVEN_INTEGERS_NO_OFFSET flag Luo Jie
@ 2026-08-07 6:53 ` Luo Jie
2026-08-07 6:53 ` [PATCH v2 05/12] clk: qcom: ipq-cmn-pll: Add NSS clock support Luo Jie
` (7 subsequent siblings)
11 siblings, 0 replies; 15+ messages in thread
From: Luo Jie @ 2026-08-07 6:53 UTC (permalink / raw)
To: Bjorn Andersson, Michael Turquette, Stephen Boyd, Brian Masney,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Luo Jie,
Konrad Dybcio
Cc: linux-arm-msm, linux-clk, linux-kernel, devicetree, quic_kkumarcs,
quic_linchen, quic_leiwei, quic_suruchia
Add a flags field to struct clk_regmap_div and OR it into the
CLK_DIVIDER_ROUND_CLOSEST flag passed to divider_determine_rate(),
divider_ro_determine_rate(), divider_get_val(), and
divider_recalc_rate(), so that qcom drivers can opt into
CLK_DIVIDER_* behavior variants such as
CLK_DIVIDER_EVEN_INTEGERS_NO_OFFSET.
This is additive and backward compatible: existing clk_regmap_div
users don't initialize .flags, so it defaults to 0, and
CLK_DIVIDER_ROUND_CLOSEST | 0 is identical to current behavior.
divider_get_val() can now return -EINVAL once a caller sets a
divisor-constraining flag such as CLK_DIVIDER_EVEN_INTEGERS_NO_OFFSET
(previously unreachable, since no flag passed here constrained
_is_valid_div()). Check for this in div_set_rate() before using the
result, mirroring clk_divider_set_rate()'s existing handling of the
same return value.
Signed-off-by: Luo Jie <jie.luo@oss.qualcomm.com>
---
drivers/clk/qcom/clk-regmap-divider.c | 14 +++++++++-----
drivers/clk/qcom/clk-regmap-divider.h | 1 +
2 files changed, 10 insertions(+), 5 deletions(-)
diff --git a/drivers/clk/qcom/clk-regmap-divider.c b/drivers/clk/qcom/clk-regmap-divider.c
index 672e82caf205..fe4542b5bd58 100644
--- a/drivers/clk/qcom/clk-regmap-divider.c
+++ b/drivers/clk/qcom/clk-regmap-divider.c
@@ -27,7 +27,8 @@ static int div_ro_determine_rate(struct clk_hw *hw,
val &= BIT(divider->width) - 1;
return divider_ro_determine_rate(hw, req, NULL, divider->width,
- CLK_DIVIDER_ROUND_CLOSEST, val);
+ CLK_DIVIDER_ROUND_CLOSEST | divider->flags,
+ val);
}
static int div_determine_rate(struct clk_hw *hw, struct clk_rate_request *req)
@@ -35,7 +36,7 @@ static int div_determine_rate(struct clk_hw *hw, struct clk_rate_request *req)
struct clk_regmap_div *divider = to_clk_regmap_div(hw);
return divider_determine_rate(hw, req, NULL, divider->width,
- CLK_DIVIDER_ROUND_CLOSEST);
+ CLK_DIVIDER_ROUND_CLOSEST | divider->flags);
}
static int div_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -43,10 +44,12 @@ static int div_set_rate(struct clk_hw *hw, unsigned long rate,
{
struct clk_regmap_div *divider = to_clk_regmap_div(hw);
struct clk_regmap *clkr = ÷r->clkr;
- u32 div;
+ int div;
div = divider_get_val(rate, parent_rate, NULL, divider->width,
- CLK_DIVIDER_ROUND_CLOSEST);
+ CLK_DIVIDER_ROUND_CLOSEST | divider->flags);
+ if (div < 0)
+ return div;
return regmap_update_bits(clkr->regmap, divider->reg,
(BIT(divider->width) - 1) << divider->shift,
@@ -65,7 +68,8 @@ static unsigned long div_recalc_rate(struct clk_hw *hw,
div &= BIT(divider->width) - 1;
return divider_recalc_rate(hw, parent_rate, div, NULL,
- CLK_DIVIDER_ROUND_CLOSEST, divider->width);
+ CLK_DIVIDER_ROUND_CLOSEST | divider->flags,
+ divider->width);
}
const struct clk_ops clk_regmap_div_ops = {
diff --git a/drivers/clk/qcom/clk-regmap-divider.h b/drivers/clk/qcom/clk-regmap-divider.h
index e75a65c3839c..4fde766b2633 100644
--- a/drivers/clk/qcom/clk-regmap-divider.h
+++ b/drivers/clk/qcom/clk-regmap-divider.h
@@ -13,6 +13,7 @@ struct clk_regmap_div {
u32 reg;
u32 shift;
u32 width;
+ unsigned long flags;
struct clk_regmap clkr;
};
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PATCH v2 05/12] clk: qcom: ipq-cmn-pll: Add NSS clock support
2026-08-07 6:53 [PATCH v2 00/12] clk: qcom: ipq-cmn-pll: Add IPQ5210 CMN PLL support Luo Jie
` (3 preceding siblings ...)
2026-08-07 6:53 ` [PATCH v2 04/12] clk: qcom: clk-regmap-divider: Support CLK_DIVIDER_* flags Luo Jie
@ 2026-08-07 6:53 ` Luo Jie
2026-08-07 6:53 ` [PATCH v2 06/12] clk: qcom: ipq-cmn-pll: Add PPE " Luo Jie
` (6 subsequent siblings)
11 siblings, 0 replies; 15+ messages in thread
From: Luo Jie @ 2026-08-07 6:53 UTC (permalink / raw)
To: Bjorn Andersson, Michael Turquette, Stephen Boyd, Brian Masney,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Luo Jie,
Konrad Dybcio
Cc: linux-arm-msm, linux-clk, linux-kernel, devicetree, quic_kkumarcs,
quic_linchen, quic_leiwei, quic_suruchia
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@oss.qualcomm.com>
---
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
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PATCH v2 06/12] clk: qcom: ipq-cmn-pll: Add PPE clock support
2026-08-07 6:53 [PATCH v2 00/12] clk: qcom: ipq-cmn-pll: Add IPQ5210 CMN PLL support Luo Jie
` (4 preceding siblings ...)
2026-08-07 6:53 ` [PATCH v2 05/12] clk: qcom: ipq-cmn-pll: Add NSS clock support Luo Jie
@ 2026-08-07 6:53 ` Luo Jie
2026-08-07 6:53 ` [PATCH v2 07/12] clk: qcom: ipq-cmn-pll: Add PON reference " Luo Jie
` (5 subsequent siblings)
11 siblings, 0 replies; 15+ messages in thread
From: Luo Jie @ 2026-08-07 6:53 UTC (permalink / raw)
To: Bjorn Andersson, Michael Turquette, Stephen Boyd, Brian Masney,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Luo Jie,
Konrad Dybcio
Cc: linux-arm-msm, linux-clk, linux-kernel, devicetree, quic_kkumarcs,
quic_linchen, quic_leiwei, quic_suruchia
The PPE (packet process engine) clock is derived from the CMN PLL
output divided by 2 and then further divided by a configurable 6-bit
divider. It shares the CMN_PLL_NSS_PPE_FREQ_CTRL register with the
NSS clock but uses a different field (bits [5:0]).
Register the PPE clock through the ipq_cmn_pll_regmap_div_register()
helper introduced for the NSS clock, passing the PPE-specific field
mask.
Signed-off-by: Luo Jie <jie.luo@oss.qualcomm.com>
---
drivers/clk/qcom/ipq-cmn-pll.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/drivers/clk/qcom/ipq-cmn-pll.c b/drivers/clk/qcom/ipq-cmn-pll.c
index c1b00699eb72..a24e57b33c6b 100644
--- a/drivers/clk/qcom/ipq-cmn-pll.c
+++ b/drivers/clk/qcom/ipq-cmn-pll.c
@@ -71,6 +71,7 @@
#define CMN_PLL_NSS_PPE_FREQ_CTRL 0x98
#define CMN_PLL_NSS_CLK_SEL GENMASK(13, 8)
+#define CMN_PLL_PPE_CLK_SEL GENMASK(5, 0)
#define CMN_PLL_POWER_ON_AND_RESET 0x780
#define CMN_ANA_EN_SW_RSTN BIT(6)
@@ -90,10 +91,12 @@
* 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
+ * @CMN_PLL_CLK_PPE: PPE clock with configurable divider
*/
enum cmn_pll_clk_type {
CMN_PLL_CLK_FIXED_RATE,
CMN_PLL_CLK_NSS,
+ CMN_PLL_CLK_PPE,
};
/**
@@ -464,6 +467,12 @@ static int ipq_cmn_pll_register_clks(struct platform_device *pdev)
fixed_clk[i].name,
CMN_PLL_NSS_CLK_SEL);
break;
+ case CMN_PLL_CLK_PPE:
+ hw = ipq_cmn_pll_regmap_div_register(pdev, cmn_pll->regmap,
+ cmn_pll_hw,
+ fixed_clk[i].name,
+ CMN_PLL_PPE_CLK_SEL);
+ break;
}
if (IS_ERR(hw))
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PATCH v2 07/12] clk: qcom: ipq-cmn-pll: Add PON reference clock support
2026-08-07 6:53 [PATCH v2 00/12] clk: qcom: ipq-cmn-pll: Add IPQ5210 CMN PLL support Luo Jie
` (5 preceding siblings ...)
2026-08-07 6:53 ` [PATCH v2 06/12] clk: qcom: ipq-cmn-pll: Add PPE " Luo Jie
@ 2026-08-07 6:53 ` Luo Jie
2026-08-07 6:53 ` [PATCH v2 08/12] clk: qcom: ipq-cmn-pll: Add EPHY-RAW " Luo Jie
` (4 subsequent siblings)
11 siblings, 0 replies; 15+ messages in thread
From: Luo Jie @ 2026-08-07 6:53 UTC (permalink / raw)
To: Bjorn Andersson, Michael Turquette, Stephen Boyd, Brian Masney,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Luo Jie,
Konrad Dybcio
Cc: linux-arm-msm, linux-clk, linux-kernel, devicetree, quic_kkumarcs,
quic_linchen, quic_leiwei, quic_suruchia
The PON (Passive Optical Network) reference clock is derived from the
CMN PLL output divided by 2 and then further divided by a configurable
8-bit divider (1-255). The clock supports two modes:
- UNIPHY mode (CMN_PLL_PON_MODE_SEL = 0): fixed 31.25 MHz output
- PON mode (CMN_PLL_PON_MODE_SEL = 1): pll_rate / (2 * divider)
The clock output can be independently enabled and disabled via the
CMN_PLL_PON_EN bit.
Signed-off-by: Luo Jie <jie.luo@oss.qualcomm.com>
---
drivers/clk/qcom/ipq-cmn-pll.c | 171 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 171 insertions(+)
diff --git a/drivers/clk/qcom/ipq-cmn-pll.c b/drivers/clk/qcom/ipq-cmn-pll.c
index a24e57b33c6b..356988c03a7a 100644
--- a/drivers/clk/qcom/ipq-cmn-pll.c
+++ b/drivers/clk/qcom/ipq-cmn-pll.c
@@ -73,6 +73,11 @@
#define CMN_PLL_NSS_CLK_SEL GENMASK(13, 8)
#define CMN_PLL_PPE_CLK_SEL GENMASK(5, 0)
+#define CMN_PLL_PON_CONFIG 0x42c
+#define CMN_PLL_PON_MODE_SEL BIT(9)
+#define CMN_PLL_PON_EN BIT(8)
+#define CMN_PLL_PON_DIV_CTRL GENMASK(7, 0)
+
#define CMN_PLL_POWER_ON_AND_RESET 0x780
#define CMN_ANA_EN_SW_RSTN BIT(6)
@@ -92,11 +97,13 @@
* @CMN_PLL_CLK_FIXED_RATE: plain fixed rate clock
* @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
*/
enum cmn_pll_clk_type {
CMN_PLL_CLK_FIXED_RATE,
CMN_PLL_CLK_NSS,
CMN_PLL_CLK_PPE,
+ CMN_PLL_CLK_PON,
};
/**
@@ -413,6 +420,164 @@ static struct clk_hw *ipq_cmn_pll_regmap_div_register(struct platform_device *pd
return &div_clk->clkr.hw;
}
+/*
+ * PON (Passive Optical Network) reference clock operations.
+ * The PON refclk is derived from CMN PLL rate / 2, then divided by
+ * a configurable 8-bit divider (1-255).
+ */
+static int clk_pon_refclk_enable(struct clk_hw *hw)
+{
+ struct clk_cmn_pll *pon_clk = to_clk_cmn_pll(hw);
+
+ return regmap_set_bits(pon_clk->regmap, CMN_PLL_PON_CONFIG,
+ CMN_PLL_PON_EN);
+}
+
+static void clk_pon_refclk_disable(struct clk_hw *hw)
+{
+ struct clk_cmn_pll *pon_clk = to_clk_cmn_pll(hw);
+
+ regmap_clear_bits(pon_clk->regmap, CMN_PLL_PON_CONFIG,
+ CMN_PLL_PON_EN);
+}
+
+static int clk_pon_refclk_is_enabled(struct clk_hw *hw)
+{
+ struct clk_cmn_pll *pon_clk = to_clk_cmn_pll(hw);
+
+ return regmap_test_bits(pon_clk->regmap, CMN_PLL_PON_CONFIG,
+ CMN_PLL_PON_EN);
+}
+
+static unsigned long clk_pon_refclk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_cmn_pll *pon_clk = to_clk_cmn_pll(hw);
+ u32 val, div;
+ int ret;
+
+ ret = regmap_read(pon_clk->regmap, CMN_PLL_PON_CONFIG, &val);
+ if (WARN_ON_ONCE(ret))
+ return 0;
+
+ /* Check if in UNIPHY mode (bit 9 = 0) - fixed 31.25 MHz */
+ if (!(val & CMN_PLL_PON_MODE_SEL))
+ return 31250000UL;
+
+ /* PON mode: calculate from divider */
+ div = FIELD_GET(CMN_PLL_PON_DIV_CTRL, val);
+ if (WARN_ON_ONCE(!div))
+ return 0;
+
+ return DIV_ROUND_CLOSEST_ULL((u64)parent_rate, 2ULL * div);
+}
+
+static int clk_pon_refclk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ unsigned long div, pon_rate, uniphy_rate = 31250000UL;
+
+ if (WARN_ON_ONCE(!req->rate))
+ return -EINVAL;
+
+ div = DIV64_U64_ROUND_CLOSEST((u64)req->best_parent_rate, 2ULL * req->rate);
+
+ /* Clamp to valid range (1-255) */
+ div = clamp_t(unsigned long, div, 1, 255);
+
+ pon_rate = DIV_ROUND_CLOSEST_ULL((u64)req->best_parent_rate, 2ULL * div);
+
+ /* Pick whichever mode gets closer to the requested rate */
+ req->rate = abs_diff(req->rate, uniphy_rate) < abs_diff(req->rate, pon_rate) ?
+ uniphy_rate : pon_rate;
+
+ return 0;
+}
+
+static int clk_pon_refclk_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_cmn_pll *pon_clk = to_clk_cmn_pll(hw);
+ unsigned long div;
+ int ret;
+
+ if (rate == 0)
+ return -EINVAL;
+
+ /*
+ * An exact request for 31.25 MHz is always satisfiable by UNIPHY
+ * mode, even though PON mode with a suitable divider can produce
+ * the same frequency for some parent rates. Preferring UNIPHY here
+ * is safe: this function is only ever called with a rate produced
+ * by clk_pon_refclk_determine_rate() through the standard
+ * clk_set_rate() path, and either mode yields the identical output
+ * rate for this value.
+ */
+ if (rate == 31250000UL)
+ return regmap_clear_bits(pon_clk->regmap, CMN_PLL_PON_CONFIG,
+ CMN_PLL_PON_MODE_SEL);
+
+ div = DIV64_U64_ROUND_CLOSEST((u64)parent_rate, 2ULL * rate);
+ if (div == 0 || div > 255)
+ return -EINVAL;
+
+ /* Switch to PON mode */
+ ret = regmap_set_bits(pon_clk->regmap, CMN_PLL_PON_CONFIG,
+ CMN_PLL_PON_MODE_SEL);
+ if (ret)
+ return ret;
+
+ return regmap_update_bits(pon_clk->regmap, CMN_PLL_PON_CONFIG,
+ CMN_PLL_PON_DIV_CTRL,
+ FIELD_PREP(CMN_PLL_PON_DIV_CTRL, div));
+}
+
+static const struct clk_ops clk_pon_refclk_ops = {
+ .enable = clk_pon_refclk_enable,
+ .disable = clk_pon_refclk_disable,
+ .is_enabled = clk_pon_refclk_is_enabled,
+ .recalc_rate = clk_pon_refclk_recalc_rate,
+ .determine_rate = clk_pon_refclk_determine_rate,
+ .set_rate = clk_pon_refclk_set_rate,
+};
+
+static struct clk_hw *ipq_cmn_pll_pon_refclk_register(struct platform_device *pdev,
+ struct regmap *regmap,
+ struct clk_hw *cmn_pll_hw,
+ const char *name)
+{
+ struct clk_parent_data pdata = { .hw = cmn_pll_hw };
+ struct device *dev = &pdev->dev;
+ struct clk_init_data init = {};
+ struct clk_cmn_pll *pon_clk;
+ int ret;
+
+ pon_clk = devm_kzalloc(dev, sizeof(*pon_clk), GFP_KERNEL);
+ if (!pon_clk)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.parent_data = &pdata;
+ init.num_parents = 1;
+ init.ops = &clk_pon_refclk_ops;
+ /*
+ * The PON reference clock may already be enabled by bootloader
+ * or consumed by hardware without an in-kernel client driver.
+ * Add CLK_IGNORE_UNUSED so the clock framework does not disable
+ * it when no consumer has claimed it.
+ */
+ init.flags = CLK_IGNORE_UNUSED;
+
+ pon_clk->hw.init = &init;
+ pon_clk->regmap = regmap;
+
+ ret = devm_clk_hw_register(dev, &pon_clk->hw);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return &pon_clk->hw;
+}
+
static int ipq_cmn_pll_register_clks(struct platform_device *pdev)
{
const struct cmn_pll_fixed_output_clk *p, *fixed_clk;
@@ -473,6 +638,12 @@ static int ipq_cmn_pll_register_clks(struct platform_device *pdev)
fixed_clk[i].name,
CMN_PLL_PPE_CLK_SEL);
break;
+ case CMN_PLL_CLK_PON:
+ hw = ipq_cmn_pll_pon_refclk_register(pdev,
+ cmn_pll->regmap,
+ cmn_pll_hw,
+ fixed_clk[i].name);
+ break;
}
if (IS_ERR(hw))
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PATCH v2 08/12] clk: qcom: ipq-cmn-pll: Add EPHY-RAW clock support
2026-08-07 6:53 [PATCH v2 00/12] clk: qcom: ipq-cmn-pll: Add IPQ5210 CMN PLL support Luo Jie
` (6 preceding siblings ...)
2026-08-07 6:53 ` [PATCH v2 07/12] clk: qcom: ipq-cmn-pll: Add PON reference " Luo Jie
@ 2026-08-07 6:53 ` Luo Jie
2026-08-07 6:53 ` [PATCH v2 09/12] clk: qcom: ipq-cmn-pll: Add clock gate support for fixed clocks Luo Jie
` (3 subsequent siblings)
11 siblings, 0 replies; 15+ messages in thread
From: Luo Jie @ 2026-08-07 6:53 UTC (permalink / raw)
To: Bjorn Andersson, Michael Turquette, Stephen Boyd, Brian Masney,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Luo Jie,
Konrad Dybcio
Cc: linux-arm-msm, linux-clk, linux-kernel, devicetree, quic_kkumarcs,
quic_linchen, quic_leiwei, quic_suruchia
The EPHY-RAW clock is an IPQ5210-specific output from the CMN PLL that
provides the raw reference clock to the SoC-integrated Ethernet PHY
(EPHY), which supports up to 2.5G link speed. The output frequency is
selected via bit 10 (CMN_PLL_GEPHY_312P5M_125M_SEL) of the
CMN_PLL_PON_CONFIG register:
- 0: 125 MHz - for 1G/100M/10M link speed
- 1: 312.5 MHz - for 2.5G link speed
Signed-off-by: Luo Jie <jie.luo@oss.qualcomm.com>
---
drivers/clk/qcom/ipq-cmn-pll.c | 98 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 98 insertions(+)
diff --git a/drivers/clk/qcom/ipq-cmn-pll.c b/drivers/clk/qcom/ipq-cmn-pll.c
index 356988c03a7a..586f9c620e7e 100644
--- a/drivers/clk/qcom/ipq-cmn-pll.c
+++ b/drivers/clk/qcom/ipq-cmn-pll.c
@@ -77,6 +77,7 @@
#define CMN_PLL_PON_MODE_SEL BIT(9)
#define CMN_PLL_PON_EN BIT(8)
#define CMN_PLL_PON_DIV_CTRL GENMASK(7, 0)
+#define CMN_PLL_GEPHY_312P5M_125M_SEL BIT(10)
#define CMN_PLL_POWER_ON_AND_RESET 0x780
#define CMN_ANA_EN_SW_RSTN BIT(6)
@@ -98,12 +99,14 @@
* @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
+ * @CMN_PLL_CLK_EPHY_RAW: EPHY-RAW clock
*/
enum cmn_pll_clk_type {
CMN_PLL_CLK_FIXED_RATE,
CMN_PLL_CLK_NSS,
CMN_PLL_CLK_PPE,
CMN_PLL_CLK_PON,
+ CMN_PLL_CLK_EPHY_RAW,
};
/**
@@ -578,6 +581,95 @@ static struct clk_hw *ipq_cmn_pll_pon_refclk_register(struct platform_device *pd
return &pon_clk->hw;
}
+/*
+ * EPHY-RAW clock operations for IPQ5210.
+ * The output clock rate is selected via bit 10 of CMN_PLL_PON_CONFIG:
+ * 0: 125 MHz (for 1G link speed)
+ * 1: 312.5 MHz (for 2.5G link speed)
+ */
+static unsigned long clk_ephy_raw_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_cmn_pll *ephy_raw_clk = to_clk_cmn_pll(hw);
+ u32 val;
+ int ret;
+
+ ret = regmap_read(ephy_raw_clk->regmap, CMN_PLL_PON_CONFIG, &val);
+ if (WARN_ON_ONCE(ret))
+ return 0;
+
+ if (val & CMN_PLL_GEPHY_312P5M_125M_SEL)
+ return 312500000UL;
+
+ return 125000000UL;
+}
+
+static int clk_ephy_raw_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ unsigned long rate_125m = 125000000UL, rate_312p5m = 312500000UL;
+
+ /* Pick whichever of the two supported rates is closer to the request */
+ req->rate = abs_diff(req->rate, rate_125m) < abs_diff(req->rate, rate_312p5m) ?
+ rate_125m : rate_312p5m;
+
+ return 0;
+}
+
+static int clk_ephy_raw_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_cmn_pll *ephy_raw_clk = to_clk_cmn_pll(hw);
+
+ if (rate == 125000000UL)
+ return regmap_clear_bits(ephy_raw_clk->regmap,
+ CMN_PLL_PON_CONFIG,
+ CMN_PLL_GEPHY_312P5M_125M_SEL);
+
+ if (rate == 312500000UL)
+ return regmap_set_bits(ephy_raw_clk->regmap,
+ CMN_PLL_PON_CONFIG,
+ CMN_PLL_GEPHY_312P5M_125M_SEL);
+
+ return -EINVAL;
+}
+
+static const struct clk_ops clk_ephy_raw_ops = {
+ .recalc_rate = clk_ephy_raw_recalc_rate,
+ .determine_rate = clk_ephy_raw_determine_rate,
+ .set_rate = clk_ephy_raw_set_rate,
+};
+
+static struct clk_hw *ipq_cmn_pll_ephy_raw_register(struct platform_device *pdev,
+ struct regmap *regmap,
+ struct clk_hw *cmn_pll_hw,
+ const char *name)
+{
+ struct clk_parent_data pdata = { .hw = cmn_pll_hw };
+ struct clk_cmn_pll *ephy_raw_clk;
+ struct device *dev = &pdev->dev;
+ struct clk_init_data init = {};
+ int ret;
+
+ ephy_raw_clk = devm_kzalloc(dev, sizeof(*ephy_raw_clk), GFP_KERNEL);
+ if (!ephy_raw_clk)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.parent_data = &pdata;
+ init.num_parents = 1;
+ init.ops = &clk_ephy_raw_ops;
+
+ ephy_raw_clk->hw.init = &init;
+ ephy_raw_clk->regmap = regmap;
+
+ ret = devm_clk_hw_register(dev, &ephy_raw_clk->hw);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return &ephy_raw_clk->hw;
+}
+
static int ipq_cmn_pll_register_clks(struct platform_device *pdev)
{
const struct cmn_pll_fixed_output_clk *p, *fixed_clk;
@@ -644,6 +736,12 @@ static int ipq_cmn_pll_register_clks(struct platform_device *pdev)
cmn_pll_hw,
fixed_clk[i].name);
break;
+ case CMN_PLL_CLK_EPHY_RAW:
+ hw = ipq_cmn_pll_ephy_raw_register(pdev,
+ cmn_pll->regmap,
+ cmn_pll_hw,
+ fixed_clk[i].name);
+ break;
}
if (IS_ERR(hw))
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PATCH v2 09/12] clk: qcom: ipq-cmn-pll: Add clock gate support for fixed clocks
2026-08-07 6:53 [PATCH v2 00/12] clk: qcom: ipq-cmn-pll: Add IPQ5210 CMN PLL support Luo Jie
` (7 preceding siblings ...)
2026-08-07 6:53 ` [PATCH v2 08/12] clk: qcom: ipq-cmn-pll: Add EPHY-RAW " Luo Jie
@ 2026-08-07 6:53 ` Luo Jie
2026-08-07 6:53 ` [PATCH v2 10/12] clk: qcom: ipq-cmn-pll: Add all output clocks for IPQ5210 Luo Jie
` (2 subsequent siblings)
11 siblings, 0 replies; 15+ messages in thread
From: Luo Jie @ 2026-08-07 6:53 UTC (permalink / raw)
To: Bjorn Andersson, Michael Turquette, Stephen Boyd, Brian Masney,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Luo Jie,
Konrad Dybcio
Cc: linux-arm-msm, linux-clk, linux-kernel, devicetree, quic_kkumarcs,
quic_linchen, quic_leiwei, quic_suruchia
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@oss.qualcomm.com>
---
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
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PATCH v2 10/12] clk: qcom: ipq-cmn-pll: Add all output clocks for IPQ5210
2026-08-07 6:53 [PATCH v2 00/12] clk: qcom: ipq-cmn-pll: Add IPQ5210 CMN PLL support Luo Jie
` (8 preceding siblings ...)
2026-08-07 6:53 ` [PATCH v2 09/12] clk: qcom: ipq-cmn-pll: Add clock gate support for fixed clocks Luo Jie
@ 2026-08-07 6:53 ` Luo Jie
2026-08-07 6:53 ` [PATCH v2 11/12] arm64: dts: qcom: ipq5210: Add CMN PLL device node Luo Jie
2026-08-07 6:53 ` [PATCH v2 12/12] arm64: dts: qcom: Update IPQ5210 xo_board to use fixed factor clock Luo Jie
11 siblings, 0 replies; 15+ messages in thread
From: Luo Jie @ 2026-08-07 6:53 UTC (permalink / raw)
To: Bjorn Andersson, Michael Turquette, Stephen Boyd, Brian Masney,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Luo Jie,
Konrad Dybcio
Cc: linux-arm-msm, linux-clk, linux-kernel, devicetree, quic_kkumarcs,
quic_linchen, quic_leiwei, quic_suruchia
Add the IPQ5210 output clock array and the device match table entry,
wiring together all the infrastructure added in the previous patches.
The IPQ5210 CMN PLL supplies 12 output clocks:
Fixed-rate: XO 24 MHz, sleep 32 kHz
Gated fixed-rate: PCS 31.25 MHz, ETH0/1/2 50 MHz, EPHY 50 MHz,
ETH 25 MHz
Configurable divider: NSS, PPE
PON reference: PON (enable/disable + 8-bit divider)
EPHY-RAW: 125/312.5 MHz select
Signed-off-by: Luo Jie <jie.luo@oss.qualcomm.com>
---
drivers/clk/qcom/ipq-cmn-pll.c | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/drivers/clk/qcom/ipq-cmn-pll.c b/drivers/clk/qcom/ipq-cmn-pll.c
index 2b05896ff00f..eeadc94a23df 100644
--- a/drivers/clk/qcom/ipq-cmn-pll.c
+++ b/drivers/clk/qcom/ipq-cmn-pll.c
@@ -55,6 +55,7 @@
#include <dt-bindings/clock/qcom,ipq-cmn-pll.h>
#include <dt-bindings/clock/qcom,ipq5018-cmn-pll.h>
+#include <dt-bindings/clock/qcom,ipq5210-cmn-pll.h>
#include <dt-bindings/clock/qcom,ipq5332-cmn-pll.h>
#include <dt-bindings/clock/qcom,ipq5424-cmn-pll.h>
#include <dt-bindings/clock/qcom,ipq6018-cmn-pll.h>
@@ -182,6 +183,18 @@ struct clk_fixed_gate {
#define CLK_PLL_GATE(_id, _name, _rate, _bit) \
CLK_PLL_OUTPUT_RAW(_id, _name, CMN_PLL_CLK_FIXED_GATE, _rate, _bit)
+#define CLK_PLL_NSS(_id, _name) \
+ CLK_PLL_OUTPUT_RAW(_id, _name, CMN_PLL_CLK_NSS, 0, -1)
+
+#define CLK_PLL_PPE(_id, _name) \
+ CLK_PLL_OUTPUT_RAW(_id, _name, CMN_PLL_CLK_PPE, 0, -1)
+
+#define CLK_PLL_PON(_id, _name) \
+ CLK_PLL_OUTPUT_RAW(_id, _name, CMN_PLL_CLK_PON, 0, -1)
+
+#define CLK_PLL_EPHY_RAW(_id, _name) \
+ CLK_PLL_OUTPUT_RAW(_id, _name, CMN_PLL_CLK_EPHY_RAW, 0, -1)
+
#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)
@@ -200,6 +213,22 @@ static const struct cmn_pll_fixed_output_clk ipq5018_output_clks[] = {
{ /* Sentinel */ }
};
+static const struct cmn_pll_fixed_output_clk ipq5210_output_clks[] = {
+ CLK_PLL_OUTPUT(IPQ5210_XO_24MHZ_CLK, "xo-24mhz", 24000000UL),
+ CLK_PLL_OUTPUT(IPQ5210_SLEEP_32KHZ_CLK, "sleep-32khz", 32000UL),
+ CLK_PLL_GATE(IPQ5210_PCS_31P25MHZ_CLK, "pcs-31p25mhz", 31250000UL, CLK31P25M_EN_BIT),
+ CLK_PLL_GATE(IPQ5210_ETH0_50MHZ_CLK, "eth0-50mhz", 50000000UL, CLK50M_EN_BIT),
+ CLK_PLL_GATE(IPQ5210_ETH1_50MHZ_CLK, "eth1-50mhz", 50000000UL, CLK50M_EN_BIT2_BIT),
+ CLK_PLL_GATE(IPQ5210_ETH2_50MHZ_CLK, "eth2-50mhz", 50000000UL, CLK50M_EN_BIT3_BIT),
+ CLK_PLL_GATE(IPQ5210_EPHY_50MHZ_CLK, "ephy-50mhz", 50000000UL, CLK250M_EN_BIT),
+ CLK_PLL_GATE(IPQ5210_ETH_25MHZ_CLK, "eth-25mhz", 25000000UL, CLK25M_EN_BIT),
+ CLK_PLL_NSS(IPQ5210_NSS_CLK, "nss"),
+ CLK_PLL_PPE(IPQ5210_PPE_CLK, "ppe"),
+ CLK_PLL_PON(IPQ5210_PON_REFCLK, "pon"),
+ CLK_PLL_EPHY_RAW(IPQ5210_EPHY_RAW_CLK, "ephy-raw"),
+ { /* Sentinel */ }
+};
+
static const struct cmn_pll_fixed_output_clk ipq6018_output_clks[] = {
CLK_PLL_OUTPUT(IPQ6018_BIAS_PLL_CC_CLK, "bias_pll_cc_clk", 300000000UL),
CLK_PLL_OUTPUT(IPQ6018_BIAS_PLL_NSS_NOC_CLK, "bias_pll_nss_noc_clk", 416500000UL),
@@ -942,6 +971,7 @@ static const struct dev_pm_ops ipq_cmn_pll_pm_ops = {
static const struct of_device_id ipq_cmn_pll_clk_ids[] = {
{ .compatible = "qcom,ipq5018-cmn-pll", .data = &ipq5018_output_clks },
+ { .compatible = "qcom,ipq5210-cmn-pll", .data = &ipq5210_output_clks },
{ .compatible = "qcom,ipq5332-cmn-pll", .data = &ipq5332_output_clks },
{ .compatible = "qcom,ipq5424-cmn-pll", .data = &ipq5424_output_clks },
{ .compatible = "qcom,ipq6018-cmn-pll", .data = &ipq6018_output_clks },
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PATCH v2 11/12] arm64: dts: qcom: ipq5210: Add CMN PLL device node
2026-08-07 6:53 [PATCH v2 00/12] clk: qcom: ipq-cmn-pll: Add IPQ5210 CMN PLL support Luo Jie
` (9 preceding siblings ...)
2026-08-07 6:53 ` [PATCH v2 10/12] clk: qcom: ipq-cmn-pll: Add all output clocks for IPQ5210 Luo Jie
@ 2026-08-07 6:53 ` Luo Jie
2026-08-07 6:53 ` [PATCH v2 12/12] arm64: dts: qcom: Update IPQ5210 xo_board to use fixed factor clock Luo Jie
11 siblings, 0 replies; 15+ messages in thread
From: Luo Jie @ 2026-08-07 6:53 UTC (permalink / raw)
To: Bjorn Andersson, Michael Turquette, Stephen Boyd, Brian Masney,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Luo Jie,
Konrad Dybcio
Cc: linux-arm-msm, linux-clk, linux-kernel, devicetree, quic_kkumarcs,
quic_linchen, quic_leiwei, quic_suruchia
Add CMN PLL node for enabling output clocks to the networking
hardware blocks on IPQ5210 devices.
The reference clock of CMN PLL is routed from XO to the CMN PLL
through the WiFi block.
XO (48 MHZ or 96 MHZ or 192 MHZ)-->WiFi (multiplier/divider)-->
48 MHZ to CMN PLL.
Signed-off-by: Luo Jie <jie.luo@oss.qualcomm.com>
---
arch/arm64/boot/dts/qcom/ipq5210-rdp504.dts | 15 +++++++++++++++
arch/arm64/boot/dts/qcom/ipq5210.dtsi | 24 ++++++++++++++++++++++++
2 files changed, 39 insertions(+)
diff --git a/arch/arm64/boot/dts/qcom/ipq5210-rdp504.dts b/arch/arm64/boot/dts/qcom/ipq5210-rdp504.dts
index 941f866ecfe9..7410d092814e 100644
--- a/arch/arm64/boot/dts/qcom/ipq5210-rdp504.dts
+++ b/arch/arm64/boot/dts/qcom/ipq5210-rdp504.dts
@@ -74,6 +74,21 @@ &uart1 {
status = "okay";
};
+/*
+ * The bootstrap pins for the board select the XO clock frequency that
+ * supports 48 MHZ, 96 MHZ or 192 MHZ. This setting automatically
+ * enables the right dividers, to ensure the reference clock output
+ * from WiFi to the CMN PLL is 48 MHZ.
+ */
+&ref_48mhz_clk {
+ clock-div = <1>;
+ clock-mult = <1>;
+};
+
&xo_board {
clock-frequency = <24000000>;
};
+
+&xo_clk {
+ clock-frequency = <48000000>;
+};
diff --git a/arch/arm64/boot/dts/qcom/ipq5210.dtsi b/arch/arm64/boot/dts/qcom/ipq5210.dtsi
index 2cdc10529c48..7025bdd9036e 100644
--- a/arch/arm64/boot/dts/qcom/ipq5210.dtsi
+++ b/arch/arm64/boot/dts/qcom/ipq5210.dtsi
@@ -5,6 +5,7 @@
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/clock/qcom,ipq5210-gcc.h>
+#include <dt-bindings/clock/qcom,ipq5210-cmn-pll.h>
#include <dt-bindings/reset/qcom,ipq5210-gcc.h>
/ {
@@ -13,6 +14,12 @@ / {
interrupt-parent = <&intc>;
clocks {
+ ref_48mhz_clk: ref-48mhz-clk {
+ compatible = "fixed-factor-clock";
+ clocks = <&xo_clk>;
+ #clock-cells = <0>;
+ };
+
sleep_clk: sleep-clk {
compatible = "fixed-clock";
#clock-cells = <0>;
@@ -22,6 +29,11 @@ xo_board: xo-board-clk {
compatible = "fixed-clock";
#clock-cells = <0>;
};
+
+ xo_clk: xo-clk {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ };
};
cpus {
@@ -130,6 +142,18 @@ soc@0 {
dma-ranges = <0 0 0 0 0x10 0>;
ranges = <0 0 0 0 0x10 0>;
+ cmn_pll: clock-controller@9b000 {
+ compatible = "qcom,ipq5210-cmn-pll";
+ reg = <0x0 0x0009b000 0x0 0x800>;
+ clocks = <&ref_48mhz_clk>,
+ <&gcc GCC_CMN_12GPLL_AHB_CLK>,
+ <&gcc GCC_CMN_12GPLL_SYS_CLK>;
+ clock-names = "ref", "ahb", "sys";
+ #clock-cells = <1>;
+ assigned-clocks = <&cmn_pll IPQ5210_CMN_PLL_CLK>;
+ assigned-clock-rates-u64 = /bits/ 64 <12000000000>;
+ };
+
tlmm: pinctrl@1000000 {
compatible = "qcom,ipq5210-tlmm";
reg = <0x0 0x01000000 0x0 0x300000>;
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PATCH v2 12/12] arm64: dts: qcom: Update IPQ5210 xo_board to use fixed factor clock
2026-08-07 6:53 [PATCH v2 00/12] clk: qcom: ipq-cmn-pll: Add IPQ5210 CMN PLL support Luo Jie
` (10 preceding siblings ...)
2026-08-07 6:53 ` [PATCH v2 11/12] arm64: dts: qcom: ipq5210: Add CMN PLL device node Luo Jie
@ 2026-08-07 6:53 ` Luo Jie
11 siblings, 0 replies; 15+ messages in thread
From: Luo Jie @ 2026-08-07 6:53 UTC (permalink / raw)
To: Bjorn Andersson, Michael Turquette, Stephen Boyd, Brian Masney,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Luo Jie,
Konrad Dybcio
Cc: linux-arm-msm, linux-clk, linux-kernel, devicetree, quic_kkumarcs,
quic_linchen, quic_leiwei, quic_suruchia
xo_board is fixed to 24 MHZ, which is routed from WiFi output clock
48 MHZ (also being the reference clock of CMN PLL) divided 2 by
analog block routing channel.
Signed-off-by: Luo Jie <jie.luo@oss.qualcomm.com>
---
arch/arm64/boot/dts/qcom/ipq5210-rdp504.dts | 7 ++++++-
arch/arm64/boot/dts/qcom/ipq5210.dtsi | 3 ++-
2 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/arch/arm64/boot/dts/qcom/ipq5210-rdp504.dts b/arch/arm64/boot/dts/qcom/ipq5210-rdp504.dts
index 7410d092814e..fa537633d5f5 100644
--- a/arch/arm64/boot/dts/qcom/ipq5210-rdp504.dts
+++ b/arch/arm64/boot/dts/qcom/ipq5210-rdp504.dts
@@ -85,8 +85,13 @@ &ref_48mhz_clk {
clock-mult = <1>;
};
+/*
+ * The frequency of xo_board is fixed to 24 MHZ, which is routed
+ * from WiFi output clock 48 MHZ divided by 2.
+ */
&xo_board {
- clock-frequency = <24000000>;
+ clock-div = <2>;
+ clock-mult = <1>;
};
&xo_clk {
diff --git a/arch/arm64/boot/dts/qcom/ipq5210.dtsi b/arch/arm64/boot/dts/qcom/ipq5210.dtsi
index 7025bdd9036e..8f6b37041132 100644
--- a/arch/arm64/boot/dts/qcom/ipq5210.dtsi
+++ b/arch/arm64/boot/dts/qcom/ipq5210.dtsi
@@ -26,7 +26,8 @@ sleep_clk: sleep-clk {
};
xo_board: xo-board-clk {
- compatible = "fixed-clock";
+ compatible = "fixed-factor-clock";
+ clocks = <&xo_clk>;
#clock-cells = <0>;
};
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread