* [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; 19+ 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] 19+ 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; 19+ 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] 19+ 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-07 7:12 ` sashiko-bot
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; 19+ 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] 19+ 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-07 7:12 ` sashiko-bot
0 siblings, 0 replies; 19+ messages in thread
From: sashiko-bot @ 2026-08-07 7:12 UTC (permalink / raw)
To: Luo Jie; +Cc: conor+dt, devicetree, robh
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
New issues:
- [High] divider_ro_determine_rate() incorrectly returns -EINVAL for zero divisors when CLK_DIVIDER_ALLOW_ZERO is set, breaking valid pass-through states.
Pre-existing issues:
- [High] The pre-existing CLK_DIVIDER_EVEN_INTEGERS flag suffers from the exact same odd-divisor truncation bug described in the commit, but was left unfixed.
- [High] _div_round_closest() triggers a division-by-zero panic when the requested rate exceeds the parent rate.
--
--- Patch [3]: [PATCH v2 03/12] clk: divider: Introduce CLK_DIVIDER_EVEN_INTEGERS_NO_OFFSET flag ---
commit 2989085a3446885e07078514557377e58a8cade1
Author: Luo Jie <jie.luo@oss.qualcomm.com>
clk: divider: Introduce CLK_DIVIDER_EVEN_INTEGERS_NO_OFFSET flag
This patch adds a new CLK_DIVIDER_EVEN_INTEGERS_NO_OFFSET flag for hardware
dividers that derive an even divisor directly from the raw register value
(2 * val). It updates the divisor conversion functions, extends validation
to reject odd divisors, and ensures rounding maps to even divisors to
prevent truncation errors.
> diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
> index b3b485d23ea85..f062d77ee106b 100644
> --- a/drivers/clk/clk-divider.c
> +++ b/drivers/clk/clk-divider.c
[ ... ]
> @@ -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);
[Severity: High]
This is a pre-existing issue, but since this patch aims to prevent odd
divisors from being silently decoded into the wrong even value for the new
flag, shouldn't the pre-existing CLK_DIVIDER_EVEN_INTEGERS flag also reject
odd divisors here?
If _is_valid_div() allows an odd divisor (like 3) for
CLK_DIVIDER_EVEN_INTEGERS, _get_val() will map it to 0, causing the
hardware to run at a potentially out-of-spec faster rate (parent / 2).
> if (table)
> return _is_valid_table_div(table, div);
[ ... ]
> @@ -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);
[Severity: High]
This is a pre-existing issue, but does the original
CLK_DIVIDER_EVEN_INTEGERS flag also need this rounding logic to force even
integers?
> 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) {
[Severity: High]
This isn't a bug introduced by this patch, but can down evaluate to 0
here and cause a division-by-zero panic?
Looking earlier in _div_round_closest(), down is computed as:
down = parent_rate / rate;
If a caller requests a rate strictly greater than parent_rate, down
evaluates to 0. When later passed to DIV_ROUND_UP_ULL() in this function:
down_rate = DIV_ROUND_UP_ULL((u64)parent_rate, down);
Does this result in a kernel panic?
> up = _round_up_table(table, up);
> down = _round_down_table(table, down);
[ ... ]
> @@ -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;
> + }
> +
[Severity: High]
Does this unconditionally return -EINVAL for a zero divisor, even when
CLK_DIVIDER_ALLOW_ZERO is set?
If the hardware register contains 0 and CLK_DIVIDER_ALLOW_ZERO is set
(which implies a pass-through clock), this correctly suppresses the
WARN, but still fails the rate determination by returning -EINVAL
instead of successfully propagating the parent rate.
> /* 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)
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260806-qcom_ipq5210_cmnpll-v2-0-cce718b8ec17@oss.qualcomm.com?part=3
^ permalink raw reply [flat|nested] 19+ 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 7:03 ` sashiko-bot
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, 1 reply; 19+ 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] 19+ messages in thread* Re: [PATCH v2 04/12] clk: qcom: clk-regmap-divider: Support CLK_DIVIDER_* flags
2026-08-07 6:53 ` [PATCH v2 04/12] clk: qcom: clk-regmap-divider: Support CLK_DIVIDER_* flags Luo Jie
@ 2026-08-07 7:03 ` sashiko-bot
0 siblings, 0 replies; 19+ messages in thread
From: sashiko-bot @ 2026-08-07 7:03 UTC (permalink / raw)
To: Luo Jie; +Cc: robh, devicetree, conor+dt
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] Type correctness regression: left shift of a signed integer without casting
--
--- Patch [4]: [PATCH v2 04/12] clk: qcom: clk-regmap-divider: Support CLK_DIVIDER_* flags ---
commit 8da3393b838b4e4b6fe1ccda790022d6a599db23
Author: Luo Jie <jie.luo@oss.qualcomm.com>
clk: qcom: clk-regmap-divider: Support CLK_DIVIDER_* flags
This commit adds a flags field to struct clk_regmap_div and passes it to
clock divider functions to support CLK_DIVIDER_* behavior variants. It also
updates div_set_rate() to handle negative error codes from divider_get_val()
by changing the div variable from an unsigned to a signed integer.
> diff --git a/drivers/clk/qcom/clk-regmap-divider.c b/drivers/clk/qcom/clk-regmap-divider.c
> index 672e82caf2050..fe4542b5bd580 100644
> --- a/drivers/clk/qcom/clk-regmap-divider.c
> +++ b/drivers/clk/qcom/clk-regmap-divider.c
[ ... ]
> @@ -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,
> div << divider->shift);
[Severity: Low]
Since div was changed to a signed int to handle error codes, should it be cast
to an unsigned type before being shifted here?
Left-shifting a signed integer without casting can trigger static analysis
warnings, or technically undefined behavior in standard C if the value
overflows into the sign bit.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260806-qcom_ipq5210_cmnpll-v2-0-cce718b8ec17@oss.qualcomm.com?part=4
^ permalink raw reply [flat|nested] 19+ 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 7:18 ` sashiko-bot
2026-08-07 6:53 ` [PATCH v2 06/12] clk: qcom: ipq-cmn-pll: Add PPE " Luo Jie
` (6 subsequent siblings)
11 siblings, 1 reply; 19+ 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] 19+ messages in thread* Re: [PATCH v2 05/12] clk: qcom: ipq-cmn-pll: Add NSS clock support
2026-08-07 6:53 ` [PATCH v2 05/12] clk: qcom: ipq-cmn-pll: Add NSS clock support Luo Jie
@ 2026-08-07 7:18 ` sashiko-bot
0 siblings, 0 replies; 19+ messages in thread
From: sashiko-bot @ 2026-08-07 7:18 UTC (permalink / raw)
To: Luo Jie; +Cc: robh, devicetree, conor+dt
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] The NSS clock is unconditionally registered as a fixed-rate clock, meaning the new divider registration logic is never invoked.
- [Medium] A kernel WARN will trigger during boot if the 6-bit hardware register happens to be 0.
--
--- Patch [5]: [PATCH v2 05/12] clk: qcom: ipq-cmn-pll: Add NSS clock support ---
commit 4b97f2d301b3cfbdff0e9b058b5b63be9ee7d0dd
Author: Luo Jie <jie.luo@oss.qualcomm.com>
clk: qcom: ipq-cmn-pll: Add NSS clock support
This patch adds NSS clock support by introducing a new clock registration
type and divider logic. It adds ipq_cmn_pll_regmap_div_register() to handle
the NSS clock divider and updates the clk_cmn_pll structures to specify the
clock type.
> diff --git a/drivers/clk/qcom/ipq-cmn-pll.c b/drivers/clk/qcom/ipq-cmn-pll.c
> index 4fdf78f6829e8..c1b00699eb72d 100644
> --- a/drivers/clk/qcom/ipq-cmn-pll.c
> +++ b/drivers/clk/qcom/ipq-cmn-pll.c
[ ... ]
> @@ -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, \
> }
[Severity: Medium]
Since CLK_PLL_OUTPUT unconditionally assigns CMN_PLL_CLK_FIXED_RATE, and no
other macro is introduced to set CMN_PLL_CLK_NSS, will the NSS clock
actually use the new registration type?
It seems the switch statement in ipq_cmn_pll_register_clks() will only ever
evaluate to CMN_PLL_CLK_FIXED_RATE, leaving the new divider registration logic
as dead code.
[ ... ]
> @@ -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;
[Severity: Medium]
Will this trigger a generic clock framework WARN during boot if the
hardware register value happens to be 0?
Since the full 6-bit register range is now allowed, if the bootloader or
hardware leaves the register at 0, clk_regmap_div_ops.recalc_rate will
evaluate a divisor of 0. Without CLK_DIVIDER_ALLOW_ZERO included in the flags,
divider_recalc_rate() will trigger a WARN.
> + 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,
> + };
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260806-qcom_ipq5210_cmnpll-v2-0-cce718b8ec17@oss.qualcomm.com?part=5
^ permalink raw reply [flat|nested] 19+ 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; 19+ 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] 19+ 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 7:03 ` sashiko-bot
2026-08-07 6:53 ` [PATCH v2 08/12] clk: qcom: ipq-cmn-pll: Add EPHY-RAW " Luo Jie
` (4 subsequent siblings)
11 siblings, 1 reply; 19+ 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] 19+ messages in thread* Re: [PATCH v2 07/12] clk: qcom: ipq-cmn-pll: Add PON reference clock support
2026-08-07 6:53 ` [PATCH v2 07/12] clk: qcom: ipq-cmn-pll: Add PON reference " Luo Jie
@ 2026-08-07 7:03 ` sashiko-bot
0 siblings, 0 replies; 19+ messages in thread
From: sashiko-bot @ 2026-08-07 7:03 UTC (permalink / raw)
To: Luo Jie; +Cc: robh, devicetree, conor+dt
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] Integer Overflow / Division by Zero when requesting a specific large clock rate.
- [Medium] Non-atomic update of the clock mode and divider creates an invalid intermediate hardware state.
- [Medium] Misuse of `WARN_ON_ONCE` for valid framework queries and uninitialized hardware states.
--
--- Patch [7]: [PATCH v2 07/12] clk: qcom: ipq-cmn-pll: Add PON reference clock support ---
commit bc0bab1980249d4673b3cc332facefa3306c107a
Author: Luo Jie <jie.luo@oss.qualcomm.com>
clk: qcom: ipq-cmn-pll: Add PON reference clock support
This patch adds support for the PON reference clock in the CMN PLL driver.
The clock is derived from the CMN PLL output divided by 2 and then by a
configurable 8-bit divider. It supports both a fixed 31.25 MHz UNIPHY mode
and a configurable PON mode.
> diff --git a/drivers/clk/qcom/ipq-cmn-pll.c b/drivers/clk/qcom/ipq-cmn-pll.c
> index a24e57b33c6b3..356988c03a7ae 100644
> --- a/drivers/clk/qcom/ipq-cmn-pll.c
> +++ b/drivers/clk/qcom/ipq-cmn-pll.c
[ ... ]
> +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;
[Severity: Medium]
Is it safe to use WARN_ON_ONCE() for this check?
If the bootloader leaves the PON hardware uninitialized with a divider of 0,
this will throw a stack trace during boot when the clock framework initially
registers the clock.
Returning 0 without the warning might be safer.
> +
> + 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;
[Severity: Medium]
Will this throw a stack trace for legitimate clock framework queries?
A user can write 0 to the clock's debugfs clk_rate file, or a consumer
driver can call clk_round_rate(clk, 0) to discover the minimum rate, which
are legal queries.
> +
> + div = DIV64_U64_ROUND_CLOSEST((u64)req->best_parent_rate, 2ULL * req->rate);
[Severity: High]
Can this multiplication overflow and cause a divide-by-zero regression?
If req->rate is set to an exceptionally large value like 1ULL << 63 (which
can be requested by writing to the clock framework's debugfs interface),
the multiplication 2ULL * req->rate evaluates to 0 due to 64-bit unsigned
integer wrapping.
This 0 is then passed as the divisor to DIV64_U64_ROUND_CLOSEST.
> +
> + /* Clamp to valid range (1-255) */
> + div = clamp_t(unsigned long, div, 1, 255);
[ ... ]
> +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;
[ ... ]
> + div = DIV64_U64_ROUND_CLOSEST((u64)parent_rate, 2ULL * rate);
[Severity: High]
Might this also trigger a divide-by-zero regression for the same reason?
If rate wraps when multiplied by 2, passing 0 as the divisor to
DIV64_U64_ROUND_CLOSEST will result in a division by zero.
> + 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));
> +}
[Severity: Medium]
Does this non-atomic update create an invalid intermediate hardware state?
When changing from UNIPHY mode to PON mode, the clock mode is switched
before the new divider is programmed.
If the old divider was zero or invalid, the hardware might momentarily
output an out-of-spec frequency or lock up, especially since this clock
lacks the CLK_SET_RATE_GATE flag and rate changes can happen while active.
Could both fields be updated atomically in a single regmap_update_bits()
call?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260806-qcom_ipq5210_cmnpll-v2-0-cce718b8ec17@oss.qualcomm.com?part=7
^ permalink raw reply [flat|nested] 19+ 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 7:04 ` sashiko-bot
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, 1 reply; 19+ 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] 19+ messages in thread* Re: [PATCH v2 08/12] clk: qcom: ipq-cmn-pll: Add EPHY-RAW clock support
2026-08-07 6:53 ` [PATCH v2 08/12] clk: qcom: ipq-cmn-pll: Add EPHY-RAW " Luo Jie
@ 2026-08-07 7:04 ` sashiko-bot
0 siblings, 0 replies; 19+ messages in thread
From: sashiko-bot @ 2026-08-07 7:04 UTC (permalink / raw)
To: Luo Jie; +Cc: conor+dt, devicetree, robh
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] clk_ephy_raw_determine_rate() fails to respect req->min_rate and req->max_rate boundaries.
--
--- Patch [8]: [PATCH v2 08/12] clk: qcom: ipq-cmn-pll: Add EPHY-RAW clock support ---
commit 49a391af3ca59aa96545f77e2d5dd595f1603325
Author: Luo Jie <jie.luo@oss.qualcomm.com>
clk: qcom: ipq-cmn-pll: Add EPHY-RAW clock support
This commit introduces EPHY-RAW clock support for the IPQ5210 from the CMN
PLL, providing the raw reference clock to the SoC-integrated Ethernet PHY. It
adds clock operations and register definitions to select the output frequency
between 125 MHz and 312.5 MHz based on the required link speed.
> diff --git a/drivers/clk/qcom/ipq-cmn-pll.c b/drivers/clk/qcom/ipq-cmn-pll.c
> index 356988c03a7ae..586f9c620e7e1 100644
> --- a/drivers/clk/qcom/ipq-cmn-pll.c
> +++ b/drivers/clk/qcom/ipq-cmn-pll.c
[ ... ]
> +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;
> +}
[Severity: Medium]
Does this rate selection logic need to check the req->min_rate and
req->max_rate boundaries?
It appears that the target frequency is selected unconditionally based solely
on the absolute difference from req->rate, ignoring bounds applied via QoS or
OPP constraints.
If a clock consumer requests a rate with a strict upper boundary (for example,
req->max_rate is 250 MHz and req->rate is clamped to 250 MHz), the absolute
difference to 312.5 MHz (62.5 MHz) is less than the difference to 125 MHz
(125 MHz).
Could this result in selecting a frequency outside the permissible bounds,
violating the Common Clock Framework determine_rate API contract and causing
upstream rate-setting routines to fail?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260806-qcom_ipq5210_cmnpll-v2-0-cce718b8ec17@oss.qualcomm.com?part=8
^ permalink raw reply [flat|nested] 19+ 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; 19+ 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] 19+ 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; 19+ 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] 19+ 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; 19+ 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] 19+ 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
2026-08-07 7:22 ` sashiko-bot
11 siblings, 1 reply; 19+ 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] 19+ messages in thread* Re: [PATCH v2 12/12] arm64: dts: qcom: Update IPQ5210 xo_board to use fixed factor clock
2026-08-07 6:53 ` [PATCH v2 12/12] arm64: dts: qcom: Update IPQ5210 xo_board to use fixed factor clock Luo Jie
@ 2026-08-07 7:22 ` sashiko-bot
0 siblings, 0 replies; 19+ messages in thread
From: sashiko-bot @ 2026-08-07 7:22 UTC (permalink / raw)
To: Luo Jie; +Cc: conor+dt, devicetree, robh
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The DT topology incorrectly parents `xo_board` to the raw `xo_clk` crystal instead of the 48MHz WiFi output clock (`ref_48mhz_clk`).
--
--- Patch [12]: [PATCH v2 12/12] arm64: dts: qcom: Update IPQ5210 xo_board to use fixed factor clock ---
commit a2a14fe4e6a6b7af2b47f570d64cc8151c8401f4
Author: Luo Jie <jie.luo@oss.qualcomm.com>
arm64: dts: qcom: Update IPQ5210 xo_board to use fixed factor clock
Update the xo_board clock definition to use a fixed factor clock divided
by 2. The commit message indicates this is routed from the 48MHz WiFi
output clock.
> diff --git a/arch/arm64/boot/dts/qcom/ipq5210.dtsi b/arch/arm64/boot/dts/qcom/ipq5210.dtsi
> --- 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>;
[Severity: Medium]
The commit message states that xo_board is routed from the WiFi output
clock (ref_48mhz_clk). Should this be parented to <&ref_48mhz_clk>
instead of <&xo_clk> to correctly reflect the hardware topology?
> + #clock-cells = <0>;
> };
> diff --git a/arch/arm64/boot/dts/qcom/ipq5210-rdp504.dts b/arch/arm64/boot/dts/qcom/ipq5210-rdp504.dts
> --- a/arch/arm64/boot/dts/qcom/ipq5210-rdp504.dts
> +++ b/arch/arm64/boot/dts/qcom/ipq5210-rdp504.dts
[ ... ]
> +/*
> + * 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>;
> };
[Severity: Medium]
Does this implicitly rely on xo_clk being exactly 48MHz?
If a future board uses a different crystal frequency, this setup might force
the board file to specify a compound divider instead of modeling the analog
block's physical divide-by-2 from the WiFi output. If xo_board is parented
to ref_48mhz_clk as described in the commit message, would this physical
divide-by-2 relationship be more accurately preserved?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260806-qcom_ipq5210_cmnpll-v2-0-cce718b8ec17@oss.qualcomm.com?part=12
^ permalink raw reply [flat|nested] 19+ messages in thread