From nobody Sat Jun 20 16:33:40 2026 Received: from mail-wr1-f41.google.com (mail-wr1-f41.google.com [209.85.221.41]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B9B523C8702 for ; Mon, 13 Apr 2026 12:49:20 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.41 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776084562; cv=none; b=uV9rqsR+3gv2NpKx+9gdKVE6P/T7xBgjTp/8bnX+s6SacEPBB8TS5lImb1HvJYJla6wI4Ox6CysQPBH1ZJrUvRxTuypHJvGaS7Vv/sfUg9cosz+heQBlt2Qc0FvXiXAzDGYmJLZwZlkrWZ7utd6PpmBnjOxZOkNR/ulDLSLvUV4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776084562; c=relaxed/simple; bh=kBqnvQDwdiPZ+uQ0CawA0daWE+Ez/OQGt5TdVXg//xk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=k7PClpLuoJIfmwpQAwXNWB3cbcf+f494h6wTx7h8GIVCcP7yxbEbw4b2LszsH/5xHTc/tHWWZAwEqk9jnomGG0lYx7vEuvEfd45oyIW53CkjJSFjFEWMKf97TH9yBFBH5Tr1hsKsFopUxb3lclDKJ1UXIU2NKJriFj9+eRYxSxg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=JcvT/ODw; arc=none smtp.client-ip=209.85.221.41 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="JcvT/ODw" Received: by mail-wr1-f41.google.com with SMTP id ffacd0b85a97d-43cfd1f9fd1so2816605f8f.3 for ; Mon, 13 Apr 2026 05:49:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1776084559; x=1776689359; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=/iZLzX9bqF3lkIILNvVjrN7/XoHKLWrdf/ip5ggw2bg=; b=JcvT/ODwioXnS7kcUN4oOz85buJqkdCMr6iwXK9kFiRh8s6fbWsGCkOUG6m9KV9mp3 lRNijl4zU6ozn3Rvu6HlY6XfIT80mqdZX0NNj2VPHgCVMRy93W/AR5SBdts8I8EFJw0L xRolZnEsJbk/AfK+wyv8jr2LfXjIVJdBMTFH1xdv7zb6O0i97V5yRTgezlW/Qc6UPhcU Lic8YKBn+vxJF12FmBJ1Aipr8QgQYOngI+Orn9wCiyrmmCijlMgzHUlt7P/pttWqAzcS pjPAaW/qM1ONrLt7BvnJhRhn2e6X/8deKoat7VPHdlyU3vOtIq0G/25BCQox2UArKfXn SdZw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1776084559; x=1776689359; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=/iZLzX9bqF3lkIILNvVjrN7/XoHKLWrdf/ip5ggw2bg=; b=Wa7UP3b+6f7LPDvWM/uMjCs2yRaPyPo6Gdq9SLzezpvIGPfl8vMGBGPJO+D9wuDBzr K9q2rPbmVBKH3pwgQK1iJwrxlyibmr1/E5z7VElDFyysLSo2MHQWexq7mluDjDfdrDJm onz8zlGQzc/u4VpFBcLpBHMIohwMlmleP9NliXQ91QdBJ+iRkN5ev7yM6VYkDMgOlza3 vIO8OGyl0dhFRMihSulKzPzqfB40c58L77nWhodjTg/Y6/B3kHQCypjCr82/jBZE2nl8 Rl+rZOY+4IOLV2UFUwO9zAI80fht19wUUDEGqttdmUpFXQjqbpbtqhqvbwSb76yIAdVX C1cA== X-Gm-Message-State: AOJu0Yzry3M3pA6H2N/0+dyAWSKEUbCxJLJPJlqVkq01yIgUsCr1Xiac PEXh2JVfjMJFi6fJCZs7bMSdB/WxiTQTv+UIP5Eia9+1ZccdwlLJl0v4 X-Gm-Gg: AeBDieu9glY6q+aiJ0jn639bXCSDx+SCLPp9A0QnPfutTDb6oe7JrSd1+ydpsNLVosG vBwbNz/vL5UXsa57Wk6swGOXrpi+dB1dYzpq0/WM31zx3YuyM80QnY5ufIaL5D593w1xeDhdS+8 nt2JTQoq1J5GBXP8g4Lf44XaWr72M9WnudkrVDM9c4Jtdzy8uo9GRRaiUiBzjhhmfWtVEm2G85O Ealj0JamQdTXaf8/7yWLh1lFwzeUEe2IJJIFD8hLApYx3/V3bQxJAh2LqOSgkBIf3QRrSn6fLZk ACbdS9/YaWNaroLyywAnBryLuZGI39mrSrm6J0btujSgsBri4YUw01VOBdCRhooAN4DLB876O0r or8UNrYhHTfyiLhtwyB28Qxyo2P0xMzAFUOGTELMAc5NoPo/57Fkegj9S/AphFAgc0KytVx3OL9 /dTYNRN5nUUeIoi4eZ2chDDB7yQYbIYax0bEfMbj3Duh+XKYTOxsXG1u7YW/YVOOt6Si2oPcfEG NnAXeZyQJuOWsqVxqel0bwz8a19J6bzL1kCsQe7aGraqh86KoVL2w899A== X-Received: by 2002:a05:6000:400e:b0:43d:1c39:26fd with SMTP id ffacd0b85a97d-43d642dc79bmr19277103f8f.43.1776084559020; Mon, 13 Apr 2026 05:49:19 -0700 (PDT) Received: from iku.example.org ([2a06:5906:61b:2d00:8060:3087:2ea2:f494]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-43d63e50015sm34634318f8f.27.2026.04.13.05.49.18 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 13 Apr 2026 05:49:18 -0700 (PDT) From: Prabhakar X-Google-Original-From: Prabhakar To: Michael Turquette , Stephen Boyd , Geert Uytterhoeven Cc: linux-kernel@vger.kernel.org, linux-clk@vger.kernel.org, linux-renesas-soc@vger.kernel.org, Prabhakar , Biju Das , Fabrizio Castro , Lad Prabhakar Subject: [PATCH v2 1/2] clk: divider: Fix clk_divider_bestdiv() returning min rate for large rate requests Date: Mon, 13 Apr 2026 13:49:11 +0100 Message-ID: <20260413124912.3260571-2-prabhakar.mahadev-lad.rj@bp.renesas.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260413124912.3260571-1-prabhakar.mahadev-lad.rj@bp.renesas.com> References: <20260413124912.3260571-1-prabhakar.mahadev-lad.rj@bp.renesas.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Lad Prabhakar clk_divider_bestdiv() clamps maxdiv using: maxdiv =3D min(ULONG_MAX / rate, maxdiv); to avoid overflow in rate * i. However, requests like clk_round_rate(clk, ULONG_MAX), which are used to determine the maximum supported rate of a clock, result in maxdiv being clamped to 1. If no valid divider of 1 exists in the table the loop is never entered and bestdiv falls back to the maximum divider with the minimum parent rate, causing clk_round_rate(clk, ULONG_MAX) to incorrectly return the minimum supported rate instead of the maximum. Fix this by removing the pre-loop maxdiv clamping and replacing the unprotected rate * i multiplication with check_mul_overflow(). Guard the exact-match short-circuit with !overflow to prevent a clamped target_parent_rate of ULONG_MAX from falsely matching parent_rate_saved and causing premature loop exit. Break out of the loop after evaluating the first overflowing divider since clk_hw_round_rate(parent, ULONG_MAX) returns a constant for all subsequent iterations, meaning no better candidate can be found, and continuing would cause exponential recursive calls in chained divider clocks. Signed-off-by: Lad Prabhakar Reviewed-by: Brian Masney --- drivers/clk/clk-divider.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index 45e7ebde4a8b..9a6a2ad6f397 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -15,6 +15,7 @@ #include #include #include +#include =20 /* * DOC: basic adjustable divider clock that cannot gate @@ -301,6 +302,7 @@ static int clk_divider_bestdiv(struct clk_hw *hw, struc= t clk_hw *parent, int i, bestdiv =3D 0; unsigned long parent_rate, best =3D 0, now, maxdiv; unsigned long parent_rate_saved =3D *best_parent_rate; + unsigned long target_parent_rate; =20 if (!rate) rate =3D 1; @@ -315,15 +317,11 @@ static int clk_divider_bestdiv(struct clk_hw *hw, str= uct clk_hw *parent, return bestdiv; } =20 - /* - * The maximum divider we can use without overflowing - * unsigned long in rate * i below - */ - maxdiv =3D min(ULONG_MAX / rate, maxdiv); - for (i =3D _next_div(table, 0, flags); i <=3D maxdiv; i =3D _next_div(table, i, flags)) { - if (rate * i =3D=3D parent_rate_saved) { + bool overflow =3D check_mul_overflow(rate, (unsigned long)i, &target_par= ent_rate); + + if (!overflow && target_parent_rate =3D=3D parent_rate_saved) { /* * It's the most ideal case if the requested rate can be * divided from parent clock without needing to change @@ -332,13 +330,24 @@ static int clk_divider_bestdiv(struct clk_hw *hw, str= uct clk_hw *parent, *best_parent_rate =3D parent_rate_saved; return i; } - parent_rate =3D clk_hw_round_rate(parent, rate * i); + /* + * Clamp target_parent_rate to ULONG_MAX on overflow. The true + * required parent rate exceeds what can be represented, so ask + * the parent for the highest rate it can produce. There is no + * point continuing the loop past this since larger dividers + * only move further from the requested rate. + */ + if (overflow) + target_parent_rate =3D ULONG_MAX; + parent_rate =3D clk_hw_round_rate(parent, target_parent_rate); now =3D DIV_ROUND_UP_ULL((u64)parent_rate, i); if (_is_best_div(rate, now, best, flags)) { bestdiv =3D i; best =3D now; *best_parent_rate =3D parent_rate; } + if (overflow) + break; } =20 if (!bestdiv) { --=20 2.53.0 From nobody Sat Jun 20 16:33:40 2026 Received: from mail-wr1-f48.google.com (mail-wr1-f48.google.com [209.85.221.48]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5172D3C7E05 for ; Mon, 13 Apr 2026 12:49:21 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.48 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776084563; cv=none; b=PTEBx/xwVPpXDmxU84lKeH/oyB/QKXy+t0oH1MLIAvzrWi6TTIfQ5YqRkOmas9+ac+hENd6yN0sg+GpUEeWfdtihtIxzXHsI+7O0ZgLEdIBUSenr/NuzQVpFIUYVj/BjxedmSq1Zi8VPnGLQ7tHPF4IQiGQuz/QNNKPFK/97S2E= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776084563; c=relaxed/simple; bh=/L73E0ZuOj9b7tO+qUXm2BUgZmzJx25hqEciKe/Vffs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=AsVC9HaOoSCp2d377phJ3JWJ35iUfgQh10bzVUGMjv6iM9PeFWFx7SiUbpaO/7SeZxazOPO2McCqv6W+JNT34n6/NCbqHIVbnNq8cAy59CifS25mymA1AI8QSGV3VjDs5RfwloKf+mdK3BiXq9N14u5EvV4wPZa4S9/nx7XvDjI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=ROVGCJMc; arc=none smtp.client-ip=209.85.221.48 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="ROVGCJMc" Received: by mail-wr1-f48.google.com with SMTP id ffacd0b85a97d-43cfbd17589so2985682f8f.0 for ; Mon, 13 Apr 2026 05:49:21 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1776084560; x=1776689360; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=zILI3bSq+qhFMWUMzmmHd5z5cGi7XjJl7b2Gmvd6Z/4=; b=ROVGCJMcqqzajsQBcnW50RKB87WorbNTkqQzl6mqk53sVqF8MQUiL89QiRV4IvYCJM ae+IwO09fzaUn4S2B3V/bfHFYTFtXIN84cBe6QTCXIxDOytR/yv7nc0tlOfUDvFQzHqe y6cwocoSkmw6YenkAdQPKIVLM2Nud+FZUPtxJD6JrKdwzhb8eBCu/VWj2wBlbq6fYy1X 1l/D9wYmVyLK1Y0pFAcRderTOgwjOkr0e6BZQPwaPvHZSo0Na+BosMyZPiL1Gb9h5BRw HDLguF8QZM9p91zc5mt/KQfDA8LHfkYLnWb1IwplXY7W9JS0eADMFz0s/b7mav3+ik+l Le8g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1776084560; x=1776689360; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=zILI3bSq+qhFMWUMzmmHd5z5cGi7XjJl7b2Gmvd6Z/4=; b=UTffegG3Xmo15nMwGD+IGyTC4gJxW+4jU4Qz37L7luuKCQ6fvmrS04CYFYnyN9tbTG wsObXYxoLTLOxjaHN5/7l5dqXTSXjg1qdHoJ98JYfGZKv8IELgBLz59Cp6CC3OHkwLGB 1f9V8Sng4SV1fd77X0BUibgjdqY6rtrJZ0yadyWivIibwafyY8/lcl7MXe9nKpF/JlFm H00W7kOVU31qtsvAADBjnBT9YnDDAcEaxKV3NQIYCq/tHnmbHtwjzYzEmR2+A+H+93qE Qx6lz7jAvSaU0hAcaypDh/G0VrNO5Ik8/ktqwsLRWpA+sHL5fV6Jz5cH9u44T3aDlnKn t1AA== X-Gm-Message-State: AOJu0YxK1Flby3gDI8xWM/8IR8JIjdnZV1kTqWc6iV3i0xzafe1audid +Fk3RIjjEKzWM0GS5lAXJIh2pROy75Q3pPd8VE5Q/KJnHLnUvXeuVY7E X-Gm-Gg: AeBDieuTkzl+Qr0EAHKHTtx0gB9FAVNgLRgetFNDHFQGSy1dItYWrNIcpoDamcAEyYe 9C+xcuJO4ayGSlSa3u31Yn20Uh8KUNVSavKXUYxa2pszC5LZnd30IKo90JHp5YFLs3RI5lnktzi BoRA8u1Qhla1O+rZ4yd2wA5cwC1IoySJO/gJgq4Ck8mtb0x01xjZZxBgHITSO3wKqUv1Fpg2cMn 5yFMG/P3sOs6mQwMRwmzRiB1ylM4WdIjTolFWlpadXNEab9K2DMVtSG8XAPwEvfBpBwyXi1LByg TBxxrEAuDxzrK8WAsJ9fl3/R7rgou0HMVa+/CZ1pBxmOo1Ksd4QHZnMj7wEXoJsZ8UvHpRPrrQ9 t7VdhvcbUcduy/0LY9Ni4bMvDv7lmiw7c69GkKuAL0ZqKbeWVe2u2VovsfEiIpIPMk6HuFzkSsq 5BJKfXQ23SnEauHmFY8QCcCfjVu+MJPI2voiBFnKeOJmgHP/MltUEAydYYUuIW0HXKS+rEQC+Ss bb5z/6aUrpU95cZoysVGyBUVkggbDhEEFrMpHLQQpY9cgk= X-Received: by 2002:a05:6000:2486:b0:43d:7518:b0fc with SMTP id ffacd0b85a97d-43d7518b2a1mr7508526f8f.43.1776084559589; Mon, 13 Apr 2026 05:49:19 -0700 (PDT) Received: from iku.example.org ([2a06:5906:61b:2d00:8060:3087:2ea2:f494]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-43d63e50015sm34634318f8f.27.2026.04.13.05.49.19 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 13 Apr 2026 05:49:19 -0700 (PDT) From: Prabhakar X-Google-Original-From: Prabhakar To: Michael Turquette , Stephen Boyd , Geert Uytterhoeven Cc: linux-kernel@vger.kernel.org, linux-clk@vger.kernel.org, linux-renesas-soc@vger.kernel.org, Prabhakar , Biju Das , Fabrizio Castro , Lad Prabhakar Subject: [PATCH v2 2/2] clk: divider: Add some kunit test suites Date: Mon, 13 Apr 2026 13:49:12 +0100 Message-ID: <20260413124912.3260571-3-prabhakar.mahadev-lad.rj@bp.renesas.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260413124912.3260571-1-prabhakar.mahadev-lad.rj@bp.renesas.com> References: <20260413124912.3260571-1-prabhakar.mahadev-lad.rj@bp.renesas.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Lad Prabhakar Add KUnit tests to verify clk_divider_bestdiv() returns the maximum achievable rate when clk_round_rate() is called with ULONG_MAX, which is the canonical way to probe the maximum rate a clock can produce. The first test uses a fixed-rate parent driving a table-based divider with no div=3D1 entry. The second test places a two-input mux between the divider and its root clocks to verify correct parent selection and that the divider loop does not make redundant calls to clk_hw_round_rate() for each remaining table entry after the first overflow. Signed-off-by: Lad Prabhakar --- drivers/clk/Kconfig | 7 ++ drivers/clk/Makefile | 1 + drivers/clk/clk-divider_test.c | 151 +++++++++++++++++++++++++++++++++ 3 files changed, 159 insertions(+) create mode 100644 drivers/clk/clk-divider_test.c diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index cc8743b11bb1..c8f9eaef6f6b 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -573,4 +573,11 @@ config CLK_FD_KUNIT_TEST help Kunit test for the clk-fractional-divider type. =20 +config CLK_DIVIDER_KUNIT_TEST + tristate "KUnit tests for clk divider bestdiv" if !KUNIT_ALL_TESTS + depends on KUNIT + default KUNIT_ALL_TESTS + help + Kunit test for the clk-divider type. + endif diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index a3e2862ebd7e..0c915c6cf3fa 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -20,6 +20,7 @@ clk-test-y :=3D clk_test.o \ kunit_clk_assigned_rates_zero_consumer.dtbo.o \ kunit_clk_hw_get_dev_of_node.dtbo.o \ kunit_clk_parent_data_test.dtbo.o +obj-$(CONFIG_CLK_DIVIDER_KUNIT_TEST) +=3D clk-divider_test.o obj-$(CONFIG_COMMON_CLK) +=3D clk-divider.o obj-$(CONFIG_COMMON_CLK) +=3D clk-fixed-factor.o obj-$(CONFIG_COMMON_CLK) +=3D clk-fixed-rate.o diff --git a/drivers/clk/clk-divider_test.c b/drivers/clk/clk-divider_test.c new file mode 100644 index 000000000000..3a5e3adccb2e --- /dev/null +++ b/drivers/clk/clk-divider_test.c @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * KUnit tests for clk_divider_bestdiv() + */ +#include +#include +#include +#include +#include + +#define PARENT_RATE_1GHZ GIGA +#define PARENT_RATE_2GHZ (2 * GIGA) +#define PARENT_RATE_4GHZ (4 * GIGA) + +static u32 fake_reg_a, fake_reg_b; + +static const struct clk_div_table no_div1_table[] =3D { + {0, 2}, + {1, 4}, + {2, 8}, + {0, 0}, +}; + +static void unregister_fixed_rate(void *hw) +{ + clk_hw_unregister_fixed_rate(hw); +} + +static void unregister_divider(void *hw) +{ + clk_hw_unregister_divider(hw); +} + +static void unregister_mux(void *hw) +{ + clk_hw_unregister_mux(hw); +} + +/* + * Test that clk_round_rate(clk, ULONG_MAX) returns the maximum achievable + * rate for a divider clock. + */ +static void clk_divider_bestdiv_ulong_max_returns_max_rate(struct kunit *t= est) +{ + struct clk_hw *parent_hw, *div_hw; + unsigned long rate; + + parent_hw =3D clk_hw_register_fixed_rate(NULL, "bestdiv-parent", + NULL, 0, PARENT_RATE_1GHZ); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent_hw); + kunit_add_action(test, unregister_fixed_rate, parent_hw); + + fake_reg_a =3D 0; + div_hw =3D clk_hw_register_divider_table(NULL, "bestdiv-div", + "bestdiv-parent", + CLK_SET_RATE_PARENT, + (void __iomem *)&fake_reg_a, + 0, 2, 0, no_div1_table, NULL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, div_hw); + kunit_add_action(test, unregister_divider, div_hw); + + /* + * ULONG_MAX is the canonical way to probe the maximum rate a clock + * can produce. With a parent at 1 GHz and the smallest table divider + * being 2, the expected maximum is 500 MHz. + * + * Before the fix this returned 125 MHz (PARENT_RATE / 8), the + * minimum rate, because the search loop was bypassed entirely. + */ + rate =3D clk_hw_round_rate(div_hw, ULONG_MAX); + KUNIT_EXPECT_EQ(test, rate, PARENT_RATE_1GHZ / 2); +} + +/* + * Test that clk_round_rate(clk, ULONG_MAX) returns the correct maximum ra= te when + * a mux clock sits between a divider and its parent candidates. + * + * Topology: + * + * [fixed 4 GHz] --\ + * +--> [mux CLK_SET_RATE_PARENT] --> [div {2,4,8} CLK_= SET_RATE_PARENT] + * [fixed 2 GHz] --/ + * + */ +static void clk_divider_bestdiv_mux_ulong_max_returns_max_rate(struct kuni= t *test) +{ + static const char *const mux_parents[] =3D { + "bestdiv-mux-parent-a", + "bestdiv-mux-parent-b", + }; + struct clk_hw *parent_a_hw, *parent_b_hw, *mux_hw, *div_hw; + unsigned long rate; + + /* Higher-rate parent: the mux should select this for ULONG_MAX. */ + parent_a_hw =3D clk_hw_register_fixed_rate(NULL, "bestdiv-mux-parent-a", + NULL, 0, PARENT_RATE_4GHZ); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent_a_hw); + kunit_add_action(test, unregister_fixed_rate, parent_a_hw); + + /* Lower-rate parent: should not be selected. */ + parent_b_hw =3D clk_hw_register_fixed_rate(NULL, "bestdiv-mux-parent-b", + NULL, 0, PARENT_RATE_2GHZ); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent_b_hw); + kunit_add_action(test, unregister_fixed_rate, parent_b_hw); + + /* + * 1-bit mux register selects between the two parents. + * CLK_SET_RATE_PARENT allows the divider's rate request to + * propagate into clk_mux_determine_rate(). + */ + fake_reg_a =3D 0; + mux_hw =3D clk_hw_register_mux(NULL, "bestdiv-mux", + mux_parents, ARRAY_SIZE(mux_parents), + CLK_SET_RATE_PARENT, + (void __iomem *)&fake_reg_a, + 0, 1, 0, NULL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, mux_hw); + kunit_add_action(test, unregister_mux, mux_hw); + + fake_reg_b =3D 0; + div_hw =3D clk_hw_register_divider_table(NULL, "bestdiv-mux-div", + "bestdiv-mux", + CLK_SET_RATE_PARENT, + (void __iomem *)&fake_reg_b, + 0, 2, 0, no_div1_table, NULL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, div_hw); + kunit_add_action(test, unregister_divider, div_hw); + + /* + * Expected maximum: mux selects the 4 GHz parent, divider applies + * the smallest table entry (2): 4 GHz / 2 =3D 2 GHz. + */ + rate =3D clk_hw_round_rate(div_hw, ULONG_MAX); + KUNIT_EXPECT_EQ(test, rate, PARENT_RATE_4GHZ / 2); +} + +static struct kunit_case clk_divider_bestdiv_test_cases[] =3D { + KUNIT_CASE(clk_divider_bestdiv_ulong_max_returns_max_rate), + KUNIT_CASE(clk_divider_bestdiv_mux_ulong_max_returns_max_rate), + {} +}; + +static struct kunit_suite clk_divider_bestdiv_test_suite =3D { + .name =3D "clk_divider_bestdiv", + .test_cases =3D clk_divider_bestdiv_test_cases, +}; + +kunit_test_suite(clk_divider_bestdiv_test_suite); + +MODULE_DESCRIPTION("KUnit tests for clk_divider_bestdiv()"); +MODULE_LICENSE("GPL"); --=20 2.53.0