summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorPavan Kunapuli <pkunapuli@nvidia.com>2010-07-26 20:56:30 +0530
committerGary King <gking@nvidia.com>2010-08-10 17:53:23 -0700
commit2cbe7eb3ee06d2b6336f92f9fd5b8afe95b1b091 (patch)
treebe37e362e81c6a9cc9da775c8e7f7c691d71cfeb /drivers
parent40d1b1638e78e72b4d2628a391c943e26a35979e (diff)
tegra sdhci: Prevent SDIO from suspend/resume
To support wifi connectivity on system suspend resume for always powered on SDIO cards. Added card_always_on flag. This flag will prevent SDIO de-init/re-init in suspend/resume. If this flag is not set, then SDIO card is de-init/re-init on suspend/resume. Change-Id: Ib092fa9e0bc63ba781e0f4b6637dad0231303ba9 Reviewed-on: http://git-master.nvidia.com/r/4257 Tested-by: Pavan Kunapuli <pkunapuli@nvidia.com> Reviewed-by: Deepesh Subhash Gujarathi (Engrg-Mobile) <dgujarathi@nvidia.com> Tested-by: Deepesh Subhash Gujarathi (Engrg-Mobile) <dgujarathi@nvidia.com> Reviewed-by: Rahul Bansal <rbansal@nvidia.com> Tested-by: Rahul Bansal <rbansal@nvidia.com> Reviewed-by: Udaykumar Rameshchan Raval <uraval@nvidia.com> Tested-by: Victor (Weiguo) Pan <wpan@nvidia.com> Reviewed-by: Venkata (Muni) Anda <vanda@nvidia.com> Tested-by: Venkata (Muni) Anda <vanda@nvidia.com> Reviewed-by: Gary King <gking@nvidia.com>
Diffstat (limited to 'drivers')
-rwxr-xr-x[-rw-r--r--]drivers/mmc/host/sdhci-tegra.c100
1 files changed, 97 insertions, 3 deletions
diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c
index d49f457dda4c..fa85df14b962 100644..100755
--- a/drivers/mmc/host/sdhci-tegra.c
+++ b/drivers/mmc/host/sdhci-tegra.c
@@ -29,6 +29,7 @@
#include <linux/mmc/card.h>
#include <linux/clk.h>
#include <linux/gpio.h>
+#include <linux/delay.h>
#include <mach/sdhci.h>
#include <mach/pinmux.h>
@@ -53,6 +54,7 @@ struct tegra_sdhci {
unsigned long max_clk;
bool card_present;
bool clk_enable;
+ bool card_always_on;
};
static inline unsigned long res_size(struct resource *res)
@@ -203,6 +205,7 @@ int __init tegra_sdhci_probe(struct platform_device *pdev)
host->gpio_polarity_cd = plat->gpio_polarity_cd;
host->gpio_wp = plat->gpio_nr_wp;
host->gpio_polarity_wp = plat->gpio_polarity_wp;
+ host->card_always_on = plat->is_always_on;
dev_dbg(&pdev->dev, "write protect: %d card detect: %d\n",
host->gpio_wp, host->gpio_cd);
host->irq_cd = -1;
@@ -377,24 +380,100 @@ static int tegra_sdhci_remove(struct platform_device *pdev)
return 0;
}
+#define is_card_sdio(_card) \
+((_card) && ((_card)->type == MMC_TYPE_SDIO))
+
#if defined(CONFIG_PM)
#define dev_to_host(_dev) platform_get_drvdata(to_platform_device(_dev))
+static void tegra_sdhci_configure_interrupts(struct sdhci_host *sdhost, bool enable)
+{
+ u32 ierr;
+ u32 clear = SDHCI_INT_ALL_MASK;
+ u32 set;
+
+ if (enable) {
+ /* enable required MMC INTs */
+ set = SDHCI_INT_BUS_POWER | SDHCI_INT_DATA_END_BIT |
+ SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_INDEX |
+ SDHCI_INT_END_BIT | SDHCI_INT_CRC | SDHCI_INT_TIMEOUT |
+ SDHCI_INT_DATA_END | SDHCI_INT_RESPONSE;
+
+ ierr = sdhci_readl(sdhost, SDHCI_INT_ENABLE);
+ ierr &= clear;
+ ierr |= set;
+ sdhci_writel(sdhost, ierr, SDHCI_INT_ENABLE);
+ sdhci_writel(sdhost, ierr, SDHCI_SIGNAL_ENABLE);
+ } else {
+ /* disable the interrupts */
+ ierr = sdhci_readl(sdhost, SDHCI_INT_ENABLE);
+ /* Card interrupt masking is done by sdio client driver */
+ ierr &= SDHCI_INT_CARD_INT;
+ sdhci_writel(sdhost, ierr, SDHCI_INT_ENABLE);
+ sdhci_writel(sdhost, ierr, SDHCI_SIGNAL_ENABLE);
+ }
+}
+
+static int tegra_sdhci_restore(struct sdhci_host *sdhost)
+{
+ unsigned long timeout;
+ u8 mask = SDHCI_RESET_ALL;
+
+ sdhci_writeb(sdhost, mask, SDHCI_SOFTWARE_RESET);
+
+ sdhost->clock = 0;
+
+ /* Wait max 100 ms */
+ timeout = 100;
+
+ /* hw clears the bit when it's done */
+ while (sdhci_readb(sdhost, SDHCI_SOFTWARE_RESET) & mask) {
+ if (timeout == 0) {
+ printk(KERN_ERR "%s: Reset 0x%x never completed.\n",
+ mmc_hostname(sdhost->mmc), (int)mask);
+ return -EIO;
+ }
+ timeout--;
+ mdelay(1);
+ }
+
+ tegra_sdhci_configure_interrupts(sdhost, true);
+ sdhost->last_clk = 0;
+ return 0;
+}
+
static int tegra_sdhci_suspend(struct device *dev)
{
struct sdhci_host *sdhost = dev_to_host(dev);
struct tegra_sdhci *host = sdhci_priv(sdhost);
struct pm_message event = { PM_EVENT_SUSPEND };
+ int ret = 0;
- int ret = sdhci_suspend_host(sdhost, event);
+ if(host->card_always_on && is_card_sdio(sdhost->mmc->card)) {
+ struct mmc_ios ios;
+ ios.clock = 0;
+ ios.vdd = 0;
+ ios.power_mode = MMC_POWER_OFF;
+ ios.bus_width = MMC_BUS_WIDTH_1;
+ ios.timing = MMC_TIMING_LEGACY;
+ sdhost->mmc->ops->set_ios(sdhost->mmc, &ios);
+
+ /* Disable the interrupts */
+ tegra_sdhci_configure_interrupts(sdhost, false);
+
+ return ret;
+ }
+
+ ret = sdhci_suspend_host(sdhost, event);
if (ret) {
dev_err(dev, "failed to suspend host\n");
return ret;
}
+
if (host->hOdmSdio)
NvOdmSdioSuspend(host->hOdmSdio);
- return 0;
+ return ret;
}
static int tegra_sdhci_resume(struct device *dev)
@@ -407,7 +486,22 @@ static int tegra_sdhci_resume(struct device *dev)
host->clk_enable = true;
}
- if (host->hOdmSdio)
+ if(host->card_always_on && is_card_sdio(sdhost->mmc->card)) {
+ int ret = 0;
+
+ /* soft reset SD host controller and enable MMC INTs */
+ ret = tegra_sdhci_restore(sdhost);
+ if (ret) {
+ dev_err(dev, "failed to resume host\n");
+ return ret;
+ }
+
+ mmiowb();
+ sdhost->mmc->ops->set_ios(sdhost->mmc, &sdhost->mmc->ios);
+ return 0;
+ }
+
+ if(host->hOdmSdio)
NvOdmSdioResume(host->hOdmSdio);
return sdhci_resume_host(sdhost);