On 09/03/2013 11:29 AM, Ulf Hansson wrote: > 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 siutuation. > > 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@xxxxxxxxxx> > Acked-by: Rickard Andersson <rickard.andersson@xxxxxxxxxxxxxx> > Cc: Daniel Lezcano <daniel.lezcano@xxxxxxxxxx> > --- Reviewed-by: Daniel Lezcano <daniel.lezcano@xxxxxxxxxx> > drivers/mmc/host/mmci.c | 43 +++++++++++++++++++++++++++++++++++++++++++ > 1 file changed, 43 insertions(+) > > diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c > index 82afcd3..97e541b 100644 > --- a/drivers/mmc/host/mmci.c > +++ b/drivers/mmc/host/mmci.c > @@ -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; > @@ -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 = { > @@ -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 = { > @@ -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 = { > @@ -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 = { > @@ -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) > @@ -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); > @@ -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); > } > > @@ -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); > } > > -- <http://www.linaro.org/> Linaro.org │ Open source software for ARM SoCs Follow Linaro: <http://www.facebook.com/pages/Linaro> Facebook | <http://twitter.com/#!/linaroorg> Twitter | <http://www.linaro.org/linaro-blog/> Blog -- To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html