aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGirish K S <girish.shivananjappa@linaro.org>2011-10-13 02:34:16 -0400
committerChris Ball <cjb@laptop.org>2011-10-26 16:32:23 -0400
commitbec8726abc72bf30d2743a722aa37cd69e7a0580 (patch)
treeeed4a3c441ff64f5719b021fce419de0fc5196d9
parent326adda53a50ece492c3edaa60afc26fba5e3232 (diff)
mmc: core: Add Power Off Notify Feature eMMC 4.5
This patch adds support for the power off notify feature, available in eMMC 4.5 devices. If the host has support for this feature, then the mmc core will notify the device by setting the POWER_OFF_NOTIFICATION byte in the extended csd register with a value of 1 (POWER_ON). For suspend mode short timeout is used, whereas for the normal poweroff long timeout is used. Signed-off-by: Girish K S <girish.shivananjappa@linaro.org> Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com> Signed-off-by: Chris Ball <cjb@laptop.org>
-rw-r--r--drivers/mmc/core/core.c34
-rw-r--r--drivers/mmc/core/mmc.c23
-rw-r--r--drivers/mmc/host/sdhci.c9
-rw-r--r--include/linux/mmc/card.h6
-rw-r--r--include/linux/mmc/host.h5
-rw-r--r--include/linux/mmc/mmc.h6
6 files changed, 81 insertions, 2 deletions
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 61d7730bc8b2..a3c4e0fe9434 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -1212,11 +1212,43 @@ static void mmc_power_up(struct mmc_host *host)
1212 1212
1213void mmc_power_off(struct mmc_host *host) 1213void mmc_power_off(struct mmc_host *host)
1214{ 1214{
1215 struct mmc_card *card;
1216 unsigned int notify_type;
1217 unsigned int timeout;
1218 int err;
1219
1215 mmc_host_clk_hold(host); 1220 mmc_host_clk_hold(host);
1216 1221
1222 card = host->card;
1217 host->ios.clock = 0; 1223 host->ios.clock = 0;
1218 host->ios.vdd = 0; 1224 host->ios.vdd = 0;
1219 1225
1226 if (card && mmc_card_mmc(card) &&
1227 (card->poweroff_notify_state == MMC_POWERED_ON)) {
1228
1229 if (host->power_notify_type == MMC_HOST_PW_NOTIFY_SHORT) {
1230 notify_type = EXT_CSD_POWER_OFF_SHORT;
1231 timeout = card->ext_csd.generic_cmd6_time;
1232 card->poweroff_notify_state = MMC_POWEROFF_SHORT;
1233 } else {
1234 notify_type = EXT_CSD_POWER_OFF_LONG;
1235 timeout = card->ext_csd.power_off_longtime;
1236 card->poweroff_notify_state = MMC_POWEROFF_LONG;
1237 }
1238
1239 err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
1240 EXT_CSD_POWER_OFF_NOTIFICATION,
1241 notify_type, timeout);
1242
1243 if (err && err != -EBADMSG)
1244 pr_err("Device failed to respond within %d poweroff "
1245 "time. Forcefully powering down the device\n",
1246 timeout);
1247
1248 /* Set the card state to no notification after the poweroff */
1249 card->poweroff_notify_state = MMC_NO_POWER_NOTIFICATION;
1250 }
1251
1220 /* 1252 /*
1221 * Reset ocr mask to be the highest possible voltage supported for 1253 * Reset ocr mask to be the highest possible voltage supported for
1222 * this mmc host. This value will be used at next power up. 1254 * this mmc host. This value will be used at next power up.
@@ -2208,6 +2240,7 @@ int mmc_pm_notify(struct notifier_block *notify_block,
2208 2240
2209 spin_lock_irqsave(&host->lock, flags); 2241 spin_lock_irqsave(&host->lock, flags);
2210 host->rescan_disable = 1; 2242 host->rescan_disable = 1;
2243 host->power_notify_type = MMC_HOST_PW_NOTIFY_SHORT;
2211 spin_unlock_irqrestore(&host->lock, flags); 2244 spin_unlock_irqrestore(&host->lock, flags);
2212 cancel_delayed_work_sync(&host->detect); 2245 cancel_delayed_work_sync(&host->detect);
2213 2246
@@ -2231,6 +2264,7 @@ int mmc_pm_notify(struct notifier_block *notify_block,
2231 2264
2232 spin_lock_irqsave(&host->lock, flags); 2265 spin_lock_irqsave(&host->lock, flags);
2233 host->rescan_disable = 0; 2266 host->rescan_disable = 0;
2267 host->power_notify_type = MMC_HOST_PW_NOTIFY_LONG;
2234 spin_unlock_irqrestore(&host->lock, flags); 2268 spin_unlock_irqrestore(&host->lock, flags);
2235 mmc_detect_change(host, 0); 2269 mmc_detect_change(host, 0);
2236 2270
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 4e869d371a03..f8ea9387d75c 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -458,10 +458,12 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
458 else 458 else
459 card->erased_byte = 0x0; 459 card->erased_byte = 0x0;
460 460
461 if (card->ext_csd.rev >= 6) 461 if (card->ext_csd.rev >= 6) {
462 card->ext_csd.generic_cmd6_time = 10 * 462 card->ext_csd.generic_cmd6_time = 10 *
463 ext_csd[EXT_CSD_GENERIC_CMD6_TIME]; 463 ext_csd[EXT_CSD_GENERIC_CMD6_TIME];
464 else 464 card->ext_csd.power_off_longtime = 10 *
465 ext_csd[EXT_CSD_POWER_OFF_LONG_TIME];
466 } else
465 card->ext_csd.generic_cmd6_time = 0; 467 card->ext_csd.generic_cmd6_time = 0;
466 468
467out: 469out:
@@ -846,6 +848,23 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
846 } 848 }
847 849
848 /* 850 /*
851 * If the host supports the power_off_notify capability then
852 * set the notification byte in the ext_csd register of device
853 */
854 if ((host->caps2 & MMC_CAP2_POWEROFF_NOTIFY) &&
855 (card->poweroff_notify_state == MMC_NO_POWER_NOTIFICATION)) {
856 err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
857 EXT_CSD_POWER_OFF_NOTIFICATION,
858 EXT_CSD_POWER_ON,
859 card->ext_csd.generic_cmd6_time);
860 if (err && err != -EBADMSG)
861 goto free_card;
862 }
863
864 if (!err)
865 card->poweroff_notify_state = MMC_POWERED_ON;
866
867 /*
849 * Activate high speed (if supported) 868 * Activate high speed (if supported)
850 */ 869 */
851 if ((card->ext_csd.hs_max_dtr != 0) && 870 if ((card->ext_csd.hs_max_dtr != 0) &&
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 2cc3ffa7d766..6d8eea323541 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -2739,6 +2739,15 @@ int sdhci_add_host(struct sdhci_host *host)
2739 if (caps[1] & SDHCI_DRIVER_TYPE_D) 2739 if (caps[1] & SDHCI_DRIVER_TYPE_D)
2740 mmc->caps |= MMC_CAP_DRIVER_TYPE_D; 2740 mmc->caps |= MMC_CAP_DRIVER_TYPE_D;
2741 2741
2742 /*
2743 * If Power Off Notify capability is enabled by the host,
2744 * set notify to short power off notify timeout value.
2745 */
2746 if (mmc->caps2 & MMC_CAP2_POWEROFF_NOTIFY)
2747 mmc->power_notify_type = MMC_HOST_PW_NOTIFY_SHORT;
2748 else
2749 mmc->power_notify_type = MMC_HOST_PW_NOTIFY_NONE;
2750
2742 /* Initial value for re-tuning timer count */ 2751 /* Initial value for re-tuning timer count */
2743 host->tuning_count = (caps[1] & SDHCI_RETUNING_TIMER_COUNT_MASK) >> 2752 host->tuning_count = (caps[1] & SDHCI_RETUNING_TIMER_COUNT_MASK) >>
2744 SDHCI_RETUNING_TIMER_COUNT_SHIFT; 2753 SDHCI_RETUNING_TIMER_COUNT_SHIFT;
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 2fcd24ccd38c..711c3f8bfabd 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -55,6 +55,7 @@ struct mmc_ext_csd {
55 unsigned int part_time; /* Units: ms */ 55 unsigned int part_time; /* Units: ms */
56 unsigned int sa_timeout; /* Units: 100ns */ 56 unsigned int sa_timeout; /* Units: 100ns */
57 unsigned int generic_cmd6_time; /* Units: 10ms */ 57 unsigned int generic_cmd6_time; /* Units: 10ms */
58 unsigned int power_off_longtime; /* Units: ms */
58 unsigned int hs_max_dtr; 59 unsigned int hs_max_dtr;
59 unsigned int sectors; 60 unsigned int sectors;
60 unsigned int card_type; 61 unsigned int card_type;
@@ -209,6 +210,11 @@ struct mmc_card {
209#define MMC_QUIRK_BLK_NO_CMD23 (1<<7) /* Avoid CMD23 for regular multiblock */ 210#define MMC_QUIRK_BLK_NO_CMD23 (1<<7) /* Avoid CMD23 for regular multiblock */
210#define MMC_QUIRK_BROKEN_BYTE_MODE_512 (1<<8) /* Avoid sending 512 bytes in */ 211#define MMC_QUIRK_BROKEN_BYTE_MODE_512 (1<<8) /* Avoid sending 512 bytes in */
211 /* byte mode */ 212 /* byte mode */
213 unsigned int poweroff_notify_state; /* eMMC4.5 notify feature */
214#define MMC_NO_POWER_NOTIFICATION 0
215#define MMC_POWERED_ON 1
216#define MMC_POWEROFF_SHORT 2
217#define MMC_POWEROFF_LONG 3
212 218
213 unsigned int erase_size; /* erase size in sectors */ 219 unsigned int erase_size; /* erase size in sectors */
214 unsigned int erase_shift; /* if erase unit is power 2 */ 220 unsigned int erase_shift; /* if erase unit is power 2 */
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index aed5bc7245f7..cd10208d9a06 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -239,8 +239,13 @@ struct mmc_host {
239 unsigned int caps2; /* More host capabilities */ 239 unsigned int caps2; /* More host capabilities */
240 240
241#define MMC_CAP2_BOOTPART_NOACC (1 << 0) /* Boot partition no access */ 241#define MMC_CAP2_BOOTPART_NOACC (1 << 0) /* Boot partition no access */
242#define MMC_CAP2_POWEROFF_NOTIFY (1 << 2) /* Notify poweroff supported */
242 243
243 mmc_pm_flag_t pm_caps; /* supported pm features */ 244 mmc_pm_flag_t pm_caps; /* supported pm features */
245 unsigned int power_notify_type;
246#define MMC_HOST_PW_NOTIFY_NONE 0
247#define MMC_HOST_PW_NOTIFY_SHORT 1
248#define MMC_HOST_PW_NOTIFY_LONG 2
244 249
245#ifdef CONFIG_MMC_CLKGATE 250#ifdef CONFIG_MMC_CLKGATE
246 int clk_requests; /* internal reference counter */ 251 int clk_requests; /* internal reference counter */
diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
index cd63c2dc95cc..02e9e3de9609 100644
--- a/include/linux/mmc/mmc.h
+++ b/include/linux/mmc/mmc.h
@@ -270,6 +270,7 @@ struct _mmc_csd {
270 * EXT_CSD fields 270 * EXT_CSD fields
271 */ 271 */
272 272
273#define EXT_CSD_POWER_OFF_NOTIFICATION 34 /* R/W */
273#define EXT_CSD_GP_SIZE_MULT 143 /* R/W */ 274#define EXT_CSD_GP_SIZE_MULT 143 /* R/W */
274#define EXT_CSD_PARTITION_ATTRIBUTE 156 /* R/W */ 275#define EXT_CSD_PARTITION_ATTRIBUTE 156 /* R/W */
275#define EXT_CSD_PARTITION_SUPPORT 160 /* RO */ 276#define EXT_CSD_PARTITION_SUPPORT 160 /* RO */
@@ -346,6 +347,11 @@ struct _mmc_csd {
346#define EXT_CSD_RST_N_EN_MASK 0x3 347#define EXT_CSD_RST_N_EN_MASK 0x3
347#define EXT_CSD_RST_N_ENABLED 1 /* RST_n is enabled on card */ 348#define EXT_CSD_RST_N_ENABLED 1 /* RST_n is enabled on card */
348 349
350#define EXT_CSD_NO_POWER_NOTIFICATION 0
351#define EXT_CSD_POWER_ON 1
352#define EXT_CSD_POWER_OFF_SHORT 2
353#define EXT_CSD_POWER_OFF_LONG 3
354
349#define EXT_CSD_PWR_CL_8BIT_MASK 0xF0 /* 8 bit PWR CLS */ 355#define EXT_CSD_PWR_CL_8BIT_MASK 0xF0 /* 8 bit PWR CLS */
350#define EXT_CSD_PWR_CL_4BIT_MASK 0x0F /* 8 bit PWR CLS */ 356#define EXT_CSD_PWR_CL_4BIT_MASK 0x0F /* 8 bit PWR CLS */
351#define EXT_CSD_PWR_CL_8BIT_SHIFT 4 357#define EXT_CSD_PWR_CL_8BIT_SHIFT 4