=================
Prototype glue between MMC and regulator stacks ... compiles,
and mmc_regulator_get_ocrmask() passed sanity testing.

NOTES:

 - The MMC core does't call mmc_regulator_set_ocr() because hosts
   may need to do that in conjunction with updating I/O voltage.

   Case in point, MMC1 on omap_hsmmc ... where the host driver
   must update MMC1_HCTL.SDVS and PBIAS registers in addition to
   the regulator, supporting 1.8V or 3.0V voltage ranges.  (MMC2
   and MMC3 use external level shifting for Vdd != 1.8V.)

   Likewise, using eMMC "managed NAND" solutions, powerup includes
   not both Vcc ("vdd" to Linux, e.g. 3.0V) and an I/O interface
   rail VccQ (e.g. 1.8V).  The JEDEC spec for eMMC requires VccQ
   powerup after Vcc, and powerdown before it.

 - The "vdd" supply name isn't fixed, since platforms may need
   to use more than one I/O supply.

   Case in point, MMC1 on omap_hsmmc (again) ... where a second
   supply is needed to kick in 8-bit I/O using DAT4..DAT7 signals.
   That would not be handled quite like VccQ, since it's only
   used for 8-bit I/O widths (MMCplus cards, some eMMC, etc).
 
---
 drivers/mmc/core/Kconfig |    8 +++
 drivers/mmc/core/core.c  |   98 +++++++++++++++++++++++++++++++++++++++++++++
 include/linux/mmc/host.h |    3 +
 3 files changed, 109 insertions(+)

--- a/drivers/mmc/core/Kconfig
+++ b/drivers/mmc/core/Kconfig
@@ -14,3 +14,11 @@ config MMC_UNSAFE_RESUME
 	  This option is usually just for embedded systems which use
 	  a MMC/SD card for rootfs. Most people should say N here.
 
+config MMC_REGULATOR
+	bool
+	depends on REGULATOR
+	default y
+	help
+	  Select this to provide some helper utilities to access the
+	  "vdd" (card) voltage supply associated with an MMC/SD slot.
+
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -21,6 +21,7 @@
 #include <linux/leds.h>
 #include <linux/scatterlist.h>
 #include <linux/log2.h>
+#include <linux/regulator/consumer.h>
 
 #include <linux/mmc/card.h>
 #include <linux/mmc/host.h>
@@ -523,6 +524,103 @@ u32 mmc_vddrange_to_ocrmask(int vdd_min,
 }
 EXPORT_SYMBOL(mmc_vddrange_to_ocrmask);
 
+#ifdef CONFIG_MMC_REGULATOR
+
+/**
+ * mmc_regulator_get_ocrmask - return mask of supported voltages
+ * @host: mmc host whose supply will be consulted
+ * @supply: supply voltage to use; "vdd" if NULL
+ *
+ * This returns either a negative errno, or a mask of voltages
+ * that can be provided to MMC/SD/SDIO devices using the specified
+ * host's "vdd" supply.
+ */
+int mmc_regulator_get_ocrmask(struct mmc_host *host, const char *supply)
+{
+	int			result = 0;
+	struct regulator	*reg;
+	int			count;
+	int			i;
+
+	reg = regulator_get(host->parent, supply ? : "vdd");
+	if (IS_ERR(reg))
+		return PTR_ERR(reg);
+
+	count = regulator_count_voltages(reg);
+	if (count < 0) {
+		result = count;
+		goto done;
+	}
+
+	for (i = 0; i < count; i++) {
+		int		vdd_uV;
+		int		vdd_mV;
+
+		vdd_uV = regulator_list_voltage(reg, i);
+		if (vdd_uV <= 0)
+			continue;
+
+		vdd_mV = vdd_uV / 1000;
+		result |= mmc_vddrange_to_ocrmask(vdd_mV, vdd_mV);
+	}
+
+done:
+	regulator_put(reg);
+	return result;
+}
+EXPORT_SYMBOL(mmc_regulator_get_ocrmask);
+
+/**
+ * mmc_regulator_set_ocr - set regulator to match host->ios voltage
+ * @host: mmc host whose supply voltage will be changed
+ * @supply: supply voltage to use; "vdd" if NULL
+ *
+ * MMC host drivers may use this to enable or disable a regulator
+ * using a particular supply voltage.  This would normally be
+ * called from the set_ios() method, possibly as part of updating
+ * digital interfaces to support that voltage.
+ */
+int mmc_regulator_set_ocr(struct mmc_host *host, const char *supply)
+{
+	int			result = 0;
+	struct regulator	*reg;
+	int			min_mV, max_mV;
+	int			enabled;
+
+	reg = regulator_get(host->parent, supply ? : "vdd");
+	if (IS_ERR(reg))
+		return PTR_ERR(reg);
+	enabled = regulator_is_enabled(reg);
+	if (WARN(enabled < 0, "%s: regulator_is_enabled --> %d\n",
+			mmc_hostname(host), enabled))
+		enabled = !host->ios.vdd;
+
+	if (host->ios.vdd) {
+		int		tmp;
+
+		tmp = host->ios.vdd - ilog2(MMC_VDD_165_195);
+		if (tmp == 0) {
+			min_mV = 1650;
+			max_mV = 1950;
+		} else {
+			min_mV = 2000 + tmp * 100;
+			max_mV = min_mV + 100;
+		}
+
+		result = regulator_set_voltage(reg, min_mV * 1000, max_mV * 1000);
+		if (result == 0 && !enabled)
+			result = regulator_enable(reg);
+	} else if (enabled) {
+		result = regulator_disable(reg);
+	}
+
+	regulator_put(reg);
+	return result;
+}
+EXPORT_SYMBOL(mmc_regulator_set_ocr);
+
+#endif
+
 /*
  * Mask off any voltages we don't support and select
  * the lowest voltage
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -192,5 +192,8 @@ static inline void mmc_signal_sdio_irq(s
 	wake_up_process(host->sdio_irq_thread);
 }
 
+int mmc_regulator_get_ocrmask(struct mmc_host *host, const char *supply);
+int mmc_regulator_set_ocr(struct mmc_host *host, const char *supply);
+
 #endif
 
