2022-02-25 22:35:24 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
/*
|
|
|
|
* Kunit test for clk rate management
|
|
|
|
*/
|
|
|
|
#include <linux/clk.h>
|
|
|
|
#include <linux/clk-provider.h>
|
|
|
|
|
|
|
|
/* Needed for clk_hw_get_clk() */
|
|
|
|
#include "clk.h"
|
|
|
|
|
|
|
|
#include <kunit/test.h>
|
|
|
|
|
2023-09-13 01:55:30 +08:00
|
|
|
static const struct clk_ops empty_clk_ops = { };
|
|
|
|
|
2022-02-25 22:35:24 +08:00
|
|
|
#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_maximize_rate(struct clk_hw *hw,
|
|
|
|
struct clk_rate_request *req)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* If there's a maximum set, always run the clock at the maximum
|
|
|
|
* allowed.
|
|
|
|
*/
|
|
|
|
if (req->max_rate < ULONG_MAX)
|
|
|
|
req->rate = req->max_rate;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int clk_dummy_minimize_rate(struct clk_hw *hw,
|
|
|
|
struct clk_rate_request *req)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* If there's a minimum set, always run the clock at the minimum
|
|
|
|
* allowed.
|
|
|
|
*/
|
|
|
|
if (req->min_rate > 0)
|
|
|
|
req->rate = req->min_rate;
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2022-03-26 00:11:43 +08:00
|
|
|
static int clk_dummy_single_set_parent(struct clk_hw *hw, u8 index)
|
|
|
|
{
|
|
|
|
if (index >= clk_hw_get_num_parents(hw))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static u8 clk_dummy_single_get_parent(struct clk_hw *hw)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-02-25 22:35:24 +08:00
|
|
|
static const struct clk_ops clk_dummy_rate_ops = {
|
|
|
|
.recalc_rate = clk_dummy_recalc_rate,
|
|
|
|
.determine_rate = clk_dummy_determine_rate,
|
|
|
|
.set_rate = clk_dummy_set_rate,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct clk_ops clk_dummy_maximize_rate_ops = {
|
|
|
|
.recalc_rate = clk_dummy_recalc_rate,
|
|
|
|
.determine_rate = clk_dummy_maximize_rate,
|
|
|
|
.set_rate = clk_dummy_set_rate,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct clk_ops clk_dummy_minimize_rate_ops = {
|
|
|
|
.recalc_rate = clk_dummy_recalc_rate,
|
|
|
|
.determine_rate = clk_dummy_minimize_rate,
|
|
|
|
.set_rate = clk_dummy_set_rate,
|
|
|
|
};
|
|
|
|
|
2022-03-26 00:11:43 +08:00
|
|
|
static const struct clk_ops clk_dummy_single_parent_ops = {
|
2023-05-05 19:25:09 +08:00
|
|
|
/*
|
|
|
|
* FIXME: Even though we should probably be able to use
|
|
|
|
* __clk_mux_determine_rate() here, if we use it and call
|
|
|
|
* clk_round_rate() or clk_set_rate() with a rate lower than
|
|
|
|
* what all the parents can provide, it will return -EINVAL.
|
|
|
|
*
|
|
|
|
* This is due to the fact that it has the undocumented
|
|
|
|
* behaviour to always pick up the closest rate higher than the
|
|
|
|
* requested rate. If we get something lower, it thus considers
|
|
|
|
* that it's not acceptable and will return an error.
|
|
|
|
*
|
|
|
|
* It's somewhat inconsistent and creates a weird threshold
|
|
|
|
* between rates above the parent rate which would be rounded to
|
|
|
|
* what the parent can provide, but rates below will simply
|
|
|
|
* return an error.
|
|
|
|
*/
|
|
|
|
.determine_rate = __clk_mux_determine_rate_closest,
|
2022-03-26 00:11:43 +08:00
|
|
|
.set_parent = clk_dummy_single_set_parent,
|
|
|
|
.get_parent = clk_dummy_single_get_parent,
|
|
|
|
};
|
|
|
|
|
2022-08-16 19:25:15 +08:00
|
|
|
struct clk_multiple_parent_ctx {
|
|
|
|
struct clk_dummy_context parents_ctx[2];
|
|
|
|
struct clk_hw hw;
|
|
|
|
u8 current_parent;
|
|
|
|
};
|
|
|
|
|
|
|
|
static int clk_multiple_parents_mux_set_parent(struct clk_hw *hw, u8 index)
|
|
|
|
{
|
|
|
|
struct clk_multiple_parent_ctx *ctx =
|
|
|
|
container_of(hw, struct clk_multiple_parent_ctx, hw);
|
|
|
|
|
|
|
|
if (index >= clk_hw_get_num_parents(hw))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
ctx->current_parent = index;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static u8 clk_multiple_parents_mux_get_parent(struct clk_hw *hw)
|
|
|
|
{
|
|
|
|
struct clk_multiple_parent_ctx *ctx =
|
|
|
|
container_of(hw, struct clk_multiple_parent_ctx, hw);
|
|
|
|
|
|
|
|
return ctx->current_parent;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct clk_ops clk_multiple_parents_mux_ops = {
|
|
|
|
.get_parent = clk_multiple_parents_mux_get_parent,
|
|
|
|
.set_parent = clk_multiple_parents_mux_set_parent,
|
|
|
|
.determine_rate = __clk_mux_determine_rate_closest,
|
|
|
|
};
|
|
|
|
|
2023-05-05 19:25:06 +08:00
|
|
|
static const struct clk_ops clk_multiple_parents_no_reparent_mux_ops = {
|
|
|
|
.determine_rate = clk_hw_determine_rate_no_reparent,
|
|
|
|
.get_parent = clk_multiple_parents_mux_get_parent,
|
|
|
|
.set_parent = clk_multiple_parents_mux_set_parent,
|
|
|
|
};
|
|
|
|
|
2022-02-25 22:35:24 +08:00
|
|
|
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_rate_ops);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int clk_maximize_test_init(struct kunit *test)
|
|
|
|
{
|
|
|
|
return clk_test_init_with_ops(test, &clk_dummy_maximize_rate_ops);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int clk_minimize_test_init(struct kunit *test)
|
|
|
|
{
|
|
|
|
return clk_test_init_with_ops(test, &clk_dummy_minimize_rate_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;
|
2022-08-16 19:25:06 +08:00
|
|
|
struct clk *clk = clk_hw_get_clk(hw, NULL);
|
2022-02-25 22:35:24 +08:00
|
|
|
unsigned long rate;
|
|
|
|
|
|
|
|
rate = clk_get_rate(clk);
|
|
|
|
KUNIT_ASSERT_GT(test, rate, 0);
|
|
|
|
KUNIT_EXPECT_EQ(test, rate, ctx->rate);
|
2022-08-16 19:25:06 +08:00
|
|
|
|
|
|
|
clk_put(clk);
|
2022-02-25 22:35:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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;
|
2022-08-16 19:25:06 +08:00
|
|
|
struct clk *clk = clk_hw_get_clk(hw, NULL);
|
2022-02-25 22:35:24 +08:00
|
|
|
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);
|
2022-08-16 19:25:06 +08:00
|
|
|
|
|
|
|
clk_put(clk);
|
2022-02-25 22:35:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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;
|
2022-08-16 19:25:06 +08:00
|
|
|
struct clk *clk = clk_hw_get_clk(hw, NULL);
|
2022-02-25 22:35:24 +08:00
|
|
|
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);
|
2022-08-16 19:25:06 +08:00
|
|
|
|
|
|
|
clk_put(clk);
|
2022-02-25 22:35:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Test that clk_round_rate and clk_set_rate are consitent and will
|
|
|
|
* return the same frequency.
|
|
|
|
*/
|
|
|
|
static void clk_test_round_set_get_rate(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_dummy_context *ctx = test->priv;
|
|
|
|
struct clk_hw *hw = &ctx->hw;
|
2022-08-16 19:25:06 +08:00
|
|
|
struct clk *clk = clk_hw_get_clk(hw, NULL);
|
2023-05-05 19:25:04 +08:00
|
|
|
unsigned long set_rate;
|
|
|
|
long rounded_rate;
|
2022-02-25 22:35:24 +08:00
|
|
|
|
|
|
|
rounded_rate = clk_round_rate(clk, DUMMY_CLOCK_RATE_1);
|
|
|
|
KUNIT_ASSERT_GT(test, rounded_rate, 0);
|
|
|
|
KUNIT_EXPECT_EQ(test, rounded_rate, DUMMY_CLOCK_RATE_1);
|
|
|
|
|
|
|
|
KUNIT_ASSERT_EQ(test,
|
|
|
|
clk_set_rate(clk, DUMMY_CLOCK_RATE_1),
|
|
|
|
0);
|
|
|
|
|
|
|
|
set_rate = clk_get_rate(clk);
|
|
|
|
KUNIT_ASSERT_GT(test, set_rate, 0);
|
|
|
|
KUNIT_EXPECT_EQ(test, rounded_rate, set_rate);
|
2022-08-16 19:25:06 +08:00
|
|
|
|
|
|
|
clk_put(clk);
|
2022-02-25 22:35:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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),
|
|
|
|
KUNIT_CASE(clk_test_round_set_get_rate),
|
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
2022-08-16 19:25:11 +08:00
|
|
|
/*
|
|
|
|
* Test suite for a basic rate clock, without any parent.
|
|
|
|
*
|
|
|
|
* These tests exercise the rate API with simple scenarios
|
|
|
|
*/
|
2022-02-25 22:35:24 +08:00
|
|
|
static struct kunit_suite clk_test_suite = {
|
|
|
|
.name = "clk-test",
|
|
|
|
.init = clk_test_init,
|
|
|
|
.exit = clk_test_exit,
|
|
|
|
.test_cases = clk_test_cases,
|
|
|
|
};
|
|
|
|
|
2022-08-16 19:25:13 +08:00
|
|
|
static int clk_uncached_test_init(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_dummy_context *ctx;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
|
|
|
|
if (!ctx)
|
|
|
|
return -ENOMEM;
|
|
|
|
test->priv = ctx;
|
|
|
|
|
|
|
|
ctx->rate = DUMMY_CLOCK_INIT_RATE;
|
|
|
|
ctx->hw.init = CLK_HW_INIT_NO_PARENT("test-clk",
|
|
|
|
&clk_dummy_rate_ops,
|
|
|
|
CLK_GET_RATE_NOCACHE);
|
|
|
|
|
|
|
|
ret = clk_hw_register(NULL, &ctx->hw);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Test that for an uncached clock, the clock framework doesn't cache
|
|
|
|
* the rate and clk_get_rate() will return the underlying clock rate
|
|
|
|
* even if it changed.
|
|
|
|
*/
|
|
|
|
static void clk_test_uncached_get_rate(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_dummy_context *ctx = test->priv;
|
|
|
|
struct clk_hw *hw = &ctx->hw;
|
|
|
|
struct clk *clk = clk_hw_get_clk(hw, NULL);
|
|
|
|
unsigned long rate;
|
|
|
|
|
|
|
|
rate = clk_get_rate(clk);
|
|
|
|
KUNIT_ASSERT_GT(test, rate, 0);
|
|
|
|
KUNIT_EXPECT_EQ(test, rate, DUMMY_CLOCK_INIT_RATE);
|
|
|
|
|
|
|
|
/* We change the rate behind the clock framework's back */
|
|
|
|
ctx->rate = DUMMY_CLOCK_RATE_1;
|
|
|
|
rate = clk_get_rate(clk);
|
|
|
|
KUNIT_ASSERT_GT(test, rate, 0);
|
|
|
|
KUNIT_EXPECT_EQ(test, rate, DUMMY_CLOCK_RATE_1);
|
|
|
|
|
|
|
|
clk_put(clk);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Test that for an uncached clock, clk_set_rate_range() will work
|
|
|
|
* properly if the rate hasn't changed.
|
|
|
|
*/
|
|
|
|
static void clk_test_uncached_set_range(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_dummy_context *ctx = test->priv;
|
|
|
|
struct clk_hw *hw = &ctx->hw;
|
|
|
|
struct clk *clk = clk_hw_get_clk(hw, NULL);
|
|
|
|
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);
|
|
|
|
|
|
|
|
clk_put(clk);
|
|
|
|
}
|
|
|
|
|
2022-08-16 19:25:17 +08:00
|
|
|
/*
|
|
|
|
* Test that for an uncached clock, clk_set_rate_range() will work
|
|
|
|
* properly if the rate has changed in hardware.
|
|
|
|
*
|
|
|
|
* In this case, it means that if the rate wasn't initially in the range
|
|
|
|
* we're trying to set, but got changed at some point into the range
|
|
|
|
* without the kernel knowing about it, its rate shouldn't be affected.
|
|
|
|
*/
|
|
|
|
static void clk_test_uncached_updated_rate_set_range(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_dummy_context *ctx = test->priv;
|
|
|
|
struct clk_hw *hw = &ctx->hw;
|
|
|
|
struct clk *clk = clk_hw_get_clk(hw, NULL);
|
|
|
|
unsigned long rate;
|
|
|
|
|
|
|
|
/* We change the rate behind the clock framework's back */
|
|
|
|
ctx->rate = DUMMY_CLOCK_RATE_1 + 1000;
|
|
|
|
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 + 1000);
|
|
|
|
|
|
|
|
clk_put(clk);
|
|
|
|
}
|
|
|
|
|
2022-08-16 19:25:13 +08:00
|
|
|
static struct kunit_case clk_uncached_test_cases[] = {
|
|
|
|
KUNIT_CASE(clk_test_uncached_get_rate),
|
|
|
|
KUNIT_CASE(clk_test_uncached_set_range),
|
2022-08-16 19:25:17 +08:00
|
|
|
KUNIT_CASE(clk_test_uncached_updated_rate_set_range),
|
2022-08-16 19:25:13 +08:00
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Test suite for a basic, uncached, rate clock, without any parent.
|
|
|
|
*
|
|
|
|
* These tests exercise the rate API with simple scenarios
|
|
|
|
*/
|
|
|
|
static struct kunit_suite clk_uncached_test_suite = {
|
|
|
|
.name = "clk-uncached-test",
|
|
|
|
.init = clk_uncached_test_init,
|
|
|
|
.exit = clk_test_exit,
|
|
|
|
.test_cases = clk_uncached_test_cases,
|
|
|
|
};
|
|
|
|
|
2022-08-16 19:25:15 +08:00
|
|
|
static int
|
|
|
|
clk_multiple_parents_mux_test_init(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_multiple_parent_ctx *ctx;
|
|
|
|
const char *parents[2] = { "parent-0", "parent-1"};
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
|
|
|
|
if (!ctx)
|
|
|
|
return -ENOMEM;
|
|
|
|
test->priv = ctx;
|
|
|
|
|
|
|
|
ctx->parents_ctx[0].hw.init = CLK_HW_INIT_NO_PARENT("parent-0",
|
|
|
|
&clk_dummy_rate_ops,
|
|
|
|
0);
|
|
|
|
ctx->parents_ctx[0].rate = DUMMY_CLOCK_RATE_1;
|
|
|
|
ret = clk_hw_register(NULL, &ctx->parents_ctx[0].hw);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ctx->parents_ctx[1].hw.init = CLK_HW_INIT_NO_PARENT("parent-1",
|
|
|
|
&clk_dummy_rate_ops,
|
|
|
|
0);
|
|
|
|
ctx->parents_ctx[1].rate = DUMMY_CLOCK_RATE_2;
|
|
|
|
ret = clk_hw_register(NULL, &ctx->parents_ctx[1].hw);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ctx->current_parent = 0;
|
|
|
|
ctx->hw.init = CLK_HW_INIT_PARENTS("test-mux", parents,
|
|
|
|
&clk_multiple_parents_mux_ops,
|
|
|
|
CLK_SET_RATE_PARENT);
|
|
|
|
ret = clk_hw_register(NULL, &ctx->hw);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
clk_multiple_parents_mux_test_exit(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_multiple_parent_ctx *ctx = test->priv;
|
|
|
|
|
|
|
|
clk_hw_unregister(&ctx->hw);
|
|
|
|
clk_hw_unregister(&ctx->parents_ctx[0].hw);
|
|
|
|
clk_hw_unregister(&ctx->parents_ctx[1].hw);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Test that for a clock with multiple parents, clk_get_parent()
|
|
|
|
* actually returns the current one.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
clk_test_multiple_parents_mux_get_parent(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_multiple_parent_ctx *ctx = test->priv;
|
|
|
|
struct clk_hw *hw = &ctx->hw;
|
|
|
|
struct clk *clk = clk_hw_get_clk(hw, NULL);
|
|
|
|
struct clk *parent = clk_hw_get_clk(&ctx->parents_ctx[0].hw, NULL);
|
|
|
|
|
|
|
|
KUNIT_EXPECT_TRUE(test, clk_is_match(clk_get_parent(clk), parent));
|
|
|
|
|
|
|
|
clk_put(parent);
|
|
|
|
clk_put(clk);
|
|
|
|
}
|
|
|
|
|
2022-08-16 19:25:24 +08:00
|
|
|
/*
|
|
|
|
* Test that for a clock with a multiple parents, clk_has_parent()
|
|
|
|
* actually reports all of them as parents.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
clk_test_multiple_parents_mux_has_parent(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_multiple_parent_ctx *ctx = test->priv;
|
|
|
|
struct clk_hw *hw = &ctx->hw;
|
|
|
|
struct clk *clk = clk_hw_get_clk(hw, NULL);
|
|
|
|
struct clk *parent;
|
|
|
|
|
|
|
|
parent = clk_hw_get_clk(&ctx->parents_ctx[0].hw, NULL);
|
|
|
|
KUNIT_EXPECT_TRUE(test, clk_has_parent(clk, parent));
|
|
|
|
clk_put(parent);
|
|
|
|
|
|
|
|
parent = clk_hw_get_clk(&ctx->parents_ctx[1].hw, NULL);
|
|
|
|
KUNIT_EXPECT_TRUE(test, clk_has_parent(clk, parent));
|
|
|
|
clk_put(parent);
|
|
|
|
|
|
|
|
clk_put(clk);
|
|
|
|
}
|
|
|
|
|
2022-08-16 19:25:30 +08:00
|
|
|
/*
|
|
|
|
* Test that for a clock with a multiple parents, if we set a range on
|
|
|
|
* that clock and the parent is changed, its rate after the reparenting
|
|
|
|
* is still within the range we asked for.
|
|
|
|
*
|
|
|
|
* FIXME: clk_set_parent() only does the reparenting but doesn't
|
|
|
|
* reevaluate whether the new clock rate is within its boundaries or
|
|
|
|
* not.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
clk_test_multiple_parents_mux_set_range_set_parent_get_rate(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_multiple_parent_ctx *ctx = test->priv;
|
|
|
|
struct clk_hw *hw = &ctx->hw;
|
|
|
|
struct clk *clk = clk_hw_get_clk(hw, NULL);
|
|
|
|
struct clk *parent1, *parent2;
|
|
|
|
unsigned long rate;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
kunit_skip(test, "This needs to be fixed in the core.");
|
|
|
|
|
|
|
|
parent1 = clk_hw_get_clk(&ctx->parents_ctx[0].hw, NULL);
|
|
|
|
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent1);
|
|
|
|
KUNIT_ASSERT_TRUE(test, clk_is_match(clk_get_parent(clk), parent1));
|
|
|
|
|
|
|
|
parent2 = clk_hw_get_clk(&ctx->parents_ctx[1].hw, NULL);
|
|
|
|
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent2);
|
|
|
|
|
|
|
|
ret = clk_set_rate(parent1, DUMMY_CLOCK_RATE_1);
|
|
|
|
KUNIT_ASSERT_EQ(test, ret, 0);
|
|
|
|
|
|
|
|
ret = clk_set_rate(parent2, DUMMY_CLOCK_RATE_2);
|
|
|
|
KUNIT_ASSERT_EQ(test, ret, 0);
|
|
|
|
|
|
|
|
ret = clk_set_rate_range(clk,
|
|
|
|
DUMMY_CLOCK_RATE_1 - 1000,
|
|
|
|
DUMMY_CLOCK_RATE_1 + 1000);
|
|
|
|
KUNIT_ASSERT_EQ(test, ret, 0);
|
|
|
|
|
|
|
|
ret = clk_set_parent(clk, parent2);
|
|
|
|
KUNIT_ASSERT_EQ(test, ret, 0);
|
|
|
|
|
|
|
|
rate = clk_get_rate(clk);
|
|
|
|
KUNIT_ASSERT_GT(test, rate, 0);
|
|
|
|
KUNIT_EXPECT_GE(test, rate, DUMMY_CLOCK_RATE_1 - 1000);
|
|
|
|
KUNIT_EXPECT_LE(test, rate, DUMMY_CLOCK_RATE_1 + 1000);
|
|
|
|
|
|
|
|
clk_put(parent2);
|
|
|
|
clk_put(parent1);
|
|
|
|
clk_put(clk);
|
|
|
|
}
|
|
|
|
|
2022-08-16 19:25:15 +08:00
|
|
|
static struct kunit_case clk_multiple_parents_mux_test_cases[] = {
|
|
|
|
KUNIT_CASE(clk_test_multiple_parents_mux_get_parent),
|
2022-08-16 19:25:24 +08:00
|
|
|
KUNIT_CASE(clk_test_multiple_parents_mux_has_parent),
|
2022-08-16 19:25:30 +08:00
|
|
|
KUNIT_CASE(clk_test_multiple_parents_mux_set_range_set_parent_get_rate),
|
2022-08-16 19:25:15 +08:00
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Test suite for a basic mux clock with two parents, with
|
|
|
|
* CLK_SET_RATE_PARENT on the child.
|
|
|
|
*
|
|
|
|
* These tests exercise the consumer API and check that the state of the
|
|
|
|
* child and parents are sane and consistent.
|
|
|
|
*/
|
|
|
|
static struct kunit_suite
|
|
|
|
clk_multiple_parents_mux_test_suite = {
|
|
|
|
.name = "clk-multiple-parents-mux-test",
|
|
|
|
.init = clk_multiple_parents_mux_test_init,
|
|
|
|
.exit = clk_multiple_parents_mux_test_exit,
|
|
|
|
.test_cases = clk_multiple_parents_mux_test_cases,
|
|
|
|
};
|
|
|
|
|
2022-08-16 19:25:16 +08:00
|
|
|
static int
|
|
|
|
clk_orphan_transparent_multiple_parent_mux_test_init(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_multiple_parent_ctx *ctx;
|
|
|
|
const char *parents[2] = { "missing-parent", "proper-parent"};
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
|
|
|
|
if (!ctx)
|
|
|
|
return -ENOMEM;
|
|
|
|
test->priv = ctx;
|
|
|
|
|
|
|
|
ctx->parents_ctx[1].hw.init = CLK_HW_INIT_NO_PARENT("proper-parent",
|
|
|
|
&clk_dummy_rate_ops,
|
|
|
|
0);
|
|
|
|
ctx->parents_ctx[1].rate = DUMMY_CLOCK_INIT_RATE;
|
|
|
|
ret = clk_hw_register(NULL, &ctx->parents_ctx[1].hw);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ctx->hw.init = CLK_HW_INIT_PARENTS("test-orphan-mux", parents,
|
|
|
|
&clk_multiple_parents_mux_ops,
|
|
|
|
CLK_SET_RATE_PARENT);
|
|
|
|
ret = clk_hw_register(NULL, &ctx->hw);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
clk_orphan_transparent_multiple_parent_mux_test_exit(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_multiple_parent_ctx *ctx = test->priv;
|
|
|
|
|
|
|
|
clk_hw_unregister(&ctx->hw);
|
|
|
|
clk_hw_unregister(&ctx->parents_ctx[1].hw);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Test that, for a mux whose current parent hasn't been registered yet and is
|
|
|
|
* thus orphan, clk_get_parent() will return NULL.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
clk_test_orphan_transparent_multiple_parent_mux_get_parent(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_multiple_parent_ctx *ctx = test->priv;
|
|
|
|
struct clk_hw *hw = &ctx->hw;
|
|
|
|
struct clk *clk = clk_hw_get_clk(hw, NULL);
|
|
|
|
|
|
|
|
KUNIT_EXPECT_PTR_EQ(test, clk_get_parent(clk), NULL);
|
|
|
|
|
|
|
|
clk_put(clk);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Test that, for a mux whose current parent hasn't been registered yet,
|
|
|
|
* calling clk_set_parent() to a valid parent will properly update the
|
|
|
|
* mux parent and its orphan status.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
clk_test_orphan_transparent_multiple_parent_mux_set_parent(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_multiple_parent_ctx *ctx = test->priv;
|
|
|
|
struct clk_hw *hw = &ctx->hw;
|
|
|
|
struct clk *clk = clk_hw_get_clk(hw, NULL);
|
|
|
|
struct clk *parent, *new_parent;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
parent = clk_hw_get_clk(&ctx->parents_ctx[1].hw, NULL);
|
|
|
|
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent);
|
|
|
|
|
|
|
|
ret = clk_set_parent(clk, parent);
|
|
|
|
KUNIT_ASSERT_EQ(test, ret, 0);
|
|
|
|
|
|
|
|
new_parent = clk_get_parent(clk);
|
|
|
|
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent);
|
|
|
|
KUNIT_EXPECT_TRUE(test, clk_is_match(parent, new_parent));
|
|
|
|
|
|
|
|
clk_put(parent);
|
|
|
|
clk_put(clk);
|
|
|
|
}
|
|
|
|
|
clk: Set req_rate on reparenting
If a non-rate clock started by default with a parent that never
registered, core->req_rate will be 0. The expectation is that whenever
the parent will be registered, req_rate will be updated with the new
value that has just been computed.
However, if that clock is a mux, clk_set_parent() can also make that
clock no longer orphan. In this case however, we never update req_rate.
The natural solution to this would be to update core->rate and
core->req_rate in clk_reparent() by calling clk_recalc().
However, this doesn't work in all cases. Indeed, clk_recalc() is called
by __clk_set_parent_before(), __clk_set_parent() and
clk_core_reparent(). Both __clk_set_parent_before() and __clk_set_parent
will call clk_recalc() with the enable_lock taken through a call to
clk_enable_lock(), the underlying locking primitive being a spinlock.
clk_recalc() calls the backing driver .recalc_rate hook, and that
implementation might sleep if the underlying device uses a bus with
accesses that might sleep, such as i2c.
In such a situation, we would end up sleeping while holding a spinlock,
and thus in an atomic section.
In order to work around this, we can move the core->rate and
core->req_rate update to the clk_recalc() calling sites, after the
enable_lock has been released if it was taken.
The only situation that could still be problematic is the
clk_core_reparent() -> clk_reparent() case that doesn't have any
locking. clk_core_reparent() is itself called by clk_hw_reparent(),
which is then called by 4 drivers:
* clk-stm32mp1.c, stm32/clk-stm32-core.c and tegra/clk-tegra210-emc.c
use it in their set_parent implementation. The set_parent hook is
only called by __clk_set_parent() and clk_change_rate(), both of
them calling it without the enable_lock taken.
* clk/tegra/clk-tegra124-emc.c calls it as part of its set_rate
implementation. set_rate is only called by clk_change_rate(), again
without the enable_lock taken.
In both cases we can't end up in a situation where the clk_hw_reparent()
caller would hold a spinlock, so it seems like this is a good
workaround.
Let's also add some unit tests to make sure we cover the original bug.
Tested-by: Alexander Stein <alexander.stein@ew.tq-group.com> # imx8mp
Tested-by: Marek Szyprowski <m.szyprowski@samsung.com> # exynos4210, meson g12b
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Link: https://lore.kernel.org/r/20220816112530.1837489-14-maxime@cerno.tech
Tested-by: Linux Kernel Functional Testing <lkft@linaro.org>
Tested-by: Naresh Kamboju <naresh.kamboju@linaro.org>
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
2022-08-16 19:25:18 +08:00
|
|
|
/*
|
|
|
|
* Test that, for a mux that started orphan but got switched to a valid
|
|
|
|
* parent, calling clk_drop_range() on the mux won't affect the parent
|
|
|
|
* rate.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
clk_test_orphan_transparent_multiple_parent_mux_set_parent_drop_range(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_multiple_parent_ctx *ctx = test->priv;
|
|
|
|
struct clk_hw *hw = &ctx->hw;
|
|
|
|
struct clk *clk = clk_hw_get_clk(hw, NULL);
|
|
|
|
struct clk *parent;
|
|
|
|
unsigned long parent_rate, new_parent_rate;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
parent = clk_hw_get_clk(&ctx->parents_ctx[1].hw, NULL);
|
|
|
|
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent);
|
|
|
|
|
|
|
|
parent_rate = clk_get_rate(parent);
|
|
|
|
KUNIT_ASSERT_GT(test, parent_rate, 0);
|
|
|
|
|
|
|
|
ret = clk_set_parent(clk, parent);
|
|
|
|
KUNIT_ASSERT_EQ(test, ret, 0);
|
|
|
|
|
|
|
|
ret = clk_drop_range(clk);
|
|
|
|
KUNIT_ASSERT_EQ(test, ret, 0);
|
|
|
|
|
|
|
|
new_parent_rate = clk_get_rate(clk);
|
|
|
|
KUNIT_ASSERT_GT(test, new_parent_rate, 0);
|
|
|
|
KUNIT_EXPECT_EQ(test, parent_rate, new_parent_rate);
|
|
|
|
|
|
|
|
clk_put(parent);
|
|
|
|
clk_put(clk);
|
|
|
|
}
|
|
|
|
|
2022-08-16 19:25:16 +08:00
|
|
|
/*
|
|
|
|
* Test that, for a mux that started orphan but got switched to a valid
|
|
|
|
* parent, the rate of the mux and its new parent are consistent.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
clk_test_orphan_transparent_multiple_parent_mux_set_parent_get_rate(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_multiple_parent_ctx *ctx = test->priv;
|
|
|
|
struct clk_hw *hw = &ctx->hw;
|
|
|
|
struct clk *clk = clk_hw_get_clk(hw, NULL);
|
|
|
|
struct clk *parent;
|
|
|
|
unsigned long parent_rate, rate;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
parent = clk_hw_get_clk(&ctx->parents_ctx[1].hw, NULL);
|
|
|
|
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent);
|
|
|
|
|
|
|
|
parent_rate = clk_get_rate(parent);
|
|
|
|
KUNIT_ASSERT_GT(test, parent_rate, 0);
|
|
|
|
|
|
|
|
ret = clk_set_parent(clk, parent);
|
|
|
|
KUNIT_ASSERT_EQ(test, ret, 0);
|
|
|
|
|
|
|
|
rate = clk_get_rate(clk);
|
|
|
|
KUNIT_ASSERT_GT(test, rate, 0);
|
|
|
|
KUNIT_EXPECT_EQ(test, parent_rate, rate);
|
|
|
|
|
|
|
|
clk_put(parent);
|
|
|
|
clk_put(clk);
|
|
|
|
}
|
|
|
|
|
clk: Set req_rate on reparenting
If a non-rate clock started by default with a parent that never
registered, core->req_rate will be 0. The expectation is that whenever
the parent will be registered, req_rate will be updated with the new
value that has just been computed.
However, if that clock is a mux, clk_set_parent() can also make that
clock no longer orphan. In this case however, we never update req_rate.
The natural solution to this would be to update core->rate and
core->req_rate in clk_reparent() by calling clk_recalc().
However, this doesn't work in all cases. Indeed, clk_recalc() is called
by __clk_set_parent_before(), __clk_set_parent() and
clk_core_reparent(). Both __clk_set_parent_before() and __clk_set_parent
will call clk_recalc() with the enable_lock taken through a call to
clk_enable_lock(), the underlying locking primitive being a spinlock.
clk_recalc() calls the backing driver .recalc_rate hook, and that
implementation might sleep if the underlying device uses a bus with
accesses that might sleep, such as i2c.
In such a situation, we would end up sleeping while holding a spinlock,
and thus in an atomic section.
In order to work around this, we can move the core->rate and
core->req_rate update to the clk_recalc() calling sites, after the
enable_lock has been released if it was taken.
The only situation that could still be problematic is the
clk_core_reparent() -> clk_reparent() case that doesn't have any
locking. clk_core_reparent() is itself called by clk_hw_reparent(),
which is then called by 4 drivers:
* clk-stm32mp1.c, stm32/clk-stm32-core.c and tegra/clk-tegra210-emc.c
use it in their set_parent implementation. The set_parent hook is
only called by __clk_set_parent() and clk_change_rate(), both of
them calling it without the enable_lock taken.
* clk/tegra/clk-tegra124-emc.c calls it as part of its set_rate
implementation. set_rate is only called by clk_change_rate(), again
without the enable_lock taken.
In both cases we can't end up in a situation where the clk_hw_reparent()
caller would hold a spinlock, so it seems like this is a good
workaround.
Let's also add some unit tests to make sure we cover the original bug.
Tested-by: Alexander Stein <alexander.stein@ew.tq-group.com> # imx8mp
Tested-by: Marek Szyprowski <m.szyprowski@samsung.com> # exynos4210, meson g12b
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Link: https://lore.kernel.org/r/20220816112530.1837489-14-maxime@cerno.tech
Tested-by: Linux Kernel Functional Testing <lkft@linaro.org>
Tested-by: Naresh Kamboju <naresh.kamboju@linaro.org>
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
2022-08-16 19:25:18 +08:00
|
|
|
/*
|
|
|
|
* Test that, for a mux that started orphan but got switched to a valid
|
|
|
|
* parent, calling clk_put() on the mux won't affect the parent rate.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
clk_test_orphan_transparent_multiple_parent_mux_set_parent_put(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_multiple_parent_ctx *ctx = test->priv;
|
|
|
|
struct clk *clk, *parent;
|
|
|
|
unsigned long parent_rate, new_parent_rate;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
parent = clk_hw_get_clk(&ctx->parents_ctx[1].hw, NULL);
|
|
|
|
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent);
|
|
|
|
|
|
|
|
clk = clk_hw_get_clk(&ctx->hw, NULL);
|
|
|
|
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, clk);
|
|
|
|
|
|
|
|
parent_rate = clk_get_rate(parent);
|
|
|
|
KUNIT_ASSERT_GT(test, parent_rate, 0);
|
|
|
|
|
|
|
|
ret = clk_set_parent(clk, parent);
|
|
|
|
KUNIT_ASSERT_EQ(test, ret, 0);
|
|
|
|
|
|
|
|
clk_put(clk);
|
|
|
|
|
|
|
|
new_parent_rate = clk_get_rate(parent);
|
|
|
|
KUNIT_ASSERT_GT(test, new_parent_rate, 0);
|
|
|
|
KUNIT_EXPECT_EQ(test, parent_rate, new_parent_rate);
|
|
|
|
|
|
|
|
clk_put(parent);
|
|
|
|
}
|
|
|
|
|
2022-08-16 19:25:16 +08:00
|
|
|
/*
|
|
|
|
* Test that, for a mux that started orphan but got switched to a valid
|
|
|
|
* parent, calling clk_set_rate_range() will affect the parent state if
|
|
|
|
* its rate is out of range.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
clk_test_orphan_transparent_multiple_parent_mux_set_parent_set_range_modified(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_multiple_parent_ctx *ctx = test->priv;
|
|
|
|
struct clk_hw *hw = &ctx->hw;
|
|
|
|
struct clk *clk = clk_hw_get_clk(hw, NULL);
|
|
|
|
struct clk *parent;
|
|
|
|
unsigned long rate;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
parent = clk_hw_get_clk(&ctx->parents_ctx[1].hw, NULL);
|
|
|
|
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent);
|
|
|
|
|
|
|
|
ret = clk_set_parent(clk, parent);
|
|
|
|
KUNIT_ASSERT_EQ(test, ret, 0);
|
|
|
|
|
|
|
|
ret = clk_set_rate_range(clk, DUMMY_CLOCK_RATE_1, DUMMY_CLOCK_RATE_2);
|
|
|
|
KUNIT_ASSERT_EQ(test, ret, 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);
|
|
|
|
|
|
|
|
clk_put(parent);
|
|
|
|
clk_put(clk);
|
|
|
|
}
|
|
|
|
|
clk: Set req_rate on reparenting
If a non-rate clock started by default with a parent that never
registered, core->req_rate will be 0. The expectation is that whenever
the parent will be registered, req_rate will be updated with the new
value that has just been computed.
However, if that clock is a mux, clk_set_parent() can also make that
clock no longer orphan. In this case however, we never update req_rate.
The natural solution to this would be to update core->rate and
core->req_rate in clk_reparent() by calling clk_recalc().
However, this doesn't work in all cases. Indeed, clk_recalc() is called
by __clk_set_parent_before(), __clk_set_parent() and
clk_core_reparent(). Both __clk_set_parent_before() and __clk_set_parent
will call clk_recalc() with the enable_lock taken through a call to
clk_enable_lock(), the underlying locking primitive being a spinlock.
clk_recalc() calls the backing driver .recalc_rate hook, and that
implementation might sleep if the underlying device uses a bus with
accesses that might sleep, such as i2c.
In such a situation, we would end up sleeping while holding a spinlock,
and thus in an atomic section.
In order to work around this, we can move the core->rate and
core->req_rate update to the clk_recalc() calling sites, after the
enable_lock has been released if it was taken.
The only situation that could still be problematic is the
clk_core_reparent() -> clk_reparent() case that doesn't have any
locking. clk_core_reparent() is itself called by clk_hw_reparent(),
which is then called by 4 drivers:
* clk-stm32mp1.c, stm32/clk-stm32-core.c and tegra/clk-tegra210-emc.c
use it in their set_parent implementation. The set_parent hook is
only called by __clk_set_parent() and clk_change_rate(), both of
them calling it without the enable_lock taken.
* clk/tegra/clk-tegra124-emc.c calls it as part of its set_rate
implementation. set_rate is only called by clk_change_rate(), again
without the enable_lock taken.
In both cases we can't end up in a situation where the clk_hw_reparent()
caller would hold a spinlock, so it seems like this is a good
workaround.
Let's also add some unit tests to make sure we cover the original bug.
Tested-by: Alexander Stein <alexander.stein@ew.tq-group.com> # imx8mp
Tested-by: Marek Szyprowski <m.szyprowski@samsung.com> # exynos4210, meson g12b
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Link: https://lore.kernel.org/r/20220816112530.1837489-14-maxime@cerno.tech
Tested-by: Linux Kernel Functional Testing <lkft@linaro.org>
Tested-by: Naresh Kamboju <naresh.kamboju@linaro.org>
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
2022-08-16 19:25:18 +08:00
|
|
|
/*
|
|
|
|
* Test that, for a mux that started orphan but got switched to a valid
|
|
|
|
* parent, calling clk_set_rate_range() won't affect the parent state if
|
|
|
|
* its rate is within range.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
clk_test_orphan_transparent_multiple_parent_mux_set_parent_set_range_untouched(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_multiple_parent_ctx *ctx = test->priv;
|
|
|
|
struct clk_hw *hw = &ctx->hw;
|
|
|
|
struct clk *clk = clk_hw_get_clk(hw, NULL);
|
|
|
|
struct clk *parent;
|
|
|
|
unsigned long parent_rate, new_parent_rate;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
parent = clk_hw_get_clk(&ctx->parents_ctx[1].hw, NULL);
|
|
|
|
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent);
|
|
|
|
|
|
|
|
parent_rate = clk_get_rate(parent);
|
|
|
|
KUNIT_ASSERT_GT(test, parent_rate, 0);
|
|
|
|
|
|
|
|
ret = clk_set_parent(clk, parent);
|
|
|
|
KUNIT_ASSERT_EQ(test, ret, 0);
|
|
|
|
|
|
|
|
ret = clk_set_rate_range(clk,
|
|
|
|
DUMMY_CLOCK_INIT_RATE - 1000,
|
|
|
|
DUMMY_CLOCK_INIT_RATE + 1000);
|
|
|
|
KUNIT_ASSERT_EQ(test, ret, 0);
|
|
|
|
|
|
|
|
new_parent_rate = clk_get_rate(parent);
|
|
|
|
KUNIT_ASSERT_GT(test, new_parent_rate, 0);
|
|
|
|
KUNIT_EXPECT_EQ(test, parent_rate, new_parent_rate);
|
|
|
|
|
|
|
|
clk_put(parent);
|
|
|
|
clk_put(clk);
|
|
|
|
}
|
|
|
|
|
2022-08-16 19:25:16 +08:00
|
|
|
/*
|
|
|
|
* Test that, for a mux whose current parent hasn't been registered yet,
|
|
|
|
* calling clk_set_rate_range() will succeed, and will be taken into
|
|
|
|
* account when rounding a rate.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
clk_test_orphan_transparent_multiple_parent_mux_set_range_round_rate(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_multiple_parent_ctx *ctx = test->priv;
|
|
|
|
struct clk_hw *hw = &ctx->hw;
|
|
|
|
struct clk *clk = clk_hw_get_clk(hw, NULL);
|
2023-05-05 19:25:04 +08:00
|
|
|
long rate;
|
2022-08-16 19:25:16 +08:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = clk_set_rate_range(clk, DUMMY_CLOCK_RATE_1, DUMMY_CLOCK_RATE_2);
|
|
|
|
KUNIT_ASSERT_EQ(test, ret, 0);
|
|
|
|
|
|
|
|
rate = clk_round_rate(clk, DUMMY_CLOCK_RATE_1 - 1000);
|
|
|
|
KUNIT_ASSERT_GT(test, rate, 0);
|
|
|
|
KUNIT_EXPECT_GE(test, rate, DUMMY_CLOCK_RATE_1);
|
|
|
|
KUNIT_EXPECT_LE(test, rate, DUMMY_CLOCK_RATE_2);
|
|
|
|
|
|
|
|
clk_put(clk);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Test that, for a mux that started orphan, was assigned and rate and
|
|
|
|
* then got switched to a valid parent, its rate is eventually within
|
|
|
|
* range.
|
|
|
|
*
|
|
|
|
* FIXME: Even though we update the rate as part of clk_set_parent(), we
|
|
|
|
* don't evaluate whether that new rate is within range and needs to be
|
|
|
|
* adjusted.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
clk_test_orphan_transparent_multiple_parent_mux_set_range_set_parent_get_rate(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_multiple_parent_ctx *ctx = test->priv;
|
|
|
|
struct clk_hw *hw = &ctx->hw;
|
|
|
|
struct clk *clk = clk_hw_get_clk(hw, NULL);
|
|
|
|
struct clk *parent;
|
|
|
|
unsigned long rate;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
kunit_skip(test, "This needs to be fixed in the core.");
|
|
|
|
|
|
|
|
clk_hw_set_rate_range(hw, DUMMY_CLOCK_RATE_1, DUMMY_CLOCK_RATE_2);
|
|
|
|
|
|
|
|
parent = clk_hw_get_clk(&ctx->parents_ctx[1].hw, NULL);
|
|
|
|
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent);
|
|
|
|
|
|
|
|
ret = clk_set_parent(clk, parent);
|
|
|
|
KUNIT_ASSERT_EQ(test, ret, 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);
|
|
|
|
|
|
|
|
clk_put(parent);
|
|
|
|
clk_put(clk);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct kunit_case clk_orphan_transparent_multiple_parent_mux_test_cases[] = {
|
|
|
|
KUNIT_CASE(clk_test_orphan_transparent_multiple_parent_mux_get_parent),
|
|
|
|
KUNIT_CASE(clk_test_orphan_transparent_multiple_parent_mux_set_parent),
|
clk: Set req_rate on reparenting
If a non-rate clock started by default with a parent that never
registered, core->req_rate will be 0. The expectation is that whenever
the parent will be registered, req_rate will be updated with the new
value that has just been computed.
However, if that clock is a mux, clk_set_parent() can also make that
clock no longer orphan. In this case however, we never update req_rate.
The natural solution to this would be to update core->rate and
core->req_rate in clk_reparent() by calling clk_recalc().
However, this doesn't work in all cases. Indeed, clk_recalc() is called
by __clk_set_parent_before(), __clk_set_parent() and
clk_core_reparent(). Both __clk_set_parent_before() and __clk_set_parent
will call clk_recalc() with the enable_lock taken through a call to
clk_enable_lock(), the underlying locking primitive being a spinlock.
clk_recalc() calls the backing driver .recalc_rate hook, and that
implementation might sleep if the underlying device uses a bus with
accesses that might sleep, such as i2c.
In such a situation, we would end up sleeping while holding a spinlock,
and thus in an atomic section.
In order to work around this, we can move the core->rate and
core->req_rate update to the clk_recalc() calling sites, after the
enable_lock has been released if it was taken.
The only situation that could still be problematic is the
clk_core_reparent() -> clk_reparent() case that doesn't have any
locking. clk_core_reparent() is itself called by clk_hw_reparent(),
which is then called by 4 drivers:
* clk-stm32mp1.c, stm32/clk-stm32-core.c and tegra/clk-tegra210-emc.c
use it in their set_parent implementation. The set_parent hook is
only called by __clk_set_parent() and clk_change_rate(), both of
them calling it without the enable_lock taken.
* clk/tegra/clk-tegra124-emc.c calls it as part of its set_rate
implementation. set_rate is only called by clk_change_rate(), again
without the enable_lock taken.
In both cases we can't end up in a situation where the clk_hw_reparent()
caller would hold a spinlock, so it seems like this is a good
workaround.
Let's also add some unit tests to make sure we cover the original bug.
Tested-by: Alexander Stein <alexander.stein@ew.tq-group.com> # imx8mp
Tested-by: Marek Szyprowski <m.szyprowski@samsung.com> # exynos4210, meson g12b
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Link: https://lore.kernel.org/r/20220816112530.1837489-14-maxime@cerno.tech
Tested-by: Linux Kernel Functional Testing <lkft@linaro.org>
Tested-by: Naresh Kamboju <naresh.kamboju@linaro.org>
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
2022-08-16 19:25:18 +08:00
|
|
|
KUNIT_CASE(clk_test_orphan_transparent_multiple_parent_mux_set_parent_drop_range),
|
2022-08-16 19:25:16 +08:00
|
|
|
KUNIT_CASE(clk_test_orphan_transparent_multiple_parent_mux_set_parent_get_rate),
|
clk: Set req_rate on reparenting
If a non-rate clock started by default with a parent that never
registered, core->req_rate will be 0. The expectation is that whenever
the parent will be registered, req_rate will be updated with the new
value that has just been computed.
However, if that clock is a mux, clk_set_parent() can also make that
clock no longer orphan. In this case however, we never update req_rate.
The natural solution to this would be to update core->rate and
core->req_rate in clk_reparent() by calling clk_recalc().
However, this doesn't work in all cases. Indeed, clk_recalc() is called
by __clk_set_parent_before(), __clk_set_parent() and
clk_core_reparent(). Both __clk_set_parent_before() and __clk_set_parent
will call clk_recalc() with the enable_lock taken through a call to
clk_enable_lock(), the underlying locking primitive being a spinlock.
clk_recalc() calls the backing driver .recalc_rate hook, and that
implementation might sleep if the underlying device uses a bus with
accesses that might sleep, such as i2c.
In such a situation, we would end up sleeping while holding a spinlock,
and thus in an atomic section.
In order to work around this, we can move the core->rate and
core->req_rate update to the clk_recalc() calling sites, after the
enable_lock has been released if it was taken.
The only situation that could still be problematic is the
clk_core_reparent() -> clk_reparent() case that doesn't have any
locking. clk_core_reparent() is itself called by clk_hw_reparent(),
which is then called by 4 drivers:
* clk-stm32mp1.c, stm32/clk-stm32-core.c and tegra/clk-tegra210-emc.c
use it in their set_parent implementation. The set_parent hook is
only called by __clk_set_parent() and clk_change_rate(), both of
them calling it without the enable_lock taken.
* clk/tegra/clk-tegra124-emc.c calls it as part of its set_rate
implementation. set_rate is only called by clk_change_rate(), again
without the enable_lock taken.
In both cases we can't end up in a situation where the clk_hw_reparent()
caller would hold a spinlock, so it seems like this is a good
workaround.
Let's also add some unit tests to make sure we cover the original bug.
Tested-by: Alexander Stein <alexander.stein@ew.tq-group.com> # imx8mp
Tested-by: Marek Szyprowski <m.szyprowski@samsung.com> # exynos4210, meson g12b
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Link: https://lore.kernel.org/r/20220816112530.1837489-14-maxime@cerno.tech
Tested-by: Linux Kernel Functional Testing <lkft@linaro.org>
Tested-by: Naresh Kamboju <naresh.kamboju@linaro.org>
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
2022-08-16 19:25:18 +08:00
|
|
|
KUNIT_CASE(clk_test_orphan_transparent_multiple_parent_mux_set_parent_put),
|
2022-08-16 19:25:16 +08:00
|
|
|
KUNIT_CASE(clk_test_orphan_transparent_multiple_parent_mux_set_parent_set_range_modified),
|
clk: Set req_rate on reparenting
If a non-rate clock started by default with a parent that never
registered, core->req_rate will be 0. The expectation is that whenever
the parent will be registered, req_rate will be updated with the new
value that has just been computed.
However, if that clock is a mux, clk_set_parent() can also make that
clock no longer orphan. In this case however, we never update req_rate.
The natural solution to this would be to update core->rate and
core->req_rate in clk_reparent() by calling clk_recalc().
However, this doesn't work in all cases. Indeed, clk_recalc() is called
by __clk_set_parent_before(), __clk_set_parent() and
clk_core_reparent(). Both __clk_set_parent_before() and __clk_set_parent
will call clk_recalc() with the enable_lock taken through a call to
clk_enable_lock(), the underlying locking primitive being a spinlock.
clk_recalc() calls the backing driver .recalc_rate hook, and that
implementation might sleep if the underlying device uses a bus with
accesses that might sleep, such as i2c.
In such a situation, we would end up sleeping while holding a spinlock,
and thus in an atomic section.
In order to work around this, we can move the core->rate and
core->req_rate update to the clk_recalc() calling sites, after the
enable_lock has been released if it was taken.
The only situation that could still be problematic is the
clk_core_reparent() -> clk_reparent() case that doesn't have any
locking. clk_core_reparent() is itself called by clk_hw_reparent(),
which is then called by 4 drivers:
* clk-stm32mp1.c, stm32/clk-stm32-core.c and tegra/clk-tegra210-emc.c
use it in their set_parent implementation. The set_parent hook is
only called by __clk_set_parent() and clk_change_rate(), both of
them calling it without the enable_lock taken.
* clk/tegra/clk-tegra124-emc.c calls it as part of its set_rate
implementation. set_rate is only called by clk_change_rate(), again
without the enable_lock taken.
In both cases we can't end up in a situation where the clk_hw_reparent()
caller would hold a spinlock, so it seems like this is a good
workaround.
Let's also add some unit tests to make sure we cover the original bug.
Tested-by: Alexander Stein <alexander.stein@ew.tq-group.com> # imx8mp
Tested-by: Marek Szyprowski <m.szyprowski@samsung.com> # exynos4210, meson g12b
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Link: https://lore.kernel.org/r/20220816112530.1837489-14-maxime@cerno.tech
Tested-by: Linux Kernel Functional Testing <lkft@linaro.org>
Tested-by: Naresh Kamboju <naresh.kamboju@linaro.org>
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
2022-08-16 19:25:18 +08:00
|
|
|
KUNIT_CASE(clk_test_orphan_transparent_multiple_parent_mux_set_parent_set_range_untouched),
|
2022-08-16 19:25:16 +08:00
|
|
|
KUNIT_CASE(clk_test_orphan_transparent_multiple_parent_mux_set_range_round_rate),
|
|
|
|
KUNIT_CASE(clk_test_orphan_transparent_multiple_parent_mux_set_range_set_parent_get_rate),
|
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Test suite for a basic mux clock with two parents. The default parent
|
|
|
|
* isn't registered, only the second parent is. By default, the clock
|
|
|
|
* will thus be orphan.
|
|
|
|
*
|
|
|
|
* These tests exercise the behaviour of the consumer API when dealing
|
|
|
|
* with an orphan clock, and how we deal with the transition to a valid
|
|
|
|
* parent.
|
|
|
|
*/
|
|
|
|
static struct kunit_suite clk_orphan_transparent_multiple_parent_mux_test_suite = {
|
|
|
|
.name = "clk-orphan-transparent-multiple-parent-mux-test",
|
|
|
|
.init = clk_orphan_transparent_multiple_parent_mux_test_init,
|
|
|
|
.exit = clk_orphan_transparent_multiple_parent_mux_test_exit,
|
|
|
|
.test_cases = clk_orphan_transparent_multiple_parent_mux_test_cases,
|
|
|
|
};
|
|
|
|
|
2022-03-26 00:11:43 +08:00
|
|
|
struct clk_single_parent_ctx {
|
|
|
|
struct clk_dummy_context parent_ctx;
|
|
|
|
struct clk_hw hw;
|
|
|
|
};
|
|
|
|
|
2022-08-16 19:25:14 +08:00
|
|
|
static int clk_single_parent_mux_test_init(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_single_parent_ctx *ctx;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
|
|
|
|
if (!ctx)
|
|
|
|
return -ENOMEM;
|
|
|
|
test->priv = ctx;
|
|
|
|
|
|
|
|
ctx->parent_ctx.rate = DUMMY_CLOCK_INIT_RATE;
|
|
|
|
ctx->parent_ctx.hw.init =
|
|
|
|
CLK_HW_INIT_NO_PARENT("parent-clk",
|
|
|
|
&clk_dummy_rate_ops,
|
|
|
|
0);
|
|
|
|
|
|
|
|
ret = clk_hw_register(NULL, &ctx->parent_ctx.hw);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ctx->hw.init = CLK_HW_INIT("test-clk", "parent-clk",
|
|
|
|
&clk_dummy_single_parent_ops,
|
|
|
|
CLK_SET_RATE_PARENT);
|
|
|
|
|
|
|
|
ret = clk_hw_register(NULL, &ctx->hw);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
clk_single_parent_mux_test_exit(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_single_parent_ctx *ctx = test->priv;
|
|
|
|
|
|
|
|
clk_hw_unregister(&ctx->hw);
|
|
|
|
clk_hw_unregister(&ctx->parent_ctx.hw);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Test that for a clock with a single parent, clk_get_parent() actually
|
|
|
|
* returns the parent.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
clk_test_single_parent_mux_get_parent(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_single_parent_ctx *ctx = test->priv;
|
|
|
|
struct clk_hw *hw = &ctx->hw;
|
|
|
|
struct clk *clk = clk_hw_get_clk(hw, NULL);
|
|
|
|
struct clk *parent = clk_hw_get_clk(&ctx->parent_ctx.hw, NULL);
|
|
|
|
|
|
|
|
KUNIT_EXPECT_TRUE(test, clk_is_match(clk_get_parent(clk), parent));
|
|
|
|
|
|
|
|
clk_put(parent);
|
|
|
|
clk_put(clk);
|
|
|
|
}
|
|
|
|
|
2022-08-16 19:25:24 +08:00
|
|
|
/*
|
|
|
|
* Test that for a clock with a single parent, clk_has_parent() actually
|
|
|
|
* reports it as a parent.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
clk_test_single_parent_mux_has_parent(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_single_parent_ctx *ctx = test->priv;
|
|
|
|
struct clk_hw *hw = &ctx->hw;
|
|
|
|
struct clk *clk = clk_hw_get_clk(hw, NULL);
|
|
|
|
struct clk *parent = clk_hw_get_clk(&ctx->parent_ctx.hw, NULL);
|
|
|
|
|
|
|
|
KUNIT_EXPECT_TRUE(test, clk_has_parent(clk, parent));
|
|
|
|
|
|
|
|
clk_put(parent);
|
|
|
|
clk_put(clk);
|
|
|
|
}
|
|
|
|
|
2022-08-16 19:25:14 +08:00
|
|
|
/*
|
|
|
|
* Test that for a clock that can't modify its rate and with a single
|
|
|
|
* parent, if we set disjoints range on the parent and then the child,
|
|
|
|
* the second will return an error.
|
|
|
|
*
|
|
|
|
* FIXME: clk_set_rate_range() only considers the current clock when
|
|
|
|
* evaluating whether ranges are disjoints and not the upstream clocks
|
|
|
|
* ranges.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
clk_test_single_parent_mux_set_range_disjoint_child_last(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_single_parent_ctx *ctx = test->priv;
|
|
|
|
struct clk_hw *hw = &ctx->hw;
|
|
|
|
struct clk *clk = clk_hw_get_clk(hw, NULL);
|
|
|
|
struct clk *parent;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
kunit_skip(test, "This needs to be fixed in the core.");
|
|
|
|
|
|
|
|
parent = clk_get_parent(clk);
|
|
|
|
KUNIT_ASSERT_PTR_NE(test, parent, NULL);
|
|
|
|
|
|
|
|
ret = clk_set_rate_range(parent, 1000, 2000);
|
|
|
|
KUNIT_ASSERT_EQ(test, ret, 0);
|
|
|
|
|
|
|
|
ret = clk_set_rate_range(clk, 3000, 4000);
|
|
|
|
KUNIT_EXPECT_LT(test, ret, 0);
|
|
|
|
|
|
|
|
clk_put(clk);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Test that for a clock that can't modify its rate and with a single
|
|
|
|
* parent, if we set disjoints range on the child and then the parent,
|
|
|
|
* the second will return an error.
|
|
|
|
*
|
|
|
|
* FIXME: clk_set_rate_range() only considers the current clock when
|
|
|
|
* evaluating whether ranges are disjoints and not the downstream clocks
|
|
|
|
* ranges.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
clk_test_single_parent_mux_set_range_disjoint_parent_last(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_single_parent_ctx *ctx = test->priv;
|
|
|
|
struct clk_hw *hw = &ctx->hw;
|
|
|
|
struct clk *clk = clk_hw_get_clk(hw, NULL);
|
|
|
|
struct clk *parent;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
kunit_skip(test, "This needs to be fixed in the core.");
|
|
|
|
|
|
|
|
parent = clk_get_parent(clk);
|
|
|
|
KUNIT_ASSERT_PTR_NE(test, parent, NULL);
|
|
|
|
|
|
|
|
ret = clk_set_rate_range(clk, 1000, 2000);
|
|
|
|
KUNIT_ASSERT_EQ(test, ret, 0);
|
|
|
|
|
|
|
|
ret = clk_set_rate_range(parent, 3000, 4000);
|
|
|
|
KUNIT_EXPECT_LT(test, ret, 0);
|
|
|
|
|
|
|
|
clk_put(clk);
|
|
|
|
}
|
|
|
|
|
clk: Stop forwarding clk_rate_requests to the parent
If the clock cannot modify its rate and has CLK_SET_RATE_PARENT,
clk_mux_determine_rate_flags(), clk_core_round_rate_nolock() and a
number of drivers will forward the clk_rate_request to the parent clock.
clk_core_round_rate_nolock() will pass the pointer directly, which means
that we pass a clk_rate_request to the parent that has the rate,
min_rate and max_rate of the child, and the best_parent_rate and
best_parent_hw fields will be relative to the child as well, so will
point to our current clock and its rate. The most common case for
CLK_SET_RATE_PARENT is that the child and parent clock rates will be
equal, so the rate field isn't a worry, but the other fields are.
Similarly, if the parent clock driver ever modifies the best_parent_rate
or best_parent_hw, this will be applied to the child once the call to
clk_core_round_rate_nolock() is done. best_parent_hw is probably not
going to be a valid parent, and best_parent_rate might lead to a parent
rate change different to the one that was initially computed.
clk_mux_determine_rate_flags() and the affected drivers will copy the
request before forwarding it to the parents, so they won't be affected
by the latter issue, but the former is still going to be there and will
lead to erroneous data and context being passed to the various clock
drivers in the same sub-tree.
Let's create two new functions, clk_core_forward_rate_req() and
clk_hw_forward_rate_request() for the framework and the clock providers
that will copy a request from a child clock and update the context to
match the parent's. We also update the relevant call sites in the
framework and drivers to use that new function.
Let's also add a test to make sure we avoid regressions there.
Tested-by: Alexander Stein <alexander.stein@ew.tq-group.com> # imx8mp
Tested-by: Marek Szyprowski <m.szyprowski@samsung.com> # exynos4210, meson g12b
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Link: https://lore.kernel.org/r/20220816112530.1837489-22-maxime@cerno.tech
Tested-by: Linux Kernel Functional Testing <lkft@linaro.org>
Tested-by: Naresh Kamboju <naresh.kamboju@linaro.org>
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
2022-08-16 19:25:26 +08:00
|
|
|
/*
|
|
|
|
* Test that for a clock that can't modify its rate and with a single
|
|
|
|
* parent, if we set a range on the parent and then call
|
|
|
|
* clk_round_rate(), the boundaries of the parent are taken into
|
|
|
|
* account.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
clk_test_single_parent_mux_set_range_round_rate_parent_only(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_single_parent_ctx *ctx = test->priv;
|
|
|
|
struct clk_hw *hw = &ctx->hw;
|
|
|
|
struct clk *clk = clk_hw_get_clk(hw, NULL);
|
|
|
|
struct clk *parent;
|
2023-05-05 19:25:04 +08:00
|
|
|
long rate;
|
clk: Stop forwarding clk_rate_requests to the parent
If the clock cannot modify its rate and has CLK_SET_RATE_PARENT,
clk_mux_determine_rate_flags(), clk_core_round_rate_nolock() and a
number of drivers will forward the clk_rate_request to the parent clock.
clk_core_round_rate_nolock() will pass the pointer directly, which means
that we pass a clk_rate_request to the parent that has the rate,
min_rate and max_rate of the child, and the best_parent_rate and
best_parent_hw fields will be relative to the child as well, so will
point to our current clock and its rate. The most common case for
CLK_SET_RATE_PARENT is that the child and parent clock rates will be
equal, so the rate field isn't a worry, but the other fields are.
Similarly, if the parent clock driver ever modifies the best_parent_rate
or best_parent_hw, this will be applied to the child once the call to
clk_core_round_rate_nolock() is done. best_parent_hw is probably not
going to be a valid parent, and best_parent_rate might lead to a parent
rate change different to the one that was initially computed.
clk_mux_determine_rate_flags() and the affected drivers will copy the
request before forwarding it to the parents, so they won't be affected
by the latter issue, but the former is still going to be there and will
lead to erroneous data and context being passed to the various clock
drivers in the same sub-tree.
Let's create two new functions, clk_core_forward_rate_req() and
clk_hw_forward_rate_request() for the framework and the clock providers
that will copy a request from a child clock and update the context to
match the parent's. We also update the relevant call sites in the
framework and drivers to use that new function.
Let's also add a test to make sure we avoid regressions there.
Tested-by: Alexander Stein <alexander.stein@ew.tq-group.com> # imx8mp
Tested-by: Marek Szyprowski <m.szyprowski@samsung.com> # exynos4210, meson g12b
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Link: https://lore.kernel.org/r/20220816112530.1837489-22-maxime@cerno.tech
Tested-by: Linux Kernel Functional Testing <lkft@linaro.org>
Tested-by: Naresh Kamboju <naresh.kamboju@linaro.org>
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
2022-08-16 19:25:26 +08:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
parent = clk_get_parent(clk);
|
|
|
|
KUNIT_ASSERT_PTR_NE(test, parent, NULL);
|
|
|
|
|
|
|
|
ret = clk_set_rate_range(parent, DUMMY_CLOCK_RATE_1, DUMMY_CLOCK_RATE_2);
|
|
|
|
KUNIT_ASSERT_EQ(test, ret, 0);
|
|
|
|
|
|
|
|
rate = clk_round_rate(clk, DUMMY_CLOCK_RATE_1 - 1000);
|
|
|
|
KUNIT_ASSERT_GT(test, rate, 0);
|
|
|
|
KUNIT_EXPECT_GE(test, rate, DUMMY_CLOCK_RATE_1);
|
|
|
|
KUNIT_EXPECT_LE(test, rate, DUMMY_CLOCK_RATE_2);
|
|
|
|
|
|
|
|
clk_put(clk);
|
|
|
|
}
|
|
|
|
|
2022-08-16 19:25:14 +08:00
|
|
|
/*
|
|
|
|
* Test that for a clock that can't modify its rate and with a single
|
|
|
|
* parent, if we set a range on the parent and a more restrictive one on
|
|
|
|
* the child, and then call clk_round_rate(), the boundaries of the
|
|
|
|
* two clocks are taken into account.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
clk_test_single_parent_mux_set_range_round_rate_child_smaller(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_single_parent_ctx *ctx = test->priv;
|
|
|
|
struct clk_hw *hw = &ctx->hw;
|
|
|
|
struct clk *clk = clk_hw_get_clk(hw, NULL);
|
|
|
|
struct clk *parent;
|
2023-05-05 19:25:04 +08:00
|
|
|
long rate;
|
2022-08-16 19:25:14 +08:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
parent = clk_get_parent(clk);
|
|
|
|
KUNIT_ASSERT_PTR_NE(test, parent, NULL);
|
|
|
|
|
|
|
|
ret = clk_set_rate_range(parent, DUMMY_CLOCK_RATE_1, DUMMY_CLOCK_RATE_2);
|
|
|
|
KUNIT_ASSERT_EQ(test, ret, 0);
|
|
|
|
|
|
|
|
ret = clk_set_rate_range(clk, DUMMY_CLOCK_RATE_1 + 1000, DUMMY_CLOCK_RATE_2 - 1000);
|
|
|
|
KUNIT_ASSERT_EQ(test, ret, 0);
|
|
|
|
|
|
|
|
rate = clk_round_rate(clk, DUMMY_CLOCK_RATE_1 - 1000);
|
|
|
|
KUNIT_ASSERT_GT(test, rate, 0);
|
|
|
|
KUNIT_EXPECT_GE(test, rate, DUMMY_CLOCK_RATE_1 + 1000);
|
|
|
|
KUNIT_EXPECT_LE(test, rate, DUMMY_CLOCK_RATE_2 - 1000);
|
|
|
|
|
|
|
|
rate = clk_round_rate(clk, DUMMY_CLOCK_RATE_2 + 1000);
|
|
|
|
KUNIT_ASSERT_GT(test, rate, 0);
|
|
|
|
KUNIT_EXPECT_GE(test, rate, DUMMY_CLOCK_RATE_1 + 1000);
|
|
|
|
KUNIT_EXPECT_LE(test, rate, DUMMY_CLOCK_RATE_2 - 1000);
|
|
|
|
|
|
|
|
clk_put(clk);
|
|
|
|
}
|
|
|
|
|
clk: Stop forwarding clk_rate_requests to the parent
If the clock cannot modify its rate and has CLK_SET_RATE_PARENT,
clk_mux_determine_rate_flags(), clk_core_round_rate_nolock() and a
number of drivers will forward the clk_rate_request to the parent clock.
clk_core_round_rate_nolock() will pass the pointer directly, which means
that we pass a clk_rate_request to the parent that has the rate,
min_rate and max_rate of the child, and the best_parent_rate and
best_parent_hw fields will be relative to the child as well, so will
point to our current clock and its rate. The most common case for
CLK_SET_RATE_PARENT is that the child and parent clock rates will be
equal, so the rate field isn't a worry, but the other fields are.
Similarly, if the parent clock driver ever modifies the best_parent_rate
or best_parent_hw, this will be applied to the child once the call to
clk_core_round_rate_nolock() is done. best_parent_hw is probably not
going to be a valid parent, and best_parent_rate might lead to a parent
rate change different to the one that was initially computed.
clk_mux_determine_rate_flags() and the affected drivers will copy the
request before forwarding it to the parents, so they won't be affected
by the latter issue, but the former is still going to be there and will
lead to erroneous data and context being passed to the various clock
drivers in the same sub-tree.
Let's create two new functions, clk_core_forward_rate_req() and
clk_hw_forward_rate_request() for the framework and the clock providers
that will copy a request from a child clock and update the context to
match the parent's. We also update the relevant call sites in the
framework and drivers to use that new function.
Let's also add a test to make sure we avoid regressions there.
Tested-by: Alexander Stein <alexander.stein@ew.tq-group.com> # imx8mp
Tested-by: Marek Szyprowski <m.szyprowski@samsung.com> # exynos4210, meson g12b
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Link: https://lore.kernel.org/r/20220816112530.1837489-22-maxime@cerno.tech
Tested-by: Linux Kernel Functional Testing <lkft@linaro.org>
Tested-by: Naresh Kamboju <naresh.kamboju@linaro.org>
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
2022-08-16 19:25:26 +08:00
|
|
|
/*
|
|
|
|
* Test that for a clock that can't modify its rate and with a single
|
|
|
|
* parent, if we set a range on the child and a more restrictive one on
|
|
|
|
* the parent, and then call clk_round_rate(), the boundaries of the
|
|
|
|
* two clocks are taken into account.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
clk_test_single_parent_mux_set_range_round_rate_parent_smaller(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_single_parent_ctx *ctx = test->priv;
|
|
|
|
struct clk_hw *hw = &ctx->hw;
|
|
|
|
struct clk *clk = clk_hw_get_clk(hw, NULL);
|
|
|
|
struct clk *parent;
|
2023-05-05 19:25:04 +08:00
|
|
|
long rate;
|
clk: Stop forwarding clk_rate_requests to the parent
If the clock cannot modify its rate and has CLK_SET_RATE_PARENT,
clk_mux_determine_rate_flags(), clk_core_round_rate_nolock() and a
number of drivers will forward the clk_rate_request to the parent clock.
clk_core_round_rate_nolock() will pass the pointer directly, which means
that we pass a clk_rate_request to the parent that has the rate,
min_rate and max_rate of the child, and the best_parent_rate and
best_parent_hw fields will be relative to the child as well, so will
point to our current clock and its rate. The most common case for
CLK_SET_RATE_PARENT is that the child and parent clock rates will be
equal, so the rate field isn't a worry, but the other fields are.
Similarly, if the parent clock driver ever modifies the best_parent_rate
or best_parent_hw, this will be applied to the child once the call to
clk_core_round_rate_nolock() is done. best_parent_hw is probably not
going to be a valid parent, and best_parent_rate might lead to a parent
rate change different to the one that was initially computed.
clk_mux_determine_rate_flags() and the affected drivers will copy the
request before forwarding it to the parents, so they won't be affected
by the latter issue, but the former is still going to be there and will
lead to erroneous data and context being passed to the various clock
drivers in the same sub-tree.
Let's create two new functions, clk_core_forward_rate_req() and
clk_hw_forward_rate_request() for the framework and the clock providers
that will copy a request from a child clock and update the context to
match the parent's. We also update the relevant call sites in the
framework and drivers to use that new function.
Let's also add a test to make sure we avoid regressions there.
Tested-by: Alexander Stein <alexander.stein@ew.tq-group.com> # imx8mp
Tested-by: Marek Szyprowski <m.szyprowski@samsung.com> # exynos4210, meson g12b
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Link: https://lore.kernel.org/r/20220816112530.1837489-22-maxime@cerno.tech
Tested-by: Linux Kernel Functional Testing <lkft@linaro.org>
Tested-by: Naresh Kamboju <naresh.kamboju@linaro.org>
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
2022-08-16 19:25:26 +08:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
parent = clk_get_parent(clk);
|
|
|
|
KUNIT_ASSERT_PTR_NE(test, parent, NULL);
|
|
|
|
|
|
|
|
ret = clk_set_rate_range(parent, DUMMY_CLOCK_RATE_1 + 1000, DUMMY_CLOCK_RATE_2 - 1000);
|
|
|
|
KUNIT_ASSERT_EQ(test, ret, 0);
|
|
|
|
|
|
|
|
ret = clk_set_rate_range(clk, DUMMY_CLOCK_RATE_1, DUMMY_CLOCK_RATE_2);
|
|
|
|
KUNIT_ASSERT_EQ(test, ret, 0);
|
|
|
|
|
|
|
|
rate = clk_round_rate(clk, DUMMY_CLOCK_RATE_1 - 1000);
|
|
|
|
KUNIT_ASSERT_GT(test, rate, 0);
|
|
|
|
KUNIT_EXPECT_GE(test, rate, DUMMY_CLOCK_RATE_1 + 1000);
|
|
|
|
KUNIT_EXPECT_LE(test, rate, DUMMY_CLOCK_RATE_2 - 1000);
|
|
|
|
|
|
|
|
rate = clk_round_rate(clk, DUMMY_CLOCK_RATE_2 + 1000);
|
|
|
|
KUNIT_ASSERT_GT(test, rate, 0);
|
|
|
|
KUNIT_EXPECT_GE(test, rate, DUMMY_CLOCK_RATE_1 + 1000);
|
|
|
|
KUNIT_EXPECT_LE(test, rate, DUMMY_CLOCK_RATE_2 - 1000);
|
|
|
|
|
|
|
|
clk_put(clk);
|
|
|
|
}
|
|
|
|
|
2022-08-16 19:25:14 +08:00
|
|
|
static struct kunit_case clk_single_parent_mux_test_cases[] = {
|
|
|
|
KUNIT_CASE(clk_test_single_parent_mux_get_parent),
|
2022-08-16 19:25:24 +08:00
|
|
|
KUNIT_CASE(clk_test_single_parent_mux_has_parent),
|
2022-08-16 19:25:14 +08:00
|
|
|
KUNIT_CASE(clk_test_single_parent_mux_set_range_disjoint_child_last),
|
|
|
|
KUNIT_CASE(clk_test_single_parent_mux_set_range_disjoint_parent_last),
|
|
|
|
KUNIT_CASE(clk_test_single_parent_mux_set_range_round_rate_child_smaller),
|
clk: Stop forwarding clk_rate_requests to the parent
If the clock cannot modify its rate and has CLK_SET_RATE_PARENT,
clk_mux_determine_rate_flags(), clk_core_round_rate_nolock() and a
number of drivers will forward the clk_rate_request to the parent clock.
clk_core_round_rate_nolock() will pass the pointer directly, which means
that we pass a clk_rate_request to the parent that has the rate,
min_rate and max_rate of the child, and the best_parent_rate and
best_parent_hw fields will be relative to the child as well, so will
point to our current clock and its rate. The most common case for
CLK_SET_RATE_PARENT is that the child and parent clock rates will be
equal, so the rate field isn't a worry, but the other fields are.
Similarly, if the parent clock driver ever modifies the best_parent_rate
or best_parent_hw, this will be applied to the child once the call to
clk_core_round_rate_nolock() is done. best_parent_hw is probably not
going to be a valid parent, and best_parent_rate might lead to a parent
rate change different to the one that was initially computed.
clk_mux_determine_rate_flags() and the affected drivers will copy the
request before forwarding it to the parents, so they won't be affected
by the latter issue, but the former is still going to be there and will
lead to erroneous data and context being passed to the various clock
drivers in the same sub-tree.
Let's create two new functions, clk_core_forward_rate_req() and
clk_hw_forward_rate_request() for the framework and the clock providers
that will copy a request from a child clock and update the context to
match the parent's. We also update the relevant call sites in the
framework and drivers to use that new function.
Let's also add a test to make sure we avoid regressions there.
Tested-by: Alexander Stein <alexander.stein@ew.tq-group.com> # imx8mp
Tested-by: Marek Szyprowski <m.szyprowski@samsung.com> # exynos4210, meson g12b
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Link: https://lore.kernel.org/r/20220816112530.1837489-22-maxime@cerno.tech
Tested-by: Linux Kernel Functional Testing <lkft@linaro.org>
Tested-by: Naresh Kamboju <naresh.kamboju@linaro.org>
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
2022-08-16 19:25:26 +08:00
|
|
|
KUNIT_CASE(clk_test_single_parent_mux_set_range_round_rate_parent_only),
|
|
|
|
KUNIT_CASE(clk_test_single_parent_mux_set_range_round_rate_parent_smaller),
|
2022-08-16 19:25:14 +08:00
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Test suite for a basic mux clock with one parent, with
|
|
|
|
* CLK_SET_RATE_PARENT on the child.
|
|
|
|
*
|
|
|
|
* These tests exercise the consumer API and check that the state of the
|
|
|
|
* child and parent are sane and consistent.
|
|
|
|
*/
|
|
|
|
static struct kunit_suite
|
|
|
|
clk_single_parent_mux_test_suite = {
|
|
|
|
.name = "clk-single-parent-mux-test",
|
|
|
|
.init = clk_single_parent_mux_test_init,
|
|
|
|
.exit = clk_single_parent_mux_test_exit,
|
|
|
|
.test_cases = clk_single_parent_mux_test_cases,
|
|
|
|
};
|
|
|
|
|
2022-03-26 00:11:43 +08:00
|
|
|
static int clk_orphan_transparent_single_parent_mux_test_init(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_single_parent_ctx *ctx;
|
|
|
|
struct clk_init_data init = { };
|
|
|
|
const char * const parents[] = { "orphan_parent" };
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
|
|
|
|
if (!ctx)
|
|
|
|
return -ENOMEM;
|
|
|
|
test->priv = ctx;
|
|
|
|
|
|
|
|
init.name = "test_orphan_dummy_parent";
|
|
|
|
init.ops = &clk_dummy_single_parent_ops;
|
|
|
|
init.parent_names = parents;
|
|
|
|
init.num_parents = ARRAY_SIZE(parents);
|
|
|
|
init.flags = CLK_SET_RATE_PARENT;
|
|
|
|
ctx->hw.init = &init;
|
|
|
|
|
|
|
|
ret = clk_hw_register(NULL, &ctx->hw);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
memset(&init, 0, sizeof(init));
|
|
|
|
init.name = "orphan_parent";
|
|
|
|
init.ops = &clk_dummy_rate_ops;
|
|
|
|
ctx->parent_ctx.hw.init = &init;
|
|
|
|
ctx->parent_ctx.rate = DUMMY_CLOCK_INIT_RATE;
|
|
|
|
|
|
|
|
ret = clk_hw_register(NULL, &ctx->parent_ctx.hw);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Test that a mux-only clock, with an initial rate within a range,
|
|
|
|
* will still have the same rate after the range has been enforced.
|
2022-08-16 19:25:12 +08:00
|
|
|
*
|
|
|
|
* See:
|
|
|
|
* https://lore.kernel.org/linux-clk/7720158d-10a7-a17b-73a4-a8615c9c6d5c@collabora.com/
|
2022-03-26 00:11:43 +08:00
|
|
|
*/
|
|
|
|
static void clk_test_orphan_transparent_parent_mux_set_range(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_single_parent_ctx *ctx = test->priv;
|
|
|
|
struct clk_hw *hw = &ctx->hw;
|
2022-08-16 19:25:06 +08:00
|
|
|
struct clk *clk = clk_hw_get_clk(hw, NULL);
|
2022-03-26 00:11:43 +08:00
|
|
|
unsigned long rate, new_rate;
|
|
|
|
|
|
|
|
rate = clk_get_rate(clk);
|
|
|
|
KUNIT_ASSERT_GT(test, rate, 0);
|
|
|
|
|
|
|
|
KUNIT_ASSERT_EQ(test,
|
|
|
|
clk_set_rate_range(clk,
|
|
|
|
ctx->parent_ctx.rate - 1000,
|
|
|
|
ctx->parent_ctx.rate + 1000),
|
|
|
|
0);
|
|
|
|
|
|
|
|
new_rate = clk_get_rate(clk);
|
|
|
|
KUNIT_ASSERT_GT(test, new_rate, 0);
|
|
|
|
KUNIT_EXPECT_EQ(test, rate, new_rate);
|
2022-08-16 19:25:06 +08:00
|
|
|
|
|
|
|
clk_put(clk);
|
2022-03-26 00:11:43 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct kunit_case clk_orphan_transparent_single_parent_mux_test_cases[] = {
|
|
|
|
KUNIT_CASE(clk_test_orphan_transparent_parent_mux_set_range),
|
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
2022-08-16 19:25:11 +08:00
|
|
|
/*
|
|
|
|
* Test suite for a basic mux clock with one parent. The parent is
|
|
|
|
* registered after its child. The clock will thus be an orphan when
|
|
|
|
* registered, but will no longer be when the tests run.
|
|
|
|
*
|
|
|
|
* These tests make sure a clock that used to be orphan has a sane,
|
|
|
|
* consistent, behaviour.
|
|
|
|
*/
|
2022-03-26 00:11:43 +08:00
|
|
|
static struct kunit_suite clk_orphan_transparent_single_parent_test_suite = {
|
|
|
|
.name = "clk-orphan-transparent-single-parent-test",
|
|
|
|
.init = clk_orphan_transparent_single_parent_mux_test_init,
|
2022-08-16 19:25:14 +08:00
|
|
|
.exit = clk_single_parent_mux_test_exit,
|
2022-03-26 00:11:43 +08:00
|
|
|
.test_cases = clk_orphan_transparent_single_parent_mux_test_cases,
|
|
|
|
};
|
|
|
|
|
clk: Set req_rate on reparenting
If a non-rate clock started by default with a parent that never
registered, core->req_rate will be 0. The expectation is that whenever
the parent will be registered, req_rate will be updated with the new
value that has just been computed.
However, if that clock is a mux, clk_set_parent() can also make that
clock no longer orphan. In this case however, we never update req_rate.
The natural solution to this would be to update core->rate and
core->req_rate in clk_reparent() by calling clk_recalc().
However, this doesn't work in all cases. Indeed, clk_recalc() is called
by __clk_set_parent_before(), __clk_set_parent() and
clk_core_reparent(). Both __clk_set_parent_before() and __clk_set_parent
will call clk_recalc() with the enable_lock taken through a call to
clk_enable_lock(), the underlying locking primitive being a spinlock.
clk_recalc() calls the backing driver .recalc_rate hook, and that
implementation might sleep if the underlying device uses a bus with
accesses that might sleep, such as i2c.
In such a situation, we would end up sleeping while holding a spinlock,
and thus in an atomic section.
In order to work around this, we can move the core->rate and
core->req_rate update to the clk_recalc() calling sites, after the
enable_lock has been released if it was taken.
The only situation that could still be problematic is the
clk_core_reparent() -> clk_reparent() case that doesn't have any
locking. clk_core_reparent() is itself called by clk_hw_reparent(),
which is then called by 4 drivers:
* clk-stm32mp1.c, stm32/clk-stm32-core.c and tegra/clk-tegra210-emc.c
use it in their set_parent implementation. The set_parent hook is
only called by __clk_set_parent() and clk_change_rate(), both of
them calling it without the enable_lock taken.
* clk/tegra/clk-tegra124-emc.c calls it as part of its set_rate
implementation. set_rate is only called by clk_change_rate(), again
without the enable_lock taken.
In both cases we can't end up in a situation where the clk_hw_reparent()
caller would hold a spinlock, so it seems like this is a good
workaround.
Let's also add some unit tests to make sure we cover the original bug.
Tested-by: Alexander Stein <alexander.stein@ew.tq-group.com> # imx8mp
Tested-by: Marek Szyprowski <m.szyprowski@samsung.com> # exynos4210, meson g12b
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Link: https://lore.kernel.org/r/20220816112530.1837489-14-maxime@cerno.tech
Tested-by: Linux Kernel Functional Testing <lkft@linaro.org>
Tested-by: Naresh Kamboju <naresh.kamboju@linaro.org>
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
2022-08-16 19:25:18 +08:00
|
|
|
struct clk_single_parent_two_lvl_ctx {
|
|
|
|
struct clk_dummy_context parent_parent_ctx;
|
|
|
|
struct clk_dummy_context parent_ctx;
|
|
|
|
struct clk_hw hw;
|
|
|
|
};
|
|
|
|
|
|
|
|
static int
|
|
|
|
clk_orphan_two_level_root_last_test_init(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_single_parent_two_lvl_ctx *ctx;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
|
|
|
|
if (!ctx)
|
|
|
|
return -ENOMEM;
|
|
|
|
test->priv = ctx;
|
|
|
|
|
|
|
|
ctx->parent_ctx.hw.init =
|
|
|
|
CLK_HW_INIT("intermediate-parent",
|
|
|
|
"root-parent",
|
|
|
|
&clk_dummy_single_parent_ops,
|
|
|
|
CLK_SET_RATE_PARENT);
|
|
|
|
ret = clk_hw_register(NULL, &ctx->parent_ctx.hw);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ctx->hw.init =
|
|
|
|
CLK_HW_INIT("test-clk", "intermediate-parent",
|
|
|
|
&clk_dummy_single_parent_ops,
|
|
|
|
CLK_SET_RATE_PARENT);
|
|
|
|
ret = clk_hw_register(NULL, &ctx->hw);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ctx->parent_parent_ctx.rate = DUMMY_CLOCK_INIT_RATE;
|
|
|
|
ctx->parent_parent_ctx.hw.init =
|
|
|
|
CLK_HW_INIT_NO_PARENT("root-parent",
|
|
|
|
&clk_dummy_rate_ops,
|
|
|
|
0);
|
|
|
|
ret = clk_hw_register(NULL, &ctx->parent_parent_ctx.hw);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
clk_orphan_two_level_root_last_test_exit(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_single_parent_two_lvl_ctx *ctx = test->priv;
|
|
|
|
|
|
|
|
clk_hw_unregister(&ctx->hw);
|
|
|
|
clk_hw_unregister(&ctx->parent_ctx.hw);
|
|
|
|
clk_hw_unregister(&ctx->parent_parent_ctx.hw);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Test that, for a clock whose parent used to be orphan, clk_get_rate()
|
|
|
|
* will return the proper rate.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
clk_orphan_two_level_root_last_test_get_rate(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_single_parent_two_lvl_ctx *ctx = test->priv;
|
|
|
|
struct clk_hw *hw = &ctx->hw;
|
|
|
|
struct clk *clk = clk_hw_get_clk(hw, NULL);
|
|
|
|
unsigned long rate;
|
|
|
|
|
|
|
|
rate = clk_get_rate(clk);
|
|
|
|
KUNIT_EXPECT_EQ(test, rate, DUMMY_CLOCK_INIT_RATE);
|
|
|
|
|
|
|
|
clk_put(clk);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Test that, for a clock whose parent used to be orphan,
|
|
|
|
* clk_set_rate_range() won't affect its rate if it is already within
|
|
|
|
* range.
|
|
|
|
*
|
|
|
|
* See (for Exynos 4210):
|
|
|
|
* https://lore.kernel.org/linux-clk/366a0232-bb4a-c357-6aa8-636e398e05eb@samsung.com/
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
clk_orphan_two_level_root_last_test_set_range(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_single_parent_two_lvl_ctx *ctx = test->priv;
|
|
|
|
struct clk_hw *hw = &ctx->hw;
|
|
|
|
struct clk *clk = clk_hw_get_clk(hw, NULL);
|
|
|
|
unsigned long rate;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = clk_set_rate_range(clk,
|
|
|
|
DUMMY_CLOCK_INIT_RATE - 1000,
|
|
|
|
DUMMY_CLOCK_INIT_RATE + 1000);
|
|
|
|
KUNIT_ASSERT_EQ(test, ret, 0);
|
|
|
|
|
|
|
|
rate = clk_get_rate(clk);
|
|
|
|
KUNIT_ASSERT_GT(test, rate, 0);
|
|
|
|
KUNIT_EXPECT_EQ(test, rate, DUMMY_CLOCK_INIT_RATE);
|
|
|
|
|
|
|
|
clk_put(clk);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct kunit_case
|
|
|
|
clk_orphan_two_level_root_last_test_cases[] = {
|
|
|
|
KUNIT_CASE(clk_orphan_two_level_root_last_test_get_rate),
|
|
|
|
KUNIT_CASE(clk_orphan_two_level_root_last_test_set_range),
|
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Test suite for a basic, transparent, clock with a parent that is also
|
|
|
|
* such a clock. The parent's parent is registered last, while the
|
|
|
|
* parent and its child are registered in that order. The intermediate
|
|
|
|
* and leaf clocks will thus be orphan when registered, but the leaf
|
|
|
|
* clock itself will always have its parent and will never be
|
|
|
|
* reparented. Indeed, it's only orphan because its parent is.
|
|
|
|
*
|
|
|
|
* These tests exercise the behaviour of the consumer API when dealing
|
|
|
|
* with an orphan clock, and how we deal with the transition to a valid
|
|
|
|
* parent.
|
|
|
|
*/
|
|
|
|
static struct kunit_suite
|
|
|
|
clk_orphan_two_level_root_last_test_suite = {
|
|
|
|
.name = "clk-orphan-two-level-root-last-test",
|
|
|
|
.init = clk_orphan_two_level_root_last_test_init,
|
|
|
|
.exit = clk_orphan_two_level_root_last_test_exit,
|
|
|
|
.test_cases = clk_orphan_two_level_root_last_test_cases,
|
|
|
|
};
|
|
|
|
|
2022-02-25 22:35:24 +08:00
|
|
|
/*
|
|
|
|
* Test that clk_set_rate_range won't return an error for a valid range
|
|
|
|
* and that it will make sure the rate of the clock is within the
|
|
|
|
* boundaries.
|
|
|
|
*/
|
|
|
|
static void clk_range_test_set_range(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_dummy_context *ctx = test->priv;
|
|
|
|
struct clk_hw *hw = &ctx->hw;
|
2022-08-16 19:25:06 +08:00
|
|
|
struct clk *clk = clk_hw_get_clk(hw, NULL);
|
2022-02-25 22:35:24 +08:00
|
|
|
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);
|
2022-08-16 19:25:06 +08:00
|
|
|
|
|
|
|
clk_put(clk);
|
2022-02-25 22:35:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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;
|
2022-08-16 19:25:06 +08:00
|
|
|
struct clk *clk = clk_hw_get_clk(hw, NULL);
|
2022-02-25 22:35:24 +08:00
|
|
|
|
|
|
|
KUNIT_EXPECT_LT(test,
|
|
|
|
clk_set_rate_range(clk,
|
|
|
|
DUMMY_CLOCK_RATE_1 + 1000,
|
|
|
|
DUMMY_CLOCK_RATE_1),
|
|
|
|
0);
|
2022-08-16 19:25:06 +08:00
|
|
|
|
|
|
|
clk_put(clk);
|
2022-02-25 22:35:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Test that users can't set multiple, disjoints, range that would be
|
|
|
|
* impossible to meet.
|
|
|
|
*/
|
|
|
|
static void clk_range_test_multiple_disjoints_range(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_dummy_context *ctx = test->priv;
|
|
|
|
struct clk_hw *hw = &ctx->hw;
|
|
|
|
struct clk *user1, *user2;
|
|
|
|
|
|
|
|
user1 = clk_hw_get_clk(hw, NULL);
|
|
|
|
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, user1);
|
|
|
|
|
|
|
|
user2 = clk_hw_get_clk(hw, NULL);
|
|
|
|
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, user2);
|
|
|
|
|
|
|
|
KUNIT_ASSERT_EQ(test,
|
|
|
|
clk_set_rate_range(user1, 1000, 2000),
|
|
|
|
0);
|
|
|
|
|
|
|
|
KUNIT_EXPECT_LT(test,
|
|
|
|
clk_set_rate_range(user2, 3000, 4000),
|
|
|
|
0);
|
|
|
|
|
|
|
|
clk_put(user2);
|
|
|
|
clk_put(user1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Test that if our clock has some boundaries and we try to round a rate
|
2022-02-25 22:35:26 +08:00
|
|
|
* lower than the minimum, the returned rate will be within range.
|
2022-02-25 22:35:24 +08:00
|
|
|
*/
|
|
|
|
static void clk_range_test_set_range_round_rate_lower(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_dummy_context *ctx = test->priv;
|
|
|
|
struct clk_hw *hw = &ctx->hw;
|
2022-08-16 19:25:06 +08:00
|
|
|
struct clk *clk = clk_hw_get_clk(hw, NULL);
|
2022-02-25 22:35:24 +08:00
|
|
|
long rate;
|
|
|
|
|
|
|
|
KUNIT_ASSERT_EQ(test,
|
|
|
|
clk_set_rate_range(clk,
|
|
|
|
DUMMY_CLOCK_RATE_1,
|
|
|
|
DUMMY_CLOCK_RATE_2),
|
|
|
|
0);
|
|
|
|
|
|
|
|
rate = clk_round_rate(clk, DUMMY_CLOCK_RATE_1 - 1000);
|
|
|
|
KUNIT_ASSERT_GT(test, rate, 0);
|
2022-02-25 22:35:26 +08:00
|
|
|
KUNIT_EXPECT_GE(test, rate, DUMMY_CLOCK_RATE_1);
|
|
|
|
KUNIT_EXPECT_LE(test, rate, DUMMY_CLOCK_RATE_2);
|
2022-08-16 19:25:06 +08:00
|
|
|
|
|
|
|
clk_put(clk);
|
2022-02-25 22:35:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Test that if our clock has some boundaries and we try to set a rate
|
2022-02-25 22:35:26 +08:00
|
|
|
* higher than the maximum, the new rate will be within range.
|
2022-02-25 22:35:24 +08:00
|
|
|
*/
|
|
|
|
static void clk_range_test_set_range_set_rate_lower(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_dummy_context *ctx = test->priv;
|
|
|
|
struct clk_hw *hw = &ctx->hw;
|
2022-08-16 19:25:06 +08:00
|
|
|
struct clk *clk = clk_hw_get_clk(hw, NULL);
|
2022-02-25 22:35:26 +08:00
|
|
|
unsigned long rate;
|
2022-02-25 22:35:24 +08:00
|
|
|
|
|
|
|
KUNIT_ASSERT_EQ(test,
|
|
|
|
clk_set_rate_range(clk,
|
|
|
|
DUMMY_CLOCK_RATE_1,
|
|
|
|
DUMMY_CLOCK_RATE_2),
|
|
|
|
0);
|
|
|
|
|
2022-02-25 22:35:26 +08:00
|
|
|
KUNIT_ASSERT_EQ(test,
|
2022-02-25 22:35:24 +08:00
|
|
|
clk_set_rate(clk, DUMMY_CLOCK_RATE_1 - 1000),
|
|
|
|
0);
|
2022-02-25 22:35:26 +08:00
|
|
|
|
|
|
|
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);
|
2022-08-16 19:25:06 +08:00
|
|
|
|
|
|
|
clk_put(clk);
|
2022-02-25 22:35:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Test that if our clock has some boundaries and we try to round and
|
2022-02-25 22:35:26 +08:00
|
|
|
* set a rate lower than the minimum, the rate returned by
|
|
|
|
* clk_round_rate() will be consistent with the new rate set by
|
|
|
|
* clk_set_rate().
|
2022-02-25 22:35:24 +08:00
|
|
|
*/
|
|
|
|
static void clk_range_test_set_range_set_round_rate_consistent_lower(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_dummy_context *ctx = test->priv;
|
|
|
|
struct clk_hw *hw = &ctx->hw;
|
2022-08-16 19:25:06 +08:00
|
|
|
struct clk *clk = clk_hw_get_clk(hw, NULL);
|
2022-02-25 22:35:24 +08:00
|
|
|
long rounded;
|
|
|
|
|
|
|
|
KUNIT_ASSERT_EQ(test,
|
|
|
|
clk_set_rate_range(clk,
|
|
|
|
DUMMY_CLOCK_RATE_1,
|
|
|
|
DUMMY_CLOCK_RATE_2),
|
|
|
|
0);
|
|
|
|
|
|
|
|
rounded = clk_round_rate(clk, DUMMY_CLOCK_RATE_1 - 1000);
|
|
|
|
KUNIT_ASSERT_GT(test, rounded, 0);
|
|
|
|
|
2022-02-25 22:35:26 +08:00
|
|
|
KUNIT_ASSERT_EQ(test,
|
2022-02-25 22:35:24 +08:00
|
|
|
clk_set_rate(clk, DUMMY_CLOCK_RATE_1 - 1000),
|
|
|
|
0);
|
|
|
|
|
2022-02-25 22:35:26 +08:00
|
|
|
KUNIT_EXPECT_EQ(test, rounded, clk_get_rate(clk));
|
2022-08-16 19:25:06 +08:00
|
|
|
|
|
|
|
clk_put(clk);
|
2022-02-25 22:35:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Test that if our clock has some boundaries and we try to round a rate
|
2022-02-25 22:35:26 +08:00
|
|
|
* higher than the maximum, the returned rate will be within range.
|
2022-02-25 22:35:24 +08:00
|
|
|
*/
|
|
|
|
static void clk_range_test_set_range_round_rate_higher(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_dummy_context *ctx = test->priv;
|
|
|
|
struct clk_hw *hw = &ctx->hw;
|
2022-08-16 19:25:06 +08:00
|
|
|
struct clk *clk = clk_hw_get_clk(hw, NULL);
|
2022-02-25 22:35:24 +08:00
|
|
|
long rate;
|
|
|
|
|
|
|
|
KUNIT_ASSERT_EQ(test,
|
|
|
|
clk_set_rate_range(clk,
|
|
|
|
DUMMY_CLOCK_RATE_1,
|
|
|
|
DUMMY_CLOCK_RATE_2),
|
|
|
|
0);
|
|
|
|
|
|
|
|
rate = clk_round_rate(clk, DUMMY_CLOCK_RATE_2 + 1000);
|
|
|
|
KUNIT_ASSERT_GT(test, rate, 0);
|
2022-02-25 22:35:26 +08:00
|
|
|
KUNIT_EXPECT_GE(test, rate, DUMMY_CLOCK_RATE_1);
|
|
|
|
KUNIT_EXPECT_LE(test, rate, DUMMY_CLOCK_RATE_2);
|
2022-08-16 19:25:06 +08:00
|
|
|
|
|
|
|
clk_put(clk);
|
2022-02-25 22:35:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Test that if our clock has some boundaries and we try to set a rate
|
2022-02-25 22:35:26 +08:00
|
|
|
* higher than the maximum, the new rate will be within range.
|
2022-02-25 22:35:24 +08:00
|
|
|
*/
|
|
|
|
static void clk_range_test_set_range_set_rate_higher(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_dummy_context *ctx = test->priv;
|
|
|
|
struct clk_hw *hw = &ctx->hw;
|
2022-08-16 19:25:06 +08:00
|
|
|
struct clk *clk = clk_hw_get_clk(hw, NULL);
|
2022-02-25 22:35:26 +08:00
|
|
|
unsigned long rate;
|
2022-02-25 22:35:24 +08:00
|
|
|
|
|
|
|
KUNIT_ASSERT_EQ(test,
|
|
|
|
clk_set_rate_range(clk,
|
|
|
|
DUMMY_CLOCK_RATE_1,
|
|
|
|
DUMMY_CLOCK_RATE_2),
|
|
|
|
0);
|
|
|
|
|
2022-02-25 22:35:26 +08:00
|
|
|
KUNIT_ASSERT_EQ(test,
|
2022-02-25 22:35:24 +08:00
|
|
|
clk_set_rate(clk, DUMMY_CLOCK_RATE_2 + 1000),
|
|
|
|
0);
|
2022-02-25 22:35:26 +08:00
|
|
|
|
|
|
|
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);
|
2022-08-16 19:25:06 +08:00
|
|
|
|
|
|
|
clk_put(clk);
|
2022-02-25 22:35:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Test that if our clock has some boundaries and we try to round and
|
2022-02-25 22:35:26 +08:00
|
|
|
* set a rate higher than the maximum, the rate returned by
|
|
|
|
* clk_round_rate() will be consistent with the new rate set by
|
|
|
|
* clk_set_rate().
|
2022-02-25 22:35:24 +08:00
|
|
|
*/
|
|
|
|
static void clk_range_test_set_range_set_round_rate_consistent_higher(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_dummy_context *ctx = test->priv;
|
|
|
|
struct clk_hw *hw = &ctx->hw;
|
2022-08-16 19:25:06 +08:00
|
|
|
struct clk *clk = clk_hw_get_clk(hw, NULL);
|
2022-02-25 22:35:24 +08:00
|
|
|
long rounded;
|
|
|
|
|
|
|
|
KUNIT_ASSERT_EQ(test,
|
|
|
|
clk_set_rate_range(clk,
|
|
|
|
DUMMY_CLOCK_RATE_1,
|
|
|
|
DUMMY_CLOCK_RATE_2),
|
|
|
|
0);
|
|
|
|
|
|
|
|
rounded = clk_round_rate(clk, DUMMY_CLOCK_RATE_2 + 1000);
|
|
|
|
KUNIT_ASSERT_GT(test, rounded, 0);
|
|
|
|
|
2022-02-25 22:35:26 +08:00
|
|
|
KUNIT_ASSERT_EQ(test,
|
2022-02-25 22:35:24 +08:00
|
|
|
clk_set_rate(clk, DUMMY_CLOCK_RATE_2 + 1000),
|
|
|
|
0);
|
|
|
|
|
2022-02-25 22:35:26 +08:00
|
|
|
KUNIT_EXPECT_EQ(test, rounded, clk_get_rate(clk));
|
2022-08-16 19:25:06 +08:00
|
|
|
|
|
|
|
clk_put(clk);
|
2022-02-25 22:35:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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;
|
2022-08-16 19:25:06 +08:00
|
|
|
struct clk *clk = clk_hw_get_clk(hw, NULL);
|
2022-02-25 22:35:24 +08:00
|
|
|
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);
|
2022-08-16 19:25:06 +08:00
|
|
|
|
|
|
|
clk_put(clk);
|
2022-02-25 22:35:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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;
|
2022-08-16 19:25:06 +08:00
|
|
|
struct clk *clk = clk_hw_get_clk(hw, NULL);
|
2022-02-25 22:35:24 +08:00
|
|
|
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);
|
2022-08-16 19:25:06 +08:00
|
|
|
|
|
|
|
clk_put(clk);
|
2022-02-25 22:35:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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_multiple_disjoints_range),
|
|
|
|
KUNIT_CASE(clk_range_test_set_range_round_rate_lower),
|
|
|
|
KUNIT_CASE(clk_range_test_set_range_set_rate_lower),
|
|
|
|
KUNIT_CASE(clk_range_test_set_range_set_round_rate_consistent_lower),
|
|
|
|
KUNIT_CASE(clk_range_test_set_range_round_rate_higher),
|
|
|
|
KUNIT_CASE(clk_range_test_set_range_set_rate_higher),
|
|
|
|
KUNIT_CASE(clk_range_test_set_range_set_round_rate_consistent_higher),
|
|
|
|
KUNIT_CASE(clk_range_test_set_range_get_rate_raised),
|
|
|
|
KUNIT_CASE(clk_range_test_set_range_get_rate_lowered),
|
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
2022-08-16 19:25:11 +08:00
|
|
|
/*
|
|
|
|
* Test suite for a basic rate clock, without any parent.
|
|
|
|
*
|
|
|
|
* These tests exercise the rate range API: clk_set_rate_range(),
|
|
|
|
* clk_set_min_rate(), clk_set_max_rate(), clk_drop_range().
|
|
|
|
*/
|
2022-02-25 22:35:24 +08:00
|
|
|
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,
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
2022-02-25 22:35:28 +08:00
|
|
|
* Test that if we have several subsequent calls to
|
|
|
|
* clk_set_rate_range(), the core will reevaluate whether a new rate is
|
|
|
|
* needed each and every time.
|
2022-02-25 22:35:24 +08:00
|
|
|
*
|
2022-02-25 22:35:28 +08:00
|
|
|
* With clk_dummy_maximize_rate_ops, this means that the rate will
|
|
|
|
* trail along the maximum as it evolves.
|
2022-02-25 22:35:24 +08:00
|
|
|
*/
|
|
|
|
static void clk_range_test_set_range_rate_maximized(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_dummy_context *ctx = test->priv;
|
|
|
|
struct clk_hw *hw = &ctx->hw;
|
2022-08-16 19:25:06 +08:00
|
|
|
struct clk *clk = clk_hw_get_clk(hw, NULL);
|
2022-02-25 22:35:24 +08:00
|
|
|
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);
|
|
|
|
|
|
|
|
KUNIT_ASSERT_EQ(test,
|
|
|
|
clk_set_rate_range(clk,
|
|
|
|
DUMMY_CLOCK_RATE_1,
|
|
|
|
DUMMY_CLOCK_RATE_2 - 1000),
|
|
|
|
0);
|
|
|
|
|
|
|
|
rate = clk_get_rate(clk);
|
|
|
|
KUNIT_ASSERT_GT(test, rate, 0);
|
|
|
|
KUNIT_EXPECT_EQ(test, rate, DUMMY_CLOCK_RATE_2 - 1000);
|
|
|
|
|
|
|
|
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);
|
2022-02-25 22:35:28 +08:00
|
|
|
KUNIT_EXPECT_EQ(test, rate, DUMMY_CLOCK_RATE_2);
|
2022-08-16 19:25:06 +08:00
|
|
|
|
|
|
|
clk_put(clk);
|
2022-02-25 22:35:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2022-02-25 22:35:28 +08:00
|
|
|
* Test that if we have several subsequent calls to
|
|
|
|
* clk_set_rate_range(), across multiple users, the core will reevaluate
|
|
|
|
* whether a new rate is needed each and every time.
|
2022-02-25 22:35:24 +08:00
|
|
|
*
|
2022-02-25 22:35:28 +08:00
|
|
|
* With clk_dummy_maximize_rate_ops, this means that the rate will
|
|
|
|
* trail along the maximum as it evolves.
|
2022-02-25 22:35:24 +08:00
|
|
|
*/
|
|
|
|
static void clk_range_test_multiple_set_range_rate_maximized(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_dummy_context *ctx = test->priv;
|
|
|
|
struct clk_hw *hw = &ctx->hw;
|
2022-08-16 19:25:06 +08:00
|
|
|
struct clk *clk = clk_hw_get_clk(hw, NULL);
|
2022-02-25 22:35:24 +08:00
|
|
|
struct clk *user1, *user2;
|
|
|
|
unsigned long rate;
|
|
|
|
|
|
|
|
user1 = clk_hw_get_clk(hw, NULL);
|
|
|
|
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, user1);
|
|
|
|
|
|
|
|
user2 = clk_hw_get_clk(hw, NULL);
|
|
|
|
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, user2);
|
|
|
|
|
|
|
|
KUNIT_ASSERT_EQ(test,
|
|
|
|
clk_set_rate(clk, DUMMY_CLOCK_RATE_2 + 1000),
|
|
|
|
0);
|
|
|
|
|
|
|
|
KUNIT_ASSERT_EQ(test,
|
|
|
|
clk_set_rate_range(user1,
|
|
|
|
0,
|
|
|
|
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);
|
|
|
|
|
|
|
|
KUNIT_ASSERT_EQ(test,
|
|
|
|
clk_set_rate_range(user2,
|
|
|
|
0,
|
|
|
|
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);
|
|
|
|
|
|
|
|
KUNIT_ASSERT_EQ(test,
|
2022-02-25 22:35:29 +08:00
|
|
|
clk_drop_range(user2),
|
2022-02-25 22:35:24 +08:00
|
|
|
0);
|
|
|
|
|
|
|
|
rate = clk_get_rate(clk);
|
|
|
|
KUNIT_ASSERT_GT(test, rate, 0);
|
2022-02-25 22:35:28 +08:00
|
|
|
KUNIT_EXPECT_EQ(test, rate, DUMMY_CLOCK_RATE_2);
|
2022-02-25 22:35:24 +08:00
|
|
|
|
|
|
|
clk_put(user2);
|
|
|
|
clk_put(user1);
|
2022-08-16 19:25:06 +08:00
|
|
|
clk_put(clk);
|
2022-02-25 22:35:24 +08:00
|
|
|
}
|
|
|
|
|
2022-08-16 19:25:07 +08:00
|
|
|
/*
|
|
|
|
* Test that if we have several subsequent calls to
|
|
|
|
* clk_set_rate_range(), across multiple users, the core will reevaluate
|
|
|
|
* whether a new rate is needed, including when a user drop its clock.
|
|
|
|
*
|
|
|
|
* With clk_dummy_maximize_rate_ops, this means that the rate will
|
|
|
|
* trail along the maximum as it evolves.
|
|
|
|
*/
|
|
|
|
static void clk_range_test_multiple_set_range_rate_put_maximized(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_dummy_context *ctx = test->priv;
|
|
|
|
struct clk_hw *hw = &ctx->hw;
|
|
|
|
struct clk *clk = clk_hw_get_clk(hw, NULL);
|
|
|
|
struct clk *user1, *user2;
|
|
|
|
unsigned long rate;
|
|
|
|
|
|
|
|
user1 = clk_hw_get_clk(hw, NULL);
|
|
|
|
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, user1);
|
|
|
|
|
|
|
|
user2 = clk_hw_get_clk(hw, NULL);
|
|
|
|
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, user2);
|
|
|
|
|
|
|
|
KUNIT_ASSERT_EQ(test,
|
|
|
|
clk_set_rate(clk, DUMMY_CLOCK_RATE_2 + 1000),
|
|
|
|
0);
|
|
|
|
|
|
|
|
KUNIT_ASSERT_EQ(test,
|
|
|
|
clk_set_rate_range(user1,
|
|
|
|
0,
|
|
|
|
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);
|
|
|
|
|
|
|
|
KUNIT_ASSERT_EQ(test,
|
|
|
|
clk_set_rate_range(user2,
|
|
|
|
0,
|
|
|
|
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);
|
|
|
|
|
|
|
|
clk_put(user2);
|
|
|
|
|
|
|
|
rate = clk_get_rate(clk);
|
|
|
|
KUNIT_ASSERT_GT(test, rate, 0);
|
|
|
|
KUNIT_EXPECT_EQ(test, rate, DUMMY_CLOCK_RATE_2);
|
|
|
|
|
|
|
|
clk_put(user1);
|
|
|
|
clk_put(clk);
|
|
|
|
}
|
|
|
|
|
2022-02-25 22:35:24 +08:00
|
|
|
static struct kunit_case clk_range_maximize_test_cases[] = {
|
|
|
|
KUNIT_CASE(clk_range_test_set_range_rate_maximized),
|
|
|
|
KUNIT_CASE(clk_range_test_multiple_set_range_rate_maximized),
|
2022-08-16 19:25:07 +08:00
|
|
|
KUNIT_CASE(clk_range_test_multiple_set_range_rate_put_maximized),
|
2022-02-25 22:35:24 +08:00
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
2022-08-16 19:25:11 +08:00
|
|
|
/*
|
|
|
|
* Test suite for a basic rate clock, without any parent.
|
|
|
|
*
|
|
|
|
* These tests exercise the rate range API: clk_set_rate_range(),
|
|
|
|
* clk_set_min_rate(), clk_set_max_rate(), clk_drop_range(), with a
|
|
|
|
* driver that will always try to run at the highest possible rate.
|
|
|
|
*/
|
2022-02-25 22:35:24 +08:00
|
|
|
static struct kunit_suite clk_range_maximize_test_suite = {
|
|
|
|
.name = "clk-range-maximize-test",
|
|
|
|
.init = clk_maximize_test_init,
|
|
|
|
.exit = clk_test_exit,
|
|
|
|
.test_cases = clk_range_maximize_test_cases,
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
2022-02-25 22:35:28 +08:00
|
|
|
* Test that if we have several subsequent calls to
|
|
|
|
* clk_set_rate_range(), the core will reevaluate whether a new rate is
|
|
|
|
* needed each and every time.
|
2022-02-25 22:35:24 +08:00
|
|
|
*
|
2022-02-25 22:35:28 +08:00
|
|
|
* With clk_dummy_minimize_rate_ops, this means that the rate will
|
|
|
|
* trail along the minimum as it evolves.
|
2022-02-25 22:35:24 +08:00
|
|
|
*/
|
|
|
|
static void clk_range_test_set_range_rate_minimized(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_dummy_context *ctx = test->priv;
|
|
|
|
struct clk_hw *hw = &ctx->hw;
|
2022-08-16 19:25:06 +08:00
|
|
|
struct clk *clk = clk_hw_get_clk(hw, NULL);
|
2022-02-25 22:35:24 +08:00
|
|
|
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);
|
|
|
|
|
|
|
|
KUNIT_ASSERT_EQ(test,
|
|
|
|
clk_set_rate_range(clk,
|
|
|
|
DUMMY_CLOCK_RATE_1 + 1000,
|
|
|
|
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 + 1000);
|
|
|
|
|
|
|
|
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);
|
2022-02-25 22:35:28 +08:00
|
|
|
KUNIT_EXPECT_EQ(test, rate, DUMMY_CLOCK_RATE_1);
|
2022-08-16 19:25:06 +08:00
|
|
|
|
|
|
|
clk_put(clk);
|
2022-02-25 22:35:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2022-02-25 22:35:28 +08:00
|
|
|
* Test that if we have several subsequent calls to
|
|
|
|
* clk_set_rate_range(), across multiple users, the core will reevaluate
|
|
|
|
* whether a new rate is needed each and every time.
|
2022-02-25 22:35:24 +08:00
|
|
|
*
|
2022-02-25 22:35:28 +08:00
|
|
|
* With clk_dummy_minimize_rate_ops, this means that the rate will
|
|
|
|
* trail along the minimum as it evolves.
|
2022-02-25 22:35:24 +08:00
|
|
|
*/
|
|
|
|
static void clk_range_test_multiple_set_range_rate_minimized(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_dummy_context *ctx = test->priv;
|
|
|
|
struct clk_hw *hw = &ctx->hw;
|
2022-08-16 19:25:06 +08:00
|
|
|
struct clk *clk = clk_hw_get_clk(hw, NULL);
|
2022-02-25 22:35:24 +08:00
|
|
|
struct clk *user1, *user2;
|
|
|
|
unsigned long rate;
|
|
|
|
|
|
|
|
user1 = clk_hw_get_clk(hw, NULL);
|
|
|
|
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, user1);
|
|
|
|
|
|
|
|
user2 = clk_hw_get_clk(hw, NULL);
|
|
|
|
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, user2);
|
|
|
|
|
|
|
|
KUNIT_ASSERT_EQ(test,
|
|
|
|
clk_set_rate_range(user1,
|
|
|
|
DUMMY_CLOCK_RATE_1,
|
|
|
|
ULONG_MAX),
|
|
|
|
0);
|
|
|
|
|
|
|
|
rate = clk_get_rate(clk);
|
|
|
|
KUNIT_ASSERT_GT(test, rate, 0);
|
|
|
|
KUNIT_EXPECT_EQ(test, rate, DUMMY_CLOCK_RATE_1);
|
|
|
|
|
|
|
|
KUNIT_ASSERT_EQ(test,
|
|
|
|
clk_set_rate_range(user2,
|
|
|
|
DUMMY_CLOCK_RATE_2,
|
|
|
|
ULONG_MAX),
|
|
|
|
0);
|
|
|
|
|
|
|
|
rate = clk_get_rate(clk);
|
|
|
|
KUNIT_ASSERT_GT(test, rate, 0);
|
|
|
|
KUNIT_EXPECT_EQ(test, rate, DUMMY_CLOCK_RATE_2);
|
|
|
|
|
|
|
|
KUNIT_ASSERT_EQ(test,
|
2022-02-25 22:35:29 +08:00
|
|
|
clk_drop_range(user2),
|
2022-02-25 22:35:24 +08:00
|
|
|
0);
|
|
|
|
|
|
|
|
rate = clk_get_rate(clk);
|
|
|
|
KUNIT_ASSERT_GT(test, rate, 0);
|
2022-02-25 22:35:28 +08:00
|
|
|
KUNIT_EXPECT_EQ(test, rate, DUMMY_CLOCK_RATE_1);
|
2022-02-25 22:35:24 +08:00
|
|
|
|
|
|
|
clk_put(user2);
|
|
|
|
clk_put(user1);
|
2022-08-16 19:25:06 +08:00
|
|
|
clk_put(clk);
|
2022-02-25 22:35:24 +08:00
|
|
|
}
|
|
|
|
|
2022-08-16 19:25:07 +08:00
|
|
|
/*
|
|
|
|
* Test that if we have several subsequent calls to
|
|
|
|
* clk_set_rate_range(), across multiple users, the core will reevaluate
|
|
|
|
* whether a new rate is needed, including when a user drop its clock.
|
|
|
|
*
|
|
|
|
* With clk_dummy_minimize_rate_ops, this means that the rate will
|
|
|
|
* trail along the minimum as it evolves.
|
|
|
|
*/
|
|
|
|
static void clk_range_test_multiple_set_range_rate_put_minimized(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_dummy_context *ctx = test->priv;
|
|
|
|
struct clk_hw *hw = &ctx->hw;
|
|
|
|
struct clk *clk = clk_hw_get_clk(hw, NULL);
|
|
|
|
struct clk *user1, *user2;
|
|
|
|
unsigned long rate;
|
|
|
|
|
|
|
|
user1 = clk_hw_get_clk(hw, NULL);
|
|
|
|
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, user1);
|
|
|
|
|
|
|
|
user2 = clk_hw_get_clk(hw, NULL);
|
|
|
|
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, user2);
|
|
|
|
|
|
|
|
KUNIT_ASSERT_EQ(test,
|
|
|
|
clk_set_rate_range(user1,
|
|
|
|
DUMMY_CLOCK_RATE_1,
|
|
|
|
ULONG_MAX),
|
|
|
|
0);
|
|
|
|
|
|
|
|
rate = clk_get_rate(clk);
|
|
|
|
KUNIT_ASSERT_GT(test, rate, 0);
|
|
|
|
KUNIT_EXPECT_EQ(test, rate, DUMMY_CLOCK_RATE_1);
|
|
|
|
|
|
|
|
KUNIT_ASSERT_EQ(test,
|
|
|
|
clk_set_rate_range(user2,
|
|
|
|
DUMMY_CLOCK_RATE_2,
|
|
|
|
ULONG_MAX),
|
|
|
|
0);
|
|
|
|
|
|
|
|
rate = clk_get_rate(clk);
|
|
|
|
KUNIT_ASSERT_GT(test, rate, 0);
|
|
|
|
KUNIT_EXPECT_EQ(test, rate, DUMMY_CLOCK_RATE_2);
|
|
|
|
|
|
|
|
clk_put(user2);
|
|
|
|
|
|
|
|
rate = clk_get_rate(clk);
|
|
|
|
KUNIT_ASSERT_GT(test, rate, 0);
|
|
|
|
KUNIT_EXPECT_EQ(test, rate, DUMMY_CLOCK_RATE_1);
|
|
|
|
|
|
|
|
clk_put(user1);
|
|
|
|
clk_put(clk);
|
|
|
|
}
|
|
|
|
|
2022-02-25 22:35:24 +08:00
|
|
|
static struct kunit_case clk_range_minimize_test_cases[] = {
|
|
|
|
KUNIT_CASE(clk_range_test_set_range_rate_minimized),
|
|
|
|
KUNIT_CASE(clk_range_test_multiple_set_range_rate_minimized),
|
2022-08-16 19:25:07 +08:00
|
|
|
KUNIT_CASE(clk_range_test_multiple_set_range_rate_put_minimized),
|
2022-02-25 22:35:24 +08:00
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
2022-08-16 19:25:11 +08:00
|
|
|
/*
|
|
|
|
* Test suite for a basic rate clock, without any parent.
|
|
|
|
*
|
|
|
|
* These tests exercise the rate range API: clk_set_rate_range(),
|
|
|
|
* clk_set_min_rate(), clk_set_max_rate(), clk_drop_range(), with a
|
|
|
|
* driver that will always try to run at the lowest possible rate.
|
|
|
|
*/
|
2022-02-25 22:35:24 +08:00
|
|
|
static struct kunit_suite clk_range_minimize_test_suite = {
|
|
|
|
.name = "clk-range-minimize-test",
|
|
|
|
.init = clk_minimize_test_init,
|
|
|
|
.exit = clk_test_exit,
|
|
|
|
.test_cases = clk_range_minimize_test_cases,
|
|
|
|
};
|
|
|
|
|
clk: Stop forwarding clk_rate_requests to the parent
If the clock cannot modify its rate and has CLK_SET_RATE_PARENT,
clk_mux_determine_rate_flags(), clk_core_round_rate_nolock() and a
number of drivers will forward the clk_rate_request to the parent clock.
clk_core_round_rate_nolock() will pass the pointer directly, which means
that we pass a clk_rate_request to the parent that has the rate,
min_rate and max_rate of the child, and the best_parent_rate and
best_parent_hw fields will be relative to the child as well, so will
point to our current clock and its rate. The most common case for
CLK_SET_RATE_PARENT is that the child and parent clock rates will be
equal, so the rate field isn't a worry, but the other fields are.
Similarly, if the parent clock driver ever modifies the best_parent_rate
or best_parent_hw, this will be applied to the child once the call to
clk_core_round_rate_nolock() is done. best_parent_hw is probably not
going to be a valid parent, and best_parent_rate might lead to a parent
rate change different to the one that was initially computed.
clk_mux_determine_rate_flags() and the affected drivers will copy the
request before forwarding it to the parents, so they won't be affected
by the latter issue, but the former is still going to be there and will
lead to erroneous data and context being passed to the various clock
drivers in the same sub-tree.
Let's create two new functions, clk_core_forward_rate_req() and
clk_hw_forward_rate_request() for the framework and the clock providers
that will copy a request from a child clock and update the context to
match the parent's. We also update the relevant call sites in the
framework and drivers to use that new function.
Let's also add a test to make sure we avoid regressions there.
Tested-by: Alexander Stein <alexander.stein@ew.tq-group.com> # imx8mp
Tested-by: Marek Szyprowski <m.szyprowski@samsung.com> # exynos4210, meson g12b
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Link: https://lore.kernel.org/r/20220816112530.1837489-22-maxime@cerno.tech
Tested-by: Linux Kernel Functional Testing <lkft@linaro.org>
Tested-by: Naresh Kamboju <naresh.kamboju@linaro.org>
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
2022-08-16 19:25:26 +08:00
|
|
|
struct clk_leaf_mux_ctx {
|
|
|
|
struct clk_multiple_parent_ctx mux_ctx;
|
|
|
|
struct clk_hw hw;
|
2023-09-13 01:55:30 +08:00
|
|
|
struct clk_hw parent;
|
|
|
|
struct clk_rate_request *req;
|
2023-09-13 01:55:31 +08:00
|
|
|
int (*determine_rate_func)(struct clk_hw *hw, struct clk_rate_request *req);
|
2023-09-13 01:55:30 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static int clk_leaf_mux_determine_rate(struct clk_hw *hw, struct clk_rate_request *req)
|
|
|
|
{
|
|
|
|
struct clk_leaf_mux_ctx *ctx = container_of(hw, struct clk_leaf_mux_ctx, hw);
|
|
|
|
int ret;
|
|
|
|
struct clk_rate_request *parent_req = ctx->req;
|
|
|
|
|
|
|
|
clk_hw_forward_rate_request(hw, req, req->best_parent_hw, parent_req, req->rate);
|
2023-09-13 01:55:31 +08:00
|
|
|
ret = ctx->determine_rate_func(req->best_parent_hw, parent_req);
|
2023-09-13 01:55:30 +08:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
req->rate = parent_req->rate;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct clk_ops clk_leaf_mux_set_rate_parent_ops = {
|
|
|
|
.determine_rate = clk_leaf_mux_determine_rate,
|
|
|
|
.set_parent = clk_dummy_single_set_parent,
|
|
|
|
.get_parent = clk_dummy_single_get_parent,
|
clk: Stop forwarding clk_rate_requests to the parent
If the clock cannot modify its rate and has CLK_SET_RATE_PARENT,
clk_mux_determine_rate_flags(), clk_core_round_rate_nolock() and a
number of drivers will forward the clk_rate_request to the parent clock.
clk_core_round_rate_nolock() will pass the pointer directly, which means
that we pass a clk_rate_request to the parent that has the rate,
min_rate and max_rate of the child, and the best_parent_rate and
best_parent_hw fields will be relative to the child as well, so will
point to our current clock and its rate. The most common case for
CLK_SET_RATE_PARENT is that the child and parent clock rates will be
equal, so the rate field isn't a worry, but the other fields are.
Similarly, if the parent clock driver ever modifies the best_parent_rate
or best_parent_hw, this will be applied to the child once the call to
clk_core_round_rate_nolock() is done. best_parent_hw is probably not
going to be a valid parent, and best_parent_rate might lead to a parent
rate change different to the one that was initially computed.
clk_mux_determine_rate_flags() and the affected drivers will copy the
request before forwarding it to the parents, so they won't be affected
by the latter issue, but the former is still going to be there and will
lead to erroneous data and context being passed to the various clock
drivers in the same sub-tree.
Let's create two new functions, clk_core_forward_rate_req() and
clk_hw_forward_rate_request() for the framework and the clock providers
that will copy a request from a child clock and update the context to
match the parent's. We also update the relevant call sites in the
framework and drivers to use that new function.
Let's also add a test to make sure we avoid regressions there.
Tested-by: Alexander Stein <alexander.stein@ew.tq-group.com> # imx8mp
Tested-by: Marek Szyprowski <m.szyprowski@samsung.com> # exynos4210, meson g12b
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Link: https://lore.kernel.org/r/20220816112530.1837489-22-maxime@cerno.tech
Tested-by: Linux Kernel Functional Testing <lkft@linaro.org>
Tested-by: Naresh Kamboju <naresh.kamboju@linaro.org>
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
2022-08-16 19:25:26 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static int
|
|
|
|
clk_leaf_mux_set_rate_parent_test_init(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_leaf_mux_ctx *ctx;
|
|
|
|
const char *top_parents[2] = { "parent-0", "parent-1" };
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
|
|
|
|
if (!ctx)
|
|
|
|
return -ENOMEM;
|
|
|
|
test->priv = ctx;
|
|
|
|
|
|
|
|
ctx->mux_ctx.parents_ctx[0].hw.init = CLK_HW_INIT_NO_PARENT("parent-0",
|
|
|
|
&clk_dummy_rate_ops,
|
|
|
|
0);
|
|
|
|
ctx->mux_ctx.parents_ctx[0].rate = DUMMY_CLOCK_RATE_1;
|
|
|
|
ret = clk_hw_register(NULL, &ctx->mux_ctx.parents_ctx[0].hw);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ctx->mux_ctx.parents_ctx[1].hw.init = CLK_HW_INIT_NO_PARENT("parent-1",
|
|
|
|
&clk_dummy_rate_ops,
|
|
|
|
0);
|
|
|
|
ctx->mux_ctx.parents_ctx[1].rate = DUMMY_CLOCK_RATE_2;
|
|
|
|
ret = clk_hw_register(NULL, &ctx->mux_ctx.parents_ctx[1].hw);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ctx->mux_ctx.current_parent = 0;
|
|
|
|
ctx->mux_ctx.hw.init = CLK_HW_INIT_PARENTS("test-mux", top_parents,
|
|
|
|
&clk_multiple_parents_mux_ops,
|
|
|
|
0);
|
|
|
|
ret = clk_hw_register(NULL, &ctx->mux_ctx.hw);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2023-09-13 01:55:30 +08:00
|
|
|
ctx->parent.init = CLK_HW_INIT_HW("test-parent", &ctx->mux_ctx.hw,
|
|
|
|
&empty_clk_ops, CLK_SET_RATE_PARENT);
|
|
|
|
ret = clk_hw_register(NULL, &ctx->parent);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ctx->hw.init = CLK_HW_INIT_HW("test-clock", &ctx->parent,
|
|
|
|
&clk_leaf_mux_set_rate_parent_ops,
|
clk: Stop forwarding clk_rate_requests to the parent
If the clock cannot modify its rate and has CLK_SET_RATE_PARENT,
clk_mux_determine_rate_flags(), clk_core_round_rate_nolock() and a
number of drivers will forward the clk_rate_request to the parent clock.
clk_core_round_rate_nolock() will pass the pointer directly, which means
that we pass a clk_rate_request to the parent that has the rate,
min_rate and max_rate of the child, and the best_parent_rate and
best_parent_hw fields will be relative to the child as well, so will
point to our current clock and its rate. The most common case for
CLK_SET_RATE_PARENT is that the child and parent clock rates will be
equal, so the rate field isn't a worry, but the other fields are.
Similarly, if the parent clock driver ever modifies the best_parent_rate
or best_parent_hw, this will be applied to the child once the call to
clk_core_round_rate_nolock() is done. best_parent_hw is probably not
going to be a valid parent, and best_parent_rate might lead to a parent
rate change different to the one that was initially computed.
clk_mux_determine_rate_flags() and the affected drivers will copy the
request before forwarding it to the parents, so they won't be affected
by the latter issue, but the former is still going to be there and will
lead to erroneous data and context being passed to the various clock
drivers in the same sub-tree.
Let's create two new functions, clk_core_forward_rate_req() and
clk_hw_forward_rate_request() for the framework and the clock providers
that will copy a request from a child clock and update the context to
match the parent's. We also update the relevant call sites in the
framework and drivers to use that new function.
Let's also add a test to make sure we avoid regressions there.
Tested-by: Alexander Stein <alexander.stein@ew.tq-group.com> # imx8mp
Tested-by: Marek Szyprowski <m.szyprowski@samsung.com> # exynos4210, meson g12b
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Link: https://lore.kernel.org/r/20220816112530.1837489-22-maxime@cerno.tech
Tested-by: Linux Kernel Functional Testing <lkft@linaro.org>
Tested-by: Naresh Kamboju <naresh.kamboju@linaro.org>
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
2022-08-16 19:25:26 +08:00
|
|
|
CLK_SET_RATE_PARENT);
|
|
|
|
ret = clk_hw_register(NULL, &ctx->hw);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void clk_leaf_mux_set_rate_parent_test_exit(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_leaf_mux_ctx *ctx = test->priv;
|
|
|
|
|
|
|
|
clk_hw_unregister(&ctx->hw);
|
2023-09-13 01:55:30 +08:00
|
|
|
clk_hw_unregister(&ctx->parent);
|
clk: Stop forwarding clk_rate_requests to the parent
If the clock cannot modify its rate and has CLK_SET_RATE_PARENT,
clk_mux_determine_rate_flags(), clk_core_round_rate_nolock() and a
number of drivers will forward the clk_rate_request to the parent clock.
clk_core_round_rate_nolock() will pass the pointer directly, which means
that we pass a clk_rate_request to the parent that has the rate,
min_rate and max_rate of the child, and the best_parent_rate and
best_parent_hw fields will be relative to the child as well, so will
point to our current clock and its rate. The most common case for
CLK_SET_RATE_PARENT is that the child and parent clock rates will be
equal, so the rate field isn't a worry, but the other fields are.
Similarly, if the parent clock driver ever modifies the best_parent_rate
or best_parent_hw, this will be applied to the child once the call to
clk_core_round_rate_nolock() is done. best_parent_hw is probably not
going to be a valid parent, and best_parent_rate might lead to a parent
rate change different to the one that was initially computed.
clk_mux_determine_rate_flags() and the affected drivers will copy the
request before forwarding it to the parents, so they won't be affected
by the latter issue, but the former is still going to be there and will
lead to erroneous data and context being passed to the various clock
drivers in the same sub-tree.
Let's create two new functions, clk_core_forward_rate_req() and
clk_hw_forward_rate_request() for the framework and the clock providers
that will copy a request from a child clock and update the context to
match the parent's. We also update the relevant call sites in the
framework and drivers to use that new function.
Let's also add a test to make sure we avoid regressions there.
Tested-by: Alexander Stein <alexander.stein@ew.tq-group.com> # imx8mp
Tested-by: Marek Szyprowski <m.szyprowski@samsung.com> # exynos4210, meson g12b
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Link: https://lore.kernel.org/r/20220816112530.1837489-22-maxime@cerno.tech
Tested-by: Linux Kernel Functional Testing <lkft@linaro.org>
Tested-by: Naresh Kamboju <naresh.kamboju@linaro.org>
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
2022-08-16 19:25:26 +08:00
|
|
|
clk_hw_unregister(&ctx->mux_ctx.hw);
|
|
|
|
clk_hw_unregister(&ctx->mux_ctx.parents_ctx[0].hw);
|
|
|
|
clk_hw_unregister(&ctx->mux_ctx.parents_ctx[1].hw);
|
|
|
|
}
|
|
|
|
|
2023-09-13 01:55:31 +08:00
|
|
|
struct clk_leaf_mux_set_rate_parent_determine_rate_test_case {
|
|
|
|
const char *desc;
|
|
|
|
int (*determine_rate_func)(struct clk_hw *hw, struct clk_rate_request *req);
|
|
|
|
};
|
|
|
|
|
|
|
|
static void
|
|
|
|
clk_leaf_mux_set_rate_parent_determine_rate_test_case_to_desc(
|
|
|
|
const struct clk_leaf_mux_set_rate_parent_determine_rate_test_case *t, char *desc)
|
|
|
|
{
|
|
|
|
strcpy(desc, t->desc);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct clk_leaf_mux_set_rate_parent_determine_rate_test_case
|
|
|
|
clk_leaf_mux_set_rate_parent_determine_rate_test_cases[] = {
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Test that __clk_determine_rate() on the parent that can't
|
|
|
|
* change rate doesn't return a clk_rate_request structure with
|
|
|
|
* the best_parent_hw pointer pointing to the parent.
|
|
|
|
*/
|
|
|
|
.desc = "clk_leaf_mux_set_rate_parent__clk_determine_rate_proper_parent",
|
|
|
|
.determine_rate_func = __clk_determine_rate,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Test that __clk_mux_determine_rate() on the parent that
|
|
|
|
* can't change rate doesn't return a clk_rate_request
|
|
|
|
* structure with the best_parent_hw pointer pointing to
|
|
|
|
* the parent.
|
|
|
|
*/
|
|
|
|
.desc = "clk_leaf_mux_set_rate_parent__clk_mux_determine_rate_proper_parent",
|
|
|
|
.determine_rate_func = __clk_mux_determine_rate,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Test that __clk_mux_determine_rate_closest() on the parent
|
|
|
|
* that can't change rate doesn't return a clk_rate_request
|
|
|
|
* structure with the best_parent_hw pointer pointing to
|
|
|
|
* the parent.
|
|
|
|
*/
|
|
|
|
.desc = "clk_leaf_mux_set_rate_parent__clk_mux_determine_rate_closest_proper_parent",
|
|
|
|
.determine_rate_func = __clk_mux_determine_rate_closest,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Test that clk_hw_determine_rate_no_reparent() on the parent
|
|
|
|
* that can't change rate doesn't return a clk_rate_request
|
|
|
|
* structure with the best_parent_hw pointer pointing to
|
|
|
|
* the parent.
|
|
|
|
*/
|
|
|
|
.desc = "clk_leaf_mux_set_rate_parent_clk_hw_determine_rate_no_reparent_proper_parent",
|
|
|
|
.determine_rate_func = clk_hw_determine_rate_no_reparent,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
KUNIT_ARRAY_PARAM(clk_leaf_mux_set_rate_parent_determine_rate_test,
|
|
|
|
clk_leaf_mux_set_rate_parent_determine_rate_test_cases,
|
|
|
|
clk_leaf_mux_set_rate_parent_determine_rate_test_case_to_desc)
|
|
|
|
|
clk: Stop forwarding clk_rate_requests to the parent
If the clock cannot modify its rate and has CLK_SET_RATE_PARENT,
clk_mux_determine_rate_flags(), clk_core_round_rate_nolock() and a
number of drivers will forward the clk_rate_request to the parent clock.
clk_core_round_rate_nolock() will pass the pointer directly, which means
that we pass a clk_rate_request to the parent that has the rate,
min_rate and max_rate of the child, and the best_parent_rate and
best_parent_hw fields will be relative to the child as well, so will
point to our current clock and its rate. The most common case for
CLK_SET_RATE_PARENT is that the child and parent clock rates will be
equal, so the rate field isn't a worry, but the other fields are.
Similarly, if the parent clock driver ever modifies the best_parent_rate
or best_parent_hw, this will be applied to the child once the call to
clk_core_round_rate_nolock() is done. best_parent_hw is probably not
going to be a valid parent, and best_parent_rate might lead to a parent
rate change different to the one that was initially computed.
clk_mux_determine_rate_flags() and the affected drivers will copy the
request before forwarding it to the parents, so they won't be affected
by the latter issue, but the former is still going to be there and will
lead to erroneous data and context being passed to the various clock
drivers in the same sub-tree.
Let's create two new functions, clk_core_forward_rate_req() and
clk_hw_forward_rate_request() for the framework and the clock providers
that will copy a request from a child clock and update the context to
match the parent's. We also update the relevant call sites in the
framework and drivers to use that new function.
Let's also add a test to make sure we avoid regressions there.
Tested-by: Alexander Stein <alexander.stein@ew.tq-group.com> # imx8mp
Tested-by: Marek Szyprowski <m.szyprowski@samsung.com> # exynos4210, meson g12b
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Link: https://lore.kernel.org/r/20220816112530.1837489-22-maxime@cerno.tech
Tested-by: Linux Kernel Functional Testing <lkft@linaro.org>
Tested-by: Naresh Kamboju <naresh.kamboju@linaro.org>
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
2022-08-16 19:25:26 +08:00
|
|
|
/*
|
2023-09-13 01:55:31 +08:00
|
|
|
* Test that when a clk that can't change rate itself calls a function like
|
|
|
|
* __clk_determine_rate() on its parent it doesn't get back a clk_rate_request
|
|
|
|
* structure that has the best_parent_hw pointer point to the clk_hw passed
|
|
|
|
* into the determine rate function. See commit 262ca38f4b6e ("clk: Stop
|
|
|
|
* forwarding clk_rate_requests to the parent") for more background.
|
clk: Stop forwarding clk_rate_requests to the parent
If the clock cannot modify its rate and has CLK_SET_RATE_PARENT,
clk_mux_determine_rate_flags(), clk_core_round_rate_nolock() and a
number of drivers will forward the clk_rate_request to the parent clock.
clk_core_round_rate_nolock() will pass the pointer directly, which means
that we pass a clk_rate_request to the parent that has the rate,
min_rate and max_rate of the child, and the best_parent_rate and
best_parent_hw fields will be relative to the child as well, so will
point to our current clock and its rate. The most common case for
CLK_SET_RATE_PARENT is that the child and parent clock rates will be
equal, so the rate field isn't a worry, but the other fields are.
Similarly, if the parent clock driver ever modifies the best_parent_rate
or best_parent_hw, this will be applied to the child once the call to
clk_core_round_rate_nolock() is done. best_parent_hw is probably not
going to be a valid parent, and best_parent_rate might lead to a parent
rate change different to the one that was initially computed.
clk_mux_determine_rate_flags() and the affected drivers will copy the
request before forwarding it to the parents, so they won't be affected
by the latter issue, but the former is still going to be there and will
lead to erroneous data and context being passed to the various clock
drivers in the same sub-tree.
Let's create two new functions, clk_core_forward_rate_req() and
clk_hw_forward_rate_request() for the framework and the clock providers
that will copy a request from a child clock and update the context to
match the parent's. We also update the relevant call sites in the
framework and drivers to use that new function.
Let's also add a test to make sure we avoid regressions there.
Tested-by: Alexander Stein <alexander.stein@ew.tq-group.com> # imx8mp
Tested-by: Marek Szyprowski <m.szyprowski@samsung.com> # exynos4210, meson g12b
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Link: https://lore.kernel.org/r/20220816112530.1837489-22-maxime@cerno.tech
Tested-by: Linux Kernel Functional Testing <lkft@linaro.org>
Tested-by: Naresh Kamboju <naresh.kamboju@linaro.org>
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
2022-08-16 19:25:26 +08:00
|
|
|
*/
|
2023-09-13 01:55:31 +08:00
|
|
|
static void clk_leaf_mux_set_rate_parent_determine_rate_test(struct kunit *test)
|
clk: Stop forwarding clk_rate_requests to the parent
If the clock cannot modify its rate and has CLK_SET_RATE_PARENT,
clk_mux_determine_rate_flags(), clk_core_round_rate_nolock() and a
number of drivers will forward the clk_rate_request to the parent clock.
clk_core_round_rate_nolock() will pass the pointer directly, which means
that we pass a clk_rate_request to the parent that has the rate,
min_rate and max_rate of the child, and the best_parent_rate and
best_parent_hw fields will be relative to the child as well, so will
point to our current clock and its rate. The most common case for
CLK_SET_RATE_PARENT is that the child and parent clock rates will be
equal, so the rate field isn't a worry, but the other fields are.
Similarly, if the parent clock driver ever modifies the best_parent_rate
or best_parent_hw, this will be applied to the child once the call to
clk_core_round_rate_nolock() is done. best_parent_hw is probably not
going to be a valid parent, and best_parent_rate might lead to a parent
rate change different to the one that was initially computed.
clk_mux_determine_rate_flags() and the affected drivers will copy the
request before forwarding it to the parents, so they won't be affected
by the latter issue, but the former is still going to be there and will
lead to erroneous data and context being passed to the various clock
drivers in the same sub-tree.
Let's create two new functions, clk_core_forward_rate_req() and
clk_hw_forward_rate_request() for the framework and the clock providers
that will copy a request from a child clock and update the context to
match the parent's. We also update the relevant call sites in the
framework and drivers to use that new function.
Let's also add a test to make sure we avoid regressions there.
Tested-by: Alexander Stein <alexander.stein@ew.tq-group.com> # imx8mp
Tested-by: Marek Szyprowski <m.szyprowski@samsung.com> # exynos4210, meson g12b
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Link: https://lore.kernel.org/r/20220816112530.1837489-22-maxime@cerno.tech
Tested-by: Linux Kernel Functional Testing <lkft@linaro.org>
Tested-by: Naresh Kamboju <naresh.kamboju@linaro.org>
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
2022-08-16 19:25:26 +08:00
|
|
|
{
|
|
|
|
struct clk_leaf_mux_ctx *ctx = test->priv;
|
|
|
|
struct clk_hw *hw = &ctx->hw;
|
|
|
|
struct clk *clk = clk_hw_get_clk(hw, NULL);
|
|
|
|
struct clk_rate_request req;
|
|
|
|
unsigned long rate;
|
2023-09-13 01:55:31 +08:00
|
|
|
const struct clk_leaf_mux_set_rate_parent_determine_rate_test_case *test_param;
|
|
|
|
|
|
|
|
test_param = test->param_value;
|
|
|
|
ctx->determine_rate_func = test_param->determine_rate_func;
|
clk: Stop forwarding clk_rate_requests to the parent
If the clock cannot modify its rate and has CLK_SET_RATE_PARENT,
clk_mux_determine_rate_flags(), clk_core_round_rate_nolock() and a
number of drivers will forward the clk_rate_request to the parent clock.
clk_core_round_rate_nolock() will pass the pointer directly, which means
that we pass a clk_rate_request to the parent that has the rate,
min_rate and max_rate of the child, and the best_parent_rate and
best_parent_hw fields will be relative to the child as well, so will
point to our current clock and its rate. The most common case for
CLK_SET_RATE_PARENT is that the child and parent clock rates will be
equal, so the rate field isn't a worry, but the other fields are.
Similarly, if the parent clock driver ever modifies the best_parent_rate
or best_parent_hw, this will be applied to the child once the call to
clk_core_round_rate_nolock() is done. best_parent_hw is probably not
going to be a valid parent, and best_parent_rate might lead to a parent
rate change different to the one that was initially computed.
clk_mux_determine_rate_flags() and the affected drivers will copy the
request before forwarding it to the parents, so they won't be affected
by the latter issue, but the former is still going to be there and will
lead to erroneous data and context being passed to the various clock
drivers in the same sub-tree.
Let's create two new functions, clk_core_forward_rate_req() and
clk_hw_forward_rate_request() for the framework and the clock providers
that will copy a request from a child clock and update the context to
match the parent's. We also update the relevant call sites in the
framework and drivers to use that new function.
Let's also add a test to make sure we avoid regressions there.
Tested-by: Alexander Stein <alexander.stein@ew.tq-group.com> # imx8mp
Tested-by: Marek Szyprowski <m.szyprowski@samsung.com> # exynos4210, meson g12b
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Link: https://lore.kernel.org/r/20220816112530.1837489-22-maxime@cerno.tech
Tested-by: Linux Kernel Functional Testing <lkft@linaro.org>
Tested-by: Naresh Kamboju <naresh.kamboju@linaro.org>
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
2022-08-16 19:25:26 +08:00
|
|
|
|
2023-09-13 01:55:30 +08:00
|
|
|
ctx->req = &req;
|
clk: Stop forwarding clk_rate_requests to the parent
If the clock cannot modify its rate and has CLK_SET_RATE_PARENT,
clk_mux_determine_rate_flags(), clk_core_round_rate_nolock() and a
number of drivers will forward the clk_rate_request to the parent clock.
clk_core_round_rate_nolock() will pass the pointer directly, which means
that we pass a clk_rate_request to the parent that has the rate,
min_rate and max_rate of the child, and the best_parent_rate and
best_parent_hw fields will be relative to the child as well, so will
point to our current clock and its rate. The most common case for
CLK_SET_RATE_PARENT is that the child and parent clock rates will be
equal, so the rate field isn't a worry, but the other fields are.
Similarly, if the parent clock driver ever modifies the best_parent_rate
or best_parent_hw, this will be applied to the child once the call to
clk_core_round_rate_nolock() is done. best_parent_hw is probably not
going to be a valid parent, and best_parent_rate might lead to a parent
rate change different to the one that was initially computed.
clk_mux_determine_rate_flags() and the affected drivers will copy the
request before forwarding it to the parents, so they won't be affected
by the latter issue, but the former is still going to be there and will
lead to erroneous data and context being passed to the various clock
drivers in the same sub-tree.
Let's create two new functions, clk_core_forward_rate_req() and
clk_hw_forward_rate_request() for the framework and the clock providers
that will copy a request from a child clock and update the context to
match the parent's. We also update the relevant call sites in the
framework and drivers to use that new function.
Let's also add a test to make sure we avoid regressions there.
Tested-by: Alexander Stein <alexander.stein@ew.tq-group.com> # imx8mp
Tested-by: Marek Szyprowski <m.szyprowski@samsung.com> # exynos4210, meson g12b
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Link: https://lore.kernel.org/r/20220816112530.1837489-22-maxime@cerno.tech
Tested-by: Linux Kernel Functional Testing <lkft@linaro.org>
Tested-by: Naresh Kamboju <naresh.kamboju@linaro.org>
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
2022-08-16 19:25:26 +08:00
|
|
|
rate = clk_get_rate(clk);
|
|
|
|
KUNIT_ASSERT_EQ(test, rate, DUMMY_CLOCK_RATE_1);
|
2023-09-13 01:55:30 +08:00
|
|
|
KUNIT_ASSERT_EQ(test, DUMMY_CLOCK_RATE_2, clk_round_rate(clk, DUMMY_CLOCK_RATE_2));
|
clk: Stop forwarding clk_rate_requests to the parent
If the clock cannot modify its rate and has CLK_SET_RATE_PARENT,
clk_mux_determine_rate_flags(), clk_core_round_rate_nolock() and a
number of drivers will forward the clk_rate_request to the parent clock.
clk_core_round_rate_nolock() will pass the pointer directly, which means
that we pass a clk_rate_request to the parent that has the rate,
min_rate and max_rate of the child, and the best_parent_rate and
best_parent_hw fields will be relative to the child as well, so will
point to our current clock and its rate. The most common case for
CLK_SET_RATE_PARENT is that the child and parent clock rates will be
equal, so the rate field isn't a worry, but the other fields are.
Similarly, if the parent clock driver ever modifies the best_parent_rate
or best_parent_hw, this will be applied to the child once the call to
clk_core_round_rate_nolock() is done. best_parent_hw is probably not
going to be a valid parent, and best_parent_rate might lead to a parent
rate change different to the one that was initially computed.
clk_mux_determine_rate_flags() and the affected drivers will copy the
request before forwarding it to the parents, so they won't be affected
by the latter issue, but the former is still going to be there and will
lead to erroneous data and context being passed to the various clock
drivers in the same sub-tree.
Let's create two new functions, clk_core_forward_rate_req() and
clk_hw_forward_rate_request() for the framework and the clock providers
that will copy a request from a child clock and update the context to
match the parent's. We also update the relevant call sites in the
framework and drivers to use that new function.
Let's also add a test to make sure we avoid regressions there.
Tested-by: Alexander Stein <alexander.stein@ew.tq-group.com> # imx8mp
Tested-by: Marek Szyprowski <m.szyprowski@samsung.com> # exynos4210, meson g12b
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Link: https://lore.kernel.org/r/20220816112530.1837489-22-maxime@cerno.tech
Tested-by: Linux Kernel Functional Testing <lkft@linaro.org>
Tested-by: Naresh Kamboju <naresh.kamboju@linaro.org>
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
2022-08-16 19:25:26 +08:00
|
|
|
|
|
|
|
KUNIT_EXPECT_EQ(test, req.rate, DUMMY_CLOCK_RATE_2);
|
|
|
|
KUNIT_EXPECT_EQ(test, req.best_parent_rate, DUMMY_CLOCK_RATE_2);
|
|
|
|
KUNIT_EXPECT_PTR_EQ(test, req.best_parent_hw, &ctx->mux_ctx.hw);
|
|
|
|
|
|
|
|
clk_put(clk);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct kunit_case clk_leaf_mux_set_rate_parent_test_cases[] = {
|
2023-09-13 01:55:31 +08:00
|
|
|
KUNIT_CASE_PARAM(clk_leaf_mux_set_rate_parent_determine_rate_test,
|
|
|
|
clk_leaf_mux_set_rate_parent_determine_rate_test_gen_params),
|
clk: Stop forwarding clk_rate_requests to the parent
If the clock cannot modify its rate and has CLK_SET_RATE_PARENT,
clk_mux_determine_rate_flags(), clk_core_round_rate_nolock() and a
number of drivers will forward the clk_rate_request to the parent clock.
clk_core_round_rate_nolock() will pass the pointer directly, which means
that we pass a clk_rate_request to the parent that has the rate,
min_rate and max_rate of the child, and the best_parent_rate and
best_parent_hw fields will be relative to the child as well, so will
point to our current clock and its rate. The most common case for
CLK_SET_RATE_PARENT is that the child and parent clock rates will be
equal, so the rate field isn't a worry, but the other fields are.
Similarly, if the parent clock driver ever modifies the best_parent_rate
or best_parent_hw, this will be applied to the child once the call to
clk_core_round_rate_nolock() is done. best_parent_hw is probably not
going to be a valid parent, and best_parent_rate might lead to a parent
rate change different to the one that was initially computed.
clk_mux_determine_rate_flags() and the affected drivers will copy the
request before forwarding it to the parents, so they won't be affected
by the latter issue, but the former is still going to be there and will
lead to erroneous data and context being passed to the various clock
drivers in the same sub-tree.
Let's create two new functions, clk_core_forward_rate_req() and
clk_hw_forward_rate_request() for the framework and the clock providers
that will copy a request from a child clock and update the context to
match the parent's. We also update the relevant call sites in the
framework and drivers to use that new function.
Let's also add a test to make sure we avoid regressions there.
Tested-by: Alexander Stein <alexander.stein@ew.tq-group.com> # imx8mp
Tested-by: Marek Szyprowski <m.szyprowski@samsung.com> # exynos4210, meson g12b
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Link: https://lore.kernel.org/r/20220816112530.1837489-22-maxime@cerno.tech
Tested-by: Linux Kernel Functional Testing <lkft@linaro.org>
Tested-by: Naresh Kamboju <naresh.kamboju@linaro.org>
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
2022-08-16 19:25:26 +08:00
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
2023-09-13 01:55:30 +08:00
|
|
|
* Test suite for a clock whose parent is a pass-through clk whose parent is a
|
|
|
|
* mux with multiple parents. The leaf and pass-through clocks have the
|
|
|
|
* CLK_SET_RATE_PARENT flag, and will forward rate requests to the mux, which
|
|
|
|
* will then select which parent is the best fit for a given rate.
|
clk: Stop forwarding clk_rate_requests to the parent
If the clock cannot modify its rate and has CLK_SET_RATE_PARENT,
clk_mux_determine_rate_flags(), clk_core_round_rate_nolock() and a
number of drivers will forward the clk_rate_request to the parent clock.
clk_core_round_rate_nolock() will pass the pointer directly, which means
that we pass a clk_rate_request to the parent that has the rate,
min_rate and max_rate of the child, and the best_parent_rate and
best_parent_hw fields will be relative to the child as well, so will
point to our current clock and its rate. The most common case for
CLK_SET_RATE_PARENT is that the child and parent clock rates will be
equal, so the rate field isn't a worry, but the other fields are.
Similarly, if the parent clock driver ever modifies the best_parent_rate
or best_parent_hw, this will be applied to the child once the call to
clk_core_round_rate_nolock() is done. best_parent_hw is probably not
going to be a valid parent, and best_parent_rate might lead to a parent
rate change different to the one that was initially computed.
clk_mux_determine_rate_flags() and the affected drivers will copy the
request before forwarding it to the parents, so they won't be affected
by the latter issue, but the former is still going to be there and will
lead to erroneous data and context being passed to the various clock
drivers in the same sub-tree.
Let's create two new functions, clk_core_forward_rate_req() and
clk_hw_forward_rate_request() for the framework and the clock providers
that will copy a request from a child clock and update the context to
match the parent's. We also update the relevant call sites in the
framework and drivers to use that new function.
Let's also add a test to make sure we avoid regressions there.
Tested-by: Alexander Stein <alexander.stein@ew.tq-group.com> # imx8mp
Tested-by: Marek Szyprowski <m.szyprowski@samsung.com> # exynos4210, meson g12b
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Link: https://lore.kernel.org/r/20220816112530.1837489-22-maxime@cerno.tech
Tested-by: Linux Kernel Functional Testing <lkft@linaro.org>
Tested-by: Naresh Kamboju <naresh.kamboju@linaro.org>
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
2022-08-16 19:25:26 +08:00
|
|
|
*
|
|
|
|
* These tests exercise the behaviour of muxes, and the proper selection
|
|
|
|
* of parents.
|
|
|
|
*/
|
|
|
|
static struct kunit_suite clk_leaf_mux_set_rate_parent_test_suite = {
|
|
|
|
.name = "clk-leaf-mux-set-rate-parent",
|
|
|
|
.init = clk_leaf_mux_set_rate_parent_test_init,
|
|
|
|
.exit = clk_leaf_mux_set_rate_parent_test_exit,
|
|
|
|
.test_cases = clk_leaf_mux_set_rate_parent_test_cases,
|
|
|
|
};
|
|
|
|
|
2022-10-10 22:47:39 +08:00
|
|
|
struct clk_mux_notifier_rate_change {
|
|
|
|
bool done;
|
|
|
|
unsigned long old_rate;
|
|
|
|
unsigned long new_rate;
|
|
|
|
wait_queue_head_t wq;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct clk_mux_notifier_ctx {
|
|
|
|
struct clk_multiple_parent_ctx mux_ctx;
|
|
|
|
struct clk *clk;
|
|
|
|
struct notifier_block clk_nb;
|
|
|
|
struct clk_mux_notifier_rate_change pre_rate_change;
|
|
|
|
struct clk_mux_notifier_rate_change post_rate_change;
|
|
|
|
};
|
|
|
|
|
|
|
|
#define NOTIFIER_TIMEOUT_MS 100
|
|
|
|
|
|
|
|
static int clk_mux_notifier_callback(struct notifier_block *nb,
|
|
|
|
unsigned long action, void *data)
|
|
|
|
{
|
|
|
|
struct clk_notifier_data *clk_data = data;
|
|
|
|
struct clk_mux_notifier_ctx *ctx = container_of(nb,
|
|
|
|
struct clk_mux_notifier_ctx,
|
|
|
|
clk_nb);
|
|
|
|
|
|
|
|
if (action & PRE_RATE_CHANGE) {
|
|
|
|
ctx->pre_rate_change.old_rate = clk_data->old_rate;
|
|
|
|
ctx->pre_rate_change.new_rate = clk_data->new_rate;
|
|
|
|
ctx->pre_rate_change.done = true;
|
|
|
|
wake_up_interruptible(&ctx->pre_rate_change.wq);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (action & POST_RATE_CHANGE) {
|
|
|
|
ctx->post_rate_change.old_rate = clk_data->old_rate;
|
|
|
|
ctx->post_rate_change.new_rate = clk_data->new_rate;
|
|
|
|
ctx->post_rate_change.done = true;
|
|
|
|
wake_up_interruptible(&ctx->post_rate_change.wq);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int clk_mux_notifier_test_init(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_mux_notifier_ctx *ctx;
|
|
|
|
const char *top_parents[2] = { "parent-0", "parent-1" };
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
|
|
|
|
if (!ctx)
|
|
|
|
return -ENOMEM;
|
|
|
|
test->priv = ctx;
|
|
|
|
ctx->clk_nb.notifier_call = clk_mux_notifier_callback;
|
|
|
|
init_waitqueue_head(&ctx->pre_rate_change.wq);
|
|
|
|
init_waitqueue_head(&ctx->post_rate_change.wq);
|
|
|
|
|
|
|
|
ctx->mux_ctx.parents_ctx[0].hw.init = CLK_HW_INIT_NO_PARENT("parent-0",
|
|
|
|
&clk_dummy_rate_ops,
|
|
|
|
0);
|
|
|
|
ctx->mux_ctx.parents_ctx[0].rate = DUMMY_CLOCK_RATE_1;
|
|
|
|
ret = clk_hw_register(NULL, &ctx->mux_ctx.parents_ctx[0].hw);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ctx->mux_ctx.parents_ctx[1].hw.init = CLK_HW_INIT_NO_PARENT("parent-1",
|
|
|
|
&clk_dummy_rate_ops,
|
|
|
|
0);
|
|
|
|
ctx->mux_ctx.parents_ctx[1].rate = DUMMY_CLOCK_RATE_2;
|
|
|
|
ret = clk_hw_register(NULL, &ctx->mux_ctx.parents_ctx[1].hw);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ctx->mux_ctx.current_parent = 0;
|
|
|
|
ctx->mux_ctx.hw.init = CLK_HW_INIT_PARENTS("test-mux", top_parents,
|
|
|
|
&clk_multiple_parents_mux_ops,
|
|
|
|
0);
|
|
|
|
ret = clk_hw_register(NULL, &ctx->mux_ctx.hw);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ctx->clk = clk_hw_get_clk(&ctx->mux_ctx.hw, NULL);
|
|
|
|
ret = clk_notifier_register(ctx->clk, &ctx->clk_nb);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void clk_mux_notifier_test_exit(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_mux_notifier_ctx *ctx = test->priv;
|
|
|
|
struct clk *clk = ctx->clk;
|
|
|
|
|
|
|
|
clk_notifier_unregister(clk, &ctx->clk_nb);
|
|
|
|
clk_put(clk);
|
|
|
|
|
|
|
|
clk_hw_unregister(&ctx->mux_ctx.hw);
|
|
|
|
clk_hw_unregister(&ctx->mux_ctx.parents_ctx[0].hw);
|
|
|
|
clk_hw_unregister(&ctx->mux_ctx.parents_ctx[1].hw);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Test that if the we have a notifier registered on a mux, the core
|
|
|
|
* will notify us when we switch to another parent, and with the proper
|
|
|
|
* old and new rates.
|
|
|
|
*/
|
|
|
|
static void clk_mux_notifier_set_parent_test(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_mux_notifier_ctx *ctx = test->priv;
|
|
|
|
struct clk_hw *hw = &ctx->mux_ctx.hw;
|
|
|
|
struct clk *clk = clk_hw_get_clk(hw, NULL);
|
|
|
|
struct clk *new_parent = clk_hw_get_clk(&ctx->mux_ctx.parents_ctx[1].hw, NULL);
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = clk_set_parent(clk, new_parent);
|
|
|
|
KUNIT_ASSERT_EQ(test, ret, 0);
|
|
|
|
|
|
|
|
ret = wait_event_interruptible_timeout(ctx->pre_rate_change.wq,
|
|
|
|
ctx->pre_rate_change.done,
|
|
|
|
msecs_to_jiffies(NOTIFIER_TIMEOUT_MS));
|
|
|
|
KUNIT_ASSERT_GT(test, ret, 0);
|
|
|
|
|
|
|
|
KUNIT_EXPECT_EQ(test, ctx->pre_rate_change.old_rate, DUMMY_CLOCK_RATE_1);
|
|
|
|
KUNIT_EXPECT_EQ(test, ctx->pre_rate_change.new_rate, DUMMY_CLOCK_RATE_2);
|
|
|
|
|
|
|
|
ret = wait_event_interruptible_timeout(ctx->post_rate_change.wq,
|
|
|
|
ctx->post_rate_change.done,
|
|
|
|
msecs_to_jiffies(NOTIFIER_TIMEOUT_MS));
|
|
|
|
KUNIT_ASSERT_GT(test, ret, 0);
|
|
|
|
|
|
|
|
KUNIT_EXPECT_EQ(test, ctx->post_rate_change.old_rate, DUMMY_CLOCK_RATE_1);
|
|
|
|
KUNIT_EXPECT_EQ(test, ctx->post_rate_change.new_rate, DUMMY_CLOCK_RATE_2);
|
|
|
|
|
|
|
|
clk_put(new_parent);
|
|
|
|
clk_put(clk);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct kunit_case clk_mux_notifier_test_cases[] = {
|
|
|
|
KUNIT_CASE(clk_mux_notifier_set_parent_test),
|
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Test suite for a mux with multiple parents, and a notifier registered
|
|
|
|
* on the mux.
|
|
|
|
*
|
|
|
|
* These tests exercise the behaviour of notifiers.
|
|
|
|
*/
|
|
|
|
static struct kunit_suite clk_mux_notifier_test_suite = {
|
|
|
|
.name = "clk-mux-notifier",
|
|
|
|
.init = clk_mux_notifier_test_init,
|
|
|
|
.exit = clk_mux_notifier_test_exit,
|
|
|
|
.test_cases = clk_mux_notifier_test_cases,
|
|
|
|
};
|
|
|
|
|
2023-05-05 19:25:06 +08:00
|
|
|
static int
|
|
|
|
clk_mux_no_reparent_test_init(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_multiple_parent_ctx *ctx;
|
|
|
|
const char *parents[2] = { "parent-0", "parent-1"};
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
|
|
|
|
if (!ctx)
|
|
|
|
return -ENOMEM;
|
|
|
|
test->priv = ctx;
|
|
|
|
|
|
|
|
ctx->parents_ctx[0].hw.init = CLK_HW_INIT_NO_PARENT("parent-0",
|
|
|
|
&clk_dummy_rate_ops,
|
|
|
|
0);
|
|
|
|
ctx->parents_ctx[0].rate = DUMMY_CLOCK_RATE_1;
|
|
|
|
ret = clk_hw_register(NULL, &ctx->parents_ctx[0].hw);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ctx->parents_ctx[1].hw.init = CLK_HW_INIT_NO_PARENT("parent-1",
|
|
|
|
&clk_dummy_rate_ops,
|
|
|
|
0);
|
|
|
|
ctx->parents_ctx[1].rate = DUMMY_CLOCK_RATE_2;
|
|
|
|
ret = clk_hw_register(NULL, &ctx->parents_ctx[1].hw);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ctx->current_parent = 0;
|
|
|
|
ctx->hw.init = CLK_HW_INIT_PARENTS("test-mux", parents,
|
|
|
|
&clk_multiple_parents_no_reparent_mux_ops,
|
|
|
|
0);
|
|
|
|
ret = clk_hw_register(NULL, &ctx->hw);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
clk_mux_no_reparent_test_exit(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_multiple_parent_ctx *ctx = test->priv;
|
|
|
|
|
|
|
|
clk_hw_unregister(&ctx->hw);
|
|
|
|
clk_hw_unregister(&ctx->parents_ctx[0].hw);
|
|
|
|
clk_hw_unregister(&ctx->parents_ctx[1].hw);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Test that if the we have a mux that cannot change parent and we call
|
|
|
|
* clk_round_rate() on it with a rate that should cause it to change
|
|
|
|
* parent, it won't.
|
|
|
|
*/
|
|
|
|
static void clk_mux_no_reparent_round_rate(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_multiple_parent_ctx *ctx = test->priv;
|
|
|
|
struct clk_hw *hw = &ctx->hw;
|
|
|
|
struct clk *clk = clk_hw_get_clk(hw, NULL);
|
|
|
|
struct clk *other_parent, *parent;
|
|
|
|
unsigned long other_parent_rate;
|
|
|
|
unsigned long parent_rate;
|
|
|
|
long rounded_rate;
|
|
|
|
|
|
|
|
parent = clk_get_parent(clk);
|
|
|
|
KUNIT_ASSERT_PTR_NE(test, parent, NULL);
|
|
|
|
|
|
|
|
parent_rate = clk_get_rate(parent);
|
|
|
|
KUNIT_ASSERT_GT(test, parent_rate, 0);
|
|
|
|
|
|
|
|
other_parent = clk_hw_get_clk(&ctx->parents_ctx[1].hw, NULL);
|
|
|
|
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, other_parent);
|
|
|
|
KUNIT_ASSERT_FALSE(test, clk_is_match(parent, other_parent));
|
|
|
|
|
|
|
|
other_parent_rate = clk_get_rate(other_parent);
|
|
|
|
KUNIT_ASSERT_GT(test, other_parent_rate, 0);
|
|
|
|
clk_put(other_parent);
|
|
|
|
|
|
|
|
rounded_rate = clk_round_rate(clk, other_parent_rate);
|
|
|
|
KUNIT_ASSERT_GT(test, rounded_rate, 0);
|
|
|
|
KUNIT_EXPECT_EQ(test, rounded_rate, parent_rate);
|
|
|
|
|
|
|
|
clk_put(clk);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Test that if the we have a mux that cannot change parent and we call
|
|
|
|
* clk_set_rate() on it with a rate that should cause it to change
|
|
|
|
* parent, it won't.
|
|
|
|
*/
|
|
|
|
static void clk_mux_no_reparent_set_rate(struct kunit *test)
|
|
|
|
{
|
|
|
|
struct clk_multiple_parent_ctx *ctx = test->priv;
|
|
|
|
struct clk_hw *hw = &ctx->hw;
|
|
|
|
struct clk *clk = clk_hw_get_clk(hw, NULL);
|
|
|
|
struct clk *other_parent, *parent;
|
|
|
|
unsigned long other_parent_rate;
|
|
|
|
unsigned long parent_rate;
|
|
|
|
unsigned long rate;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
parent = clk_get_parent(clk);
|
|
|
|
KUNIT_ASSERT_PTR_NE(test, parent, NULL);
|
|
|
|
|
|
|
|
parent_rate = clk_get_rate(parent);
|
|
|
|
KUNIT_ASSERT_GT(test, parent_rate, 0);
|
|
|
|
|
|
|
|
other_parent = clk_hw_get_clk(&ctx->parents_ctx[1].hw, NULL);
|
|
|
|
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, other_parent);
|
|
|
|
KUNIT_ASSERT_FALSE(test, clk_is_match(parent, other_parent));
|
|
|
|
|
|
|
|
other_parent_rate = clk_get_rate(other_parent);
|
|
|
|
KUNIT_ASSERT_GT(test, other_parent_rate, 0);
|
|
|
|
clk_put(other_parent);
|
|
|
|
|
|
|
|
ret = clk_set_rate(clk, other_parent_rate);
|
|
|
|
KUNIT_ASSERT_EQ(test, ret, 0);
|
|
|
|
|
|
|
|
rate = clk_get_rate(clk);
|
|
|
|
KUNIT_ASSERT_GT(test, rate, 0);
|
|
|
|
KUNIT_EXPECT_EQ(test, rate, parent_rate);
|
|
|
|
|
|
|
|
clk_put(clk);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct kunit_case clk_mux_no_reparent_test_cases[] = {
|
|
|
|
KUNIT_CASE(clk_mux_no_reparent_round_rate),
|
|
|
|
KUNIT_CASE(clk_mux_no_reparent_set_rate),
|
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Test suite for a clock mux that isn't allowed to change parent, using
|
|
|
|
* the clk_hw_determine_rate_no_reparent() helper.
|
|
|
|
*
|
|
|
|
* These tests exercise that helper, and the proper selection of
|
|
|
|
* rates and parents.
|
|
|
|
*/
|
|
|
|
static struct kunit_suite clk_mux_no_reparent_test_suite = {
|
|
|
|
.name = "clk-mux-no-reparent",
|
|
|
|
.init = clk_mux_no_reparent_test_init,
|
|
|
|
.exit = clk_mux_no_reparent_test_exit,
|
|
|
|
.test_cases = clk_mux_no_reparent_test_cases,
|
|
|
|
};
|
|
|
|
|
2022-02-25 22:35:24 +08:00
|
|
|
kunit_test_suites(
|
clk: Stop forwarding clk_rate_requests to the parent
If the clock cannot modify its rate and has CLK_SET_RATE_PARENT,
clk_mux_determine_rate_flags(), clk_core_round_rate_nolock() and a
number of drivers will forward the clk_rate_request to the parent clock.
clk_core_round_rate_nolock() will pass the pointer directly, which means
that we pass a clk_rate_request to the parent that has the rate,
min_rate and max_rate of the child, and the best_parent_rate and
best_parent_hw fields will be relative to the child as well, so will
point to our current clock and its rate. The most common case for
CLK_SET_RATE_PARENT is that the child and parent clock rates will be
equal, so the rate field isn't a worry, but the other fields are.
Similarly, if the parent clock driver ever modifies the best_parent_rate
or best_parent_hw, this will be applied to the child once the call to
clk_core_round_rate_nolock() is done. best_parent_hw is probably not
going to be a valid parent, and best_parent_rate might lead to a parent
rate change different to the one that was initially computed.
clk_mux_determine_rate_flags() and the affected drivers will copy the
request before forwarding it to the parents, so they won't be affected
by the latter issue, but the former is still going to be there and will
lead to erroneous data and context being passed to the various clock
drivers in the same sub-tree.
Let's create two new functions, clk_core_forward_rate_req() and
clk_hw_forward_rate_request() for the framework and the clock providers
that will copy a request from a child clock and update the context to
match the parent's. We also update the relevant call sites in the
framework and drivers to use that new function.
Let's also add a test to make sure we avoid regressions there.
Tested-by: Alexander Stein <alexander.stein@ew.tq-group.com> # imx8mp
Tested-by: Marek Szyprowski <m.szyprowski@samsung.com> # exynos4210, meson g12b
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Link: https://lore.kernel.org/r/20220816112530.1837489-22-maxime@cerno.tech
Tested-by: Linux Kernel Functional Testing <lkft@linaro.org>
Tested-by: Naresh Kamboju <naresh.kamboju@linaro.org>
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
2022-08-16 19:25:26 +08:00
|
|
|
&clk_leaf_mux_set_rate_parent_test_suite,
|
2022-02-25 22:35:24 +08:00
|
|
|
&clk_test_suite,
|
2022-08-16 19:25:15 +08:00
|
|
|
&clk_multiple_parents_mux_test_suite,
|
2023-05-05 19:25:06 +08:00
|
|
|
&clk_mux_no_reparent_test_suite,
|
2022-10-10 22:47:39 +08:00
|
|
|
&clk_mux_notifier_test_suite,
|
2022-08-16 19:25:16 +08:00
|
|
|
&clk_orphan_transparent_multiple_parent_mux_test_suite,
|
2022-03-26 00:11:43 +08:00
|
|
|
&clk_orphan_transparent_single_parent_test_suite,
|
clk: Set req_rate on reparenting
If a non-rate clock started by default with a parent that never
registered, core->req_rate will be 0. The expectation is that whenever
the parent will be registered, req_rate will be updated with the new
value that has just been computed.
However, if that clock is a mux, clk_set_parent() can also make that
clock no longer orphan. In this case however, we never update req_rate.
The natural solution to this would be to update core->rate and
core->req_rate in clk_reparent() by calling clk_recalc().
However, this doesn't work in all cases. Indeed, clk_recalc() is called
by __clk_set_parent_before(), __clk_set_parent() and
clk_core_reparent(). Both __clk_set_parent_before() and __clk_set_parent
will call clk_recalc() with the enable_lock taken through a call to
clk_enable_lock(), the underlying locking primitive being a spinlock.
clk_recalc() calls the backing driver .recalc_rate hook, and that
implementation might sleep if the underlying device uses a bus with
accesses that might sleep, such as i2c.
In such a situation, we would end up sleeping while holding a spinlock,
and thus in an atomic section.
In order to work around this, we can move the core->rate and
core->req_rate update to the clk_recalc() calling sites, after the
enable_lock has been released if it was taken.
The only situation that could still be problematic is the
clk_core_reparent() -> clk_reparent() case that doesn't have any
locking. clk_core_reparent() is itself called by clk_hw_reparent(),
which is then called by 4 drivers:
* clk-stm32mp1.c, stm32/clk-stm32-core.c and tegra/clk-tegra210-emc.c
use it in their set_parent implementation. The set_parent hook is
only called by __clk_set_parent() and clk_change_rate(), both of
them calling it without the enable_lock taken.
* clk/tegra/clk-tegra124-emc.c calls it as part of its set_rate
implementation. set_rate is only called by clk_change_rate(), again
without the enable_lock taken.
In both cases we can't end up in a situation where the clk_hw_reparent()
caller would hold a spinlock, so it seems like this is a good
workaround.
Let's also add some unit tests to make sure we cover the original bug.
Tested-by: Alexander Stein <alexander.stein@ew.tq-group.com> # imx8mp
Tested-by: Marek Szyprowski <m.szyprowski@samsung.com> # exynos4210, meson g12b
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Link: https://lore.kernel.org/r/20220816112530.1837489-14-maxime@cerno.tech
Tested-by: Linux Kernel Functional Testing <lkft@linaro.org>
Tested-by: Naresh Kamboju <naresh.kamboju@linaro.org>
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
2022-08-16 19:25:18 +08:00
|
|
|
&clk_orphan_two_level_root_last_test_suite,
|
2022-02-25 22:35:24 +08:00
|
|
|
&clk_range_test_suite,
|
|
|
|
&clk_range_maximize_test_suite,
|
2022-08-16 19:25:13 +08:00
|
|
|
&clk_range_minimize_test_suite,
|
2022-08-16 19:25:14 +08:00
|
|
|
&clk_single_parent_mux_test_suite,
|
2022-08-16 19:25:13 +08:00
|
|
|
&clk_uncached_test_suite
|
2022-02-25 22:35:24 +08:00
|
|
|
);
|
|
|
|
MODULE_LICENSE("GPL v2");
|