diff options
author | Ulf Hansson <ulf.hansson@stericsson.com> | 2011-03-30 10:02:53 +0200 |
---|---|---|
committer | Sebastian RASMUSSEN <sebastian.rasmussen@stericsson.com> | 2011-04-06 15:15:13 +0200 |
commit | 07e27a61d759c845b5eb99cc9677d8e489043a11 (patch) | |
tree | 6d55a5d6eef7af41328a263f9c027818607c4afa /drivers/mmc | |
parent | 9c1bb3b43a64c8eb3a72acdd3841c8c1f1a859f7 (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.c | 115 | ||||
-rw-r--r-- | drivers/mmc/host/mmci.h | 5 |
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; |