* [PATCH v2 0/2] clk: en7523: full clk_hw + set_rate for en7581 @ 2026-11-03 20:54 Christian Marangi 2026-11-03 20:54 ` [PATCH v2 1/2] clk: en7523: convert to full clk_hw implementation " Christian Marangi 2026-11-03 20:54 ` [PATCH v2 2/2] clk: en7523: add support for .set_rate " Christian Marangi 0 siblings, 2 replies; 5+ messages in thread From: Christian Marangi @ 2026-11-03 20:54 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 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 | 222 ++++++++++++++++++++++++++++++++++----- 1 file changed, 198 insertions(+), 24 deletions(-) -- 2.55.0 ^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v2 1/2] clk: en7523: convert to full clk_hw implementation for en7581 2026-11-03 20:54 [PATCH v2 0/2] clk: en7523: full clk_hw + set_rate for en7581 Christian Marangi @ 2026-11-03 20:54 ` Christian Marangi 2026-11-04 21:02 ` Brian Masney 2026-11-03 20:54 ` [PATCH v2 2/2] clk: en7523: add support for .set_rate " Christian Marangi 1 sibling, 1 reply; 5+ messages in thread From: Christian Marangi @ 2026-11-03 20:54 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 | 86 +++++++++++++++++++++++++++++----------- 1 file changed, 62 insertions(+), 24 deletions(-) diff --git a/drivers/clk/clk-en7523.c b/drivers/clk/clk-en7523.c index 24fdec2e6198..aab4306ae037 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,46 +748,76 @@ static int en7523_clk_hw_init(struct platform_device *pdev, en7581_rst_ofs); } -static void en7581_register_clocks(struct device *dev, struct clk_hw_onecell_data *clk_data, - struct regmap *map, void __iomem *base) +static unsigned long en7581_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) { - struct clk_hw *hw; + struct en_clk *c = container_of(hw, struct en_clk, hw); + const struct en_clk_desc *desc = c->desc; + struct regmap *map = c->map; + u32 val, reg; u32 rate; + 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); + 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) { + pr_err("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 int en7581_register_clocks(struct device *dev, struct clk_hw_onecell_data *clk_data, + struct regmap *map, void __iomem *base) +{ + struct clk_hw *hw; 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); - continue; - } - rate = en7523_get_base_rate(desc, val); + en_clk = devm_kzalloc(dev, sizeof(*en_clk), GFP_KERNEL); + if (!en_clk) + return -ENOMEM; - err = regmap_read(map, reg, &val); + init.name = desc->name; + en_clk->map = map; + en_clk->desc = desc; + en_clk->hw.init = &init; + + err = devm_clk_hw_register(dev, &en_clk->hw); if (err) { - pr_err("Failed reading fixed clk div %s: %d\n", + pr_err("Failed to register clk %s: %d\n", desc->name, err); - continue; + return err; } - rate /= en7523_get_div(desc, val); - 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)); - continue; - } - - clk_data->hws[desc->id] = hw; + clk_data->hws[desc->id] = &en_clk->hw; } hw = en7523_register_pcie_clk(dev, base); clk_data->hws[EN7523_CLK_PCIE] = hw; + + return 0; } static int en7523_reset_update(struct reset_controller_dev *rcdev, @@ -1062,7 +1098,9 @@ static int en7581_clk_hw_init(struct platform_device *pdev, priv->base = base; - en7581_register_clocks(&pdev->dev, clk_data, map, base); + ret = en7581_register_clocks(&pdev->dev, clk_data, map, base); + if (ret) + return ret; val = readl(base + REG_NP_SCU_SSTR); val &= ~(REG_PCIE_XSI0_SEL_MASK | REG_PCIE_XSI1_SEL_MASK); -- 2.55.0 ^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH v2 1/2] clk: en7523: convert to full clk_hw implementation for en7581 2026-11-03 20:54 ` [PATCH v2 1/2] clk: en7523: convert to full clk_hw implementation " Christian Marangi @ 2026-11-04 21:02 ` Brian Masney 0 siblings, 0 replies; 5+ messages in thread From: Brian Masney @ 2026-11-04 21:02 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 | 86 +++++++++++++++++++++++++++++----------- > 1 file changed, 62 insertions(+), 24 deletions(-) > > diff --git a/drivers/clk/clk-en7523.c b/drivers/clk/clk-en7523.c > index 24fdec2e6198..aab4306ae037 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,46 +748,76 @@ static int en7523_clk_hw_init(struct platform_device *pdev, > en7581_rst_ofs); > } > > -static void en7581_register_clocks(struct device *dev, struct clk_hw_onecell_data *clk_data, > - struct regmap *map, void __iomem *base) > +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 regmap *map = c->map; > + u32 val, reg; > + u32 rate; > + 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); Use dev_err() and elsewhere in this driver since a struct dev is available here. > + 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) { > + pr_err("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 int 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(dev, sizeof(*en_clk), GFP_KERNEL); devm_kzalloc_obj() is now available in the tree. > + if (!en_clk) > + return -ENOMEM; > + > + init.name = desc->name; > + en_clk->map = map; > + en_clk->desc = desc; > + en_clk->hw.init = &init; > + > + err = devm_clk_hw_register(dev, &en_clk->hw); > if (err) { > - pr_err("Failed reading fixed clk rate %s: %d\n", > + pr_err("Failed to register clk %s: %d\n", Use dev_err_probe() > desc->name, err); > - 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); > - > - 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)); > - continue; > + return err; Prior to this patch, a registration failure was logged, and the other clocks were tried. Now the probe fails. This is fine, however the commit message says there is no behavior change. This change should be called out in the commit message. Brian -- Brian Masney <masneyb@kernel.org> ^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v2 2/2] clk: en7523: add support for .set_rate for en7581 2026-11-03 20:54 [PATCH v2 0/2] clk: en7523: full clk_hw + set_rate for en7581 Christian Marangi 2026-11-03 20:54 ` [PATCH v2 1/2] clk: en7523: convert to full clk_hw implementation " Christian Marangi @ 2026-11-03 20:54 ` Christian Marangi 2026-11-04 21:02 ` Brian Masney 1 sibling, 1 reply; 5+ messages in thread From: Christian Marangi @ 2026-11-03 20:54 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". Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> --- drivers/clk/clk-en7523.c | 136 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) diff --git a/drivers/clk/clk-en7523.c b/drivers/clk/clk-en7523.c index aab4306ae037..5ce4dc692ab2 100644 --- a/drivers/clk/clk-en7523.c +++ b/drivers/clk/clk-en7523.c @@ -777,8 +777,144 @@ static unsigned long en7581_recalc_rate(struct clk_hw *hw, return rate / en7523_get_div(desc, val); } +static int en7581_get_base_val_for_rate(const struct en_clk_desc *desc, + int div, unsigned long rate) +{ + int i; + + /* Single base rate */ + if (!desc->base_bits) { + if (rate != desc->base_value / div) + goto err; + + return 0; + } + + /* Check every base rate with provided divisor */ + for (i = 0; i < desc->n_base_values; i++) + if (rate == desc->base_values[i] / div) + return i; + +err: + return -EINVAL; +} + +static int en7581_get_vals_for_rate(const struct en_clk_desc *desc, + unsigned long rate, + u32 *base_val, u32 *div_val) +{ + int tmp_base_val = 0; + int tmp_div_val = 0; + + if (!desc->base_bits && !desc->div_bits) + return -EINVAL; + + /* Divisor not supported, just search in base rate */ + if (!desc->div_bits) { + tmp_base_val = en7581_get_base_val_for_rate(desc, 1, rate); + if (tmp_base_val < 0) { + pr_err("Invalid rate for clock %s\n", + desc->name); + return -EINVAL; + } + + goto exit; + } + + /* Check if div0 satisfy the request */ + if (desc->div_val0) { + tmp_base_val = en7581_get_base_val_for_rate(desc, + desc->div_val0, + rate); + if (tmp_base_val >= 0) + goto exit; + + /* Skip checking first divisor val */ + tmp_div_val = 1; + } + + /* Simulate rate with every divisor supported */ + for (; tmp_div_val < BIT(desc->div_bits); tmp_div_val++) { + int div = (tmp_div_val + desc->div_offset) * desc->div_step; + + tmp_base_val = en7581_get_base_val_for_rate(desc, div, + rate); + if (tmp_base_val >= 0) + goto exit; + } + + if (tmp_div_val == BIT(desc->div_bits)) { + pr_err("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(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 regmap *map = en_clk->map; + u32 base_val, div_val; + u32 reg, val, mask; + int err; + + err = en7581_get_vals_for_rate(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) { + pr_err("Failed to update div reg for clock %s\n", + desc->name); + return -EINVAL; + } + } + + 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) { + pr_err("Failed to update reg for clock %s\n", + desc->name); + return -EINVAL; + } + } + + 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 int en7581_register_clocks(struct device *dev, struct clk_hw_onecell_data *clk_data, -- 2.55.0 ^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH v2 2/2] clk: en7523: add support for .set_rate for en7581 2026-11-03 20:54 ` [PATCH v2 2/2] clk: en7523: add support for .set_rate " Christian Marangi @ 2026-11-04 21:02 ` Brian Masney 0 siblings, 0 replies; 5+ messages in thread From: Brian Masney @ 2026-11-04 21:02 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". > > Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> > --- > drivers/clk/clk-en7523.c | 136 +++++++++++++++++++++++++++++++++++++++ > 1 file changed, 136 insertions(+) > > diff --git a/drivers/clk/clk-en7523.c b/drivers/clk/clk-en7523.c > index aab4306ae037..5ce4dc692ab2 100644 > --- a/drivers/clk/clk-en7523.c > +++ b/drivers/clk/clk-en7523.c > @@ -777,8 +777,144 @@ static unsigned long en7581_recalc_rate(struct clk_hw *hw, > return rate / en7523_get_div(desc, val); > } > > +static int en7581_get_base_val_for_rate(const struct en_clk_desc *desc, > + int div, unsigned long rate) > +{ > + int i; > + > + /* Single base rate */ > + if (!desc->base_bits) { > + if (rate != desc->base_value / div) > + goto err; > + > + return 0; > + } > + > + /* Check every base rate with provided divisor */ > + for (i = 0; i < desc->n_base_values; i++) > + if (rate == desc->base_values[i] / div) > + return i; > + > +err: > + return -EINVAL; > +} Can this be updated to use the common functions in clk-divider.c like divider_get_val() rather than rolling this same functionality in this driver? > + > +static int en7581_get_vals_for_rate(const struct en_clk_desc *desc, > + unsigned long rate, > + u32 *base_val, u32 *div_val) > +{ > + int tmp_base_val = 0; > + int tmp_div_val = 0; > + > + if (!desc->base_bits && !desc->div_bits) > + return -EINVAL; > + > + /* Divisor not supported, just search in base rate */ > + if (!desc->div_bits) { > + tmp_base_val = en7581_get_base_val_for_rate(desc, 1, rate); > + if (tmp_base_val < 0) { > + pr_err("Invalid rate for clock %s\n", > + desc->name); > + return -EINVAL; > + } > + > + goto exit; > + } > + > + /* Check if div0 satisfy the request */ > + if (desc->div_val0) { > + tmp_base_val = en7581_get_base_val_for_rate(desc, > + desc->div_val0, > + rate); > + if (tmp_base_val >= 0) > + goto exit; > + > + /* Skip checking first divisor val */ > + tmp_div_val = 1; > + } > + > + /* Simulate rate with every divisor supported */ > + for (; tmp_div_val < BIT(desc->div_bits); tmp_div_val++) { > + int div = (tmp_div_val + desc->div_offset) * desc->div_step; > + > + tmp_base_val = en7581_get_base_val_for_rate(desc, div, > + rate); > + if (tmp_base_val >= 0) > + goto exit; > + } > + > + if (tmp_div_val == BIT(desc->div_bits)) { This is always true, right? > + pr_err("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(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 regmap *map = en_clk->map; > + u32 base_val, div_val; > + u32 reg, val, mask; > + int err; > + > + err = en7581_get_vals_for_rate(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) { > + pr_err("Failed to update div reg for clock %s\n", > + desc->name); > + return -EINVAL; return err; > + } > + } > + > + 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) { > + pr_err("Failed to update reg for clock %s\n", > + desc->name); > + return -EINVAL; return err; Brian -- Brian Masney <masneyb@kernel.org> ^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-11-04 21:02 UTC | newest] Thread overview: 5+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-11-03 20:54 [PATCH v2 0/2] clk: en7523: full clk_hw + set_rate for en7581 Christian Marangi 2026-11-03 20:54 ` [PATCH v2 1/2] clk: en7523: convert to full clk_hw implementation " Christian Marangi 2026-11-04 21:02 ` Brian Masney 2026-11-03 20:54 ` [PATCH v2 2/2] clk: en7523: add support for .set_rate " Christian Marangi 2026-11-04 21:02 ` Brian Masney
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox