diff mbox

OMAP: HSMMC: Initialize hsmmc controller registers when resuming

Message ID 4d34a0a70902200400s252f48ddvfd6e0d83e91fa291@mail.gmail.com (mailing list archive)
State Changes Requested, archived
Headers show

Commit Message

kim kyuwon Feb. 20, 2009, noon UTC
Most registers lose its state when the processor wakes up from sleep state.
Thus registers should be initialized, when the processor wakes up. However the
current hsmmc 'resume' function doesn't consider this issue and finally makes
deadlock. So this patch fixes this problem.

Signed-off-by: Kim Kyuwon <chammoru@gmail.com>
---
 drivers/mmc/host/omap_hsmmc.c |  155 +++++++++++++++++++++--------------------
 1 files changed, 79 insertions(+), 76 deletions(-)

 		dev_err(&pdev->dev, "Platform Data is missing\n");
@@ -981,28 +1009,7 @@ static int __init omap_mmc_probe(struct
platform_device *pdev)
 	if (pdata->slots[host->slot_id].wires >= 4)
 		mmc->caps |= MMC_CAP_4_BIT_DATA;

-	/* Only MMC1 supports 3.0V */
-	if (host->id == OMAP_MMC1_DEVID) {
-		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);
-
-	/* Set the controller to AUTO IDLE mode */
-	OMAP_HSMMC_WRITE(host->base, SYSCONFIG,
-			OMAP_HSMMC_READ(host->base, SYSCONFIG) | AUTOIDLE);
-
-	/* Set SD bus power bit */
-	OMAP_HSMMC_WRITE(host->base, HCTL,
-			OMAP_HSMMC_READ(host->base, HCTL) | SDBP);
+	omap_hsmmc_init(host);

 	/* Request IRQ for MMC operations */
 	ret = request_irq(host->irq, mmc_omap_irq, IRQF_DISABLED,
@@ -1127,41 +1134,38 @@ static int omap_mmc_suspend(struct
platform_device *pdev, pm_message_t state)
 	if (host && host->suspended)
 		return 0;

-	if (host) {
-		ret = mmc_suspend_host(host->mmc, state);
-		if (ret == 0) {
-			host->suspended = 1;
-
-			OMAP_HSMMC_WRITE(host->base, ISE, 0);
-			OMAP_HSMMC_WRITE(host->base, IE, 0);
+	ret = mmc_suspend_host(host->mmc, state);
+	if (ret == 0) {
+		host->suspended = 1;

-			if (host->pdata->suspend) {
-				ret = host->pdata->suspend(&pdev->dev,
-								host->slot_id);
-				if (ret)
-					dev_dbg(mmc_dev(host->mmc),
-						"Unable to handle MMC board"
-						" level suspend\n");
-			}
+		OMAP_HSMMC_WRITE(host->base, ISE, 0);
+		OMAP_HSMMC_WRITE(host->base, IE, 0);

-			if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) {
-				OMAP_HSMMC_WRITE(host->base, HCTL,
-					OMAP_HSMMC_READ(host->base, HCTL)
-					& SDVSCLR);
-				OMAP_HSMMC_WRITE(host->base, HCTL,
-					OMAP_HSMMC_READ(host->base, HCTL)
-					| SDVS30);
-				OMAP_HSMMC_WRITE(host->base, HCTL,
-					OMAP_HSMMC_READ(host->base, HCTL)
-					| SDBP);
-			}
+		if (host->pdata->suspend) {
+			ret = host->pdata->suspend(&pdev->dev, host->slot_id);
+			if (ret)
+				dev_dbg(mmc_dev(host->mmc),
+					"Unable to handle MMC board"
+					" level suspend\n");
+		}

-			clk_disable(host->fclk);
-			clk_disable(host->iclk);
-			clk_disable(host->dbclk);
+		if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) {
+			OMAP_HSMMC_WRITE(host->base, HCTL,
+				OMAP_HSMMC_READ(host->base, HCTL)
+				& SDVSCLR);
+			OMAP_HSMMC_WRITE(host->base, HCTL,
+				OMAP_HSMMC_READ(host->base, HCTL)
+				| SDVS30);
+			OMAP_HSMMC_WRITE(host->base, HCTL,
+				OMAP_HSMMC_READ(host->base, HCTL)
+				| SDBP);
 		}

+		clk_disable(host->fclk);
+		clk_disable(host->iclk);
+		clk_disable(host->dbclk);
 	}
+
 	return ret;
 }

@@ -1174,36 +1178,35 @@ static int omap_mmc_resume(struct platform_device *pdev)
 	if (host && !host->suspended)
 		return 0;

-	if (host) {
+	ret = clk_enable(host->fclk);
+	if (ret)
+		goto clk_en_err;

-		ret = clk_enable(host->fclk);
-		if (ret)
-			goto clk_en_err;
-
-		ret = clk_enable(host->iclk);
-		if (ret) {
-			clk_disable(host->fclk);
-			clk_put(host->fclk);
-			goto clk_en_err;
-		}
+	ret = clk_enable(host->iclk);
+	if (ret) {
+		clk_disable(host->fclk);
+		clk_put(host->fclk);
+		goto clk_en_err;
+	}

-		if (clk_enable(host->dbclk) != 0)
-			dev_dbg(mmc_dev(host->mmc),
-					"Enabling debounce clk failed\n");
+	if (clk_enable(host->dbclk) != 0)
+		dev_dbg(mmc_dev(host->mmc),
+				"Enabling debounce clk failed\n");

-		if (host->pdata->resume) {
-			ret = host->pdata->resume(&pdev->dev, host->slot_id);
-			if (ret)
-				dev_dbg(mmc_dev(host->mmc),
-					"Unmask interrupt failed\n");
-		}
+	omap_hsmmc_init(host);

-		/* Notify the core to resume the host */
-		ret = mmc_resume_host(host->mmc);
-		if (ret == 0)
-			host->suspended = 0;
+	if (host->pdata->resume) {
+		ret = host->pdata->resume(&pdev->dev, host->slot_id);
+		if (ret)
+			dev_dbg(mmc_dev(host->mmc),
+				"Unmask interrupt failed\n");
 	}

+	/* Notify the core to resume the host */
+	ret = mmc_resume_host(host->mmc);
+	if (ret == 0)
+		host->suspended = 0;
+
 	return ret;

 clk_en_err:

Comments

David Brownell Feb. 20, 2009, 9:11 p.m. UTC | #1
On Friday 20 February 2009, Kim Kyuwon wrote:
> +static void omap_hsmmc_init(struct mmc_omap_host *host)
> +{
> +       u32 hctl, capa, value;
> +
> +       /* Only MMC1 supports 3.0V */
> +       if (host->id == OMAP_MMC1_DEVID) {
> +               hctl = SDVS30;

Shouldn't it be remembering what voltage it was using,
and then restore that, instead of always making MMC1
restart at a 3.0V level?  That's pretty awkward to test
unless you have a 1.8V-capable card in MMC1...

Somewhat related:  I think the PBIAS register updates
should be moved out of mach-omap2/mmc-twl4030.c into
this driver.  They're needed no matter what flavor
regulator is used to with MMC1 voltage over 1.8V,
and it's a bit odd to split the state machine for
1.8V -vs- 3.0V I/O voltages the way it's now done.

- Dave

--
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
kim kyuwon Feb. 23, 2009, 5:41 a.m. UTC | #2
Hi,

On Sat, Feb 21, 2009 at 6:11 AM, David Brownell <david-b@pacbell.net> wrote:
> On Friday 20 February 2009, Kim Kyuwon wrote:
>> +static void omap_hsmmc_init(struct mmc_omap_host *host)
>> +{
>> +       u32 hctl, capa, value;
>> +
>> +       /* Only MMC1 supports 3.0V */
>> +       if (host->id == OMAP_MMC1_DEVID) {
>> +               hctl = SDVS30;
>
> Shouldn't it be remembering what voltage it was using,
> and then restore that, instead of always making MMC1
> restart at a 3.0V level?  That's pretty awkward to test
> unless you have a 1.8V-capable card in MMC1...

You are somewhat right, thank you.
But remebering what voltage it was using doesn't feasible to me,
because the card can be changed while in 'Sleep' state. I should have
inserted a function that detect the right voltage after intializing. I
will resend the patch later.
Adrian Hunter Feb. 23, 2009, 8:04 a.m. UTC | #3
ext Kim Kyuwon wrote:
> Hi,
> 
> On Sat, Feb 21, 2009 at 6:11 AM, David Brownell <david-b@pacbell.net> wrote:
>> On Friday 20 February 2009, Kim Kyuwon wrote:
>>> +static void omap_hsmmc_init(struct mmc_omap_host *host)
>>> +{
>>> +       u32 hctl, capa, value;
>>> +
>>> +       /* Only MMC1 supports 3.0V */
>>> +       if (host->id == OMAP_MMC1_DEVID) {
>>> +               hctl = SDVS30;
>> Shouldn't it be remembering what voltage it was using,
>> and then restore that, instead of always making MMC1
>> restart at a 3.0V level?  That's pretty awkward to test
>> unless you have a 1.8V-capable card in MMC1...
> 
> You are somewhat right, thank you.
> But remebering what voltage it was using doesn't feasible to me,
> because the card can be changed while in 'Sleep' state. I should have
> inserted a function that detect the right voltage after intializing. I
> will resend the patch later.

Doesn't it already do that? Can you explain more?  

Although I have not tested it, I very much doubt
dual-voltage cards work.  That is because VMMC1_185V
is zero, which has the side-effect of turning the
regulator off (see arch/arm/mach-omap2/mmc-twl4030.c)
--
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
Kyungmin Park Feb. 23, 2009, 12:26 p.m. UTC | #4
Hi,

On Mon, Feb 23, 2009 at 5:04 PM, Adrian Hunter
<ext-adrian.hunter@nokia.com> wrote:
> ext Kim Kyuwon wrote:
>> Hi,
>>
>> On Sat, Feb 21, 2009 at 6:11 AM, David Brownell <david-b@pacbell.net> wrote:
>>> On Friday 20 February 2009, Kim Kyuwon wrote:
>>>> +static void omap_hsmmc_init(struct mmc_omap_host *host)
>>>> +{
>>>> +       u32 hctl, capa, value;
>>>> +
>>>> +       /* Only MMC1 supports 3.0V */
>>>> +       if (host->id == OMAP_MMC1_DEVID) {
>>>> +               hctl = SDVS30;
>>> Shouldn't it be remembering what voltage it was using,
>>> and then restore that, instead of always making MMC1
>>> restart at a 3.0V level?  That's pretty awkward to test
>>> unless you have a 1.8V-capable card in MMC1...
>>
>> You are somewhat right, thank you.
>> But remebering what voltage it was using doesn't feasible to me,
>> because the card can be changed while in 'Sleep' state. I should have
>> inserted a function that detect the right voltage after intializing. I
>> will resend the patch later.
>
> Doesn't it already do that? Can you explain more?
>
> Although I have not tested it, I very much doubt
> dual-voltage cards work.  That is because VMMC1_185V
> is zero, which has the side-effect of turning the
> regulator off (see arch/arm/mach-omap2/mmc-twl4030.c)

It's also to difficult to test in our H/W since it's configured only
support 3.0V.

How about to separate it two phases, first fix the mmc suspend/resume
works again, and then verify dual voltage if there are these hardware

How to you think?

Thank you,
Kyungmin Park
--
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
David Brownell Feb. 23, 2009, 6:30 p.m. UTC | #5
On Monday 23 February 2009, Kyungmin Park wrote:
> > Although I have not tested it, I very much doubt
> > dual-voltage cards work.  That is because VMMC1_185V
> > is zero, which has the side-effect of turning the
> > regulator off (see arch/arm/mach-omap2/mmc-twl4030.c)

Right, looks like a longstanding bug in that glue.
So IMO, no rush to fix for 2.6.29-rc ...


> It's also to difficult to test in our H/W since it's configured only
> support 3.0V.

Easily tested with a Beagle or SDP though, if you
have a 1.8V or dual-voltage card (maybe RS-MMC, as
for an N770 tablet).


> How about to separate it two phases, first fix the mmc suspend/resume
> works again, and then verify dual voltage if there are these hardware

Well, two patches for sure; I don't know that the order
will matter.  I just sent a patch fixing that code in
the current OMAP git tree, which has some fixes that are
scheduled to merge for 2.6.30-rc.

- Dave

--
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
David Brownell March 11, 2009, 3:33 a.m. UTC | #6
On Monday 23 February 2009, Adrian Hunter wrote:
> Although I have not tested it, I very much doubt
> dual-voltage cards work.  That is because VMMC1_185V
> is zero, which has the side-effect of turning the
> regulator off (see arch/arm/mach-omap2/mmc-twl4030.c)

And a second reason to know they don't quite work ... in
the file drivers/mmc/host/omap_hsmmc.c, omap_mmc_set_ios()
sets the voltage for MMC_POWER_OFF (0) or MMC_POWER_UP (1_,
which gives the initial setting -- e.g. 3.15 Volts, so it
can enumerate at the high range.

But after enumerating the card at that voltage, checking
the OCR values, and concluding that the slot and card can
both run at 1.85V ... the MMC_POWER_ON (2) code is used.
But the driver completely ignores it ... the low voltage
(more power-efficient!) voltage range never kicks in.

It'd be nice to have a nice unambiguous set_voltage()
request from the MMC core.  The set_ios() thing has
always been confusing.

- Dave


--
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
Pierre Ossman March 11, 2009, 6:50 a.m. UTC | #7
On Tue, 10 Mar 2009 19:33:50 -0800
David Brownell <david-b@pacbell.net> wrote:

> 
> It'd be nice to have a nice unambiguous set_voltage()
> request from the MMC core.  The set_ios() thing has
> always been confusing.
> 

set_ios() should be taken out back. But someone has to have the time to
reimplement things. :/
diff mbox

Patch

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 56363c5..5a73fa6 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -55,6 +55,7 @@ 
 #define VS30			(1 << 25)
 #define SDVS18			(0x5 << 9)
 #define SDVS30			(0x6 << 9)
+#define SDVS_MASK		0x00000E00
 #define SDVSCLR			0xFFFFF1FF
 #define SDVSDET			0x00000400
 #define AUTOIDLE		0x1
@@ -861,6 +862,34 @@  static int omap_hsmmc_get_ro(struct mmc_host *mmc)
 	return pdata->slots[0].get_ro(host->dev, 0);
 }

+static void omap_hsmmc_init(struct mmc_omap_host *host)
+{
+	u32 hctl, capa, value;
+
+	/* Only MMC1 supports 3.0V */
+	if (host->id == OMAP_MMC1_DEVID) {
+		hctl = SDVS30;
+		capa = VS30 | VS18;
+	} else {
+		hctl = SDVS18;
+		capa = VS18;
+	}
+
+	value = OMAP_HSMMC_READ(host->base, HCTL) & ~SDVS_MASK;
+	OMAP_HSMMC_WRITE(host->base, HCTL, value | hctl);
+
+	value = OMAP_HSMMC_READ(host->base, CAPA);
+	OMAP_HSMMC_WRITE(host->base, CAPA, value | capa);
+
+	/* Set the controller to AUTO IDLE mode */
+	value = OMAP_HSMMC_READ(host->base, SYSCONFIG);
+	OMAP_HSMMC_WRITE(host->base, SYSCONFIG, value | AUTOIDLE);
+
+	/* Set SD bus power bit */
+	value = OMAP_HSMMC_READ(host->base, HCTL);
+	OMAP_HSMMC_WRITE(host->base, HCTL, value | SDBP);
+}
+
 static struct mmc_host_ops mmc_omap_ops = {
 	.request = omap_mmc_request,
 	.set_ios = omap_mmc_set_ios,
@@ -876,7 +905,6 @@  static int __init omap_mmc_probe(struct
platform_device *pdev)
 	struct mmc_omap_host *host = NULL;
 	struct resource *res;
 	int ret = 0, irq;
-	u32 hctl, capa;

 	if (pdata == NULL) {