diff options
author | Sahitya Tummala <stummala@codeaurora.org> | 2013-05-23 15:59:22 +0530 |
---|---|---|
committer | Subhash Jadavani <subhashj@codeaurora.org> | 2016-05-27 10:28:40 -0700 |
commit | 165bd22b550bdf0353299045c562fd675bc4a8b7 (patch) | |
tree | 145b8e5ee4a4848ced16bfe1eccf66994ebfd84c | |
parent | 27212a9a828eacf9380fec5b8352676328af126d (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.c | 59 |
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); |