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);
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;
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,
.change_bus_speed = mmc_change_bus_speed,
.reset = mmc_reset,
.shutdown = mmc_shutdown,
+ .pre_hibernate = mmc_pre_hibernate,
+ .post_hibernate = mmc_post_hibernate
};
/*