1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * KUnit tests for clk_divider_bestdiv()
4 */
5 #include <kunit/test.h>
6 #include <linux/clk.h>
7 #include <linux/clk-provider.h>
8 #include <linux/limits.h>
9 #include <linux/units.h>
10
11 #define PARENT_RATE_1GHZ GIGA
12 #define PARENT_RATE_2GHZ (2 * GIGA)
13 #define PARENT_RATE_4GHZ (4 * GIGA)
14
15 KUNIT_DEFINE_ACTION_WRAPPER(clk_hw_unregister_fixed_rate_wrapper,
16 clk_hw_unregister_fixed_rate, struct clk_hw *);
17 KUNIT_DEFINE_ACTION_WRAPPER(clk_hw_unregister_divider_wrapper,
18 clk_hw_unregister_divider, struct clk_hw *);
19 KUNIT_DEFINE_ACTION_WRAPPER(clk_hw_unregister_mux_wrapper,
20 clk_hw_unregister_mux, struct clk_hw *);
21
22 static const struct clk_div_table bestdiv_table[] = {
23 { .val = 0, .div = 2 },
24 { .val = 1, .div = 4 },
25 { .val = 2, .div = 8 },
26 { /* sentinel */ }
27 };
28
29 /*
30 * Test that clk_round_rate(clk, ULONG_MAX) returns the maximum achievable
31 * rate for a divider clock.
32 */
clk_divider_bestdiv_ulong_max_returns_max_rate(struct kunit * test)33 static void clk_divider_bestdiv_ulong_max_returns_max_rate(struct kunit *test)
34 {
35 struct clk_hw *parent_hw, *div_hw;
36 unsigned long rate;
37 u32 *fake_reg;
38
39 fake_reg = kunit_kzalloc(test, sizeof(*fake_reg), GFP_KERNEL);
40 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fake_reg);
41
42 parent_hw = clk_hw_register_fixed_rate(NULL, "bestdiv-parent",
43 NULL, 0, PARENT_RATE_1GHZ);
44 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent_hw);
45 KUNIT_ASSERT_EQ(test, 0,
46 kunit_add_action_or_reset(test, clk_hw_unregister_fixed_rate_wrapper,
47 parent_hw));
48
49 div_hw = clk_hw_register_divider_table(NULL, "bestdiv-div",
50 "bestdiv-parent",
51 CLK_SET_RATE_PARENT,
52 (void __iomem __force *)fake_reg,
53 0, 2, 0, bestdiv_table, NULL);
54 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, div_hw);
55 KUNIT_ASSERT_EQ(test, 0,
56 kunit_add_action_or_reset(test, clk_hw_unregister_divider_wrapper,
57 div_hw));
58
59 /*
60 * ULONG_MAX is the canonical way to probe the maximum rate a clock
61 * can produce.
62 */
63 rate = clk_hw_round_rate(div_hw, ULONG_MAX);
64 KUNIT_EXPECT_EQ(test, rate, PARENT_RATE_1GHZ / 2);
65 }
66
67 /*
68 * Test that clk_round_rate(clk, ULONG_MAX) returns the correct maximum rate
69 * when a mux clock sits between a divider and its parent candidates.
70 *
71 * Topology:
72 *
73 * [fixed 4 GHz] --\
74 * +--> [mux CLK_SET_RATE_PARENT] --> [div {2,4,8} CLK_SET_RATE_PARENT]
75 * [fixed 2 GHz] --/
76 *
77 */
clk_divider_bestdiv_mux_ulong_max_returns_max_rate(struct kunit * test)78 static void clk_divider_bestdiv_mux_ulong_max_returns_max_rate(struct kunit *test)
79 {
80 static const char * const mux_parents[] = {
81 "bestdiv-mux-parent-a",
82 "bestdiv-mux-parent-b",
83 };
84 struct clk_hw *parent_a_hw, *parent_b_hw, *mux_hw, *div_hw;
85 u32 *fake_reg_mux, *fake_reg_div;
86 unsigned long rate;
87
88 fake_reg_mux = kunit_kzalloc(test, sizeof(*fake_reg_mux), GFP_KERNEL);
89 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fake_reg_mux);
90
91 fake_reg_div = kunit_kzalloc(test, sizeof(*fake_reg_div), GFP_KERNEL);
92 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fake_reg_div);
93
94 /* Higher-rate parent: the mux should select this for ULONG_MAX. */
95 parent_a_hw = clk_hw_register_fixed_rate(NULL, "bestdiv-mux-parent-a",
96 NULL, 0, PARENT_RATE_4GHZ);
97 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent_a_hw);
98 KUNIT_ASSERT_EQ(test, 0,
99 kunit_add_action_or_reset(test, clk_hw_unregister_fixed_rate_wrapper,
100 parent_a_hw));
101
102 /* Lower-rate parent: should not be selected. */
103 parent_b_hw = clk_hw_register_fixed_rate(NULL, "bestdiv-mux-parent-b",
104 NULL, 0, PARENT_RATE_2GHZ);
105 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent_b_hw);
106 KUNIT_ASSERT_EQ(test, 0,
107 kunit_add_action_or_reset(test, clk_hw_unregister_fixed_rate_wrapper,
108 parent_b_hw));
109
110 /*
111 * 1-bit mux register selects between the two parents.
112 * CLK_SET_RATE_PARENT allows the divider's rate request to
113 * propagate into clk_mux_determine_rate().
114 */
115 mux_hw = clk_hw_register_mux(NULL, "bestdiv-mux",
116 mux_parents, ARRAY_SIZE(mux_parents),
117 CLK_SET_RATE_PARENT,
118 (void __iomem __force *)fake_reg_mux,
119 0, 1, 0, NULL);
120 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, mux_hw);
121 KUNIT_ASSERT_EQ(test, 0,
122 kunit_add_action_or_reset(test, clk_hw_unregister_mux_wrapper,
123 mux_hw));
124
125 div_hw = clk_hw_register_divider_table(NULL, "bestdiv-mux-div",
126 "bestdiv-mux",
127 CLK_SET_RATE_PARENT,
128 (void __iomem __force *)fake_reg_div,
129 0, 2, 0, bestdiv_table, NULL);
130 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, div_hw);
131 KUNIT_ASSERT_EQ(test, 0,
132 kunit_add_action_or_reset(test, clk_hw_unregister_divider_wrapper,
133 div_hw));
134
135 rate = clk_hw_round_rate(div_hw, ULONG_MAX);
136 KUNIT_EXPECT_EQ(test, rate, PARENT_RATE_4GHZ / 2);
137 }
138
139 static struct kunit_case clk_divider_bestdiv_test_cases[] = {
140 KUNIT_CASE(clk_divider_bestdiv_ulong_max_returns_max_rate),
141 KUNIT_CASE(clk_divider_bestdiv_mux_ulong_max_returns_max_rate),
142 {}
143 };
144
145 static struct kunit_suite clk_divider_bestdiv_test_suite = {
146 .name = "clk_divider_bestdiv",
147 .test_cases = clk_divider_bestdiv_test_cases,
148 };
149
150 kunit_test_suite(clk_divider_bestdiv_test_suite);
151
152 MODULE_DESCRIPTION("KUnit tests for clk divider");
153 MODULE_LICENSE("GPL");
154