diff mbox

[V2,13/32] omap_hsmmc: context save/restore support

Message ID 20090728104011.2371.89249.sendpatchset@ahunter-laptop (mailing list archive)
State Awaiting Upstream, archived
Headers show

Commit Message

Adrian Hunter July 28, 2009, 10:40 a.m. UTC
From 43e9fa346d7e386328876a8535dc8619bd1f47ae Mon Sep 17 00:00:00 2001
From: Denis Karpov <ext-denis.2.karpov@nokia.com>
Date: Wed, 22 Apr 2009 16:04:25 +0200
Subject: [PATCH] omap_hsmmc: context save/restore support

Keep the context over PM dynamic OFF states.

Signed-off-by: Denis Karpov <ext-denis.2.karpov@nokia.com>
Signed-off-by: Adrian Hunter <adrian.hunter@nokia.com>
---
 drivers/mmc/host/omap_hsmmc.c |  194 ++++++++++++++++++++++++++++++++++++++--
 1 files changed, 184 insertions(+), 10 deletions(-)

Comments

Madhusudhan July 30, 2009, 2 a.m. UTC | #1
> -----Original Message-----
> From: linux-omap-owner@vger.kernel.org [mailto:linux-omap-
> owner@vger.kernel.org] On Behalf Of Adrian Hunter
> Sent: Tuesday, July 28, 2009 5:40 AM
> To: Andrew Morton
> Cc: Jarkko Lavinen; Adrian Hunter; linux-omap Mailing List; Pierre Ossman;
> Denis Karpov; Matt Fleming; lkml
> Subject: [PATCH V2 13/32] omap_hsmmc: context save/restore support
> 
> From 43e9fa346d7e386328876a8535dc8619bd1f47ae Mon Sep 17 00:00:00 2001
> From: Denis Karpov <ext-denis.2.karpov@nokia.com>
> Date: Wed, 22 Apr 2009 16:04:25 +0200
> Subject: [PATCH] omap_hsmmc: context save/restore support
> 
> Keep the context over PM dynamic OFF states.
> 
> Signed-off-by: Denis Karpov <ext-denis.2.karpov@nokia.com>
> Signed-off-by: Adrian Hunter <adrian.hunter@nokia.com>
> ---
>  drivers/mmc/host/omap_hsmmc.c |  194
> ++++++++++++++++++++++++++++++++++++++--
>  1 files changed, 184 insertions(+), 10 deletions(-)
> 
> diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
> index c28d055..ac1a3bf 100644
> --- a/drivers/mmc/host/omap_hsmmc.c
> +++ b/drivers/mmc/host/omap_hsmmc.c
> @@ -37,6 +37,7 @@
> 
>  /* OMAP HSMMC Host Controller Registers */
>  #define OMAP_HSMMC_SYSCONFIG	0x0010
> +#define OMAP_HSMMC_SYSSTATUS	0x0014
>  #define OMAP_HSMMC_CON		0x002C
>  #define OMAP_HSMMC_BLK		0x0104
>  #define OMAP_HSMMC_ARG		0x0108
> @@ -94,6 +95,8 @@
>  #define DUAL_VOLT_OCR_BIT	7
>  #define SRC			(1 << 25)
>  #define SRD			(1 << 26)
> +#define SOFTRESET		(1 << 1)
> +#define RESETDONE		(1 << 0)
> 
>  /*
>   * FIXME: Most likely all the data using these _DEVID defines should come
> @@ -152,6 +155,8 @@ struct mmc_omap_host {
>  	int			slot_id;
>  	int			dbclk_enabled;
>  	int			response_busy;
> +	int			context_loss;
> +
>  	struct	omap_mmc_platform_data	*pdata;
>  };
> 
> @@ -166,6 +171,166 @@ static void omap_mmc_stop_clock(struct mmc_omap_host
> *host)
>  		dev_dbg(mmc_dev(host->mmc), "MMC Clock is not stoped\n");
>  }
> 
> +#ifdef CONFIG_PM
> +
> +/*
> + * Restore the MMC host context, if it was lost as result of a
> + * power state change.
> + */
> +static int omap_mmc_restore_ctx(struct mmc_omap_host *host)
> +{
> +	struct mmc_ios *ios = &host->mmc->ios;
> +	struct omap_mmc_platform_data *pdata = host->pdata;
> +	int context_loss = 0;
> +	u32 hctl, capa, con;
> +	u16 dsor = 0;
> +	unsigned long timeout;
> +
> +	if (pdata->get_context_loss_count) {
> +		context_loss = pdata->get_context_loss_count(host->dev);
> +		if (context_loss < 0)
> +			return 1;
> +	}
This seems to restore the context always. The context should be restored
only if the card was powered OFF, Right?

The context could also be lost if the CORE transitions to OFF. I assume that
case gets handled here without anything extra required if "power_saving" is
set to true. Am I right?

How about the case of eMMC? Since it is not a removable device the card is
not powered OFF. But the CORE OFF would result in context loss. Do we hit
the restore_ctx path in that case?

> +
> +	dev_dbg(mmc_dev(host->mmc), "context was %slost\n",
> +		context_loss == host->context_loss ? "not " : "");
> +	if (host->context_loss == context_loss)
> +		return 1;
> +
> +	/* Wait for hardware reset */
> +	timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS);
> +	while ((OMAP_HSMMC_READ(host->base, SYSSTATUS) & RESETDONE) !=
> RESETDONE
> +		&& time_before(jiffies, timeout))
> +		;
> +
> +	/* Do software reset */
> +	OMAP_HSMMC_WRITE(host->base, SYSCONFIG, SOFTRESET);
> +	timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS);
> +	while ((OMAP_HSMMC_READ(host->base, SYSSTATUS) & RESETDONE) !=
> RESETDONE
> +		&& time_before(jiffies, timeout))
> +		;
> +
> +	OMAP_HSMMC_WRITE(host->base, SYSCONFIG,
> +			OMAP_HSMMC_READ(host->base, SYSCONFIG) | AUTOIDLE);
> +
> +	if (host->id == OMAP_MMC1_DEVID) {
> +		if (host->power_mode != MMC_POWER_OFF &&
> +		    (1 << ios->vdd) <= MMC_VDD_23_24)
> +			hctl = SDVS18;
> +		else
> +			hctl = SDVS30;
> +		capa = VS30 | VS18;
> +	} else {
> +		hctl = SDVS18;
> +		capa = VS18;
> +	}
> +
> +	OMAP_HSMMC_WRITE(host->base, HCTL,
> +			OMAP_HSMMC_READ(host->base, HCTL) | hctl);
> +
> +	OMAP_HSMMC_WRITE(host->base, CAPA,
> +			OMAP_HSMMC_READ(host->base, CAPA) | capa);
> +
> +	OMAP_HSMMC_WRITE(host->base, HCTL,
> +			OMAP_HSMMC_READ(host->base, HCTL) | SDBP);
> +
> +	timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS);
> +	while ((OMAP_HSMMC_READ(host->base, HCTL) & SDBP) != SDBP
> +		&& time_before(jiffies, timeout))
> +		;
> +
> +	OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
> +	OMAP_HSMMC_WRITE(host->base, ISE, INT_EN_MASK);
> +	OMAP_HSMMC_WRITE(host->base, IE, INT_EN_MASK);
> +
> +	/* Do not initialize card-specific things if the power is off */
> +	if (host->power_mode == MMC_POWER_OFF)
> +		goto out;
> +
> +	con = OMAP_HSMMC_READ(host->base, CON);
> +	switch (ios->bus_width) {
> +	case MMC_BUS_WIDTH_8:
> +		OMAP_HSMMC_WRITE(host->base, CON, con | DW8);
> +		break;
> +	case MMC_BUS_WIDTH_4:
> +		OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8);
> +		OMAP_HSMMC_WRITE(host->base, HCTL,
> +			OMAP_HSMMC_READ(host->base, HCTL) | FOUR_BIT);
> +		break;
> +	case MMC_BUS_WIDTH_1:
> +		OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8);
> +		OMAP_HSMMC_WRITE(host->base, HCTL,
> +			OMAP_HSMMC_READ(host->base, HCTL) & ~FOUR_BIT);
> +		break;
> +	}
> +
> +	if (ios->clock) {
> +		dsor = OMAP_MMC_MASTER_CLOCK / ios->clock;
> +		if (dsor < 1)
> +			dsor = 1;
> +
> +		if (OMAP_MMC_MASTER_CLOCK / dsor > ios->clock)
> +			dsor++;
> +
> +		if (dsor > 250)
> +			dsor = 250;
> +	}
> +
> +	OMAP_HSMMC_WRITE(host->base, SYSCTL,
> +		OMAP_HSMMC_READ(host->base, SYSCTL) & ~CEN);
> +	OMAP_HSMMC_WRITE(host->base, SYSCTL, (dsor << 6) | (DTO << 16));
> +	OMAP_HSMMC_WRITE(host->base, SYSCTL,
> +		OMAP_HSMMC_READ(host->base, SYSCTL) | ICE);
> +
> +	timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS);
> +	while ((OMAP_HSMMC_READ(host->base, SYSCTL) & ICS) != ICS
> +		&& time_before(jiffies, timeout))
> +		;
> +
> +	OMAP_HSMMC_WRITE(host->base, SYSCTL,
> +		OMAP_HSMMC_READ(host->base, SYSCTL) | CEN);
> +
> +	con = OMAP_HSMMC_READ(host->base, CON);
> +	if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)
> +		OMAP_HSMMC_WRITE(host->base, CON, con | OD);
> +	else
> +		OMAP_HSMMC_WRITE(host->base, CON, con & ~OD);
> +out:
> +	host->context_loss = context_loss;
> +
> +	dev_dbg(mmc_dev(host->mmc), "context is restored\n");
> +	return 0;
> +}
> +
> +/*
> + * Save the MMC host context (store the number of power state changes so
> far).
> + */
> +static void omap_mmc_save_ctx(struct mmc_omap_host *host)
> +{
> +	struct omap_mmc_platform_data *pdata = host->pdata;
> +	int context_loss;
> +
> +	if (pdata->get_context_loss_count) {
> +		context_loss = pdata->get_context_loss_count(host->dev);
> +		if (context_loss < 0)
> +			return;
> +		host->context_loss = context_loss;
> +	}
> +}
> +
> +#else
> +
> +static int omap_mmc_restore_ctx(struct mmc_omap_host *host)
> +{
> +	return 0;
> +}
> +
> +static void omap_mmc_save_ctx(struct mmc_omap_host *host)
> +{
> +}
> +
> +#endif
> +
>  /*
>   * Send init stream sequence to card
>   * before sending IDLE command
> @@ -823,6 +988,7 @@ static int omap_mmc_enable(struct mmc_host *mmc)
>  	if (err)
>  		return err;
>  	dev_dbg(mmc_dev(host->mmc), "mmc_fclk: enabled\n");
> +	omap_mmc_restore_ctx(host);
>  	return 0;
>  }
> 
> @@ -830,6 +996,7 @@ static int omap_mmc_disable(struct mmc_host *mmc, int
> lazy)
>  {
>  	struct mmc_omap_host *host = mmc_priv(mmc);
> 
> +	omap_mmc_save_ctx(host);
>  	clk_disable(host->fclk);
>  	dev_dbg(mmc_dev(host->mmc), "mmc_fclk: disabled\n");
>  	return 0;
> @@ -934,7 +1101,7 @@ static void omap_mmc_set_ios(struct mmc_host *mmc,
> struct mmc_ios *ios)
> 
>  	/* Wait till the ICS bit is set */
>  	timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS);
> -	while ((OMAP_HSMMC_READ(host->base, SYSCTL) & ICS) != 0x2
> +	while ((OMAP_HSMMC_READ(host->base, SYSCTL) & ICS) != ICS
>  		&& time_before(jiffies, timeout))
>  		msleep(1);
> 
> @@ -1014,12 +1181,19 @@ static int mmc_regs_show(struct seq_file *s, void
> *data)
>  {
>  	struct mmc_host *mmc = s->private;
>  	struct mmc_omap_host *host = mmc_priv(mmc);
> +	struct omap_mmc_platform_data *pdata = host->pdata;
> +	int context_loss = 0;
> +
> +	if (pdata->get_context_loss_count)
> +		context_loss = pdata->get_context_loss_count(host->dev);
> 
>  	seq_printf(s, "mmc%d:\n"
>  			" enabled:\t%d\n"
>  			" nesting_cnt:\t%d\n"
> +			" ctx_loss:\t%d:%d\n"
>  			"\nregs:\n",
> -			mmc->index, mmc->enabled ? 1 : 0, mmc->nesting_cnt);
> +			mmc->index, mmc->enabled ? 1 : 0, mmc->nesting_cnt,
> +			host->context_loss, context_loss);
> 
>  	if (clk_enable(host->fclk) != 0) {
>  		seq_printf(s, "can't read the regs\n");
> @@ -1144,6 +1318,8 @@ static int __init omap_mmc_probe(struct
> platform_device *pdev)
>  		goto err1;
>  	}
> 
> +	omap_mmc_save_ctx(host);
> +
>  	mmc->caps |= MMC_CAP_DISABLE;
>  	mmc_set_disable_delay(mmc, 100);
>  	if (mmc_host_enable(host->mmc) != 0) {
> @@ -1380,21 +1556,19 @@ static int omap_mmc_resume(struct platform_device
> *pdev)
>  		return 0;
> 
>  	if (host) {
> -
> -		if (mmc_host_enable(host->mmc) != 0)
> -			goto clk_en_err;
> -
>  		ret = clk_enable(host->iclk);
> -		if (ret) {
> -			mmc_host_disable(host->mmc);
> -			clk_put(host->fclk);
> +		if (ret)
>  			goto clk_en_err;
> -		}
> 
>  		if (clk_enable(host->dbclk) != 0)
>  			dev_dbg(mmc_dev(host->mmc),
>  					"Enabling debounce clk failed\n");
> 
> +		if (mmc_host_enable(host->mmc) != 0) {
> +			clk_disable(host->iclk);
> +			goto clk_en_err;
> +		}
> +
>  		omap_hsmmc_init(host);
> 
>  		if (host->pdata->resume) {
> --
> 1.5.6.3
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-omap" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Adrian Hunter July 30, 2009, 7:40 a.m. UTC | #2
Madhusudhan wrote:
> 
>> -----Original Message-----
>> From: linux-omap-owner@vger.kernel.org [mailto:linux-omap-
>> owner@vger.kernel.org] On Behalf Of Adrian Hunter
>> Sent: Tuesday, July 28, 2009 5:40 AM
>> To: Andrew Morton
>> Cc: Jarkko Lavinen; Adrian Hunter; linux-omap Mailing List; Pierre Ossman;
>> Denis Karpov; Matt Fleming; lkml
>> Subject: [PATCH V2 13/32] omap_hsmmc: context save/restore support
>>
>> From 43e9fa346d7e386328876a8535dc8619bd1f47ae Mon Sep 17 00:00:00 2001
>> From: Denis Karpov <ext-denis.2.karpov@nokia.com>
>> Date: Wed, 22 Apr 2009 16:04:25 +0200
>> Subject: [PATCH] omap_hsmmc: context save/restore support
>>
>> Keep the context over PM dynamic OFF states.
>>
>> Signed-off-by: Denis Karpov <ext-denis.2.karpov@nokia.com>
>> Signed-off-by: Adrian Hunter <adrian.hunter@nokia.com>
>> ---
>>  drivers/mmc/host/omap_hsmmc.c |  194
>> ++++++++++++++++++++++++++++++++++++++--
>>  1 files changed, 184 insertions(+), 10 deletions(-)
>>
>> diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
>> index c28d055..ac1a3bf 100644
>> --- a/drivers/mmc/host/omap_hsmmc.c
>> +++ b/drivers/mmc/host/omap_hsmmc.c
>> @@ -37,6 +37,7 @@
>>
>>  /* OMAP HSMMC Host Controller Registers */
>>  #define OMAP_HSMMC_SYSCONFIG	0x0010
>> +#define OMAP_HSMMC_SYSSTATUS	0x0014
>>  #define OMAP_HSMMC_CON		0x002C
>>  #define OMAP_HSMMC_BLK		0x0104
>>  #define OMAP_HSMMC_ARG		0x0108
>> @@ -94,6 +95,8 @@
>>  #define DUAL_VOLT_OCR_BIT	7
>>  #define SRC			(1 << 25)
>>  #define SRD			(1 << 26)
>> +#define SOFTRESET		(1 << 1)
>> +#define RESETDONE		(1 << 0)
>>
>>  /*
>>   * FIXME: Most likely all the data using these _DEVID defines should come
>> @@ -152,6 +155,8 @@ struct mmc_omap_host {
>>  	int			slot_id;
>>  	int			dbclk_enabled;
>>  	int			response_busy;
>> +	int			context_loss;
>> +
>>  	struct	omap_mmc_platform_data	*pdata;
>>  };
>>
>> @@ -166,6 +171,166 @@ static void omap_mmc_stop_clock(struct mmc_omap_host
>> *host)
>>  		dev_dbg(mmc_dev(host->mmc), "MMC Clock is not stoped\n");
>>  }
>>
>> +#ifdef CONFIG_PM
>> +
>> +/*
>> + * Restore the MMC host context, if it was lost as result of a
>> + * power state change.
>> + */
>> +static int omap_mmc_restore_ctx(struct mmc_omap_host *host)
>> +{
>> +	struct mmc_ios *ios = &host->mmc->ios;
>> +	struct omap_mmc_platform_data *pdata = host->pdata;
>> +	int context_loss = 0;
>> +	u32 hctl, capa, con;
>> +	u16 dsor = 0;
>> +	unsigned long timeout;
>> +
>> +	if (pdata->get_context_loss_count) {
>> +		context_loss = pdata->get_context_loss_count(host->dev);
>> +		if (context_loss < 0)
>> +			return 1;
>> +	}
> This seems to restore the context always. The context should be restored
> only if the card was powered OFF, Right?

Not exactly. The "context" is just the host controller registers, which get
lost if power management decides to power off the host controller, which it
will if it decides it is not doing anything.  For us, switching off the
functional clock lets PM power off the host controller.  The card can be in
any power state: on, off, card/regulator sleep.

The context is restored only when it has been lost - see the following
lines.  But this is done from the "enable" method, so this is only called
if someone is trying to access the card.

+	dev_dbg(mmc_dev(host->mmc), "context was %slost\n",
+		context_loss == host->context_loss ? "not " : "");
+	if (host->context_loss == context_loss)
+		return 1;

And then further down, we skip some things if the card is off:

+	/* Do not initialize card-specific things if the power is off */
+	if (host->power_mode == MMC_POWER_OFF)
+		goto out;


> The context could also be lost if the CORE transitions to OFF. I assume that
> case gets handled here without anything extra required if "power_saving" is
> set to true. Am I right?

Yes, "power_saving" is not related to "Power Management".  "power_saving"
is about controlling the power regulators, which the host controller does
itself without any support from PM.

To put it another way, "power_saving" and CONFIG_PM can be set independently
of one another.

> How about the case of eMMC? Since it is not a removable device the card is
> not powered OFF. But the CORE OFF would result in context loss. Do we hit
> the restore_ctx path in that case?

On the contrary, eMMC *is* powered off.  After 100ms of inactivity, eMMC is
put to card sleep, and the regulator is put to sleep also - in fact there
are two regulators and one is powered off here.  After 8 seconds
the other regulator is powered off and consequently after that the eMMC must
be reinitialised (rescanned) before it can be used.
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Madhusudhan July 30, 2009, 7:12 p.m. UTC | #3
> -----Original Message-----
> From: Adrian Hunter [mailto:adrian.hunter@nokia.com]
> Sent: Thursday, July 30, 2009 2:40 AM
> To: Madhusudhan
> Cc: 'Andrew Morton'; Lavinen Jarkko (Nokia-D/Helsinki); 'linux-omap
> Mailing List'; 'Pierre Ossman'; Karpov Denis.2 (EXT-Teleca/Helsinki);
> 'Matt Fleming'; 'lkml'
> Subject: Re: [PATCH V2 13/32] omap_hsmmc: context save/restore support
> 
> Madhusudhan wrote:
> >
> >> -----Original Message-----
> >> From: linux-omap-owner@vger.kernel.org [mailto:linux-omap-
> >> owner@vger.kernel.org] On Behalf Of Adrian Hunter
> >> Sent: Tuesday, July 28, 2009 5:40 AM
> >> To: Andrew Morton
> >> Cc: Jarkko Lavinen; Adrian Hunter; linux-omap Mailing List; Pierre
> Ossman;
> >> Denis Karpov; Matt Fleming; lkml
> >> Subject: [PATCH V2 13/32] omap_hsmmc: context save/restore support
> >>
> >> From 43e9fa346d7e386328876a8535dc8619bd1f47ae Mon Sep 17 00:00:00 2001
> >> From: Denis Karpov <ext-denis.2.karpov@nokia.com>
> >> Date: Wed, 22 Apr 2009 16:04:25 +0200
> >> Subject: [PATCH] omap_hsmmc: context save/restore support
> >>
> >> Keep the context over PM dynamic OFF states.
> >>
> >> Signed-off-by: Denis Karpov <ext-denis.2.karpov@nokia.com>
> >> Signed-off-by: Adrian Hunter <adrian.hunter@nokia.com>
> >> ---
> >>  drivers/mmc/host/omap_hsmmc.c |  194
> >> ++++++++++++++++++++++++++++++++++++++--
> >>  1 files changed, 184 insertions(+), 10 deletions(-)
> >>
> >> diff --git a/drivers/mmc/host/omap_hsmmc.c
> b/drivers/mmc/host/omap_hsmmc.c
> >> index c28d055..ac1a3bf 100644
> >> --- a/drivers/mmc/host/omap_hsmmc.c
> >> +++ b/drivers/mmc/host/omap_hsmmc.c
> >> @@ -37,6 +37,7 @@
> >>
> >>  /* OMAP HSMMC Host Controller Registers */
> >>  #define OMAP_HSMMC_SYSCONFIG	0x0010
> >> +#define OMAP_HSMMC_SYSSTATUS	0x0014
> >>  #define OMAP_HSMMC_CON		0x002C
> >>  #define OMAP_HSMMC_BLK		0x0104
> >>  #define OMAP_HSMMC_ARG		0x0108
> >> @@ -94,6 +95,8 @@
> >>  #define DUAL_VOLT_OCR_BIT	7
> >>  #define SRC			(1 << 25)
> >>  #define SRD			(1 << 26)
> >> +#define SOFTRESET		(1 << 1)
> >> +#define RESETDONE		(1 << 0)
> >>
> >>  /*
> >>   * FIXME: Most likely all the data using these _DEVID defines should
> come
> >> @@ -152,6 +155,8 @@ struct mmc_omap_host {
> >>  	int			slot_id;
> >>  	int			dbclk_enabled;
> >>  	int			response_busy;
> >> +	int			context_loss;
> >> +
> >>  	struct	omap_mmc_platform_data	*pdata;
> >>  };
> >>
> >> @@ -166,6 +171,166 @@ static void omap_mmc_stop_clock(struct
> mmc_omap_host
> >> *host)
> >>  		dev_dbg(mmc_dev(host->mmc), "MMC Clock is not stoped\n");
> >>  }
> >>
> >> +#ifdef CONFIG_PM
> >> +
> >> +/*
> >> + * Restore the MMC host context, if it was lost as result of a
> >> + * power state change.
> >> + */
> >> +static int omap_mmc_restore_ctx(struct mmc_omap_host *host)
> >> +{
> >> +	struct mmc_ios *ios = &host->mmc->ios;
> >> +	struct omap_mmc_platform_data *pdata = host->pdata;
> >> +	int context_loss = 0;
> >> +	u32 hctl, capa, con;
> >> +	u16 dsor = 0;
> >> +	unsigned long timeout;
> >> +
> >> +	if (pdata->get_context_loss_count) {
> >> +		context_loss = pdata->get_context_loss_count(host->dev);
> >> +		if (context_loss < 0)
> >> +			return 1;
> >> +	}
> > This seems to restore the context always. The context should be restored
> > only if the card was powered OFF, Right?
> 
> Not exactly. The "context" is just the host controller registers, which
> get
> lost if power management decides to power off the host controller, which
> it
> will if it decides it is not doing anything.  For us, switching off the
> functional clock lets PM power off the host controller.  The card can be
> in
> any power state: on, off, card/regulator sleep.
> 
> The context is restored only when it has been lost - see the following
> lines.  But this is done from the "enable" method, so this is only called
> if someone is trying to access the card.
> 
> +	dev_dbg(mmc_dev(host->mmc), "context was %slost\n",
> +		context_loss == host->context_loss ? "not " : "");
> +	if (host->context_loss == context_loss)
> +		return 1;
> 
> And then further down, we skip some things if the card is off:
> 
> +	/* Do not initialize card-specific things if the power is off */
> +	if (host->power_mode == MMC_POWER_OFF)
> +		goto out;
> 
> 
> > The context could also be lost if the CORE transitions to OFF. I assume
> that
> > case gets handled here without anything extra required if "power_saving"
> is
> > set to true. Am I right?
> 
> Yes, "power_saving" is not related to "Power Management".  "power_saving"
> is about controlling the power regulators, which the host controller does
> itself without any support from PM.
> 
> To put it another way, "power_saving" and CONFIG_PM can be set
> independently
> of one another.

But the "omap_hsmmc_context_save" and " omap_hsmmc_context_restore" are
under CONFIG_PM. They are empty functions if CONFIG_PM is not set. Does this
mean that if CONFIG_PM is not set the host regulators are not turned OFF
even if there is no activity on MMC?

> 
> > How about the case of eMMC? Since it is not a removable device the card
> is
> > not powered OFF. But the CORE OFF would result in context loss. Do we
> hit
> > the restore_ctx path in that case?
> 
> On the contrary, eMMC *is* powered off.  After 100ms of inactivity, eMMC
> is
> put to card sleep, and the regulator is put to sleep also - in fact there
> are two regulators and one is powered off here.  After 8 seconds
> the other regulator is powered off and consequently after that the eMMC
> must
> be reinitialised (rescanned) before it can be used.


--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Adrian Hunter July 30, 2009, 7:33 p.m. UTC | #4
ext Madhusudhan wrote:
> 
>> -----Original Message-----
>> From: Adrian Hunter [mailto:adrian.hunter@nokia.com]
>> Sent: Thursday, July 30, 2009 2:40 AM
>> To: Madhusudhan
>> Cc: 'Andrew Morton'; Lavinen Jarkko (Nokia-D/Helsinki); 'linux-omap
>> Mailing List'; 'Pierre Ossman'; Karpov Denis.2 (EXT-Teleca/Helsinki);
>> 'Matt Fleming'; 'lkml'
>> Subject: Re: [PATCH V2 13/32] omap_hsmmc: context save/restore support
>>
>> Madhusudhan wrote:
>>>> -----Original Message-----
>>>> From: linux-omap-owner@vger.kernel.org [mailto:linux-omap-
>>>> owner@vger.kernel.org] On Behalf Of Adrian Hunter
>>>> Sent: Tuesday, July 28, 2009 5:40 AM
>>>> To: Andrew Morton
>>>> Cc: Jarkko Lavinen; Adrian Hunter; linux-omap Mailing List; Pierre
>> Ossman;
>>>> Denis Karpov; Matt Fleming; lkml
>>>> Subject: [PATCH V2 13/32] omap_hsmmc: context save/restore support
>>>>
>>>> From 43e9fa346d7e386328876a8535dc8619bd1f47ae Mon Sep 17 00:00:00 2001
>>>> From: Denis Karpov <ext-denis.2.karpov@nokia.com>
>>>> Date: Wed, 22 Apr 2009 16:04:25 +0200
>>>> Subject: [PATCH] omap_hsmmc: context save/restore support
>>>>
>>>> Keep the context over PM dynamic OFF states.
>>>>
>>>> Signed-off-by: Denis Karpov <ext-denis.2.karpov@nokia.com>
>>>> Signed-off-by: Adrian Hunter <adrian.hunter@nokia.com>
>>>> ---
>>>>  drivers/mmc/host/omap_hsmmc.c |  194
>>>> ++++++++++++++++++++++++++++++++++++++--
>>>>  1 files changed, 184 insertions(+), 10 deletions(-)
>>>>
>>>> diff --git a/drivers/mmc/host/omap_hsmmc.c
>> b/drivers/mmc/host/omap_hsmmc.c
>>>> index c28d055..ac1a3bf 100644
>>>> --- a/drivers/mmc/host/omap_hsmmc.c
>>>> +++ b/drivers/mmc/host/omap_hsmmc.c
>>>> @@ -37,6 +37,7 @@
>>>>
>>>>  /* OMAP HSMMC Host Controller Registers */
>>>>  #define OMAP_HSMMC_SYSCONFIG	0x0010
>>>> +#define OMAP_HSMMC_SYSSTATUS	0x0014
>>>>  #define OMAP_HSMMC_CON		0x002C
>>>>  #define OMAP_HSMMC_BLK		0x0104
>>>>  #define OMAP_HSMMC_ARG		0x0108
>>>> @@ -94,6 +95,8 @@
>>>>  #define DUAL_VOLT_OCR_BIT	7
>>>>  #define SRC			(1 << 25)
>>>>  #define SRD			(1 << 26)
>>>> +#define SOFTRESET		(1 << 1)
>>>> +#define RESETDONE		(1 << 0)
>>>>
>>>>  /*
>>>>   * FIXME: Most likely all the data using these _DEVID defines should
>> come
>>>> @@ -152,6 +155,8 @@ struct mmc_omap_host {
>>>>  	int			slot_id;
>>>>  	int			dbclk_enabled;
>>>>  	int			response_busy;
>>>> +	int			context_loss;
>>>> +
>>>>  	struct	omap_mmc_platform_data	*pdata;
>>>>  };
>>>>
>>>> @@ -166,6 +171,166 @@ static void omap_mmc_stop_clock(struct
>> mmc_omap_host
>>>> *host)
>>>>  		dev_dbg(mmc_dev(host->mmc), "MMC Clock is not stoped\n");
>>>>  }
>>>>
>>>> +#ifdef CONFIG_PM
>>>> +
>>>> +/*
>>>> + * Restore the MMC host context, if it was lost as result of a
>>>> + * power state change.
>>>> + */
>>>> +static int omap_mmc_restore_ctx(struct mmc_omap_host *host)
>>>> +{
>>>> +	struct mmc_ios *ios = &host->mmc->ios;
>>>> +	struct omap_mmc_platform_data *pdata = host->pdata;
>>>> +	int context_loss = 0;
>>>> +	u32 hctl, capa, con;
>>>> +	u16 dsor = 0;
>>>> +	unsigned long timeout;
>>>> +
>>>> +	if (pdata->get_context_loss_count) {
>>>> +		context_loss = pdata->get_context_loss_count(host->dev);
>>>> +		if (context_loss < 0)
>>>> +			return 1;
>>>> +	}
>>> This seems to restore the context always. The context should be restored
>>> only if the card was powered OFF, Right?
>> Not exactly. The "context" is just the host controller registers, which
>> get
>> lost if power management decides to power off the host controller, which
>> it
>> will if it decides it is not doing anything.  For us, switching off the
>> functional clock lets PM power off the host controller.  The card can be
>> in
>> any power state: on, off, card/regulator sleep.
>>
>> The context is restored only when it has been lost - see the following
>> lines.  But this is done from the "enable" method, so this is only called
>> if someone is trying to access the card.
>>
>> +	dev_dbg(mmc_dev(host->mmc), "context was %slost\n",
>> +		context_loss == host->context_loss ? "not " : "");
>> +	if (host->context_loss == context_loss)
>> +		return 1;
>>
>> And then further down, we skip some things if the card is off:
>>
>> +	/* Do not initialize card-specific things if the power is off */
>> +	if (host->power_mode == MMC_POWER_OFF)
>> +		goto out;
>>
>>
>>> The context could also be lost if the CORE transitions to OFF. I assume
>> that
>>> case gets handled here without anything extra required if "power_saving"
>> is
>>> set to true. Am I right?
>> Yes, "power_saving" is not related to "Power Management".  "power_saving"
>> is about controlling the power regulators, which the host controller does
>> itself without any support from PM.
>>
>> To put it another way, "power_saving" and CONFIG_PM can be set
>> independently
>> of one another.
> 
> But the "omap_hsmmc_context_save" and " omap_hsmmc_context_restore" are
> under CONFIG_PM. They are empty functions if CONFIG_PM is not set. Does this
> mean that if CONFIG_PM is not set the host regulators are not turned OFF
> even if there is no activity on MMC?

No.  Context save/restore *is* a part of Power Management.  It is needed
to support Dynamic Power Switching (DPS).

The voltage regulators are controlled via the enable/disable methods
even when CONFIG_PM is not defined.

Note that the voltage regulators to which I refer, power the cards not
the host controllers.

So the power to the host controller is under the control of Power Management
and is supported by context save/restore.

The power to the cards is controlled by the driver (omap_hsmmc) using the
enable/disable methods.


--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index c28d055..ac1a3bf 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -37,6 +37,7 @@ 
 
 /* OMAP HSMMC Host Controller Registers */
 #define OMAP_HSMMC_SYSCONFIG	0x0010
+#define OMAP_HSMMC_SYSSTATUS	0x0014
 #define OMAP_HSMMC_CON		0x002C
 #define OMAP_HSMMC_BLK		0x0104
 #define OMAP_HSMMC_ARG		0x0108
@@ -94,6 +95,8 @@ 
 #define DUAL_VOLT_OCR_BIT	7
 #define SRC			(1 << 25)
 #define SRD			(1 << 26)
+#define SOFTRESET		(1 << 1)
+#define RESETDONE		(1 << 0)
 
 /*
  * FIXME: Most likely all the data using these _DEVID defines should come
@@ -152,6 +155,8 @@  struct mmc_omap_host {
 	int			slot_id;
 	int			dbclk_enabled;
 	int			response_busy;
+	int			context_loss;
+
 	struct	omap_mmc_platform_data	*pdata;
 };
 
@@ -166,6 +171,166 @@  static void omap_mmc_stop_clock(struct mmc_omap_host *host)
 		dev_dbg(mmc_dev(host->mmc), "MMC Clock is not stoped\n");
 }
 
+#ifdef CONFIG_PM
+
+/*
+ * Restore the MMC host context, if it was lost as result of a
+ * power state change.
+ */
+static int omap_mmc_restore_ctx(struct mmc_omap_host *host)
+{
+	struct mmc_ios *ios = &host->mmc->ios;
+	struct omap_mmc_platform_data *pdata = host->pdata;
+	int context_loss = 0;
+	u32 hctl, capa, con;
+	u16 dsor = 0;
+	unsigned long timeout;
+
+	if (pdata->get_context_loss_count) {
+		context_loss = pdata->get_context_loss_count(host->dev);
+		if (context_loss < 0)
+			return 1;
+	}
+
+	dev_dbg(mmc_dev(host->mmc), "context was %slost\n",
+		context_loss == host->context_loss ? "not " : "");
+	if (host->context_loss == context_loss)
+		return 1;
+
+	/* Wait for hardware reset */
+	timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS);
+	while ((OMAP_HSMMC_READ(host->base, SYSSTATUS) & RESETDONE) != RESETDONE
+		&& time_before(jiffies, timeout))
+		;
+
+	/* Do software reset */
+	OMAP_HSMMC_WRITE(host->base, SYSCONFIG, SOFTRESET);
+	timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS);
+	while ((OMAP_HSMMC_READ(host->base, SYSSTATUS) & RESETDONE) != RESETDONE
+		&& time_before(jiffies, timeout))
+		;
+
+	OMAP_HSMMC_WRITE(host->base, SYSCONFIG,
+			OMAP_HSMMC_READ(host->base, SYSCONFIG) | AUTOIDLE);
+
+	if (host->id == OMAP_MMC1_DEVID) {
+		if (host->power_mode != MMC_POWER_OFF &&
+		    (1 << ios->vdd) <= MMC_VDD_23_24)
+			hctl = SDVS18;
+		else
+			hctl = SDVS30;
+		capa = VS30 | VS18;
+	} else {
+		hctl = SDVS18;
+		capa = VS18;
+	}
+
+	OMAP_HSMMC_WRITE(host->base, HCTL,
+			OMAP_HSMMC_READ(host->base, HCTL) | hctl);
+
+	OMAP_HSMMC_WRITE(host->base, CAPA,
+			OMAP_HSMMC_READ(host->base, CAPA) | capa);
+
+	OMAP_HSMMC_WRITE(host->base, HCTL,
+			OMAP_HSMMC_READ(host->base, HCTL) | SDBP);
+
+	timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS);
+	while ((OMAP_HSMMC_READ(host->base, HCTL) & SDBP) != SDBP
+		&& time_before(jiffies, timeout))
+		;
+
+	OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
+	OMAP_HSMMC_WRITE(host->base, ISE, INT_EN_MASK);
+	OMAP_HSMMC_WRITE(host->base, IE, INT_EN_MASK);
+
+	/* Do not initialize card-specific things if the power is off */
+	if (host->power_mode == MMC_POWER_OFF)
+		goto out;
+
+	con = OMAP_HSMMC_READ(host->base, CON);
+	switch (ios->bus_width) {
+	case MMC_BUS_WIDTH_8:
+		OMAP_HSMMC_WRITE(host->base, CON, con | DW8);
+		break;
+	case MMC_BUS_WIDTH_4:
+		OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8);
+		OMAP_HSMMC_WRITE(host->base, HCTL,
+			OMAP_HSMMC_READ(host->base, HCTL) | FOUR_BIT);
+		break;
+	case MMC_BUS_WIDTH_1:
+		OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8);
+		OMAP_HSMMC_WRITE(host->base, HCTL,
+			OMAP_HSMMC_READ(host->base, HCTL) & ~FOUR_BIT);
+		break;
+	}
+
+	if (ios->clock) {
+		dsor = OMAP_MMC_MASTER_CLOCK / ios->clock;
+		if (dsor < 1)
+			dsor = 1;
+
+		if (OMAP_MMC_MASTER_CLOCK / dsor > ios->clock)
+			dsor++;
+
+		if (dsor > 250)
+			dsor = 250;
+	}
+
+	OMAP_HSMMC_WRITE(host->base, SYSCTL,
+		OMAP_HSMMC_READ(host->base, SYSCTL) & ~CEN);
+	OMAP_HSMMC_WRITE(host->base, SYSCTL, (dsor << 6) | (DTO << 16));
+	OMAP_HSMMC_WRITE(host->base, SYSCTL,
+		OMAP_HSMMC_READ(host->base, SYSCTL) | ICE);
+
+	timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS);
+	while ((OMAP_HSMMC_READ(host->base, SYSCTL) & ICS) != ICS
+		&& time_before(jiffies, timeout))
+		;
+
+	OMAP_HSMMC_WRITE(host->base, SYSCTL,
+		OMAP_HSMMC_READ(host->base, SYSCTL) | CEN);
+
+	con = OMAP_HSMMC_READ(host->base, CON);
+	if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)
+		OMAP_HSMMC_WRITE(host->base, CON, con | OD);
+	else
+		OMAP_HSMMC_WRITE(host->base, CON, con & ~OD);
+out:
+	host->context_loss = context_loss;
+
+	dev_dbg(mmc_dev(host->mmc), "context is restored\n");
+	return 0;
+}
+
+/*
+ * Save the MMC host context (store the number of power state changes so far).
+ */
+static void omap_mmc_save_ctx(struct mmc_omap_host *host)
+{
+	struct omap_mmc_platform_data *pdata = host->pdata;
+	int context_loss;
+
+	if (pdata->get_context_loss_count) {
+		context_loss = pdata->get_context_loss_count(host->dev);
+		if (context_loss < 0)
+			return;
+		host->context_loss = context_loss;
+	}
+}
+
+#else
+
+static int omap_mmc_restore_ctx(struct mmc_omap_host *host)
+{
+	return 0;
+}
+
+static void omap_mmc_save_ctx(struct mmc_omap_host *host)
+{
+}
+
+#endif
+
 /*
  * Send init stream sequence to card
  * before sending IDLE command
@@ -823,6 +988,7 @@  static int omap_mmc_enable(struct mmc_host *mmc)
 	if (err)
 		return err;
 	dev_dbg(mmc_dev(host->mmc), "mmc_fclk: enabled\n");
+	omap_mmc_restore_ctx(host);
 	return 0;
 }
 
@@ -830,6 +996,7 @@  static int omap_mmc_disable(struct mmc_host *mmc, int lazy)
 {
 	struct mmc_omap_host *host = mmc_priv(mmc);
 
+	omap_mmc_save_ctx(host);
 	clk_disable(host->fclk);
 	dev_dbg(mmc_dev(host->mmc), "mmc_fclk: disabled\n");
 	return 0;
@@ -934,7 +1101,7 @@  static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 
 	/* Wait till the ICS bit is set */
 	timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS);
-	while ((OMAP_HSMMC_READ(host->base, SYSCTL) & ICS) != 0x2
+	while ((OMAP_HSMMC_READ(host->base, SYSCTL) & ICS) != ICS
 		&& time_before(jiffies, timeout))
 		msleep(1);
 
@@ -1014,12 +1181,19 @@  static int mmc_regs_show(struct seq_file *s, void *data)
 {
 	struct mmc_host *mmc = s->private;
 	struct mmc_omap_host *host = mmc_priv(mmc);
+	struct omap_mmc_platform_data *pdata = host->pdata;
+	int context_loss = 0;
+
+	if (pdata->get_context_loss_count)
+		context_loss = pdata->get_context_loss_count(host->dev);
 
 	seq_printf(s, "mmc%d:\n"
 			" enabled:\t%d\n"
 			" nesting_cnt:\t%d\n"
+			" ctx_loss:\t%d:%d\n"
 			"\nregs:\n",
-			mmc->index, mmc->enabled ? 1 : 0, mmc->nesting_cnt);
+			mmc->index, mmc->enabled ? 1 : 0, mmc->nesting_cnt,
+			host->context_loss, context_loss);
 
 	if (clk_enable(host->fclk) != 0) {
 		seq_printf(s, "can't read the regs\n");
@@ -1144,6 +1318,8 @@  static int __init omap_mmc_probe(struct platform_device *pdev)
 		goto err1;
 	}
 
+	omap_mmc_save_ctx(host);
+
 	mmc->caps |= MMC_CAP_DISABLE;
 	mmc_set_disable_delay(mmc, 100);
 	if (mmc_host_enable(host->mmc) != 0) {
@@ -1380,21 +1556,19 @@  static int omap_mmc_resume(struct platform_device *pdev)
 		return 0;
 
 	if (host) {
-
-		if (mmc_host_enable(host->mmc) != 0)
-			goto clk_en_err;
-
 		ret = clk_enable(host->iclk);
-		if (ret) {
-			mmc_host_disable(host->mmc);
-			clk_put(host->fclk);
+		if (ret)
 			goto clk_en_err;
-		}
 
 		if (clk_enable(host->dbclk) != 0)
 			dev_dbg(mmc_dev(host->mmc),
 					"Enabling debounce clk failed\n");
 
+		if (mmc_host_enable(host->mmc) != 0) {
+			clk_disable(host->iclk);
+			goto clk_en_err;
+		}
+
 		omap_hsmmc_init(host);
 
 		if (host->pdata->resume) {