// SPDX-License-Identifier: GPL-2.0 /* * KUnit tests for clk_divider_bestdiv() */ #include #include #include #include #include #define PARENT_RATE_1GHZ GIGA #define PARENT_RATE_2GHZ (2 * GIGA) #define PARENT_RATE_4GHZ (4 * GIGA) KUNIT_DEFINE_ACTION_WRAPPER(clk_hw_unregister_fixed_rate_wrapper, clk_hw_unregister_fixed_rate, struct clk_hw *); KUNIT_DEFINE_ACTION_WRAPPER(clk_hw_unregister_divider_wrapper, clk_hw_unregister_divider, struct clk_hw *); KUNIT_DEFINE_ACTION_WRAPPER(clk_hw_unregister_mux_wrapper, clk_hw_unregister_mux, struct clk_hw *); static const struct clk_div_table bestdiv_table[] = { { .val = 0, .div = 2 }, { .val = 1, .div = 4 }, { .val = 2, .div = 8 }, { /* sentinel */ } }; /* * Test that clk_round_rate(clk, ULONG_MAX) returns the maximum achievable * rate for a divider clock. */ static void clk_divider_bestdiv_ulong_max_returns_max_rate(struct kunit *test) { struct clk_hw *parent_hw, *div_hw; unsigned long rate; u32 *fake_reg; fake_reg = kunit_kzalloc(test, sizeof(*fake_reg), GFP_KERNEL); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fake_reg); parent_hw = clk_hw_register_fixed_rate(NULL, "bestdiv-parent", NULL, 0, PARENT_RATE_1GHZ); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent_hw); KUNIT_ASSERT_EQ(test, 0, kunit_add_action_or_reset(test, clk_hw_unregister_fixed_rate_wrapper, parent_hw)); div_hw = clk_hw_register_divider_table(NULL, "bestdiv-div", "bestdiv-parent", CLK_SET_RATE_PARENT, (void __iomem __force *)fake_reg, 0, 2, 0, bestdiv_table, NULL); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, div_hw); KUNIT_ASSERT_EQ(test, 0, kunit_add_action_or_reset(test, clk_hw_unregister_divider_wrapper, div_hw)); /* * ULONG_MAX is the canonical way to probe the maximum rate a clock * can produce. */ rate = clk_hw_round_rate(div_hw, ULONG_MAX); KUNIT_EXPECT_EQ(test, rate, PARENT_RATE_1GHZ / 2); } /* * Test that clk_round_rate(clk, ULONG_MAX) returns the correct maximum rate * when a mux clock sits between a divider and its parent candidates. * * Topology: * * [fixed 4 GHz] --\ * +--> [mux CLK_SET_RATE_PARENT] --> [div {2,4,8} CLK_SET_RATE_PARENT] * [fixed 2 GHz] --/ * */ static void clk_divider_bestdiv_mux_ulong_max_returns_max_rate(struct kunit *test) { static const char * const mux_parents[] = { "bestdiv-mux-parent-a", "bestdiv-mux-parent-b", }; struct clk_hw *parent_a_hw, *parent_b_hw, *mux_hw, *div_hw; u32 *fake_reg_mux, *fake_reg_div; unsigned long rate; fake_reg_mux = kunit_kzalloc(test, sizeof(*fake_reg_mux), GFP_KERNEL); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fake_reg_mux); fake_reg_div = kunit_kzalloc(test, sizeof(*fake_reg_div), GFP_KERNEL); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fake_reg_div); /* Higher-rate parent: the mux should select this for ULONG_MAX. */ parent_a_hw = clk_hw_register_fixed_rate(NULL, "bestdiv-mux-parent-a", NULL, 0, PARENT_RATE_4GHZ); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent_a_hw); KUNIT_ASSERT_EQ(test, 0, kunit_add_action_or_reset(test, clk_hw_unregister_fixed_rate_wrapper, parent_a_hw)); /* Lower-rate parent: should not be selected. */ parent_b_hw = clk_hw_register_fixed_rate(NULL, "bestdiv-mux-parent-b", NULL, 0, PARENT_RATE_2GHZ); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent_b_hw); KUNIT_ASSERT_EQ(test, 0, kunit_add_action_or_reset(test, clk_hw_unregister_fixed_rate_wrapper, parent_b_hw)); /* * 1-bit mux register selects between the two parents. * CLK_SET_RATE_PARENT allows the divider's rate request to * propagate into clk_mux_determine_rate(). */ mux_hw = clk_hw_register_mux(NULL, "bestdiv-mux", mux_parents, ARRAY_SIZE(mux_parents), CLK_SET_RATE_PARENT, (void __iomem __force *)fake_reg_mux, 0, 1, 0, NULL); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, mux_hw); KUNIT_ASSERT_EQ(test, 0, kunit_add_action_or_reset(test, clk_hw_unregister_mux_wrapper, mux_hw)); div_hw = clk_hw_register_divider_table(NULL, "bestdiv-mux-div", "bestdiv-mux", CLK_SET_RATE_PARENT, (void __iomem __force *)fake_reg_div, 0, 2, 0, bestdiv_table, NULL); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, div_hw); KUNIT_ASSERT_EQ(test, 0, kunit_add_action_or_reset(test, clk_hw_unregister_divider_wrapper, div_hw)); rate = clk_hw_round_rate(div_hw, ULONG_MAX); KUNIT_EXPECT_EQ(test, rate, PARENT_RATE_4GHZ / 2); } static struct kunit_case clk_divider_bestdiv_test_cases[] = { KUNIT_CASE(clk_divider_bestdiv_ulong_max_returns_max_rate), KUNIT_CASE(clk_divider_bestdiv_mux_ulong_max_returns_max_rate), {} }; static struct kunit_suite clk_divider_bestdiv_test_suite = { .name = "clk_divider_bestdiv", .test_cases = clk_divider_bestdiv_test_cases, }; kunit_test_suite(clk_divider_bestdiv_test_suite); MODULE_DESCRIPTION("KUnit tests for clk divider"); MODULE_LICENSE("GPL");