diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index d40f6c852727c978ade296978c08e8f4097121f8..d0c8249866effdd43261aa750ecb783e54b1f67e 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -44,6 +44,11 @@ #include "sd_ops.h" #include "sdio_ops.h" +#include <linux/fs.h> +#include <linux/delay.h> + +extern void kernel_restart(char *cmd); + /* * Background operations can take a long time, depending on the housekeeping * operations the card has to perform. @@ -1468,6 +1473,40 @@ void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type) mmc_host_clk_release(host); } +void mmc_force_poweroff_notify(struct mmc_host *host) +{ + int err = 0; + unsigned int timeout; + + mmc_claim_host(host); + + if (mmc_card_is_sleep(host->card)) { + BUG_ON(!host->bus_ops->resume); + err = host->bus_ops->resume(host); + } + + if (err) { + pr_err("failed to resume for force poweroff notify\n"); + return; + } + + timeout = host->card->ext_csd.generic_cmd6_time; + + pr_info("sending poweroff notify\n"); + err = mmc_switch(host->card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_POWER_OFF_NOTIFICATION, + EXT_CSD_POWER_OFF_SHORT, timeout); + if (err && err != -EBADMSG) { + pr_err("Device failed to respond within %d poweroff time\n", + timeout); + return; + } + + pr_info("poweroff notify sent\n"); + mmc_release_host(host); + return; +} + /* * Apply power to the MMC stack. This is a two-stage process. * First, we enable power to the card without the clock running. @@ -2868,6 +2907,29 @@ int mmc_pm_notify(struct notifier_block *notify_block, } #endif + +/* force send poweroff notify to Kingston eMMC + * while long press power key before hw reset + */ +int force_poweroff_notify(struct notifier_block *notify_block, + unsigned long mode, void *unused) +{ + struct mmc_host *host = container_of( + notify_block, struct mmc_host, force_poweroff_notifier); + + if (host->card == NULL) + return 0; + /* only for Kingston card */ + if (mmc_card_mmc(host->card) + && host->card->cid.manfid == 0x70) { + emergency_sync(); + emergency_remount(); + msleep(1000); + kernel_restart(NULL); + } + return 0; +} + #ifdef CONFIG_MMC_EMBEDDED_SDIO void mmc_set_embedded_sdio_data(struct mmc_host *host, struct sdio_cis *cis, diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index 85d273775ac08260d207816c19e15e7be36f57f0..2b4c51ff39aa490d8f0750b07ec5f12c92f7700c 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -47,6 +47,7 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, void mmc_set_timing(struct mmc_host *host, unsigned int timing); void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type); void mmc_power_off(struct mmc_host *host); +void mmc_force_poweroff_notify(struct mmc_host *host); static inline void mmc_delay(unsigned int ms) { @@ -70,6 +71,9 @@ int mmc_attach_mmc(struct mmc_host *host); int mmc_attach_sd(struct mmc_host *host); int mmc_attach_sdio(struct mmc_host *host); +int force_poweroff_notify(struct notifier_block *notify_block, + unsigned long mode, void *unused); + /* Module parameters */ extern bool use_spi_crc; diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 29b64f0b7c862e74327b0286757f9c68e0adf0e3..e249788661a9ac4f6c512422ecd0276182415b33 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -27,6 +27,8 @@ #include "core.h" #include "host.h" +#include <linux/gpio_keys.h> + #define cls_dev_to_mmc_host(d) container_of(d, struct mmc_host, class_dev) static void mmc_host_classdev_release(struct device *dev) @@ -335,6 +337,8 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev) #ifdef CONFIG_PM host->pm_notify.notifier_call = mmc_pm_notify; #endif + host->force_poweroff_notifier.notifier_call = force_poweroff_notify; + register_resetkey_notifier(&host->force_poweroff_notifier); /* * By default, hosts do not support SGIO or large requests. diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index ea1eca731ae5323607c08c5a6474bf1340a1376f..e56d8479b9453eb650334e5b0346c59f2b417c7c 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1464,6 +1464,8 @@ static int mmc_suspend(struct mmc_host *host) err = mmc_card_sleep(host); else if (!mmc_host_is_spi(host)) mmc_deselect_cards(host); + + mmc_card_set_sleep(host->card); host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200); out: @@ -1484,6 +1486,7 @@ static int mmc_resume(struct mmc_host *host) BUG_ON(!host); BUG_ON(!host->card); + mmc_card_clr_sleep(host->card); mmc_claim_host(host); err = mmc_init_card(host, host->ocr, host->card); mmc_release_host(host); @@ -1496,6 +1499,7 @@ static int mmc_power_restore(struct mmc_host *host) int ret; host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200); + mmc_card_clr_sleep(host->card); mmc_claim_host(host); ret = mmc_init_card(host, host->ocr, host->card); mmc_release_host(host); diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index 91aa05f94bb5f0febdda0097cf52e9f9921a3443..8781bfeb114ab843cf61122537c67fbe0465499a 100644 --- a/drivers/mmc/host/msm_sdcc.c +++ b/drivers/mmc/host/msm_sdcc.c @@ -60,6 +60,7 @@ #include "msm_sdcc.h" #include "msm_sdcc_dml.h" +#include "../core/core.h" #define DRIVER_NAME "msm-sdcc" @@ -6256,6 +6257,21 @@ static int msmsdcc_remove(struct platform_device *pdev) return 0; } +static void msmsdcc_shutdown(struct platform_device *pdev) +{ + struct mmc_host *mmc = mmc_get_drvdata(pdev); + + if (!mmc || !mmc->card) + return; + + /* only for Kingston eMMC */ + if (mmc_card_mmc(mmc->card) + && mmc->card->cid.manfid == 0x70) + mmc_force_poweroff_notify(mmc); + + return; +} + #ifdef CONFIG_MSM_SDIO_AL int msmsdcc_sdio_al_lpm(struct mmc_host *mmc, bool enable) { @@ -6616,6 +6632,7 @@ MODULE_DEVICE_TABLE(of, msmsdcc_dt_match); static struct platform_driver msmsdcc_driver = { .probe = msmsdcc_probe, .remove = msmsdcc_remove, + .shutdown = msmsdcc_shutdown, .driver = { .name = "msm_sdcc", .pm = &msmsdcc_dev_pm_ops, diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 6aade3e4d34fb4eea7b0bbd31cb5b1bf5e750978..9d698ed331a965346bbecaa831ce57810e076368 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -300,6 +300,7 @@ struct mmc_card { #define MMC_STATE_HIGHSPEED_200 (1<<8) /* card is in HS200 mode */ #define MMC_STATE_DOING_BKOPS (1<<10) /* card is doing BKOPS */ #define MMC_STATE_NEED_BKOPS (1<<11) /* card needs to do BKOPS */ +#define MMC_STATE_SLEEP (1<<12) /* card is in sleep/deselect state */ unsigned int quirks; /* card quirks */ #define MMC_QUIRK_LENIENT_FN0 (1<<0) /* allow SDIO FN0 writes outside of the VS CCCR range */ #define MMC_QUIRK_BLKSZ_FOR_BYTE_MODE (1<<1) /* use func->cur_blksize */ @@ -472,6 +473,7 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data) #define mmc_card_removed(c) ((c) && ((c)->state & MMC_CARD_REMOVED)) #define mmc_card_doing_bkops(c) ((c)->state & MMC_STATE_DOING_BKOPS) #define mmc_card_need_bkops(c) ((c)->state & MMC_STATE_NEED_BKOPS) +#define mmc_card_is_sleep(c) ((c)->state & MMC_STATE_SLEEP) #define mmc_card_set_present(c) ((c)->state |= MMC_STATE_PRESENT) #define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY) @@ -487,6 +489,9 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data) #define mmc_card_clr_doing_bkops(c) ((c)->state &= ~MMC_STATE_DOING_BKOPS) #define mmc_card_set_need_bkops(c) ((c)->state |= MMC_STATE_NEED_BKOPS) #define mmc_card_clr_need_bkops(c) ((c)->state &= ~MMC_STATE_NEED_BKOPS) +#define mmc_card_set_sleep(c) ((c)->state |= MMC_STATE_SLEEP) +#define mmc_card_clr_sleep(c) ((c)->state &= ~MMC_STATE_SLEEP) + /* * Quirk add/remove for MMC products. */ diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index e7d34fc168395f896b80103e678e5c895af12f50..df526d6d12e991dce7d289e0a37cc94d79828f92 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -169,6 +169,7 @@ struct mmc_host { u32 ocr_avail_sd; /* SD-specific OCR */ u32 ocr_avail_mmc; /* MMC-specific OCR */ struct notifier_block pm_notify; + struct notifier_block force_poweroff_notifier; #define MMC_VDD_165_195 0x00000080 /* VDD voltage 1.65 - 1.95 */ #define MMC_VDD_20_21 0x00000100 /* VDD voltage 2.0 ~ 2.1 */