summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSahitya Tummala <stummala@codeaurora.org>2013-05-23 15:59:22 +0530
committerSubhash Jadavani <subhashj@codeaurora.org>2016-05-27 10:28:40 -0700
commit165bd22b550bdf0353299045c562fd675bc4a8b7 (patch)
tree145b8e5ee4a4848ced16bfe1eccf66994ebfd84c
parent27212a9a828eacf9380fec5b8352676328af126d (diff)
mmc: sdhci-msm: Vote for MSM bus clocks before enabling iface_clk
The current driver just enables "iface_clk" before accessing its registers but MSM bus clocks are also required for register access without which any register access would result in chip reset. The MSM bus clocks can be enabled by setting vote to MSM bus bandwidth driver. Currently, voting is being done in sdhci_enable/disable but these functions will not be invoked by MMC core layer for some cases such as mmc_power_up/mmc_power_off, which require peripheral register access. To resolve the above mentioned problem, bus voting and de-voting will now be done as part of clock management within the sdhci MSM driver i.e., before enabling SDHC clocks and after disabling SDHC clocks. Change-Id: Iff608fba4c58bf37a6f4ce8eb36876c79969feaf Signed-off-by: Sahitya Tummala <stummala@codeaurora.org>
-rw-r--r--drivers/mmc/host/sdhci-msm.c59
1 files changed, 42 insertions, 17 deletions
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index 11ecd98fcbb1..572f14676170 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -1389,10 +1389,20 @@ static void sdhci_msm_bus_voting(struct sdhci_host *host, u32 enable)
return;
bw = sdhci_get_bw_required(host, ios);
- if (enable)
+ if (enable) {
sdhci_msm_bus_cancel_work_and_set_vote(host, bw);
- else
- sdhci_msm_bus_queue_work(host);
+ } else {
+ /*
+ * If clock gating is enabled, then remove the vote
+ * immediately because clocks will be disabled only
+ * after SDHCI_MSM_MMC_CLK_GATE_DELAY and thus no
+ * additional delay is required to remove the bus vote.
+ */
+ if (host->mmc->clkgate_delay)
+ sdhci_msm_bus_cancel_work_and_set_vote(host, 0);
+ else
+ sdhci_msm_bus_queue_work(host);
+ }
}
/* Regulator utility functions */
@@ -1901,12 +1911,15 @@ static int sdhci_msm_prepare_clocks(struct sdhci_host *host, bool enable)
if (enable && !atomic_read(&msm_host->clks_on)) {
pr_debug("%s: request to enable clocks\n",
mmc_hostname(host->mmc));
+
+ sdhci_msm_bus_voting(host, 1);
+
if (!IS_ERR_OR_NULL(msm_host->bus_clk)) {
rc = clk_prepare_enable(msm_host->bus_clk);
if (rc) {
pr_err("%s: %s: failed to enable the bus-clock with error %d\n",
mmc_hostname(host->mmc), __func__, rc);
- goto out;
+ goto remove_vote;
}
}
if (!IS_ERR(msm_host->pclk)) {
@@ -1935,6 +1948,8 @@ static int sdhci_msm_prepare_clocks(struct sdhci_host *host, bool enable)
clk_disable_unprepare(msm_host->pclk);
if (!IS_ERR_OR_NULL(msm_host->bus_clk))
clk_disable_unprepare(msm_host->bus_clk);
+
+ sdhci_msm_bus_voting(host, 0);
}
atomic_set(&msm_host->clks_on, enable);
goto out;
@@ -1944,6 +1959,9 @@ disable_pclk:
disable_bus_clk:
if (!IS_ERR_OR_NULL(msm_host->bus_clk))
clk_disable_unprepare(msm_host->bus_clk);
+remove_vote:
+ if (msm_host->msm_bus_vote.client_handle)
+ sdhci_msm_bus_cancel_work_and_set_vote(host, 0);
out:
return rc;
}
@@ -1990,6 +2008,11 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock)
}
msm_host->clk_rate = sup_clock;
host->clock = clock;
+ /*
+ * Update the bus vote in case of frequency change due to
+ * clock scaling.
+ */
+ sdhci_msm_bus_voting(host, 1);
}
out:
sdhci_set_clock(host, clock);
@@ -2037,7 +2060,6 @@ static struct sdhci_ops sdhci_msm_ops = {
.toggle_cdr = sdhci_msm_toggle_cdr,
.get_max_segments = sdhci_msm_max_segs,
.set_clock = sdhci_msm_set_clock,
- .platform_bus_voting = sdhci_msm_bus_voting,
.get_min_clock = sdhci_msm_get_min_clock,
.get_max_clock = sdhci_msm_get_max_clock,
};
@@ -2139,11 +2161,20 @@ static int sdhci_msm_probe(struct platform_device *pdev)
msm_host->clk_rate = sdhci_msm_get_min_clock(host);
atomic_set(&msm_host->clks_on, 1);
+ ret = sdhci_msm_bus_register(msm_host, pdev);
+ if (ret)
+ goto clk_disable;
+
+ if (msm_host->msm_bus_vote.client_handle)
+ INIT_DELAYED_WORK(&msm_host->msm_bus_vote.vote_work,
+ sdhci_msm_bus_work);
+ sdhci_msm_bus_voting(host, 1);
+
/* Setup regulators */
ret = sdhci_msm_vreg_init(&pdev->dev, msm_host->pdata, true);
if (ret) {
dev_err(&pdev->dev, "Regulator setup failed (%d)\n", ret);
- goto clk_disable;
+ goto bus_unregister;
}
/* Reset the core and Enable SDHC mode */
@@ -2271,14 +2302,6 @@ static int sdhci_msm_probe(struct platform_device *pdev)
host->cpu_dma_latency_us = msm_host->pdata->cpu_dma_latency_us;
- ret = sdhci_msm_bus_register(msm_host, pdev);
- if (ret)
- goto vreg_deinit;
-
- if (msm_host->msm_bus_vote.client_handle)
- INIT_DELAYED_WORK(&msm_host->msm_bus_vote.vote_work,
- sdhci_msm_bus_work);
-
init_completion(&msm_host->pwr_irq_completion);
if (gpio_is_valid(msm_host->pdata->status_gpio)) {
@@ -2287,7 +2310,7 @@ static int sdhci_msm_probe(struct platform_device *pdev)
if (ret) {
dev_err(&pdev->dev, "%s: Failed to request card detection IRQ %d\n",
__func__, ret);
- goto bus_unregister;
+ goto vreg_deinit;
}
}
@@ -2323,10 +2346,12 @@ remove_host:
free_cd_gpio:
if (gpio_is_valid(msm_host->pdata->status_gpio))
mmc_gpio_free_cd(msm_host->mmc);
-bus_unregister:
- sdhci_msm_bus_unregister(msm_host);
vreg_deinit:
sdhci_msm_vreg_init(&pdev->dev, msm_host->pdata, false);
+bus_unregister:
+ if (msm_host->msm_bus_vote.client_handle)
+ sdhci_msm_bus_cancel_work_and_set_vote(host, 0);
+ sdhci_msm_bus_unregister(msm_host);
clk_disable:
if (!IS_ERR(msm_host->clk))
clk_disable_unprepare(msm_host->clk);