aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhsuan-chih_chen <hsuan-chih_chen@asus.com>2015-04-29 20:09:30 +0800
committerEd Tam <etam@google.com>2015-05-27 14:04:31 -0700
commitfe3bd6122588e0f99e1cb7034cfc89fa0faec754 (patch)
tree78254ff6d4830f4d0f96776066ed70170721a288
parent077e2228daf0c09da0220c922489d6be46408289 (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.c62
-rw-r--r--drivers/mmc/core/core.h4
-rw-r--r--drivers/mmc/core/host.c4
-rw-r--r--drivers/mmc/core/mmc.c4
-rw-r--r--drivers/mmc/host/msm_sdcc.c17
-rw-r--r--include/linux/mmc/card.h5
-rw-r--r--include/linux/mmc/host.h1
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 */