diff options
Diffstat (limited to 'drivers/mmc')
-rw-r--r-- | drivers/mmc/core/sd.c | 11 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci.c | 71 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci.h | 7 |
3 files changed, 57 insertions, 32 deletions
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 206b21f9797a..53735815e95e 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -349,6 +349,8 @@ static int mmc_read_switch(struct mmc_card *card) if (status[13] & 0x08) /* UHS104 mode */ card->sw_caps.hs_max_dtr = 208000000; + else if (status[13] & 0x10) /* DDR50 mode */ + card->sw_caps.hs_max_dtr = 50000000; else if (status[13] & 0x04) /* UHS50 mode */ card->sw_caps.hs_max_dtr = 104000000; else if (status[13] & 0x02) /* high speed mode */ @@ -356,7 +358,6 @@ static int mmc_read_switch(struct mmc_card *card) out: kfree(status); - return err; } @@ -486,12 +487,10 @@ int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid) ocr |= 1 << 30; /* - * Check the voltage switching capability if the host supports SDR - * or DDR modes. + * Check voltage switching capability of the card if the host supports it. */ - if (((host->caps & MMC_CAP_SDR50) || (host->caps & MMC_CAP_SDR104) || - (host->caps & MMC_CAP_DDR50))&& (!mmc_host_is_spi(host))) - ocr |= (1 << 24); + if ((host->caps & MMC_CAP_VOLTAGE_SWITCHING) && (!mmc_host_is_spi(host))) + ocr |= (1 << 24); err = mmc_send_app_op_cond(host, ocr, &rocr); if (err) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 1e16944ad2e4..153866bc8e0e 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -176,10 +176,11 @@ static void sdhci_reset(struct sdhci_host *host, u8 mask) if (host->quirks & SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET) sdhci_clear_set_irqs(host, SDHCI_INT_ALL_MASK, ier); - if (mask & SDHCI_RESET_ALL) + if (mask & SDHCI_RESET_ALL) { host->ops->configure_capabilities(host); - - host->uhs_mode = 0; + host->uhs_mode = 0; + host->mmc->ios.signalling_voltage = MMC_3_3_VOLT_SIGNALLING; + } } static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios); @@ -1256,27 +1257,6 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); } - if ((ios->signalling_voltage == MMC_1_8_VOLT_SIGNALLING) && - (ios->signalling_voltage != host->signalling_voltage)) { - /* Switch OFF SD clock */ - clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); - clk &= (~SDHCI_CLOCK_CARD_EN); - sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); - - ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL_2); - ctrl |= SDHCI_CTRL_2_VOLT_18_EN; - sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL_2); - - /* Wait for 5msec for the output to be stable */ - mdelay(5); - - /* Switch ON sd clock */ - clk |= SDHCI_CLOCK_CARD_EN; - sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); - - host->signalling_voltage = ios->signalling_voltage; - } - if (ios->tuning_arg) { switch(ios->tuning_arg) { case MMC_EXECUTE_TUNING: @@ -1304,6 +1284,36 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) } } + if (ios->signalling_voltage != host->signalling_voltage) { + /* Switch OFF SD clock */ + clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); + clk &= (~SDHCI_CLOCK_CARD_EN); + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); + + if (ios->signalling_voltage == MMC_1_8_VOLT_SIGNALLING) { + ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL_2); + ctrl |= SDHCI_CTRL_2_VOLT_18_EN; + sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL_2); + } + + if (host->quirks & SDHCI_QUIRK_BROKEN_VOLTAGE_SWITCHING) { + spin_unlock_irqrestore(&host->lock, flags); + if (host->ops->set_signalling_voltage) { + host->ops->set_signalling_voltage(host, + ios->signalling_voltage); + } + spin_lock_irqsave(&host->lock, flags); + } + + /* Wait for 5msec for the output to be stable */ + mdelay(5); + + /* Switch ON sd clock */ + clk |= SDHCI_CLOCK_CARD_EN; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); + + host->signalling_voltage = ios->signalling_voltage; + } out: mmiowb(); spin_unlock_irqrestore(&host->lock, flags); @@ -1878,7 +1888,7 @@ int sdhci_add_host(struct sdhci_host *host) >> SDHCI_SPEC_VER_SHIFT; } - if (host->version > SDHCI_SPEC_200) { + if (host->version > SDHCI_SPEC_300) { printk(KERN_ERR "%s: Unknown controller version (%d). " "You may experience problems.\n", mmc_hostname(mmc), host->version); @@ -2099,6 +2109,17 @@ int sdhci_add_host(struct sdhci_host *host) if (caps & SDHCI_CAN_SDR50_TUNING) mmc->caps |= MMC_CAP_SDR50_TUNING; + if ((caps & SDHCI_CAN_SUPPORT_DDR50) || (caps & SDHCI_CAN_SUPPORT_SDR104) || + (caps & SDHCI_CAN_SUPPORT_SDR50)) { + if (!(host->quirks & SDHCI_QUIRK_BROKEN_VOLTAGE_SWITCHING)) + mmc->caps |= MMC_CAP_VOLTAGE_SWITCHING; + else { + /* Do the voltage switching using a regulator */ + if (host->ops->set_signalling_voltage) + mmc->caps |= MMC_CAP_VOLTAGE_SWITCHING; + } + } + /* * Init tasklets. */ diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 69576e84fa3d..9dfa40cf393c 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -147,7 +147,7 @@ #define SDHCI_TIMEOUT_CLK_MASK 0x0000003F #define SDHCI_TIMEOUT_CLK_SHIFT 0 #define SDHCI_TIMEOUT_CLK_UNIT 0x00000080 -#define SDHCI_CLOCK_BASE_MASK 0x00003F00 +#define SDHCI_CLOCK_BASE_MASK 0x0000FF00 #define SDHCI_CLOCK_BASE_SHIFT 8 #define SDHCI_MAX_BLOCK_MASK 0x00030000 #define SDHCI_MAX_BLOCK_SHIFT 16 @@ -190,6 +190,7 @@ #define SDHCI_SPEC_VER_SHIFT 0 #define SDHCI_SPEC_100 0 #define SDHCI_SPEC_200 1 +#define SDHCI_SPEC_300 2 struct sdhci_ops; @@ -277,6 +278,8 @@ struct sdhci_host { #define SDHCI_QUIRK_FORCE_HIGH_SPEED_MODE (1LL<<37) /* Controller allows runtime enable / disable */ #define SDHCI_QUIRK_RUNTIME_DISABLE (1LL<<38) +/* Controller cannot switch signalling voltage automatically */ +#define SDHCI_QUIRK_BROKEN_VOLTAGE_SWITCHING (1LL<<39) int irq; /* Device IRQ */ void __iomem * ioaddr; /* Mapped address */ @@ -351,6 +354,8 @@ struct sdhci_ops { void (*set_clock)(struct sdhci_host *host, unsigned int clock); void (*configure_capabilities)(struct sdhci_host *host); + void (*set_signalling_voltage)(struct sdhci_host *host, + unsigned int signalling_voltage); int (*enable_dma)(struct sdhci_host *host); int (*get_ro)(struct sdhci_host *host); |