All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH RFC v5 0/4] clk: add support for v1 / v2 clock rate negotiation and kunit tests
@ 2026-03-06 23:21 Brian Masney
  2026-03-06 23:21 ` [PATCH RFC v5 1/4] clk: test: introduce clk_dummy_div for a mock divider Brian Masney
                   ` (4 more replies)
  0 siblings, 5 replies; 10+ messages in thread
From: Brian Masney @ 2026-03-06 23:21 UTC (permalink / raw)
  To: Michael Turquette, Stephen Boyd, Maxime Ripard, Alberto Ruiz
  Cc: linux-clk, linux-kernel, Brian Masney

The Common Clock Framework is expected to keep a clock’s rate stable
after setting a new rate with:

    clk_set_rate(clk, NEW_RATE);

Clock consumers do not know about the clock hierarchy, sibling clocks,
or the type of clocks involved. However, several longstanding issues
affect how rate changes propagate through the clock tree when
CLK_SET_RATE_PARENT is involved, and the parent's clock rate is changed:

- A clock in some cases can unknowingly change a sibling clock's rate.
  More details about this particular case are documented at:
  https://lore.kernel.org/linux-clk/20250528-clk-wip-v2-v2-2-0d2c2f220442@redhat.com/

- No negotiation is done with the sibling clocks, so an inappropriate
  or less than ideal parent rate can be selected.

A selection of some real world examples of where this shows up is at
[1]. DRM needs to run at precise clock rates, and this issue shows up
there, however will also show up in other subsystems that require
precise clock rates, such as sound.

This series introduces kunit tests to illustrate the current behavior in
the clk core. As discussed at Linux Plumbers Conference 2025 in Tokyo
[2], it was suggested to move the negotiation logic into the clk
providers themselves so that only the clks with this issue can have
their rate preserved, and add some common helpers to the clk core.

Note I am marking this series as a RFC because:

- I only have kunit tests, and there's no actual real users of these
  new helpers. (kunit doesn't count.) I'm hoping to get feedback from
  Stephen if this is what he had in mind for an acceptable solution
  based on earlier discussions. In the mean time, I'll see if I can
  get a board that has this issue, or work with someone in another
  company that can test this for me.

- I feel like the names of some of these newly-introduced members
  could be improved. Naming things can be hard.

Changes since v4:
https://lore.kernel.org/linux-clk/20250923-clk-tests-docs-v4-0-9205cb3d3cba@redhat.com/
- Reworked based on feedback at Linux Plumbers [2] as described in two
  paragraphs above.
- Dropped gate and mux tests.

Changes since v3:
https://lore.kernel.org/r/20250812-clk-tests-docs-v3-0-054aed58dcd3@redhat.com
- Update clk_core struct members (Maxime)
- Add v2 rate negotiation logic and additional kunit tests
- Drop clk_dummy_rate_mhz() in kunit tests; use HZ_PER_MHZ

[1] https://lore.kernel.org/all/20260303115550.9279-1-mitltlatltl@gmail.com/
    https://lore.kernel.org/lkml/20230825-pll-mipi_keep_rate-v1-0-35bc43570730@oltmanns.dev/
    https://lore.kernel.org/linux-kernel/20230807-pll-mipi_set_rate_parent-v6-0-f173239a4b59@oltmanns.dev/
    https://lore.kernel.org/all/20241114065759.3341908-1-victor.liu@nxp.com/
    https://lore.kernel.org/linux-clk/20241121-ge-ian-debug-imx8-clk-tree-v1-0-0f1b722588fe@bootlin.com/

[2] Fixing Clock Tree Propagation in the Common Clk Framework
    https://www.youtube.com/watch?v=R8TytDzlcFs
    https://lpc.events/event/19/contributions/2152/

Signed-off-by: Brian Masney <bmasney@redhat.com>
---
Brian Masney (4):
      clk: test: introduce clk_dummy_div for a mock divider
      clk: test: introduce test suite for sibling rate changes on a divider
      clk: add support for coordinated rate changes from the providers
      clk: test: add kunit test for coordinated rate change

 drivers/clk/clk.c            | 207 +++++++++++++++++++++++++---
 drivers/clk/clk_test.c       | 317 +++++++++++++++++++++++++++++++++++++++++++
 include/linux/clk-provider.h |  39 +++++-
 3 files changed, 546 insertions(+), 17 deletions(-)
---
base-commit: fc7b1a72c6cd5cbbd989c6c32a6486e3e4e3594d
change-id: 20260305-clk-scaling-b3b63cae7624

Best regards,
-- 
Brian Masney <bmasney@redhat.com>


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH RFC v5 1/4] clk: test: introduce clk_dummy_div for a mock divider
  2026-03-06 23:21 [PATCH RFC v5 0/4] clk: add support for v1 / v2 clock rate negotiation and kunit tests Brian Masney
@ 2026-03-06 23:21 ` Brian Masney
  2026-03-06 23:21 ` [PATCH RFC v5 2/4] clk: test: introduce test suite for sibling rate changes on a divider Brian Masney
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 10+ messages in thread
From: Brian Masney @ 2026-03-06 23:21 UTC (permalink / raw)
  To: Michael Turquette, Stephen Boyd, Maxime Ripard, Alberto Ruiz
  Cc: linux-clk, linux-kernel, Brian Masney

This is used to mock up a divider in the clk kunit tests.

Signed-off-by: Brian Masney <bmasney@redhat.com>
---
 drivers/clk/clk_test.c | 44 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 44 insertions(+)

diff --git a/drivers/clk/clk_test.c b/drivers/clk/clk_test.c
index a268d7b5d4cb28ec1f029f828c31107f8e130556..88e35f4419c958983578750356a97c0a45effb55 100644
--- a/drivers/clk/clk_test.c
+++ b/drivers/clk/clk_test.c
@@ -136,6 +136,50 @@ static const struct clk_ops clk_dummy_single_parent_ops = {
 	.get_parent = clk_dummy_single_get_parent,
 };
 
+/* 4 ought to be enough for anybody */
+#define CLK_DUMMY_DIV_WIDTH 4
+#define CLK_DUMMY_DIV_FLAGS (CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ROUND_CLOSEST)
+
+struct clk_dummy_div {
+	struct clk_hw hw;
+	unsigned int div;
+};
+
+static unsigned long clk_dummy_div_recalc_rate(struct clk_hw *hw,
+					       unsigned long parent_rate)
+{
+	struct clk_dummy_div *div = container_of(hw, struct clk_dummy_div, hw);
+
+	return divider_recalc_rate(hw, parent_rate, div->div, NULL,
+				   CLK_DUMMY_DIV_FLAGS, CLK_DUMMY_DIV_WIDTH);
+}
+
+static int clk_dummy_div_determine_rate(struct clk_hw *hw,
+					struct clk_rate_request *req)
+{
+	if (!(clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) && req->best_parent_rate < req->rate)
+		return -EINVAL;
+
+	return divider_determine_rate(hw, req, NULL, CLK_DUMMY_DIV_WIDTH, CLK_DUMMY_DIV_FLAGS);
+}
+
+static int clk_dummy_div_set_rate(struct clk_hw *hw, unsigned long rate,
+				  unsigned long parent_rate)
+{
+	struct clk_dummy_div *div = container_of(hw, struct clk_dummy_div, hw);
+
+	div->div = divider_get_val(rate, parent_rate, NULL, CLK_DUMMY_DIV_WIDTH,
+				   CLK_DUMMY_DIV_FLAGS);
+
+	return 0;
+}
+
+static const struct clk_ops clk_dummy_div_ops = {
+	.recalc_rate = clk_dummy_div_recalc_rate,
+	.determine_rate = clk_dummy_div_determine_rate,
+	.set_rate = clk_dummy_div_set_rate,
+};
+
 struct clk_multiple_parent_ctx {
 	struct clk_dummy_context parents_ctx[2];
 	struct clk_hw hw;

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH RFC v5 2/4] clk: test: introduce test suite for sibling rate changes on a divider
  2026-03-06 23:21 [PATCH RFC v5 0/4] clk: add support for v1 / v2 clock rate negotiation and kunit tests Brian Masney
  2026-03-06 23:21 ` [PATCH RFC v5 1/4] clk: test: introduce clk_dummy_div for a mock divider Brian Masney
@ 2026-03-06 23:21 ` Brian Masney
  2026-03-08 13:46   ` kernel test robot
                     ` (2 more replies)
  2026-03-06 23:21 ` [PATCH RFC v5 3/4] clk: add support for coordinated rate changes from the providers Brian Masney
                   ` (2 subsequent siblings)
  4 siblings, 3 replies; 10+ messages in thread
From: Brian Masney @ 2026-03-06 23:21 UTC (permalink / raw)
  To: Michael Turquette, Stephen Boyd, Maxime Ripard, Alberto Ruiz
  Cc: linux-clk, linux-kernel, Brian Masney

Introduce a test suite that creates a parent with two divider-only
children, and add kunit tests that document the current behavior when a
sibling clk can unknowingly change the rate of another clk. Some boards
are unknowingly dependent on this behavior, and per discussions at the
2025 Linux Plumbers Conference in Tokyo, we can't break the existing
behavior. So let's add kunit tests with the current behavior so that
we can be made aware if that functionality changes in the future.

Link: https://lore.kernel.org/linux-clk/aUSWU7UymULCXOeF@redhat.com/
Link: https://lpc.events/event/19/contributions/2152/
Signed-off-by: Brian Masney <bmasney@redhat.com>
---
 drivers/clk/clk_test.c | 174 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 174 insertions(+)

diff --git a/drivers/clk/clk_test.c b/drivers/clk/clk_test.c
index 88e35f4419c958983578750356a97c0a45effb55..97cbf9dd16ee504d7687d8f0729b6e6e22a21afb 100644
--- a/drivers/clk/clk_test.c
+++ b/drivers/clk/clk_test.c
@@ -652,6 +652,179 @@ clk_multiple_parents_mux_test_suite = {
 	.test_cases = clk_multiple_parents_mux_test_cases,
 };
 
+struct clk_rate_change_sibling_div_div_context {
+	struct clk_dummy_context parent;
+	struct clk_dummy_div child1, child2;
+	struct clk *parent_clk, *child1_clk, *child2_clk;
+};
+
+struct clk_rate_change_sibling_div_div_test_param {
+	const char *desc;
+	const struct clk_ops *ops;
+};
+
+static const struct clk_rate_change_sibling_div_div_test_param
+clk_rate_change_sibling_div_div_test_regular_ops_params[] = {
+	{ .desc = "regular_ops", .ops = &clk_dummy_div_ops },
+};
+
+KUNIT_ARRAY_PARAM_DESC(clk_rate_change_sibling_div_div_test_regular_ops,
+		       clk_rate_change_sibling_div_div_test_regular_ops_params, desc)
+
+static int clk_rate_change_sibling_div_div_test_init(struct kunit *test)
+{
+	const struct clk_rate_change_sibling_div_div_test_param *param = test->param_value;
+	struct clk_rate_change_sibling_div_div_context *ctx;
+	int ret;
+
+	ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+	if (!ctx)
+		return -ENOMEM;
+	test->priv = ctx;
+
+	ctx->parent.hw.init = CLK_HW_INIT_NO_PARENT("parent", &clk_dummy_rate_ops, 0);
+	ctx->parent.rate = 24 * HZ_PER_MHZ;
+	ret = clk_hw_register_kunit(test, NULL, &ctx->parent.hw);
+	if (ret)
+		return ret;
+
+	ctx->child1.hw.init = CLK_HW_INIT_HW("child1", &ctx->parent.hw,
+					     param->ops, CLK_SET_RATE_PARENT);
+	ctx->child1.div = 1;
+	ret = clk_hw_register_kunit(test, NULL, &ctx->child1.hw);
+	if (ret)
+		return ret;
+
+	ctx->child2.hw.init = CLK_HW_INIT_HW("child2", &ctx->parent.hw,
+					     param->ops, CLK_SET_RATE_PARENT);
+	ctx->child2.div = 1;
+	ret = clk_hw_register_kunit(test, NULL, &ctx->child2.hw);
+	if (ret)
+		return ret;
+
+	ctx->parent_clk = clk_hw_get_clk(&ctx->parent.hw, NULL);
+	ctx->child1_clk = clk_hw_get_clk(&ctx->child1.hw, NULL);
+	ctx->child2_clk = clk_hw_get_clk(&ctx->child2.hw, NULL);
+
+	KUNIT_EXPECT_EQ(test, clk_get_rate(ctx->parent_clk), 24 * HZ_PER_MHZ);
+	KUNIT_EXPECT_EQ(test, clk_get_rate(ctx->child1_clk), 24 * HZ_PER_MHZ);
+	KUNIT_EXPECT_EQ(test, clk_get_rate(ctx->child2_clk), 24 * HZ_PER_MHZ);
+
+	return 0;
+}
+
+static void clk_rate_change_sibling_div_div_test_exit(struct kunit *test)
+{
+	struct clk_rate_change_sibling_div_div_context *ctx = test->priv;
+
+	clk_put(ctx->parent_clk);
+	clk_put(ctx->child1_clk);
+	clk_put(ctx->child2_clk);
+}
+
+/*
+ * Test that, for a parent with two divider-only children with CLK_SET_RATE_PARENT set
+ * and one requests a rate compatible with the existing parent rate, the parent and
+ * sibling rates are not affected.
+ */
+static void clk_test_rate_change_sibling_div_div_1(struct kunit *test)
+{
+	struct clk_rate_change_sibling_div_div_context *ctx = test->priv;
+	int ret;
+
+	ret = clk_set_rate(ctx->child1_clk, 6 * HZ_PER_MHZ);
+	KUNIT_ASSERT_EQ(test, ret, 0);
+
+	KUNIT_EXPECT_EQ(test, clk_get_rate(ctx->parent_clk), 24 * HZ_PER_MHZ);
+	KUNIT_EXPECT_EQ(test, clk_get_rate(ctx->child1_clk), 6 * HZ_PER_MHZ);
+	KUNIT_EXPECT_EQ(test, ctx->child1.div, 4);
+	KUNIT_EXPECT_EQ(test, clk_get_rate(ctx->child2_clk), 24 * HZ_PER_MHZ);
+	KUNIT_EXPECT_EQ(test, ctx->child2.div, 1);
+}
+
+/*
+ * Test that, for a parent with two divider-only children with CLK_SET_RATE_PARENT
+ * set and one requests a rate incompatible with the existing parent rate, the
+ * sibling rate is also affected. This preserves existing behavior in the clk
+ * core that some drivers may be unknowingly dependent on.
+ */
+static void clk_test_rate_change_sibling_div_div_2(struct kunit *test)
+{
+	struct clk_rate_change_sibling_div_div_context *ctx = test->priv;
+	int ret;
+
+	ret = clk_set_rate(ctx->child1_clk, 48 * HZ_PER_MHZ);
+	KUNIT_ASSERT_EQ(test, ret, 0);
+
+	KUNIT_EXPECT_EQ(test, clk_get_rate(ctx->parent_clk), 48 * HZ_PER_MHZ);
+	KUNIT_EXPECT_EQ(test, clk_get_rate(ctx->child1_clk), 48 * HZ_PER_MHZ);
+	KUNIT_EXPECT_EQ(test, ctx->child1.div, 1);
+
+	/*
+	 * The sibling's rate is changed from 24 to 48 MHz. This keeps the
+	 * long-standing behavior of the clk core that some drivers may be
+	 * unknowingly dependent on.
+	 */
+	KUNIT_EXPECT_EQ(test, clk_get_rate(ctx->child2_clk), 48 * HZ_PER_MHZ);
+	KUNIT_EXPECT_EQ(test, ctx->child2.div, 1);
+}
+
+/*
+ * Test that, for a parent with two divider-only children with CLK_SET_RATE_PARENT
+ * set and one requests a rate incompatible with the existing parent rate, the
+ * sibling rate is also affected. This preserves existing behavior in the clk
+ * core that some drivers may be unknowingly dependent on.
+ */
+static void clk_test_rate_change_sibling_div_div_3(struct kunit *test)
+{
+	struct clk_rate_change_sibling_div_div_context *ctx = test->priv;
+	int ret;
+
+	ret = clk_set_rate(ctx->child1_clk, 32 * HZ_PER_MHZ);
+	KUNIT_ASSERT_EQ(test, ret, 0);
+
+	ret = clk_set_rate(ctx->child2_clk, 48 * HZ_PER_MHZ);
+	KUNIT_ASSERT_EQ(test, ret, 0);
+
+	/*
+	 * The last sibling rate change is the one that was successful, and
+	 * wins. The parent, and two children are all changed to 48 MHz. This
+	 * keeps the long-standing behavior of the clk core that some drivers
+	 * may be unknowingly dependent on.
+	 *
+	 * In the case where the parent needs to be 96 MHz, and the children
+	 * need to be at 32 MHz / 48 MHz, then that logic needs to exist in
+	 * the clk provider for the relevant clks.
+	 */
+	KUNIT_EXPECT_EQ(test, clk_get_rate(ctx->parent_clk), 48 * HZ_PER_MHZ);
+	KUNIT_EXPECT_EQ(test, clk_get_rate(ctx->child1_clk), 48 * HZ_PER_MHZ);
+	KUNIT_EXPECT_EQ(test, ctx->child1.div, 1);
+	KUNIT_EXPECT_EQ(test, clk_get_rate(ctx->child2_clk), 48 * HZ_PER_MHZ);
+	KUNIT_EXPECT_EQ(test, ctx->child2.div, 1);
+}
+
+static struct kunit_case clk_rate_change_sibling_div_div_cases[] = {
+	KUNIT_CASE_PARAM(clk_test_rate_change_sibling_div_div_1,
+			 clk_rate_change_sibling_div_div_test_regular_ops_gen_params),
+	KUNIT_CASE_PARAM(clk_test_rate_change_sibling_div_div_2,
+			 clk_rate_change_sibling_div_div_test_regular_ops_gen_params),
+	KUNIT_CASE_PARAM(clk_test_rate_change_sibling_div_div_3,
+			 clk_rate_change_sibling_div_div_test_regular_ops_gen_params),
+	{}
+};
+
+/*
+ * Test suite that creates a parent with two divider-only children, and
+ * documents the behavior of what happens to the sibling clock when one child
+ * changes its rate.
+ */
+static struct kunit_suite clk_rate_change_sibling_div_div_test_suite = {
+	.name = "clk-rate-change-sibling-div-div",
+	.init = clk_rate_change_sibling_div_div_test_init,
+	.exit = clk_rate_change_sibling_div_div_test_exit,
+	.test_cases = clk_rate_change_sibling_div_div_cases,
+};
+
 static int
 clk_orphan_transparent_multiple_parent_mux_test_init(struct kunit *test)
 {
@@ -3592,6 +3765,7 @@ kunit_test_suites(
 	&clk_leaf_mux_set_rate_parent_test_suite,
 	&clk_test_suite,
 	&clk_multiple_parents_mux_test_suite,
+	&clk_rate_change_sibling_div_div_test_suite,
 	&clk_mux_no_reparent_test_suite,
 	&clk_mux_notifier_test_suite,
 	&clk_orphan_transparent_multiple_parent_mux_test_suite,

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH RFC v5 3/4] clk: add support for coordinated rate changes from the providers
  2026-03-06 23:21 [PATCH RFC v5 0/4] clk: add support for v1 / v2 clock rate negotiation and kunit tests Brian Masney
  2026-03-06 23:21 ` [PATCH RFC v5 1/4] clk: test: introduce clk_dummy_div for a mock divider Brian Masney
  2026-03-06 23:21 ` [PATCH RFC v5 2/4] clk: test: introduce test suite for sibling rate changes on a divider Brian Masney
@ 2026-03-06 23:21 ` Brian Masney
  2026-03-06 23:21 ` [PATCH RFC v5 4/4] clk: test: add kunit test for coordinated rate change Brian Masney
  2026-03-12 13:09 ` [PATCH RFC v5 0/4] clk: add support for v1 / v2 clock rate negotiation and kunit tests Brian Masney
  4 siblings, 0 replies; 10+ messages in thread
From: Brian Masney @ 2026-03-06 23:21 UTC (permalink / raw)
  To: Michael Turquette, Stephen Boyd, Maxime Ripard, Alberto Ruiz
  Cc: linux-clk, linux-kernel, Brian Masney

Some clock topologies require multiple clocks to change rates in a
specific sequence to maintain stability and avoid glitches. For example,
sibling clocks derived from a shared parent may need coordinated updates
to prevent output disruption.

The existing rate change propagation mechanism changes clocks
recursively from parent to children, but doesn't provide a way for
providers to explicitly control the order of rate changes across
multiple clocks or coordinate updates among siblings.

At the moment, changes to sibling clocks can unknowingly affect the
rate of other clocks in the tree, and some boards are unknowingly
dependent on this behavior.

Let's add infrastructure to the clk core so that clks can opt into
a new way to allow changing the rates of multiple clks by:

- Add a CLK_V2_RATE_NEGOTIATION flag for clks that want to participate
  in this new negotiation logic.

- A clk provider calls clk_hw_get_v2_stable_clks() to get the list of
  clks with this set as part of the planning phase.

- It is up the to clk provider to determine what the new parent rate
  should be for that part of the clk subtree.

- The clk provider calls clk_hw_add_coordinated_rate_changes() for any
  clks that should have also their rate changed. It is up to the
  provider to provide the correct order that the clks should be changed.

- The clk core executes the rate changes serially in the order
  specified by the provider.

If the change list is empty, then the behavior falls back to the
existing propagation mechanism to maintain compatibility with existing
clk drivers.

Link: https://lore.kernel.org/linux-clk/aUSWU7UymULCXOeF@redhat.com/
Link: https://lpc.events/event/19/contributions/2152/
Signed-off-by: Brian Masney <bmasney@redhat.com>
---
 drivers/clk/clk.c            | 207 +++++++++++++++++++++++++++++++++++++++----
 include/linux/clk-provider.h |  39 +++++++-
 2 files changed, 229 insertions(+), 17 deletions(-)

diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 47093cda9df32223c1120c3710261296027c4cd3..c8ff31a6799c75746f2e4095cc3ff77fdbbdf6ef 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -838,6 +838,111 @@ void clk_hw_set_rate_range(struct clk_hw *hw, unsigned long min_rate,
 }
 EXPORT_SYMBOL_GPL(clk_hw_set_rate_range);
 
+static int __clk_hw_get_v2_stable_clks(struct clk_hw *hw,
+				       struct clk_hw *requesting_hw,
+				       unsigned long requesting_rate,
+				       struct list_head *stable_clks)
+{
+	struct clk_core *child;
+	int ret;
+
+	if (hw->core->flags & CLK_V2_RATE_NEGOTIATION) {
+		struct clk_rate_change *clk_node;
+
+		clk_node = kzalloc_obj(*clk_node);
+		if (!clk_node)
+			return -ENOMEM;
+
+		clk_node->hw = hw;
+		clk_node->current_rate = clk_hw_get_rate(hw);
+
+		if (hw == requesting_hw)
+			clk_node->target_rate = requesting_rate;
+		else
+			clk_node->target_rate = clk_hw_get_rate(hw);
+
+		clk_node->new_parent_index = 0;
+		list_add_tail(&clk_node->node, stable_clks);
+	}
+
+	hlist_for_each_entry(child, &hw->core->children, child_node) {
+		ret = __clk_hw_get_v2_stable_clks(child->hw, requesting_hw,
+						  requesting_rate,
+						  stable_clks);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+int clk_hw_get_v2_stable_clks(struct clk_rate_request *req, struct clk_hw *parent_hw,
+			      struct list_head *stable_clks)
+{
+	return __clk_hw_get_v2_stable_clks(parent_hw, req->core->hw, req->rate,
+					   stable_clks);
+}
+EXPORT_SYMBOL_GPL(clk_hw_get_v2_stable_clks);
+
+void clk_hw_free_rate_changes(struct list_head *stable_clks)
+{
+	struct clk_rate_change *clk_node, *tmp;
+
+	list_for_each_entry_safe(clk_node, tmp, stable_clks, node) {
+		list_del(&clk_node->node);
+		kfree(clk_node);
+	}
+}
+EXPORT_SYMBOL_GPL(clk_hw_free_rate_changes);
+
+static int clk_hw_add_rate_change(struct clk_rate_request *req, struct clk_hw *hw,
+				  unsigned long old_rate, unsigned long new_rate,
+				  u8 new_parent_index)
+{
+	struct clk_rate_change *change;
+
+	change = kzalloc_obj(*change);
+	if (!change)
+		return -ENOMEM;
+
+	change->hw = hw;
+	change->current_rate = old_rate;
+	change->target_rate = new_rate;
+	change->new_parent_index = new_parent_index;
+	list_add_tail(&change->node, &req->ordered_rate_changes);
+
+	return 0;
+}
+
+int clk_hw_add_coordinated_rate_changes(struct clk_rate_request *req,
+					struct clk_hw *parent_hw,
+					unsigned long parent_old_rate,
+					unsigned long parent_new_rate,
+					struct list_head *stable_clks)
+{
+	struct clk_rate_change *clk_node;
+	int ret;
+
+	ret = clk_hw_add_rate_change(req, parent_hw, parent_old_rate,
+				     parent_new_rate, 0);
+	if (ret)
+		goto out_free;
+
+	list_for_each_entry(clk_node, stable_clks, node) {
+		ret = clk_hw_add_rate_change(req, clk_node->hw,
+					     clk_node->current_rate,
+					     clk_node->target_rate, 0);
+		if (ret)
+			goto out_free;
+	}
+
+	ret = 0;
+out_free:
+	clk_hw_free_rate_changes(stable_clks);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(clk_hw_add_coordinated_rate_changes);
+
 /*
  * __clk_mux_determine_rate - clk_ops::determine_rate implementation for a mux type clk
  * @hw: mux type clk to determine rate on
@@ -1615,6 +1720,7 @@ static void clk_core_init_rate_req(struct clk_core * const core,
 		return;
 
 	memset(req, 0, sizeof(*req));
+	INIT_LIST_HEAD(&req->ordered_rate_changes);
 	req->max_rate = ULONG_MAX;
 
 	if (!core)
@@ -2268,7 +2374,8 @@ static void clk_calc_subtree(struct clk_core *core, unsigned long new_rate,
  * changed.
  */
 static struct clk_core *clk_calc_new_rates(struct clk_core *core,
-					   unsigned long rate)
+					   unsigned long rate,
+					   struct list_head *ordered_rate_changes)
 {
 	struct clk_core *top = core;
 	struct clk_core *old_parent, *parent;
@@ -2308,6 +2415,12 @@ static struct clk_core *clk_calc_new_rates(struct clk_core *core,
 		new_rate = req.rate;
 		parent = req.best_parent_hw ? req.best_parent_hw->core : NULL;
 
+		/* Clock provider populated coordinated rate changes */
+		if (ordered_rate_changes && !list_empty(&req.ordered_rate_changes)) {
+			/* Transfer ownership of the list to caller */
+			list_splice_init(&req.ordered_rate_changes, ordered_rate_changes);
+		}
+
 		if (new_rate < min_rate || new_rate > max_rate)
 			return NULL;
 	} else if (!parent || !(core->flags & CLK_SET_RATE_PARENT)) {
@@ -2316,7 +2429,7 @@ static struct clk_core *clk_calc_new_rates(struct clk_core *core,
 		return NULL;
 	} else {
 		/* pass-through clock with adjustable parent */
-		top = clk_calc_new_rates(parent, rate);
+		top = clk_calc_new_rates(parent, rate, NULL);
 		new_rate = parent->new_rate;
 		goto out;
 	}
@@ -2341,7 +2454,7 @@ static struct clk_core *clk_calc_new_rates(struct clk_core *core,
 
 	if ((core->flags & CLK_SET_RATE_PARENT) && parent &&
 	    best_parent_rate != parent->rate)
-		top = clk_calc_new_rates(parent, best_parent_rate);
+		top = clk_calc_new_rates(parent, best_parent_rate, NULL);
 
 out:
 	clk_calc_subtree(core, new_rate, parent, p_index);
@@ -2461,7 +2574,7 @@ static void clk_change_rate(struct clk_core *core)
 		__clk_notify(core, POST_RATE_CHANGE, old_rate, core->rate);
 
 	if (core->flags & CLK_RECALC_NEW_RATES)
-		(void)clk_calc_new_rates(core, core->new_rate);
+		(void)clk_calc_new_rates(core, core->new_rate, NULL);
 
 	/*
 	 * Use safe iteration, as change_rate can actually swap parents
@@ -2511,9 +2624,58 @@ static unsigned long clk_core_req_round_rate_nolock(struct clk_core *core,
 	return ret ? 0 : req.rate;
 }
 
+static int clk_apply_coordinated_rate_changes(struct clk_rate_request *req)
+{
+	struct clk_rate_change *change;
+	int ret;
+
+	/*
+	 * FIXME: There's currently no mechanism to roll back in case of an
+	 * error. This can leave the clock tree in an inconsistent state.
+	 */
+	list_for_each_entry(change, &req->ordered_rate_changes, node) {
+		struct clk_hw *hw = change->hw;
+
+		if (!hw || !hw->core)
+			continue;
+
+		if (change->new_parent_index && hw->core->ops->set_parent) {
+			ret = hw->core->ops->set_parent(hw, change->new_parent_index);
+			if (ret)
+				return ret;
+		}
+
+		if (hw->core->ops->set_rate) {
+			unsigned long parent_rate = 0;
+
+			if (hw->core->parent)
+				parent_rate = hw->core->parent->rate;
+
+			ret = hw->core->ops->set_rate(hw, change->target_rate, parent_rate);
+			if (ret)
+				return ret;
+
+			hw->core->rate = change->target_rate;
+		}
+	}
+
+	return 0;
+}
+
+static void clk_rate_request_cleanup(struct clk_rate_request *req)
+{
+	struct clk_rate_change *change, *tmp;
+
+	list_for_each_entry_safe(change, tmp, &req->ordered_rate_changes, node) {
+		list_del(&change->node);
+		kfree(change);
+	}
+}
+
 static int clk_core_set_rate_nolock(struct clk_core *core,
 				    unsigned long req_rate)
 {
+	struct clk_rate_request rate_req;
 	struct clk_core *top, *fail_clk;
 	unsigned long rate;
 	int ret;
@@ -2521,6 +2683,8 @@ static int clk_core_set_rate_nolock(struct clk_core *core,
 	if (!core)
 		return 0;
 
+	clk_core_init_rate_req(core, &rate_req, req_rate);
+
 	rate = clk_core_req_round_rate_nolock(core, req_rate);
 
 	/* bail early if nothing to do */
@@ -2532,7 +2696,7 @@ static int clk_core_set_rate_nolock(struct clk_core *core,
 		return -EBUSY;
 
 	/* calculate new rates and get the topmost changed clock */
-	top = clk_calc_new_rates(core, req_rate);
+	top = clk_calc_new_rates(core, req_rate, &rate_req.ordered_rate_changes);
 	if (!top)
 		return -EINVAL;
 
@@ -2540,22 +2704,33 @@ static int clk_core_set_rate_nolock(struct clk_core *core,
 	if (ret)
 		return ret;
 
-	/* notify that we are about to change rates */
-	fail_clk = clk_propagate_rate_change(top, PRE_RATE_CHANGE);
-	if (fail_clk) {
-		pr_debug("%s: failed to set %s rate\n", __func__,
-				fail_clk->name);
-		clk_propagate_rate_change(top, ABORT_RATE_CHANGE);
-		ret = -EBUSY;
-		goto err;
-	}
+	if (!list_empty(&rate_req.ordered_rate_changes)) {
+		/* Apply coordinated rate changes in order */
+		ret = clk_apply_coordinated_rate_changes(&rate_req);
+		if (ret) {
+			pr_debug("%s: failed to apply coordinated rate changes\n", __func__);
+			goto err;
+		}
+	} else {
+		/* Use traditional rate change propagation */
+		/* notify that we are about to change rates */
+		fail_clk = clk_propagate_rate_change(top, PRE_RATE_CHANGE);
+		if (fail_clk) {
+			pr_debug("%s: failed to set %s rate\n", __func__,
+					fail_clk->name);
+			clk_propagate_rate_change(top, ABORT_RATE_CHANGE);
+			ret = -EBUSY;
+			goto err;
+		}
 
-	/* change the rates */
-	clk_change_rate(top);
+		/* change the rates */
+		clk_change_rate(top);
+	}
 
 	core->req_rate = req_rate;
 err:
 	clk_pm_runtime_put(core);
+	clk_rate_request_cleanup(&rate_req);
 
 	return ret;
 }
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index 630705a47129453c241f1b1755f2c2f2a7ed8f77..26ab04196411f96dcb5d7c91c06ce3ca4117ca50 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -32,6 +32,8 @@
 #define CLK_OPS_PARENT_ENABLE	BIT(12)
 /* duty cycle call may be forwarded to the parent clock */
 #define CLK_DUTY_CYCLE_PARENT	BIT(13)
+/* clock participates in v2 rate negotiation */
+#define CLK_V2_RATE_NEGOTIATION	BIT(14)
 
 struct clk;
 struct clk_hw;
@@ -53,7 +55,10 @@ struct dentry;
  *			requested constraints.
  * @best_parent_hw:	The most appropriate parent clock that fulfills the
  *			requested constraints.
- *
+ * @ordered_rate_changes:	Execution list of rate changes for coordinated updates.
+ *				Clock providers populate this list when they need to
+ *				update multiple clocks in a specific order to maintain
+ *				rate stability for all consumers.
  */
 struct clk_rate_request {
 	struct clk_core *core;
@@ -62,6 +67,7 @@ struct clk_rate_request {
 	unsigned long max_rate;
 	unsigned long best_parent_rate;
 	struct clk_hw *best_parent_hw;
+	struct list_head ordered_rate_changes;
 };
 
 void clk_hw_init_rate_request(const struct clk_hw *hw,
@@ -1437,6 +1443,37 @@ static inline void __clk_hw_set_clk(struct clk_hw *dst, struct clk_hw *src)
 	dst->core = src->core;
 }
 
+/**
+ * struct clk_rate_change - Describes a clock rate change
+ *
+ * Used in both planning and execution phases of coordinated rate changes.
+ * During planning, collects information about sibling clocks. During execution,
+ * contains the final rate change instructions to apply.
+ *
+ * @hw:			The clock hardware that needs to change rate
+ * @current_rate:	The current rate of this clock
+ * @target_rate:	The target rate for this clock
+ * @new_parent_index:	The index of the new parent (for muxes), or 0
+ * @node:		List node for chaining entries
+ */
+struct clk_rate_change {
+	struct clk_hw *hw;
+	unsigned long current_rate;
+	unsigned long target_rate;
+	u8 new_parent_index;
+	struct list_head node;
+};
+
+int clk_hw_get_v2_stable_clks(struct clk_rate_request *req,
+			      struct clk_hw *parent_hw,
+			      struct list_head *stable_clks);
+void clk_hw_free_rate_changes(struct list_head *stable_clks);
+int clk_hw_add_coordinated_rate_changes(struct clk_rate_request *req,
+					struct clk_hw *parent_hw,
+					unsigned long parent_old_rate,
+					unsigned long parent_new_rate,
+					struct list_head *stable_clks);
+
 static inline long divider_round_rate(struct clk_hw *hw, unsigned long rate,
 				      unsigned long *prate,
 				      const struct clk_div_table *table,

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH RFC v5 4/4] clk: test: add kunit test for coordinated rate change
  2026-03-06 23:21 [PATCH RFC v5 0/4] clk: add support for v1 / v2 clock rate negotiation and kunit tests Brian Masney
                   ` (2 preceding siblings ...)
  2026-03-06 23:21 ` [PATCH RFC v5 3/4] clk: add support for coordinated rate changes from the providers Brian Masney
@ 2026-03-06 23:21 ` Brian Masney
  2026-03-12 13:09 ` [PATCH RFC v5 0/4] clk: add support for v1 / v2 clock rate negotiation and kunit tests Brian Masney
  4 siblings, 0 replies; 10+ messages in thread
From: Brian Masney @ 2026-03-06 23:21 UTC (permalink / raw)
  To: Michael Turquette, Stephen Boyd, Maxime Ripard, Alberto Ruiz
  Cc: linux-clk, linux-kernel, Brian Masney

Add kunit tests to demonstrate how to use the coordinated rate change
clk APIs, and to validate that they are working as expected.

Link: https://lore.kernel.org/linux-clk/aUSWU7UymULCXOeF@redhat.com/
Link: https://lpc.events/event/19/contributions/2152/
Signed-off-by: Brian Masney <bmasney@redhat.com>
---
 drivers/clk/clk_test.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 101 insertions(+), 2 deletions(-)

diff --git a/drivers/clk/clk_test.c b/drivers/clk/clk_test.c
index 97cbf9dd16ee504d7687d8f0729b6e6e22a21afb..886190408545635926689046f6237260303b2271 100644
--- a/drivers/clk/clk_test.c
+++ b/drivers/clk/clk_test.c
@@ -5,6 +5,7 @@
 #include <linux/clk.h>
 #include <linux/clk-provider.h>
 #include <linux/clk/clk-conf.h>
+#include <linux/lcm.h>
 #include <linux/of.h>
 #include <linux/platform_device.h>
 
@@ -180,6 +181,46 @@ static const struct clk_ops clk_dummy_div_ops = {
 	.set_rate = clk_dummy_div_set_rate,
 };
 
+static int clk_dummy_div_coordinating_determine_rate(struct clk_hw *hw,
+						     struct clk_rate_request *req)
+{
+	struct clk_hw *parent_hw = clk_hw_get_parent(hw);
+	struct clk_rate_change *child_node;
+	unsigned long lcm_rate = 0;
+	LIST_HEAD(stable_clks);
+	int ret;
+
+	ret = clk_hw_get_v2_stable_clks(req, parent_hw, &stable_clks);
+	if (ret)
+		return ret;
+
+	list_for_each_entry(child_node, &stable_clks, node) {
+		if (child_node->target_rate == 0)
+			continue;
+		if (lcm_rate == 0)
+			lcm_rate = child_node->target_rate;
+		else
+			lcm_rate = lcm(lcm_rate, child_node->target_rate);
+	}
+
+	ret = clk_hw_add_coordinated_rate_changes(req, parent_hw,
+						  clk_hw_get_rate(parent_hw),
+						  lcm_rate, &stable_clks);
+	if (ret)
+		return ret;
+
+	req->best_parent_rate = lcm_rate;
+	req->best_parent_hw = parent_hw;
+
+	return 0;
+}
+
+static const struct clk_ops clk_dummy_div_coordinating_ops = {
+	.recalc_rate = clk_dummy_div_recalc_rate,
+	.determine_rate = clk_dummy_div_coordinating_determine_rate,
+	.set_rate = clk_dummy_div_set_rate,
+};
+
 struct clk_multiple_parent_ctx {
 	struct clk_dummy_context parents_ctx[2];
 	struct clk_hw hw;
@@ -671,6 +712,14 @@ clk_rate_change_sibling_div_div_test_regular_ops_params[] = {
 KUNIT_ARRAY_PARAM_DESC(clk_rate_change_sibling_div_div_test_regular_ops,
 		       clk_rate_change_sibling_div_div_test_regular_ops_params, desc)
 
+static const struct clk_rate_change_sibling_div_div_test_param
+clk_rate_change_sibling_div_div_test_coord_ops_params[] = {
+	{ .desc = "coordinating_ops", .ops = &clk_dummy_div_coordinating_ops },
+};
+
+KUNIT_ARRAY_PARAM_DESC(clk_rate_change_sibling_div_div_test_coord_ops,
+		       clk_rate_change_sibling_div_div_test_coord_ops_params, desc)
+
 static int clk_rate_change_sibling_div_div_test_init(struct kunit *test)
 {
 	const struct clk_rate_change_sibling_div_div_test_param *param = test->param_value;
@@ -689,14 +738,16 @@ static int clk_rate_change_sibling_div_div_test_init(struct kunit *test)
 		return ret;
 
 	ctx->child1.hw.init = CLK_HW_INIT_HW("child1", &ctx->parent.hw,
-					     param->ops, CLK_SET_RATE_PARENT);
+					     param->ops,
+					     CLK_SET_RATE_PARENT | CLK_V2_RATE_NEGOTIATION);
 	ctx->child1.div = 1;
 	ret = clk_hw_register_kunit(test, NULL, &ctx->child1.hw);
 	if (ret)
 		return ret;
 
 	ctx->child2.hw.init = CLK_HW_INIT_HW("child2", &ctx->parent.hw,
-					     param->ops, CLK_SET_RATE_PARENT);
+					     param->ops,
+					     CLK_SET_RATE_PARENT | CLK_V2_RATE_NEGOTIATION);
 	ctx->child2.div = 1;
 	ret = clk_hw_register_kunit(test, NULL, &ctx->child2.hw);
 	if (ret)
@@ -803,6 +854,48 @@ static void clk_test_rate_change_sibling_div_div_3(struct kunit *test)
 	KUNIT_EXPECT_EQ(test, ctx->child2.div, 1);
 }
 
+static void clk_test_rate_change_sibling_div_div_4(struct kunit *test)
+{
+	struct clk_rate_change_sibling_div_div_context *ctx = test->priv;
+	int ret;
+
+	ret = clk_set_rate(ctx->child1_clk, 48 * HZ_PER_MHZ);
+	KUNIT_ASSERT_EQ(test, ret, 0);
+
+	/*
+	 * With coordinated rate changes, the parent should be at 48 MHz,
+	 * child1 at 48 MHz (div=1), and child2 at 24 MHz (div=2). Child2's
+	 * keeps the same frequency, and the divider is changed from 1 to 2.
+	 */
+	KUNIT_EXPECT_EQ(test, clk_get_rate(ctx->parent_clk), 48 * HZ_PER_MHZ);
+	KUNIT_EXPECT_EQ(test, clk_get_rate(ctx->child1_clk), 48 * HZ_PER_MHZ);
+	KUNIT_EXPECT_EQ(test, ctx->child1.div, 1);
+	KUNIT_EXPECT_EQ(test, clk_get_rate(ctx->child2_clk), 24 * HZ_PER_MHZ);
+	KUNIT_EXPECT_EQ(test, ctx->child2.div, 2);
+}
+
+static void clk_test_rate_change_sibling_div_div_5(struct kunit *test)
+{
+	struct clk_rate_change_sibling_div_div_context *ctx = test->priv;
+	int ret;
+
+	ret = clk_set_rate(ctx->child1_clk, 32 * HZ_PER_MHZ);
+	KUNIT_ASSERT_EQ(test, ret, 0);
+
+	ret = clk_set_rate(ctx->child2_clk, 48 * HZ_PER_MHZ);
+	KUNIT_ASSERT_EQ(test, ret, 0);
+
+	/*
+	 * With coordinated rate changes, the parent should be at 96 MHz,
+	 * child1 at 32 MHz (div=3), and child2 at 48 MHz (div=2).
+	 */
+	KUNIT_EXPECT_EQ(test, clk_get_rate(ctx->parent_clk), 96 * HZ_PER_MHZ);
+	KUNIT_EXPECT_EQ(test, clk_get_rate(ctx->child1_clk), 32 * HZ_PER_MHZ);
+	KUNIT_EXPECT_EQ(test, ctx->child1.div, 3);
+	KUNIT_EXPECT_EQ(test, clk_get_rate(ctx->child2_clk), 48 * HZ_PER_MHZ);
+	KUNIT_EXPECT_EQ(test, ctx->child2.div, 2);
+}
+
 static struct kunit_case clk_rate_change_sibling_div_div_cases[] = {
 	KUNIT_CASE_PARAM(clk_test_rate_change_sibling_div_div_1,
 			 clk_rate_change_sibling_div_div_test_regular_ops_gen_params),
@@ -810,6 +903,12 @@ static struct kunit_case clk_rate_change_sibling_div_div_cases[] = {
 			 clk_rate_change_sibling_div_div_test_regular_ops_gen_params),
 	KUNIT_CASE_PARAM(clk_test_rate_change_sibling_div_div_3,
 			 clk_rate_change_sibling_div_div_test_regular_ops_gen_params),
+	KUNIT_CASE_PARAM(clk_test_rate_change_sibling_div_div_1,
+			 clk_rate_change_sibling_div_div_test_coord_ops_gen_params),
+	KUNIT_CASE_PARAM(clk_test_rate_change_sibling_div_div_4,
+			 clk_rate_change_sibling_div_div_test_coord_ops_gen_params),
+	KUNIT_CASE_PARAM(clk_test_rate_change_sibling_div_div_5,
+			 clk_rate_change_sibling_div_div_test_coord_ops_gen_params),
 	{}
 };
 

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH RFC v5 2/4] clk: test: introduce test suite for sibling rate changes on a divider
  2026-03-06 23:21 ` [PATCH RFC v5 2/4] clk: test: introduce test suite for sibling rate changes on a divider Brian Masney
@ 2026-03-08 13:46   ` kernel test robot
  2026-03-08 14:37   ` kernel test robot
  2026-03-09 10:32   ` Brian Masney
  2 siblings, 0 replies; 10+ messages in thread
From: kernel test robot @ 2026-03-08 13:46 UTC (permalink / raw)
  To: Brian Masney; +Cc: oe-kbuild-all

Hi Brian,

[This is a private test report for your RFC patch.]
kernel test robot noticed the following build errors:

[auto build test ERROR on fc7b1a72c6cd5cbbd989c6c32a6486e3e4e3594d]

url:    https://github.com/intel-lab-lkp/linux/commits/Brian-Masney/clk-test-introduce-clk_dummy_div-for-a-mock-divider/20260307-074328
base:   fc7b1a72c6cd5cbbd989c6c32a6486e3e4e3594d
patch link:    https://lore.kernel.org/r/20260306-clk-scaling-v5-2-d21b84ee6f27%40redhat.com
patch subject: [PATCH RFC v5 2/4] clk: test: introduce test suite for sibling rate changes on a divider
config: alpha-allyesconfig (https://download.01.org/0day-ci/archive/20260308/202603082131.EH1YuBck-lkp@intel.com/config)
compiler: alpha-linux-gcc (GCC) 15.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260308/202603082131.EH1YuBck-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202603082131.EH1YuBck-lkp@intel.com/

All errors (new ones prefixed by >>):

   drivers/clk/clk_test.c: In function 'clk_rate_change_sibling_div_div_test_init':
>> drivers/clk/clk_test.c:686:33: error: 'HZ_PER_MHZ' undeclared (first use in this function)
     686 |         ctx->parent.rate = 24 * HZ_PER_MHZ;
         |                                 ^~~~~~~~~~
   drivers/clk/clk_test.c:686:33: note: each undeclared identifier is reported only once for each function it appears in
   drivers/clk/clk_test.c: In function 'clk_test_rate_change_sibling_div_div_1':
   drivers/clk/clk_test.c:735:49: error: 'HZ_PER_MHZ' undeclared (first use in this function)
     735 |         ret = clk_set_rate(ctx->child1_clk, 6 * HZ_PER_MHZ);
         |                                                 ^~~~~~~~~~
   drivers/clk/clk_test.c: In function 'clk_test_rate_change_sibling_div_div_2':
   drivers/clk/clk_test.c:756:50: error: 'HZ_PER_MHZ' undeclared (first use in this function)
     756 |         ret = clk_set_rate(ctx->child1_clk, 48 * HZ_PER_MHZ);
         |                                                  ^~~~~~~~~~
   drivers/clk/clk_test.c: In function 'clk_test_rate_change_sibling_div_div_3':
   drivers/clk/clk_test.c:783:50: error: 'HZ_PER_MHZ' undeclared (first use in this function)
     783 |         ret = clk_set_rate(ctx->child1_clk, 32 * HZ_PER_MHZ);
         |                                                  ^~~~~~~~~~


vim +/HZ_PER_MHZ +686 drivers/clk/clk_test.c

   670	
   671	KUNIT_ARRAY_PARAM_DESC(clk_rate_change_sibling_div_div_test_regular_ops,
   672			       clk_rate_change_sibling_div_div_test_regular_ops_params, desc)
   673	
   674	static int clk_rate_change_sibling_div_div_test_init(struct kunit *test)
   675	{
   676		const struct clk_rate_change_sibling_div_div_test_param *param = test->param_value;
   677		struct clk_rate_change_sibling_div_div_context *ctx;
   678		int ret;
   679	
   680		ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
   681		if (!ctx)
   682			return -ENOMEM;
   683		test->priv = ctx;
   684	
   685		ctx->parent.hw.init = CLK_HW_INIT_NO_PARENT("parent", &clk_dummy_rate_ops, 0);
 > 686		ctx->parent.rate = 24 * HZ_PER_MHZ;
   687		ret = clk_hw_register_kunit(test, NULL, &ctx->parent.hw);
   688		if (ret)
   689			return ret;
   690	
   691		ctx->child1.hw.init = CLK_HW_INIT_HW("child1", &ctx->parent.hw,
   692						     param->ops, CLK_SET_RATE_PARENT);
   693		ctx->child1.div = 1;
   694		ret = clk_hw_register_kunit(test, NULL, &ctx->child1.hw);
   695		if (ret)
   696			return ret;
   697	
   698		ctx->child2.hw.init = CLK_HW_INIT_HW("child2", &ctx->parent.hw,
   699						     param->ops, CLK_SET_RATE_PARENT);
   700		ctx->child2.div = 1;
   701		ret = clk_hw_register_kunit(test, NULL, &ctx->child2.hw);
   702		if (ret)
   703			return ret;
   704	
   705		ctx->parent_clk = clk_hw_get_clk(&ctx->parent.hw, NULL);
   706		ctx->child1_clk = clk_hw_get_clk(&ctx->child1.hw, NULL);
   707		ctx->child2_clk = clk_hw_get_clk(&ctx->child2.hw, NULL);
   708	
   709		KUNIT_EXPECT_EQ(test, clk_get_rate(ctx->parent_clk), 24 * HZ_PER_MHZ);
   710		KUNIT_EXPECT_EQ(test, clk_get_rate(ctx->child1_clk), 24 * HZ_PER_MHZ);
   711		KUNIT_EXPECT_EQ(test, clk_get_rate(ctx->child2_clk), 24 * HZ_PER_MHZ);
   712	
   713		return 0;
   714	}
   715	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH RFC v5 2/4] clk: test: introduce test suite for sibling rate changes on a divider
  2026-03-06 23:21 ` [PATCH RFC v5 2/4] clk: test: introduce test suite for sibling rate changes on a divider Brian Masney
  2026-03-08 13:46   ` kernel test robot
@ 2026-03-08 14:37   ` kernel test robot
  2026-03-09 10:30     ` Brian Masney
  2026-03-09 10:32   ` Brian Masney
  2 siblings, 1 reply; 10+ messages in thread
From: kernel test robot @ 2026-03-08 14:37 UTC (permalink / raw)
  To: Brian Masney; +Cc: llvm, oe-kbuild-all

Hi Brian,

[This is a private test report for your RFC patch.]
kernel test robot noticed the following build errors:

[auto build test ERROR on fc7b1a72c6cd5cbbd989c6c32a6486e3e4e3594d]

url:    https://github.com/intel-lab-lkp/linux/commits/Brian-Masney/clk-test-introduce-clk_dummy_div-for-a-mock-divider/20260307-074328
base:   fc7b1a72c6cd5cbbd989c6c32a6486e3e4e3594d
patch link:    https://lore.kernel.org/r/20260306-clk-scaling-v5-2-d21b84ee6f27%40redhat.com
patch subject: [PATCH RFC v5 2/4] clk: test: introduce test suite for sibling rate changes on a divider
config: hexagon-allmodconfig (https://download.01.org/0day-ci/archive/20260308/202603082228.3RYA8l9q-lkp@intel.com/config)
compiler: clang version 17.0.6 (https://github.com/llvm/llvm-project 6009708b4367171ccdbf4b5905cb6a803753fe18)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260308/202603082228.3RYA8l9q-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202603082228.3RYA8l9q-lkp@intel.com/

All errors (new ones prefixed by >>):

>> drivers/clk/clk_test.c:686:26: error: use of undeclared identifier 'HZ_PER_MHZ'
     686 |         ctx->parent.rate = 24 * HZ_PER_MHZ;
         |                                 ^
   drivers/clk/clk_test.c:709:60: error: use of undeclared identifier 'HZ_PER_MHZ'
     709 |         KUNIT_EXPECT_EQ(test, clk_get_rate(ctx->parent_clk), 24 * HZ_PER_MHZ);
         |                                                                   ^
   drivers/clk/clk_test.c:709:60: error: use of undeclared identifier 'HZ_PER_MHZ'
   drivers/clk/clk_test.c:710:60: error: use of undeclared identifier 'HZ_PER_MHZ'
     710 |         KUNIT_EXPECT_EQ(test, clk_get_rate(ctx->child1_clk), 24 * HZ_PER_MHZ);
         |                                                                   ^
   drivers/clk/clk_test.c:710:60: error: use of undeclared identifier 'HZ_PER_MHZ'
   drivers/clk/clk_test.c:711:60: error: use of undeclared identifier 'HZ_PER_MHZ'
     711 |         KUNIT_EXPECT_EQ(test, clk_get_rate(ctx->child2_clk), 24 * HZ_PER_MHZ);
         |                                                                   ^
   drivers/clk/clk_test.c:711:60: error: use of undeclared identifier 'HZ_PER_MHZ'
   drivers/clk/clk_test.c:735:42: error: use of undeclared identifier 'HZ_PER_MHZ'
     735 |         ret = clk_set_rate(ctx->child1_clk, 6 * HZ_PER_MHZ);
         |                                                 ^
   drivers/clk/clk_test.c:738:60: error: use of undeclared identifier 'HZ_PER_MHZ'
     738 |         KUNIT_EXPECT_EQ(test, clk_get_rate(ctx->parent_clk), 24 * HZ_PER_MHZ);
         |                                                                   ^
   drivers/clk/clk_test.c:738:60: error: use of undeclared identifier 'HZ_PER_MHZ'
   drivers/clk/clk_test.c:739:59: error: use of undeclared identifier 'HZ_PER_MHZ'
     739 |         KUNIT_EXPECT_EQ(test, clk_get_rate(ctx->child1_clk), 6 * HZ_PER_MHZ);
         |                                                                  ^
   drivers/clk/clk_test.c:739:59: error: use of undeclared identifier 'HZ_PER_MHZ'
   drivers/clk/clk_test.c:741:60: error: use of undeclared identifier 'HZ_PER_MHZ'
     741 |         KUNIT_EXPECT_EQ(test, clk_get_rate(ctx->child2_clk), 24 * HZ_PER_MHZ);
         |                                                                   ^
   drivers/clk/clk_test.c:741:60: error: use of undeclared identifier 'HZ_PER_MHZ'
   drivers/clk/clk_test.c:756:43: error: use of undeclared identifier 'HZ_PER_MHZ'
     756 |         ret = clk_set_rate(ctx->child1_clk, 48 * HZ_PER_MHZ);
         |                                                  ^
   drivers/clk/clk_test.c:759:60: error: use of undeclared identifier 'HZ_PER_MHZ'
     759 |         KUNIT_EXPECT_EQ(test, clk_get_rate(ctx->parent_clk), 48 * HZ_PER_MHZ);
         |                                                                   ^
   drivers/clk/clk_test.c:759:60: error: use of undeclared identifier 'HZ_PER_MHZ'
   drivers/clk/clk_test.c:760:60: error: use of undeclared identifier 'HZ_PER_MHZ'
     760 |         KUNIT_EXPECT_EQ(test, clk_get_rate(ctx->child1_clk), 48 * HZ_PER_MHZ);
         |                                                                   ^
   drivers/clk/clk_test.c:760:60: error: use of undeclared identifier 'HZ_PER_MHZ'
   fatal error: too many errors emitted, stopping now [-ferror-limit=]
   20 errors generated.


vim +/HZ_PER_MHZ +686 drivers/clk/clk_test.c

   670	
   671	KUNIT_ARRAY_PARAM_DESC(clk_rate_change_sibling_div_div_test_regular_ops,
   672			       clk_rate_change_sibling_div_div_test_regular_ops_params, desc)
   673	
   674	static int clk_rate_change_sibling_div_div_test_init(struct kunit *test)
   675	{
   676		const struct clk_rate_change_sibling_div_div_test_param *param = test->param_value;
   677		struct clk_rate_change_sibling_div_div_context *ctx;
   678		int ret;
   679	
   680		ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
   681		if (!ctx)
   682			return -ENOMEM;
   683		test->priv = ctx;
   684	
   685		ctx->parent.hw.init = CLK_HW_INIT_NO_PARENT("parent", &clk_dummy_rate_ops, 0);
 > 686		ctx->parent.rate = 24 * HZ_PER_MHZ;
   687		ret = clk_hw_register_kunit(test, NULL, &ctx->parent.hw);
   688		if (ret)
   689			return ret;
   690	
   691		ctx->child1.hw.init = CLK_HW_INIT_HW("child1", &ctx->parent.hw,
   692						     param->ops, CLK_SET_RATE_PARENT);
   693		ctx->child1.div = 1;
   694		ret = clk_hw_register_kunit(test, NULL, &ctx->child1.hw);
   695		if (ret)
   696			return ret;
   697	
   698		ctx->child2.hw.init = CLK_HW_INIT_HW("child2", &ctx->parent.hw,
   699						     param->ops, CLK_SET_RATE_PARENT);
   700		ctx->child2.div = 1;
   701		ret = clk_hw_register_kunit(test, NULL, &ctx->child2.hw);
   702		if (ret)
   703			return ret;
   704	
   705		ctx->parent_clk = clk_hw_get_clk(&ctx->parent.hw, NULL);
   706		ctx->child1_clk = clk_hw_get_clk(&ctx->child1.hw, NULL);
   707		ctx->child2_clk = clk_hw_get_clk(&ctx->child2.hw, NULL);
   708	
   709		KUNIT_EXPECT_EQ(test, clk_get_rate(ctx->parent_clk), 24 * HZ_PER_MHZ);
   710		KUNIT_EXPECT_EQ(test, clk_get_rate(ctx->child1_clk), 24 * HZ_PER_MHZ);
   711		KUNIT_EXPECT_EQ(test, clk_get_rate(ctx->child2_clk), 24 * HZ_PER_MHZ);
   712	
   713		return 0;
   714	}
   715	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH RFC v5 2/4] clk: test: introduce test suite for sibling rate changes on a divider
  2026-03-08 14:37   ` kernel test robot
@ 2026-03-09 10:30     ` Brian Masney
  0 siblings, 0 replies; 10+ messages in thread
From: Brian Masney @ 2026-03-09 10:30 UTC (permalink / raw)
  To: kernel test robot; +Cc: llvm, oe-kbuild-all

On Sun, Mar 8, 2026 at 10:37 AM kernel test robot <lkp@intel.com> wrote:
> [This is a private test report for your RFC patch.]
> kernel test robot noticed the following build errors:
>
> [auto build test ERROR on fc7b1a72c6cd5cbbd989c6c32a6486e3e4e3594d]
>
> url:    https://github.com/intel-lab-lkp/linux/commits/Brian-Masney/clk-test-introduce-clk_dummy_div-for-a-mock-divider/20260307-074328
> base:   fc7b1a72c6cd5cbbd989c6c32a6486e3e4e3594d
> patch link:    https://lore.kernel.org/r/20260306-clk-scaling-v5-2-d21b84ee6f27%40redhat.com
> patch subject: [PATCH RFC v5 2/4] clk: test: introduce test suite for sibling rate changes on a divider
> config: hexagon-allmodconfig (https://download.01.org/0day-ci/archive/20260308/202603082228.3RYA8l9q-lkp@intel.com/config)
> compiler: clang version 17.0.6 (https://github.com/llvm/llvm-project 6009708b4367171ccdbf4b5905cb6a803753fe18)
> reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260308/202603082228.3RYA8l9q-lkp@intel.com/reproduce)
>
> If you fix the issue in a separate patch/commit (i.e. not just a new version of
> the same patch/commit), kindly add following tags
> | Reported-by: kernel test robot <lkp@intel.com>
> | Closes: https://lore.kernel.org/oe-kbuild-all/202603082228.3RYA8l9q-lkp@intel.com/
>
> All errors (new ones prefixed by >>):
>
> >> drivers/clk/clk_test.c:686:26: error: use of undeclared identifier 'HZ_PER_MHZ'
>      686 |         ctx->parent.rate = 24 * HZ_PER_MHZ;
>          |                                 ^

Thanks for the report. I forgot to mark my series dependent on this on
series I had posted that includes units.h.

https://lore.kernel.org/linux-clk/20260304-clk-docs-v1-0-fee468db99f1@redhat.com/

Brian


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH RFC v5 2/4] clk: test: introduce test suite for sibling rate changes on a divider
  2026-03-06 23:21 ` [PATCH RFC v5 2/4] clk: test: introduce test suite for sibling rate changes on a divider Brian Masney
  2026-03-08 13:46   ` kernel test robot
  2026-03-08 14:37   ` kernel test robot
@ 2026-03-09 10:32   ` Brian Masney
  2 siblings, 0 replies; 10+ messages in thread
From: Brian Masney @ 2026-03-09 10:32 UTC (permalink / raw)
  To: Michael Turquette, Stephen Boyd, Maxime Ripard, Alberto Ruiz
  Cc: linux-clk, linux-kernel

On Fri, Mar 6, 2026 at 6:23 PM Brian Masney <bmasney@redhat.com> wrote:
> Introduce a test suite that creates a parent with two divider-only
> children, and add kunit tests that document the current behavior when a
> sibling clk can unknowingly change the rate of another clk. Some boards
> are unknowingly dependent on this behavior, and per discussions at the
> 2025 Linux Plumbers Conference in Tokyo, we can't break the existing
> behavior. So let's add kunit tests with the current behavior so that
> we can be made aware if that functionality changes in the future.
>
> Link: https://lore.kernel.org/linux-clk/aUSWU7UymULCXOeF@redhat.com/
> Link: https://lpc.events/event/19/contributions/2152/
> Signed-off-by: Brian Masney <bmasney@redhat.com>

If anyone tries to test this, then you'll also need to apply this
series from me that includes units.h in clk-test.c.

https://lore.kernel.org/linux-clk/20260304-clk-docs-v1-0-fee468db99f1@redhat.com/

I forgot to mark that as a dependency in b4.

Brian


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH RFC v5 0/4] clk: add support for v1 / v2 clock rate negotiation and kunit tests
  2026-03-06 23:21 [PATCH RFC v5 0/4] clk: add support for v1 / v2 clock rate negotiation and kunit tests Brian Masney
                   ` (3 preceding siblings ...)
  2026-03-06 23:21 ` [PATCH RFC v5 4/4] clk: test: add kunit test for coordinated rate change Brian Masney
@ 2026-03-12 13:09 ` Brian Masney
  4 siblings, 0 replies; 10+ messages in thread
From: Brian Masney @ 2026-03-12 13:09 UTC (permalink / raw)
  To: Michael Turquette, Stephen Boyd, Maxime Ripard, Alberto Ruiz
  Cc: linux-clk, linux-kernel

On Fri, Mar 6, 2026 at 6:22 PM Brian Masney <bmasney@redhat.com> wrote:
> The Common Clock Framework is expected to keep a clock’s rate stable
> after setting a new rate with:
>
>     clk_set_rate(clk, NEW_RATE);
>
> Clock consumers do not know about the clock hierarchy, sibling clocks,
> or the type of clocks involved. However, several longstanding issues
> affect how rate changes propagate through the clock tree when
> CLK_SET_RATE_PARENT is involved, and the parent's clock rate is changed:
>
> - A clock in some cases can unknowingly change a sibling clock's rate.
>   More details about this particular case are documented at:
>   https://lore.kernel.org/linux-clk/20250528-clk-wip-v2-v2-2-0d2c2f220442@redhat.com/
>
> - No negotiation is done with the sibling clocks, so an inappropriate
>   or less than ideal parent rate can be selected.
>
> A selection of some real world examples of where this shows up is at
> [1]. DRM needs to run at precise clock rates, and this issue shows up
> there, however will also show up in other subsystems that require
> precise clock rates, such as sound.
>
> This series introduces kunit tests to illustrate the current behavior in
> the clk core. As discussed at Linux Plumbers Conference 2025 in Tokyo
> [2], it was suggested to move the negotiation logic into the clk
> providers themselves so that only the clks with this issue can have
> their rate preserved, and add some common helpers to the clk core.
>
> Note I am marking this series as a RFC because:
>
> - I only have kunit tests, and there's no actual real users of these
>   new helpers. (kunit doesn't count.) I'm hoping to get feedback from
>   Stephen if this is what he had in mind for an acceptable solution
>   based on earlier discussions. In the mean time, I'll see if I can
>   get a board that has this issue, or work with someone in another
>   company that can test this for me.
>
> - I feel like the names of some of these newly-introduced members
>   could be improved. Naming things can be hard.

I came up with an idea to simplify this patch set even more, so folks
can skip reviewing this version. I'll post a new even more simplified
version next week hopefully.

Brian


^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2026-03-12 13:10 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-06 23:21 [PATCH RFC v5 0/4] clk: add support for v1 / v2 clock rate negotiation and kunit tests Brian Masney
2026-03-06 23:21 ` [PATCH RFC v5 1/4] clk: test: introduce clk_dummy_div for a mock divider Brian Masney
2026-03-06 23:21 ` [PATCH RFC v5 2/4] clk: test: introduce test suite for sibling rate changes on a divider Brian Masney
2026-03-08 13:46   ` kernel test robot
2026-03-08 14:37   ` kernel test robot
2026-03-09 10:30     ` Brian Masney
2026-03-09 10:32   ` Brian Masney
2026-03-06 23:21 ` [PATCH RFC v5 3/4] clk: add support for coordinated rate changes from the providers Brian Masney
2026-03-06 23:21 ` [PATCH RFC v5 4/4] clk: test: add kunit test for coordinated rate change Brian Masney
2026-03-12 13:09 ` [PATCH RFC v5 0/4] clk: add support for v1 / v2 clock rate negotiation and kunit tests Brian Masney

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.