mmc: sdhci: add power management capability.
Current mmc stack doesn't use the framework provided by power management subsystem. It doesn't let each device suspend itself and the pm operations are solely handled by the platform driver. This may lead to races, since the concurrency of pm framework is not used. The pm core does its best to reduce the probability of a race between system suspend/resume and runtime PM by decrementing/incrementing the usage counters of respective devices during system-pm operations. Moreover, it disables runtime PM altogether after suspending the device and re-enables the same on resume. To avoid this, the parent child relationship between the platform, mmc_host and mmc_card devices is used. In this case, the relation is defined as, mmc_card -> (child of) -> mmc_host -> (child of) -> platform_dev Each device is now responsible for its power management. * mmc_card -> schedules the runtime-suspend * mmc_host -> actually suspends/resume the host & card i.e. invokes mmc_[suspend/resume]_host * pltform_dev -> disables irqs Typically, the card device serves as a trigger for scheduling the runtime-suspend and invoking runtime-resume. Two new runtime-pm functions have been introduced: * mmc_rpm_hold -> resumes the device passed as a parameter * mmc_rpm_release -> suspends the device passed as a parameter The above two functions are invoked from the below contexts: * mmc_rescan * bkops * mmc-queue Change-Id: Icf9dd34a445abfaf8dbb974ab1255feeda2581c9 Signed-off-by: Asutosh Das <asutoshd@codeaurora.org>
This commit is contained in:
parent
8ebf379082
commit
6dd7712d7b
|
@ -34,6 +34,7 @@
|
|||
#include <linux/delay.h>
|
||||
#include <linux/capability.h>
|
||||
#include <linux/compat.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include <trace/events/mmc.h>
|
||||
|
@ -2400,6 +2401,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
|
|||
#endif
|
||||
|
||||
if (req && !mq->mqrq_prev->req) {
|
||||
mmc_rpm_hold(host, &card->dev);
|
||||
/* claim host only for the first request */
|
||||
mmc_claim_host(card->host);
|
||||
if (card->ext_csd.bkops_en)
|
||||
|
@ -2453,6 +2455,7 @@ out:
|
|||
* the 'mmc_blk_issue_rq' with 'mqrq_prev->req'.
|
||||
*/
|
||||
mmc_release_host(card->host);
|
||||
mmc_rpm_release(host, &card->dev);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -2695,6 +2695,7 @@ static void mmc_test_run(struct mmc_test_card *test, int testcase)
|
|||
pr_info("%s: Starting tests of card %s...\n",
|
||||
mmc_hostname(test->card->host), mmc_card_id(test->card));
|
||||
|
||||
mmc_rpm_hold(test->card->host, &test->card->dev);
|
||||
mmc_claim_host(test->card->host);
|
||||
|
||||
for (i = 0;i < ARRAY_SIZE(mmc_test_cases);i++) {
|
||||
|
@ -2778,6 +2779,7 @@ static void mmc_test_run(struct mmc_test_card *test, int testcase)
|
|||
}
|
||||
|
||||
mmc_release_host(test->card->host);
|
||||
mmc_rpm_release(test->card->host, &test->card->dev);
|
||||
|
||||
pr_info("%s: Tests completed.\n",
|
||||
mmc_hostname(test->card->host));
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "bus.h"
|
||||
|
||||
#define to_mmc_driver(d) container_of(d, struct mmc_driver, drv)
|
||||
#define RUNTIME_SUSPEND_DELAY_MS 10000
|
||||
|
||||
static ssize_t mmc_type_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
|
@ -152,19 +153,38 @@ static int mmc_runtime_suspend(struct device *dev)
|
|||
{
|
||||
struct mmc_card *card = mmc_dev_to_card(dev);
|
||||
|
||||
return mmc_power_save_host(card->host);
|
||||
if (mmc_use_core_runtime_pm(card->host))
|
||||
return 0;
|
||||
else
|
||||
return mmc_power_save_host(card->host);
|
||||
}
|
||||
|
||||
static int mmc_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct mmc_card *card = mmc_dev_to_card(dev);
|
||||
|
||||
return mmc_power_restore_host(card->host);
|
||||
if (mmc_use_core_runtime_pm(card->host))
|
||||
return 0;
|
||||
else
|
||||
return mmc_power_restore_host(card->host);
|
||||
}
|
||||
|
||||
static int mmc_runtime_idle(struct device *dev)
|
||||
{
|
||||
return pm_runtime_suspend(dev);
|
||||
struct mmc_card *card = mmc_dev_to_card(dev);
|
||||
struct mmc_host *host = card->host;
|
||||
int ret = 0;
|
||||
|
||||
if (mmc_use_core_runtime_pm(card->host)) {
|
||||
ret = pm_schedule_suspend(dev, card->idle_timeout);
|
||||
if (ret) {
|
||||
pr_err("%s: %s: pm_schedule_suspend failed: err: %d\n",
|
||||
mmc_hostname(host), __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* !CONFIG_PM_RUNTIME */
|
||||
|
@ -175,6 +195,42 @@ static const struct dev_pm_ops mmc_bus_pm_ops = {
|
|||
SET_SYSTEM_SLEEP_PM_OPS(mmc_bus_suspend, mmc_bus_resume)
|
||||
};
|
||||
|
||||
static ssize_t show_rpm_delay(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct mmc_card *card = mmc_dev_to_card(dev);
|
||||
|
||||
if (!card) {
|
||||
pr_err("%s: %s: card is NULL\n", dev_name(dev), __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", card->idle_timeout);
|
||||
}
|
||||
|
||||
static ssize_t store_rpm_delay(struct device *dev, struct device_attribute
|
||||
*attr, const char *buf, size_t count)
|
||||
{
|
||||
struct mmc_card *card = mmc_dev_to_card(dev);
|
||||
unsigned int delay;
|
||||
|
||||
if (!card) {
|
||||
pr_err("%s: %s: card is NULL\n", dev_name(dev), __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!kstrtou32(buf, 0, &delay)) {
|
||||
if (delay < 2000) {
|
||||
pr_err("%s: %s: less than 2 sec delay is unsupported\n",
|
||||
mmc_hostname(card->host), __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
card->idle_timeout = delay;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static struct bus_type mmc_bus_type = {
|
||||
.name = "mmc",
|
||||
.dev_attrs = mmc_dev_attrs,
|
||||
|
@ -326,10 +382,34 @@ int mmc_add_card(struct mmc_card *card)
|
|||
#endif
|
||||
mmc_init_context_info(card->host);
|
||||
|
||||
if (mmc_use_core_runtime_pm(card->host)) {
|
||||
ret = pm_runtime_set_active(&card->dev);
|
||||
if (ret)
|
||||
pr_err("%s: %s: failed setting runtime active: ret: %d\n",
|
||||
mmc_hostname(card->host), __func__, ret);
|
||||
else
|
||||
pm_runtime_enable(&card->dev);
|
||||
}
|
||||
|
||||
ret = device_add(&card->dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (mmc_use_core_runtime_pm(card->host)) {
|
||||
card->rpm_attrib.show = show_rpm_delay;
|
||||
card->rpm_attrib.store = store_rpm_delay;
|
||||
sysfs_attr_init(&card->rpm_attrib.attr);
|
||||
card->rpm_attrib.attr.name = "runtime_pm_timeout";
|
||||
card->rpm_attrib.attr.mode = S_IRUGO | S_IWUSR;
|
||||
|
||||
ret = device_create_file(&card->dev, &card->rpm_attrib);
|
||||
if (ret)
|
||||
pr_err("%s: %s: creating runtime pm sysfs entry: failed: %d\n",
|
||||
mmc_hostname(card->host), __func__, ret);
|
||||
/* Default timeout is 10 seconds */
|
||||
card->idle_timeout = RUNTIME_SUSPEND_DELAY_MS;
|
||||
}
|
||||
|
||||
mmc_card_set_present(card);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -408,9 +408,12 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception)
|
|||
return;
|
||||
}
|
||||
|
||||
mmc_rpm_hold(card->host, &card->dev);
|
||||
/* In case of delayed bkops we might be in race with suspend. */
|
||||
if (!mmc_try_claim_host(card->host))
|
||||
if (!mmc_try_claim_host(card->host)) {
|
||||
mmc_rpm_release(card->host, &card->dev);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Since the cancel_delayed_work can be changed while we are waiting
|
||||
|
@ -485,6 +488,7 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception)
|
|||
|
||||
out:
|
||||
mmc_release_host(card->host);
|
||||
mmc_rpm_release(card->host, &card->dev);
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_start_bkops);
|
||||
|
||||
|
@ -526,6 +530,7 @@ void mmc_bkops_completion_polling(struct work_struct *work)
|
|||
* the host from getting into suspend
|
||||
*/
|
||||
do {
|
||||
mmc_rpm_hold(card->host, &card->dev);
|
||||
mmc_claim_host(card->host);
|
||||
|
||||
if (!mmc_card_doing_bkops(card))
|
||||
|
@ -552,6 +557,7 @@ void mmc_bkops_completion_polling(struct work_struct *work)
|
|||
}
|
||||
|
||||
mmc_release_host(card->host);
|
||||
mmc_rpm_release(card->host, &card->dev);
|
||||
|
||||
/*
|
||||
* Sleep before checking the card status again to allow the
|
||||
|
@ -570,6 +576,7 @@ void mmc_bkops_completion_polling(struct work_struct *work)
|
|||
return;
|
||||
out:
|
||||
mmc_release_host(card->host);
|
||||
mmc_rpm_release(card->host, &card->dev);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2714,8 +2721,9 @@ static void mmc_clk_scale_work(struct work_struct *work)
|
|||
if (!host->card || !host->bus_ops ||
|
||||
!host->bus_ops->change_bus_speed ||
|
||||
!host->clk_scaling.enable || !host->ios.clock)
|
||||
goto out;
|
||||
return;
|
||||
|
||||
mmc_rpm_hold(host, &host->card->dev);
|
||||
if (!mmc_try_claim_host(host)) {
|
||||
/* retry after a timer tick */
|
||||
queue_delayed_work(system_nrt_wq, &host->clk_scaling.work, 1);
|
||||
|
@ -2725,6 +2733,7 @@ static void mmc_clk_scale_work(struct work_struct *work)
|
|||
mmc_clk_scaling(host, true);
|
||||
mmc_release_host(host);
|
||||
out:
|
||||
mmc_rpm_release(host, &host->card->dev);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -3086,11 +3095,12 @@ void mmc_rescan(struct work_struct *work)
|
|||
goto out;
|
||||
}
|
||||
|
||||
mmc_rpm_hold(host, &host->class_dev);
|
||||
mmc_claim_host(host);
|
||||
if (!mmc_rescan_try_freq(host, host->f_min))
|
||||
extend_wakelock = true;
|
||||
mmc_release_host(host);
|
||||
|
||||
mmc_rpm_release(host, &host->class_dev);
|
||||
out:
|
||||
if (extend_wakelock)
|
||||
wake_lock_timeout(&host->detect_wake_lock, HZ / 2);
|
||||
|
@ -3319,9 +3329,6 @@ int mmc_suspend_host(struct mmc_host *host)
|
|||
if (mmc_bus_needs_resume(host))
|
||||
return 0;
|
||||
|
||||
cancel_delayed_work(&host->detect);
|
||||
mmc_flush_scheduled_work();
|
||||
|
||||
mmc_bus_get(host);
|
||||
if (host->bus_ops && !host->bus_dead) {
|
||||
/*
|
||||
|
@ -3517,6 +3524,37 @@ int mmc_pm_notify(struct notifier_block *notify_block,
|
|||
}
|
||||
#endif
|
||||
|
||||
void mmc_rpm_hold(struct mmc_host *host, struct device *dev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!mmc_use_core_runtime_pm(host))
|
||||
return;
|
||||
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
if (ret < 0) {
|
||||
pr_err("%s: %s: %s: error resuming device: %d\n",
|
||||
dev_name(dev), mmc_hostname(host), __func__, ret);
|
||||
if (pm_runtime_suspended(dev))
|
||||
BUG_ON(1);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_rpm_hold);
|
||||
|
||||
void mmc_rpm_release(struct mmc_host *host, struct device *dev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!mmc_use_core_runtime_pm(host))
|
||||
return;
|
||||
|
||||
ret = pm_runtime_put_sync(dev);
|
||||
if (ret < 0 && ret != -EBUSY)
|
||||
pr_err("%s: %s: %s: put sync ret: %d\n",
|
||||
dev_name(dev), mmc_hostname(host), __func__, ret);
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_rpm_release);
|
||||
|
||||
/**
|
||||
* mmc_init_context_info() - init synchronization context
|
||||
* @host: mmc host
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <linux/leds.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/suspend.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/card.h>
|
||||
|
@ -41,9 +42,73 @@ static void mmc_host_classdev_release(struct device *dev)
|
|||
kfree(host);
|
||||
}
|
||||
|
||||
static int mmc_host_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct mmc_host *host = cls_dev_to_mmc_host(dev);
|
||||
int ret = 0;
|
||||
|
||||
ret = mmc_suspend_host(host);
|
||||
if (ret < 0)
|
||||
pr_err("%s: %s: suspend host failed: %d\n", mmc_hostname(host),
|
||||
__func__, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mmc_host_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct mmc_host *host = cls_dev_to_mmc_host(dev);
|
||||
int ret = 0;
|
||||
|
||||
ret = mmc_resume_host(host);
|
||||
if (ret < 0) {
|
||||
pr_err("%s: %s: resume host: failed: ret: %d\n",
|
||||
mmc_hostname(host), __func__, ret);
|
||||
if (pm_runtime_suspended(dev))
|
||||
BUG_ON(1);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mmc_host_suspend(struct device *dev)
|
||||
{
|
||||
struct mmc_host *host = cls_dev_to_mmc_host(dev);
|
||||
int ret = 0;
|
||||
|
||||
if (!pm_runtime_suspended(dev)) {
|
||||
ret = mmc_suspend_host(host);
|
||||
if (ret < 0)
|
||||
pr_err("%s: %s: failed: ret: %d\n", mmc_hostname(host),
|
||||
__func__, ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mmc_host_resume(struct device *dev)
|
||||
{
|
||||
struct mmc_host *host = cls_dev_to_mmc_host(dev);
|
||||
int ret = 0;
|
||||
|
||||
if (!pm_runtime_suspended(dev)) {
|
||||
ret = mmc_resume_host(host);
|
||||
if (ret < 0)
|
||||
pr_err("%s: %s: failed: ret: %d\n", mmc_hostname(host),
|
||||
__func__, ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops mmc_host_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(mmc_host_suspend, mmc_host_resume)
|
||||
SET_RUNTIME_PM_OPS(mmc_host_runtime_suspend, mmc_host_runtime_resume,
|
||||
pm_generic_runtime_idle)
|
||||
};
|
||||
|
||||
static struct class mmc_host_class = {
|
||||
.name = "mmc_host",
|
||||
.dev_release = mmc_host_classdev_release,
|
||||
.pm = &mmc_host_pm_ops,
|
||||
};
|
||||
|
||||
int mmc_register_host_class(void)
|
||||
|
@ -731,6 +796,14 @@ int mmc_add_host(struct mmc_host *host)
|
|||
WARN_ON((host->caps & MMC_CAP_SDIO_IRQ) &&
|
||||
!host->ops->enable_sdio_irq);
|
||||
|
||||
if (mmc_use_core_runtime_pm(host)) {
|
||||
err = pm_runtime_set_active(&host->class_dev);
|
||||
if (err)
|
||||
pr_err("%s: %s: failed setting runtime active: err: %d\n",
|
||||
mmc_hostname(host), __func__, err);
|
||||
else
|
||||
pm_runtime_enable(&host->class_dev);
|
||||
}
|
||||
err = device_add(&host->class_dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/card.h>
|
||||
#include <linux/mmc/mmc.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "bus.h"
|
||||
|
@ -1503,6 +1504,7 @@ static void mmc_detect(struct mmc_host *host)
|
|||
BUG_ON(!host);
|
||||
BUG_ON(!host->card);
|
||||
|
||||
mmc_rpm_hold(host, &host->card->dev);
|
||||
mmc_claim_host(host);
|
||||
|
||||
/*
|
||||
|
@ -1512,6 +1514,13 @@ static void mmc_detect(struct mmc_host *host)
|
|||
|
||||
mmc_release_host(host);
|
||||
|
||||
/*
|
||||
* if detect fails, the device would be removed anyway;
|
||||
* the rpm framework would mark the device state suspended.
|
||||
*/
|
||||
if (!err)
|
||||
mmc_rpm_release(host, &host->card->dev);
|
||||
|
||||
if (err) {
|
||||
mmc_remove(host);
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <linux/mmc/card.h>
|
||||
#include <linux/mmc/mmc.h>
|
||||
#include <linux/mmc/sd.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "bus.h"
|
||||
|
@ -1135,6 +1136,7 @@ static void mmc_sd_detect(struct mmc_host *host)
|
|||
BUG_ON(!host);
|
||||
BUG_ON(!host->card);
|
||||
|
||||
mmc_rpm_hold(host, &host->card->dev);
|
||||
mmc_claim_host(host);
|
||||
|
||||
/*
|
||||
|
@ -1161,6 +1163,13 @@ static void mmc_sd_detect(struct mmc_host *host)
|
|||
|
||||
mmc_release_host(host);
|
||||
|
||||
/*
|
||||
* if detect fails, the device would be removed anyway;
|
||||
* the rpm framework would mark the device state suspended.
|
||||
*/
|
||||
if (!err)
|
||||
mmc_rpm_release(host, &host->card->dev);
|
||||
|
||||
if (err) {
|
||||
mmc_sd_remove(host);
|
||||
|
||||
|
|
|
@ -32,6 +32,8 @@
|
|||
#include <linux/scatterlist.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mmc/mmc.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <mach/gpio.h>
|
||||
#include <mach/msm_bus.h>
|
||||
|
||||
|
@ -216,6 +218,7 @@ struct sdhci_msm_bus_vote {
|
|||
struct sdhci_msm_host {
|
||||
struct platform_device *pdev;
|
||||
void __iomem *core_mem; /* MSM SDCC mapped address */
|
||||
int pwr_irq; /* power irq */
|
||||
struct clk *clk; /* main SD/MMC bus clock */
|
||||
struct clk *pclk; /* SDHC peripheral bus clock */
|
||||
struct clk *bus_clk; /* SDHC bus voter clock */
|
||||
|
@ -1852,7 +1855,7 @@ static int sdhci_msm_probe(struct platform_device *pdev)
|
|||
struct sdhci_pltfm_host *pltfm_host;
|
||||
struct sdhci_msm_host *msm_host;
|
||||
struct resource *core_memres = NULL;
|
||||
int ret = 0, pwr_irq = 0, dead = 0;
|
||||
int ret = 0, dead = 0;
|
||||
u32 host_version;
|
||||
|
||||
pr_debug("%s: Enter %s\n", dev_name(&pdev->dev), __func__);
|
||||
|
@ -1974,18 +1977,18 @@ static int sdhci_msm_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
/* Setup PWRCTL irq */
|
||||
pwr_irq = platform_get_irq_byname(pdev, "pwr_irq");
|
||||
if (pwr_irq < 0) {
|
||||
msm_host->pwr_irq = platform_get_irq_byname(pdev, "pwr_irq");
|
||||
if (msm_host->pwr_irq < 0) {
|
||||
dev_err(&pdev->dev, "Failed to get pwr_irq by name (%d)\n",
|
||||
pwr_irq);
|
||||
msm_host->pwr_irq);
|
||||
goto vreg_deinit;
|
||||
}
|
||||
ret = devm_request_threaded_irq(&pdev->dev, pwr_irq, NULL,
|
||||
ret = devm_request_threaded_irq(&pdev->dev, msm_host->pwr_irq, NULL,
|
||||
sdhci_msm_pwr_irq, IRQF_ONESHOT,
|
||||
dev_name(&pdev->dev), host);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Request threaded irq(%d) failed (%d)\n",
|
||||
pwr_irq, ret);
|
||||
msm_host->pwr_irq, ret);
|
||||
goto vreg_deinit;
|
||||
}
|
||||
|
||||
|
@ -2000,6 +2003,7 @@ static int sdhci_msm_probe(struct platform_device *pdev)
|
|||
msm_host->mmc->caps |= msm_host->pdata->caps;
|
||||
msm_host->mmc->caps |= MMC_CAP_HW_RESET;
|
||||
msm_host->mmc->caps2 |= msm_host->pdata->caps2;
|
||||
msm_host->mmc->caps2 |= MMC_CAP2_CORE_RUNTIME_PM;
|
||||
msm_host->mmc->caps2 |= MMC_CAP2_PACKED_WR;
|
||||
msm_host->mmc->caps2 |= MMC_CAP2_PACKED_WR_CONTROL;
|
||||
msm_host->mmc->caps2 |= (MMC_CAP2_BOOTPART_NOACC |
|
||||
|
@ -2046,6 +2050,13 @@ static int sdhci_msm_probe(struct platform_device *pdev)
|
|||
if (ret)
|
||||
goto remove_host;
|
||||
|
||||
ret = pm_runtime_set_active(&pdev->dev);
|
||||
if (ret)
|
||||
pr_err("%s: %s: pm_runtime_set_active failed: err: %d\n",
|
||||
mmc_hostname(host->mmc), __func__, ret);
|
||||
else
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
/* Successful initialization */
|
||||
goto out;
|
||||
|
||||
|
@ -2084,6 +2095,7 @@ static int sdhci_msm_remove(struct platform_device *pdev)
|
|||
pr_debug("%s: %s\n", dev_name(&pdev->dev), __func__);
|
||||
device_remove_file(&pdev->dev, &msm_host->msm_bus_vote.max_bus_bw);
|
||||
sdhci_remove_host(host, dead);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
sdhci_pltfm_free(pdev);
|
||||
sdhci_msm_vreg_init(&pdev->dev, msm_host->pdata, false);
|
||||
|
||||
|
@ -2097,6 +2109,77 @@ static int sdhci_msm_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int sdhci_msm_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_msm_host *msm_host = pltfm_host->priv;
|
||||
|
||||
disable_irq(host->irq);
|
||||
disable_irq(msm_host->pwr_irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sdhci_msm_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_msm_host *msm_host = pltfm_host->priv;
|
||||
|
||||
enable_irq(msm_host->pwr_irq);
|
||||
enable_irq(host->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
||||
static int sdhci_msm_suspend(struct device *dev)
|
||||
{
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
|
||||
if (pm_runtime_suspended(dev)) {
|
||||
pr_debug("%s: %s: already runtime suspended\n",
|
||||
mmc_hostname(host->mmc), __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
return sdhci_msm_runtime_suspend(dev);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sdhci_msm_resume(struct device *dev)
|
||||
{
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
|
||||
if (pm_runtime_suspended(dev)) {
|
||||
pr_debug("%s: %s: runtime suspended, defer system resume\n",
|
||||
mmc_hostname(host->mmc), __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
return sdhci_msm_runtime_resume(dev);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static const struct dev_pm_ops sdhci_msm_pmops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(sdhci_msm_suspend, sdhci_msm_resume)
|
||||
SET_RUNTIME_PM_OPS(sdhci_msm_runtime_suspend, sdhci_msm_runtime_resume,
|
||||
NULL)
|
||||
};
|
||||
|
||||
#define SDHCI_MSM_PMOPS (&sdhci_msm_pmops)
|
||||
|
||||
#else
|
||||
#define SDHCI_PM_OPS NULL
|
||||
#endif
|
||||
static const struct of_device_id sdhci_msm_dt_match[] = {
|
||||
{.compatible = "qcom,sdhci-msm"},
|
||||
};
|
||||
|
@ -2109,6 +2192,7 @@ static struct platform_driver sdhci_msm_driver = {
|
|||
.name = "sdhci_msm",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = sdhci_msm_dt_match,
|
||||
.pm = SDHCI_MSM_PMOPS,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -2782,13 +2782,20 @@ EXPORT_SYMBOL_GPL(sdhci_resume_host);
|
|||
|
||||
static int sdhci_runtime_pm_get(struct sdhci_host *host)
|
||||
{
|
||||
return pm_runtime_get_sync(host->mmc->parent);
|
||||
if (!mmc_use_core_runtime_pm(host->mmc))
|
||||
return pm_runtime_get_sync(host->mmc->parent);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sdhci_runtime_pm_put(struct sdhci_host *host)
|
||||
{
|
||||
pm_runtime_mark_last_busy(host->mmc->parent);
|
||||
return pm_runtime_put_autosuspend(host->mmc->parent);
|
||||
if (!mmc_use_core_runtime_pm(host->mmc)) {
|
||||
pm_runtime_mark_last_busy(host->mmc->parent);
|
||||
return pm_runtime_put_autosuspend(host->mmc->parent);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int sdhci_runtime_suspend_host(struct sdhci_host *host)
|
||||
|
|
|
@ -384,6 +384,9 @@ struct mmc_card {
|
|||
struct mmc_wr_pack_stats wr_pack_stats; /* packed commands stats*/
|
||||
|
||||
struct mmc_bkops_info bkops_info;
|
||||
|
||||
struct device_attribute rpm_attrib;
|
||||
unsigned int idle_timeout;
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
@ -169,6 +169,8 @@ extern int mmc_flush_cache(struct mmc_card *);
|
|||
extern int mmc_detect_card_removed(struct mmc_host *host);
|
||||
|
||||
extern void mmc_blk_init_bkops_statistics(struct mmc_card *card);
|
||||
extern void mmc_rpm_hold(struct mmc_host *host, struct device *dev);
|
||||
extern void mmc_rpm_release(struct mmc_host *host, struct device *dev);
|
||||
|
||||
/**
|
||||
* mmc_claim_host - exclusively claim a host
|
||||
|
|
|
@ -287,6 +287,8 @@ struct mmc_host {
|
|||
#define MMC_CAP2_INIT_BKOPS (1 << 15) /* Need to set BKOPS_EN */
|
||||
#define MMC_CAP2_PACKED_WR_CONTROL (1 << 16) /* Allow write packing control */
|
||||
#define MMC_CAP2_CLK_SCALE (1 << 17) /* Allow dynamic clk scaling */
|
||||
/* Use runtime PM framework provided by MMC core */
|
||||
#define MMC_CAP2_CORE_RUNTIME_PM (1 << 19)
|
||||
mmc_pm_flag_t pm_caps; /* supported pm features */
|
||||
|
||||
int clk_requests; /* internal reference counter */
|
||||
|
@ -555,4 +557,10 @@ static inline unsigned int mmc_host_clk_rate(struct mmc_host *host)
|
|||
return host->ios.clock;
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline int mmc_use_core_runtime_pm(struct mmc_host *host)
|
||||
{
|
||||
return host->caps2 & MMC_CAP2_CORE_RUNTIME_PM;
|
||||
}
|
||||
|
||||
#endif /* LINUX_MMC_HOST_H */
|
||||
|
|
Loading…
Reference in New Issue