mail archive of the barebox mailing list
 help / color / mirror / Atom feed
From: Sascha Hauer <s.hauer@pengutronix.de>
To: BAREBOX <barebox@lists.infradead.org>
Cc: Sascha Hauer <sascha@saschahauer.de>,
	"Claude Opus 4.7" <noreply@anthropic.com>
Subject: [PATCH 5/6] clk: composite: pick best parent for round_rate / set_rate
Date: Thu, 07 May 2026 09:02:51 +0200	[thread overview]
Message-ID: <20260507-rockchip-emmc-v1-5-5e8109e8059d@pengutronix.de> (raw)
In-Reply-To: <20260507-rockchip-emmc-v1-0-5e8109e8059d@pengutronix.de>

From: Sascha Hauer <sascha@saschahauer.de>

Currently our clk composite driver uses the set_rate hook to set the rate
when available, otherwise it uses the mux to set the rate, but it never
tries to find the best combination of both. This patch fills the gap.
Doing this is necessary for example on Rockchip RK3588 where the clock
is adjustable only external to the SDHC controller. The knobs are a
6bit divider and a mux. The divider alone is not sufficient to scale a
high frequency down to the necessary 400kHz init clock while the 24MHz
clock is not high enough for faster speeds.

Walk all parents and pick the one whose rate-clock gets closest to
the requested rate, then reparent if needed before applying the
divider.

For mux-only, rate-only and clocks with CLK_SET_RATE_NO_REPARENT the
previous behaviour is preserved.

Assisted-by: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/clk/clk-composite.c | 110 ++++++++++++++++++++++++++++++++++++++------
 1 file changed, 95 insertions(+), 15 deletions(-)

diff --git a/drivers/clk/clk-composite.c b/drivers/clk/clk-composite.c
index e8f1fa7a72..a23e828f58 100644
--- a/drivers/clk/clk-composite.c
+++ b/drivers/clk/clk-composite.c
@@ -39,16 +39,91 @@ static unsigned long clk_composite_recalc_rate(struct clk_hw *hw,
 	return parent_rate;
 }
 
+/*
+ * Walk all parents of a (rate_hw + mux_hw) composite and pick the one
+ * whose rate-clock can get closest to the requested rate. Returns the
+ * chosen parent's index in *out_idx (or -1 to mean "stay on current parent"),
+ * the parent's rate in *parent_rate, and the achievable output rate as the
+ * function value.
+ * Falls back to round_rate on the current parent when reparenting
+ * isn't possible (no mux, or CLK_SET_RATE_NO_REPARENT).
+ */
+static long clk_composite_pick_parent(struct clk_hw *hw, unsigned long rate,
+				      int *out_idx, unsigned long *parent_rate)
+{
+	struct clk_composite *composite = to_clk_composite(hw);
+	struct clk_hw *rate_hw = composite->rate_hw;
+	struct clk_hw *mux_hw = composite->mux_hw;
+	unsigned long best_rate = 0, best_prate = 0, best_diff = ULONG_MAX;
+	int best_idx = -1;
+	int i;
+
+	if (!rate_hw || !rate_hw->clk.ops->round_rate)
+		return -ENOSYS;
+
+	if (!mux_hw || (hw->clk.flags & CLK_SET_RATE_NO_REPARENT)) {
+		unsigned long prate = *parent_rate;
+		long achievable;
+
+		achievable = rate_hw->clk.ops->round_rate(rate_hw, rate, &prate);
+		if (achievable < 0)
+			return achievable;
+
+		*out_idx = -1;
+		*parent_rate = prate;
+		return achievable;
+	}
+
+	for (i = 0; i < hw->clk.num_parents; i++) {
+		struct clk_hw *p_hw = clk_hw_get_parent_by_index(hw, i);
+		unsigned long prate, diff;
+		long achievable;
+
+		if (!p_hw)
+			continue;
+
+		prate = clk_hw_get_rate(p_hw);
+		achievable = rate_hw->clk.ops->round_rate(rate_hw, rate, &prate);
+		if (achievable < 0)
+			continue;
+
+		diff = (achievable >= rate) ? achievable - rate
+					    : rate - achievable;
+
+		if (diff < best_diff) {
+			best_idx = i;
+			best_prate = prate;
+			best_rate = achievable;
+			best_diff = diff;
+			if (!diff)
+				break;
+		}
+	}
+
+	if (best_idx < 0)
+		return -EINVAL;
+
+	*out_idx = best_idx;
+	*parent_rate = best_prate;
+	return best_rate;
+}
+
 static long clk_composite_round_rate(struct clk_hw *hw, unsigned long rate,
 				  unsigned long *prate)
 {
 	struct clk_composite *composite = to_clk_composite(hw);
-	struct clk_hw *rate_hw = composite->rate_hw;
 	struct clk_hw *mux_hw = composite->mux_hw;
+	int idx;
+	long achievable;
 
-	if (rate_hw)
-		return rate_hw->clk.ops->round_rate(rate_hw, rate, prate);
+	achievable = clk_composite_pick_parent(hw, rate, &idx, prate);
+	if (achievable >= 0)
+		return achievable;
+
+	if (achievable != -ENOSYS)
+		return achievable;
 
+	/* No rate_hw — fall back to mux's round_rate if available. */
 	if (!(hw->clk.flags & CLK_SET_RATE_NO_REPARENT) &&
 	    mux_hw &&
 	    mux_hw->clk.ops->round_rate)
@@ -63,24 +138,29 @@ static int clk_composite_set_rate(struct clk_hw *hw, unsigned long rate,
 	struct clk_composite *composite = to_clk_composite(hw);
 	struct clk_hw *rate_hw = composite->rate_hw;
 	struct clk_hw *mux_hw = composite->mux_hw;
+	int idx = -1;
+	long achievable;
+
+	achievable = clk_composite_pick_parent(hw, rate, &idx, &parent_rate);
+	if (achievable >= 0) {
+		if (idx >= 0 && mux_hw && mux_hw->clk.ops->set_parent) {
+			int ret = mux_hw->clk.ops->set_parent(mux_hw, idx);
+			if (ret)
+				return ret;
+		}
+		return rate_hw->clk.ops->set_rate(rate_hw, rate, parent_rate);
+	}
+
+	if (achievable != -ENOSYS)
+		return achievable;
 
 	/*
-	 * When the rate clock is present use that to set the rate,
-	 * otherwise try the mux clock. We currently do not support
-	 * to find the best rate using a combination of both.
+	 * No rate_hw. Fall back to letting the mux clk reparent itself,
+	 * preserving the existing enable-count handoff.
 	 */
-	if (rate_hw)
-		return rate_hw->clk.ops->set_rate(rate_hw, rate, parent_rate);
-
 	if (!(hw->clk.flags & CLK_SET_RATE_NO_REPARENT) &&
 	    mux_hw &&
 	    mux_hw->clk.ops->set_rate) {
-		/*
-		 * We'll call set_rate on the mux clk which in turn results
-		 * in reparenting the mux clk. Make sure the enable count
-		 * (which is stored in the composite clk, not the mux clk)
-		 * is transferred correctly.
-		 */
 		mux_hw->clk.enable_count = hw->clk.enable_count;
 		return mux_hw->clk.ops->set_rate(mux_hw, rate, parent_rate);
 	}

-- 
2.47.3




  parent reply	other threads:[~2026-05-07  7:04 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-05-07  7:02 [PATCH 0/6] mci: rockchip-dwcmshc: add HS200 support Sascha Hauer
2026-05-07  7:02 ` [PATCH 1/6] mci: sdhci: rockchip: set hidspd before re-enabling the clock Sascha Hauer
2026-05-07  8:05   ` Ahmad Fatoum
2026-05-07  7:02 ` [PATCH 2/6] mci: sdhci: rockchip: disable clock while setting DLL Sascha Hauer
2026-05-07  8:10   ` Ahmad Fatoum
2026-05-07  7:02 ` [PATCH 3/6] mci: sdhci: rockchip: Wait for transfer complete interrupt with MMC_RSP_BUSY cmd Sascha Hauer
2026-05-07  8:11   ` Ahmad Fatoum
2026-05-07  7:02 ` [PATCH 4/6] mci: sdhci: rockchip: Update pre-change delay for rockchip platform Sascha Hauer
2026-05-07  8:12   ` Ahmad Fatoum
2026-05-07  7:02 ` Sascha Hauer [this message]
2026-05-07  7:02 ` [PATCH 6/6] mci: sdhci: rockchip: officially support HS200 Sascha Hauer
2026-05-07  8:15   ` Ahmad Fatoum
2026-05-07  8:36     ` Sascha Hauer
2026-05-07  7:47 ` [PATCH 0/6] mci: rockchip-dwcmshc: add HS200 support Ahmad Fatoum

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=20260507-rockchip-emmc-v1-5-5e8109e8059d@pengutronix.de \
    --to=s.hauer@pengutronix.de \
    --cc=barebox@lists.infradead.org \
    --cc=noreply@anthropic.com \
    --cc=sascha@saschahauer.de \
    /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