Skip to content

Commit

Permalink
ARM: 7834/1: mmc: mmci: Save and restore register context
Browse files Browse the repository at this point in the history
If a corresponding power domain exists for the device and it manages
to cut the domain regulator while the device is runtime suspended,
the IP loses it's registers context. We restore the context in the
.runtime_resume callback from the existing register caches to adapt
to this situation.

We also want to make sure the registers are in a known state while
restoring context in the case when the power domain did not drop the
power, since there are restrictions for the order of writing to these
registers. To handle this, we clear the registers in the
.runtime_suspend callback.

Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
Acked-by: Rickard Andersson <rickard.andersson@stericsson.com>
Reviewed-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
  • Loading branch information
Ulf Hansson authored and Russell King committed Sep 19, 2013
1 parent f829c04 commit 1ff4443
Showing 1 changed file with 43 additions and 0 deletions.
43 changes: 43 additions & 0 deletions drivers/mmc/host/mmci.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ static unsigned int fmax = 515633;
* @signal_direction: input/out direction of bus signals can be indicated
* @pwrreg_clkgate: MMCIPOWER register must be used to gate the clock
* @busy_detect: true if busy detection on dat0 is supported
* @pwrreg_nopower: bits in MMCIPOWER don't controls ext. power supply
*/
struct variant_data {
unsigned int clkreg;
Expand All @@ -76,6 +77,7 @@ struct variant_data {
bool signal_direction;
bool pwrreg_clkgate;
bool busy_detect;
bool pwrreg_nopower;
};

static struct variant_data variant_arm = {
Expand Down Expand Up @@ -109,6 +111,7 @@ static struct variant_data variant_u300 = {
.pwrreg_powerup = MCI_PWR_ON,
.signal_direction = true,
.pwrreg_clkgate = true,
.pwrreg_nopower = true,
};

static struct variant_data variant_nomadik = {
Expand All @@ -121,6 +124,7 @@ static struct variant_data variant_nomadik = {
.pwrreg_powerup = MCI_PWR_ON,
.signal_direction = true,
.pwrreg_clkgate = true,
.pwrreg_nopower = true,
};

static struct variant_data variant_ux500 = {
Expand All @@ -135,6 +139,7 @@ static struct variant_data variant_ux500 = {
.signal_direction = true,
.pwrreg_clkgate = true,
.busy_detect = true,
.pwrreg_nopower = true,
};

static struct variant_data variant_ux500v2 = {
Expand All @@ -150,6 +155,7 @@ static struct variant_data variant_ux500v2 = {
.signal_direction = true,
.pwrreg_clkgate = true,
.busy_detect = true,
.pwrreg_nopower = true,
};

static int mmci_card_busy(struct mmc_host *mmc)
Expand Down Expand Up @@ -1759,6 +1765,41 @@ static int mmci_resume(struct device *dev)
#endif

#ifdef CONFIG_PM_RUNTIME
static void mmci_save(struct mmci_host *host)
{
unsigned long flags;

if (host->variant->pwrreg_nopower) {
spin_lock_irqsave(&host->lock, flags);

writel(0, host->base + MMCIMASK0);
writel(0, host->base + MMCIDATACTRL);
writel(0, host->base + MMCIPOWER);
writel(0, host->base + MMCICLOCK);
mmci_reg_delay(host);

spin_unlock_irqrestore(&host->lock, flags);
}

}

static void mmci_restore(struct mmci_host *host)
{
unsigned long flags;

if (host->variant->pwrreg_nopower) {
spin_lock_irqsave(&host->lock, flags);

writel(host->clk_reg, host->base + MMCICLOCK);
writel(host->datactrl_reg, host->base + MMCIDATACTRL);
writel(host->pwr_reg, host->base + MMCIPOWER);
writel(MCI_IRQENABLE, host->base + MMCIMASK0);
mmci_reg_delay(host);

spin_unlock_irqrestore(&host->lock, flags);
}
}

static int mmci_runtime_suspend(struct device *dev)
{
struct amba_device *adev = to_amba_device(dev);
Expand All @@ -1767,6 +1808,7 @@ static int mmci_runtime_suspend(struct device *dev)
if (mmc) {
struct mmci_host *host = mmc_priv(mmc);
pinctrl_pm_select_sleep_state(dev);
mmci_save(host);
clk_disable_unprepare(host->clk);
}

Expand All @@ -1781,6 +1823,7 @@ static int mmci_runtime_resume(struct device *dev)
if (mmc) {
struct mmci_host *host = mmc_priv(mmc);
clk_prepare_enable(host->clk);
mmci_restore(host);
pinctrl_pm_select_default_state(dev);
}

Expand Down

0 comments on commit 1ff4443

Please sign in to comment.