From 39c9af33129c729be813e302fbe4aecfef48064f Mon Sep 17 00:00:00 2001
From: Phil Elwell <phil@raspberrypi.com>
Date: Fri, 28 Mar 2025 16:35:39 +0000
Subject: [PATCH 1/3] mmc: Speed up reboot with an absent card

On devices with no card detect mechanism, the host ends up polling for
the insertion of a card. This polling happens at multiple frequencies
and with many steps (SDIO, SD, MMC), some of which involve timeouts.
If a reboot is requested during one of those scans, it will stall,
pointlessly, for up to a minute while it completes.

Attempt to short circuit the rescan if the MMC interface is being
removed.

Signed-off-by: Phil Elwell <phil@raspberrypi.com>
---
 drivers/mmc/core/core.c    | 15 ++++++++++++++-
 drivers/mmc/core/mmc_ops.c |  3 +++
 drivers/mmc/core/sdio.c    |  4 ++++
 3 files changed, 21 insertions(+), 1 deletion(-)

diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 95d40fd263698c..bed4d7fe8b9cca 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -399,10 +399,16 @@ void mmc_wait_for_req_done(struct mmc_host *host, struct mmc_request *mrq)
 	struct mmc_command *cmd;
 
 	while (1) {
-		wait_for_completion(&mrq->completion);
+		if (!host->rescan_disable)
+			wait_for_completion(&mrq->completion);
 
 		cmd = mrq->cmd;
 
+		if (host->rescan_disable) {
+			cmd->error = -ENOENT;
+			break;
+		}
+
 		if (!cmd->error || !cmd->retries ||
 		    mmc_card_removed(host->card))
 			break;
@@ -619,6 +625,11 @@ EXPORT_SYMBOL(mmc_is_req_done);
  */
 void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)
 {
+	if (host->rescan_disable) {
+		mrq->cmd->error = -ENOENT;
+		return;
+	}
+
 	__mmc_start_req(host, mrq);
 
 	if (!mrq->cap_cmd_during_tfr)
@@ -2265,6 +2276,8 @@ void mmc_rescan(struct work_struct *work)
 
 	for (i = 0; i < ARRAY_SIZE(freqs); i++) {
 		unsigned int freq = freqs[i];
+		if (host->rescan_disable)
+			return;
 		if (freq > host->f_max) {
 			if (i + 1 < ARRAY_SIZE(freqs))
 				continue;
diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
index 3b3adbddf6641e..7db55752a671d9 100644
--- a/drivers/mmc/core/mmc_ops.c
+++ b/drivers/mmc/core/mmc_ops.c
@@ -512,6 +512,9 @@ int __mmc_poll_for_busy(struct mmc_host *host, unsigned int period_us,
 
 	timeout = jiffies + msecs_to_jiffies(timeout_ms) + 1;
 	do {
+		if (host->rescan_disable)
+			return -ENOENT;
+
 		/*
 		 * Due to the possibility of being preempted while polling,
 		 * check the expiration time first.
diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
index 4b19b8a16b0968..0954d43e41365b 100644
--- a/drivers/mmc/core/sdio.c
+++ b/drivers/mmc/core/sdio.c
@@ -673,6 +673,10 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
 		ocr |= R4_18V_PRESENT;
 
 try_again:
+	if (host->rescan_disable) {
+		err = -ENOENT;
+		goto remove;
+	}
 	if (!retries) {
 		pr_warn("%s: Skipping voltage switch\n", mmc_hostname(host));
 		ocr &= ~R4_18V_PRESENT;

From 0c7e4a4d03471d6a316a940fd79ecaa0dc482989 Mon Sep 17 00:00:00 2001
From: Phil Elwell <phil@raspberrypi.com>
Date: Mon, 31 Mar 2025 11:24:35 +0100
Subject: [PATCH 2/3] mmc: sdhci: reduce default timeout to 5s

Reduce the maximum time it takes to get to reboot by decreasing the
default SDHCI timeout to 5 seconds.

Signed-off-by: Phil Elwell <phil@raspberrypi.com>
---
 drivers/mmc/host/sdhci.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 9bb360efe5b857..80027f0ee8205e 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1629,6 +1629,7 @@ static void sdhci_finish_data(struct sdhci_host *host)
 
 static bool sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
 {
+	const unsigned long default_timeout_secs = 5;
 	int flags;
 	u32 mask;
 	unsigned long timeout;
@@ -1704,10 +1705,10 @@ static bool sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
 	timeout = jiffies;
 	if (host->data_timeout)
 		timeout += nsecs_to_jiffies(host->data_timeout);
-	else if (!cmd->data && cmd->busy_timeout > 9000)
+	else if (!cmd->data && cmd->busy_timeout > (default_timeout_secs - 1) * 1000)
 		timeout += DIV_ROUND_UP(cmd->busy_timeout, 1000) * HZ + HZ;
 	else
-		timeout += 10 * HZ;
+		timeout += default_timeout_secs * HZ;
 	sdhci_mod_timer(host, cmd->mrq, timeout);
 
 	if (host->use_external_dma)

From a5804e5715411a4533d7ec06283847d6c226f29c Mon Sep 17 00:00:00 2001
From: Phil Elwell <phil@raspberrypi.com>
Date: Mon, 31 Mar 2025 10:16:09 +0100
Subject: [PATCH 3/3] mmc: sdhci: Avoid a NULL pointer crash

Two days into running a reboot test over the weekend, the SDHCI driver
hit a NULL pointer in sdhci_needs_reset. It's not clear how that has
been added by the changes for a faster reboot, but guard against it
explicitly.

Signed-off-by: Phil Elwell <phil@raspberrypi.com>
---
 drivers/mmc/host/sdhci.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 80027f0ee8205e..64c44a74cb5966 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1490,6 +1490,8 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host,
 
 static bool sdhci_needs_reset(struct sdhci_host *host, struct mmc_request *mrq)
 {
+	if (!mrq)
+		return false;
 	return (!(host->flags & SDHCI_DEVICE_DEAD) &&
 		((mrq->cmd && mrq->cmd->error) ||
 		 (mrq->sbc && mrq->sbc->error) ||