summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdrian Hunter <adrian.hunter@intel.com>2014-11-06 15:19:06 +0200
committerNitin Garg <nitin.garg@freescale.com>2015-04-14 14:02:36 -0500
commite7c7011f01ccf2260f0d754cf168fa8da5d8cf0f (patch)
treeb9729f593a5ca8ac24214b55d5c91061b1155b80
parent596a7237a57236d940f1f17ee7837ad91a04c994 (diff)
mmc: sdhci: Add HS400 support to SDHCI driver
MMC core already has support for HS400. Add HS400 support to SDHCI driver. The SDHC Standard specification does not define HS400 so consequently HS400 support is non-standard. However HS400 is not selected without the host controller setting the corresponding capability flags so host controllers not yet supporting HS400 will not be affected. To support that, a quirk SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400 is introduced to enable the use of capabilities register reserved bit-63 to indicate HS400 support. Because HS400 is non-standard for SDHCI, it is possible that different vendors will do things in different ways. However HS200 support faced the same issue but currently there is only one solution. As such, no attempt has been made to provide for alternate HS400 solutions except for SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400. Conflicts: drivers/mmc/host/sdhci.c drivers/mmc/host/sdhci.h include/linux/mmc/host.h include/linux/mmc/sdhci.h Signed-off-by: Adrian Hunter <adrian.hunter@intel.com> Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org> (cherry picked from commit e9fb05d5bca7428f2749d059559e9657c710fe53)
-rw-r--r--drivers/mmc/host/sdhci.c35
-rw-r--r--drivers/mmc/host/sdhci.h3
-rw-r--r--include/linux/mmc/host.h2
-rw-r--r--include/linux/mmc/sdhci.h3
4 files changed, 40 insertions, 3 deletions
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index b945bae40f7b..8c61394d92f6 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1130,6 +1130,9 @@ static u16 sdhci_get_preset_value(struct sdhci_host *host)
case SDHCI_CTRL_UHS_DDR50:
preset = sdhci_readw(host, SDHCI_PRESET_FOR_DDR50);
break;
+ case SDHCI_CTRL_HS400:
+ preset = sdhci_readw(host, SDHCI_PRESET_FOR_HS400);
+ break;
default:
pr_warn("%s: Invalid UHS-I mode selected\n",
mmc_hostname(host->mmc));
@@ -1513,7 +1516,8 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
u16 clk, ctrl_2;
/* In case of UHS-I modes, set High Speed Enable */
- if ((ios->timing == MMC_TIMING_MMC_HS200) ||
+ if ((ios->timing == MMC_TIMING_MMC_HS400) ||
+ (ios->timing == MMC_TIMING_MMC_HS200) ||
(ios->timing == MMC_TIMING_UHS_SDR50) ||
(ios->timing == MMC_TIMING_UHS_SDR104) ||
(ios->timing == MMC_TIMING_UHS_DDR50) ||
@@ -1576,6 +1580,8 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
else if (ios->timing == MMC_TIMING_UHS_DDR50)
ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
+ else if (ios->timing == MMC_TIMING_MMC_HS400)
+ ctrl_2 |= SDHCI_CTRL_HS400;
sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
}
@@ -1863,6 +1869,18 @@ static int sdhci_card_busy(struct mmc_host *mmc)
return !(present_state & SDHCI_DATA_LVL_MASK);
}
+static int sdhci_prepare_hs400_tuning(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ unsigned long flags;
+
+ spin_lock_irqsave(&host->lock, flags);
+ host->flags |= SDHCI_HS400_TUNING;
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ return 0;
+}
+
static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
{
struct sdhci_host *host;
@@ -2200,6 +2218,7 @@ static const struct mmc_host_ops sdhci_ops = {
.hw_reset = sdhci_hw_reset,
.enable_sdio_irq = sdhci_enable_sdio_irq,
.start_signal_voltage_switch = sdhci_start_signal_voltage_switch,
+ .prepare_hs400_tuning = sdhci_prepare_hs400_tuning,
.execute_tuning = sdhci_execute_tuning,
.card_event = sdhci_card_event,
.card_busy = sdhci_card_busy,
@@ -3102,8 +3121,8 @@ int sdhci_add_host(struct sdhci_host *host)
}
if (host->quirks2 & SDHCI_QUIRK2_NO_1_8_V)
- caps[1] &= ~(SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50 |
- SDHCI_SUPPORT_DDR50);
+ caps[1] &= ~(SDHCI_SUPPORT_HS400 | SDHCI_SUPPORT_SDR104 |
+ SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_DDR50);
/* Any UHS-I mode in caps implies SDR12 and SDR25 support. */
if (caps[1] & (SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50 |
@@ -3121,6 +3140,16 @@ int sdhci_add_host(struct sdhci_host *host)
} else if (caps[1] & SDHCI_SUPPORT_SDR50)
mmc->caps |= MMC_CAP_UHS_SDR50;
+ if (host->quirks2 & SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400 &&
+ (caps[1] & SDHCI_SUPPORT_HS400))
+ mmc->caps2 |= MMC_CAP2_HS400;
+
+ if ((mmc->caps2 & MMC_CAP2_HSX00_1_2V) &&
+ (IS_ERR_OR_NULL(host->vqmmc) ||
+ !regulator_is_supported_voltage(host->vqmmc, 1100000,
+ 1300000)))
+ mmc->caps2 &= ~MMC_CAP2_HSX00_1_2V;
+
if (caps[1] & SDHCI_SUPPORT_DDR50)
mmc->caps |= MMC_CAP_UHS_DDR50;
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 51bd04abddc9..6e681bf34d5b 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -161,6 +161,7 @@
#define SDHCI_CTRL_UHS_SDR104 0x0003
#define SDHCI_CTRL_UHS_DDR50 0x0004
#define SDHCI_CTRL_HS_SDR200 0x0005 /* reserved value in SDIO spec */
+#define SDHCI_CTRL_HS400 0x0006
#define SDHCI_CTRL_VDD_180 0x0008
#define SDHCI_CTRL_DRV_TYPE_MASK 0x0030
#define SDHCI_CTRL_DRV_TYPE_B 0x0000
@@ -203,6 +204,7 @@
#define SDHCI_RETUNING_MODE_SHIFT 14
#define SDHCI_CLOCK_MUL_MASK 0x00FF0000
#define SDHCI_CLOCK_MUL_SHIFT 16
+#define SDHCI_SUPPORT_HS400 0x80000000 /* Non-standard */
#define SDHCI_CAPABILITIES_1 0x44
@@ -234,6 +236,7 @@
#define SDHCI_PRESET_FOR_SDR50 0x6A
#define SDHCI_PRESET_FOR_SDR104 0x6C
#define SDHCI_PRESET_FOR_DDR50 0x6E
+#define SDHCI_PRESET_FOR_HS400 0x74 /* Non-standard */
#define SDHCI_PRESET_DRV_MASK 0xC000
#define SDHCI_PRESET_DRV_SHIFT 14
#define SDHCI_PRESET_CLKGEN_SEL_MASK 0x400
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 31525c9ae53d..b4945d11f6f8 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -291,6 +291,8 @@ struct mmc_host {
#define MMC_CAP2_HS400_1_2V_DDR (1 << 17) /* Can support HS400 1.2V */
#define MMC_CAP2_HS400 (MMC_CAP2_HS400_1_8V_DDR | \
MMC_CAP2_HS400_1_2V_DDR)
+#define MMC_CAP2_HSX00_1_2V (MMC_CAP2_HS200_1_2V_SDR | \
+ MMC_CAP2_HS400_1_2V_DDR)
mmc_pm_flag_t pm_caps; /* supported pm features */
diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h
index 28914bb9c013..0fdff063287f 100644
--- a/include/linux/mmc/sdhci.h
+++ b/include/linux/mmc/sdhci.h
@@ -106,6 +106,8 @@ struct sdhci_host {
#define SDHCI_QUIRK2_BROKEN_HOST_CONTROL (1<<5)
/* Controller does not support HS200 */
#define SDHCI_QUIRK2_BROKEN_HS200 (1<<6)
+/* Capability register bit-63 indicates HS400 support */
+#define SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400 (1<<7)
int irq; /* Device IRQ */
void __iomem *ioaddr; /* Mapped address */
@@ -139,6 +141,7 @@ struct sdhci_host {
#define SDHCI_SDIO_IRQ_ENABLED (1<<9) /* SDIO irq enabled */
#define SDHCI_SDR104_NEEDS_TUNING (1<<10) /* SDR104/HS200 needs tuning */
#define SDHCI_USING_RETUNING_TIMER (1<<11) /* Host is using a retuning timer for the card */
+#define SDHCI_HS400_TUNING (1<<12) /* Tuning for HS400 */
unsigned int version; /* SDHCI spec. version */