From nobody Sat Oct 4 01:41:10 2025 Received: from metis.whiteo.stw.pengutronix.de (metis.whiteo.stw.pengutronix.de [185.203.201.7]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8988532274E for ; Thu, 21 Aug 2025 13:07:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.203.201.7 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755781680; cv=none; b=PzJg7FVdc/zpdZZECPh8C5LTUsqvFa2M5m2XGgGWvcwCjcCf5gR1Ob6rVrwhJtkfZKrM+NYUplHauwZX+ZmuJ/fOe4K6r70tPDFohXvzZkQCJescAYlCTOHwu4d15Kff/PTFPM/TTqZrMNrQsmUv+Jk0hY9WOT+IdzSsEkT09YY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755781680; c=relaxed/simple; bh=clUjc6iM5rYPo+PgiNX0yGj1ORiRkLxVFKBHx69GVOQ=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=O10UNRMbxFdFOjgqEkaZUfApuSzSx299ej1uy9b9vQ67Ni6bUWWxTJ3PqRdHYidCaaM6fMJgNxzhea6xKoDcaf1nyuKaJKGOb2A63m6GSCu+20WCwUmvwiKWMCSRyzAEz3NLt9czMB6Oh/cSZguyn06Fmq2nSKMSbEvnWClRyEE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=pengutronix.de; spf=pass smtp.mailfrom=pengutronix.de; arc=none smtp.client-ip=185.203.201.7 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=pengutronix.de Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=pengutronix.de Received: from drehscheibe.grey.stw.pengutronix.de ([2a0a:edc0:0:c01:1d::a2]) by metis.whiteo.stw.pengutronix.de with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1up51H-0006wE-Mx; Thu, 21 Aug 2025 15:07:55 +0200 Received: from dude04.red.stw.pengutronix.de ([2a0a:edc0:0:1101:1d::ac]) by drehscheibe.grey.stw.pengutronix.de with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1up51H-001Q1q-17; Thu, 21 Aug 2025 15:07:55 +0200 Received: from ore by dude04.red.stw.pengutronix.de with local (Exim 4.96) (envelope-from ) id 1up51H-008lbZ-0t; Thu, 21 Aug 2025 15:07:55 +0200 From: Oleksij Rempel To: Ulf Hansson Cc: Oleksij Rempel , kernel@pengutronix.de, linux-kernel@vger.kernel.org, linux-mmc@vger.kernel.org, Greg Kroah-Hartman , Mark Brown , "Rafael J. Wysocki" , =?UTF-8?q?S=C3=B8ren=20Andersen?= , Christian Loehle , Adrian Hunter , Avri Altman Subject: [PATCH v9 1/2] mmc: core: Add infrastructure for undervoltage handling Date: Thu, 21 Aug 2025 15:07:50 +0200 Message-Id: <20250821130751.2089587-2-o.rempel@pengutronix.de> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250821130751.2089587-1-o.rempel@pengutronix.de> References: <20250821130751.2089587-1-o.rempel@pengutronix.de> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-SA-Exim-Connect-IP: 2a0a:edc0:0:c01:1d::a2 X-SA-Exim-Mail-From: ore@pengutronix.de X-SA-Exim-Scanned: No (on metis.whiteo.stw.pengutronix.de); SAEximRunCond expanded to false X-PTX-Original-Recipient: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" Implement the core infrastructure to allow MMC bus types to handle REGULATOR_EVENT_UNDER_VOLTAGE events from power regulators. This is primarily aimed at allowing devices like eMMC to perform an emergency shutdown to prevent data corruption when a power failure is imminent. This patch introduces: - A new 'handle_undervoltage' function pointer to 'struct mmc_bus_ops'. Bus drivers (e.g., for eMMC) can implement this to define their emergency procedures. - A workqueue ('uv_work') in 'struct mmc_supply' to handle the event asynchronously in a high-priority context. - A new function 'mmc_handle_undervoltage()' which is called from the workqueue. It stops the host queue to prevent races with card removal, checks for the bus op, and invokes the handler. - Functions to register and unregister the regulator notifier, intended to be called by bus drivers like 'mmc_attach_mmc' when a compatible card is detected. The notifier is only registered for the main vmmc supply, as undervoltage handling for vqmmc or vqmmc2 is not required at this time. Signed-off-by: Oleksij Rempel --- changes v9: - Drop stray whitespace after mmc_claim_host() in mmc_attach_mmc() - Remove unnecessary #include from host.h, add forward declarations instead - Move internal prototypes for undervoltage helpers (mmc_regulator_register/unregister_undervoltage_notifier(), mmc_undervoltage_workfn()) from host.h to core.h changes v7: - Remove all usage of the redundant undervoltage_notify_registered flag - Register undervoltage notifier in mmc_add_card() after setting card as present, for all supported cards. - Unregister undervoltage notifier in mmc_remove_card() based on card prese= nce - Remove all unnecessary EXPORT_SYMBOL_GPL for functions only used within M= MC core. - Move all host claiming and releasing responsibility for undervoltage even= ts into the bus_ops callback; - add comment for host->undervoltage changes v6: - Rewrite commit message to be more technical per reviewer feedback. - Address race conditions by using __mmc_stop_host() instead of only claiming the host in the undervoltage handler. - Move notifier registration from mmc_regulator_get_supply() to the end of a successful card initialization in mmc_attach_mmc(), ensuring it only runs for capable cards. - Centralize notifier unregistration in mmc_remove_card() to correctly handle all card removal and error paths. - Add 'undervoltage_notify_registered' flag to struct mmc_host to reliably track the notifier state. - Consolidate multiple notifier callbacks into a single, generic handler. - Remove premature notifier support for vqmmc and vqmmc2 regulators. - Move INIT_WORK() for the undervoltage workqueue to mmc_alloc_host(). changes v3: - filter supported cards at early stage - add locking in mmc_handle_regulator_event() - claim/release host in mmc_handle_undervoltage() 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. --- drivers/mmc/core/bus.c | 12 ++++++ drivers/mmc/core/core.c | 23 +++++++++++ drivers/mmc/core/core.h | 5 +++ drivers/mmc/core/host.c | 2 + drivers/mmc/core/regulator.c | 77 ++++++++++++++++++++++++++++++++++++ include/linux/mmc/host.h | 11 ++++++ 6 files changed, 130 insertions(+) diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index 1cf64e0952fb..ec4f3462bf80 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c @@ -19,6 +19,7 @@ =20 #include #include +#include =20 #include "core.h" #include "card.h" @@ -383,6 +384,14 @@ int mmc_add_card(struct mmc_card *card) =20 mmc_card_set_present(card); =20 + /* + * Register for undervoltage notification if the card supports + * power-off notification, enabling emergency shutdowns. + */ + if (mmc_card_mmc(card) && + card->ext_csd.power_off_notification =3D=3D EXT_CSD_POWER_ON) + mmc_regulator_register_undervoltage_notifier(card->host); + return 0; } =20 @@ -394,6 +403,9 @@ void mmc_remove_card(struct mmc_card *card) { struct mmc_host *host =3D card->host; =20 + if (mmc_card_present(card)) + mmc_regulator_unregister_undervoltage_notifier(host); + mmc_remove_card_debugfs(card); =20 if (mmc_card_present(card)) { diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 88fd231fee1d..860378bea557 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1398,6 +1398,29 @@ void mmc_power_cycle(struct mmc_host *host, u32 ocr) mmc_power_up(host, ocr); } =20 +/** + * 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) +{ + /* Stop the host to prevent races with card removal */ + __mmc_stop_host(host); + + if (!host->bus_ops || !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 73f5d3d8c77d..a028b48be164 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); }; =20 void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops); @@ -59,6 +60,10 @@ 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); +void mmc_regulator_register_undervoltage_notifier(struct mmc_host *host); +void mmc_regulator_unregister_undervoltage_notifier(struct mmc_host *host); +void mmc_undervoltage_workfn(struct work_struct *work); =20 static inline void mmc_delay(unsigned int ms) { diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index f14671ea5716..5f0ec23aeff5 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -564,6 +564,8 @@ struct mmc_host *mmc_alloc_host(int extra, struct devic= e *dev) INIT_WORK(&host->sdio_irq_work, sdio_irq_work); timer_setup(&host->retune_timer, mmc_retune_timer, 0); =20 + INIT_WORK(&host->supply.uv_work, mmc_undervoltage_workfn); + /* * By default, hosts do not support SGIO or large requests. * They have to set these according to their abilities. diff --git a/drivers/mmc/core/regulator.c b/drivers/mmc/core/regulator.c index 3dae2e9b7978..a85179f1a4de 100644 --- a/drivers/mmc/core/regulator.c +++ b/drivers/mmc/core/regulator.c @@ -7,6 +7,7 @@ #include #include #include +#include =20 #include =20 @@ -262,6 +263,82 @@ static inline int mmc_regulator_get_ocrmask(struct reg= ulator *supply) =20 #endif /* CONFIG_REGULATOR */ =20 +/* To be called from a high-priority workqueue */ +void mmc_undervoltage_workfn(struct work_struct *work) +{ + struct mmc_supply *supply; + struct mmc_host *host; + + supply =3D container_of(work, struct mmc_supply, uv_work); + host =3D container_of(supply, struct mmc_host, supply); + + mmc_handle_undervoltage(host); +} + +static int mmc_handle_regulator_event(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct mmc_supply *supply =3D container_of(nb, struct mmc_supply, + vmmc_nb); + struct mmc_host *host =3D container_of(supply, struct mmc_host, supply); + unsigned long flags; + + switch (event) { + case REGULATOR_EVENT_UNDER_VOLTAGE: + spin_lock_irqsave(&host->lock, flags); + if (host->undervoltage) { + spin_unlock_irqrestore(&host->lock, flags); + return NOTIFY_OK; + } + + host->undervoltage =3D true; + spin_unlock_irqrestore(&host->lock, flags); + + queue_work(system_highpri_wq, &host->supply.uv_work); + break; + default: + return NOTIFY_DONE; + } + + return NOTIFY_OK; +} + +/** + * mmc_regulator_register_undervoltage_notifier - Register for undervoltage + * events + * @host: MMC host + * + * To be called by a bus driver when a card supporting graceful shutdown + * is attached. + */ +void mmc_regulator_register_undervoltage_notifier(struct mmc_host *host) +{ + int ret; + + if (IS_ERR_OR_NULL(host->supply.vmmc)) + return; + + host->supply.vmmc_nb.notifier_call =3D mmc_handle_regulator_event; + ret =3D regulator_register_notifier(host->supply.vmmc, + &host->supply.vmmc_nb); + if (ret) + dev_warn(mmc_dev(host), "Failed to register vmmc notifier: %d\n", ret); +} + +/** + * mmc_regulator_unregister_undervoltage_notifier - Unregister undervoltage + * notifier + * @host: MMC host + */ +void mmc_regulator_unregister_undervoltage_notifier(struct mmc_host *host) +{ + if (IS_ERR_OR_NULL(host->supply.vmmc)) + return; + + regulator_unregister_notifier(host->supply.vmmc, &host->supply.vmmc_nb); + cancel_work_sync(&host->supply.uv_work); +} + /** * mmc_regulator_get_supply - try to get VMMC and VQMMC regulators for a h= ost * @mmc: the host to regulate diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 5ed5d203de23..e0d935a4ac1d 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -337,11 +337,15 @@ struct mmc_slot { =20 struct regulator; struct mmc_pwrseq; +struct notifier_block; =20 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 work_struct uv_work; /* Undervoltage work */ }; =20 struct mmc_ctx { @@ -494,6 +498,13 @@ struct mmc_host { unsigned int can_dma_map_merge:1; /* merging can be used */ unsigned int vqmmc_enabled:1; /* vqmmc regulator is enabled */ =20 + /* + * Indicates if an undervoltage event has already been handled. + * This prevents repeated regulator notifiers from triggering + * multiple REGULATOR_EVENT_UNDER_VOLTAGE events. + */ + unsigned int undervoltage:1; /* Undervoltage state */ + int rescan_disable; /* disable card detection */ int rescan_entered; /* used with nonremovable devices */ =20 --=20 2.39.5 From nobody Sat Oct 4 01:41:10 2025 Received: from metis.whiteo.stw.pengutronix.de (metis.whiteo.stw.pengutronix.de [185.203.201.7]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 79910321F55 for ; Thu, 21 Aug 2025 13:07:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.203.201.7 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755781680; cv=none; b=DZbzAlFv3D+Lxzzc7KZ30mzfc6PVGWXHfDcuK9HMDg7o1j0q2OMLFzQDq4C/za9wbU/qJf+pZcrs11xokVt9hTGEdskWTlRp3vOZuK2kggqyZefG+2MpX0lDMDTYOJOJP5LNRkZ0n608j3w8BwdcRnfCUN05R2sZKawbIeRaQeg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755781680; c=relaxed/simple; bh=eGQ0CfNbA2Jn0IeZ2cMUSXIb6cbfso5zyJoOLg94ZhM=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=VVWV2Qj+0sbbLxcflvnp/lOc5zhGPswqKMevn/x1fh/lN2gaJWbbg2LU9maLxjyfwlNYcF8Szljqd9zpvOOKQ/XsweAN7ew4qXdgIH2tyG7Wp/YKCgszgltiuyXkovjQePN/pcl1hIFz5mttagzKa7Z2xL6CfMJbRpPZ4z3bO64= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=pengutronix.de; spf=pass smtp.mailfrom=pengutronix.de; arc=none smtp.client-ip=185.203.201.7 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=pengutronix.de Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=pengutronix.de Received: from drehscheibe.grey.stw.pengutronix.de ([2a0a:edc0:0:c01:1d::a2]) by metis.whiteo.stw.pengutronix.de with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1up51H-0006wF-Mx; Thu, 21 Aug 2025 15:07:55 +0200 Received: from dude04.red.stw.pengutronix.de ([2a0a:edc0:0:1101:1d::ac]) by drehscheibe.grey.stw.pengutronix.de with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1up51H-001Q1r-1E; Thu, 21 Aug 2025 15:07:55 +0200 Received: from ore by dude04.red.stw.pengutronix.de with local (Exim 4.96) (envelope-from ) id 1up51H-008lbk-10; Thu, 21 Aug 2025 15:07:55 +0200 From: Oleksij Rempel To: Ulf Hansson Cc: Oleksij Rempel , kernel@pengutronix.de, linux-kernel@vger.kernel.org, linux-mmc@vger.kernel.org, Greg Kroah-Hartman , Mark Brown , "Rafael J. Wysocki" , =?UTF-8?q?S=C3=B8ren=20Andersen?= , Christian Loehle , Adrian Hunter , Avri Altman Subject: [PATCH v9 2/2] mmc: core: add undervoltage handler for MMC/eMMC devices Date: Thu, 21 Aug 2025 15:07:51 +0200 Message-Id: <20250821130751.2089587-3-o.rempel@pengutronix.de> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250821130751.2089587-1-o.rempel@pengutronix.de> References: <20250821130751.2089587-1-o.rempel@pengutronix.de> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-SA-Exim-Connect-IP: 2a0a:edc0:0:c01:1d::a2 X-SA-Exim-Mail-From: ore@pengutronix.de X-SA-Exim-Scanned: No (on metis.whiteo.stw.pengutronix.de); SAEximRunCond expanded to false X-PTX-Original-Recipient: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" Add infrastructure to handle regulator undervoltage events for MMC/eMMC cards. When an undervoltage is detected, the new handler performs a controlled emergency suspend using a short power-off notification, skipping the cache flush to maximize the chance of a safe shutdown. After the operation, the card is marked as removed to prevent further I/O and possible data corruption. This is implemented by introducing MMC_POWEROFF_UNDERVOLTAGE to the mmc_poweroff_type enum and refactoring the suspend logic into an internal __mmc_suspend() helper that allows the caller to skip the cache flush if required. The undervoltage handler is registered as a bus operation and invoked from the core undervoltage path. If power-off notification is not supported by the card, the handler falls back to sleep or deselecting the card. Additionally, update the shutdown path to avoid redundant shutdown steps if the card is already removed Signed-off-by: Oleksij Rempel --- changes v9: - remove host->card check changes v7: - Squash undervoltage suspend preparation and handler into one patch. - Use mmc_card_removed() in shutdown path instead of host->undervoltage. - Remove redundant card presence check in undervoltage handler. changes v6: - Refactor suspend logic: move cache flush skipping during undervoltage to a separate, preceding commit. - update commit message changes v5: - Rebased on top of patch introducing enum mmc_poweroff_type - Updated call to __mmc_suspend() to use MMC_POWEROFF_UNDERVOLTAGE - Dropped __mmc_resume() helper, as it is no longer needed - Updated commit message to reflect API change and code removal changes v4: - Drop HPI step. changes v3: - reword commit message. - add comments in the code - do not try to resume sleeping device --- drivers/mmc/core/mmc.c | 70 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 67 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 5be9b42d5057..3e7d9437477c 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -36,6 +36,7 @@ enum mmc_poweroff_type { MMC_POWEROFF_SUSPEND, MMC_POWEROFF_SHUTDOWN, + MMC_POWEROFF_UNDERVOLTAGE, MMC_POWEROFF_UNBIND, }; =20 @@ -2132,9 +2133,15 @@ static int _mmc_suspend(struct mmc_host *host, enum = mmc_poweroff_type pm_type) if (mmc_card_suspended(host->card)) goto out; =20 - err =3D _mmc_flush_cache(host); - if (err) - goto out; + /* + * For the undervoltage case, we care more about device integrity. + * Avoid cache flush and notify the device to power off quickly. + */ + if (pm_type !=3D MMC_POWEROFF_UNDERVOLTAGE) { + err =3D _mmc_flush_cache(host); + if (err) + goto out; + } =20 if (mmc_card_can_poweroff_notify(host->card) && mmc_host_can_poweroff_notify(host, pm_type)) @@ -2212,6 +2219,13 @@ static int mmc_shutdown(struct mmc_host *host) { int err =3D 0; =20 + /* + * In case of undervoltage, the card will be powered off (removed) by + * _mmc_handle_undervoltage() + */ + if (mmc_card_removed(host->card)) + return 0; + /* * If the card remains suspended at this point and it was done by using * the sleep-cmd (CMD5), we may need to re-initialize it first, to allow @@ -2302,6 +2316,55 @@ static int _mmc_hw_reset(struct mmc_host *host) return mmc_init_card(host, card->ocr, card); } =20 +/** + * _mmc_handle_undervoltage - Handle an undervoltage event for MMC/eMMC de= vices + * @host: MMC host structure + * + * This function is triggered when an undervoltage condition is detected. + * It attempts to transition the device into a low-power or safe state to + * prevent data corruption. + * + * Steps performed: + * - Perform an emergency suspend using EXT_CSD_POWER_OFF_SHORT if possibl= e. + * - If power-off notify is not supported, fallback mechanisms like sle= ep or + * deselecting the card are attempted. + * - Cache flushing is skipped to reduce execution time. + * - Mark the card as removed to prevent further interactions after + * undervoltage. + * + * Note: This function does not handle host claiming or releasing. The cal= ler + * must ensure that the host is properly claimed before calling this + * function and released afterward. + * + * Returns: 0 on success, or a negative error code if any step fails. + */ +static int _mmc_handle_undervoltage(struct mmc_host *host) +{ + struct mmc_card *card =3D host->card; + int err; + + /* + * Perform an emergency suspend to power off the eMMC quickly. + * This ensures the device enters a safe state before power is lost. + * We first attempt EXT_CSD_POWER_OFF_SHORT, but if power-off notify + * is not supported, we fall back to sleep mode or deselecting the card. + * Cache flushing is skipped to minimize delay. + */ + err =3D _mmc_suspend(host, MMC_POWEROFF_UNDERVOLTAGE); + if (err) + pr_err("%s: undervoltage suspend failed: %pe\n", + mmc_hostname(host), ERR_PTR(err)); + + /* + * Mark the card as removed to prevent further operations. + * This ensures the system does not attempt to access the device + * after an undervoltage event, avoiding potential corruption. + */ + mmc_card_set_removed(card); + + return err; +} + static const struct mmc_bus_ops mmc_ops =3D { .remove =3D mmc_remove, .detect =3D mmc_detect, @@ -2314,6 +2377,7 @@ static const struct mmc_bus_ops mmc_ops =3D { .hw_reset =3D _mmc_hw_reset, .cache_enabled =3D _mmc_cache_enabled, .flush_cache =3D _mmc_flush_cache, + .handle_undervoltage =3D _mmc_handle_undervoltage, }; =20 /* --=20 2.39.5