[PATCH 11/12] clk: Test parent/clk flags combos while unregistering a clk_hw

Stephen Boyd
2024-08-15T00:55:27Z

Extend the clk_unregister_consumer_clk test suite to test different
combinations of number of parents and clk flags. The behavior should
stay consistent regardless of how many parents there are and what clk
flags were used during registration.

Cc: Nuno Sá <nuno.sa@analog.com>
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
---
drivers/clk/clk_test.c | 203 ++++++++++++++++++++++++++++++++++-------
1 file changed, 169 insertions(+), 34 deletions(-)

diff --git a/drivers/clk/clk_test.c b/drivers/clk/clk_test.c
index 90bd0e0b93d5..591897162056 100644
--- a/drivers/clk/clk_test.c
+++ b/drivers/clk/clk_test.c
@@ -3148,6 +3148,9 @@ static void clk_unregister_consumer_clk_prepare_fails(struct kunit *test)
{
struct clk_unregister_consumer_clk_ctx *ctx = test->priv;

+ if (clk_hw_get_flags(&ctx->hw) & CLK_IS_CRITICAL)
+ kunit_skip(test, "Critical clks are already prepared");
+
clk_unregister_consumer_clk_unregister(test);

KUNIT_EXPECT_GT(test, 0, clk_prepare(ctx->clk));
@@ -3172,6 +3175,9 @@ static void clk_unregister_consumer_clk_enable_fails(struct kunit *test)
{
struct clk_unregister_consumer_clk_ctx *ctx = test->priv;

+ if (clk_hw_get_flags(&ctx->hw) & CLK_IS_CRITICAL)
+ kunit_skip(test, "Critical clks are already enabled");
+
KUNIT_ASSERT_EQ(test, 0, clk_prepare(ctx->clk));
clk_unregister_consumer_clk_unregister(test);

@@ -3231,11 +3237,17 @@ static void clk_unregister_consumer_clk_set_rate_fails(struct kunit *test)
static void clk_unregister_consumer_clk_get_rate_skips(struct kunit *test)
{
struct clk_unregister_consumer_clk_ctx *ctx = test->priv;
+ const unsigned long flags = clk_hw_get_flags(&ctx->hw);

KUNIT_ASSERT_EQ(test, ctx->rate, clk_get_rate(ctx->clk));
clk_unregister_consumer_clk_unregister(test);

- KUNIT_EXPECT_EQ(test, ctx->rate, clk_get_rate(ctx->clk));
+ if (flags & CLK_GET_RATE_NOCACHE) {
+ /* No cache clks start returning 0 and also skip */
+ KUNIT_EXPECT_EQ(test, 0, clk_get_rate(ctx->clk));
+ } else {
+ KUNIT_EXPECT_EQ(test, ctx->rate, clk_get_rate(ctx->clk));
+ }
}

/*
@@ -3245,11 +3257,17 @@ static void clk_unregister_consumer_clk_get_rate_skips(struct kunit *test)
static void clk_unregister_consumer_clk_get_accuracy_skips(struct kunit *test)
{
struct clk_unregister_consumer_clk_ctx *ctx = test->priv;
+ const unsigned long flags = clk_hw_get_flags(&ctx->hw);

KUNIT_ASSERT_EQ(test, ctx->accuracy, clk_get_accuracy(ctx->clk));
clk_unregister_consumer_clk_unregister(test);

- KUNIT_EXPECT_EQ(test, ctx->accuracy, clk_get_accuracy(ctx->clk));
+ if (flags & CLK_GET_ACCURACY_NOCACHE) {
+ /* No cache clks start returning 0 and also skip */
+ KUNIT_EXPECT_EQ(test, 0, clk_get_accuracy(ctx->clk));
+ } else {
+ KUNIT_EXPECT_EQ(test, ctx->accuracy, clk_get_accuracy(ctx->clk));
+ }
}

/*
@@ -3334,7 +3352,8 @@ static void clk_unregister_consumer_clk_set_parent_fails(struct kunit *test)
/* Setting current parent is a no-op */
KUNIT_EXPECT_EQ(test, 0, clk_set_parent(ctx->clk, ctx->parents[0].clk));
/* Setting a new parent should fail */
- KUNIT_EXPECT_GT(test, 0, clk_set_parent(ctx->clk, ctx->parents[1].clk));
+ if (ctx->parents[1].clk)
+ KUNIT_EXPECT_GT(test, 0, clk_set_parent(ctx->clk, ctx->parents[1].clk));
/* Parent is unchanged */
KUNIT_EXPECT_TRUE(test, clk_is_match(clk_get_parent(ctx->clk), ctx->parents[0].clk));
}
@@ -3354,22 +3373,108 @@ static void clk_unregister_consumer_clk_get_parent_skips(struct kunit *test)
KUNIT_EXPECT_TRUE(test, clk_is_match(clk_get_parent(ctx->clk), ctx->parents[0].clk));
}

+/**
+ * struct clk_register_params - Test parameters for different combinations of clk_init_data
+ * @clk_flags: Flags to set in struct clk_init_data::flags
+ * @num_parents: Number of parents to register (0, 1, or 2)
+ */
+struct clk_register_params {
+ unsigned long clk_flags;
+ unsigned int num_parents;
+};
+
+static void clk_register_params_to_desc(const char *test_name,
+ const struct clk_register_params *p,
+ char *desc)
+{
+ snprintf(desc, KUNIT_PARAM_DESC_SIZE, "%s %d parents %#lx flags",
+ test_name, p->num_parents, p->clk_flags);
+}
+
+/**
+ * clk_register_gen_params - Generate parameters for struct clk_init_data
+ * @test_name: Test name
+ * @_prev: Previous return value from this function
+ * @desc: Test description (to be filled in)
+ *
+ * Use this function in KUNIT_CASE_PARAM to generate struct clk_init_data
+ * parameters for a test that registers clks. It will return combinations of
+ * clk flags (exclusive of each other) and numbers of parents.
+ *
+ * Return: Test parameters in a struct clk_register_params.
+ */
+static const void *clk_register_gen_params(const char *test_name,
+ const void *_prev, char *desc)
+{
+ const struct clk_register_params *prev = _prev;
+ struct clk_register_params *next;
+
+ next = krealloc(prev, sizeof(*next), GFP_KERNEL);
+ if (!next)
+ return NULL;
+ if (!prev)
+ memset(next, 0, sizeof(*next));
+
+ if (prev) {
+ if (next->clk_flags == 0)
+ next->clk_flags = 1;
+ else
+ next->clk_flags <<= 1;
+ }
+
+ if (next->clk_flags > CLK_DUTY_CYCLE_PARENT) {
+ next->clk_flags = 0;
+ next->num_parents++;
+ if (next->num_parents > 2)
+ return NULL;
+ }
+
+ clk_register_params_to_desc(test_name, next, desc);
+ return next;
+}
+
+#define CLK_REGISTER_GEN_PARAMS(name) \
+ static const void *name##_gen_params(const void *prev, \
+ char *desc) \
+ { \
+ return clk_register_gen_params(#name, prev, desc); \
+ }
+
+#define CLK_REGISTER_KUNIT_CASE_PARAM(name) \
+ KUNIT_CASE_PARAM(name, name##_gen_params)
+
+CLK_REGISTER_GEN_PARAMS(clk_unregister_consumer_clk_prepare_fails)
+CLK_REGISTER_GEN_PARAMS(clk_unregister_consumer_clk_unprepare_skips)
+CLK_REGISTER_GEN_PARAMS(clk_unregister_consumer_clk_enable_fails)
+CLK_REGISTER_GEN_PARAMS(clk_unregister_consumer_clk_disable_skips)
+CLK_REGISTER_GEN_PARAMS(clk_unregister_consumer_clk_round_rate_fails)
+CLK_REGISTER_GEN_PARAMS(clk_unregister_consumer_clk_set_rate_fails)
+CLK_REGISTER_GEN_PARAMS(clk_unregister_consumer_clk_get_rate_skips)
+CLK_REGISTER_GEN_PARAMS(clk_unregister_consumer_clk_get_accuracy_skips)
+CLK_REGISTER_GEN_PARAMS(clk_unregister_consumer_clk_set_phase_fails)
+CLK_REGISTER_GEN_PARAMS(clk_unregister_consumer_clk_get_phase_skips)
+CLK_REGISTER_GEN_PARAMS(clk_unregister_consumer_clk_set_duty_cycle_fails)
+CLK_REGISTER_GEN_PARAMS(clk_unregister_consumer_clk_get_duty_cycle_skips)
+CLK_REGISTER_GEN_PARAMS(clk_unregister_consumer_clk_set_parent_fails)
+CLK_REGISTER_GEN_PARAMS(clk_unregister_consumer_clk_get_parent_skips)
+CLK_REGISTER_GEN_PARAMS(clk_unregister_consumer_clk_put)
+
static struct kunit_case clk_unregister_consumer_clk_test_cases[] = {
- KUNIT_CASE(clk_unregister_consumer_clk_prepare_fails),
- KUNIT_CASE(clk_unregister_consumer_clk_unprepare_skips),
- KUNIT_CASE(clk_unregister_consumer_clk_enable_fails),
- KUNIT_CASE(clk_unregister_consumer_clk_disable_skips),
- KUNIT_CASE(clk_unregister_consumer_clk_round_rate_fails),
- KUNIT_CASE(clk_unregister_consumer_clk_set_rate_fails),
- KUNIT_CASE(clk_unregister_consumer_clk_get_rate_skips),
- KUNIT_CASE(clk_unregister_consumer_clk_get_accuracy_skips),
- KUNIT_CASE(clk_unregister_consumer_clk_set_phase_fails),
- KUNIT_CASE(clk_unregister_consumer_clk_get_phase_skips),
- KUNIT_CASE(clk_unregister_consumer_clk_set_duty_cycle_fails),
- KUNIT_CASE(clk_unregister_consumer_clk_get_duty_cycle_skips),
- KUNIT_CASE(clk_unregister_consumer_clk_set_parent_fails),
- KUNIT_CASE(clk_unregister_consumer_clk_get_parent_skips),
- KUNIT_CASE(clk_unregister_consumer_clk_put),
+ CLK_REGISTER_KUNIT_CASE_PARAM(clk_unregister_consumer_clk_prepare_fails),
+ CLK_REGISTER_KUNIT_CASE_PARAM(clk_unregister_consumer_clk_unprepare_skips),
+ CLK_REGISTER_KUNIT_CASE_PARAM(clk_unregister_consumer_clk_enable_fails),
+ CLK_REGISTER_KUNIT_CASE_PARAM(clk_unregister_consumer_clk_disable_skips),
+ CLK_REGISTER_KUNIT_CASE_PARAM(clk_unregister_consumer_clk_round_rate_fails),
+ CLK_REGISTER_KUNIT_CASE_PARAM(clk_unregister_consumer_clk_set_rate_fails),
+ CLK_REGISTER_KUNIT_CASE_PARAM(clk_unregister_consumer_clk_get_rate_skips),
+ CLK_REGISTER_KUNIT_CASE_PARAM(clk_unregister_consumer_clk_get_accuracy_skips),
+ CLK_REGISTER_KUNIT_CASE_PARAM(clk_unregister_consumer_clk_set_phase_fails),
+ CLK_REGISTER_KUNIT_CASE_PARAM(clk_unregister_consumer_clk_get_phase_skips),
+ CLK_REGISTER_KUNIT_CASE_PARAM(clk_unregister_consumer_clk_set_duty_cycle_fails),
+ CLK_REGISTER_KUNIT_CASE_PARAM(clk_unregister_consumer_clk_get_duty_cycle_skips),
+ CLK_REGISTER_KUNIT_CASE_PARAM(clk_unregister_consumer_clk_set_parent_fails),
+ CLK_REGISTER_KUNIT_CASE_PARAM(clk_unregister_consumer_clk_get_parent_skips),
+ CLK_REGISTER_KUNIT_CASE_PARAM(clk_unregister_consumer_clk_put),
{}
};

@@ -3586,33 +3691,63 @@ static int clk_unregister_consumer_clk_init(struct kunit *test)
struct clk *clk;
struct clk_init_data init = { };
struct clk_unregister_consumer_clk_ctx *ctx;
- struct clk_hw *parent0_hw, *parent1_hw;
+ struct clk_hw *parent0_hw = NULL;
+ struct clk_hw *parent1_hw = NULL;
+ const struct clk_register_params *test_param;
+ unsigned int num_parents;
+ unsigned long clk_flags;
+ struct clk_ops *clk_ops;
+
+ test_param = test->param_value;
+ if (test_param) {
+ num_parents = test_param->num_parents;
+ clk_flags = test_param->clk_flags;
+ } else {
+ num_parents = 0;
+ clk_flags = 0;
+ }

ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
test->priv = ctx;
ctx->test = test;

- parent0_hw = &ctx->parents[0].ctx.hw;
- parent0_hw->init = CLK_HW_INIT_NO_PARENT("parent-clk0",
- &clk_dummy_rate_ops, 0);
- KUNIT_ASSERT_EQ(test, 0, clk_hw_register_kunit(test, NULL, parent0_hw));
- ctx->parents[0].clk = clk_hw_get_clk_kunit(test, parent0_hw, "p0");
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->parents[0].clk);
+ if (num_parents >= 1) {
+ parent0_hw = &ctx->parents[0].ctx.hw;
+ parent0_hw->init = CLK_HW_INIT_NO_PARENT("parent-clk0",
+ &clk_dummy_rate_ops, 0);
+ KUNIT_ASSERT_EQ(test, 0, clk_hw_register_kunit(test, NULL, parent0_hw));
+ ctx->parents[0].clk = clk_hw_get_clk_kunit(test, parent0_hw, "p0");
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->parents[0].clk);
+ }

- parent1_hw = &ctx->parents[1].ctx.hw;
- parent1_hw->init = CLK_HW_INIT_NO_PARENT("parent-clk1",
- &clk_dummy_rate_ops, 0);
- KUNIT_ASSERT_EQ(test, 0, clk_hw_register_kunit(test, NULL, parent1_hw));
- ctx->parents[1].clk = clk_hw_get_clk_kunit(test, parent1_hw, "p1");
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->parents[1].clk);
+ if (num_parents >= 2) {
+ parent1_hw = &ctx->parents[1].ctx.hw;
+ parent1_hw->init = CLK_HW_INIT_NO_PARENT("parent-clk1",
+ &clk_dummy_rate_ops, 0);
+ KUNIT_ASSERT_EQ(test, 0, clk_hw_register_kunit(test, NULL, parent1_hw));
+ ctx->parents[1].clk = clk_hw_get_clk_kunit(test, parent1_hw, "p1");
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->parents[1].clk);
+ }

init.name = "unregister_consumer_clk_test_clk";
- init.ops = &clk_unregister_consumer_clk_clk_ops;
- init.parent_hws = (const struct clk_hw *[]){ parent0_hw, parent1_hw };
- init.num_parents = ARRAY_SIZE(ctx->parents);
ctx->hw.init = &init;

+ clk_ops = kunit_kzalloc(test, sizeof(*clk_ops), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, clk_ops);
+ /* Copy clk_ops so we can modify them for different scenarios */
+ memcpy(clk_ops, &clk_unregister_consumer_clk_clk_ops, sizeof(*clk_ops));
+ init.ops = clk_ops;
+
+ init.flags = clk_flags;
+ init.num_parents = num_parents;
+ init.parent_hws = (const struct clk_hw *[]){ parent0_hw, parent1_hw };
+ if (!num_parents) {
+ clk_ops->get_parent = NULL;
+ clk_ops->set_parent = NULL;
+ clk_ops->set_rate_and_parent = NULL;
+ }
+
ctx->rate = 42;
ctx->accuracy = 34;
ctx->phase = 90;