[PATCH v25 08/11] mmc: renesas_sdhi: Add TMIO_MMC_INTERNAL_DIVIDER flag
From: Biju Das
Date: Wed Oct 28 2026 - 08:54:39 EST
From: Biju Das <biju.das.jz@xxxxxxxxxxxxxx>
RZ/G3L has a fixed internal /2 clock divider active in all bus
timing modes except HS400, where the divider is bypassed. Add a
TMIO_MMC_INTERNAL_DIVIDER flag to mark controllers with this
divider.
Add renesas_sdhi_clk_get_rate() to fold the /2 division into the
reported clock rate when the flag is set, and use it in
renesas_sdhi_clk_update() in place of clk_get_rate(). HS400 is not
yet supported in software for this SoC. Adjust clkh_shift for the
internal divider case.
No existing platform sets this flag, so this is infrastructure only,
with no functional change for current SoCs.
Signed-off-by: Biju Das <biju.das.jz@xxxxxxxxxxxxxx>
---
v24->v25:
* Dropped renesas_sdhi_is_internal_divider_enabled().
* Added a comment about the internal divider in
renesas_sdhi_clk_update() and replaced 'clkh_shift = 1' with
'clkh_shift--'
* Moved mmc->f_max to probe() and made it fail if max-frequency is
not defined in DT for SoCs with an internal divider.
v23->v24:
* No change.
v22->v23:
* Updated commit description.
v21->v22:
* Updated commit description.
* Added renesas_sdhi_is_internal_divider_enabled() and
renesas_sdhi_clk_get_rate() to simplify the clock rate logic.
v20->v21:
* No change.
v19->v20:
* No change.
v18->v19:
* Updated commit description.
* Dropped divider variable from struct renesas_sdhi.
* Updated renesas_sdhi_clk_update() to return rate for HS400mode
and nonHS400mode(uses 1/2 internal divider).
v18:
* New patch using flag.
---
drivers/mmc/host/renesas_sdhi_core.c | 47 +++++++++++++++++++++++++++---------
include/linux/platform_data/tmio.h | 3 +++
2 files changed, 38 insertions(+), 12 deletions(-)
diff --git a/drivers/mmc/host/renesas_sdhi_core.c b/drivers/mmc/host/renesas_sdhi_core.c
index 5c317ab29d92..3a1a558bc98c 100644
--- a/drivers/mmc/host/renesas_sdhi_core.c
+++ b/drivers/mmc/host/renesas_sdhi_core.c
@@ -106,15 +106,6 @@ static int renesas_sdhi_clk_enable(struct tmio_mmc_host *host)
if (ret < 0)
return ret;
- /*
- * The clock driver may not know what maximum frequency
- * actually works, so it should be set with the max-frequency
- * property which will already have been read to f_max. If it
- * was missing, assume the current frequency is the maximum.
- */
- if (!mmc->f_max)
- mmc->f_max = clk_get_rate(priv->clk);
-
/*
* Minimum frequency is the minimum input clock frequency
* divided by our maximum divider.
@@ -133,6 +124,17 @@ static int renesas_sdhi_clk_enable(struct tmio_mmc_host *host)
return 0;
}
+static unsigned int renesas_sdhi_clk_get_rate(struct tmio_mmc_host *host,
+ struct clk *clk)
+{
+ unsigned int freq = clk_get_rate(clk);
+
+ if (host->pdata->flags & TMIO_MMC_INTERNAL_DIVIDER)
+ freq /= 2;
+
+ return freq;
+}
+
static unsigned int renesas_sdhi_clk_update(struct tmio_mmc_host *host,
unsigned int wanted_clock)
{
@@ -149,7 +151,7 @@ static unsigned int renesas_sdhi_clk_update(struct tmio_mmc_host *host,
* clock during tuning, so we don't change the external clock setup.
*/
if (!(host->pdata->flags & TMIO_MMC_MIN_RCAR2) || mmc_doing_tune(host->mmc))
- return clk_get_rate(priv->clk);
+ return renesas_sdhi_clk_get_rate(host, priv->clk);
if (priv->clkh) {
/* HS400 with 4TAP needs different clock settings */
@@ -192,10 +194,15 @@ static unsigned int renesas_sdhi_clk_update(struct tmio_mmc_host *host,
clk_set_rate(ref_clk, best_freq);
- if (priv->clkh)
+ if (priv->clkh) {
+ /* internal /2 divider is always active here */
+ if (host->pdata->flags & TMIO_MMC_INTERNAL_DIVIDER)
+ clkh_shift--;
+
clk_set_rate(priv->clk, best_freq >> clkh_shift);
+ }
- return clk_get_rate(priv->clk);
+ return renesas_sdhi_clk_get_rate(host, priv->clk);
}
static void renesas_sdhi_set_clock(struct tmio_mmc_host *host,
@@ -1255,6 +1262,22 @@ int renesas_sdhi_probe(struct platform_device *pdev,
if (ret)
return ret;
+ /*
+ * The clock driver may not know the maximum working frequency, so it
+ * should come from the 'max-frequency' property. If it is missing,
+ * assume the current rate is the maximum. SoCs with an internal
+ * divider can't do that, because the current rate is not
+ * meaningful for them, so the property is mandatory there.
+ */
+ if (!host->mmc->f_max) {
+ if (mmc_data->flags & TMIO_MMC_INTERNAL_DIVIDER) {
+ ret = dev_err_probe(dev, -EINVAL, "max-frequency is required\n");
+ goto edisclk;
+ }
+
+ host->mmc->f_max = clk_get_rate(priv->clk);
+ }
+
rcfg.of_node = of_get_available_child_by_name(dev->of_node, "vqmmc-regulator");
if (rcfg.of_node) {
rcfg.driver_data = priv->host;
diff --git a/include/linux/platform_data/tmio.h b/include/linux/platform_data/tmio.h
index 77d3dc91c828..09b3411a59c9 100644
--- a/include/linux/platform_data/tmio.h
+++ b/include/linux/platform_data/tmio.h
@@ -53,6 +53,9 @@
/* Some controllers have tuning delay */
#define TMIO_MMC_TUNING_DELAY BIT(13)
+/* Some controllers have internal divider */
+#define TMIO_MMC_INTERNAL_DIVIDER BIT(14)
+
struct tmio_mmc_data {
void *chan_priv_tx;
void *chan_priv_rx;
--
2.43.0