Linux-RISC-V Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: Yixun Lan <dlan@kernel.org>
To: Adrian Hunter <adrian.hunter@intel.com>, Ulf Hansson <ulfh@kernel.org>
Cc: Long Wan <long.wan@linux.spacemit.com>,
	linux-mmc@vger.kernel.org,  linux-kernel@vger.kernel.org,
	linux-riscv@lists.infradead.org,  spacemit@lists.linux.dev,
	Yixun Lan <dlan@kernel.org>
Subject: [PATCH 7/7] mmc: sdhci-of-k1: Implement SD clock gating for voltage switch
Date: Wed, 02 Sep 2026 08:04:28 +0000	[thread overview]
Message-ID: <20260902-07-k3-sdhci-fix-v1-7-b15c5d0f64fd@kernel.org> (raw)
In-Reply-To: <20260902-07-k3-sdhci-fix-v1-0-b15c5d0f64fd@kernel.org>

From: Long Wan <long.wan@linux.spacemit.com>

reset() permanently set OVRRD_CLK_OEN and FORCE_CLK_ON for SD/SDIO hosts
and never cleared them, so the SD clock was forced on for the lifetime of
the controller and hardware auto gating never took effect.

These bits are only needed during the CMD11 1.8V voltage switch, where
the clock has to keep running so DAT[3:0] can be sampled after the
switch. Add a set_clk_gate() helper, force the clock on from set_clock()
when CMD11 is in flight, and restore auto gating from card_busy() once
the switch completes.

card_busy() is registered for all card types since the CMD11 sequence is
an SD/SDIO path and the DAT[0] level check is generic.

Signed-off-by: Long Wan <long.wan@linux.spacemit.com>
Signed-off-by: Yixun Lan <dlan@kernel.org>
---
 drivers/mmc/host/sdhci-of-k1.c | 49 ++++++++++++++++++++++++++++++++++++++----
 1 file changed, 45 insertions(+), 4 deletions(-)

diff --git a/drivers/mmc/host/sdhci-of-k1.c b/drivers/mmc/host/sdhci-of-k1.c
index a07e7db0853e..5260421d394c 100644
--- a/drivers/mmc/host/sdhci-of-k1.c
+++ b/drivers/mmc/host/sdhci-of-k1.c
@@ -12,6 +12,7 @@
 #include <linux/mmc/card.h>
 #include <linux/mmc/host.h>
 #include <linux/mmc/mmc.h>
+#include <linux/mmc/sd.h>
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
@@ -162,6 +163,16 @@ static void spacemit_sdhci_prepare_tuning(struct sdhci_host *host)
 		spacemit_sdhci_setbits(host, SDHC_HS200_USE_RFIFO, SPACEMIT_SDHC_PHY_FUNC_REG);
 }
 
+static void spacemit_sdhci_set_clk_gate(struct sdhci_host *host, unsigned int auto_gate)
+{
+	if (auto_gate)
+		spacemit_sdhci_clrbits(host, SDHC_OVRRD_CLK_OEN | SDHC_FORCE_CLK_ON,
+				       SPACEMIT_SDHC_OP_EXT_REG);
+	else
+		spacemit_sdhci_setbits(host, SDHC_OVRRD_CLK_OEN | SDHC_FORCE_CLK_ON,
+				       SPACEMIT_SDHC_OP_EXT_REG);
+}
+
 static void spacemit_sdhci_reset(struct sdhci_host *host, u8 mask)
 {
 	sdhci_reset(host, mask);
@@ -172,9 +183,6 @@ static void spacemit_sdhci_reset(struct sdhci_host *host, u8 mask)
 	if (host->mmc->caps2 & MMC_CAP2_NO_MMC) {
 		/* SD/SDIO: bypass PHY, use TX internal clock */
 		spacemit_sdhci_setbits(host, SDHC_TX_INT_CLK_SEL, SPACEMIT_SDHC_TX_CFG_REG);
-
-		spacemit_sdhci_setbits(host, SDHC_OVRRD_CLK_OEN | SDHC_FORCE_CLK_ON,
-				       SPACEMIT_SDHC_OP_EXT_REG);
 	} else {
 		/* eMMC: use PHY function mode */
 		spacemit_sdhci_setbits(host, SDHC_PHY_FUNC_EN | SDHC_PHY_PLL_LOCK,
@@ -209,6 +217,20 @@ static void spacemit_sdhci_set_clock(struct sdhci_host *host, unsigned int clock
 		spacemit_sdhci_clrbits(host, SDHC_TX_INT_CLK_SEL, SPACEMIT_SDHC_TX_CFG_REG);
 
 	sdhci_set_clock(host, clock);
+
+	if (host->mmc->caps2 & MMC_CAP2_NO_MMC) {
+		/*
+		 * During CMD11 voltage switch to 1.8V, temporarily force the
+		 * clock on to ensure DAT[3:0] can be sampled correctly after
+		 * the switch. Auto gating is restored in card_busy() once the
+		 * switch completes.
+		 */
+		if ((SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND)) == SD_SWITCH_VOLTAGE) &&
+		    host->mmc->ios.signal_voltage == MMC_SIGNAL_VOLTAGE_180) {
+			if (clock)
+				spacemit_sdhci_set_clk_gate(host, 0);
+		}
+	}
 };
 
 static void spacemit_sdhci_phy_dll_init(struct sdhci_host *host)
@@ -491,6 +513,23 @@ static inline void spacemit_sdhci_get_pins(struct device *dev,
 		sdhst->pinctrl_default, sdhst->pinctrl_uhs);
 }
 
+static int spacemit_sdhci_card_busy(struct mmc_host *mmc)
+{
+	struct sdhci_host *host = mmc_priv(mmc);
+	u32 present_state;
+
+	present_state = sdhci_readl(host, SDHCI_PRESENT_STATE);
+
+	if (host->mmc->caps2 & MMC_CAP2_NO_MMC) {
+		if ((SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND)) == SD_SWITCH_VOLTAGE) &&
+		    host->mmc->ios.signal_voltage == MMC_SIGNAL_VOLTAGE_180)
+			/* Recover auto clock after voltage switch */
+			spacemit_sdhci_set_clk_gate(host, 1);
+	}
+
+	return !(present_state & SDHCI_DATA_0_LVL_MASK);
+}
+
 static const struct sdhci_ops spacemit_sdhci_ops = {
 	.get_max_clock		= spacemit_sdhci_clk_get_max_clock,
 	.reset			= spacemit_sdhci_reset,
@@ -555,8 +594,10 @@ static int spacemit_sdhci_probe(struct platform_device *pdev)
 
 	sdhci_get_of_property(pdev);
 
+	mops = &host->mmc_host_ops;
+	mops->card_busy = spacemit_sdhci_card_busy;
+
 	if (!(host->mmc->caps2 & MMC_CAP2_NO_MMC)) {
-		mops = &host->mmc_host_ops;
 		mops->hs400_prepare_ddr	= spacemit_sdhci_pre_select_hs400;
 		mops->hs400_complete	= spacemit_sdhci_post_select_hs400;
 		mops->hs400_downgrade	= spacemit_sdhci_pre_hs400_to_hs200;

-- 
2.55.0


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

  parent reply	other threads:[~2026-09-02  8:05 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-02  8:04 [PATCH 0/7] mmc: spacemit: improve clock and tuning logic Yixun Lan
2026-09-02  8:04 ` [PATCH 1/7] mmc: sdhci-of-k1: no pinctrl state switching Yixun Lan
2026-09-02  8:04 ` [PATCH 2/7] mmc: sdhci-of-k1: drop duplicated voltage settings Yixun Lan
2026-09-02  8:04 ` [PATCH 3/7] mmc: sdhci-of-k1: configure PHY mode Yixun Lan
2026-09-02  8:04 ` [PATCH 4/7] mmc: sdhci-of-k1: Improve power control Yixun Lan
2026-09-02  8:04 ` [PATCH 5/7] mmc: sdhci-of-k1: Use finest delay granularity for RX tuning Yixun Lan
2026-09-02  8:04 ` [PATCH 6/7] mmc: sdhci-of-k1: Improve RX tuning window Yixun Lan
2026-09-07 15:53   ` Hung-Chun Tseng
2026-09-02  8:04 ` Yixun Lan [this message]
2026-09-09  8:40 ` [PATCH 0/7] mmc: spacemit: improve clock and tuning logic Adrian Hunter

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=20260902-07-k3-sdhci-fix-v1-7-b15c5d0f64fd@kernel.org \
    --to=dlan@kernel.org \
    --cc=adrian.hunter@intel.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mmc@vger.kernel.org \
    --cc=linux-riscv@lists.infradead.org \
    --cc=long.wan@linux.spacemit.com \
    --cc=spacemit@lists.linux.dev \
    --cc=ulfh@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