Commit eb250826 authored by David Brownell's avatar David Brownell Committed by Pierre Ossman

omap_hsmmc: only MMC1 allows HCTL.SDVS != 1.8V

Based on a patch from Tony Lindgren ... after initialization,
never change HCTL.SDVS except for MMC1.  The other controller
instances only support 1.8V in that field, although they can
suport other card/SDIO/eMMC/... voltages with level shifting
solutions such as external transceivers.

MMC2 behavior sanity tested on Overo/WLAN, OMAP3430 SDP, and
custom hardware.  MMC1 also sanity tested on those platforms
plus Beagle.  This also fixes a bug preventing MMC2 (and also
presumably MMC3) from powering down when requested.
Signed-off-by: default avatarDavid Brownell <dbrownell@users.sourceforge.net>
Acked-by: default avatarTony Lindgren <tony@atomide.com>
Signed-off-by: default avatarPierre Ossman <drzeus@drzeus.cx>
parent 249d0fa9
...@@ -55,6 +55,7 @@ ...@@ -55,6 +55,7 @@
#define VS30 (1 << 25) #define VS30 (1 << 25)
#define SDVS18 (0x5 << 9) #define SDVS18 (0x5 << 9)
#define SDVS30 (0x6 << 9) #define SDVS30 (0x6 << 9)
#define SDVS33 (0x7 << 9)
#define SDVSCLR 0xFFFFF1FF #define SDVSCLR 0xFFFFF1FF
#define SDVSDET 0x00000400 #define SDVSDET 0x00000400
#define AUTOIDLE 0x1 #define AUTOIDLE 0x1
...@@ -456,13 +457,20 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id) ...@@ -456,13 +457,20 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
} }
/* /*
* Switch MMC operating voltage * Switch MMC interface voltage ... only relevant for MMC1.
*
* MMC2 and MMC3 use fixed 1.8V levels, and maybe a transceiver.
* The MMC2 transceiver controls are used instead of DAT4..DAT7.
* Some chips, like eMMC ones, use internal transceivers.
*/ */
static int omap_mmc_switch_opcond(struct mmc_omap_host *host, int vdd) static int omap_mmc_switch_opcond(struct mmc_omap_host *host, int vdd)
{ {
u32 reg_val = 0; u32 reg_val = 0;
int ret; int ret;
if (host->id != OMAP_MMC1_DEVID)
return 0;
/* Disable the clocks */ /* Disable the clocks */
clk_disable(host->fclk); clk_disable(host->fclk);
clk_disable(host->iclk); clk_disable(host->iclk);
...@@ -485,19 +493,26 @@ static int omap_mmc_switch_opcond(struct mmc_omap_host *host, int vdd) ...@@ -485,19 +493,26 @@ static int omap_mmc_switch_opcond(struct mmc_omap_host *host, int vdd)
OMAP_HSMMC_WRITE(host->base, HCTL, OMAP_HSMMC_WRITE(host->base, HCTL,
OMAP_HSMMC_READ(host->base, HCTL) & SDVSCLR); OMAP_HSMMC_READ(host->base, HCTL) & SDVSCLR);
reg_val = OMAP_HSMMC_READ(host->base, HCTL); reg_val = OMAP_HSMMC_READ(host->base, HCTL);
/* /*
* If a MMC dual voltage card is detected, the set_ios fn calls * If a MMC dual voltage card is detected, the set_ios fn calls
* this fn with VDD bit set for 1.8V. Upon card removal from the * this fn with VDD bit set for 1.8V. Upon card removal from the
* slot, omap_mmc_set_ios sets the VDD back to 3V on MMC_POWER_OFF. * slot, omap_mmc_set_ios sets the VDD back to 3V on MMC_POWER_OFF.
* *
* Only MMC1 supports 3.0V. MMC2 will not function if SDVS30 is * Cope with a bit of slop in the range ... per data sheets:
* set in HCTL. * - "1.8V" for vdds_mmc1/vdds_mmc1a can be up to 2.45V max,
* but recommended values are 1.71V to 1.89V
* - "3.0V" for vdds_mmc1/vdds_mmc1a can be up to 3.5V max,
* but recommended values are 2.7V to 3.3V
*
* Board setup code shouldn't permit anything very out-of-range.
* TWL4030-family VMMC1 and VSIM regulators are fine (avoiding the
* middle range) but VSIM can't power DAT4..DAT7 at more than 3V.
*/ */
if (host->id == OMAP_MMC1_DEVID && (((1 << vdd) == MMC_VDD_32_33) || if ((1 << vdd) <= MMC_VDD_23_24)
((1 << vdd) == MMC_VDD_33_34)))
reg_val |= SDVS30;
if ((1 << vdd) == MMC_VDD_165_195)
reg_val |= SDVS18; reg_val |= SDVS18;
else
reg_val |= SDVS30;
OMAP_HSMMC_WRITE(host->base, HCTL, reg_val); OMAP_HSMMC_WRITE(host->base, HCTL, reg_val);
...@@ -759,10 +774,14 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ...@@ -759,10 +774,14 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
case MMC_POWER_OFF: case MMC_POWER_OFF:
mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0); mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0);
/* /*
* Reset bus voltage to 3V if it got set to 1.8V earlier. * Reset interface voltage to 3V if it's 1.8V now;
* only relevant on MMC-1, the others always use 1.8V.
*
* REVISIT: If we are able to detect cards after unplugging * REVISIT: If we are able to detect cards after unplugging
* a 1.8V card, this code should not be needed. * a 1.8V card, this code should not be needed.
*/ */
if (host->id != OMAP_MMC1_DEVID)
break;
if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) { if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) {
int vdd = fls(host->mmc->ocr_avail) - 1; int vdd = fls(host->mmc->ocr_avail) - 1;
if (omap_mmc_switch_opcond(host, vdd) != 0) if (omap_mmc_switch_opcond(host, vdd) != 0)
...@@ -786,7 +805,9 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ...@@ -786,7 +805,9 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
} }
if (host->id == OMAP_MMC1_DEVID) { if (host->id == OMAP_MMC1_DEVID) {
/* Only MMC1 can operate at 3V/1.8V */ /* Only MMC1 can interface at 3V without some flavor
* of external transceiver; but they all handle 1.8V.
*/
if ((OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET) && if ((OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET) &&
(ios->vdd == DUAL_VOLT_OCR_BIT)) { (ios->vdd == DUAL_VOLT_OCR_BIT)) {
/* /*
...@@ -1139,7 +1160,9 @@ static int omap_mmc_suspend(struct platform_device *pdev, pm_message_t state) ...@@ -1139,7 +1160,9 @@ static int omap_mmc_suspend(struct platform_device *pdev, pm_message_t state)
" level suspend\n"); " level suspend\n");
} }
if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) { if (host->id == OMAP_MMC1_DEVID
&& !(OMAP_HSMMC_READ(host->base, HCTL)
& SDVSDET)) {
OMAP_HSMMC_WRITE(host->base, HCTL, OMAP_HSMMC_WRITE(host->base, HCTL,
OMAP_HSMMC_READ(host->base, HCTL) OMAP_HSMMC_READ(host->base, HCTL)
& SDVSCLR); & SDVSCLR);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment