From: Kamal Dasu <kamal.dasu@broadcom.com>
To: Ulf Hansson <ulf.hansson@linaro.org>,
Adrian Hunter <adrian.hunter@intel.com>,
Kees Cook <kees@kernel.org>
Cc: Tony Luck <tony.luck@intel.com>,
"Guilherme G . Piccoli" <gpiccoli@igalia.com>,
Florian Fainelli <florian.fainelli@broadcom.com>,
Arend van Spriel <arend.vanspriel@broadcom.com>,
William Zhang <william.zhang@broadcom.com>,
bcm-kernel-feedback-list@broadcom.com, linux-mmc@vger.kernel.org,
linux-kernel@vger.kernel.org,
Kamal Dasu <kamal.dasu@broadcom.com>
Subject: [PATCH v4 2/4] mmc: sdhci: Implement panic-context write support
Date: Fri, 20 Mar 2026 16:32:47 -0400 [thread overview]
Message-ID: <20260320203249.1965044-3-kamal.dasu@broadcom.com> (raw)
In-Reply-To: <20260320203249.1965044-1-kamal.dasu@broadcom.com>
Implement the panic-context host operations for SDHCI controllers:
sdhci_panic_prepare(): Reset the controller, drain any pending
requests by polling Present State, and clear interrupt status to
start from a known-good state.
sdhci_panic_poll_completion(): Poll for command and data completion
using register reads instead of waiting for interrupts.
sdhci_panic_complete(): Clear interrupt status and restore the
host to normal operation after panic I/O.
Make sdhci_send_command_retry() panic-safe by using mdelay() instead
of usleep_range() when oops_in_progress is set, and suppress WARN
output during panic.
Add oops_in_progress guards to sdhci_timeout_timer() and
sdhci_timeout_data_timer() to prevent spurious timeout handling
during panic writes.
Signed-off-by: Kamal Dasu <kamal.dasu@broadcom.com>
---
drivers/mmc/host/sdhci.c | 169 ++++++++++++++++++++++++++++++++++++++-
drivers/mmc/host/sdhci.h | 6 ++
2 files changed, 171 insertions(+), 4 deletions(-)
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index fec9329e1edb..c40084e07eca 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -15,6 +15,7 @@
#include <linux/ktime.h>
#include <linux/highmem.h>
#include <linux/io.h>
+#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
@@ -1765,17 +1766,22 @@ static bool sdhci_send_command_retry(struct sdhci_host *host,
while (!sdhci_send_command(host, cmd)) {
if (!timeout--) {
- pr_err("%s: Controller never released inhibit bit(s).\n",
- mmc_hostname(host->mmc));
+ if (!oops_in_progress) {
+ pr_err("%s: Controller never released inhibit bit(s).\n",
+ mmc_hostname(host->mmc));
+ sdhci_dumpregs(host);
+ }
sdhci_err_stats_inc(host, CTRL_TIMEOUT);
- sdhci_dumpregs(host);
cmd->error = -EIO;
return false;
}
spin_unlock_irqrestore(&host->lock, flags);
- usleep_range(1000, 1250);
+ if (unlikely(oops_in_progress))
+ mdelay(1);
+ else
+ usleep_range(1000, 1250);
present = host->mmc->ops->get_cd(host->mmc);
@@ -3076,6 +3082,152 @@ static void sdhci_card_event(struct mmc_host *mmc)
spin_unlock_irqrestore(&host->lock, flags);
}
+/*
+ * Panic-context operations for pstore backends.
+ * These run with interrupts disabled and other CPUs stopped.
+ */
+
+#define SDHCI_PANIC_POLL_ITERATIONS 2000
+#define SDHCI_PANIC_POLL_DELAY_US 500
+#define SDHCI_PANIC_MIN_POLL_COUNT 300
+#define SDHCI_PANIC_RESET_TIMEOUT_US 100000
+#define SDHCI_PANIC_DRAIN_TIMEOUT_US 100000
+
+/**
+ * sdhci_panic_prepare - Prepare SDHCI controller for panic-context I/O
+ * @mmc: MMC host structure
+ *
+ * Called during kernel panic. Drains any in-flight request, resets the
+ * CMD and DATA lines, then clears software state under spinlock.
+ * The drain + reset ensures no stopped CPU is still inside sdhci_irq
+ * holding host->lock by the time we take it.
+ */
+int sdhci_panic_prepare(struct mmc_host *mmc)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ unsigned long flags;
+ u32 present;
+ u8 val;
+ int ret;
+
+ /*
+ * If the controller has a request in flight, give it a short
+ * bounded time to finish. The CMD/DATA reset below will
+ * force-abort anything that doesn't complete in time.
+ */
+ present = sdhci_readl(host, SDHCI_PRESENT_STATE);
+ if (present & (SDHCI_CMD_INHIBIT | SDHCI_DATA_INHIBIT)) {
+ readl_poll_timeout_atomic(host->ioaddr + SDHCI_PRESENT_STATE,
+ present,
+ !(present & (SDHCI_CMD_INHIBIT |
+ SDHCI_DATA_INHIBIT)),
+ 10, SDHCI_PANIC_DRAIN_TIMEOUT_US);
+ }
+
+ sdhci_writeb(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA,
+ SDHCI_SOFTWARE_RESET);
+
+ ret = readb_poll_timeout_atomic(host->ioaddr + SDHCI_SOFTWARE_RESET,
+ val, !(val & (SDHCI_RESET_CMD | SDHCI_RESET_DATA)),
+ 10, SDHCI_PANIC_RESET_TIMEOUT_US);
+
+ spin_lock_irqsave(&host->lock, flags);
+ host->cmd = NULL;
+ host->data = NULL;
+ host->data_cmd = NULL;
+ host->mrqs_done[0] = NULL;
+ host->mrqs_done[1] = NULL;
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ if (host->ops && host->ops->panic_prepare)
+ host->ops->panic_prepare(host);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(sdhci_panic_prepare);
+
+/**
+ * sdhci_panic_poll_completion - Poll SDHCI registers for request completion
+ * @mmc: MMC host structure
+ * @mrq: MMC request being polled for completion
+ *
+ * Checks interrupt status and present state registers to determine if a
+ * request has completed. Used during panic when interrupts are disabled.
+ */
+bool sdhci_panic_poll_completion(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ unsigned int poll_count;
+ u32 int_status, present;
+
+ for (poll_count = 0; poll_count < SDHCI_PANIC_POLL_ITERATIONS;
+ poll_count++) {
+ cpu_relax();
+ udelay(SDHCI_PANIC_POLL_DELAY_US);
+
+ int_status = sdhci_readl(host, SDHCI_INT_STATUS);
+
+ if (int_status & SDHCI_INT_ERROR) {
+ if (mrq->cmd)
+ mrq->cmd->error = -EIO;
+ if (mrq->data)
+ mrq->data->error = -EIO;
+ sdhci_writel(host, int_status, SDHCI_INT_STATUS);
+ return true;
+ }
+
+ if (int_status & SDHCI_INT_RESPONSE)
+ sdhci_writel(host, SDHCI_INT_RESPONSE,
+ SDHCI_INT_STATUS);
+
+ if (int_status & SDHCI_INT_DATA_END)
+ sdhci_writel(host, SDHCI_INT_DATA_END,
+ SDHCI_INT_STATUS);
+
+ /*
+ * Use the same completion heuristic as the working v5
+ * implementation: after a minimum number of poll
+ * iterations, treat the request as complete when the
+ * DATA_INHIBIT bit clears (controller is idle).
+ */
+ if (poll_count >= SDHCI_PANIC_MIN_POLL_COUNT) {
+ present = sdhci_readl(host, SDHCI_PRESENT_STATE);
+ if (!(present & SDHCI_DATA_INHIBIT))
+ return true;
+ }
+ }
+
+ if (mrq->cmd)
+ mrq->cmd->error = -ETIMEDOUT;
+ if (mrq->data)
+ mrq->data->error = -ETIMEDOUT;
+ return false;
+}
+EXPORT_SYMBOL_GPL(sdhci_panic_poll_completion);
+
+/**
+ * sdhci_panic_complete - Clean up SDHCI state after a panic-context request
+ * @mmc: MMC host structure
+ * @mrq: MMC request that has completed
+ *
+ * Clears host software state under spinlock so the next panic request
+ * starts clean.
+ */
+void sdhci_panic_complete(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ unsigned long flags;
+
+ spin_lock_irqsave(&host->lock, flags);
+ host->cmd = NULL;
+ host->data = NULL;
+ host->data_cmd = NULL;
+ host->mrqs_done[0] = NULL;
+ host->mrqs_done[1] = NULL;
+ spin_unlock_irqrestore(&host->lock, flags);
+}
+EXPORT_SYMBOL_GPL(sdhci_panic_complete);
+
static const struct mmc_host_ops sdhci_ops = {
.request = sdhci_request,
.post_req = sdhci_post_req,
@@ -3091,6 +3243,9 @@ static const struct mmc_host_ops sdhci_ops = {
.execute_tuning = sdhci_execute_tuning,
.card_event = sdhci_card_event,
.card_busy = sdhci_card_busy,
+ .panic_prepare = sdhci_panic_prepare,
+ .panic_poll_completion = sdhci_panic_poll_completion,
+ .panic_complete = sdhci_panic_complete,
};
/*****************************************************************************\
@@ -3242,6 +3397,9 @@ static void sdhci_timeout_timer(struct timer_list *t)
host = timer_container_of(host, t, timer);
+ if (oops_in_progress)
+ return;
+
spin_lock_irqsave(&host->lock, flags);
if (host->cmd && !sdhci_data_line_cmd(host->cmd)) {
@@ -3264,6 +3422,9 @@ static void sdhci_timeout_data_timer(struct timer_list *t)
host = timer_container_of(host, t, data_timer);
+ if (oops_in_progress)
+ return;
+
spin_lock_irqsave(&host->lock, flags);
if (host->data || host->data_cmd ||
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index b6a571d866fa..396eca56439f 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -724,6 +724,7 @@ struct sdhci_ops {
void (*dump_vendor_regs)(struct sdhci_host *host);
void (*dump_uhs2_regs)(struct sdhci_host *host);
void (*uhs2_pre_detect_init)(struct sdhci_host *host);
+ void (*panic_prepare)(struct sdhci_host *host);
};
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
@@ -906,6 +907,11 @@ void sdhci_switch_external_dma(struct sdhci_host *host, bool en);
void sdhci_set_data_timeout_irq(struct sdhci_host *host, bool enable);
void __sdhci_set_timeout(struct sdhci_host *host, struct mmc_command *cmd);
+int sdhci_panic_prepare(struct mmc_host *mmc);
+bool sdhci_panic_poll_completion(struct mmc_host *mmc,
+ struct mmc_request *mrq);
+void sdhci_panic_complete(struct mmc_host *mmc, struct mmc_request *mrq);
+
#if defined(CONFIG_DYNAMIC_DEBUG) || \
(defined(CONFIG_DYNAMIC_DEBUG_CORE) && defined(DYNAMIC_DEBUG_MODULE))
#define SDHCI_DBG_ANYWAY 0
--
2.34.1
next prev parent reply other threads:[~2026-03-20 20:33 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-20 20:32 [PATCH v4 0/4] mmc: Add pstore backend for crash dump storage on eMMC Kamal Dasu
2026-03-20 20:32 ` [PATCH v4 1/4] mmc: core: Add panic-context host operations for pstore backends Kamal Dasu
2026-03-20 20:32 ` Kamal Dasu [this message]
2026-03-20 20:32 ` [PATCH v4 3/4] mmc: block: Add helper to look up mmc_card by device name Kamal Dasu
2026-03-20 20:32 ` [PATCH v4 4/4] mmc: core: Add MMC pstore backend driver Kamal Dasu
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=20260320203249.1965044-3-kamal.dasu@broadcom.com \
--to=kamal.dasu@broadcom.com \
--cc=adrian.hunter@intel.com \
--cc=arend.vanspriel@broadcom.com \
--cc=bcm-kernel-feedback-list@broadcom.com \
--cc=florian.fainelli@broadcom.com \
--cc=gpiccoli@igalia.com \
--cc=kees@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mmc@vger.kernel.org \
--cc=tony.luck@intel.com \
--cc=ulf.hansson@linaro.org \
--cc=william.zhang@broadcom.com \
/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