diff mbox

[2.6.29-rc7-omap,5/5] mmc-twl4030 uses regulator framework

Message ID 200903110524.16644.david-b@pacbell.net (mailing list archive)
State Accepted
Commit 3fe326511c66ab842ef0a09a1f4c564b1a8beecf
Headers show

Commit Message

David Brownell March 11, 2009, 12:24 p.m. UTC
From: David Brownell <dbrownell@users.sourceforge.net>
Subject: mmc-twl4030 uses regulator framework

Finish decoupling the HSMMC glue from the twl4030 as the only
regulator provider, using the regulator framework instead.
This makes the glue's "mmc-twl4030" name become a complete
misnomer ... this code could probably all migrate into the
HSMMC driver now.

Tested on 3430SDP (SD and low-voltage MMC) and Beagle (SD),
plus some other boards (including Overo) after they were
converted to set up MMC regulators properly.

Eventually all boards should just associate a regulator with
each MMC controller they use.  In some cases (Overo MMC2 and
Pandora MMC3, at least) that would be a fixed-voltage regulator
with no real software control.  As a temporary hack (pending
regulator-next updates to make the "fixed.c" regulator become
usable) there's a new ocr_mask field for those boards.

Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
---
 arch/arm/mach-omap2/mmc-twl4030.c |  262 +++++++++++++-----------------------
 arch/arm/mach-omap2/mmc-twl4030.h |    3 
 drivers/mmc/host/omap_hsmmc.c     |    6 
 3 files changed, 100 insertions(+), 171 deletions(-)

--
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

Comments

Tony Lindgren March 11, 2009, 4:09 p.m. UTC | #1
This patch has been applied to the linux-omap
by youw fwiendly patch wobot.

Commit: 3fe326511c66ab842ef0a09a1f4c564b1a8beecf

PatchWorks
http://patchwork.kernel.org/patch/11121/

Git
http://git.kernel.org/?p=linux/kernel/git/tmlind/linux-omap-2.6.git;a=commit;h=3fe326511c66ab842ef0a09a1f4c564b1a8beecf


--
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 March 12, 2009, 8:57 a.m. UTC | #2
David Brownell wrote:
> From: David Brownell <dbrownell@users.sourceforge.net>
> Subject: mmc-twl4030 uses regulator framework
> 
> Finish decoupling the HSMMC glue from the twl4030 as the only
> regulator provider, using the regulator framework instead.
> This makes the glue's "mmc-twl4030" name become a complete
> misnomer ... this code could probably all migrate into the
> HSMMC driver now.
> 
> Tested on 3430SDP (SD and low-voltage MMC) and Beagle (SD),
> plus some other boards (including Overo) after they were
> converted to set up MMC regulators properly.
> 
> Eventually all boards should just associate a regulator with
> each MMC controller they use.  In some cases (Overo MMC2 and
> Pandora MMC3, at least) that would be a fixed-voltage regulator
> with no real software control.  As a temporary hack (pending
> regulator-next updates to make the "fixed.c" regulator become
> usable) there's a new ocr_mask field for those boards.
> 
> Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
> ---

snip

> @@ -349,24 +276,23 @@ static int twl_mmc2_set_power(struct dev
>                         reg |= OMAP2_MMCSDIO2ADPCLKISEL;
>                         omap_ctrl_writel(reg, control_devconf1_offset);
>                 }
> -               ret = twl_mmc_set_voltage(c, vdd);
> +               ret = mmc_regulator_set_ocr(c->vcc, vdd);
> +               /* enable interface voltage rail, if needed */
> +               if (ret == 0 && c->vcc_aux) {
> +                       ret = regulator_enable(c->vcc_aux);
> +                       if (ret < 0)
> +                               ret = mmc_regulator_set_ocr(c->vcc, 0);
> +               }
>         } else {
> -               ret = twl_mmc_set_voltage(c, 0);
> +               if (c->vcc_aux)
> +                       ret = regulator_enable(c->vcc_aux);

Sorry for slow reply.  Shouldn't that be regulator_disable() ?

> +               if (ret == 0)
> +                       ret = mmc_regulator_set_ocr(c->vcc, 0);
>         }
> 
>         return ret;
>  }
> 

snip
--
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 12, 2009, 9:22 a.m. UTC | #3
On Thursday 12 March 2009, Adrian Hunter wrote:
> > +               }
> >         } else {
> > -               ret = twl_mmc_set_voltage(c, 0);
> > +               if (c->vcc_aux)
> > +                       ret = regulator_enable(c->vcc_aux);
> 
> Sorry for slow reply.  Shouldn't that be regulator_disable() ?

Indeed it should be.  In fact I think this probably explains
the failure I saw the other day, trying to go from 3.3V on one
chip down to 1.8V, where it failed (reasons unknown so far)
but the cleanup left vcc_aux active.  Got patch?  :)

I meant this one to be a patch/RFC ... so you could see how I
thought those issues should be addressed, vs the less general
patches you sent the other day.  Actually merging this one
runs into that annoying regulator framework isssue on most
systems.

- 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
Adrian Hunter March 16, 2009, 2:44 p.m. UTC | #4
ext David Brownell wrote:
> On Thursday 12 March 2009, Adrian Hunter wrote:
>>> +               }
>>>         } else {
>>> -               ret = twl_mmc_set_voltage(c, 0);
>>> +               if (c->vcc_aux)
>>> +                       ret = regulator_enable(c->vcc_aux);
>> Sorry for slow reply.  Shouldn't that be regulator_disable() ?
> 
> Indeed it should be.  In fact I think this probably explains
> the failure I saw the other day, trying to go from 3.3V on one
> chip down to 1.8V, where it failed (reasons unknown so far)
> but the cleanup left vcc_aux active.  Got patch?  :)
> 
> I meant this one to be a patch/RFC ... so you could see how I
> thought those issues should be addressed, vs the less general
> patches you sent the other day.  Actually merging this one
> runs into that annoying regulator framework isssue on most
> systems.
> 
> - Dave
> 
> 
> 


I have another problem:


arch/arm/mach-omap2/built-in.o: In function `twl_mmc23_set_power':
/home/ahunter/git/lo-on-rover/arch/arm/mach-omap2/mmc-twl4030.c:282: undefined reference to `mmc_regulator_set_ocr'
/home/ahunter/git/lo-on-rover/arch/arm/mach-omap2/mmc-twl4030.c:293: undefined reference to `mmc_regulator_set_ocr'
arch/arm/mach-omap2/built-in.o: In function `twl_mmc1_set_power':
/home/ahunter/git/lo-on-rover/arch/arm/mach-omap2/mmc-twl4030.c:219: undefined reference to `mmc_regulator_set_ocr'
/home/ahunter/git/lo-on-rover/arch/arm/mach-omap2/mmc-twl4030.c:235: undefined reference to `mmc_regulator_set_ocr'
arch/arm/mach-omap2/built-in.o: In function `twl_mmc_late_init':
/home/ahunter/git/lo-on-rover/arch/arm/mach-omap2/mmc-twl4030.c:128: undefined reference to `mmc_regulator_get_ocrmask'
make: *** [.tmp_vmlinux1] Error 1


The linker is not happy with built-in code trying to call functions in a module directly.

--
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 16, 2009, 7:33 p.m. UTC | #5
On Monday 16 March 2009, Adrian Hunter wrote:
> The linker is not happy with built-in code trying to call functions in a module directly.

Hmm, I guess I mis-read something ... didn't know the MMC core
could be built as a module!

So the workaround is not to build it that way.  And the long-term
fix is to merge the mmc-twl4030 code into the hsmmc driver.

Any problem with using that workaround for a while?

- 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
Adrian Hunter March 17, 2009, 6:38 a.m. UTC | #6
David Brownell wrote:
> On Monday 16 March 2009, Adrian Hunter wrote:
>> The linker is not happy with built-in code trying to call functions in a module directly.
> 
> Hmm, I guess I mis-read something ... didn't know the MMC core
> could be built as a module!
> 
> So the workaround is not to build it that way.  And the long-term
> fix is to merge the mmc-twl4030 code into the hsmmc driver.

Yes the code should be moved into omap_hsmmc.c

> 
> Any problem with using that workaround for a while?

No problem with that.

Also vsim (vmmc_aux) is not being found.

--
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 17, 2009, 7:07 a.m. UTC | #7
On Monday 16 March 2009, Adrian Hunter wrote:
> Also vsim (vmmc_aux) is not being found.

Elaborate please?  The regulator framework complains
if it's not there, even when that's not a bug.


--
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 March 17, 2009, 7:12 a.m. UTC | #8
David Brownell wrote:
> On Monday 16 March 2009, Adrian Hunter wrote:
>> Also vsim (vmmc_aux) is not being found.
> 
> Elaborate please?  The regulator framework complains
> if it's not there, even when that's not a bug.

Sorry, I am at home but I realise now that I did record the message:

regulator: Unable to get requested regulator: vmmc_aux
--
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 17, 2009, 10:25 a.m. UTC | #9
On Tuesday 17 March 2009, Adrian Hunter wrote:
> > 
> > Elaborate please?  The regulator framework complains
> > if it's not there, even when that's not a bug.
> 
> Sorry, I am at home but I realise now that I did record the message:
> 
> regulator: Unable to get requested regulator: vmmc_aux

That's just the regulator framework being stupid.
In two ways:  (a) doesn't say which device didn't
have a "vmmc_aux" supply, (b) even if it did indicate
a bug, that would be the responsibility of the driver
to handle (when it gets an error from that lookup).


--
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

--- a/arch/arm/mach-omap2/mmc-twl4030.c
+++ b/arch/arm/mach-omap2/mmc-twl4030.c
@@ -16,8 +16,8 @@ 
 #include <linux/interrupt.h>
 #include <linux/delay.h>
 #include <linux/gpio.h>
-#include <linux/i2c/twl4030.h>
-#include <linux/regulator/machine.h>
+#include <linux/mmc/host.h>
+#include <linux/regulator/consumer.h>
 
 #include <mach/hardware.h>
 #include <mach/control.h>
@@ -26,31 +26,9 @@ 
 
 #include "mmc-twl4030.h"
 
-#if defined(CONFIG_TWL4030_CORE) && \
-	(defined(CONFIG_MMC_OMAP_HS) || defined(CONFIG_MMC_OMAP_HS_MODULE))
-
-#define LDO_CLR			0x00
-#define VSEL_S2_CLR		0x40
-
-#define VMMC1_DEV_GRP		0x27
-#define VMMC1_CLR		0x00
-#define VMMC1_315V		0x03
-#define VMMC1_300V		0x02
-#define VMMC1_285V		0x01
-#define VMMC1_185V		0x00
-#define VMMC1_DEDICATED		0x2A
 
-#define VMMC2_DEV_GRP		0x2B
-#define VMMC2_CLR		0x40
-#define VMMC2_315V		0x0c
-#define VMMC2_300V		0x0b
-#define VMMC2_285V		0x0a
-#define VMMC2_280V		0x09
-#define VMMC2_260V		0x08
-#define VMMC2_185V		0x06
-#define VMMC2_DEDICATED		0x2E
-
-#define VMMC_DEV_GRP_P1		0x20
+#if defined(CONFIG_REGULATOR) && \
+	(defined(CONFIG_MMC_OMAP_HS) || defined(CONFIG_MMC_OMAP_HS_MODULE))
 
 static u16 control_pbias_offset;
 static u16 control_devconf1_offset;
@@ -59,19 +37,16 @@  static u16 control_devconf1_offset;
 
 static struct twl_mmc_controller {
 	struct omap_mmc_platform_data	*mmc;
-	u8		twl_vmmc_dev_grp;
-	u8		twl_mmc_dedicated;
-	char		name[HSMMC_NAME_LEN + 1];
-} hsmmc[OMAP34XX_NR_MMC] = {
-	{
-		.twl_vmmc_dev_grp		= VMMC1_DEV_GRP,
-		.twl_mmc_dedicated		= VMMC1_DEDICATED,
-	},
-	{
-		.twl_vmmc_dev_grp		= VMMC2_DEV_GRP,
-		.twl_mmc_dedicated		= VMMC2_DEDICATED,
-	},
-};
+	/* Vcc == configured supply
+	 * Vcc_alt == optional
+	 *   -	MMC1, supply for DAT4..DAT7
+	 *   -	MMC2/MMC2, external level shifter voltage supply, for
+	 *	chip (SDIO, eMMC, etc) or transceiver (MMC2 only)
+	 */
+	struct regulator		*vcc;
+	struct regulator		*vcc_aux;
+	char				name[HSMMC_NAME_LEN + 1];
+} hsmmc[OMAP34XX_NR_MMC];
 
 static int twl_mmc_card_detect(int irq)
 {
@@ -117,16 +92,42 @@  static int twl_mmc_late_init(struct devi
 	int ret = 0;
 	int i;
 
-	ret = gpio_request(mmc->slots[0].switch_pin, "mmc_cd");
-	if (ret)
-		goto done;
-	ret = gpio_direction_input(mmc->slots[0].switch_pin);
-	if (ret)
-		goto err;
+	/* MMC/SD/SDIO doesn't require a card detect switch */
+	if (gpio_is_valid(mmc->slots[0].switch_pin)) {
+		ret = gpio_request(mmc->slots[0].switch_pin, "mmc_cd");
+		if (ret)
+			goto done;
+		ret = gpio_direction_input(mmc->slots[0].switch_pin);
+		if (ret)
+			goto err;
+	}
 
+	/* require at least main regulator */
 	for (i = 0; i < ARRAY_SIZE(hsmmc); i++) {
 		if (hsmmc[i].name == mmc->slots[0].name) {
+			struct regulator *reg;
+
 			hsmmc[i].mmc = mmc;
+
+			reg = regulator_get(dev, "vmmc");
+			if (IS_ERR(reg)) {
+				dev_dbg(dev, "vmmc regulator missing\n");
+				/* HACK: until fixed.c regulator is usable,
+				 * we don't require a main regulator
+				 * for MMC2 or MMC3
+				 */
+				if (i != 0)
+					break;
+				ret = PTR_ERR(reg);
+				goto err;
+			}
+			hsmmc[i].vcc = reg;
+			mmc->slots[0].ocr_mask = mmc_regulator_get_ocrmask(reg);
+
+			/* allow an aux regulator */
+			reg = regulator_get(dev, "vmmc_aux");
+			hsmmc[i].vcc_aux = IS_ERR(reg) ? NULL : reg;
+
 			break;
 		}
 	}
@@ -173,96 +174,6 @@  static int twl_mmc_resume(struct device 
 #define twl_mmc_resume	NULL
 #endif
 
-/*
- * Sets the MMC voltage in twl4030
- */
-
-#define MMC1_OCR	(MMC_VDD_165_195 \
-		|MMC_VDD_28_29|MMC_VDD_29_30|MMC_VDD_30_31|MMC_VDD_31_32)
-#define MMC2_OCR	(MMC_VDD_165_195 \
-		|MMC_VDD_25_26|MMC_VDD_26_27|MMC_VDD_27_28 \
-		|MMC_VDD_28_29|MMC_VDD_29_30|MMC_VDD_30_31|MMC_VDD_31_32)
-
-static int twl_mmc_set_voltage(struct twl_mmc_controller *c, int vdd)
-{
-	int ret;
-	u8 vmmc = 0, dev_grp_val;
-
-	if (!vdd)
-		goto doit;
-
-	if (c->twl_vmmc_dev_grp == VMMC1_DEV_GRP) {
-		/* VMMC1:  max 220 mA.  And for 8-bit mode,
-		 * VSIM:  max 50 mA
-		 */
-		switch (1 << vdd) {
-		case MMC_VDD_165_195:
-			vmmc = VMMC1_185V;
-			/* and VSIM_180V */
-			break;
-		case MMC_VDD_28_29:
-			vmmc = VMMC1_285V;
-			/* and VSIM_280V */
-			break;
-		case MMC_VDD_29_30:
-		case MMC_VDD_30_31:
-			vmmc = VMMC1_300V;
-			/* and VSIM_300V */
-			break;
-		case MMC_VDD_31_32:
-			vmmc = VMMC1_315V;
-			/* error if VSIM needed */
-			break;
-		default:
-			return -EINVAL;
-		}
-	} else if (c->twl_vmmc_dev_grp == VMMC2_DEV_GRP) {
-		/* VMMC2:  max 100 mA */
-		switch (1 << vdd) {
-		case MMC_VDD_165_195:
-			vmmc = VMMC2_185V;
-			break;
-		case MMC_VDD_25_26:
-		case MMC_VDD_26_27:
-			vmmc = VMMC2_260V;
-			break;
-		case MMC_VDD_27_28:
-			vmmc = VMMC2_280V;
-			break;
-		case MMC_VDD_28_29:
-			vmmc = VMMC2_285V;
-			break;
-		case MMC_VDD_29_30:
-		case MMC_VDD_30_31:
-			vmmc = VMMC2_300V;
-			break;
-		case MMC_VDD_31_32:
-			vmmc = VMMC2_315V;
-			break;
-		default:
-			return -EINVAL;
-		}
-	} else {
-		return -EINVAL;
-	}
-
-doit:
-	if (vdd)
-		dev_grp_val = VMMC_DEV_GRP_P1;	/* Power up */
-	else
-		dev_grp_val = LDO_CLR;		/* Power down */
-
-	ret = twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
-					dev_grp_val, c->twl_vmmc_dev_grp);
-	if (ret || !vdd)
-		return ret;
-
-	ret = twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
-					vmmc, c->twl_mmc_dedicated);
-
-	return ret;
-}
-
 static int twl_mmc1_set_power(struct device *dev, int slot, int power_on,
 				int vdd)
 {
@@ -273,11 +184,13 @@  static int twl_mmc1_set_power(struct dev
 
 	/*
 	 * Assume we power both OMAP VMMC1 (for CMD, CLK, DAT0..3) and the
-	 * card using the same TWL VMMC1 supply (hsmmc[0]); OMAP has both
+	 * card with Vcc regulator (from twl4030 or whatever).  OMAP has both
 	 * 1.8V and 3.0V modes, controlled by the PBIAS register.
 	 *
 	 * In 8-bit modes, OMAP VMMC1A (for DAT4..7) needs a supply, which
 	 * is most naturally TWL VSIM; those pins also use PBIAS.
+	 *
+	 * FIXME handle VMMC1A as needed ...
 	 */
 	if (power_on) {
 		if (cpu_is_omap2430()) {
@@ -300,7 +213,7 @@  static int twl_mmc1_set_power(struct dev
 		reg &= ~OMAP2_PBIASLITEPWRDNZ0;
 		omap_ctrl_writel(reg, control_pbias_offset);
 
-		ret = twl_mmc_set_voltage(c, vdd);
+		ret = mmc_regulator_set_ocr(c->vcc, vdd);
 
 		/* 100ms delay required for PBIAS configuration */
 		msleep(100);
@@ -316,7 +229,7 @@  static int twl_mmc1_set_power(struct dev
 		reg &= ~OMAP2_PBIASLITEPWRDNZ0;
 		omap_ctrl_writel(reg, control_pbias_offset);
 
-		ret = twl_mmc_set_voltage(c, 0);
+		ret = mmc_regulator_set_ocr(c->vcc, 0);
 
 		/* 100ms delay required for PBIAS configuration */
 		msleep(100);
@@ -329,19 +242,33 @@  static int twl_mmc1_set_power(struct dev
 	return ret;
 }
 
-static int twl_mmc2_set_power(struct device *dev, int slot, int power_on, int vdd)
+static int twl_mmc23_set_power(struct device *dev, int slot, int power_on, int vdd)
 {
-	int ret;
+	int ret = 0;
 	struct twl_mmc_controller *c = &hsmmc[1];
 	struct omap_mmc_platform_data *mmc = dev->platform_data;
 
+	/* If we don't see a Vcc regulator, assume it's a fixed
+	 * voltage always-on regulator.
+	 */
+	if (!c->vcc)
+		return 0;
+
 	/*
-	 * Assume TWL VMMC2 (hsmmc[1]) is used only to power the card ... OMAP
+	 * Assume Vcc regulator is used only to power the card ... OMAP
 	 * VDDS is used to power the pins, optionally with a transceiver to
 	 * support cards using voltages other than VDDS (1.8V nominal).  When a
 	 * transceiver is used, DAT3..7 are muxed as transceiver control pins.
+	 *
+	 * In some cases this regulator won't support enable/disable;
+	 * e.g. it's a fixed rail for a WLAN chip.
+	 *
+	 * In other cases vcc_aux switches interface power.  Example, for
+	 * eMMC cards it represents VccQ.  Sometimes transceivers or SDIO
+	 * chips/cards need an interface voltage rail too.
 	 */
 	if (power_on) {
+		/* only MMC2 supports a CLKIN */
 		if (mmc->slots[0].internal_clock) {
 			u32 reg;
 
@@ -349,24 +276,23 @@  static int twl_mmc2_set_power(struct dev
 			reg |= OMAP2_MMCSDIO2ADPCLKISEL;
 			omap_ctrl_writel(reg, control_devconf1_offset);
 		}
-		ret = twl_mmc_set_voltage(c, vdd);
+		ret = mmc_regulator_set_ocr(c->vcc, vdd);
+		/* enable interface voltage rail, if needed */
+		if (ret == 0 && c->vcc_aux) {
+			ret = regulator_enable(c->vcc_aux);
+			if (ret < 0)
+				ret = mmc_regulator_set_ocr(c->vcc, 0);
+		}
 	} else {
-		ret = twl_mmc_set_voltage(c, 0);
+		if (c->vcc_aux)
+			ret = regulator_enable(c->vcc_aux);
+		if (ret == 0)
+			ret = mmc_regulator_set_ocr(c->vcc, 0);
 	}
 
 	return ret;
 }
 
-static int twl_mmc3_set_power(struct device *dev, int slot, int power_on,
-		int vdd)
-{
-	/*
-	 * Assume MMC3 has self-powered device connected, for example on-board
-	 * chip with external power source.
-	 */
-	return 0;
-}
-
 static struct omap_mmc_platform_data *hsmmc_data[OMAP34XX_NR_MMC] __initdata;
 
 void __init twl4030_mmc_init(struct twl4030_hsmmc_info *controllers)
@@ -411,10 +337,10 @@  void __init twl4030_mmc_init(struct twl4
 		mmc->slots[0].wires = c->wires;
 		mmc->slots[0].internal_clock = !c->ext_clock;
 		mmc->dma_mask = 0xffffffff;
+		mmc->init = twl_mmc_late_init;
 
-		/* note: twl4030 card detect GPIOs normally switch VMMCx ... */
+		/* note: twl4030 card detect GPIOs can disable VMMCx ... */
 		if (gpio_is_valid(c->gpio_cd)) {
-			mmc->init = twl_mmc_late_init;
 			mmc->cleanup = twl_mmc_cleanup;
 			mmc->suspend = twl_mmc_suspend;
 			mmc->resume = twl_mmc_resume;
@@ -438,26 +364,28 @@  void __init twl4030_mmc_init(struct twl4
 		} else
 			mmc->slots[0].gpio_wp = -EINVAL;
 
-		/* NOTE:  we assume OMAP's MMC1 and MMC2 use
-		 * the TWL4030's VMMC1 and VMMC2, respectively;
-		 * and that MMC3 device has it's own power source.
+		/* NOTE:  MMC slots should have a Vcc regulator set up.
+		 * This may be from a TWL4030-family chip, another
+		 * controllable regulator, or a fixed supply.
+		 *
+		 * temporary HACK: ocr_mask instead of fixed supply
 		 */
+		mmc->slots[0].ocr_mask = c->ocr_mask;
 
 		switch (c->mmc) {
 		case 1:
+			/* on-chip level shifting via PBIAS0/PBIAS1 */
 			mmc->slots[0].set_power = twl_mmc1_set_power;
-			mmc->slots[0].ocr_mask = MMC1_OCR;
 			break;
 		case 2:
-			mmc->slots[0].set_power = twl_mmc2_set_power;
-			if (c->transceiver)
-				mmc->slots[0].ocr_mask = MMC2_OCR;
-			else
-				mmc->slots[0].ocr_mask = MMC_VDD_165_195;
-			break;
+			if (c->ext_clock)
+				c->transceiver = 1;
+			if (c->transceiver && c->wires > 4)
+				c->wires = 4;
+			/* FALLTHROUGH */
 		case 3:
-			mmc->slots[0].set_power = twl_mmc3_set_power;
-			mmc->slots[0].ocr_mask = MMC_VDD_165_195;
+			/* off-chip level shifting, or none */
+			mmc->slots[0].set_power = twl_mmc23_set_power;
 			break;
 		default:
 			pr_err("MMC%d configuration not supported!\n", c->mmc);
--- a/arch/arm/mach-omap2/mmc-twl4030.h
+++ b/arch/arm/mach-omap2/mmc-twl4030.h
@@ -16,9 +16,10 @@  struct twl4030_hsmmc_info {
 	int	gpio_wp;	/* or -EINVAL */
 	char	*name;		/* or NULL for default */
 	struct device *dev;	/* returned: pointer to mmc adapter */
+	int	ocr_mask;	/* temporary HACK */
 };
 
-#if	defined(CONFIG_TWL4030_CORE) && \
+#if defined(CONFIG_REGULATOR) && \
 	(defined(CONFIG_MMC_OMAP) || defined(CONFIG_MMC_OMAP_MODULE) || \
 	 defined(CONFIG_MMC_OMAP_HS) || defined(CONFIG_MMC_OMAP_HS_MODULE))
 
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -1005,7 +1005,6 @@  static int __init omap_mmc_probe(struct 
 	mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
 	mmc->max_seg_size = mmc->max_req_size;
 
-	mmc->ocr_avail = mmc_slot(host).ocr_mask;
 	mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED;
 
 	if (pdata->slots[host->slot_id].wires >= 4)
@@ -1042,13 +1041,14 @@  static int __init omap_mmc_probe(struct 
 		goto err_irq;
 	}
 
+	/* initialize power supplies, gpios, etc */
 	if (pdata->init != NULL) {
 		if (pdata->init(&pdev->dev) != 0) {
-			dev_dbg(mmc_dev(host->mmc),
-				"Unable to configure MMC IRQs\n");
+			dev_dbg(mmc_dev(host->mmc), "late init error\n");
 			goto err_irq_cd_init;
 		}
 	}
+	mmc->ocr_avail = mmc_slot(host).ocr_mask;
 
 	/* Request IRQ for card detect */
 	if ((mmc_slot(host).card_detect_irq) && (mmc_slot(host).card_detect)) {