diff options
author | hsuan-chih_chen <hsuan-chih_chen@asus.com> | 2015-04-29 20:09:30 +0800 |
---|---|---|
committer | Ed Tam <etam@google.com> | 2015-05-27 14:04:31 -0700 |
commit | fe3bd6122588e0f99e1cb7034cfc89fa0faec754 (patch) | |
tree | 78254ff6d4830f4d0f96776066ed70170721a288 | |
parent | 077e2228daf0c09da0220c922489d6be46408289 (diff) |
mmc: add force poweroff notify and sw reset for some cardandroid-5.1.1_r0.22android-5.1.1_r0.13
sending power off notify to eMMC before power off.
trigger sw reset before actual HW reset.
Change-Id: I4e3df098b084cc5f39cfd1c52718ed0833d8ba24
Signed-off-by: hsuan-chih_chen <hsuan-chih_chen@asus.com>
-rw-r--r-- | drivers/mmc/core/core.c | 62 | ||||
-rw-r--r-- | drivers/mmc/core/core.h | 4 | ||||
-rw-r--r-- | drivers/mmc/core/host.c | 4 | ||||
-rw-r--r-- | drivers/mmc/core/mmc.c | 4 | ||||
-rw-r--r-- | drivers/mmc/host/msm_sdcc.c | 17 | ||||
-rw-r--r-- | include/linux/mmc/card.h | 5 | ||||
-rw-r--r-- | include/linux/mmc/host.h | 1 |
7 files changed, 97 insertions, 0 deletions
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index d40f6c852727..d0c8249866ef 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 85d273775ac0..2b4c51ff39aa 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 29b64f0b7c86..e249788661a9 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 ea1eca731ae5..e56d8479b945 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 91aa05f94bb5..8781bfeb114a 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 6aade3e4d34f..9d698ed331a9 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 e7d34fc16839..df526d6d12e9 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 */ |