Skip to content

Commit

Permalink
ARM: OMAP3: mmc-twl4030 uses regulator framework
Browse files Browse the repository at this point in the history
Decouple 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.

Patch updated with a fix for disabling vcc_aux by
Adrian Hunter <adrian.hunter@nokia.com>

Cc: Pierre Ossman <drzeus-list@drzeus.cx>
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Tony Lindgren <tony@atomide.com>
  • Loading branch information
David Brownell authored and Tony Lindgren committed May 28, 2009
1 parent 4a899d5 commit b583f26
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 171 deletions.
280 changes: 113 additions & 167 deletions arch/arm/mach-omap2/mmc-twl4030.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand All @@ -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;
Expand All @@ -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)
{
Expand Down Expand Up @@ -117,16 +92,60 @@ static int twl_mmc_late_init(struct device *dev)
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;

/* UGLY HACK: workaround regulator framework bugs.
* When the bootloader leaves a supply active, it's
* initialized with zero usecount ... and we can't
* disable it without first enabling it. Until the
* framework is fixed, we need a workaround like this
* (which is safe for MMC, but not in general).
*/
if (regulator_is_enabled(hsmmc[i].vcc) > 0) {
regulator_enable(hsmmc[i].vcc);
regulator_disable(hsmmc[i].vcc);
}
if (hsmmc[i].vcc_aux) {
if (regulator_is_enabled(reg) > 0) {
regulator_enable(reg);
regulator_disable(reg);
}
}

break;
}
}
Expand Down Expand Up @@ -173,96 +192,6 @@ static int twl_mmc_resume(struct device *dev, int slot)
#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)
{
Expand All @@ -273,11 +202,13 @@ static int twl_mmc1_set_power(struct device *dev, int slot, int power_on,

/*
* 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()) {
Expand All @@ -300,7 +231,7 @@ static int twl_mmc1_set_power(struct device *dev, int slot, int power_on,
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);
Expand All @@ -316,7 +247,7 @@ static int twl_mmc1_set_power(struct device *dev, int slot, int power_on,
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);
Expand All @@ -329,44 +260,57 @@ static int twl_mmc1_set_power(struct device *dev, int slot, int power_on,
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;

reg = omap_ctrl_readl(control_devconf1_offset);
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_is_enabled(c->vcc_aux)) > 0)
ret = regulator_disable(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)
Expand Down Expand Up @@ -412,10 +356,10 @@ void __init twl4030_mmc_init(struct twl4030_hsmmc_info *controllers)
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;
Expand All @@ -439,26 +383,28 @@ void __init twl4030_mmc_init(struct twl4030_hsmmc_info *controllers)
} 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);
Expand Down
Loading

0 comments on commit b583f26

Please sign in to comment.