Linux clock framework development
 help / color / mirror / Atom feed
* [PATCH 0/3] clk: Some Clock Range Fixes
@ 2022-03-25 10:58 Maxime Ripard
  2022-03-25 10:58 ` [PATCH 1/3] clk: Initialize orphan req_rate Maxime Ripard
                   ` (2 more replies)
  0 siblings, 3 replies; 5+ messages in thread
From: Maxime Ripard @ 2022-03-25 10:58 UTC (permalink / raw)
  To: linux-clk, Mike Turquette, Stephen Boyd; +Cc: Dmitry Osipenko, Maxime Ripard

Hi,

This fixes one regression for Tegra30 reported by Dmitry, and another issue I
came across while looking at it.

We're also adding a bunch of unit tests to cover those cases.

Let me know what you think,
Maxime

Maxime Ripard (3):
  clk: Initialize orphan req_rate
  clk: test: Test clk_set_rate_range on orphan mux
  clk: Drop the rate range on clk_put

 drivers/clk/clk.c      |  47 +++++++--
 drivers/clk/clk_test.c | 213 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 254 insertions(+), 6 deletions(-)

-- 
2.35.1


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 1/3] clk: Initialize orphan req_rate
  2022-03-25 10:58 [PATCH 0/3] clk: Some Clock Range Fixes Maxime Ripard
@ 2022-03-25 10:58 ` Maxime Ripard
  2022-03-25 10:58 ` [PATCH 2/3] clk: test: Test clk_set_rate_range on orphan mux Maxime Ripard
  2022-03-25 10:58 ` [PATCH 3/3] clk: Drop the rate range on clk_put Maxime Ripard
  2 siblings, 0 replies; 5+ messages in thread
From: Maxime Ripard @ 2022-03-25 10:58 UTC (permalink / raw)
  To: linux-clk, Mike Turquette, Stephen Boyd; +Cc: Dmitry Osipenko, Maxime Ripard

When registering a clock that doesn't have a recalc_rate implementation,
and doesn't have its parent registered yet, we initialize the clk_core
rate and req_rate fields to 0.

The rate field is later updated when the parent is registered in
clk_core_reparent_orphans_nolock() using __clk_recalc_rates(), but the
req_rate field is never updated.

This leads to an issue in clk_set_rate_range() and clk_put(), since
those functions will call clk_set_rate with the content of req_rate to
provide drivers with the opportunity to change the rate based on the new
boundaries. In this case, we would call clk_set_rate() with a rate of 0,
effectively enforcing the minimum allowed for this clock whenever we
would call one of those two functions, even though the actual rate might
be within range.

Let's fix this by setting req_rate in clk_core_reparent_orphans_nolock()
with the rate field content just updated by the call to
__clk_recalc_rates().

Fixes: 1c8e600440c7 ("clk: Add rate constraints to clocks")
Reported-by: Dmitry Osipenko <dmitry.osipenko@collabora.com>
Tested-by: Dmitry Osipenko <dmitry.osipenko@collabora.com> # T30 Nexus7
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/clk/clk.c | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 9bc8bf434b94..915a2fa363b1 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -3479,6 +3479,19 @@ static void clk_core_reparent_orphans_nolock(void)
 			__clk_set_parent_after(orphan, parent, NULL);
 			__clk_recalc_accuracies(orphan);
 			__clk_recalc_rates(orphan, 0);
+
+			/*
+			 * If the clock doesn't have .recalc_rate (so
+			 * wouldn't affect the parent rate) and is
+			 * orphan when it's registered, the
+			 * __clk_init_parent will set the initial
+			 * req_rate to 0.
+			 *
+			 * req_rate is then used by clk_set_rate_range
+			 * and clk_put to trigger a clk_set_rate call
+			 * whenever the boundaries are modified.
+			 */
+			orphan->req_rate = orphan->rate;
 		}
 	}
 }
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 2/3] clk: test: Test clk_set_rate_range on orphan mux
  2022-03-25 10:58 [PATCH 0/3] clk: Some Clock Range Fixes Maxime Ripard
  2022-03-25 10:58 ` [PATCH 1/3] clk: Initialize orphan req_rate Maxime Ripard
@ 2022-03-25 10:58 ` Maxime Ripard
  2022-03-25 10:58 ` [PATCH 3/3] clk: Drop the rate range on clk_put Maxime Ripard
  2 siblings, 0 replies; 5+ messages in thread
From: Maxime Ripard @ 2022-03-25 10:58 UTC (permalink / raw)
  To: linux-clk, Mike Turquette, Stephen Boyd; +Cc: Dmitry Osipenko, Maxime Ripard

A bug recently affected the Tegra30 where calling clk_set_rate_range()
on a clock would make it change its rate to the minimum.

This was due to the clock in question being a mux that was orphan at
registration, which lead to the clk_core req_rate being 0, and the
clk_set_rate_range() function then calling clk_set_rate() with req_rate,
effectively making that clock running at the minimum rate allowed, even
though the initial rate was within that range.

Make a test suite to create a mux initially orphan, and then make sure
that if our clock rate was initially within a given range, then
enforcing that range won't affect it.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/clk/clk_test.c | 105 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 105 insertions(+)

diff --git a/drivers/clk/clk_test.c b/drivers/clk/clk_test.c
index a92600311506..146b1759798e 100644
--- a/drivers/clk/clk_test.c
+++ b/drivers/clk/clk_test.c
@@ -72,6 +72,19 @@ static int clk_dummy_set_rate(struct clk_hw *hw,
 	return 0;
 }
 
+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;
+}
+
 static const struct clk_ops clk_dummy_rate_ops = {
 	.recalc_rate = clk_dummy_recalc_rate,
 	.determine_rate = clk_dummy_determine_rate,
@@ -90,6 +103,11 @@ static const struct clk_ops clk_dummy_minimize_rate_ops = {
 	.set_rate = clk_dummy_set_rate,
 };
 
+static const struct clk_ops clk_dummy_single_parent_ops = {
+	.set_parent = clk_dummy_single_set_parent,
+	.get_parent = clk_dummy_single_get_parent,
+};
+
 static int clk_test_init_with_ops(struct kunit *test, const struct clk_ops *ops)
 {
 	struct clk_dummy_context *ctx;
@@ -239,6 +257,92 @@ static struct kunit_suite clk_test_suite = {
 	.test_cases = clk_test_cases,
 };
 
+struct clk_single_parent_ctx {
+	struct clk_dummy_context parent_ctx;
+	struct clk_hw hw;
+};
+
+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 *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;
+}
+
+static void clk_orphan_transparent_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 a mux-only clock, with an initial rate within a range,
+ * will still have the same rate after the range has been enforced.
+ */
+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;
+	struct clk *clk = hw->clk;
+	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);
+}
+
+static struct kunit_case clk_orphan_transparent_single_parent_mux_test_cases[] = {
+	KUNIT_CASE(clk_test_orphan_transparent_parent_mux_set_range),
+	{}
+};
+
+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,
+	.exit = clk_orphan_transparent_single_parent_mux_test_exit,
+	.test_cases = clk_orphan_transparent_single_parent_mux_test_cases,
+};
+
 /*
  * 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
@@ -788,6 +892,7 @@ static struct kunit_suite clk_range_minimize_test_suite = {
 
 kunit_test_suites(
 	&clk_test_suite,
+	&clk_orphan_transparent_single_parent_test_suite,
 	&clk_range_test_suite,
 	&clk_range_maximize_test_suite,
 	&clk_range_minimize_test_suite
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 3/3] clk: Drop the rate range on clk_put
  2022-03-25 10:58 [PATCH 0/3] clk: Some Clock Range Fixes Maxime Ripard
  2022-03-25 10:58 ` [PATCH 1/3] clk: Initialize orphan req_rate Maxime Ripard
  2022-03-25 10:58 ` [PATCH 2/3] clk: test: Test clk_set_rate_range on orphan mux Maxime Ripard
@ 2022-03-25 10:58 ` Maxime Ripard
  2022-03-25 15:13   ` kernel test robot
  2 siblings, 1 reply; 5+ messages in thread
From: Maxime Ripard @ 2022-03-25 10:58 UTC (permalink / raw)
  To: linux-clk, Mike Turquette, Stephen Boyd; +Cc: Dmitry Osipenko, Maxime Ripard

While the current code will trigger a new clk_set_rate call whenever the
rate boundaries are changed through clk_set_rate_range, this doesn't
occur when clk_put() is called.

However, this is essentially equivalent since, after clk_put()
completes, those boundaries won't be enforced anymore.

Let's add a call to clk_set_rate_range in clk_put to make sure those
rate boundaries are dropped and the clock drivers can react.

Let's also add a few tests to make sure this case is covered.

Fixes: c80ac50cbb37 ("clk: Always set the rate on clk_set_range_rate")
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/clk/clk.c      |  34 ++++++++++---
 drivers/clk/clk_test.c | 108 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 136 insertions(+), 6 deletions(-)

diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 915a2fa363b1..f21901b83a9b 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -2340,11 +2340,15 @@ EXPORT_SYMBOL_GPL(clk_set_rate_exclusive);
  *
  * Returns success (0) or negative errno.
  */
-int clk_set_rate_range(struct clk *clk, unsigned long min, unsigned long max)
+static int clk_set_rate_range_nolock(struct clk *clk,
+				     unsigned long min,
+				     unsigned long max)
 {
 	int ret = 0;
 	unsigned long old_min, old_max, rate;
 
+	lockdep_assert_held(&prepare_lock);
+
 	if (!clk)
 		return 0;
 
@@ -2357,8 +2361,6 @@ int clk_set_rate_range(struct clk *clk, unsigned long min, unsigned long max)
 		return -EINVAL;
 	}
 
-	clk_prepare_lock();
-
 	if (clk->exclusive_count)
 		clk_core_rate_unprotect(clk->core);
 
@@ -2402,6 +2404,28 @@ int clk_set_rate_range(struct clk *clk, unsigned long min, unsigned long max)
 	if (clk->exclusive_count)
 		clk_core_rate_protect(clk->core);
 
+	return ret;
+}
+
+/**
+ * clk_set_rate_range - set a rate range for a clock source
+ * @clk: clock source
+ * @min: desired minimum clock rate in Hz, inclusive
+ * @max: desired maximum clock rate in Hz, inclusive
+ *
+ * Returns success (0) or negative errno.
+ */
+int clk_set_rate_range(struct clk *clk, unsigned long min, unsigned long max)
+{
+	int ret;
+
+	if (!clk)
+		return 0;
+
+	clk_prepare_lock();
+
+	ret = clk_set_rate_range_nolock(clk, min, max);
+
 	clk_prepare_unlock();
 
 	return ret;
@@ -4403,9 +4427,7 @@ void __clk_put(struct clk *clk)
 	}
 
 	hlist_del(&clk->clks_node);
-	if (clk->min_rate > clk->core->req_rate ||
-	    clk->max_rate < clk->core->req_rate)
-		clk_core_set_rate_nolock(clk->core, clk->core->req_rate);
+	clk_set_rate_range_nolock(clk, 0, ULONG_MAX);
 
 	owner = clk->core->owner;
 	kref_put(&clk->core->ref, __clk_release);
diff --git a/drivers/clk/clk_test.c b/drivers/clk/clk_test.c
index 146b1759798e..b205c329cf32 100644
--- a/drivers/clk/clk_test.c
+++ b/drivers/clk/clk_test.c
@@ -760,9 +760,65 @@ static void clk_range_test_multiple_set_range_rate_maximized(struct kunit *test)
 	clk_put(user1);
 }
 
+/*
+ * 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 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 = hw->clk;
+	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);
+}
+
 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),
+	KUNIT_CASE(clk_range_test_multiple_set_range_rate_put_maximized),
 	{}
 };
 
@@ -877,9 +933,61 @@ static void clk_range_test_multiple_set_range_rate_minimized(struct kunit *test)
 	clk_put(user1);
 }
 
+/*
+ * 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 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 = hw->clk;
+	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);
+}
+
 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),
+	KUNIT_CASE(clk_range_test_multiple_set_range_rate_put_minimized),
 	{}
 };
 
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH 3/3] clk: Drop the rate range on clk_put
  2022-03-25 10:58 ` [PATCH 3/3] clk: Drop the rate range on clk_put Maxime Ripard
@ 2022-03-25 15:13   ` kernel test robot
  0 siblings, 0 replies; 5+ messages in thread
From: kernel test robot @ 2022-03-25 15:13 UTC (permalink / raw)
  To: Maxime Ripard, linux-clk, Mike Turquette, Stephen Boyd
  Cc: kbuild-all, Dmitry Osipenko, Maxime Ripard

Hi Maxime,

I love your patch! Perhaps something to improve:

[auto build test WARNING on clk/clk-next]
[also build test WARNING on next-20220325]
[cannot apply to v5.17]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Maxime-Ripard/clk-Some-Clock-Range-Fixes/20220325-190055
base:   https://git.kernel.org/pub/scm/linux/kernel/git/clk/linux.git clk-next
config: arm-oxnas_v6_defconfig (https://download.01.org/0day-ci/archive/20220325/202203252333.iuCaJrqe-lkp@intel.com/config)
compiler: arm-linux-gnueabi-gcc (GCC) 11.2.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/0day-ci/linux/commit/b7aab8e3d4794c4df1a43452696f95b4eee510f5
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Maxime-Ripard/clk-Some-Clock-Range-Fixes/20220325-190055
        git checkout b7aab8e3d4794c4df1a43452696f95b4eee510f5
        # save the config file to linux build tree
        mkdir build_dir
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross O=build_dir ARCH=arm SHELL=/bin/bash drivers/clk/

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

>> drivers/clk/clk.c:2346: warning: expecting prototype for clk_set_rate_range(). Prototype was for clk_set_rate_range_nolock() instead


vim +2346 drivers/clk/clk.c

55e9b8b7b806ec Jerome Brunet 2017-12-01  2334  
4dff95dc9477a3 Stephen Boyd  2015-04-30  2335  /**
4dff95dc9477a3 Stephen Boyd  2015-04-30  2336   * clk_set_rate_range - set a rate range for a clock source
4dff95dc9477a3 Stephen Boyd  2015-04-30  2337   * @clk: clock source
4dff95dc9477a3 Stephen Boyd  2015-04-30  2338   * @min: desired minimum clock rate in Hz, inclusive
4dff95dc9477a3 Stephen Boyd  2015-04-30  2339   * @max: desired maximum clock rate in Hz, inclusive
4dff95dc9477a3 Stephen Boyd  2015-04-30  2340   *
4dff95dc9477a3 Stephen Boyd  2015-04-30  2341   * Returns success (0) or negative errno.
4dff95dc9477a3 Stephen Boyd  2015-04-30  2342   */
b7aab8e3d4794c Maxime Ripard 2022-03-25  2343  static int clk_set_rate_range_nolock(struct clk *clk,
b7aab8e3d4794c Maxime Ripard 2022-03-25  2344  				     unsigned long min,
b7aab8e3d4794c Maxime Ripard 2022-03-25  2345  				     unsigned long max)
a093bde2b45a0a Ulf Hansson   2012-08-31 @2346  {
4dff95dc9477a3 Stephen Boyd  2015-04-30  2347  	int ret = 0;
6562fbcf3ad5ff Jerome Brunet 2017-12-01  2348  	unsigned long old_min, old_max, rate;
4dff95dc9477a3 Stephen Boyd  2015-04-30  2349  
b7aab8e3d4794c Maxime Ripard 2022-03-25  2350  	lockdep_assert_held(&prepare_lock);
b7aab8e3d4794c Maxime Ripard 2022-03-25  2351  
4dff95dc9477a3 Stephen Boyd  2015-04-30  2352  	if (!clk)
4dff95dc9477a3 Stephen Boyd  2015-04-30  2353  		return 0;
4dff95dc9477a3 Stephen Boyd  2015-04-30  2354  
03813d9b7d4368 Maxime Ripard 2020-12-07  2355  	trace_clk_set_rate_range(clk->core, min, max);
03813d9b7d4368 Maxime Ripard 2020-12-07  2356  
4dff95dc9477a3 Stephen Boyd  2015-04-30  2357  	if (min > max) {
4dff95dc9477a3 Stephen Boyd  2015-04-30  2358  		pr_err("%s: clk %s dev %s con %s: invalid range [%lu, %lu]\n",
4dff95dc9477a3 Stephen Boyd  2015-04-30  2359  		       __func__, clk->core->name, clk->dev_id, clk->con_id,
4dff95dc9477a3 Stephen Boyd  2015-04-30  2360  		       min, max);
4dff95dc9477a3 Stephen Boyd  2015-04-30  2361  		return -EINVAL;
4dff95dc9477a3 Stephen Boyd  2015-04-30  2362  	}
a093bde2b45a0a Ulf Hansson   2012-08-31  2363  
55e9b8b7b806ec Jerome Brunet 2017-12-01  2364  	if (clk->exclusive_count)
55e9b8b7b806ec Jerome Brunet 2017-12-01  2365  		clk_core_rate_unprotect(clk->core);
55e9b8b7b806ec Jerome Brunet 2017-12-01  2366  
6562fbcf3ad5ff Jerome Brunet 2017-12-01  2367  	/* Save the current values in case we need to rollback the change */
6562fbcf3ad5ff Jerome Brunet 2017-12-01  2368  	old_min = clk->min_rate;
6562fbcf3ad5ff Jerome Brunet 2017-12-01  2369  	old_max = clk->max_rate;
4dff95dc9477a3 Stephen Boyd  2015-04-30  2370  	clk->min_rate = min;
4dff95dc9477a3 Stephen Boyd  2015-04-30  2371  	clk->max_rate = max;
6562fbcf3ad5ff Jerome Brunet 2017-12-01  2372  
10c46f2ea91420 Maxime Ripard 2022-02-25  2373  	if (!clk_core_check_boundaries(clk->core, min, max)) {
10c46f2ea91420 Maxime Ripard 2022-02-25  2374  		ret = -EINVAL;
10c46f2ea91420 Maxime Ripard 2022-02-25  2375  		goto out;
10c46f2ea91420 Maxime Ripard 2022-02-25  2376  	}
10c46f2ea91420 Maxime Ripard 2022-02-25  2377  
6562fbcf3ad5ff Jerome Brunet 2017-12-01  2378  	/*
c80ac50cbb378a Maxime Ripard 2022-02-25  2379  	 * Since the boundaries have been changed, let's give the
c80ac50cbb378a Maxime Ripard 2022-02-25  2380  	 * opportunity to the provider to adjust the clock rate based on
c80ac50cbb378a Maxime Ripard 2022-02-25  2381  	 * the new boundaries.
c80ac50cbb378a Maxime Ripard 2022-02-25  2382  	 *
c80ac50cbb378a Maxime Ripard 2022-02-25  2383  	 * We also need to handle the case where the clock is currently
c80ac50cbb378a Maxime Ripard 2022-02-25  2384  	 * outside of the boundaries. Clamping the last requested rate
c80ac50cbb378a Maxime Ripard 2022-02-25  2385  	 * to the current minimum and maximum will also handle this.
c80ac50cbb378a Maxime Ripard 2022-02-25  2386  	 *
6562fbcf3ad5ff Jerome Brunet 2017-12-01  2387  	 * FIXME:
c80ac50cbb378a Maxime Ripard 2022-02-25  2388  	 * There is a catch. It may fail for the usual reason (clock
c80ac50cbb378a Maxime Ripard 2022-02-25  2389  	 * broken, clock protected, etc) but also because:
6562fbcf3ad5ff Jerome Brunet 2017-12-01  2390  	 * - round_rate() was not favorable and fell on the wrong
6562fbcf3ad5ff Jerome Brunet 2017-12-01  2391  	 *   side of the boundary
6562fbcf3ad5ff Jerome Brunet 2017-12-01  2392  	 * - the determine_rate() callback does not really check for
6562fbcf3ad5ff Jerome Brunet 2017-12-01  2393  	 *   this corner case when determining the rate
6562fbcf3ad5ff Jerome Brunet 2017-12-01  2394  	 */
a9b269310ad9ab Maxime Ripard 2022-02-25  2395  	rate = clamp(clk->core->req_rate, min, max);
6562fbcf3ad5ff Jerome Brunet 2017-12-01  2396  	ret = clk_core_set_rate_nolock(clk->core, rate);
6562fbcf3ad5ff Jerome Brunet 2017-12-01  2397  	if (ret) {
6562fbcf3ad5ff Jerome Brunet 2017-12-01  2398  		/* rollback the changes */
6562fbcf3ad5ff Jerome Brunet 2017-12-01  2399  		clk->min_rate = old_min;
6562fbcf3ad5ff Jerome Brunet 2017-12-01  2400  		clk->max_rate = old_max;
4dff95dc9477a3 Stephen Boyd  2015-04-30  2401  	}
a093bde2b45a0a Ulf Hansson   2012-08-31  2402  
10c46f2ea91420 Maxime Ripard 2022-02-25  2403  out:
55e9b8b7b806ec Jerome Brunet 2017-12-01  2404  	if (clk->exclusive_count)
55e9b8b7b806ec Jerome Brunet 2017-12-01  2405  		clk_core_rate_protect(clk->core);
a093bde2b45a0a Ulf Hansson   2012-08-31  2406  
b7aab8e3d4794c Maxime Ripard 2022-03-25  2407  	return ret;
b7aab8e3d4794c Maxime Ripard 2022-03-25  2408  }
b7aab8e3d4794c Maxime Ripard 2022-03-25  2409  

-- 
0-DAY CI Kernel Test Service
https://01.org/lkp

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2022-03-25 15:17 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2022-03-25 10:58 [PATCH 0/3] clk: Some Clock Range Fixes Maxime Ripard
2022-03-25 10:58 ` [PATCH 1/3] clk: Initialize orphan req_rate Maxime Ripard
2022-03-25 10:58 ` [PATCH 2/3] clk: test: Test clk_set_rate_range on orphan mux Maxime Ripard
2022-03-25 10:58 ` [PATCH 3/3] clk: Drop the rate range on clk_put Maxime Ripard
2022-03-25 15:13   ` kernel test robot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox