Quoting Maxime Ripard (2022-01-25 06:15:40) > diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile > index 6a98291350b6..2664aaab8068 100644 > --- a/drivers/clk/Makefile > +++ b/drivers/clk/Makefile > @@ -2,6 +2,7 @@ > # common clock types > obj-$(CONFIG_HAVE_CLK) += clk-devres.o clk-bulk.o clkdev.o > obj-$(CONFIG_COMMON_CLK) += clk.o > +obj-$(CONFIG_CLK_KUNIT_TEST) += clk-test.o The file name should be clk_test.c with an underscore. > obj-$(CONFIG_COMMON_CLK) += clk-divider.o > obj-$(CONFIG_COMMON_CLK) += clk-fixed-factor.o > obj-$(CONFIG_COMMON_CLK) += clk-fixed-rate.o > diff --git a/drivers/clk/clk-test.c b/drivers/clk/clk-test.c > new file mode 100644 > index 000000000000..47a600d590c1 > --- /dev/null > +++ b/drivers/clk/clk-test.c > @@ -0,0 +1,285 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Kunit test for clk rate management > + */ > +#include <linux/clk.h> > +#include <linux/clk-provider.h> > +#include <linux/slab.h> > + > +#include <kunit/test.h> > + > +#define DUMMY_CLOCK_INIT_RATE (42 * 1000 * 1000) > +#define DUMMY_CLOCK_RATE_1 (142 * 1000 * 1000) > +#define DUMMY_CLOCK_RATE_2 (242 * 1000 * 1000) > + > +struct clk_dummy_context { > + struct clk_hw hw; > + unsigned long rate; > +}; > + > +static unsigned long clk_dummy_recalc_rate(struct clk_hw *hw, > + unsigned long parent_rate) > +{ > + struct clk_dummy_context *ctx = > + container_of(hw, struct clk_dummy_context, hw); > + > + return ctx->rate; > +} > + > +static int clk_dummy_determine_rate(struct clk_hw *hw, > + struct clk_rate_request *req) > +{ > + /* Just return the same rate without modifying it */ > + return 0; > +} > + > +static int clk_dummy_set_rate(struct clk_hw *hw, > + unsigned long rate, > + unsigned long parent_rate) > +{ > + struct clk_dummy_context *ctx = > + container_of(hw, struct clk_dummy_context, hw); > + > + ctx->rate = rate; > + return 0; > +} > + > +static const struct clk_ops clk_dummy_ops = { Maybe clk_dummy_rate_ops? So we don't mix it up with other dummy ops in this file testing things that aren't rates. > + .recalc_rate = clk_dummy_recalc_rate, > + .determine_rate = clk_dummy_determine_rate, > + .set_rate = clk_dummy_set_rate, > +}; > + > +static int clk_test_init_with_ops(struct kunit *test, const struct clk_ops *ops) > +{ > + struct clk_dummy_context *ctx; > + struct clk_init_data init = { }; > + int ret; > + > + ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); > + if (!ctx) > + return -ENOMEM; > + ctx->rate = DUMMY_CLOCK_INIT_RATE; > + test->priv = ctx; > + > + init.name = "test_dummy_rate"; > + init.ops = ops; > + ctx->hw.init = &init; > + > + ret = clk_hw_register(NULL, &ctx->hw); > + if (ret) > + return ret; > + > + return 0; > +} > + > +static int clk_test_init(struct kunit *test) > +{ > + return clk_test_init_with_ops(test, &clk_dummy_ops); > +} > + > +static void clk_test_exit(struct kunit *test) > +{ > + struct clk_dummy_context *ctx = test->priv; > + > + clk_hw_unregister(&ctx->hw); > +} > + > +/* > + * Test that the actual rate matches what is returned by clk_get_rate() > + */ > +static void clk_test_get_rate(struct kunit *test) > +{ > + struct clk_dummy_context *ctx = test->priv; > + struct clk_hw *hw = &ctx->hw; > + struct clk *clk = hw->clk; > + unsigned long rate; > + > + rate = clk_get_rate(clk); > + KUNIT_ASSERT_TRUE(test, rate > 0); > + KUNIT_EXPECT_EQ(test, rate, ctx->rate); > +} > + > +/* > + * Test that, after a call to clk_set_rate(), the rate returned by > + * clk_get_rate() matches. > + * > + * This assumes that clk_ops.determine_rate or clk_ops.round_rate won't > + * modify the requested rate, which is our case in clk_dummy_rate_ops. > + */ > +static void clk_test_set_get_rate(struct kunit *test) > +{ > + struct clk_dummy_context *ctx = test->priv; > + struct clk_hw *hw = &ctx->hw; > + struct clk *clk = hw->clk; > + unsigned long rate; > + > + KUNIT_ASSERT_EQ(test, > + clk_set_rate(clk, DUMMY_CLOCK_RATE_1), > + 0); > + > + rate = clk_get_rate(clk); > + KUNIT_ASSERT_GT(test, rate, 0); > + KUNIT_EXPECT_EQ(test, rate, DUMMY_CLOCK_RATE_1); > +} > + > +/* > + * Test that, after several calls to clk_set_rate(), the rate returned > + * by clk_get_rate() matches the last one. > + * > + * This assumes that clk_ops.determine_rate or clk_ops.round_rate won't > + * modify the requested rate, which is our case in clk_dummy_rate_ops. > + */ > +static void clk_test_set_set_get_rate(struct kunit *test) > +{ > + struct clk_dummy_context *ctx = test->priv; > + struct clk_hw *hw = &ctx->hw; > + struct clk *clk = hw->clk; > + unsigned long rate; > + > + KUNIT_ASSERT_EQ(test, > + clk_set_rate(clk, DUMMY_CLOCK_RATE_1), > + 0); > + > + KUNIT_ASSERT_EQ(test, > + clk_set_rate(clk, DUMMY_CLOCK_RATE_2), > + 0); > + > + rate = clk_get_rate(clk); > + KUNIT_ASSERT_GT(test, rate, 0); > + KUNIT_EXPECT_EQ(test, rate, DUMMY_CLOCK_RATE_2); > +} > + > +static struct kunit_case clk_test_cases[] = { > + KUNIT_CASE(clk_test_get_rate), > + KUNIT_CASE(clk_test_set_get_rate), > + KUNIT_CASE(clk_test_set_set_get_rate), > + {} > +}; > + > +static struct kunit_suite clk_test_suite = { > + .name = "clk-test", > + .init = clk_test_init, > + .exit = clk_test_exit, > + .test_cases = clk_test_cases, > +}; > + > +/* > + * Test that clk_set_rate_range won't return an error for a valid range. > + */ > +static void clk_range_test_set_range(struct kunit *test) > +{ > + struct clk_dummy_context *ctx = test->priv; > + struct clk_hw *hw = &ctx->hw; > + struct clk *clk = hw->clk; > + unsigned long rate; > + > + KUNIT_ASSERT_EQ(test, > + clk_set_rate_range(clk, > + DUMMY_CLOCK_RATE_1, > + DUMMY_CLOCK_RATE_2), > + 0); > + > + rate = clk_get_rate(clk); > + KUNIT_ASSERT_GT(test, rate, 0); > + KUNIT_EXPECT_GE(test, rate, DUMMY_CLOCK_RATE_1); > + KUNIT_EXPECT_LE(test, rate, DUMMY_CLOCK_RATE_2); > +} > + > +/* > + * Test that calling clk_set_rate_range with a minimum rate higher than > + * the maximum rate returns an error. > + */ > +static void clk_range_test_set_range_invalid(struct kunit *test) > +{ > + struct clk_dummy_context *ctx = test->priv; > + struct clk_hw *hw = &ctx->hw; > + struct clk *clk = hw->clk; > + > + KUNIT_EXPECT_LT(test, > + clk_set_rate_range(clk, > + DUMMY_CLOCK_RATE_1 + 1000, > + DUMMY_CLOCK_RATE_1), > + 0); > +} > + > +/* > + * Test that if our clock has a rate lower than the minimum set by a > + * call to clk_set_rate_range(), the rate will be raised to match the > + * new minimum. > + * > + * This assumes that clk_ops.determine_rate or clk_ops.round_rate won't > + * modify the requested rate, which is our case in clk_dummy_rate_ops. > + */ > +static void clk_range_test_set_range_get_rate_raised(struct kunit *test) > +{ > + struct clk_dummy_context *ctx = test->priv; > + struct clk_hw *hw = &ctx->hw; > + struct clk *clk = hw->clk; > + unsigned long rate; > + > + KUNIT_ASSERT_EQ(test, > + clk_set_rate(clk, DUMMY_CLOCK_RATE_1 - 1000), > + 0); > + > + KUNIT_ASSERT_EQ(test, > + clk_set_rate_range(clk, > + DUMMY_CLOCK_RATE_1, > + DUMMY_CLOCK_RATE_2), > + 0); > + > + rate = clk_get_rate(clk); > + KUNIT_ASSERT_GT(test, rate, 0); > + KUNIT_EXPECT_EQ(test, rate, DUMMY_CLOCK_RATE_1); > +} > + > +/* > + * Test that if our clock has a rate higher than the maximum set by a > + * call to clk_set_rate_range(), the rate will be lowered to match the > + * new maximum. > + * > + * This assumes that clk_ops.determine_rate or clk_ops.round_rate won't > + * modify the requested rate, which is our case in clk_dummy_rate_ops. > + */ > +static void clk_range_test_set_range_get_rate_lowered(struct kunit *test) > +{ > + struct clk_dummy_context *ctx = test->priv; > + struct clk_hw *hw = &ctx->hw; > + struct clk *clk = hw->clk; > + unsigned long rate; > + > + KUNIT_ASSERT_EQ(test, > + clk_set_rate(clk, DUMMY_CLOCK_RATE_2 + 1000), > + 0); > + > + KUNIT_ASSERT_EQ(test, > + clk_set_rate_range(clk, > + DUMMY_CLOCK_RATE_1, > + DUMMY_CLOCK_RATE_2), > + 0); > + > + rate = clk_get_rate(clk); > + KUNIT_ASSERT_GT(test, rate, 0); > + KUNIT_EXPECT_EQ(test, rate, DUMMY_CLOCK_RATE_2); > +} > + > +static struct kunit_case clk_range_test_cases[] = { > + KUNIT_CASE(clk_range_test_set_range), > + KUNIT_CASE(clk_range_test_set_range_invalid), > + KUNIT_CASE(clk_range_test_set_range_get_rate_raised), > + KUNIT_CASE(clk_range_test_set_range_get_rate_lowered), Can you add a test case for round_rate matching what set_rate did, i.e. calling clk_round_rate() and then clk_set_rate() followed by clk_get_rate() with the same argument for round and set rate leads to the same frequency? It would also be good to add a test that tries to set the clk rate with clk_set_rate() after a range has been set that is outside the acceptable range and verify that it fails, and one that tries to set it within the range and make sure it succeeds (and changes it to be exactly what was set). Similarly, a call to set two disjoint ranges and verify that the call that tries to set the second disjoint range fails. We want to test the failure paths as well, to make sure we don't start causing them to pass, unless it's expected. This patch could also contain the failure scenario you're experiencing and mark it as expecting to fail. Then the patch that fixes it in the core could mark the test as expecting to pass, which may help us understand more easily what exactly changed instead of having to figure that out after the fact by reading the entire test. > + {} > +}; > + > +static struct kunit_suite clk_range_test_suite = { > + .name = "clk-range-test", > + .init = clk_test_init, > + .exit = clk_test_exit, > + .test_cases = clk_range_test_cases, > +};