* [PATCH v2 0/3] Fix clk-divider rates above 4.29 GHz on 64-bit
@ 2026-11-12 9:36 Carlo Caione
2026-11-12 9:36 ` [PATCH v2 1/3] clk: divider: Fix the divisor for rates above 4.29 GHz Carlo Caione
` (2 more replies)
0 siblings, 3 replies; 9+ messages in thread
From: Carlo Caione @ 2026-11-12 9:36 UTC (permalink / raw)
To: Stephen Boyd, Brian Masney, Jerome Brunet, Brian Norris,
Maxime Coquelin, Uwe Kleine-König, Sascha Hauer,
Michael Turquette
Cc: Stephen Boyd, David Laight, Sebastian Reichel, linux-clk,
linux-kernel, Jerome Brunet, Carlo Caione
On 64-bit platforms, the clk-divider truncates requested rates above
4.29 GHz when computing a divisor. Fix it and add KUnit tests.
Sebastian Reichel fixed this in 2023 [1], but later versions moved to a
new generic macro and were never applied. Patch 1 builds on his v1 and
rounds up with (parent_rate - 1) / rate + 1, which does not overflow
with clk_round_rate(clk, ULONG_MAX). Patch 2 fixes a division by zero
found by the new tests.
[1] https://lore.kernel.org/linux-clk/20230519190522.194729-3-sebastian.reichel@collabora.com/
---
Changes in v2:
- Patch 1: round up with (parent_rate - 1) / rate + 1, as
DIV64_U64_ROUND_UP() overflows with clk_round_rate(clk, ULONG_MAX)
(Jerome, David)
- New patch 2: fix a division by zero in round-closest dividers
- Patch 3: also test the maximum rate (Brian) and rates above the parent
- Link to v1: https://patch.msgid.link/20261102-upstream-clk-divider-large-rates-v1-0-eb17035bfad2@baylibre.com
---
Carlo Caione (3):
clk: divider: Fix the divisor for rates above 4.29 GHz
clk: divider: Fix round-closest for rates above the parent rate
clk: divider: test: Add tests for rates above 4.29 GHz
drivers/clk/clk-divider.c | 21 +++-
drivers/clk/clk-divider_test.c | 244 ++++++++++++++++++++++++++++++++++++++++-
2 files changed, 260 insertions(+), 5 deletions(-)
---
base-commit: ab94dbf302331eed3679e262e1d8d1a73f308640
change-id: 20261102-upstream-clk-divider-large-rates-872467864e39
Best regards,
--
Carlo Caione <ccaione@baylibre.com>
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH v2 1/3] clk: divider: Fix the divisor for rates above 4.29 GHz 2026-11-12 9:36 [PATCH v2 0/3] Fix clk-divider rates above 4.29 GHz on 64-bit Carlo Caione @ 2026-11-12 9:36 ` Carlo Caione 2026-11-12 15:09 ` Brian Masney 2026-11-12 15:34 ` Sebastian Reichel 2026-11-12 9:37 ` [PATCH v2 2/3] clk: divider: Fix round-closest for rates above the parent rate Carlo Caione 2026-11-12 9:37 ` [PATCH v2 3/3] clk: divider: test: Add tests for rates above 4.29 GHz Carlo Caione 2 siblings, 2 replies; 9+ messages in thread From: Carlo Caione @ 2026-11-12 9:36 UTC (permalink / raw) To: Stephen Boyd, Brian Masney, Jerome Brunet, Brian Norris, Maxime Coquelin, Uwe Kleine-König, Sascha Hauer, Michael Turquette Cc: Stephen Boyd, David Laight, Sebastian Reichel, linux-clk, linux-kernel, Jerome Brunet, Carlo Caione DIV_ROUND_UP_ULL() takes a 32-bit divisor, so on 64-bit platforms a requested rate above 4.29 GHz is truncated. A divider fed by a 5.94 GHz PLL then computes a divisor of 7, not 1, for 5.94 GHz. Round up with (parent_rate - 1) / rate + 1 instead, building on an earlier fix by Sebastian Reichel. It divides by the full rate and does not overflow with clk_round_rate(clk, ULONG_MAX). Fixes: 9556f9dad8f5 ("clk: divider: handle integer overflow when dividing large clock rates") Suggested-by: Jerome Brunet <jbrunet@baylibre.com> Suggested-by: David Laight <david.laight.linux@gmail.com> Signed-off-by: Carlo Caione <ccaione@baylibre.com> --- drivers/clk/clk-divider.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index f68216c65d3d..c3f46f9ee451 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -223,11 +223,21 @@ static int _round_down_table(const struct clk_div_table *table, int div) return down; } +/* + * DIV_ROUND_UP() without the addition, which overflows for rates close to + * ULONG_MAX, as with clk_round_rate(clk, ULONG_MAX) + */ +static unsigned long _get_div_round_up(unsigned long parent_rate, + unsigned long rate) +{ + return parent_rate ? (parent_rate - 1) / rate + 1 : 0; +} + static int _div_round_up(const struct clk_div_table *table, unsigned long parent_rate, unsigned long rate, unsigned long flags) { - int div = DIV_ROUND_UP_ULL((u64)parent_rate, rate); + int div = _get_div_round_up(parent_rate, rate); if (flags & CLK_DIVIDER_POWER_OF_TWO) div = __roundup_pow_of_two(div); @@ -244,7 +254,7 @@ static int _div_round_closest(const struct clk_div_table *table, int up, down; unsigned long up_rate, down_rate; - up = DIV_ROUND_UP_ULL((u64)parent_rate, rate); + up = _get_div_round_up(parent_rate, rate); down = parent_rate / rate; if (flags & CLK_DIVIDER_POWER_OF_TWO) { @@ -423,7 +433,7 @@ int divider_get_val(unsigned long rate, unsigned long parent_rate, { unsigned int div, value; - div = DIV_ROUND_UP_ULL((u64)parent_rate, rate); + div = _get_div_round_up(parent_rate, rate); if (!_is_valid_div(table, div, flags)) return -EINVAL; -- 2.56.0 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH v2 1/3] clk: divider: Fix the divisor for rates above 4.29 GHz 2026-11-12 9:36 ` [PATCH v2 1/3] clk: divider: Fix the divisor for rates above 4.29 GHz Carlo Caione @ 2026-11-12 15:09 ` Brian Masney 2026-11-12 15:29 ` Carlo Caione 2026-11-12 15:34 ` Sebastian Reichel 1 sibling, 1 reply; 9+ messages in thread From: Brian Masney @ 2026-11-12 15:09 UTC (permalink / raw) To: Carlo Caione Cc: Stephen Boyd, Brian Masney, Jerome Brunet, Brian Norris, Maxime Coquelin, Uwe Kleine-König, Sascha Hauer, Michael Turquette, Stephen Boyd, David Laight, Sebastian Reichel, linux-clk, linux-kernel, Jerome Brunet, Brian Masney Hi Carlo, > DIV_ROUND_UP_ULL() takes a 32-bit divisor, so on 64-bit platforms a > requested rate above 4.29 GHz is truncated. A divider fed by a 5.94 GHz > PLL then computes a divisor of 7, not 1, for 5.94 GHz. > > Round up with (parent_rate - 1) / rate + 1 instead, building on an > earlier fix by Sebastian Reichel. It divides by the full rate and does > not overflow with clk_round_rate(clk, ULONG_MAX). > > Fixes: 9556f9dad8f5 ("clk: divider: handle integer overflow when dividing large clock rates") > Suggested-by: Jerome Brunet <jbrunet@baylibre.com> > Suggested-by: David Laight <david.laight.linux@gmail.com> > Signed-off-by: Carlo Caione <ccaione@baylibre.com> > --- > drivers/clk/clk-divider.c | 16 +++++++++++++--- > 1 file changed, 13 insertions(+), 3 deletions(-) > > diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c > index f68216c65d3d..c3f46f9ee451 100644 > --- a/drivers/clk/clk-divider.c > +++ b/drivers/clk/clk-divider.c > @@ -223,11 +223,21 @@ static int _round_down_table(const struct clk_div_table *table, int div) > return down; > } > > +/* > + * DIV_ROUND_UP() without the addition, which overflows for rates close to > + * ULONG_MAX, as with clk_round_rate(clk, ULONG_MAX) > + */ > +static unsigned long _get_div_round_up(unsigned long parent_rate, > + unsigned long rate) > +{ > + return parent_rate ? (parent_rate - 1) / rate + 1 : 0; > +} > + > static int _div_round_up(const struct clk_div_table *table, > unsigned long parent_rate, unsigned long rate, > unsigned long flags) > { > - int div = DIV_ROUND_UP_ULL((u64)parent_rate, rate); > + int div = _get_div_round_up(parent_rate, rate); _get_div_round_up() returns an unsigned long however it's downsized to an int here. Brian -- Brian Masney <masneyb@kernel.org> ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2 1/3] clk: divider: Fix the divisor for rates above 4.29 GHz 2026-11-12 15:09 ` Brian Masney @ 2026-11-12 15:29 ` Carlo Caione 2026-11-12 15:38 ` Sebastian Reichel 0 siblings, 1 reply; 9+ messages in thread From: Carlo Caione @ 2026-11-12 15:29 UTC (permalink / raw) To: Brian Masney Cc: Stephen Boyd, Brian Masney, Jerome Brunet, Brian Norris, Maxime Coquelin, Uwe Kleine-König, Sascha Hauer, Michael Turquette, Stephen Boyd, David Laight, Sebastian Reichel, linux-clk, linux-kernel, Jerome Brunet On Thu, Nov 12, 2026 at 10:09:46 +0100, Brian Masney wrote: > Hi Carlo, Hi Brian, > > static int _div_round_up(const struct clk_div_table *table, > > unsigned long parent_rate, unsigned long rate, > > unsigned long flags) > > { > > - int div = DIV_ROUND_UP_ULL((u64)parent_rate, rate); > > + int div = _get_div_round_up(parent_rate, rate); > > _get_div_round_up() returns an unsigned long however it's downsized to > an int here. Right, but wasn't also DIV_ROUND_UP_ULL() previously returning a u64 that was stored in the same int? thanks, -- Carlo Caione ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2 1/3] clk: divider: Fix the divisor for rates above 4.29 GHz 2026-11-12 15:29 ` Carlo Caione @ 2026-11-12 15:38 ` Sebastian Reichel 0 siblings, 0 replies; 9+ messages in thread From: Sebastian Reichel @ 2026-11-12 15:38 UTC (permalink / raw) To: Carlo Caione Cc: Brian Masney, Stephen Boyd, Brian Masney, Jerome Brunet, Brian Norris, Maxime Coquelin, Uwe Kleine-König, Sascha Hauer, Michael Turquette, Stephen Boyd, David Laight, linux-clk, linux-kernel, Jerome Brunet [-- Attachment #1: Type: text/plain, Size: 1261 bytes --] Hi, On Thu, Nov 12, 2026 at 04:29:55PM +0100, Carlo Caione wrote: > On Thu, Nov 12, 2026 at 10:09:46 +0100, Brian Masney wrote: > > Hi Carlo, > > Hi Brian, > > > > static int _div_round_up(const struct clk_div_table *table, > > > unsigned long parent_rate, unsigned long rate, > > > unsigned long flags) > > > { > > > - int div = DIV_ROUND_UP_ULL((u64)parent_rate, rate); > > > + int div = _get_div_round_up(parent_rate, rate); > > > > _get_div_round_up() returns an unsigned long however it's downsized to > > an int here. > > Right, but wasn't also DIV_ROUND_UP_ULL() previously returning a u64 > that was stored in the same int? I would also argue, that this is a separate fix. Looking at the code involved it goes all the way down to this incorrectly using int for a clock rate: /* * ... * @determine_rate: Given a target rate as input, returns the closest rate * actually supported by the clock, and optionally the parent clock * that should be used to provide the clock rate. * ... */ struct clk_ops { ... int (*determine_rate)(struct clk_hw *hw, struct clk_rate_request *req); ... } Fixing that will be a bigger task. Greetings, -- Sebastian [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 833 bytes --] ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2 1/3] clk: divider: Fix the divisor for rates above 4.29 GHz 2026-11-12 9:36 ` [PATCH v2 1/3] clk: divider: Fix the divisor for rates above 4.29 GHz Carlo Caione 2026-11-12 15:09 ` Brian Masney @ 2026-11-12 15:34 ` Sebastian Reichel 1 sibling, 0 replies; 9+ messages in thread From: Sebastian Reichel @ 2026-11-12 15:34 UTC (permalink / raw) To: Carlo Caione Cc: Stephen Boyd, Brian Masney, Jerome Brunet, Brian Norris, Maxime Coquelin, Uwe Kleine-König, Sascha Hauer, Michael Turquette, Stephen Boyd, David Laight, linux-clk, linux-kernel, Jerome Brunet [-- Attachment #1: Type: text/plain, Size: 2617 bytes --] Hi, On Thu, Nov 12, 2026 at 10:36:59AM +0100, Carlo Caione wrote: > DIV_ROUND_UP_ULL() takes a 32-bit divisor, so on 64-bit platforms a > requested rate above 4.29 GHz is truncated. A divider fed by a 5.94 GHz > PLL then computes a divisor of 7, not 1, for 5.94 GHz. > > Round up with (parent_rate - 1) / rate + 1 instead, building on an > earlier fix by Sebastian Reichel. It divides by the full rate and does > not overflow with clk_round_rate(clk, ULONG_MAX). > > Fixes: 9556f9dad8f5 ("clk: divider: handle integer overflow when dividing large clock rates") > Suggested-by: Jerome Brunet <jbrunet@baylibre.com> > Suggested-by: David Laight <david.laight.linux@gmail.com> > Signed-off-by: Carlo Caione <ccaione@baylibre.com> > --- Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com> Greetings, -- Sebastian > drivers/clk/clk-divider.c | 16 +++++++++++++--- > 1 file changed, 13 insertions(+), 3 deletions(-) > > diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c > index f68216c65d3d..c3f46f9ee451 100644 > --- a/drivers/clk/clk-divider.c > +++ b/drivers/clk/clk-divider.c > @@ -223,11 +223,21 @@ static int _round_down_table(const struct clk_div_table *table, int div) > return down; > } > > +/* > + * DIV_ROUND_UP() without the addition, which overflows for rates close to > + * ULONG_MAX, as with clk_round_rate(clk, ULONG_MAX) > + */ > +static unsigned long _get_div_round_up(unsigned long parent_rate, > + unsigned long rate) > +{ > + return parent_rate ? (parent_rate - 1) / rate + 1 : 0; > +} > + > static int _div_round_up(const struct clk_div_table *table, > unsigned long parent_rate, unsigned long rate, > unsigned long flags) > { > - int div = DIV_ROUND_UP_ULL((u64)parent_rate, rate); > + int div = _get_div_round_up(parent_rate, rate); > > if (flags & CLK_DIVIDER_POWER_OF_TWO) > div = __roundup_pow_of_two(div); > @@ -244,7 +254,7 @@ static int _div_round_closest(const struct clk_div_table *table, > int up, down; > unsigned long up_rate, down_rate; > > - up = DIV_ROUND_UP_ULL((u64)parent_rate, rate); > + up = _get_div_round_up(parent_rate, rate); > down = parent_rate / rate; > > if (flags & CLK_DIVIDER_POWER_OF_TWO) { > @@ -423,7 +433,7 @@ int divider_get_val(unsigned long rate, unsigned long parent_rate, > { > unsigned int div, value; > > - div = DIV_ROUND_UP_ULL((u64)parent_rate, rate); > + div = _get_div_round_up(parent_rate, rate); > > if (!_is_valid_div(table, div, flags)) > return -EINVAL; > > -- > 2.56.0 > [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 833 bytes --] ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v2 2/3] clk: divider: Fix round-closest for rates above the parent rate 2026-11-12 9:36 [PATCH v2 0/3] Fix clk-divider rates above 4.29 GHz on 64-bit Carlo Caione 2026-11-12 9:36 ` [PATCH v2 1/3] clk: divider: Fix the divisor for rates above 4.29 GHz Carlo Caione @ 2026-11-12 9:37 ` Carlo Caione 2026-11-12 15:41 ` Sebastian Reichel 2026-11-12 9:37 ` [PATCH v2 3/3] clk: divider: test: Add tests for rates above 4.29 GHz Carlo Caione 2 siblings, 1 reply; 9+ messages in thread From: Carlo Caione @ 2026-11-12 9:37 UTC (permalink / raw) To: Stephen Boyd, Brian Masney, Jerome Brunet, Brian Norris, Maxime Coquelin, Uwe Kleine-König, Sascha Hauer, Michael Turquette Cc: Stephen Boyd, David Laight, Sebastian Reichel, linux-clk, linux-kernel, Carlo Caione When the requested rate is above the parent rate, _div_round_closest() computes a down divider of 0 and divides the parent rate by it. Asking a CLK_DIVIDER_ROUND_CLOSEST divider without CLK_SET_RATE_PARENT for its maximum rate with clk_round_rate(clk, ULONG_MAX) then traps on x86. No divider goes above the parent rate, so the smallest one is the closest. Return it before dividing. Fixes: 26bac95aa88c ("clk: divider: fix selection of divider when rounding to closest") Signed-off-by: Carlo Caione <ccaione@baylibre.com> --- drivers/clk/clk-divider.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index c3f46f9ee451..ed60fa9b04fa 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -254,6 +254,11 @@ static int _div_round_closest(const struct clk_div_table *table, int up, down; unsigned long up_rate, down_rate; + /* No divider goes above the parent rate: the smallest one is the closest */ + if (rate >= parent_rate) + return table && !(flags & CLK_DIVIDER_POWER_OF_TWO) ? + _get_table_mindiv(table) : 1; + up = _get_div_round_up(parent_rate, rate); down = parent_rate / rate; -- 2.56.0 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH v2 2/3] clk: divider: Fix round-closest for rates above the parent rate 2026-11-12 9:37 ` [PATCH v2 2/3] clk: divider: Fix round-closest for rates above the parent rate Carlo Caione @ 2026-11-12 15:41 ` Sebastian Reichel 0 siblings, 0 replies; 9+ messages in thread From: Sebastian Reichel @ 2026-11-12 15:41 UTC (permalink / raw) To: Carlo Caione Cc: Stephen Boyd, Brian Masney, Jerome Brunet, Brian Norris, Maxime Coquelin, Uwe Kleine-König, Sascha Hauer, Michael Turquette, Stephen Boyd, David Laight, linux-clk, linux-kernel [-- Attachment #1: Type: text/plain, Size: 1477 bytes --] Hi, On Thu, Nov 12, 2026 at 10:37:00AM +0100, Carlo Caione wrote: > When the requested rate is above the parent rate, _div_round_closest() > computes a down divider of 0 and divides the parent rate by it. Asking a > CLK_DIVIDER_ROUND_CLOSEST divider without CLK_SET_RATE_PARENT for its > maximum rate with clk_round_rate(clk, ULONG_MAX) then traps on x86. > > No divider goes above the parent rate, so the smallest one is the > closest. Return it before dividing. > > Fixes: 26bac95aa88c ("clk: divider: fix selection of divider when rounding to closest") > Signed-off-by: Carlo Caione <ccaione@baylibre.com> > --- Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com> Greetings, -- Sebastian > drivers/clk/clk-divider.c | 5 +++++ > 1 file changed, 5 insertions(+) > > diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c > index c3f46f9ee451..ed60fa9b04fa 100644 > --- a/drivers/clk/clk-divider.c > +++ b/drivers/clk/clk-divider.c > @@ -254,6 +254,11 @@ static int _div_round_closest(const struct clk_div_table *table, > int up, down; > unsigned long up_rate, down_rate; > > + /* No divider goes above the parent rate: the smallest one is the closest */ > + if (rate >= parent_rate) > + return table && !(flags & CLK_DIVIDER_POWER_OF_TWO) ? > + _get_table_mindiv(table) : 1; > + > up = _get_div_round_up(parent_rate, rate); > down = parent_rate / rate; > > > -- > 2.56.0 > [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 833 bytes --] ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v2 3/3] clk: divider: test: Add tests for rates above 4.29 GHz 2026-11-12 9:36 [PATCH v2 0/3] Fix clk-divider rates above 4.29 GHz on 64-bit Carlo Caione 2026-11-12 9:36 ` [PATCH v2 1/3] clk: divider: Fix the divisor for rates above 4.29 GHz Carlo Caione 2026-11-12 9:37 ` [PATCH v2 2/3] clk: divider: Fix round-closest for rates above the parent rate Carlo Caione @ 2026-11-12 9:37 ` Carlo Caione 2 siblings, 0 replies; 9+ messages in thread From: Carlo Caione @ 2026-11-12 9:37 UTC (permalink / raw) To: Stephen Boyd, Brian Masney, Jerome Brunet, Brian Norris, Maxime Coquelin, Uwe Kleine-König, Sascha Hauer, Michael Turquette Cc: Stephen Boyd, David Laight, Sebastian Reichel, linux-clk, linux-kernel, Carlo Caione On 64-bit platforms, the divider truncated rates above 4.29 GHz when computing a divisor. Add tests rounding and setting such rates on a divider with a 6 GHz parent, one for each divisor computation, and rounding the maximum rate (ULONG_MAX). They are skipped on 32-bit platforms, where rates cannot exceed 4.29 GHz. Also test round-closest dividers above their parent rate. Signed-off-by: Carlo Caione <ccaione@baylibre.com> --- drivers/clk/clk-divider_test.c | 244 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 242 insertions(+), 2 deletions(-) diff --git a/drivers/clk/clk-divider_test.c b/drivers/clk/clk-divider_test.c index 8ee1614128d0..734cbdad55dd 100644 --- a/drivers/clk/clk-divider_test.c +++ b/drivers/clk/clk-divider_test.c @@ -1,16 +1,20 @@ // SPDX-License-Identifier: GPL-2.0 /* - * KUnit tests for clk_divider_bestdiv() + * KUnit tests for clk divider */ +#include <kunit/clk.h> #include <kunit/test.h> #include <linux/clk.h> #include <linux/clk-provider.h> +#include <linux/io.h> #include <linux/limits.h> #include <linux/units.h> #define PARENT_RATE_1GHZ GIGA #define PARENT_RATE_2GHZ (2 * GIGA) #define PARENT_RATE_4GHZ (4 * GIGA) +#define PARENT_RATE_6GHZ (6 * GIGA) +#define PARENT_RATE_10GHZ (10 * GIGA) KUNIT_DEFINE_ACTION_WRAPPER(clk_hw_unregister_fixed_rate_wrapper, clk_hw_unregister_fixed_rate, struct clk_hw *); @@ -147,7 +151,243 @@ static struct kunit_suite clk_divider_bestdiv_test_suite = { .test_cases = clk_divider_bestdiv_test_cases, }; -kunit_test_suite(clk_divider_bestdiv_test_suite); +/* + * Register a fixed-rate parent at @parent_rate and a 3-bit divider (/1 to + * /8) using @fake_reg as its register. + */ +static struct clk_hw *clk_divider_large_rate_init(struct kunit *test, + u32 *fake_reg, + unsigned long parent_rate, + unsigned long flags, + u8 div_flags) +{ + struct clk_hw *parent_hw, *div_hw; + + if (BITS_PER_LONG < 64) + kunit_skip(test, "rates above 4.29 GHz need a 64-bit unsigned long"); + + parent_hw = clk_hw_register_fixed_rate(NULL, "large-rate-parent", + NULL, 0, parent_rate); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent_hw); + KUNIT_ASSERT_EQ(test, 0, + kunit_add_action_or_reset(test, clk_hw_unregister_fixed_rate_wrapper, + parent_hw)); + + div_hw = clk_hw_register_divider(NULL, "large-rate-div", + "large-rate-parent", flags, + (void __iomem __force *)fake_reg, + 0, 3, div_flags, NULL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, div_hw); + KUNIT_ASSERT_EQ(test, 0, + kunit_add_action_or_reset(test, clk_hw_unregister_divider_wrapper, + div_hw)); + + return div_hw; +} + +/* + * Test that rounding a rate above 4.29 GHz computes the divisor from the + * full rate: the parent rate needs /1. + */ +static void clk_divider_large_rate_round_up(struct kunit *test) +{ + struct clk_hw *div_hw; + u32 *fake_reg; + + fake_reg = kunit_kzalloc(test, sizeof(*fake_reg), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fake_reg); + + div_hw = clk_divider_large_rate_init(test, fake_reg, PARENT_RATE_6GHZ, + 0, 0); + + KUNIT_EXPECT_EQ(test, clk_hw_round_rate(div_hw, PARENT_RATE_6GHZ), + PARENT_RATE_6GHZ); +} + +/* + * Same with CLK_DIVIDER_ROUND_CLOSEST: 4.4 GHz is closer to 3 GHz (/2) than + * to 6 GHz (/1). + */ +static void clk_divider_large_rate_round_closest(struct kunit *test) +{ + struct clk_hw *div_hw; + u32 *fake_reg; + + fake_reg = kunit_kzalloc(test, sizeof(*fake_reg), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fake_reg); + + div_hw = clk_divider_large_rate_init(test, fake_reg, PARENT_RATE_6GHZ, + 0, CLK_DIVIDER_ROUND_CLOSEST); + + KUNIT_EXPECT_EQ(test, clk_hw_round_rate(div_hw, 4400 * MEGA), + PARENT_RATE_6GHZ / 2); +} + +/* + * Test that setting a rate above 4.29 GHz programs the right divisor. + * With CLK_SET_RATE_PARENT the rounding searches the parent rates and + * finds /1, so this only checks the set_rate path. + */ +static void clk_divider_large_rate_set_rate(struct kunit *test) +{ + struct clk_hw *div_hw; + struct clk *clk; + u32 *fake_reg; + + fake_reg = kunit_kzalloc(test, sizeof(*fake_reg), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fake_reg); + + /* Start from /2 */ + writel(1, (void __iomem __force *)fake_reg); + + div_hw = clk_divider_large_rate_init(test, fake_reg, PARENT_RATE_6GHZ, + CLK_SET_RATE_PARENT, 0); + clk = clk_hw_get_clk_kunit(test, div_hw, NULL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, clk); + + KUNIT_ASSERT_EQ(test, clk_set_rate(clk, PARENT_RATE_6GHZ), 0); + KUNIT_EXPECT_EQ(test, clk_get_rate(clk), PARENT_RATE_6GHZ); + KUNIT_EXPECT_EQ(test, *fake_reg, 0); +} + +/* + * Round ULONG_MAX, the usual way to ask for the maximum rate, on a divider + * with a @parent_rate parent: it must return the parent rate. + */ +static void clk_divider_large_rate_check_max(struct kunit *test, + unsigned long parent_rate, + u8 div_flags) +{ + struct clk_hw *div_hw; + u32 *fake_reg; + + fake_reg = kunit_kzalloc(test, sizeof(*fake_reg), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fake_reg); + + div_hw = clk_divider_large_rate_init(test, fake_reg, parent_rate, 0, + div_flags); + + KUNIT_EXPECT_EQ(test, clk_hw_round_rate(div_hw, ULONG_MAX), parent_rate); +} + +static void clk_divider_large_rate_round_max(struct kunit *test) +{ + clk_divider_large_rate_check_max(test, PARENT_RATE_6GHZ, 0); +} + +/* Same with a parent more than twice the 32-bit limit */ +static void clk_divider_large_rate_round_max_10ghz(struct kunit *test) +{ + clk_divider_large_rate_check_max(test, PARENT_RATE_10GHZ, 0); +} + +/* Same with CLK_DIVIDER_ROUND_CLOSEST */ +static void clk_divider_large_rate_round_max_closest(struct kunit *test) +{ + clk_divider_large_rate_check_max(test, PARENT_RATE_6GHZ, + CLK_DIVIDER_ROUND_CLOSEST); +} + +/* Same with CLK_DIVIDER_POWER_OF_TWO and a 1 GHz parent */ +static void clk_divider_large_rate_round_max_pow2(struct kunit *test) +{ + clk_divider_large_rate_check_max(test, PARENT_RATE_1GHZ, + CLK_DIVIDER_POWER_OF_TWO); +} + +static struct kunit_case clk_divider_large_rate_test_cases[] = { + KUNIT_CASE(clk_divider_large_rate_round_up), + KUNIT_CASE(clk_divider_large_rate_round_closest), + KUNIT_CASE(clk_divider_large_rate_set_rate), + KUNIT_CASE(clk_divider_large_rate_round_max), + KUNIT_CASE(clk_divider_large_rate_round_max_10ghz), + KUNIT_CASE(clk_divider_large_rate_round_max_closest), + KUNIT_CASE(clk_divider_large_rate_round_max_pow2), + {} +}; + +static struct kunit_suite clk_divider_large_rate_test_suite = { + .name = "clk_divider_large_rate", + .test_cases = clk_divider_large_rate_test_cases, +}; + +/* + * Register a 1 GHz fixed-rate parent and a round-closest 3-bit divider using + * @fake_reg as its register and @table, if not NULL, as its divider table. + */ +static struct clk_hw *clk_divider_above_parent_init(struct kunit *test, + u32 *fake_reg, + const struct clk_div_table *table) +{ + struct clk_hw *parent_hw, *div_hw; + + parent_hw = clk_hw_register_fixed_rate(NULL, "above-parent-parent", + NULL, 0, PARENT_RATE_1GHZ); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent_hw); + KUNIT_ASSERT_EQ(test, 0, + kunit_add_action_or_reset(test, clk_hw_unregister_fixed_rate_wrapper, + parent_hw)); + + div_hw = clk_hw_register_divider_table(NULL, "above-parent-div", + "above-parent-parent", 0, + (void __iomem __force *)fake_reg, + 0, 3, CLK_DIVIDER_ROUND_CLOSEST, + table, NULL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, div_hw); + KUNIT_ASSERT_EQ(test, 0, + kunit_add_action_or_reset(test, clk_hw_unregister_divider_wrapper, + div_hw)); + + return div_hw; +} + +/* + * Test that a round-closest divider asked for more than its parent rate + * picks its smallest divider: /1. + */ +static void clk_divider_above_parent_round_closest(struct kunit *test) +{ + struct clk_hw *div_hw; + u32 *fake_reg; + + fake_reg = kunit_kzalloc(test, sizeof(*fake_reg), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fake_reg); + + div_hw = clk_divider_above_parent_init(test, fake_reg, NULL); + + KUNIT_EXPECT_EQ(test, clk_hw_round_rate(div_hw, PARENT_RATE_2GHZ), + PARENT_RATE_1GHZ); +} + +/* Same with a divider table, whose smallest divider is /2 */ +static void clk_divider_above_parent_round_closest_table(struct kunit *test) +{ + struct clk_hw *div_hw; + u32 *fake_reg; + + fake_reg = kunit_kzalloc(test, sizeof(*fake_reg), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fake_reg); + + div_hw = clk_divider_above_parent_init(test, fake_reg, bestdiv_table); + + KUNIT_EXPECT_EQ(test, clk_hw_round_rate(div_hw, PARENT_RATE_2GHZ), + PARENT_RATE_1GHZ / 2); +} + +static struct kunit_case clk_divider_above_parent_test_cases[] = { + KUNIT_CASE(clk_divider_above_parent_round_closest), + KUNIT_CASE(clk_divider_above_parent_round_closest_table), + {} +}; + +static struct kunit_suite clk_divider_above_parent_test_suite = { + .name = "clk_divider_above_parent", + .test_cases = clk_divider_above_parent_test_cases, +}; + +kunit_test_suites(&clk_divider_bestdiv_test_suite, + &clk_divider_large_rate_test_suite, + &clk_divider_above_parent_test_suite); MODULE_DESCRIPTION("KUnit tests for clk divider"); MODULE_LICENSE("GPL"); -- 2.56.0 ^ permalink raw reply related [flat|nested] 9+ messages in thread
end of thread, other threads:[~2026-11-12 15:42 UTC | newest] Thread overview: 9+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-11-12 9:36 [PATCH v2 0/3] Fix clk-divider rates above 4.29 GHz on 64-bit Carlo Caione 2026-11-12 9:36 ` [PATCH v2 1/3] clk: divider: Fix the divisor for rates above 4.29 GHz Carlo Caione 2026-11-12 15:09 ` Brian Masney 2026-11-12 15:29 ` Carlo Caione 2026-11-12 15:38 ` Sebastian Reichel 2026-11-12 15:34 ` Sebastian Reichel 2026-11-12 9:37 ` [PATCH v2 2/3] clk: divider: Fix round-closest for rates above the parent rate Carlo Caione 2026-11-12 15:41 ` Sebastian Reichel 2026-11-12 9:37 ` [PATCH v2 3/3] clk: divider: test: Add tests for rates above 4.29 GHz Carlo Caione
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox