public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/6] mmc: handle undervoltage events and prevent eMMC corruption
@ 2025-02-20  7:44 Oleksij Rempel
  2025-02-20  7:44 ` [PATCH v2 1/6] mmc: core: Handle undervoltage events and register regulator notifiers Oleksij Rempel
                   ` (5 more replies)
  0 siblings, 6 replies; 11+ messages in thread
From: Oleksij Rempel @ 2025-02-20  7:44 UTC (permalink / raw)
  To: Ulf Hansson
  Cc: Oleksij Rempel, kernel, linux-kernel, linux-mmc,
	Greg Kroah-Hartman, Mark Brown, Rafael J. Wysocki,
	Søren Andersen, Christian Loehle

This patch set introduces a framework for handling undervoltage events
in the MMC subsystem. The goal is to improve system reliability by
ensuring graceful handling of power fluctuations that could otherwise
lead to metadata corruption, potentially rendering the eMMC chip
unusable or causing significant data loss.

## Problem Statement

Power fluctuations and sudden losses can leave eMMC devices in an
undefined state, leading to severe consequences. The worst case can
result in metadata corruption, making the entire storage inaccessible.
While some eMMC devices promise to handle such situations internally,
experience shows that some chip variants are still affected. This has
led vendors to take a more protective approach, implementing external
undervoltage handling as a precautionary measure to avoid costly field
failures and returns.

The existence of the "Power Off Notification" feature in the eMMC
standard itself serves as indirect evidence that this is a real-world
issue.  While some projects have already faced the consequences of
ignoring this problem (often at significant cost), specific cases cannot
be disclosed due to NDAs.

## Challenges and Implementation Approach

1. **Raising awareness of the problem**: While vendors have used
   proprietary solutions for years, a unified approach is needed upstream.
   This patch set is a first step in making that happen.

2. **Finding an acceptable implementation path**: There are multiple
   ways to handle undervoltage - either in the kernel or in user space,
   through a global shutdown mechanism, or using the regulator framework.
   This patch set takes the kernel-based approach but does not prevent
   future extensions, such as allowing user-space handoff once available.

3. **Preparing for vendor adoption and testing**: By providing a
   structured solution upstream, this patch set lowers the barrier for
   vendors to standardize their undervoltage handling instead of relying on
   fragmented, out-of-tree implementations.

## Current Limitations

This patch set is an initial step and does not yet cover all possible
design restrictions or edge cases. Future improvements may include
better coordination with user space and enhancements based on broader
testing.

## Testing Details

The implementation was tested on an iMX8MP-based system. The board had
approximately 100ms of available power hold-up time. The Power Off
Notification was sent ~4ms after the board was detached from the power
supply, allowing sufficient time for the eMMC to handle the event
properly.  Tests were conducted under both idle conditions and active
read/write operations.

Oleksij Rempel (6):
  mmc: core: Handle undervoltage events and register regulator notifiers
  mmc: core: make mmc_interrupt_hpi() global
  mmc: core: refactor _mmc_suspend() for undervoltage handling
  mmc: core: add undervoltage handler for MMC/eMMC devices
  mmc: block: abort requests and suppress errors after undervoltage
    shutdown
  mmc: sdhci: prevent command execution after undervoltage shutdown

 drivers/mmc/core/block.c     |   2 +-
 drivers/mmc/core/core.c      |  20 ++++++
 drivers/mmc/core/core.h      |   2 +
 drivers/mmc/core/mmc.c       | 101 ++++++++++++++++++++++++------
 drivers/mmc/core/mmc_ops.c   |   2 +-
 drivers/mmc/core/mmc_ops.h   |   1 +
 drivers/mmc/core/queue.c     |   2 +-
 drivers/mmc/core/regulator.c | 115 +++++++++++++++++++++++++++++++++++
 drivers/mmc/host/sdhci.c     |   9 +++
 include/linux/mmc/host.h     |   9 +++
 10 files changed, 241 insertions(+), 22 deletions(-)

--
2.39.5


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH v2 1/6] mmc: core: Handle undervoltage events and register regulator notifiers
  2025-02-20  7:44 [PATCH v2 0/6] mmc: handle undervoltage events and prevent eMMC corruption Oleksij Rempel
@ 2025-02-20  7:44 ` Oleksij Rempel
  2025-02-20  7:44 ` [PATCH v2 2/6] mmc: core: make mmc_interrupt_hpi() global Oleksij Rempel
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 11+ messages in thread
From: Oleksij Rempel @ 2025-02-20  7:44 UTC (permalink / raw)
  To: Ulf Hansson
  Cc: Oleksij Rempel, kernel, linux-kernel, linux-mmc,
	Greg Kroah-Hartman, Mark Brown, Rafael J. Wysocki,
	Søren Andersen, Christian Loehle

Extend the MMC core to handle undervoltage events by implementing
infrastructure to notify the MMC bus about voltage drops.

Background & Decision at LPC24:

This solution was proposed and refined during LPC24 in the talk
"Graceful Under Pressure: Prioritizing Shutdown to Protect Your Data in
Embedded Systems," which aimed to address how Linux should handle power
fluctuations in embedded devices to prevent data corruption or storage
damage.

At the time, multiple possible solutions were considered:
1. Triggering a system-wide suspend or shutdown: when undervoltage is
   detected, with device-specific prioritization to ensure critical
   components shut down first.
   - This approach was disliked by Greg Kroah-Hartman, as it introduced
     complexity and was not suitable for all use cases.

2. Notifying relevant devices through the regulator framework: to allow
   graceful per-device handling.
   - This approach was agreed upon as the most acceptable by participants
     in the discussion, including Greg Kroah-Hartman, Mark Brown,
     and Rafael J. Wysocki.
   - This patch implements that decision by integrating undervoltage
     handling into the MMC subsystem.

Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
---
 drivers/mmc/core/core.c      |  20 ++++++
 drivers/mmc/core/core.h      |   2 +
 drivers/mmc/core/regulator.c | 115 +++++++++++++++++++++++++++++++++++
 include/linux/mmc/host.h     |   8 +++
 4 files changed, 145 insertions(+)

diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 5241528f8b90..3ee6d83eb354 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -1399,6 +1399,26 @@ void mmc_power_cycle(struct mmc_host *host, u32 ocr)
 	mmc_power_up(host, ocr);
 }
 
+/**
+ * mmc_handle_undervoltage - Handle an undervoltage event on the MMC bus
+ * @host: The MMC host that detected the undervoltage condition
+ *
+ * This function is called when an undervoltage event is detected on one of
+ * the MMC regulators.
+ *
+ * Returns: 0 on success or a negative error code on failure.
+ */
+int mmc_handle_undervoltage(struct mmc_host *host)
+{
+	if (!host->bus_ops->handle_undervoltage)
+		return 0;
+
+	dev_warn(mmc_dev(host), "%s: Undervoltage detected, initiating emergency stop\n",
+		 mmc_hostname(host));
+
+	return host->bus_ops->handle_undervoltage(host);
+}
+
 /*
  * Assign a mmc bus handler to a host. Only one bus handler may control a
  * host at any given time.
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index fc9c066e6468..b77f053039ab 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -31,6 +31,7 @@ struct mmc_bus_ops {
 	int (*sw_reset)(struct mmc_host *);
 	bool (*cache_enabled)(struct mmc_host *);
 	int (*flush_cache)(struct mmc_host *);
+	int (*handle_undervoltage)(struct mmc_host *host);
 };
 
 void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops);
@@ -59,6 +60,7 @@ void mmc_power_off(struct mmc_host *host);
 void mmc_power_cycle(struct mmc_host *host, u32 ocr);
 void mmc_set_initial_state(struct mmc_host *host);
 u32 mmc_vddrange_to_ocrmask(int vdd_min, int vdd_max);
+int mmc_handle_undervoltage(struct mmc_host *host);
 
 static inline void mmc_delay(unsigned int ms)
 {
diff --git a/drivers/mmc/core/regulator.c b/drivers/mmc/core/regulator.c
index 3dae2e9b7978..d3f3eb8ea3d7 100644
--- a/drivers/mmc/core/regulator.c
+++ b/drivers/mmc/core/regulator.c
@@ -7,6 +7,7 @@
 #include <linux/err.h>
 #include <linux/log2.h>
 #include <linux/regulator/consumer.h>
+#include <linux/workqueue.h>
 
 #include <linux/mmc/host.h>
 
@@ -262,6 +263,98 @@ static inline int mmc_regulator_get_ocrmask(struct regulator *supply)
 
 #endif /* CONFIG_REGULATOR */
 
+static void mmc_undervoltage_workfn(struct work_struct *work)
+{
+	struct mmc_supply *supply;
+	struct mmc_host *mmc;
+
+	supply = container_of(work, struct mmc_supply, uv_work);
+	mmc = container_of(supply, struct mmc_host, supply);
+
+	mmc_handle_undervoltage(mmc);
+}
+
+static int mmc_handle_regulator_event(struct mmc_host *mmc,
+				      const char *regulator_name,
+				      unsigned long event)
+{
+	switch (event) {
+	case REGULATOR_EVENT_UNDER_VOLTAGE:
+		if (mmc->undervoltage)
+			return NOTIFY_OK;
+
+		mmc->undervoltage = true;
+		queue_work(system_highpri_wq, &mmc->supply.uv_work);
+		break;
+	default:
+		return NOTIFY_DONE;
+	}
+
+	return NOTIFY_OK;
+}
+
+static int mmc_vmmc_notifier_callback(struct notifier_block *nb,
+				      unsigned long event, void *data)
+{
+	struct mmc_supply *supply;
+	struct mmc_host *mmc;
+
+	supply = container_of(nb, struct mmc_supply, vmmc_nb);
+	mmc = container_of(supply, struct mmc_host, supply);
+
+	return mmc_handle_regulator_event(mmc, "vmmc", event);
+}
+
+static int mmc_vqmmc_notifier_callback(struct notifier_block *nb,
+				       unsigned long event, void *data)
+{
+	struct mmc_supply *supply;
+	struct mmc_host *mmc;
+
+	supply = container_of(nb, struct mmc_supply, vqmmc_nb);
+	mmc = container_of(supply, struct mmc_host, supply);
+
+	return mmc_handle_regulator_event(mmc, "vqmmc", event);
+}
+
+static int mmc_vqmmc2_notifier_callback(struct notifier_block *nb,
+					unsigned long event, void *data)
+{
+	struct mmc_supply *supply;
+	struct mmc_host *mmc;
+
+	supply = container_of(nb, struct mmc_supply, vqmmc2_nb);
+	mmc = container_of(supply, struct mmc_host, supply);
+
+	return mmc_handle_regulator_event(mmc, "vqmmc2", event);
+}
+
+static void
+mmc_register_regulator_notifier(struct mmc_host *mmc,
+				struct regulator *regulator,
+				struct notifier_block *nb,
+				int (*callback)(struct notifier_block *,
+						unsigned long, void *),
+				const char *name)
+{
+	struct device *dev = mmc_dev(mmc);
+	int ret;
+
+	nb->notifier_call = callback;
+	ret = devm_regulator_register_notifier(regulator, nb);
+	if (ret)
+		dev_warn(dev, "Failed to register %s notifier: %pe\n", name,
+			 ERR_PTR(ret));
+}
+
+static void mmc_undervoltage_work_cleanup(void *data)
+{
+	struct mmc_supply *supply = data;
+
+	/* Ensure the work is canceled or flushed here */
+	cancel_work_sync(&supply->uv_work);
+}
+
 /**
  * mmc_regulator_get_supply - try to get VMMC and VQMMC regulators for a host
  * @mmc: the host to regulate
@@ -281,6 +374,13 @@ int mmc_regulator_get_supply(struct mmc_host *mmc)
 	mmc->supply.vqmmc = devm_regulator_get_optional(dev, "vqmmc");
 	mmc->supply.vqmmc2 = devm_regulator_get_optional(dev, "vqmmc2");
 
+	INIT_WORK(&mmc->supply.uv_work, mmc_undervoltage_workfn);
+
+	ret = devm_add_action_or_reset(dev, mmc_undervoltage_work_cleanup,
+				       &mmc->supply);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to add cleanup action\n");
+
 	if (IS_ERR(mmc->supply.vmmc)) {
 		if (PTR_ERR(mmc->supply.vmmc) == -EPROBE_DEFER)
 			return dev_err_probe(dev, -EPROBE_DEFER,
@@ -293,6 +393,11 @@ int mmc_regulator_get_supply(struct mmc_host *mmc)
 			mmc->ocr_avail = ret;
 		else
 			dev_warn(dev, "Failed getting OCR mask: %d\n", ret);
+
+		mmc_register_regulator_notifier(mmc, mmc->supply.vmmc,
+						&mmc->supply.vmmc_nb,
+						mmc_vmmc_notifier_callback,
+						"vmmc");
 	}
 
 	if (IS_ERR(mmc->supply.vqmmc)) {
@@ -301,12 +406,22 @@ int mmc_regulator_get_supply(struct mmc_host *mmc)
 					     "vqmmc regulator not available\n");
 
 		dev_dbg(dev, "No vqmmc regulator found\n");
+	} else {
+		mmc_register_regulator_notifier(mmc, mmc->supply.vqmmc,
+						&mmc->supply.vqmmc_nb,
+						mmc_vqmmc_notifier_callback,
+						"vqmmc");
 	}
 
 	if (IS_ERR(mmc->supply.vqmmc2)) {
 		if (PTR_ERR(mmc->supply.vqmmc2) == -EPROBE_DEFER)
 			return -EPROBE_DEFER;
 		dev_dbg(dev, "No vqmmc2 regulator found\n");
+	} else {
+		mmc_register_regulator_notifier(mmc, mmc->supply.vqmmc2,
+						&mmc->supply.vqmmc2_nb,
+						mmc_vqmmc2_notifier_callback,
+						"vqmmc2");
 	}
 
 	return 0;
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 68f09a955a90..4e147ad82804 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -11,6 +11,7 @@
 #include <linux/device.h>
 #include <linux/fault-inject.h>
 #include <linux/debugfs.h>
+#include <linux/workqueue.h>
 
 #include <linux/mmc/core.h>
 #include <linux/mmc/card.h>
@@ -342,6 +343,12 @@ struct mmc_supply {
 	struct regulator *vmmc;		/* Card power supply */
 	struct regulator *vqmmc;	/* Optional Vccq supply */
 	struct regulator *vqmmc2;	/* Optional supply for phy */
+
+	struct notifier_block vmmc_nb;		/* Notifier for vmmc */
+	struct notifier_block vqmmc_nb;		/* Notifier for vqmmc */
+	struct notifier_block vqmmc2_nb;	/* Notifier for vqmmc2 */
+
+	struct work_struct uv_work;		/* Undervoltage work */
 };
 
 struct mmc_ctx {
@@ -493,6 +500,7 @@ struct mmc_host {
 	unsigned int		retune_crc_disable:1; /* don't trigger retune upon crc */
 	unsigned int		can_dma_map_merge:1; /* merging can be used */
 	unsigned int		vqmmc_enabled:1; /* vqmmc regulator is enabled */
+	unsigned int		undervoltage:1;	 /* Undervoltage state */
 
 	int			rescan_disable;	/* disable card detection */
 	int			rescan_entered;	/* used with nonremovable devices */
-- 
2.39.5


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v2 2/6] mmc: core: make mmc_interrupt_hpi() global
  2025-02-20  7:44 [PATCH v2 0/6] mmc: handle undervoltage events and prevent eMMC corruption Oleksij Rempel
  2025-02-20  7:44 ` [PATCH v2 1/6] mmc: core: Handle undervoltage events and register regulator notifiers Oleksij Rempel
@ 2025-02-20  7:44 ` Oleksij Rempel
  2025-02-20  7:44 ` [PATCH v2 3/6] mmc: core: refactor _mmc_suspend() for undervoltage handling Oleksij Rempel
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 11+ messages in thread
From: Oleksij Rempel @ 2025-02-20  7:44 UTC (permalink / raw)
  To: Ulf Hansson
  Cc: Oleksij Rempel, kernel, linux-kernel, linux-mmc,
	Greg Kroah-Hartman, Mark Brown, Rafael J. Wysocki,
	Søren Andersen, Christian Loehle

Make mmc_interrupt_hpi() non-static. This enables usage of HPI outside
mmc_ops.c and will be used in a follow-up patch.

Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
---
 drivers/mmc/core/mmc_ops.c | 2 +-
 drivers/mmc/core/mmc_ops.h | 1 +
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
index 5c8e62e8f331..dbb1b5ec4132 100644
--- a/drivers/mmc/core/mmc_ops.c
+++ b/drivers/mmc/core/mmc_ops.c
@@ -903,7 +903,7 @@ static int mmc_send_hpi_cmd(struct mmc_card *card)
  *	Issued High Priority Interrupt, and check for card status
  *	until out-of prg-state.
  */
-static int mmc_interrupt_hpi(struct mmc_card *card)
+int mmc_interrupt_hpi(struct mmc_card *card)
 {
 	int err;
 	u32 status;
diff --git a/drivers/mmc/core/mmc_ops.h b/drivers/mmc/core/mmc_ops.h
index 0df3ebd900d1..a16361dc5909 100644
--- a/drivers/mmc/core/mmc_ops.h
+++ b/drivers/mmc/core/mmc_ops.h
@@ -37,6 +37,7 @@ int mmc_send_cid(struct mmc_host *host, u32 *cid);
 int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp);
 int mmc_spi_set_crc(struct mmc_host *host, int use_crc);
 int mmc_bus_test(struct mmc_card *card, u8 bus_width);
+int mmc_interrupt_hpi(struct mmc_card *card);
 int mmc_can_ext_csd(struct mmc_card *card);
 int mmc_switch_status(struct mmc_card *card, bool crc_err_fatal);
 bool mmc_prepare_busy_cmd(struct mmc_host *host, struct mmc_command *cmd,
-- 
2.39.5


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v2 3/6] mmc: core: refactor _mmc_suspend() for undervoltage handling
  2025-02-20  7:44 [PATCH v2 0/6] mmc: handle undervoltage events and prevent eMMC corruption Oleksij Rempel
  2025-02-20  7:44 ` [PATCH v2 1/6] mmc: core: Handle undervoltage events and register regulator notifiers Oleksij Rempel
  2025-02-20  7:44 ` [PATCH v2 2/6] mmc: core: make mmc_interrupt_hpi() global Oleksij Rempel
@ 2025-02-20  7:44 ` Oleksij Rempel
  2025-02-20 11:05   ` Christian Loehle
  2025-02-20  7:44 ` [PATCH v2 4/6] mmc: core: add undervoltage handler for MMC/eMMC devices Oleksij Rempel
                   ` (2 subsequent siblings)
  5 siblings, 1 reply; 11+ messages in thread
From: Oleksij Rempel @ 2025-02-20  7:44 UTC (permalink / raw)
  To: Ulf Hansson
  Cc: Oleksij Rempel, kernel, linux-kernel, linux-mmc,
	Greg Kroah-Hartman, Mark Brown, Rafael J. Wysocki,
	Søren Andersen, Christian Loehle

Introduce an is_undervoltage parameter to _mmc_suspend() to apply a
short power-off sequence and optionally flush the cache. This refactoring
prepares for undervoltage support in a follow-up patch.

Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
---
 drivers/mmc/core/mmc.c | 25 ++++++++++++++++---------
 1 file changed, 16 insertions(+), 9 deletions(-)

diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 6a23be214543..86b608843232 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -2104,20 +2104,27 @@ static int _mmc_flush_cache(struct mmc_host *host)
 	return err;
 }
 
-static int _mmc_suspend(struct mmc_host *host, bool is_suspend)
+static int _mmc_suspend(struct mmc_host *host, bool is_suspend,
+			bool is_undervoltage)
 {
+	unsigned int notify_type;
 	int err = 0;
-	unsigned int notify_type = is_suspend ? EXT_CSD_POWER_OFF_SHORT :
-					EXT_CSD_POWER_OFF_LONG;
+
+	if (is_undervoltage || is_suspend)
+		notify_type = EXT_CSD_POWER_OFF_SHORT;
+	else
+		notify_type = EXT_CSD_POWER_OFF_LONG;
 
 	mmc_claim_host(host);
 
 	if (mmc_card_suspended(host->card))
 		goto out;
 
-	err = _mmc_flush_cache(host);
-	if (err)
-		goto out;
+	if (is_undervoltage) {
+		err = _mmc_flush_cache(host);
+		if (err)
+			goto out;
+	}
 
 	if (mmc_can_poweroff_notify(host->card) &&
 	    ((host->caps2 & MMC_CAP2_FULL_PWR_CYCLE) || !is_suspend ||
@@ -2144,7 +2151,7 @@ static int mmc_suspend(struct mmc_host *host)
 {
 	int err;
 
-	err = _mmc_suspend(host, true);
+	err = _mmc_suspend(host, true, false);
 	if (!err) {
 		pm_runtime_disable(&host->card->dev);
 		pm_runtime_set_suspended(&host->card->dev);
@@ -2191,7 +2198,7 @@ static int mmc_shutdown(struct mmc_host *host)
 		err = _mmc_resume(host);
 
 	if (!err)
-		err = _mmc_suspend(host, false);
+		err = _mmc_suspend(host, false, false);
 
 	return err;
 }
@@ -2215,7 +2222,7 @@ static int mmc_runtime_suspend(struct mmc_host *host)
 	if (!(host->caps & MMC_CAP_AGGRESSIVE_PM))
 		return 0;
 
-	err = _mmc_suspend(host, true);
+	err = _mmc_suspend(host, true, false);
 	if (err)
 		pr_err("%s: error %d doing aggressive suspend\n",
 			mmc_hostname(host), err);
-- 
2.39.5


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v2 4/6] mmc: core: add undervoltage handler for MMC/eMMC devices
  2025-02-20  7:44 [PATCH v2 0/6] mmc: handle undervoltage events and prevent eMMC corruption Oleksij Rempel
                   ` (2 preceding siblings ...)
  2025-02-20  7:44 ` [PATCH v2 3/6] mmc: core: refactor _mmc_suspend() for undervoltage handling Oleksij Rempel
@ 2025-02-20  7:44 ` Oleksij Rempel
  2025-02-20 10:47   ` Christian Loehle
  2025-02-20  7:44 ` [PATCH v2 5/6] mmc: block: abort requests and suppress errors after undervoltage shutdown Oleksij Rempel
  2025-02-20  7:44 ` [PATCH v2 6/6] mmc: sdhci: prevent command execution " Oleksij Rempel
  5 siblings, 1 reply; 11+ messages in thread
From: Oleksij Rempel @ 2025-02-20  7:44 UTC (permalink / raw)
  To: Ulf Hansson
  Cc: Oleksij Rempel, kernel, linux-kernel, linux-mmc,
	Greg Kroah-Hartman, Mark Brown, Rafael J. Wysocki,
	Søren Andersen, Christian Loehle

Introduce _mmc_handle_undervoltage() to handle undervoltage events for
MMC/eMMC devices. This function interrupts ongoing operations using HPI
and performs a controlled suspend. After completing the sequence, the card
is marked as removed to prevent further interactions.

To support this, introduce __mmc_suspend() and __mmc_resume() as internal
helpers that omit mmc_claim_host()/mmc_release_host(), allowing them to be
called when the host is already claimed. Update mmc_shutdown() to skip the
normal shutdown sequence if the host is flagged as undervoltage to avoid
repeating of the shutdown procedure.

Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
---
 drivers/mmc/core/mmc.c | 81 +++++++++++++++++++++++++++++++++++-------
 1 file changed, 68 insertions(+), 13 deletions(-)

diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 86b608843232..e626213e7a52 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -2104,8 +2104,8 @@ static int _mmc_flush_cache(struct mmc_host *host)
 	return err;
 }
 
-static int _mmc_suspend(struct mmc_host *host, bool is_suspend,
-			bool is_undervoltage)
+static int __mmc_suspend(struct mmc_host *host, bool is_suspend,
+			 bool is_undervoltage)
 {
 	unsigned int notify_type;
 	int err = 0;
@@ -2115,8 +2115,6 @@ static int _mmc_suspend(struct mmc_host *host, bool is_suspend,
 	else
 		notify_type = EXT_CSD_POWER_OFF_LONG;
 
-	mmc_claim_host(host);
-
 	if (mmc_card_suspended(host->card))
 		goto out;
 
@@ -2140,7 +2138,18 @@ static int _mmc_suspend(struct mmc_host *host, bool is_suspend,
 		mmc_card_set_suspended(host->card);
 	}
 out:
+	return err;
+}
+
+static int _mmc_suspend(struct mmc_host *host, bool is_suspend,
+			bool is_undervoltage)
+{
+	int err;
+
+	mmc_claim_host(host);
+	err = __mmc_suspend(host, is_suspend, is_undervoltage);
 	mmc_release_host(host);
+
 	return err;
 }
 
@@ -2160,6 +2169,20 @@ static int mmc_suspend(struct mmc_host *host)
 	return err;
 }
 
+static int __mmc_resume(struct mmc_host *host)
+{
+	int err;
+
+	if (!mmc_card_suspended(host->card))
+		return 0;
+
+	mmc_power_up(host, host->card->ocr);
+	err = mmc_init_card(host, host->card->ocr, host->card);
+	mmc_card_clr_suspended(host->card);
+
+	return err;
+}
+
 /*
  * This function tries to determine if the same card is still present
  * and, if so, restore all state to it.
@@ -2169,16 +2192,9 @@ static int _mmc_resume(struct mmc_host *host)
 	int err = 0;
 
 	mmc_claim_host(host);
-
-	if (!mmc_card_suspended(host->card))
-		goto out;
-
-	mmc_power_up(host, host->card->ocr);
-	err = mmc_init_card(host, host->card->ocr, host->card);
-	mmc_card_clr_suspended(host->card);
-
-out:
+	err = __mmc_resume(host);
 	mmc_release_host(host);
+
 	return err;
 }
 
@@ -2189,6 +2205,9 @@ static int mmc_shutdown(struct mmc_host *host)
 {
 	int err = 0;
 
+	if (host->undervoltage)
+		return 0;
+
 	/*
 	 * In a specific case for poweroff notify, we need to resume the card
 	 * before we can shutdown it properly.
@@ -2280,6 +2299,41 @@ static int _mmc_hw_reset(struct mmc_host *host)
 	return mmc_init_card(host, card->ocr, card);
 }
 
+static int _mmc_handle_undervoltage(struct mmc_host *host)
+{
+	struct mmc_card *card = host->card;
+	int err = 0;
+
+	mmc_claim_host(host);
+
+	if (!host->card)
+		goto out;
+
+	if (mmc_can_poweroff_notify(host->card) &&
+		!(host->caps2 & MMC_CAP2_FULL_PWR_CYCLE))
+		err = __mmc_resume(host);
+
+	if (!err) {
+		err = mmc_interrupt_hpi(card);
+		if (err)
+			pr_err("%s: Interrupt HPI failed, error %d\n",
+				mmc_hostname(host), err);
+
+		err = __mmc_suspend(host, false, true);
+	}
+
+	if (err)
+		pr_err("%s: Undervoltage emergency stop failed\n",
+			mmc_hostname(host));
+
+	mmc_card_set_removed(host->card);
+
+out:
+	mmc_release_host(host);
+
+	return err;
+}
+
 static const struct mmc_bus_ops mmc_ops = {
 	.remove = mmc_remove,
 	.detect = mmc_detect,
@@ -2292,6 +2346,7 @@ static const struct mmc_bus_ops mmc_ops = {
 	.hw_reset = _mmc_hw_reset,
 	.cache_enabled = _mmc_cache_enabled,
 	.flush_cache = _mmc_flush_cache,
+	.handle_undervoltage = _mmc_handle_undervoltage,
 };
 
 /*
-- 
2.39.5


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v2 5/6] mmc: block: abort requests and suppress errors after undervoltage shutdown
  2025-02-20  7:44 [PATCH v2 0/6] mmc: handle undervoltage events and prevent eMMC corruption Oleksij Rempel
                   ` (3 preceding siblings ...)
  2025-02-20  7:44 ` [PATCH v2 4/6] mmc: core: add undervoltage handler for MMC/eMMC devices Oleksij Rempel
@ 2025-02-20  7:44 ` Oleksij Rempel
  2025-02-20  7:44 ` [PATCH v2 6/6] mmc: sdhci: prevent command execution " Oleksij Rempel
  5 siblings, 0 replies; 11+ messages in thread
From: Oleksij Rempel @ 2025-02-20  7:44 UTC (permalink / raw)
  To: Ulf Hansson
  Cc: Oleksij Rempel, kernel, linux-kernel, linux-mmc,
	Greg Kroah-Hartman, Mark Brown, Rafael J. Wysocki,
	Søren Andersen, Christian Loehle

Extend the existing card removal checks in mmc_blk_mq_complete_rq() and
mmc_mq_queue_rq() to also account for the undervoltage state. If the host
has entered undervoltage shutdown, mark requests as quiet and abort them
early to prevent unnecessary retries and error logging. This ensures no
further operations are attempted on the card after an emergency stop.

Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
---
 drivers/mmc/core/block.c | 2 +-
 drivers/mmc/core/queue.c | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c
index 4830628510e6..ecb87da0e257 100644
--- a/drivers/mmc/core/block.c
+++ b/drivers/mmc/core/block.c
@@ -2129,7 +2129,7 @@ static void mmc_blk_mq_complete_rq(struct mmc_queue *mq, struct request *req)
 	} else if (mqrq->retries++ < MMC_MAX_RETRIES) {
 		blk_mq_requeue_request(req, true);
 	} else {
-		if (mmc_card_removed(mq->card))
+		if (mmc_card_removed(mq->card) || mq->card->host->undervoltage)
 			req->rq_flags |= RQF_QUIET;
 		blk_mq_end_request(req, BLK_STS_IOERR);
 	}
diff --git a/drivers/mmc/core/queue.c b/drivers/mmc/core/queue.c
index ab662f502fe7..f46e01988fe8 100644
--- a/drivers/mmc/core/queue.c
+++ b/drivers/mmc/core/queue.c
@@ -239,7 +239,7 @@ static blk_status_t mmc_mq_queue_rq(struct blk_mq_hw_ctx *hctx,
 	bool get_card, cqe_retune_ok;
 	blk_status_t ret;
 
-	if (mmc_card_removed(mq->card)) {
+	if (mmc_card_removed(mq->card) || mq->card->host->undervoltage) {
 		req->rq_flags |= RQF_QUIET;
 		return BLK_STS_IOERR;
 	}
-- 
2.39.5


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v2 6/6] mmc: sdhci: prevent command execution after undervoltage shutdown
  2025-02-20  7:44 [PATCH v2 0/6] mmc: handle undervoltage events and prevent eMMC corruption Oleksij Rempel
                   ` (4 preceding siblings ...)
  2025-02-20  7:44 ` [PATCH v2 5/6] mmc: block: abort requests and suppress errors after undervoltage shutdown Oleksij Rempel
@ 2025-02-20  7:44 ` Oleksij Rempel
  5 siblings, 0 replies; 11+ messages in thread
From: Oleksij Rempel @ 2025-02-20  7:44 UTC (permalink / raw)
  To: Ulf Hansson
  Cc: Oleksij Rempel, kernel, linux-kernel, linux-mmc,
	Greg Kroah-Hartman, Mark Brown, Rafael J. Wysocki,
	Søren Andersen, Christian Loehle

Introduce an emergency_stop flag in struct mmc_host to block further
MMC/SD commands after an undervoltage shutdown. If emergency_stop is
set, sdhci_send_command() will reject new requests with -EBUSY and log a
warning. This helps diagnose and identify code paths that may still
attempt writes after the undervoltage shutdown sequence has completed.

Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
---
 drivers/mmc/core/mmc.c   | 1 +
 drivers/mmc/host/sdhci.c | 9 +++++++++
 include/linux/mmc/host.h | 1 +
 3 files changed, 11 insertions(+)

diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index e626213e7a52..8aa5881293d8 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -2326,6 +2326,7 @@ static int _mmc_handle_undervoltage(struct mmc_host *host)
 		pr_err("%s: Undervoltage emergency stop failed\n",
 			mmc_hostname(host));
 
+	host->emergency_stop = 1;
 	mmc_card_set_removed(host->card);
 
 out:
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index f4a7733a8ad2..8d67f27e7d9e 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1658,6 +1658,15 @@ static bool sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
 
 	WARN_ON(host->cmd);
 
+	if (host->mmc->emergency_stop) {
+		pr_warn("%s: Ignoring normal request, emergency stop is active\n",
+			mmc_hostname(host->mmc));
+		WARN_ON_ONCE(1);
+
+		cmd->error = -EBUSY;
+		return true;
+	}
+
 	/* Initially, a command has no error */
 	cmd->error = 0;
 
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 4e147ad82804..5dfe2cdde59f 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -501,6 +501,7 @@ struct mmc_host {
 	unsigned int		can_dma_map_merge:1; /* merging can be used */
 	unsigned int		vqmmc_enabled:1; /* vqmmc regulator is enabled */
 	unsigned int		undervoltage:1;	 /* Undervoltage state */
+	unsigned int		emergency_stop:1; /* Emergency stop. No transfers are allowed. */
 
 	int			rescan_disable;	/* disable card detection */
 	int			rescan_entered;	/* used with nonremovable devices */
-- 
2.39.5


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [PATCH v2 4/6] mmc: core: add undervoltage handler for MMC/eMMC devices
  2025-02-20  7:44 ` [PATCH v2 4/6] mmc: core: add undervoltage handler for MMC/eMMC devices Oleksij Rempel
@ 2025-02-20 10:47   ` Christian Loehle
  2025-02-20 10:56     ` Oleksij Rempel
  0 siblings, 1 reply; 11+ messages in thread
From: Christian Loehle @ 2025-02-20 10:47 UTC (permalink / raw)
  To: Oleksij Rempel, Ulf Hansson
  Cc: kernel, linux-kernel, linux-mmc, Greg Kroah-Hartman, Mark Brown,
	Rafael J. Wysocki, Søren Andersen

On 2/20/25 07:44, Oleksij Rempel wrote:
> Introduce _mmc_handle_undervoltage() to handle undervoltage events for
> MMC/eMMC devices. This function interrupts ongoing operations using HPI
> and performs a controlled suspend. After completing the sequence, the card
> is marked as removed to prevent further interactions.
> 
> To support this, introduce __mmc_suspend() and __mmc_resume() as internal
> helpers that omit mmc_claim_host()/mmc_release_host(), allowing them to be
> called when the host is already claimed. Update mmc_shutdown() to skip the
> normal shutdown sequence if the host is flagged as undervoltage to avoid
> repeating of the shutdown procedure.

"of" can be removed here.

Given that this introduces large parts of the mmc handling IMO this commit
deserves a lot more explanation of what steps exactly do for which cards
and why.

> 
> Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
> ---
>  drivers/mmc/core/mmc.c | 81 +++++++++++++++++++++++++++++++++++-------
>  1 file changed, 68 insertions(+), 13 deletions(-)
> 
> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
> index 86b608843232..e626213e7a52 100644
> --- a/drivers/mmc/core/mmc.c
> +++ b/drivers/mmc/core/mmc.c
> @@ -2104,8 +2104,8 @@ static int _mmc_flush_cache(struct mmc_host *host)
>  	return err;
>  }
>  
> -static int _mmc_suspend(struct mmc_host *host, bool is_suspend,
> -			bool is_undervoltage)
> +static int __mmc_suspend(struct mmc_host *host, bool is_suspend,
> +			 bool is_undervoltage)

The is_undervoltage doesn't do anything? Did you forget something here?

>  {
>  	unsigned int notify_type;
>  	int err = 0;
> @@ -2115,8 +2115,6 @@ static int _mmc_suspend(struct mmc_host *host, bool is_suspend,
>  	else
>  		notify_type = EXT_CSD_POWER_OFF_LONG;
>  
> -	mmc_claim_host(host);
> -
>  	if (mmc_card_suspended(host->card))
>  		goto out;
>  
> @@ -2140,7 +2138,18 @@ static int _mmc_suspend(struct mmc_host *host, bool is_suspend,
>  		mmc_card_set_suspended(host->card);
>  	}
>  out:
> +	return err;
> +}
> +
> +static int _mmc_suspend(struct mmc_host *host, bool is_suspend,
> +			bool is_undervoltage)
> +{
> +	int err;
> +
> +	mmc_claim_host(host);
> +	err = __mmc_suspend(host, is_suspend, is_undervoltage);
>  	mmc_release_host(host);
> +
>  	return err;
>  }
>  
> @@ -2160,6 +2169,20 @@ static int mmc_suspend(struct mmc_host *host)
>  	return err;
>  }
>  
> +static int __mmc_resume(struct mmc_host *host)
> +{
> +	int err;
> +
> +	if (!mmc_card_suspended(host->card))
> +		return 0;
> +
> +	mmc_power_up(host, host->card->ocr);
> +	err = mmc_init_card(host, host->card->ocr, host->card);
> +	mmc_card_clr_suspended(host->card);
> +
> +	return err;
> +}
> +
>  /*
>   * This function tries to determine if the same card is still present
>   * and, if so, restore all state to it.
> @@ -2169,16 +2192,9 @@ static int _mmc_resume(struct mmc_host *host)
>  	int err = 0;
>  
>  	mmc_claim_host(host);
> -
> -	if (!mmc_card_suspended(host->card))
> -		goto out;
> -
> -	mmc_power_up(host, host->card->ocr);
> -	err = mmc_init_card(host, host->card->ocr, host->card);
> -	mmc_card_clr_suspended(host->card);
> -
> -out:
> +	err = __mmc_resume(host);
>  	mmc_release_host(host);
> +
>  	return err;
>  }
>  
> @@ -2189,6 +2205,9 @@ static int mmc_shutdown(struct mmc_host *host)
>  {
>  	int err = 0;
>  
> +	if (host->undervoltage)
> +		return 0;
> +

Probably deserves a comment.

>  	/*
>  	 * In a specific case for poweroff notify, we need to resume the card
>  	 * before we can shutdown it properly.
> @@ -2280,6 +2299,41 @@ static int _mmc_hw_reset(struct mmc_host *host)
>  	return mmc_init_card(host, card->ocr, card);
>  }
>  
> +static int _mmc_handle_undervoltage(struct mmc_host *host)
> +{
> +	struct mmc_card *card = host->card;
> +	int err = 0;
> +
> +	mmc_claim_host(host);
> +
> +	if (!host->card)
> +		goto out;
> +
> +	if (mmc_can_poweroff_notify(host->card) &&
> +		!(host->caps2 & MMC_CAP2_FULL_PWR_CYCLE))
> +		err = __mmc_resume(host);

I'm not sure I follow this.
Why would we power-up a card that currently doesn't have power when we
know we are about to powerfail it?

> +
> +	if (!err) {
> +		err = mmc_interrupt_hpi(card);
> +		if (err)
> +			pr_err("%s: Interrupt HPI failed, error %d\n",
> +				mmc_hostname(host), err);

There's no point in calling this for SD but I don't see why it currently
wouldn't be called for SD.

> +
> +		err = __mmc_suspend(host, false, true);
> +	}
> +
> +	if (err)
> +		pr_err("%s: Undervoltage emergency stop failed\n",
> +			mmc_hostname(host));
> +
> +	mmc_card_set_removed(host->card);
> +
> +out:
> +	mmc_release_host(host);
> +
> +	return err;
> +}
> +
>  static const struct mmc_bus_ops mmc_ops = {
>  	.remove = mmc_remove,
>  	.detect = mmc_detect,
> @@ -2292,6 +2346,7 @@ static const struct mmc_bus_ops mmc_ops = {
>  	.hw_reset = _mmc_hw_reset,
>  	.cache_enabled = _mmc_cache_enabled,
>  	.flush_cache = _mmc_flush_cache,
> +	.handle_undervoltage = _mmc_handle_undervoltage,
>  };
>  
>  /*


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v2 4/6] mmc: core: add undervoltage handler for MMC/eMMC devices
  2025-02-20 10:47   ` Christian Loehle
@ 2025-02-20 10:56     ` Oleksij Rempel
  2025-02-20 11:22       ` Christian Loehle
  0 siblings, 1 reply; 11+ messages in thread
From: Oleksij Rempel @ 2025-02-20 10:56 UTC (permalink / raw)
  To: Christian Loehle
  Cc: Ulf Hansson, kernel, linux-kernel, linux-mmc, Greg Kroah-Hartman,
	Mark Brown, Rafael J. Wysocki, Søren Andersen

On Thu, Feb 20, 2025 at 10:47:01AM +0000, Christian Loehle wrote:
> On 2/20/25 07:44, Oleksij Rempel wrote:
> > Introduce _mmc_handle_undervoltage() to handle undervoltage events for
> > MMC/eMMC devices. This function interrupts ongoing operations using HPI
> > and performs a controlled suspend. After completing the sequence, the card
> > is marked as removed to prevent further interactions.
> > 
> > To support this, introduce __mmc_suspend() and __mmc_resume() as internal
> > helpers that omit mmc_claim_host()/mmc_release_host(), allowing them to be
> > called when the host is already claimed. Update mmc_shutdown() to skip the
> > normal shutdown sequence if the host is flagged as undervoltage to avoid
> > repeating of the shutdown procedure.
> 
> "of" can be removed here.
> 
> Given that this introduces large parts of the mmc handling IMO this commit
> deserves a lot more explanation of what steps exactly do for which cards
> and why.

ack

> > 
> > Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
> > ---
> >  drivers/mmc/core/mmc.c | 81 +++++++++++++++++++++++++++++++++++-------
> >  1 file changed, 68 insertions(+), 13 deletions(-)
> > 
> > diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
> > index 86b608843232..e626213e7a52 100644
> > --- a/drivers/mmc/core/mmc.c
> > +++ b/drivers/mmc/core/mmc.c
> > @@ -2104,8 +2104,8 @@ static int _mmc_flush_cache(struct mmc_host *host)
> >  	return err;
> >  }
> >  
> > -static int _mmc_suspend(struct mmc_host *host, bool is_suspend,
> > -			bool is_undervoltage)
> > +static int __mmc_suspend(struct mmc_host *host, bool is_suspend,
> > +			 bool is_undervoltage)
> 
> The is_undervoltage doesn't do anything? Did you forget something here?

This was done in the previous patch "mmc: core: refactor _mmc_suspend()
for undervoltage handling"

> > @@ -2189,6 +2205,9 @@ static int mmc_shutdown(struct mmc_host *host)
> >  {
> >  	int err = 0;
> >  
> > +	if (host->undervoltage)
> > +		return 0;
> > +
> 
> Probably deserves a comment.

ack

> >  	/*
> >  	 * In a specific case for poweroff notify, we need to resume the card
> >  	 * before we can shutdown it properly.
> > @@ -2280,6 +2299,41 @@ static int _mmc_hw_reset(struct mmc_host *host)
> >  	return mmc_init_card(host, card->ocr, card);
> >  }
> >  
> > +static int _mmc_handle_undervoltage(struct mmc_host *host)
> > +{
> > +	struct mmc_card *card = host->card;
> > +	int err = 0;
> > +
> > +	mmc_claim_host(host);
> > +
> > +	if (!host->card)
> > +		goto out;
> > +
> > +	if (mmc_can_poweroff_notify(host->card) &&
> > +		!(host->caps2 & MMC_CAP2_FULL_PWR_CYCLE))
> > +		err = __mmc_resume(host);
> 
> I'm not sure I follow this.
> Why would we power-up a card that currently doesn't have power when we
> know we are about to powerfail it?

It is part of the mmc_shutdown, but it is not used on my HW. So, can be
skip it.

> > +
> > +	if (!err) {
> > +		err = mmc_interrupt_hpi(card);
> > +		if (err)
> > +			pr_err("%s: Interrupt HPI failed, error %d\n",
> > +				mmc_hostname(host), err);
> 
> There's no point in calling this for SD but I don't see why it currently
> wouldn't be called for SD.

I tried to keep budget low, until we agree that it is the way to go.
After this patch stack is accepted, i can try to request more time to
add and test the SD handler.

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v2 3/6] mmc: core: refactor _mmc_suspend() for undervoltage handling
  2025-02-20  7:44 ` [PATCH v2 3/6] mmc: core: refactor _mmc_suspend() for undervoltage handling Oleksij Rempel
@ 2025-02-20 11:05   ` Christian Loehle
  0 siblings, 0 replies; 11+ messages in thread
From: Christian Loehle @ 2025-02-20 11:05 UTC (permalink / raw)
  To: Oleksij Rempel, Ulf Hansson
  Cc: kernel, linux-kernel, linux-mmc, Greg Kroah-Hartman, Mark Brown,
	Rafael J. Wysocki, Søren Andersen

On 2/20/25 07:44, Oleksij Rempel wrote:
> Introduce an is_undervoltage parameter to _mmc_suspend() to apply a
> short power-off sequence and optionally flush the cache. This refactoring
> prepares for undervoltage support in a follow-up patch.
> 
> Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
> ---
>  drivers/mmc/core/mmc.c | 25 ++++++++++++++++---------
>  1 file changed, 16 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
> index 6a23be214543..86b608843232 100644
> --- a/drivers/mmc/core/mmc.c
> +++ b/drivers/mmc/core/mmc.c
> @@ -2104,20 +2104,27 @@ static int _mmc_flush_cache(struct mmc_host *host)
>  	return err;
>  }
>  
> -static int _mmc_suspend(struct mmc_host *host, bool is_suspend)
> +static int _mmc_suspend(struct mmc_host *host, bool is_suspend,
> +			bool is_undervoltage)

I see, sorry about missing this on 4/6 :/

>  {
> +	unsigned int notify_type;
>  	int err = 0;
> -	unsigned int notify_type = is_suspend ? EXT_CSD_POWER_OFF_SHORT :
> -					EXT_CSD_POWER_OFF_LONG;
> +
> +	if (is_undervoltage || is_suspend)
> +		notify_type = EXT_CSD_POWER_OFF_SHORT;
> +	else
> +		notify_type = EXT_CSD_POWER_OFF_LONG;
>  
>  	mmc_claim_host(host);
>  
>  	if (mmc_card_suspended(host->card))
>  		goto out;
>  
> -	err = _mmc_flush_cache(host);
> -	if (err)
> -		goto out;
> +	if (is_undervoltage) {
> +		err = _mmc_flush_cache(host);
> +		if (err)
> +			goto out;
> +	}

This is supposed to be !is_undervoltage, isn't it?

>  
>  	if (mmc_can_poweroff_notify(host->card) &&
>  	    ((host->caps2 & MMC_CAP2_FULL_PWR_CYCLE) || !is_suspend ||
> @@ -2144,7 +2151,7 @@ static int mmc_suspend(struct mmc_host *host)
>  {
>  	int err;
>  
> -	err = _mmc_suspend(host, true);
> +	err = _mmc_suspend(host, true, false);
>  	if (!err) {
>  		pm_runtime_disable(&host->card->dev);
>  		pm_runtime_set_suspended(&host->card->dev);
> @@ -2191,7 +2198,7 @@ static int mmc_shutdown(struct mmc_host *host)
>  		err = _mmc_resume(host);
>  
>  	if (!err)
> -		err = _mmc_suspend(host, false);
> +		err = _mmc_suspend(host, false, false);
>  
>  	return err;
>  }
> @@ -2215,7 +2222,7 @@ static int mmc_runtime_suspend(struct mmc_host *host)
>  	if (!(host->caps & MMC_CAP_AGGRESSIVE_PM))
>  		return 0;
>  
> -	err = _mmc_suspend(host, true);
> +	err = _mmc_suspend(host, true, false);
>  	if (err)
>  		pr_err("%s: error %d doing aggressive suspend\n",
>  			mmc_hostname(host), err);


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v2 4/6] mmc: core: add undervoltage handler for MMC/eMMC devices
  2025-02-20 10:56     ` Oleksij Rempel
@ 2025-02-20 11:22       ` Christian Loehle
  0 siblings, 0 replies; 11+ messages in thread
From: Christian Loehle @ 2025-02-20 11:22 UTC (permalink / raw)
  To: Oleksij Rempel
  Cc: Ulf Hansson, kernel, linux-kernel, linux-mmc, Greg Kroah-Hartman,
	Mark Brown, Rafael J. Wysocki, Søren Andersen

On 2/20/25 10:56, Oleksij Rempel wrote:
> On Thu, Feb 20, 2025 at 10:47:01AM +0000, Christian Loehle wrote:
>> On 2/20/25 07:44, Oleksij Rempel wrote:
>>> Introduce _mmc_handle_undervoltage() to handle undervoltage events for
>>> MMC/eMMC devices. This function interrupts ongoing operations using HPI
>>> and performs a controlled suspend. After completing the sequence, the card
>>> is marked as removed to prevent further interactions.
>>>
>>> To support this, introduce __mmc_suspend() and __mmc_resume() as internal
>>> helpers that omit mmc_claim_host()/mmc_release_host(), allowing them to be
>>> called when the host is already claimed. Update mmc_shutdown() to skip the
>>> normal shutdown sequence if the host is flagged as undervoltage to avoid
>>> repeating of the shutdown procedure.
>>
>> "of" can be removed here.
>>
>> Given that this introduces large parts of the mmc handling IMO this commit
>> deserves a lot more explanation of what steps exactly do for which cards
>> and why.
> 
> ack
> 
>>>
>>> Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
>>> ---
>>>  drivers/mmc/core/mmc.c | 81 +++++++++++++++++++++++++++++++++++-------
>>>  1 file changed, 68 insertions(+), 13 deletions(-)
>>>
>>> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
>>> index 86b608843232..e626213e7a52 100644
>>> --- a/drivers/mmc/core/mmc.c
>>> +++ b/drivers/mmc/core/mmc.c
>>> @@ -2104,8 +2104,8 @@ static int _mmc_flush_cache(struct mmc_host *host)
>>>  	return err;
>>>  }
>>>  
>>> -static int _mmc_suspend(struct mmc_host *host, bool is_suspend,
>>> -			bool is_undervoltage)
>>> +static int __mmc_suspend(struct mmc_host *host, bool is_suspend,
>>> +			 bool is_undervoltage)
>>
>> The is_undervoltage doesn't do anything? Did you forget something here?
> 
> This was done in the previous patch "mmc: core: refactor _mmc_suspend()
> for undervoltage handling"

Sorry!

> 
>>> @@ -2189,6 +2205,9 @@ static int mmc_shutdown(struct mmc_host *host)
>>>  {
>>>  	int err = 0;
>>>  
>>> +	if (host->undervoltage)
>>> +		return 0;
>>> +
>>
>> Probably deserves a comment.
> 
> ack
> 
>>>  	/*
>>>  	 * In a specific case for poweroff notify, we need to resume the card
>>>  	 * before we can shutdown it properly.
>>> @@ -2280,6 +2299,41 @@ static int _mmc_hw_reset(struct mmc_host *host)
>>>  	return mmc_init_card(host, card->ocr, card);
>>>  }
>>>  
>>> +static int _mmc_handle_undervoltage(struct mmc_host *host)
>>> +{
>>> +	struct mmc_card *card = host->card;
>>> +	int err = 0;
>>> +
>>> +	mmc_claim_host(host);
>>> +
>>> +	if (!host->card)
>>> +		goto out;
>>> +
>>> +	if (mmc_can_poweroff_notify(host->card) &&
>>> +		!(host->caps2 & MMC_CAP2_FULL_PWR_CYCLE))
>>> +		err = __mmc_resume(host);
>>
>> I'm not sure I follow this.
>> Why would we power-up a card that currently doesn't have power when we
>> know we are about to powerfail it?
> 
> It is part of the mmc_shutdown, but it is not used on my HW. So, can be
> skip it.
> 
>>> +
>>> +	if (!err) {
>>> +		err = mmc_interrupt_hpi(card);
>>> +		if (err)
>>> +			pr_err("%s: Interrupt HPI failed, error %d\n",
>>> +				mmc_hostname(host), err);
>>
>> There's no point in calling this for SD but I don't see why it currently
>> wouldn't be called for SD.
> 
> I tried to keep budget low, until we agree that it is the way to go.
> After this patch stack is accepted, i can try to request more time to
> add and test the SD handler.
If you're not implementing this for now, why not just drop the undervoltage
event at patch 1/6 if host doesn't have an eMMC attached?

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2025-02-20 11:22 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-02-20  7:44 [PATCH v2 0/6] mmc: handle undervoltage events and prevent eMMC corruption Oleksij Rempel
2025-02-20  7:44 ` [PATCH v2 1/6] mmc: core: Handle undervoltage events and register regulator notifiers Oleksij Rempel
2025-02-20  7:44 ` [PATCH v2 2/6] mmc: core: make mmc_interrupt_hpi() global Oleksij Rempel
2025-02-20  7:44 ` [PATCH v2 3/6] mmc: core: refactor _mmc_suspend() for undervoltage handling Oleksij Rempel
2025-02-20 11:05   ` Christian Loehle
2025-02-20  7:44 ` [PATCH v2 4/6] mmc: core: add undervoltage handler for MMC/eMMC devices Oleksij Rempel
2025-02-20 10:47   ` Christian Loehle
2025-02-20 10:56     ` Oleksij Rempel
2025-02-20 11:22       ` Christian Loehle
2025-02-20  7:44 ` [PATCH v2 5/6] mmc: block: abort requests and suppress errors after undervoltage shutdown Oleksij Rempel
2025-02-20  7:44 ` [PATCH v2 6/6] mmc: sdhci: prevent command execution " Oleksij Rempel

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox