From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 919EAC433F5 for ; Fri, 8 Apr 2022 09:14:06 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233632AbiDHJQE (ORCPT ); Fri, 8 Apr 2022 05:16:04 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:36604 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233062AbiDHJOC (ORCPT ); Fri, 8 Apr 2022 05:14:02 -0400 Received: from out3-smtp.messagingengine.com (out3-smtp.messagingengine.com [66.111.4.27]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id DE369124C0B for ; Fri, 8 Apr 2022 02:11:02 -0700 (PDT) Received: from compute4.internal (compute4.nyi.internal [10.202.2.44]) by mailout.nyi.internal (Postfix) with ESMTP id 4E4AA5C013A; Fri, 8 Apr 2022 05:11:02 -0400 (EDT) Received: from mailfrontend1 ([10.202.2.162]) by compute4.internal (MEProxy); Fri, 08 Apr 2022 05:11:02 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cerno.tech; h=cc :cc:content-transfer-encoding:date:date:from:from:in-reply-to :in-reply-to:message-id:mime-version:references:reply-to:sender :subject:subject:to:to; s=fm3; bh=eN9cAZfllC/Q1c0jFouk0LanqqLrGT 7W/Vb6xXaC/sg=; b=DNhWe1VFdp9GOgou5Yzvd+7Dh+NTPrS7zN8jruBKaLyAHq 7HQ/wZDd/wUacOKSYL/BAm4/KNhvvrYKjbJ+Z4RgQazOiyHff7bnQP/iwRhAAxXj /PckUbp9B1rzoNsTSnAU4GvQJKr4TiVtaPknRJ3c9XiwviDNfQw+yOkgeLONTOLp CxvdaHLoWXy3bqaoebAFHQdW2PBKZ0jjlKVIerBy2NUGOq4zAk0P5yBaMZ/nLNmd S8zDyLHg81SGiwMZ0UmgBQbVM6aWK7Uvwd/iMIm2La2zv41SGGxPEnjfFjJPaVLJ FFk3QTIj9MzYTwuZsqayRy6J0gq4kxz0NzQwiVuQ== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-transfer-encoding:date:date :from:from:in-reply-to:in-reply-to:message-id:mime-version :references:reply-to:sender:subject:subject:to:to:x-me-proxy :x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s=fm3; bh=eN9cAZ fllC/Q1c0jFouk0LanqqLrGT7W/Vb6xXaC/sg=; b=dWhdBeAlBRqhnOAKeF+Zk+ fY/Z0yQrIQTLaz7GnJOGpjso8x9OgLj0tIvz8GANfOAw7y/2ZS3QU+C4O/qgfm7/ q3WRuM97Lpt/utHpMqDl2wz5KYYUirsFH5lAk7IQ3uRuluCbYUh7H3lUOWQa4hWF /bNgfIxfXDRqU8Qc+5BBy1mQnxxzc/Xbb32OdP8x5ydAeHAv25hlPOvcx4wQ/s4E W0DX6Uc5/vbGQ2Ge7RPGcgYFfWTyQl/ByUFPF4Wgw83M7SvUjhwk0GPhLUSMkiL/ 66Dz9VIqNalngslPJJEITozMu5PhyPmX7qrusrMA5Zf0cTA/lqJ+mcsp/0+rCVLg == X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgedvvddrudektddgudduucetufdoteggodetrfdotf fvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfqfgfvpdfurfetoffkrfgpnffqhgen uceurghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmne cujfgurhephffvufffkffojghfggfgsedtkeertdertddtnecuhfhrohhmpeforgigihhm vgcutfhiphgrrhguuceomhgrgihimhgvsegtvghrnhhordhtvggthheqnecuggftrfgrth htvghrnhepjeetueeludeikefgheethfefgfektdfgheefleetheffuddvleegffekvdfh vdffnecuffhomhgrihhnpehkvghrnhgvlhdrohhrghenucevlhhushhtvghrufhiiigvpe dunecurfgrrhgrmhepmhgrihhlfhhrohhmpehmrgigihhmvgestggvrhhnohdrthgvtghh X-ME-Proxy: Received: by mail.messagingengine.com (Postfix) with ESMTPA; Fri, 8 Apr 2022 05:11:01 -0400 (EDT) From: Maxime Ripard To: Mike Turquette , Stephen Boyd , linux-clk@vger.kernel.org Cc: Naresh Kamboju , Alexander Stein , Marek Szyprowski , Tony Lindgren , Jerome Brunet , Yassine Oudjana , Neil Armstrong , Maxime Ripard Subject: [PATCH 10/22] clk: Set req_rate on reparenting Date: Fri, 8 Apr 2022 11:10:25 +0200 Message-Id: <20220408091037.2041955-11-maxime@cerno.tech> X-Mailer: git-send-email 2.35.1 In-Reply-To: <20220408091037.2041955-1-maxime@cerno.tech> References: <20220408091037.2041955-1-maxime@cerno.tech> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Precedence: bulk List-ID: X-Mailing-List: linux-clk@vger.kernel.org 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. Let's make sure it's the case for the newly unorphan clock and all its children. Signed-off-by: Maxime Ripard --- drivers/clk/clk.c | 18 ++++ drivers/clk/clk_test.c | 231 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 249 insertions(+) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 80eebf62d1f3..5b6ec11fcfcd 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -1759,6 +1759,23 @@ static void clk_core_update_orphan_status(struct clk_core *core, bool is_orphan) clk_core_update_orphan_status(child, is_orphan); } +/* + * Update the orphan rate and req_rate of @core and all its children. + */ +static void clk_core_update_orphan_child_rates(struct clk_core *core) +{ + struct clk_core *child; + unsigned long parent_rate = 0; + + if (core->parent) + parent_rate = core->parent->rate; + + core->rate = core->req_rate = clk_recalc(core, parent_rate); + + hlist_for_each_entry(child, &core->children, child_node) + clk_core_update_orphan_child_rates(child); +} + static void clk_reparent(struct clk_core *core, struct clk_core *new_parent) { bool was_orphan = core->orphan; @@ -1783,6 +1800,7 @@ static void clk_reparent(struct clk_core *core, struct clk_core *new_parent) } core->parent = new_parent; + clk_core_update_orphan_child_rates(core); } static struct clk_core *__clk_set_parent_before(struct clk_core *core, diff --git a/drivers/clk/clk_test.c b/drivers/clk/clk_test.c index 0f2d52d3a25d..fa6e32c460ac 100644 --- a/drivers/clk/clk_test.c +++ b/drivers/clk/clk_test.c @@ -575,6 +575,39 @@ clk_test_orphan_transparent_multiple_parent_mux_set_parent(struct kunit *test) clk_put(parent); } +/* + * 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 = hw->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); +} + /* * 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. @@ -603,6 +636,39 @@ clk_test_orphan_transparent_multiple_parent_mux_set_parent_get_rate(struct kunit KUNIT_EXPECT_EQ(test, parent_rate, rate); } +/* + * 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); +} + /* * 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 @@ -634,6 +700,41 @@ clk_test_orphan_transparent_multiple_parent_mux_set_parent_set_range_modified(st clk_put(parent); } +/* + * 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 = hw->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); +} + /* * 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 @@ -660,8 +761,11 @@ clk_test_orphan_transparent_multiple_parent_mux_set_range_round_rate(struct kuni 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), + KUNIT_CASE(clk_test_orphan_transparent_multiple_parent_mux_set_parent_drop_range), KUNIT_CASE(clk_test_orphan_transparent_multiple_parent_mux_set_parent_get_rate), + KUNIT_CASE(clk_test_orphan_transparent_multiple_parent_mux_set_parent_put), KUNIT_CASE(clk_test_orphan_transparent_multiple_parent_mux_set_parent_set_range_modified), + KUNIT_CASE(clk_test_orphan_transparent_multiple_parent_mux_set_parent_set_range_untouched), KUNIT_CASE(clk_test_orphan_transparent_multiple_parent_mux_set_range_round_rate), {} }; @@ -946,6 +1050,132 @@ static struct kunit_suite clk_orphan_transparent_single_parent_test_suite = { .test_cases = clk_orphan_transparent_single_parent_mux_test_cases, }; +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 = hw->clk; + unsigned long rate; + + rate = clk_get_rate(clk); + KUNIT_EXPECT_EQ(test, rate, DUMMY_CLOCK_INIT_RATE); +} + +/* + * 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 = hw->clk; + 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); +} + +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 are supposed to 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, +}; + /* * 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 @@ -1629,6 +1859,7 @@ kunit_test_suites( &clk_multiple_parents_mux_test_suite, &clk_orphan_transparent_multiple_parent_mux_test_suite, &clk_orphan_transparent_single_parent_test_suite, + &clk_orphan_two_level_root_last_test_suite, &clk_range_test_suite, &clk_range_maximize_test_suite, &clk_range_minimize_test_suite, -- 2.35.1