]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
mmc: core: Add support for graceful host removal for eMMC
authorUlf Hansson <ulf.hansson@linaro.org>
Mon, 7 Apr 2025 15:27:54 +0000 (17:27 +0200)
committerUlf Hansson <ulf.hansson@linaro.org>
Wed, 14 May 2025 14:59:16 +0000 (16:59 +0200)
An mmc host driver may allow to unbind from its corresponding host device.
If an eMMC card is attached to the host, the mmc core will just try to cut
the power for it, without obeying to the eMMC spec.

Potentially this may damage the card and it may also prevent us from
successfully doing a re-initialization of it, which would typically happen
if/when we try to re-bind the mmc host driver.

To fix these problems, let's implement a graceful power-down of the card at
host removal.

Reported-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
Reviewed-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
Tested-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
Link: https://lore.kernel.org/r/20250407152759.25160-5-ulf.hansson@linaro.org
drivers/mmc/core/mmc.c

index c41cee7ef2677afa5a8854df18702873c8045892..48656dadf93b85a1b07793bfc389020bd39a6fc3 100644 (file)
@@ -36,6 +36,7 @@
 enum mmc_poweroff_type {
        MMC_POWEROFF_SUSPEND,
        MMC_POWEROFF_SHUTDOWN,
+       MMC_POWEROFF_UNBIND,
 };
 
 static const unsigned int tran_exp[] = {
@@ -2054,15 +2055,6 @@ static int mmc_poweroff_notify(struct mmc_card *card, unsigned int notify_type)
        return err;
 }
 
-/*
- * Host is being removed. Free up the current card.
- */
-static void mmc_remove(struct mmc_host *host)
-{
-       mmc_remove_card(host->card);
-       host->card = NULL;
-}
-
 /*
  * Card detection - card is alive.
  */
@@ -2088,7 +2080,8 @@ static void mmc_detect(struct mmc_host *host)
        mmc_put_card(host->card, NULL);
 
        if (err) {
-               mmc_remove(host);
+               mmc_remove_card(host->card);
+               host->card = NULL;
 
                mmc_claim_host(host);
                mmc_detach_bus(host);
@@ -2160,6 +2153,20 @@ out:
        return err;
 }
 
+/*
+ * Host is being removed. Free up the current card and do a graceful power-off.
+ */
+static void mmc_remove(struct mmc_host *host)
+{
+       get_device(&host->card->dev);
+       mmc_remove_card(host->card);
+
+       _mmc_suspend(host, MMC_POWEROFF_UNBIND);
+
+       put_device(&host->card->dev);
+       host->card = NULL;
+}
+
 /*
  * Suspend callback
  */