From: Christian Marangi <ansuelsmth@gmail.com>
To: Stephen Boyd <sboyd@kernel.org>,
Brian Masney <bmasney+clk@redhat.com>,
Jerome Brunet <jbrunet+clk@baylibre.com>,
linux-clk@vger.kernel.org, linux-kernel@vger.kernel.org
Cc: Christian Marangi <ansuelsmth@gmail.com>
Subject: [PATCH v3 2/2] clk: en7523: add support for .set_rate for en7581
Date: Thu, 5 Nov 2026 16:02:06 +0100 [thread overview]
Message-ID: <20261105150207.23023-3-ansuelsmth@gmail.com> (raw)
In-Reply-To: <20261105150207.23023-1-ansuelsmth@gmail.com>
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 | 207 ++++++++++++++++++++++++++++++++++++++-
1 file changed, 206 insertions(+), 1 deletion(-)
diff --git a/drivers/clk/clk-en7523.c b/drivers/clk/clk-en7523.c
index 6640e814d36a..cf0b6ccacede 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,127 @@ 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;
+}
+
+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);
+ int tmp_base_val = 0;
+ int tmp_div_val = 0;
+
+ if (!desc->base_bits && !desc->div_bits)
+ return -EINVAL;
+
+ /* Search requested rate with every matching base rate + divisor */
+ for (; tmp_base_val < desc->n_base_values; tmp_base_val++) {
+ unsigned long parent_rate = desc->base_values[tmp_base_val];
+ unsigned long tmp_rate = parent_rate;
+
+ /*
+ * Simulate rate with every divisor supported
+ * 1. Search the divisor for the requested rate
+ * 2. Simulate the final rate with the new divisor
+ */
+ if (desc->div_bits) {
+ tmp_div_val = divider_get_val(rate, parent_rate,
+ desc->div_table,
+ desc->div_bits,
+ desc->div_flags);
+ tmp_rate = divider_recalc_rate(hw, parent_rate,
+ tmp_div_val,
+ desc->div_table,
+ desc->div_flags,
+ desc->div_bits);
+ }
+
+ /* Matching clock found */
+ if (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;
+ }
+
+ *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
prev parent reply other threads:[~2026-11-05 15:02 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-05 15:02 [PATCH v3 0/2] clk: en7523: full clk_hw + set_rate " Christian Marangi
2026-11-05 15:02 ` [PATCH v3 1/2] clk: en7523: convert to full clk_hw implementation " Christian Marangi
2026-11-05 15:02 ` Christian Marangi [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261105150207.23023-3-ansuelsmth@gmail.com \
--to=ansuelsmth@gmail.com \
--cc=bmasney+clk@redhat.com \
--cc=jbrunet+clk@baylibre.com \
--cc=linux-clk@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=sboyd@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®