aboutsummaryrefslogtreecommitdiff
path: root/drivers/mmc
diff options
context:
space:
mode:
authorUlf Hansson <ulf.hansson@stericsson.com>2011-03-30 10:02:53 +0200
committerSebastian RASMUSSEN <sebastian.rasmussen@stericsson.com>2011-04-06 15:15:13 +0200
commit07e27a61d759c845b5eb99cc9677d8e489043a11 (patch)
tree6d55a5d6eef7af41328a263f9c027818607c4afa /drivers/mmc
parent9c1bb3b43a64c8eb3a72acdd3841c8c1f1a859f7 (diff)
MMCI: Re-design of power management
Cards are kept powered until we enter suspend mode. Since the cards are still powered after a "disable" has been done, the disable/enable function do only save/restore the PL180 register settings. Earlier the card needed to be re-initialized as well. This minimizes the access latency to the card to below 0 ms. Earlier the worst case seen with a certain SD card was around 1000 ms. In the suspend function the power to the card is cut to save current. This also means the cards must be re-initialized when doing resume. Unfortunately the resume time is then increased since the re-initialization of a SD card might as stated take up to 1000 ms in worst case. ST-Ericsson ID: 327273, 318306 Change-Id: Ifefc7a140dfa78509ee9e808863671ce812b4f20 Signed-off-by: Ulf Hansson <ulf.hansson@stericsson.com> Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/19662 Reviewed-by: Stefan NILSSON9 <stefan.xk.nilsson@stericsson.com> Reviewed-by: Joakim AXELSSON <joakim.axelsson@stericsson.com> Reviewed-by: Sebastian RASMUSSEN <sebastian.rasmussen@stericsson.com>
Diffstat (limited to 'drivers/mmc')
-rw-r--r--drivers/mmc/host/mmci.c115
-rw-r--r--drivers/mmc/host/mmci.h5
2 files changed, 58 insertions, 62 deletions
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 25dc5df1ca7..dd1c21c1ed2 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -1265,9 +1265,9 @@ static int mmci_get_cd(struct mmc_host *mmc)
#ifdef CONFIG_PM
static int mmci_enable(struct mmc_host *mmc)
{
+ unsigned long flags;
struct mmci_host *host = mmc_priv(mmc);
int ret = 0;
- unsigned int mask;
clk_enable(host->clk);
if (host->vcc) {
@@ -1287,11 +1287,7 @@ static int mmci_enable(struct mmc_host *mmc)
if (host->plat->wakeup_handler)
host->plat->wakeup_handler(host->mmc, true);
- /* Enable IRQ */
- mask = MCI_IRQENABLE;
- /* Don't use the datablockend flag if it's broken */
- if (host->variant->broken_blockend)
- mask &= ~MCI_DATABLOCKEND;
+ spin_lock_irqsave(&host->lock, flags);
if (host->variant->sdio &&
host->mmc->card &&
@@ -1304,9 +1300,12 @@ static int mmci_enable(struct mmc_host *mmc)
writel(MCI_ST_DPSM_SDIOEN, host->base + MMCIDATACTRL);
}
- writel(mask, host->base + MMCIMASK0);
+ /* Restore registers for POWER, CLOCK and IRQMASK0 */
+ writel(host->irqmask0_reg, host->base + MMCIMASK0);
+ writel(host->pwr_reg, host->base + MMCIPOWER);
+ writel(host->clk_reg, host->base + MMCICLOCK);
- mmc_power_restore_host(host->mmc);
+ spin_unlock_irqrestore(&host->lock, flags);
return ret;
}
@@ -1316,9 +1315,6 @@ static int mmci_disable(struct mmc_host *mmc, int lazy)
unsigned long flags;
struct mmci_host *host = mmc_priv(mmc);
- if (lazy && !mmc_try_claim_host(host->mmc))
- return host->plat->disable;
-
/*
* To be able to handle specific shutdown scenarios for each host,
* the following function shall be implemented.
@@ -1326,8 +1322,12 @@ static int mmci_disable(struct mmc_host *mmc, int lazy)
if (host->plat->wakeup_handler)
host->plat->wakeup_handler(host->mmc, false);
- /* Take actions specific for power save. */
- mmc_power_save_host(host->mmc);
+ spin_lock_irqsave(&host->lock, flags);
+
+ /* Save registers for POWER, CLOCK and IRQMASK0 */
+ host->irqmask0_reg = readl(host->base + MMCIMASK0);
+ host->pwr_reg = readl(host->base + MMCIPOWER);
+ host->clk_reg = readl(host->base + MMCICLOCK);
/*
* Make sure we do not get any interrupts when we disabled the
@@ -1335,20 +1335,15 @@ static int mmci_disable(struct mmc_host *mmc, int lazy)
* interrupts, since they may occur even without any active
* request handling ongoing.
*/
- spin_lock_irqsave(&host->lock, flags);
writel(0, host->base + MMCIMASK0);
+
spin_unlock_irqrestore(&host->lock, flags);
clk_disable(host->clk);
if (host->vcc) {
regulator_disable(host->vcc);
- dev_dbg(mmc_dev(host->mmc), "disabled regulator %s\n",
- host->plat->vcc);
}
- if (lazy)
- mmc_release_host(host->mmc);
-
return 0;
}
#else
@@ -1426,6 +1421,10 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id)
host->gpio_cd = -ENOSYS;
host->gpio_cd_irq = -1;
+ host->irqmask0_reg = 0;
+ host->pwr_reg = 0;
+ host->clk_reg = 0;
+
host->hw_designer = amba_manf(dev);
host->hw_revision = amba_rev(dev);
dev_dbg(mmc_dev(mmc), "designer ID = 0x%02x\n", host->hw_designer);
@@ -1438,10 +1437,6 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id)
goto host_free;
}
- ret = clk_enable(host->clk);
- if (ret)
- goto clk_free;
-
host->mclk = clk_get_rate(host->clk);
/*
* According to the spec, mclk is max 100 MHz,
@@ -1451,7 +1446,7 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id)
if (host->mclk > 100000000) {
ret = clk_set_rate(host->clk, 100000000);
if (ret < 0)
- goto clk_disable;
+ goto clk_free;
host->mclk = clk_get_rate(host->clk);
dev_dbg(mmc_dev(mmc), "eventual mclk rate: %u Hz\n",
host->mclk);
@@ -1460,7 +1455,7 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id)
host->base = ioremap(dev->res.start, resource_size(&dev->res));
if (!host->base) {
ret = -ENOMEM;
- goto clk_disable;
+ goto clk_free;
}
mmc->ops = &mmci_ops;
@@ -1485,14 +1480,9 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id)
ret = IS_ERR(host->vcc);
if (ret) {
host->vcc = NULL;
- goto clk_disable;
+ goto clk_free;
}
-
- ret = regulator_enable(host->vcc);
- if (ret)
- goto vcc_free;
-
- dev_dbg(mmc_dev(host->mmc), "enabled regulator %s\n",
+ dev_dbg(mmc_dev(host->mmc), "fetched regulator %s\n",
plat->vcc);
}
@@ -1503,7 +1493,7 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id)
ret = IS_ERR(host->vcard);
if (ret) {
host->vcard = NULL;
- goto vcc_disable;
+ goto vcc_free;
}
dev_dbg(mmc_dev(host->mmc), "fetched regulator %s\n",
plat->vcard);
@@ -1519,13 +1509,19 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id)
"Provided ocr_mask/setpower will not be used "
"(using regulator instead)\n");
}
- }
- /* Use platform data if no regulator for vcard is used */
- if (host->vcard == NULL)
+ } else {
+ /* Use platform data if no regulator for vcard is used */
mmc->ocr_avail = plat->ocr_mask;
+ }
+
+ /* Use platform capabilities */
mmc->caps = plat->capabilities;
+ /* Set disable timeout if supported */
+ if (mmc->caps & MMC_CAP_DISABLE)
+ mmc_set_disable_delay(mmc, plat->disable);
+
/* Use platform bus_resume_flags */
mmc->bus_resume_flags = plat->bus_resume_flags;
@@ -1562,10 +1558,6 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id)
spin_lock_init(&host->lock);
- writel(0, host->base + MMCIMASK0);
- writel(0, host->base + MMCIMASK1);
- writel(0xfff, host->base + MMCICLEAR);
-
if (gpio_is_valid(plat->gpio_cd)) {
ret = gpio_request(plat->gpio_cd, DRIVER_NAME " (cd)");
if (ret == 0)
@@ -1611,7 +1603,10 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id)
goto irq0_free;
}
- writel(MCI_IRQENABLE, host->base + MMCIMASK0);
+ /* Prepare IRQMASK0 */
+ host->irqmask0_reg = MCI_IRQENABLE;
+ if (host->variant->broken_blockend)
+ host->irqmask0_reg &= ~MCI_DATABLOCKEND;
amba_set_drvdata(dev, mmc);
@@ -1623,16 +1618,6 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id)
amba_config(dev), (unsigned long long)dev->res.start);
dev_info(&dev->dev, "IRQ %d, %d (pio)\n", dev->irq[0], dev->irq[1]);
- /* Disable clock and set disable delay if supported */
- if (mmc->caps & MMC_CAP_DISABLE) {
- mmc_set_disable_delay(mmc, plat->disable);
- clk_disable(host->clk);
- }
-
- /* Disable host regulator if supported */
- if (host->vcc)
- regulator_disable(host->vcc);
-
/* Ugly hack for u8500_sdio_detect_card, to be removed soon. */
sdio_host_ptr = host;
@@ -1651,14 +1636,9 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id)
gpio_free(host->gpio_cd);
err_gpio_cd:
iounmap(host->base);
- vcc_disable:
- if (host->vcc)
- regulator_disable(host->vcc);
vcc_free:
if (host->vcc)
regulator_put(host->vcc);
- clk_disable:
- clk_disable(host->clk);
clk_free:
clk_put(host->clk);
host_free:
@@ -1730,22 +1710,30 @@ static int mmci_suspend(struct amba_device *dev, pm_message_t state)
int ret = 0;
if (mmc) {
-
/*
* The host must be claimed to prevent request handling etc.
* Also make sure to not sleep, since we must return with a
* response quite quickly.
*/
if (mmc_try_claim_host(mmc)) {
- if (mmc->enabled) {
+ struct mmci_host *host = mmc_priv(mmc);
+ if (mmc->enabled) {
/*
* Do not suspend if the host has not been
* disabled.
*/
mmc_do_release_host(mmc);
ret = -EBUSY;
+ } else {
+ /* Cut the power to the card to save current. */
+ mmc_host_enable(mmc);
+ mmc_power_save_host(mmc);
+ mmc_host_disable(mmc);
+ host->pwr_reg = 0;
+ host->clk_reg = 0;
}
+
} else {
/*
* We did not manage to claim the host, thus someone
@@ -1763,12 +1751,15 @@ static int mmci_resume(struct amba_device *dev)
struct mmc_host *mmc = amba_get_drvdata(dev);
if (mmc) {
- /*
- * We expect the host to be claimed.
- * Release the host to provide access to it again.
- */
+ /* We expect the host to be claimed. */
WARN_ON(!mmc->claimed);
+ /* Restore power and re-initialize the card. */
+ mmc_host_enable(mmc);
+ mmc_power_restore_host(mmc);
+ mmc_host_disable(mmc);
+
+ /* Release the host to provide access to it again. */
mmc_do_release_host(mmc);
}
diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
index 73e9cff3333..bd842430696 100644
--- a/drivers/mmc/host/mmci.h
+++ b/drivers/mmc/host/mmci.h
@@ -191,6 +191,11 @@ struct mmci_host {
bool last_blockend;
bool dataend;
+ /* register cache */
+ unsigned int irqmask0_reg;
+ unsigned int pwr_reg;
+ unsigned int clk_reg;
+
/* pio stuff */
struct sg_mapping_iter sg_miter;
unsigned int size;