diff options
| -rw-r--r-- | drivers/mmc/core/core.c | 10 | ||||
| -rw-r--r-- | drivers/mmc/core/mmc.c | 69 | ||||
| -rw-r--r-- | include/linux/mmc/core.h | 2 | ||||
| -rw-r--r-- | include/linux/mmc/host.h | 1 |
4 files changed, 79 insertions, 3 deletions
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 547d18c9feef..151643caac84 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -4526,9 +4526,11 @@ int mmc_pm_notify(struct notifier_block *notify_block, int err = 0; switch (mode) { + case PM_RESTORE_PREPARE: case PM_HIBERNATION_PREPARE: + if (host->bus_ops && host->bus_ops->pre_hibernate) + host->bus_ops->pre_hibernate(host); case PM_SUSPEND_PREPARE: - case PM_RESTORE_PREPARE: spin_lock_irqsave(&host->lock, flags); host->rescan_disable = 1; spin_unlock_irqrestore(&host->lock, flags); @@ -4560,9 +4562,11 @@ int mmc_pm_notify(struct notifier_block *notify_block, host->pm_flags = 0; break; - case PM_POST_SUSPEND: - case PM_POST_HIBERNATION: case PM_POST_RESTORE: + case PM_POST_HIBERNATION: + if (host->bus_ops && host->bus_ops->post_hibernate) + host->bus_ops->post_hibernate(host); + case PM_POST_SUSPEND: spin_lock_irqsave(&host->lock, flags); host->rescan_disable = 0; diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index c8f85b31e2ac..999fdb9bad7d 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -2953,6 +2953,73 @@ static int mmc_shutdown(struct mmc_host *host) return 0; } +static int mmc_pre_hibernate(struct mmc_host *host) +{ + int ret = 0; + + mmc_get_card(host->card); + host->cached_caps2 = host->caps2; + + /* + * Increase usage_count of card and host device till + * hibernation is over. This will ensure they will not runtime suspend. + */ + pm_runtime_get_noresume(mmc_dev(host)); + pm_runtime_get_noresume(&host->card->dev); + + if (!mmc_can_scale_clk(host)) + goto out; + /* + * Suspend clock scaling and mask host capability so that + * we will run in max frequency during: + * 1. Hibernation preparation and image creation + * 2. After finding hibernation image during reboot + * 3. Once hibernation image is loaded and till hibernation + * restore is complete. + */ + if (host->clk_scaling.enable) + mmc_suspend_clk_scaling(host); + host->caps2 &= ~MMC_CAP2_CLK_SCALE; + host->clk_scaling.state = MMC_LOAD_HIGH; + ret = mmc_clk_update_freq(host, host->card->clk_scaling_highest, + host->clk_scaling.state); + if (ret) + pr_err("%s: %s: Setting clk frequency to max failed: %d\n", + mmc_hostname(host), __func__, ret); +out: + mmc_host_clk_hold(host); + mmc_put_card(host->card); + return ret; +} + +static int mmc_post_hibernate(struct mmc_host *host) +{ + int ret = 0; + + mmc_get_card(host->card); + if (!(host->cached_caps2 & MMC_CAP2_CLK_SCALE)) + goto enable_pm; + /* Enable the clock scaling and set the host capability */ + host->caps2 |= MMC_CAP2_CLK_SCALE; + if (!host->clk_scaling.enable) + ret = mmc_resume_clk_scaling(host); + if (ret) + pr_err("%s: %s: Resuming clk scaling failed: %d\n", + mmc_hostname(host), __func__, ret); +enable_pm: + /* + * Reduce usage count of card and host device so that they may + * runtime suspend. + */ + pm_runtime_put_noidle(&host->card->dev); + pm_runtime_put_noidle(mmc_dev(host)); + + mmc_host_clk_release(host); + + mmc_put_card(host->card); + return ret; +} + static const struct mmc_bus_ops mmc_ops = { .remove = mmc_remove, .detect = mmc_detect, @@ -2964,6 +3031,8 @@ static const struct mmc_bus_ops mmc_ops = { .change_bus_speed = mmc_change_bus_speed, .reset = mmc_reset, .shutdown = mmc_shutdown, + .pre_hibernate = mmc_pre_hibernate, + .post_hibernate = mmc_post_hibernate }; /* diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index 862d8d1bae8f..ef3e628388cf 100644 --- a/include/linux/mmc/core.h +++ b/include/linux/mmc/core.h @@ -131,6 +131,8 @@ struct mmc_bus_ops { int (*shutdown)(struct mmc_host *); int (*reset)(struct mmc_host *); int (*change_bus_speed)(struct mmc_host *, unsigned long *); + int (*pre_hibernate)(struct mmc_host *); + int (*post_hibernate)(struct mmc_host *); }; struct mmc_card; diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index f564303a28f9..89e19dd4b144 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -419,6 +419,7 @@ struct mmc_host { #define MMC_CAP_HW_RESET (1 << 31) /* Hardware reset */ u32 caps2; /* More host capabilities */ + u32 cached_caps2; #define MMC_CAP2_BOOTPART_NOACC (1 << 0) /* Boot partition no access */ #define MMC_CAP2_FULL_PWR_CYCLE (1 << 2) /* Can do full power cycle */ |
