Add CLK_DIVIDER_EVEN_INTEGERS flag to support divisor of 2, 4, 6, etc.
The same divisor can be done using a table, which would be big and
wasteful for a clock dividor of width 8 (256 entries).
Require increasing flags size from u8 to u16 because
CLK_DIVIDER_EVEN_INTEGERS is the eighth flag.
Signed-off-by: Théo Lebrun <theo.lebrun@bootlin.com>
---
drivers/clk/clk-divider.c | 12 +++++++++---
include/linux/clk-provider.h | 11 +++++++----
2 files changed, 16 insertions(+), 7 deletions(-)
diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
index a2c2b5203b0a..b6654c5c36d2 100644
--- a/drivers/clk/clk-divider.c
+++ b/drivers/clk/clk-divider.c
@@ -72,6 +72,8 @@ static unsigned int _get_maxdiv(const struct clk_div_table *table, u8 width,
return clk_div_mask(width);
if (flags & CLK_DIVIDER_POWER_OF_TWO)
return 1 << clk_div_mask(width);
+ if (flags & CLK_DIVIDER_EVEN_INTEGERS)
+ return 2 * (clk_div_mask(width) + 1);
if (table)
return _get_table_maxdiv(table, width);
return clk_div_mask(width) + 1;
@@ -97,6 +99,8 @@ static unsigned int _get_div(const struct clk_div_table *table,
return 1 << val;
if (flags & CLK_DIVIDER_MAX_AT_ZERO)
return val ? val : clk_div_mask(width) + 1;
+ if (flags & CLK_DIVIDER_EVEN_INTEGERS)
+ return 2 * (val + 1);
if (table)
return _get_table_div(table, val);
return val + 1;
@@ -122,6 +126,8 @@ static unsigned int _get_val(const struct clk_div_table *table,
return __ffs(div);
if (flags & CLK_DIVIDER_MAX_AT_ZERO)
return (div == clk_div_mask(width) + 1) ? 0 : div;
+ if (flags & CLK_DIVIDER_EVEN_INTEGERS)
+ return (div >> 1) - 1;
if (table)
return _get_table_val(table, div);
return div - 1;
@@ -538,7 +544,7 @@ struct clk_hw *__clk_hw_register_divider(struct device *dev,
struct device_node *np, const char *name,
const char *parent_name, const struct clk_hw *parent_hw,
const struct clk_parent_data *parent_data, unsigned long flags,
- void __iomem *reg, u8 shift, u8 width, u8 clk_divider_flags,
+ void __iomem *reg, u8 shift, u8 width, u16 clk_divider_flags,
const struct clk_div_table *table, spinlock_t *lock)
{
struct clk_divider *div;
@@ -610,7 +616,7 @@ EXPORT_SYMBOL_GPL(__clk_hw_register_divider);
struct clk *clk_register_divider_table(struct device *dev, const char *name,
const char *parent_name, unsigned long flags,
void __iomem *reg, u8 shift, u8 width,
- u8 clk_divider_flags, const struct clk_div_table *table,
+ u16 clk_divider_flags, const struct clk_div_table *table,
spinlock_t *lock)
{
struct clk_hw *hw;
@@ -664,7 +670,7 @@ struct clk_hw *__devm_clk_hw_register_divider(struct device *dev,
struct device_node *np, const char *name,
const char *parent_name, const struct clk_hw *parent_hw,
const struct clk_parent_data *parent_data, unsigned long flags,
- void __iomem *reg, u8 shift, u8 width, u8 clk_divider_flags,
+ void __iomem *reg, u8 shift, u8 width, u16 clk_divider_flags,
const struct clk_div_table *table, spinlock_t *lock)
{
struct clk_hw **ptr, *hw;
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index 4a537260f655..cb348e502e41 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -675,13 +675,15 @@ struct clk_div_table {
* CLK_DIVIDER_BIG_ENDIAN - By default little endian register accesses are used
* for the divider register. Setting this flag makes the register accesses
* big endian.
+ * CLK_DIVIDER_EVEN_INTEGERS - clock divisor is 2, 4, 6, 8, 10, etc.
+ * Formula is 2 * (value read from hardware + 1).
*/
struct clk_divider {
struct clk_hw hw;
void __iomem *reg;
u8 shift;
u8 width;
- u8 flags;
+ u16 flags;
const struct clk_div_table *table;
spinlock_t *lock;
};
@@ -697,6 +699,7 @@ struct clk_divider {
#define CLK_DIVIDER_READ_ONLY BIT(5)
#define CLK_DIVIDER_MAX_AT_ZERO BIT(6)
#define CLK_DIVIDER_BIG_ENDIAN BIT(7)
+#define CLK_DIVIDER_EVEN_INTEGERS BIT(8)
extern const struct clk_ops clk_divider_ops;
extern const struct clk_ops clk_divider_ro_ops;
@@ -726,18 +729,18 @@ struct clk_hw *__clk_hw_register_divider(struct device *dev,
struct device_node *np, const char *name,
const char *parent_name, const struct clk_hw *parent_hw,
const struct clk_parent_data *parent_data, unsigned long flags,
- void __iomem *reg, u8 shift, u8 width, u8 clk_divider_flags,
+ void __iomem *reg, u8 shift, u8 width, u16 clk_divider_flags,
const struct clk_div_table *table, spinlock_t *lock);
struct clk_hw *__devm_clk_hw_register_divider(struct device *dev,
struct device_node *np, const char *name,
const char *parent_name, const struct clk_hw *parent_hw,
const struct clk_parent_data *parent_data, unsigned long flags,
- void __iomem *reg, u8 shift, u8 width, u8 clk_divider_flags,
+ void __iomem *reg, u8 shift, u8 width, u16 clk_divider_flags,
const struct clk_div_table *table, spinlock_t *lock);
struct clk *clk_register_divider_table(struct device *dev, const char *name,
const char *parent_name, unsigned long flags,
void __iomem *reg, u8 shift, u8 width,
- u8 clk_divider_flags, const struct clk_div_table *table,
+ u16 clk_divider_flags, const struct clk_div_table *table,
spinlock_t *lock);
/**
* clk_register_divider - register a divider clock with the clock framework
--
2.44.0
Quoting Théo Lebrun (2024-04-10 10:12:33)
> index 4a537260f655..cb348e502e41 100644
> --- a/include/linux/clk-provider.h
> +++ b/include/linux/clk-provider.h
> @@ -675,13 +675,15 @@ struct clk_div_table {
> * CLK_DIVIDER_BIG_ENDIAN - By default little endian register accesses are used
> * for the divider register. Setting this flag makes the register accesses
> * big endian.
> + * CLK_DIVIDER_EVEN_INTEGERS - clock divisor is 2, 4, 6, 8, 10, etc.
> + * Formula is 2 * (value read from hardware + 1).
> */
> struct clk_divider {
> struct clk_hw hw;
> void __iomem *reg;
> u8 shift;
> u8 width;
> - u8 flags;
> + u16 flags;
This can stay u8
> const struct clk_div_table *table;
> spinlock_t *lock;
> };
We should add a kunit test.
Hello,
On Thu Apr 11, 2024 at 5:06 AM CEST, Stephen Boyd wrote:
> Quoting Théo Lebrun (2024-04-10 10:12:33)
> > index 4a537260f655..cb348e502e41 100644
> > --- a/include/linux/clk-provider.h
> > +++ b/include/linux/clk-provider.h
> > @@ -675,13 +675,15 @@ struct clk_div_table {
> > * CLK_DIVIDER_BIG_ENDIAN - By default little endian register accesses are used
> > * for the divider register. Setting this flag makes the register accesses
> > * big endian.
> > + * CLK_DIVIDER_EVEN_INTEGERS - clock divisor is 2, 4, 6, 8, 10, etc.
> > + * Formula is 2 * (value read from hardware + 1).
> > */
> > struct clk_divider {
> > struct clk_hw hw;
> > void __iomem *reg;
> > u8 shift;
> > u8 width;
> > - u8 flags;
> > + u16 flags;
>
> This can stay u8
It is unclear to me why it can stay u8? __clk_hw_register_divider() puts
clk_divider_flags into flags field of struct clk_divider.
BIT(8) overflows u8.
>
> > const struct clk_div_table *table;
> > spinlock_t *lock;
> > };
>
> We should add a kunit test.
Will look into how this works and try something for next revision. I
guess you are talking about adding clk_divider tests, not only tests
for this flag? I cannot find any existing kunit tests for clk_divider.
Thanks,
--
Théo Lebrun, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
Quoting Théo Lebrun (2024-04-11 03:14:09)
> Hello,
>
> On Thu Apr 11, 2024 at 5:06 AM CEST, Stephen Boyd wrote:
> > Quoting Théo Lebrun (2024-04-10 10:12:33)
> > > index 4a537260f655..cb348e502e41 100644
> > > --- a/include/linux/clk-provider.h
> > > +++ b/include/linux/clk-provider.h
> > > @@ -675,13 +675,15 @@ struct clk_div_table {
> > > * CLK_DIVIDER_BIG_ENDIAN - By default little endian register accesses are used
> > > * for the divider register. Setting this flag makes the register accesses
> > > * big endian.
> > > + * CLK_DIVIDER_EVEN_INTEGERS - clock divisor is 2, 4, 6, 8, 10, etc.
> > > + * Formula is 2 * (value read from hardware + 1).
> > > */
> > > struct clk_divider {
> > > struct clk_hw hw;
> > > void __iomem *reg;
> > > u8 shift;
> > > u8 width;
> > > - u8 flags;
> > > + u16 flags;
> >
> > This can stay u8
>
> It is unclear to me why it can stay u8? __clk_hw_register_divider() puts
> clk_divider_flags into flags field of struct clk_divider.
> BIT(8) overflows u8.
Oh, I missed that part.
>
> >
> > > const struct clk_div_table *table;
> > > spinlock_t *lock;
> > > };
> >
> > We should add a kunit test.
>
> Will look into how this works and try something for next revision. I
> guess you are talking about adding clk_divider tests, not only tests
> for this flag? I cannot find any existing kunit tests for clk_divider.
>
Right, there aren't any tests today. Thanks.
© 2016 - 2026 Red Hat, Inc.