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:
parent
a507b5a6c4
commit
15685a80db
2 changed files with 90 additions and 18 deletions
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Add table
Reference in a new issue