[PATCH v2 03/12] clk: divider: Introduce CLK_DIVIDER_EVEN_INTEGERS_NO_OFFSET flag

From: Luo Jie

Date: Fri Aug 07 2026 - 02:55:25 EST


Some hardware dividers derive an even divisor directly from the raw
register value, i.e. divisor = 2 * val, with no +1 offset. This differs
from CLK_DIVIDER_EVEN_INTEGERS, which is one-based (divisor = 2 *
(val + 1)).

Add CLK_DIVIDER_EVEN_INTEGERS_NO_OFFSET for this variant, updating
_get_maxdiv()/_get_div()/_get_val() accordingly. _is_valid_div() is
extended to also reject odd divisors, since _get_val()'s `div >> 1`
would otherwise silently decode them to the wrong even value (e.g. 3
-> 1 -> 2).

To avoid regressing rate requests that previously computed an odd
divisor (which used to succeed with a silently wrong rate), round to
the nearest even divisor in _div_round_up(), _div_round_closest(), and
_next_div(), mirroring how CLK_DIVIDER_POWER_OF_TWO already rounds to
the nearest power of two.

Also guard divider_ro_determine_rate() against a zero divisor, which
can occur from an unprogrammed/reset register, the same way
divider_recalc_rate() already does: WARN unless CLK_DIVIDER_ALLOW_ZERO
is set, and return -EINVAL.

Signed-off-by: Luo Jie <jie.luo@xxxxxxxxxxxxxxxx>
---
drivers/clk/clk-divider.c | 22 ++++++++++++++++++++++
include/linux/clk-provider.h | 3 +++
2 files changed, 25 insertions(+)

diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
index b3b485d23ea8..f062d77ee106 100644
--- a/drivers/clk/clk-divider.c
+++ b/drivers/clk/clk-divider.c
@@ -74,6 +74,8 @@ static unsigned int _get_maxdiv(const struct clk_div_table *table, u8 width,
return 1 << clk_div_mask(width);
if (flags & CLK_DIVIDER_EVEN_INTEGERS)
return 2 * (clk_div_mask(width) + 1);
+ if (flags & CLK_DIVIDER_EVEN_INTEGERS_NO_OFFSET)
+ return 2 * clk_div_mask(width);
if (table)
return _get_table_maxdiv(table, width);
return clk_div_mask(width) + 1;
@@ -101,6 +103,8 @@ static unsigned int _get_div(const struct clk_div_table *table,
return val ? val : clk_div_mask(width) + 1;
if (flags & CLK_DIVIDER_EVEN_INTEGERS)
return 2 * (val + 1);
+ if (flags & CLK_DIVIDER_EVEN_INTEGERS_NO_OFFSET)
+ return 2 * val;
if (table)
return _get_table_div(table, val);
return val + 1;
@@ -128,6 +132,8 @@ static unsigned int _get_val(const struct clk_div_table *table,
return (div == clk_div_mask(width) + 1) ? 0 : div;
if (flags & CLK_DIVIDER_EVEN_INTEGERS)
return (div >> 1) - 1;
+ if (flags & CLK_DIVIDER_EVEN_INTEGERS_NO_OFFSET)
+ return div >> 1;
if (table)
return _get_table_val(table, div);
return div - 1;
@@ -181,6 +187,8 @@ static bool _is_valid_div(const struct clk_div_table *table, unsigned int div,
{
if (flags & CLK_DIVIDER_POWER_OF_TWO)
return is_power_of_2(div);
+ if (flags & CLK_DIVIDER_EVEN_INTEGERS_NO_OFFSET)
+ return div >= 2 && !(div & 1);
if (table)
return _is_valid_table_div(table, div);
return true;
@@ -230,6 +238,8 @@ static int _div_round_up(const struct clk_div_table *table,

if (flags & CLK_DIVIDER_POWER_OF_TWO)
div = __roundup_pow_of_two(div);
+ else if (flags & CLK_DIVIDER_EVEN_INTEGERS_NO_OFFSET)
+ div = max(2, (div + 1) & ~1);
if (table)
div = _round_up_table(table, div);

@@ -249,6 +259,9 @@ static int _div_round_closest(const struct clk_div_table *table,
if (flags & CLK_DIVIDER_POWER_OF_TWO) {
up = __roundup_pow_of_two(up);
down = __rounddown_pow_of_two(down);
+ } else if (flags & CLK_DIVIDER_EVEN_INTEGERS_NO_OFFSET) {
+ up = max(2, (up + 1) & ~1);
+ down = max(2, down & ~1);
} else if (table) {
up = _round_up_table(table, up);
down = _round_down_table(table, down);
@@ -286,6 +299,8 @@ static int _next_div(const struct clk_div_table *table, int div,

if (flags & CLK_DIVIDER_POWER_OF_TWO)
return __roundup_pow_of_two(div);
+ if (flags & CLK_DIVIDER_EVEN_INTEGERS_NO_OFFSET)
+ return div + (div & 1);
if (table)
return _round_up_table(table, div);

@@ -372,6 +387,13 @@ int divider_ro_determine_rate(struct clk_hw *hw, struct clk_rate_request *req,

div = _get_div(table, val, flags, width);

+ if (!div) {
+ WARN(!(flags & CLK_DIVIDER_ALLOW_ZERO),
+ "%s: Zero divisor and CLK_DIVIDER_ALLOW_ZERO not set\n",
+ clk_hw_get_name(hw));
+ return -EINVAL;
+ }
+
/* Even a read-only clock can propagate a rate change */
if (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) {
if (!req->best_parent_hw)
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index b01a38fef8cf..368970d6a12a 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -703,6 +703,8 @@ struct clk_div_table {
* big endian.
* CLK_DIVIDER_EVEN_INTEGERS - clock divisor is 2, 4, 6, 8, 10, etc.
* Formula is 2 * (value read from hardware + 1).
+ * CLK_DIVIDER_EVEN_INTEGERS_NO_OFFSET - clock divisor is 2, 4, 6, 8, etc.
+ * Formula is 2 * (value read from hardware).
*/
struct clk_divider {
struct clk_hw hw;
@@ -726,6 +728,7 @@ struct clk_divider {
#define CLK_DIVIDER_MAX_AT_ZERO BIT(6)
#define CLK_DIVIDER_BIG_ENDIAN BIT(7)
#define CLK_DIVIDER_EVEN_INTEGERS BIT(8)
+#define CLK_DIVIDER_EVEN_INTEGERS_NO_OFFSET BIT(9)

extern const struct clk_ops clk_divider_ops;
extern const struct clk_ops clk_divider_ro_ops;

--
2.43.0