mmc: sdhci: Add host driver support to enable clock gating

Enable config MMC_CLKGATE to enable aggressive clock gating framework that
will disable clocks when the host is not in use for 200ms.

Change-Id: I6bef5dc18b561871689b3d730fd3486323b12520
Signed-off-by: Sahitya Tummala <stummala@codeaurora.org>
[venkatg@codeaurora.org: sdhci_set_clock is now a library function thats
called from platform clock handler, make changes to address that]
Signed-off-by: Venkat Gopalakrishnan <venkatg@codeaurora.org>
[subhashj@codeaurora.org: fixed minor merge conflict]
Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org>
This commit is contained in:
Sahitya Tummala 2013-01-11 11:30:45 +05:30 committed by Subhash Jadavani
parent a507b5a6c4
commit 15685a80db
2 changed files with 90 additions and 18 deletions

View file

@ -78,6 +78,7 @@
/* 8KB descriptors */
#define SDHCI_MSM_MAX_SEGMENTS (1 << 13)
#define SDHCI_MSM_MMC_CLK_GATE_DELAY 200 /* msecs */
static const u32 tuning_block_64[] = {
0x00FF0FFF, 0xCCC3CCFF, 0xFFCC3CC3, 0xEFFEFFFE,
@ -198,6 +199,7 @@ struct sdhci_msm_host {
struct clk *clk; /* main SD/MMC bus clock */
struct clk *pclk; /* SDHC peripheral bus clock */
struct clk *bus_clk; /* SDHC bus voter clock */
atomic_t clks_on; /* Set if clocks are enabled */
struct sdhci_msm_pltfm_data *pdata;
struct mmc_host *mmc;
struct sdhci_pltfm_data sdhci_msm_pdata;
@ -1506,11 +1508,73 @@ static unsigned int sdhci_msm_max_segs(void)
return SDHCI_MSM_MAX_SEGMENTS;
}
void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock)
{
int rc;
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_msm_host *msm_host = pltfm_host->priv;
unsigned long flags;
if (clock && !atomic_read(&msm_host->clks_on)) {
pr_debug("%s: request to enable clock at rate %u\n",
mmc_hostname(host->mmc), clock);
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;
}
}
if (!IS_ERR(msm_host->pclk)) {
rc = clk_prepare_enable(msm_host->pclk);
if (rc) {
pr_err("%s: %s: failed to enable the pclk with error %d\n",
mmc_hostname(host->mmc), __func__, rc);
goto disable_bus_clk;
}
}
rc = clk_prepare_enable(msm_host->clk);
if (rc) {
pr_err("%s: %s: failed to enable the host-clk with error %d\n",
mmc_hostname(host->mmc), __func__, rc);
goto disable_pclk;
}
mb();
atomic_set(&msm_host->clks_on, 1);
} else if (!clock && atomic_read(&msm_host->clks_on)) {
pr_debug("%s: request to disable clocks\n",
mmc_hostname(host->mmc));
sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
mb();
clk_disable_unprepare(msm_host->clk);
if (!IS_ERR(msm_host->pclk))
clk_disable_unprepare(msm_host->pclk);
if (!IS_ERR_OR_NULL(msm_host->bus_clk))
clk_disable_unprepare(msm_host->bus_clk);
atomic_set(&msm_host->clks_on, 0);
}
spin_lock_irqsave(&host->lock, flags);
host->clock = clock;
spin_unlock_irqrestore(&host->lock, flags);
goto out;
disable_pclk:
if (!IS_ERR_OR_NULL(msm_host->pclk))
clk_disable_unprepare(msm_host->pclk);
disable_bus_clk:
if (!IS_ERR_OR_NULL(msm_host->bus_clk))
clk_disable_unprepare(msm_host->bus_clk);
out:
return;
}
static struct sdhci_ops sdhci_msm_ops = {
.check_power_status = sdhci_msm_check_power_status,
.platform_execute_tuning = sdhci_msm_execute_tuning,
.toggle_cdr = sdhci_msm_toggle_cdr,
.get_max_segments = sdhci_msm_max_segs,
.set_clock = sdhci_msm_set_clock,
};
static int sdhci_msm_probe(struct platform_device *pdev)
@ -1587,6 +1651,7 @@ static int sdhci_msm_probe(struct platform_device *pdev)
if (ret)
goto pclk_disable;
atomic_set(&msm_host->clks_on, 1);
/* Setup regulators */
ret = sdhci_msm_vreg_init(&pdev->dev, msm_host->pdata, true);
if (ret) {
@ -1657,6 +1722,9 @@ static int sdhci_msm_probe(struct platform_device *pdev)
/* Enable pwr irq interrupts */
writel_relaxed(INT_MASK, (msm_host->core_mem + CORE_PWRCTL_MASK));
/* Set clock gating delay to be used when CONFIG_MMC_CLKGATE is set */
msm_host->mmc->clkgate_delay = SDHCI_MSM_MMC_CLK_GATE_DELAY;
/* Set host capabilities */
msm_host->mmc->caps |= msm_host->pdata->mmc_bus_width;
msm_host->mmc->caps |= msm_host->pdata->caps;
@ -1725,12 +1793,7 @@ static int sdhci_msm_remove(struct platform_device *pdev)
sdhci_remove_host(host, dead);
sdhci_pltfm_free(pdev);
sdhci_msm_vreg_init(&pdev->dev, msm_host->pdata, false);
if (!IS_ERR(msm_host->clk))
clk_disable_unprepare(msm_host->clk);
if (!IS_ERR(msm_host->pclk))
clk_disable_unprepare(msm_host->pclk);
if (!IS_ERR_OR_NULL(msm_host->bus_clk))
clk_disable_unprepare(msm_host->bus_clk);
if (pdata->pin_data)
sdhci_msm_setup_pins(pdata, false);
return 0;

View file

@ -1168,7 +1168,8 @@ void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
host->mmc->actual_clock = 0;
sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
if (host->clock)
sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
if (host->quirks2 & SDHCI_QUIRK2_NEED_DELAY_AFTER_INT_CLK_RST)
mdelay(1);
@ -1472,22 +1473,16 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
return;
}
/*
* Reset the chip on each power off.
* Should clear out any weird states.
*/
if (ios->power_mode == MMC_POWER_OFF) {
sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE);
sdhci_reinit(host);
}
if (host->version >= SDHCI_SPEC_300 &&
(ios->power_mode == MMC_POWER_UP) &&
!(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN))
sdhci_enable_preset_value(host, false);
spin_lock_irqsave(&host->lock, flags);
if (!ios->clock || ios->clock != host->clock) {
spin_unlock_irqrestore(&host->lock, flags);
host->ops->set_clock(host, ios->clock);
spin_lock_irqsave(&host->lock, flags);
host->clock = ios->clock;
if (host->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK &&
@ -1502,8 +1497,10 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
host->mmc->max_busy_timeout /= host->timeout_clk;
}
}
spin_unlock_irqrestore(&host->lock, flags);
sdhci_set_power(host, ios->power_mode, ios->vdd);
if (ios->power_mode & (MMC_POWER_UP | MMC_POWER_ON))
sdhci_set_power(host, ios->power_mode, ios->vdd);
if (host->ops->platform_send_init_74_clocks)
host->ops->platform_send_init_74_clocks(host, ios->power_mode);
@ -1602,6 +1599,7 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
} else
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
spin_unlock_irqrestore(&host->lock, flags);
/*
* Some (ENE) controllers go apeshit on some ios operation,
* signalling timeout and CRC errors even on CMD0. Resetting
@ -1610,8 +1608,19 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
if (host->quirks & SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS)
sdhci_do_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
/*
* Reset the chip on each power off.
* Should clear out any weird states.
*/
if (ios->power_mode == MMC_POWER_OFF) {
sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE);
sdhci_reinit(host);
sdhci_set_power(host, ios->power_mode, ios->vdd);
}
if (!ios->clock)
sdhci_set_clock(host, ios->clock);
mmiowb();
spin_unlock_irqrestore(&host->lock, flags);
}
static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)