* [PATCH v5 0/2] clk: en7523: full clk_hw + set_rate for en7581 @ 2026-11-10 0:11 Christian Marangi 2026-11-10 0:11 ` [PATCH v5 1/2] clk: en7523: convert to full clk_hw implementation " Christian Marangi 2026-11-10 0:11 ` [PATCH v5 2/2] clk: en7523: add support for .set_rate " Christian Marangi 0 siblings, 2 replies; 6+ messages in thread From: Christian Marangi @ 2026-11-10 0:11 UTC (permalink / raw) To: Stephen Boyd, Brian Masney, Jerome Brunet, linux-clk, linux-kernel Cc: Christian Marangi This simple patch convert the en7523 clk for EN7581 to full clk and adds the .set_rate OPs. Needed mostly to configure MDC clk rate. This will be also implemented by AN7583 since they share the same exact implementation. Changes v5: - Handle requesting 0 rate Changes v4: - Fix regression where base_bits is 0 - Handle divider_get_val returning -EINVAL Changes v3: - Rework to clock divisor API - Return error instead of -EINVAL - Use dev_err where possible - Use devm_kzalloc_obj - Revert to not fail on clk registration failure Changes v2: - Fix recalc_rate return value on error Christian Marangi (2): clk: en7523: convert to full clk_hw implementation for en7581 clk: en7523: add support for .set_rate for en7581 drivers/clk/clk-en7523.c | 301 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 281 insertions(+), 20 deletions(-) -- 2.55.0 ^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v5 1/2] clk: en7523: convert to full clk_hw implementation for en7581 2026-11-10 0:11 [PATCH v5 0/2] clk: en7523: full clk_hw + set_rate for en7581 Christian Marangi @ 2026-11-10 0:11 ` Christian Marangi 2026-11-11 23:55 ` Brian Masney 2026-11-10 0:11 ` [PATCH v5 2/2] clk: en7523: add support for .set_rate " Christian Marangi 1 sibling, 1 reply; 6+ messages in thread From: Christian Marangi @ 2026-11-10 0:11 UTC (permalink / raw) To: Stephen Boyd, Brian Masney, Jerome Brunet, linux-clk, linux-kernel Cc: Christian Marangi In preparation for support of .set_rate, convert the clock register logic from fixed clock implementation to full clk_hw implementation with dedicated OPs for Airoha EN7581 SoC. This is just a rework and no behaviour change is expected. Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> --- drivers/clk/clk-en7523.c | 75 +++++++++++++++++++++++++++++----------- 1 file changed, 55 insertions(+), 20 deletions(-) diff --git a/drivers/clk/clk-en7523.c b/drivers/clk/clk-en7523.c index 24fdec2e6198..6640e814d36a 100644 --- a/drivers/clk/clk-en7523.c +++ b/drivers/clk/clk-en7523.c @@ -126,6 +126,12 @@ struct en_clk_gate { struct clk_hw hw; }; +struct en_clk { + struct regmap *map; + const struct en_clk_desc *desc; + struct clk_hw hw; +}; + struct en_rst_data { const u16 *bank_ofs; const u16 *idx_map; @@ -742,42 +748,71 @@ static int en7523_clk_hw_init(struct platform_device *pdev, en7581_rst_ofs); } +static unsigned long en7581_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct en_clk *c = container_of(hw, struct en_clk, hw); + const struct en_clk_desc *desc = c->desc; + struct device *dev = clk_hw_get_dev(hw); + struct regmap *map = c->map; + u32 val, reg; + u32 rate; + int err; + + err = regmap_read(map, desc->base_reg, &val); + if (err) { + dev_err(dev, "Failed reading fixed clk rate %s: %d\n", + desc->name, err); + return 0; + } + rate = en7523_get_base_rate(desc, val); + + reg = desc->div_reg ? desc->div_reg : desc->base_reg; + err = regmap_read(map, reg, &val); + if (err) { + dev_err(dev, "Failed reading fixed clk div %s: %d\n", + desc->name, err); + return 0; + } + + return rate / en7523_get_div(desc, val); +} + +static const struct clk_ops en7581_clk_ops = { + .recalc_rate = en7581_recalc_rate, +}; + static void en7581_register_clocks(struct device *dev, struct clk_hw_onecell_data *clk_data, struct regmap *map, void __iomem *base) { struct clk_hw *hw; - u32 rate; int i; for (i = 0; i < ARRAY_SIZE(en7581_base_clks); i++) { const struct en_clk_desc *desc = &en7581_base_clks[i]; - u32 val, reg = desc->div_reg ? desc->div_reg : desc->base_reg; + struct clk_init_data init = { + .ops = &en7581_clk_ops, + }; + struct en_clk *en_clk; int err; - err = regmap_read(map, desc->base_reg, &val); - if (err) { - pr_err("Failed reading fixed clk rate %s: %d\n", - desc->name, err); + en_clk = devm_kzalloc_obj(dev, *en_clk); + if (!en_clk) continue; - } - rate = en7523_get_base_rate(desc, val); - err = regmap_read(map, reg, &val); - if (err) { - pr_err("Failed reading fixed clk div %s: %d\n", - desc->name, err); - continue; - } - rate /= en7523_get_div(desc, val); + init.name = desc->name; + en_clk->map = map; + en_clk->desc = desc; + en_clk->hw.init = &init; - hw = clk_hw_register_fixed_rate(dev, desc->name, NULL, 0, rate); - if (IS_ERR(hw)) { - pr_err("Failed to register clk %s: %ld\n", - desc->name, PTR_ERR(hw)); + err = devm_clk_hw_register(dev, &en_clk->hw); + if (err) { + dev_err_probe(dev, err, "Failed to register clk %s\n", + desc->name); continue; } - clk_data->hws[desc->id] = hw; + clk_data->hws[desc->id] = &en_clk->hw; } hw = en7523_register_pcie_clk(dev, base); -- 2.55.0 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH v5 1/2] clk: en7523: convert to full clk_hw implementation for en7581 2026-11-10 0:11 ` [PATCH v5 1/2] clk: en7523: convert to full clk_hw implementation " Christian Marangi @ 2026-11-11 23:55 ` Brian Masney 0 siblings, 0 replies; 6+ messages in thread From: Brian Masney @ 2026-11-11 23:55 UTC (permalink / raw) To: Christian Marangi Cc: Stephen Boyd, Brian Masney, Jerome Brunet, linux-clk, linux-kernel, Brian Masney Hi Christian, > In preparation for support of .set_rate, convert the clock register > logic from fixed clock implementation to full clk_hw implementation with > dedicated OPs for Airoha EN7581 SoC. > > This is just a rework and no behaviour change is expected. > > Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> > --- > drivers/clk/clk-en7523.c | 75 +++++++++++++++++++++++++++++----------- > 1 file changed, 55 insertions(+), 20 deletions(-) > > diff --git a/drivers/clk/clk-en7523.c b/drivers/clk/clk-en7523.c > index 24fdec2e6198..6640e814d36a 100644 > --- a/drivers/clk/clk-en7523.c > +++ b/drivers/clk/clk-en7523.c > @@ -126,6 +126,12 @@ struct en_clk_gate { > struct clk_hw hw; > }; > > +struct en_clk { > + struct regmap *map; > + const struct en_clk_desc *desc; > + struct clk_hw hw; > +}; > + > struct en_rst_data { > const u16 *bank_ofs; > const u16 *idx_map; > @@ -742,42 +748,71 @@ static int en7523_clk_hw_init(struct platform_device *pdev, > en7581_rst_ofs); > } > > +static unsigned long en7581_recalc_rate(struct clk_hw *hw, > + unsigned long parent_rate) > +{ > + struct en_clk *c = container_of(hw, struct en_clk, hw); > + const struct en_clk_desc *desc = c->desc; > + struct device *dev = clk_hw_get_dev(hw); > + struct regmap *map = c->map; > + u32 val, reg; > + u32 rate; > + int err; > + > + err = regmap_read(map, desc->base_reg, &val); > + if (err) { > + dev_err(dev, "Failed reading fixed clk rate %s: %d\n", > + desc->name, err); > + return 0; > + } > + rate = en7523_get_base_rate(desc, val); > + > + reg = desc->div_reg ? desc->div_reg : desc->base_reg; > + err = regmap_read(map, reg, &val); > + if (err) { > + dev_err(dev, "Failed reading fixed clk div %s: %d\n", > + desc->name, err); > + return 0; > + } > + > + return rate / en7523_get_div(desc, val); > +} > + > +static const struct clk_ops en7581_clk_ops = { > + .recalc_rate = en7581_recalc_rate, > +}; > + > static void en7581_register_clocks(struct device *dev, struct clk_hw_onecell_data *clk_data, > struct regmap *map, void __iomem *base) > { > struct clk_hw *hw; > - u32 rate; > int i; > > for (i = 0; i < ARRAY_SIZE(en7581_base_clks); i++) { > const struct en_clk_desc *desc = &en7581_base_clks[i]; > - u32 val, reg = desc->div_reg ? desc->div_reg : desc->base_reg; > + struct clk_init_data init = { > + .ops = &en7581_clk_ops, > + }; > + struct en_clk *en_clk; > int err; > > - err = regmap_read(map, desc->base_reg, &val); > + en_clk = devm_kzalloc_obj(dev, *en_clk); > + if (!en_clk) > + continue; return -ENOMEM; Brian -- Brian Masney <masneyb@kernel.org> ^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v5 2/2] clk: en7523: add support for .set_rate for en7581 2026-11-10 0:11 [PATCH v5 0/2] clk: en7523: full clk_hw + set_rate for en7581 Christian Marangi 2026-11-10 0:11 ` [PATCH v5 1/2] clk: en7523: convert to full clk_hw implementation " Christian Marangi @ 2026-11-10 0:11 ` Christian Marangi 2026-11-10 0:33 ` sashiko-bot 2026-11-11 23:55 ` Brian Masney 1 sibling, 2 replies; 6+ messages in thread From: Christian Marangi @ 2026-11-10 0:11 UTC (permalink / raw) To: Stephen Boyd, Brian Masney, Jerome Brunet, linux-clk, linux-kernel Cc: Christian Marangi Add support for EN7523 driver to configure rate for Airoha EN7581 SoC. The SoC expose both base clock selector and clock divisor hence it's possible to change the rate. This will be especially needed for new SoC AN7583 that require changes for the MDIO and the eMMC. The clock were assumed correctly configured by the bootloader but this goes against the rule of "kernel should not depend on external configuration". To support the use of standard clock divider API, clock_div_table are added where needed (BUS, SPI and SLIC) in additionl to the previous info to derive and simulate the clock from the divisor registers. Recalc rate for Airoha EN7581 is updated to use the divider_recalc_rate API for consistency with .set_rate and .determine_rate. Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> --- drivers/clk/clk-en7523.c | 228 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 227 insertions(+), 1 deletion(-) diff --git a/drivers/clk/clk-en7523.c b/drivers/clk/clk-en7523.c index 6640e814d36a..b0aed88528c5 100644 --- a/drivers/clk/clk-en7523.c +++ b/drivers/clk/clk-en7523.c @@ -119,6 +119,8 @@ struct en_clk_desc { u16 div_val0; u8 div_step; u8 div_offset; + unsigned long div_flags; + const struct clk_div_table *div_table; }; struct en_clk_gate { @@ -161,7 +163,88 @@ struct en_clk_soc_data { static const u32 gsw_base[] = { 400000000, 500000000 }; static const u32 emi_base[] = { 333000000, 400000000 }; static const u32 bus_base[] = { 500000000, 540000000 }; +/* From documentation divisor 1 is n/a */ +static const struct clk_div_table bus_div[] = { + { .val = 1, .div = 2 }, + { .val = 2, .div = 3 }, + { .val = 3, .div = 4 }, + { .val = 4, .div = 5 }, + { .val = 5, .div = 6 }, + { .val = 6, .div = 7 }, + { .val = 7, .div = 8 }, + { /* sentinel */ } +}; static const u32 slic_base[] = { 100000000, 3125000 }; +static const struct clk_div_table slic_div[] = { + { .val = 0, .div = 20 }, + { .val = 1, .div = 2 }, + { .val = 2, .div = 4 }, + { .val = 3, .div = 6 }, + { .val = 4, .div = 8 }, + { .val = 5, .div = 10 }, + { .val = 6, .div = 12 }, + { .val = 7, .div = 14 }, + { .val = 8, .div = 16 }, + { .val = 9, .div = 18 }, + { .val = 10, .div = 20 }, + { .val = 11, .div = 22 }, + { .val = 12, .div = 24 }, + { .val = 13, .div = 26 }, + { .val = 14, .div = 28 }, + { .val = 15, .div = 30 }, + { .val = 16, .div = 32 }, + { .val = 17, .div = 34 }, + { .val = 18, .div = 36 }, + { .val = 19, .div = 38 }, + { .val = 20, .div = 40 }, + { .val = 21, .div = 42 }, + { .val = 22, .div = 44 }, + { .val = 23, .div = 46 }, + { .val = 24, .div = 48 }, + { .val = 25, .div = 50 }, + { .val = 26, .div = 52 }, + { .val = 27, .div = 54 }, + { .val = 28, .div = 56 }, + { .val = 29, .div = 58 }, + { .val = 30, .div = 60 }, + { .val = 31, .div = 62 }, + { /* sentinel */ } +}; +static const struct clk_div_table spi_div[] = { + { .val = 0, .div = 40 }, + { .val = 1, .div = 2 }, + { .val = 2, .div = 4 }, + { .val = 3, .div = 6 }, + { .val = 4, .div = 8 }, + { .val = 5, .div = 10 }, + { .val = 6, .div = 12 }, + { .val = 7, .div = 14 }, + { .val = 8, .div = 16 }, + { .val = 9, .div = 18 }, + { .val = 10, .div = 20 }, + { .val = 11, .div = 22 }, + { .val = 12, .div = 24 }, + { .val = 13, .div = 26 }, + { .val = 14, .div = 28 }, + { .val = 15, .div = 30 }, + { .val = 16, .div = 32 }, + { .val = 17, .div = 34 }, + { .val = 18, .div = 36 }, + { .val = 19, .div = 38 }, + { .val = 20, .div = 40 }, + { .val = 21, .div = 42 }, + { .val = 22, .div = 44 }, + { .val = 23, .div = 46 }, + { .val = 24, .div = 48 }, + { .val = 25, .div = 50 }, + { .val = 26, .div = 52 }, + { .val = 27, .div = 54 }, + { .val = 28, .div = 56 }, + { .val = 29, .div = 58 }, + { .val = 30, .div = 60 }, + { .val = 31, .div = 62 }, + { /* sentinel */ } +}; static const u32 npu_base[] = { 333000000, 400000000, 500000000 }; /* EN7581 */ static const u32 emi7581_base[] = { 540000000, 480000000, 400000000, 300000000 }; @@ -215,6 +298,7 @@ static const struct en_clk_desc en7523_base_clks[] = { .div_shift = 0, .div_step = 1, .div_offset = 1, + .div_table = bus_div, }, { .id = EN7523_CLK_SLIC, .name = "slic", @@ -230,6 +314,7 @@ static const struct en_clk_desc en7523_base_clks[] = { .div_shift = 24, .div_val0 = 20, .div_step = 2, + .div_table = slic_div, }, { .id = EN7523_CLK_SPI, .name = "spi", @@ -242,6 +327,7 @@ static const struct en_clk_desc en7523_base_clks[] = { .div_shift = 8, .div_val0 = 40, .div_step = 2, + .div_table = spi_div, }, { .id = EN7523_CLK_NPU, .name = "npu", @@ -311,6 +397,7 @@ static const struct en_clk_desc en7581_base_clks[] = { .div_shift = 0, .div_step = 1, .div_offset = 1, + .div_table = bus_div, }, { .id = EN7523_CLK_SLIC, .name = "slic", @@ -326,6 +413,7 @@ static const struct en_clk_desc en7581_base_clks[] = { .div_shift = 24, .div_val0 = 20, .div_step = 2, + .div_table = slic_div, }, { .id = EN7523_CLK_SPI, .name = "spi", @@ -338,6 +426,7 @@ static const struct en_clk_desc en7581_base_clks[] = { .div_shift = 8, .div_val0 = 40, .div_step = 2, + .div_table = spi_div, }, { .id = EN7523_CLK_NPU, .name = "npu", @@ -775,11 +864,148 @@ static unsigned long en7581_recalc_rate(struct clk_hw *hw, return 0; } - return rate / en7523_get_div(desc, val); + if (desc->div_bits) { + val >>= desc->div_shift; + val &= (1 << desc->div_bits) - 1; + + rate = divider_recalc_rate(hw, rate, val, + desc->div_table, + desc->div_flags, + desc->div_bits); + } + + return rate; +} + +/* + * Simulate rate with every divisor supported + * 1. Search the divisor for the requested rate + * 2. Simulate the final rate with the new divisor + */ +static unsigned long en7581_get_div_for_rate(struct clk_hw *hw, + unsigned long rate, + unsigned long parent_rate, + const struct en_clk_desc *desc, + int *div_val) +{ + *div_val = divider_get_val(rate, parent_rate, desc->div_table, + desc->div_bits, desc->div_flags); + if (*div_val < 0) + return 0; + + return divider_recalc_rate(hw, parent_rate, *div_val, + desc->div_table, desc->div_flags, + desc->div_bits); +} + +static int en7581_get_vals_for_rate(struct clk_hw *hw, + const struct en_clk_desc *desc, + unsigned long rate, + u32 *base_val, u32 *div_val) +{ + struct device *dev = clk_hw_get_dev(hw); + unsigned long parent_rate, tmp_rate; + int tmp_base_val = 0; + int tmp_div_val = 0; + + if (!rate || (!desc->base_bits && !desc->div_bits)) + return -EINVAL; + + if (!desc->base_bits) { + parent_rate = desc->base_value; + + tmp_rate = en7581_get_div_for_rate(hw, rate, parent_rate, + desc, &tmp_div_val); + + /* Matching clock found */ + if (tmp_rate && tmp_rate == rate) + goto exit; + } + + /* Search requested rate with every matching base rate + divisor */ + for (; tmp_base_val < desc->n_base_values; tmp_base_val++) { + parent_rate = desc->base_values[tmp_base_val]; + tmp_rate = parent_rate; + + if (desc->div_bits) + tmp_rate = en7581_get_div_for_rate(hw, rate, parent_rate, + desc, &tmp_div_val); + + /* Matching clock found */ + if (tmp_rate && tmp_rate == rate) + break; + } + + /* Failed to found matching clock, return error */ + if (tmp_base_val >= desc->n_base_values) { + dev_err(dev, "Invalid rate for clock %s\n", + desc->name); + return -EINVAL; + } + +exit: + *base_val = tmp_base_val; + *div_val = tmp_div_val; + + return 0; +} + +static int en7581_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct en_clk *en_clk = container_of(hw, struct en_clk, hw); + u32 div_val, base_val; + + /* Just check if the rate is possible */ + return en7581_get_vals_for_rate(hw, en_clk->desc, req->rate, + &base_val, &div_val); +} + +static int en7581_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct en_clk *en_clk = container_of(hw, struct en_clk, hw); + const struct en_clk_desc *desc = en_clk->desc; + struct device *dev = clk_hw_get_dev(hw); + struct regmap *map = en_clk->map; + u32 base_val, div_val; + u32 reg, val, mask; + int err; + + err = en7581_get_vals_for_rate(hw, en_clk->desc, rate, + &base_val, &div_val); + if (err) + return err; + + if (desc->div_bits) { + reg = desc->div_reg ? desc->div_reg : desc->base_reg; + + mask = (BIT(desc->div_bits) - 1) << desc->div_shift; + val = div_val << desc->div_shift; + + err = regmap_update_bits(map, reg, mask, val); + if (err) + return dev_err_probe(dev, err, "Failed to update div reg for clock %s\n", + desc->name); + } + + if (desc->base_bits) { + mask = (BIT(desc->base_bits) - 1) << desc->base_shift; + val = base_val << desc->base_shift; + + err = regmap_update_bits(map, desc->base_reg, mask, val); + if (err) + return dev_err_probe(dev, err, "Failed to update reg for clock %s\n", + desc->name); + } + + return 0; } static const struct clk_ops en7581_clk_ops = { .recalc_rate = en7581_recalc_rate, + .determine_rate = en7581_determine_rate, + .set_rate = en7581_set_rate, }; static void en7581_register_clocks(struct device *dev, struct clk_hw_onecell_data *clk_data, -- 2.55.0 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH v5 2/2] clk: en7523: add support for .set_rate for en7581 2026-11-10 0:11 ` [PATCH v5 2/2] clk: en7523: add support for .set_rate " Christian Marangi @ 2026-11-10 0:33 ` sashiko-bot 2026-11-11 23:55 ` Brian Masney 1 sibling, 0 replies; 6+ messages in thread From: sashiko-bot @ 2026-11-10 0:33 UTC (permalink / raw) To: Christian Marangi, linux-clk Thank you for your contribution! Sashiko AI review found 1 potential issue to consider: - [Low] clk: en7523: misuse of dev_err_probe() in runtime en7581_set_rate() -- commit 5164adf91e05547277b7acad91e9f5b487a3781f Author: Christian Marangi <ansuelsmth@gmail.com> clk: en7523: add support for .set_rate for en7581 This commit adds clk_div_table definitions for the BUS, SLIC, and SPI clocks and implements .determine_rate and .set_rate callbacks for the Airoha EN7581 SoC. > diff --git a/drivers/clk/clk-en7523.c b/drivers/clk/clk-en7523.c > index 6640e814d36a4..b0aed88528c5d 100644 > --- a/drivers/clk/clk-en7523.c > +++ b/drivers/clk/clk-en7523.c [ ... ] > @@ -775,11 +864,148 @@ static unsigned long en7581_recalc_rate(struct clk_hw *hw, [ ... ] > +static int en7581_set_rate(struct clk_hw *hw, unsigned long rate, > + unsigned long parent_rate) > +{ > + struct en_clk *en_clk = container_of(hw, struct en_clk, hw); > + const struct en_clk_desc *desc = en_clk->desc; > + struct device *dev = clk_hw_get_dev(hw); > + struct regmap *map = en_clk->map; > + u32 base_val, div_val; > + u32 reg, val, mask; > + int err; > + > + err = en7581_get_vals_for_rate(hw, en_clk->desc, rate, > + &base_val, &div_val); > + if (err) > + return err; > + > + if (desc->div_bits) { > + reg = desc->div_reg ? desc->div_reg : desc->base_reg; > + > + mask = (BIT(desc->div_bits) - 1) << desc->div_shift; > + val = div_val << desc->div_shift; > + > + err = regmap_update_bits(map, reg, mask, val); > + if (err) > + return dev_err_probe(dev, err, "Failed to update div reg for clock %s\n", > + desc->name); > + } > + > + if (desc->base_bits) { > + mask = (BIT(desc->base_bits) - 1) << desc->base_shift; > + val = base_val << desc->base_shift; > + > + err = regmap_update_bits(map, desc->base_reg, mask, val); > + if (err) > + return dev_err_probe(dev, err, "Failed to update reg for clock %s\n", > + desc->name); > + } > + > + return 0; > } [Severity: Low] Should en7581_set_rate() use dev_err() instead of dev_err_probe()? en7581_set_rate() is a runtime clk_ops.set_rate callback, whereas dev_err_probe() is only meant for probe paths (suppressing -ENOMEM logs and recording dev->p->deferred_probe_reason on -EPROBE_DEFER), and the sibling runtime callbacks en7581_recalc_rate() and en7581_get_vals_for_rate() use dev_err(). -- Sashiko AI review · https://sashiko.dev/#/patchset/20261110001128.1041-1-ansuelsmth@gmail.com?part=2 ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v5 2/2] clk: en7523: add support for .set_rate for en7581 2026-11-10 0:11 ` [PATCH v5 2/2] clk: en7523: add support for .set_rate " Christian Marangi 2026-11-10 0:33 ` sashiko-bot @ 2026-11-11 23:55 ` Brian Masney 1 sibling, 0 replies; 6+ messages in thread From: Brian Masney @ 2026-11-11 23:55 UTC (permalink / raw) To: Christian Marangi Cc: Stephen Boyd, Brian Masney, Jerome Brunet, linux-clk, linux-kernel, Brian Masney Hi Christian, > Add support for EN7523 driver to configure rate for Airoha EN7581 SoC. > The SoC expose both base clock selector and clock divisor hence it's > possible to change the rate. > > This will be especially needed for new SoC AN7583 that require changes > for the MDIO and the eMMC. > > The clock were assumed correctly configured by the bootloader but this > goes against the rule of "kernel should not depend on external > configuration". > > To support the use of standard clock divider API, clock_div_table are added > where needed (BUS, SPI and SLIC) in additionl to the previous info to > derive and simulate the clock from the divisor registers. > > Recalc rate for Airoha EN7581 is updated to use the divider_recalc_rate API > for consistency with .set_rate and .determine_rate. > > Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> > --- > drivers/clk/clk-en7523.c | 228 ++++++++++++++++++++++++++++++++++++++- > 1 file changed, 227 insertions(+), 1 deletion(-) > > diff --git a/drivers/clk/clk-en7523.c b/drivers/clk/clk-en7523.c > index 6640e814d36a..b0aed88528c5 100644 > --- a/drivers/clk/clk-en7523.c > +++ b/drivers/clk/clk-en7523.c > @@ -119,6 +119,8 @@ struct en_clk_desc { > u16 div_val0; > u8 div_step; > u8 div_offset; > + unsigned long div_flags; Nothing sets this flag. > + const struct clk_div_table *div_table; > }; > > struct en_clk_gate { > @@ -161,7 +163,88 @@ struct en_clk_soc_data { > static const u32 gsw_base[] = { 400000000, 500000000 }; > static const u32 emi_base[] = { 333000000, 400000000 }; > static const u32 bus_base[] = { 500000000, 540000000 }; > +/* From documentation divisor 1 is n/a */ > +static const struct clk_div_table bus_div[] = { > + { .val = 1, .div = 2 }, > + { .val = 2, .div = 3 }, > + { .val = 3, .div = 4 }, > + { .val = 4, .div = 5 }, > + { .val = 5, .div = 6 }, > + { .val = 6, .div = 7 }, > + { .val = 7, .div = 8 }, > + { /* sentinel */ } > +}; > static const u32 slic_base[] = { 100000000, 3125000 }; > +static const struct clk_div_table slic_div[] = { > + { .val = 0, .div = 20 }, > + { .val = 1, .div = 2 }, > + { .val = 2, .div = 4 }, > + { .val = 3, .div = 6 }, > + { .val = 4, .div = 8 }, > + { .val = 5, .div = 10 }, > + { .val = 6, .div = 12 }, > + { .val = 7, .div = 14 }, > + { .val = 8, .div = 16 }, > + { .val = 9, .div = 18 }, > + { .val = 10, .div = 20 }, > + { .val = 11, .div = 22 }, > + { .val = 12, .div = 24 }, > + { .val = 13, .div = 26 }, > + { .val = 14, .div = 28 }, > + { .val = 15, .div = 30 }, > + { .val = 16, .div = 32 }, > + { .val = 17, .div = 34 }, > + { .val = 18, .div = 36 }, > + { .val = 19, .div = 38 }, > + { .val = 20, .div = 40 }, > + { .val = 21, .div = 42 }, > + { .val = 22, .div = 44 }, > + { .val = 23, .div = 46 }, > + { .val = 24, .div = 48 }, > + { .val = 25, .div = 50 }, > + { .val = 26, .div = 52 }, > + { .val = 27, .div = 54 }, > + { .val = 28, .div = 56 }, > + { .val = 29, .div = 58 }, > + { .val = 30, .div = 60 }, > + { .val = 31, .div = 62 }, > + { /* sentinel */ } > +}; > +static const struct clk_div_table spi_div[] = { > + { .val = 0, .div = 40 }, > + { .val = 1, .div = 2 }, > + { .val = 2, .div = 4 }, > + { .val = 3, .div = 6 }, > + { .val = 4, .div = 8 }, > + { .val = 5, .div = 10 }, > + { .val = 6, .div = 12 }, > + { .val = 7, .div = 14 }, > + { .val = 8, .div = 16 }, > + { .val = 9, .div = 18 }, > + { .val = 10, .div = 20 }, > + { .val = 11, .div = 22 }, > + { .val = 12, .div = 24 }, > + { .val = 13, .div = 26 }, > + { .val = 14, .div = 28 }, > + { .val = 15, .div = 30 }, > + { .val = 16, .div = 32 }, > + { .val = 17, .div = 34 }, > + { .val = 18, .div = 36 }, > + { .val = 19, .div = 38 }, > + { .val = 20, .div = 40 }, > + { .val = 21, .div = 42 }, > + { .val = 22, .div = 44 }, > + { .val = 23, .div = 46 }, > + { .val = 24, .div = 48 }, > + { .val = 25, .div = 50 }, > + { .val = 26, .div = 52 }, > + { .val = 27, .div = 54 }, > + { .val = 28, .div = 56 }, > + { .val = 29, .div = 58 }, > + { .val = 30, .div = 60 }, > + { .val = 31, .div = 62 }, > + { /* sentinel */ } > +}; > static const u32 npu_base[] = { 333000000, 400000000, 500000000 }; > /* EN7581 */ > static const u32 emi7581_base[] = { 540000000, 480000000, 400000000, 300000000 }; > @@ -215,6 +298,7 @@ static const struct en_clk_desc en7523_base_clks[] = { > .div_shift = 0, > .div_step = 1, > .div_offset = 1, > + .div_table = bus_div, en7523_get_div() is still present, and en7523_register_clocks() still refers to it. Should it be made generic to use the div_table as well? > }, { > .id = EN7523_CLK_SLIC, > .name = "slic", > @@ -230,6 +314,7 @@ static const struct en_clk_desc en7523_base_clks[] = { > .div_shift = 24, > .div_val0 = 20, > .div_step = 2, > + .div_table = slic_div, > }, { > .id = EN7523_CLK_SPI, > .name = "spi", > @@ -242,6 +327,7 @@ static const struct en_clk_desc en7523_base_clks[] = { > .div_shift = 8, > .div_val0 = 40, > .div_step = 2, > + .div_table = spi_div, > }, { > .id = EN7523_CLK_NPU, > .name = "npu", > @@ -311,6 +397,7 @@ static const struct en_clk_desc en7581_base_clks[] = { > .div_shift = 0, > .div_step = 1, > .div_offset = 1, > + .div_table = bus_div, > }, { > .id = EN7523_CLK_SLIC, > .name = "slic", > @@ -326,6 +413,7 @@ static const struct en_clk_desc en7581_base_clks[] = { > .div_shift = 24, > .div_val0 = 20, > .div_step = 2, > + .div_table = slic_div, > }, { > .id = EN7523_CLK_SPI, > .name = "spi", > @@ -338,6 +426,7 @@ static const struct en_clk_desc en7581_base_clks[] = { > .div_shift = 8, > .div_val0 = 40, > .div_step = 2, > + .div_table = spi_div, > }, { > .id = EN7523_CLK_NPU, > .name = "npu", > @@ -775,11 +864,148 @@ static unsigned long en7581_recalc_rate(struct clk_hw *hw, > return 0; > } > > - return rate / en7523_get_div(desc, val); > + if (desc->div_bits) { > + val >>= desc->div_shift; > + val &= (1 << desc->div_bits) - 1; > + > + rate = divider_recalc_rate(hw, rate, val, > + desc->div_table, > + desc->div_flags, > + desc->div_bits); > + } > + > + return rate; > +} > + > +/* > + * Simulate rate with every divisor supported > + * 1. Search the divisor for the requested rate > + * 2. Simulate the final rate with the new divisor > + */ > +static unsigned long en7581_get_div_for_rate(struct clk_hw *hw, > + unsigned long rate, > + unsigned long parent_rate, > + const struct en_clk_desc *desc, > + int *div_val) > +{ > + *div_val = divider_get_val(rate, parent_rate, desc->div_table, > + desc->div_bits, desc->div_flags); > + if (*div_val < 0) > + return 0; > + > + return divider_recalc_rate(hw, parent_rate, *div_val, > + desc->div_table, desc->div_flags, > + desc->div_bits); > +} > + > +static int en7581_get_vals_for_rate(struct clk_hw *hw, > + const struct en_clk_desc *desc, > + unsigned long rate, > + u32 *base_val, u32 *div_val) > +{ > + struct device *dev = clk_hw_get_dev(hw); > + unsigned long parent_rate, tmp_rate; > + int tmp_base_val = 0; > + int tmp_div_val = 0; > + > + if (!rate || (!desc->base_bits && !desc->div_bits)) > + return -EINVAL; > + > + if (!desc->base_bits) { > + parent_rate = desc->base_value; > + > + tmp_rate = en7581_get_div_for_rate(hw, rate, parent_rate, > + desc, &tmp_div_val); > + > + /* Matching clock found */ > + if (tmp_rate && tmp_rate == rate) is the 'tmp_rate && ' redundant? > + goto exit; > + } > + > + /* Search requested rate with every matching base rate + divisor */ > + for (; tmp_base_val < desc->n_base_values; tmp_base_val++) { > + parent_rate = desc->base_values[tmp_base_val]; > + tmp_rate = parent_rate; > + > + if (desc->div_bits) > + tmp_rate = en7581_get_div_for_rate(hw, rate, parent_rate, > + desc, &tmp_div_val); > + > + /* Matching clock found */ > + if (tmp_rate && tmp_rate == rate) > + break; > + } > + > + /* Failed to found matching clock, return error */ Typically if it can't find an exact rate, then it will return the closest matching rate. As written this means that callers need to know in advance the valid rounded rates. > + if (tmp_base_val >= desc->n_base_values) { > + dev_err(dev, "Invalid rate for clock %s\n", Since this runs from determine_rate(), make this dev_dbg(). > + desc->name); > + return -EINVAL; > + } > + > +exit: > + *base_val = tmp_base_val; > + *div_val = tmp_div_val; > + > + return 0; > +} > + > +static int en7581_determine_rate(struct clk_hw *hw, > + struct clk_rate_request *req) > +{ > + struct en_clk *en_clk = container_of(hw, struct en_clk, hw); > + u32 div_val, base_val; > + > + /* Just check if the rate is possible */ > + return en7581_get_vals_for_rate(hw, en_clk->desc, req->rate, > + &base_val, &div_val); > +} > + > +static int en7581_set_rate(struct clk_hw *hw, unsigned long rate, > + unsigned long parent_rate) > +{ > + struct en_clk *en_clk = container_of(hw, struct en_clk, hw); > + const struct en_clk_desc *desc = en_clk->desc; > + struct device *dev = clk_hw_get_dev(hw); > + struct regmap *map = en_clk->map; > + u32 base_val, div_val; > + u32 reg, val, mask; > + int err; > + > + err = en7581_get_vals_for_rate(hw, en_clk->desc, rate, > + &base_val, &div_val); > + if (err) > + return err; > + > + if (desc->div_bits) { > + reg = desc->div_reg ? desc->div_reg : desc->base_reg; > + > + mask = (BIT(desc->div_bits) - 1) << desc->div_shift; > + val = div_val << desc->div_shift; > + > + err = regmap_update_bits(map, reg, mask, val); > + if (err) > + return dev_err_probe(dev, err, "Failed to update div reg for clock %s\n", > + desc->name); As pointed out by Sashiko, use dev_err() Brian -- Brian Masney <masneyb@kernel.org> ^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-11-11 23:56 UTC | newest] Thread overview: 6+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-11-10 0:11 [PATCH v5 0/2] clk: en7523: full clk_hw + set_rate for en7581 Christian Marangi 2026-11-10 0:11 ` [PATCH v5 1/2] clk: en7523: convert to full clk_hw implementation " Christian Marangi 2026-11-11 23:55 ` Brian Masney 2026-11-10 0:11 ` [PATCH v5 2/2] clk: en7523: add support for .set_rate " Christian Marangi 2026-11-10 0:33 ` sashiko-bot 2026-11-11 23:55 ` Brian Masney
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox