diff options
author | Rahul Bansal <rbansal@nvidia.com> | 2010-08-20 18:22:12 +0530 |
---|---|---|
committer | Bharat Nihalani <bnihalani@nvidia.com> | 2010-08-31 05:31:16 -0700 |
commit | 30d90c8af0bb790e4ce2cf256dcb4ff87ad9e3a8 (patch) | |
tree | 54ebe7051949ba59860023bd33c980781e6d891e | |
parent | 9b19c94aafed014ce68ee16df37897c437dd8c68 (diff) |
tegra sdhci: Restore SDHCI interrupts on resume
On resume restore SDHCI interrupts to the state which
was before entering into suspend for SDIO (always_pwr_on)
slot. Also, in suspend keep CARD_INT enabled if it was
before going to suspend, so that it can be used as wake
source.
Broadcom wifi driver does not disable/enable SDIO_INT
on early_suspend/late_resume, it requires SDIO INTs to
be enabled on resume, as even before broadcom driver's
late_resume is called which puts wifi is high_pwr, it
needs to communicate with MAC for incoming IOCTLs from
wpa_supplicant.
Bug: 723708
Change-Id: Id1bfe67f415080eeb7563428322dbec3df0f27d2
Reviewed-on: http://git-master/r/5407
Reviewed-by: Rahul Bansal <rbansal@nvidia.com>
Tested-by: Rahul Bansal <rbansal@nvidia.com>
Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com>
-rwxr-xr-x | drivers/mmc/host/sdhci-tegra.c | 63 |
1 files changed, 35 insertions, 28 deletions
diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index fa85df14b962..6061d713e7ca 100755 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c @@ -55,6 +55,7 @@ struct tegra_sdhci { bool card_present; bool clk_enable; bool card_always_on; + u32 sdhci_ints; }; static inline unsigned long res_size(struct resource *res) @@ -386,31 +387,24 @@ static int tegra_sdhci_remove(struct platform_device *pdev) #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) +static void tegra_sdhci_restore_interrupts(struct sdhci_host *sdhost) { 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); + struct tegra_sdhci *host = sdhci_priv(sdhost); + + /* enable required interrupts */ + ierr = sdhci_readl(sdhost, SDHCI_INT_ENABLE); + ierr &= ~clear; + ierr |= host->sdhci_ints; + sdhci_writel(sdhost, ierr, SDHCI_INT_ENABLE); + sdhci_writel(sdhost, ierr, SDHCI_SIGNAL_ENABLE); + + if ( (host->sdhci_ints & SDHCI_INT_CARD_INT) && + (sdhost->quirks & SDHCI_QUIRK_ENABLE_INTERRUPT_AT_BLOCK_GAP)) { + u8 gap_ctrl = sdhci_readb(sdhost, SDHCI_BLOCK_GAP_CONTROL); + gap_ctrl |= 0x8; + sdhci_writeb(sdhost, gap_ctrl, SDHCI_BLOCK_GAP_CONTROL); } } @@ -437,7 +431,7 @@ static int tegra_sdhci_restore(struct sdhci_host *sdhost) mdelay(1); } - tegra_sdhci_configure_interrupts(sdhost, true); + tegra_sdhci_restore_interrupts(sdhost); sdhost->last_clk = 0; return 0; } @@ -449,17 +443,30 @@ static int tegra_sdhci_suspend(struct device *dev) struct pm_message event = { PM_EVENT_SUSPEND }; int ret = 0; - if(host->card_always_on && is_card_sdio(sdhost->mmc->card)) { + 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); + /* save interrupt status before suspending */ + host->sdhci_ints = sdhci_readl(sdhost, SDHCI_INT_ENABLE); + sdhost->mmc->ops->set_ios(sdhost->mmc, &ios); + /* keep CARD_INT enabled - if used as wakeup source */ + if (host->sdhci_ints & SDHCI_INT_CARD_INT) { + u32 ier = sdhci_readl(host, SDHCI_INT_ENABLE); + ier |= SDHCI_INT_CARD_INT; + sdhci_writel(host, ier, SDHCI_INT_ENABLE); + sdhci_writel(host, ier, SDHCI_SIGNAL_ENABLE); + + if (sdhost->quirks & SDHCI_QUIRK_ENABLE_INTERRUPT_AT_BLOCK_GAP) { + u8 gap_ctrl = sdhci_readb(sdhost, SDHCI_BLOCK_GAP_CONTROL); + gap_ctrl |= 0x8; + sdhci_writeb(sdhost, gap_ctrl, SDHCI_BLOCK_GAP_CONTROL); + } + } return ret; } @@ -489,7 +496,7 @@ static int tegra_sdhci_resume(struct device *dev) if(host->card_always_on && is_card_sdio(sdhost->mmc->card)) { int ret = 0; - /* soft reset SD host controller and enable MMC INTs */ + /* soft reset SD host controller and enable interrupts */ ret = tegra_sdhci_restore(sdhost); if (ret) { dev_err(dev, "failed to resume host\n"); |