[PATCH RFC v5 4/4] clk: test: add kunit test for coordinated rate change

Brian Masney posted 4 patches 1 month ago
There is a newer version of this series
[PATCH RFC v5 4/4] clk: test: add kunit test for coordinated rate change
Posted by Brian Masney 1 month ago
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