summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGilad Broner <gbroner@codeaurora.org>2015-09-29 16:57:21 +0300
committerSubhash Jadavani <subhashj@codeaurora.org>2016-05-31 15:27:30 -0700
commit17a072dd256d9a3246abf28e78e62a65c63f84f1 (patch)
treea559ff01369a334d73a7ecec8cfc30659ee81570
parent64be1cd3e02145b3ab5918b4526081840cbff477 (diff)
mmc: sdhci-msm: add PM QoS legacy voting
Add PM QoS voting mechanism to sdhci-msm driver for legacy eMMC. Two types of voting schemes are supported: 1) Vote for HW IRQ 2) Vote for a cpu group according to the request's designated cpu Using PM QoS voting should benefit performance. Change-Id: I5d2b71fc4eabfa5060f343634fbc7363f2ee1344 Signed-off-by: Konstantin Dorfman <kdorfman@codeaurora.org> Signed-off-by: Gilad Broner <gbroner@codeaurora.org> [subhashj@codeaurora.org: fixed merge conflicts] Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org>
-rw-r--r--drivers/mmc/card/queue.c4
-rw-r--r--drivers/mmc/host/sdhci-msm.c66
-rw-r--r--drivers/mmc/host/sdhci-msm.h4
-rw-r--r--drivers/mmc/host/sdhci.c15
-rw-r--r--drivers/mmc/host/sdhci.h3
-rw-r--r--include/linux/mmc/host.h1
6 files changed, 88 insertions, 5 deletions
diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
index d647a1a19575..efe2059d4fad 100644
--- a/drivers/mmc/card/queue.c
+++ b/drivers/mmc/card/queue.c
@@ -479,6 +479,10 @@ cur_sg_alloc_failed:
success:
sema_init(&mq->thread_sem, 1);
+ /* hook for pm qos legacy init */
+ if (card->host->ops->init)
+ card->host->ops->init(card->host);
+
mq->thread = kthread_run(mmc_queue_thread, mq, "mmcqd/%d%s",
host->index, subname ? subname : "");
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index 382df8e0df39..8801b3afa6ac 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -3291,7 +3291,7 @@ static void sdhci_msm_pm_qos_cpu_unvote_work(struct work_struct *work)
pm_qos_update_request(&group->req, group->latency);
}
-void sdhci_msm_pm_qos_cpu_unvote(struct sdhci_host *host, int cpu, bool async)
+bool sdhci_msm_pm_qos_cpu_unvote(struct sdhci_host *host, int cpu, bool async)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_msm_host *msm_host = pltfm_host->priv;
@@ -3299,16 +3299,17 @@ void sdhci_msm_pm_qos_cpu_unvote(struct sdhci_host *host, int cpu, bool async)
if (!msm_host->pm_qos_group_enable || group < 0 ||
atomic_dec_return(&msm_host->pm_qos[group].counter))
- return;
+ return false;
if (async) {
schedule_work(&msm_host->pm_qos[group].unvote_work);
- return;
+ return true;
}
msm_host->pm_qos[group].latency = PM_QOS_DEFAULT_VALUE;
pm_qos_update_request(&msm_host->pm_qos[group].req,
msm_host->pm_qos[group].latency);
+ return true;
}
void sdhci_msm_pm_qos_cpu_init(struct sdhci_host *host,
@@ -3346,9 +3347,65 @@ void sdhci_msm_pm_qos_cpu_init(struct sdhci_host *host,
group->latency,
&latency[i].latency[SDHCI_PERFORMANCE_MODE]);
}
+ msm_host->pm_qos_prev_cpu = -1;
msm_host->pm_qos_group_enable = true;
}
+static void sdhci_msm_pre_req(struct sdhci_host *host,
+ struct mmc_request *mmc_req)
+{
+ int cpu;
+ int group;
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ int prev_group = sdhci_msm_get_cpu_group(msm_host,
+ msm_host->pm_qos_prev_cpu);
+
+ sdhci_msm_pm_qos_irq_vote(host);
+
+ cpu = get_cpu();
+ put_cpu();
+ group = sdhci_msm_get_cpu_group(msm_host, cpu);
+ if (group < 0)
+ return;
+
+ if (group != prev_group && prev_group >= 0) {
+ sdhci_msm_pm_qos_cpu_unvote(host,
+ msm_host->pm_qos_prev_cpu, false);
+ prev_group = -1; /* make sure to vote for new group */
+ }
+
+ if (prev_group < 0) {
+ sdhci_msm_pm_qos_cpu_vote(host,
+ msm_host->pdata->pm_qos_data.latency, cpu);
+ msm_host->pm_qos_prev_cpu = cpu;
+ }
+}
+
+static void sdhci_msm_post_req(struct sdhci_host *host,
+ struct mmc_request *mmc_req)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+
+ sdhci_msm_pm_qos_irq_unvote(host, false);
+
+ if (sdhci_msm_pm_qos_cpu_unvote(host, msm_host->pm_qos_prev_cpu, false))
+ msm_host->pm_qos_prev_cpu = -1;
+}
+
+static void sdhci_msm_init(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+
+ sdhci_msm_pm_qos_irq_init(host);
+
+ if (msm_host->pdata->pm_qos_data.legacy_valid)
+ sdhci_msm_pm_qos_cpu_init(host,
+ msm_host->pdata->pm_qos_data.latency);
+}
+
static struct sdhci_ops sdhci_msm_ops = {
.crypto_engine_cfg = sdhci_msm_ice_cfg,
.crypto_cfg_reset = sdhci_msm_ice_cfg_reset,
@@ -3372,6 +3429,9 @@ static struct sdhci_ops sdhci_msm_ops = {
.detect = sdhci_msm_detect,
.notify_load = sdhci_msm_notify_load,
.reset_workaround = sdhci_msm_reset_workaround,
+ .init = sdhci_msm_init,
+ .pre_req = sdhci_msm_pre_req,
+ .post_req = sdhci_msm_post_req,
};
static void sdhci_set_default_hw_caps(struct sdhci_msm_host *msm_host,
diff --git a/drivers/mmc/host/sdhci-msm.h b/drivers/mmc/host/sdhci-msm.h
index 50db9363ef4e..01ad6d1593a2 100644
--- a/drivers/mmc/host/sdhci-msm.h
+++ b/drivers/mmc/host/sdhci-msm.h
@@ -203,7 +203,7 @@ struct sdhci_msm_host {
struct sdhci_msm_ice_data ice;
u32 ice_clk_rate;
struct sdhci_msm_pm_qos_group *pm_qos;
- int pm_qos_prev_group;
+ int pm_qos_prev_cpu;
bool pm_qos_group_enable;
struct sdhci_msm_pm_qos_irq pm_qos_irq;
};
@@ -218,7 +218,7 @@ void sdhci_msm_pm_qos_cpu_init(struct sdhci_host *host,
struct sdhci_msm_pm_qos_latency *latency);
void sdhci_msm_pm_qos_cpu_vote(struct sdhci_host *host,
struct sdhci_msm_pm_qos_latency *latency, int cpu);
-void sdhci_msm_pm_qos_cpu_unvote(struct sdhci_host *host, int cpu, bool async);
+bool sdhci_msm_pm_qos_cpu_unvote(struct sdhci_host *host, int cpu, bool async);
#endif /* __SDHCI_MSM_H__ */
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index aed0584379d1..5f139a5b9c87 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -2512,6 +2512,8 @@ static void sdhci_post_req(struct mmc_host *mmc, struct mmc_request *mrq,
DMA_TO_DEVICE : DMA_FROM_DEVICE);
data->host_cookie = COOKIE_UNMAPPED;
}
+ if (host->ops->post_req)
+ host->ops->post_req(host, mrq);
}
static int sdhci_pre_dma_transfer(struct sdhci_host *host,
@@ -2548,6 +2550,9 @@ static void sdhci_pre_req(struct mmc_host *mmc, struct mmc_request *mrq,
if (host->flags & SDHCI_REQ_USE_DMA)
sdhci_pre_dma_transfer(host, mrq->data);
+
+ if (host->ops->pre_req)
+ host->ops->pre_req(host, mrq);
}
static void sdhci_card_event(struct mmc_host *mmc)
@@ -2589,7 +2594,17 @@ static void sdhci_detect(struct mmc_host *mmc, bool detected)
host->ops->detect(host, detected);
}
+static int sdhci_late_init(struct mmc_host *mmc)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+
+ if (host->ops->init)
+ host->ops->init(host);
+
+ return 0;
+}
static const struct mmc_host_ops sdhci_ops = {
+ .init = sdhci_late_init,
.request = sdhci_request,
.post_req = sdhci_post_req,
.pre_req = sdhci_pre_req,
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 5468abe9698b..a4017e6f6c8c 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -680,6 +680,9 @@ struct sdhci_ops {
int card_drv, int *drv_type);
int (*notify_load)(struct sdhci_host *host, enum mmc_load state);
void (*reset_workaround)(struct sdhci_host *host, u32 enable);
+ void (*init)(struct sdhci_host *host);
+ void (*pre_req)(struct sdhci_host *host, struct mmc_request *req);
+ void (*post_req)(struct sdhci_host *host, struct mmc_request *req);
};
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 0759649084dd..367e122d1f2f 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -103,6 +103,7 @@ struct mmc_cmdq_host_ops {
};
struct mmc_host_ops {
+ int (*init)(struct mmc_host *host);
/*
* 'enable' is called when the host is claimed and 'disable' is called
* when the host is released. 'enable' and 'disable' are deprecated.