1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
|
// SPDX-License-Identifier: GPL-2.0
/*
* KUnit tests for clk_divider_bestdiv()
*/
#include <kunit/test.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/limits.h>
#include <linux/units.h>
#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");
|