From 59072fa8fea50634f01009e51c4b5e16308ab466 Mon Sep 17 00:00:00 2001
From: Zhangfei Gao <zhangfei.gao@marvell.com>
Date: Mon, 1 Nov 2010 07:43:57 -0400
Subject: [PATCH] sdhci: support DDR50 mode
Verified DDR50 mode on sdhci-pxa and HYNIX and toshiba eMMC
Signed-off-by: Zhangfei Gao <zhangfei.gao@marvell.com>
---
drivers/mmc/host/sdhci.c | 48 +++++++++++++++++++++++++++++++++++++++++++++-
drivers/mmc/host/sdhci.h | 15 ++++++++++++-
2 files changed, 60 insertions(+), 3 deletions(-)
@@ -982,6 +982,44 @@ static void sdhci_finish_command(struct sdhci_host *host)
host->cmd = NULL;
}
+static void sdhci_set_ddr(struct sdhci_host *host, unsigned int ddr)
+{
+ u16 con;
+ unsigned long timeout;
+
+ if (ddr == MMC_SDR_MODE)
+ return;
+
+ if (host->ops->set_ddr)
+ host->ops->set_ddr(host, ddr);
+
+ /* Fixme, how to support 1.2v Mode */
+ if (ddr & MMC_1_8V_DDR_MODE) {
+ con = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+ con |= SDHCI_CTRL2_1_8V;
+ sdhci_writew(host, con, SDHCI_HOST_CONTROL2);
+
+ /* Wait max 5 ms */
+ timeout = 5;
+ while (!((con = sdhci_readw(host, SDHCI_HOST_CONTROL2))
+ & SDHCI_CTRL2_1_8V)) {
+ if (timeout == 0) {
+ printk(KERN_ERR "%s: HOST CONTROL fail switch to 1.8v\n",
+ mmc_hostname(host->mmc));
+ sdhci_dumpregs(host);
+ return;
+ }
+ timeout--;
+ mdelay(1);
+ }
+ con = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+ con &= ~SDHCI_CTRL2_UHS_MASK;
+ /* only support DDR50 */
+ con |= SDHCI_CTRL2_DDR50;
+ sdhci_writew(host, con, SDHCI_HOST_CONTROL2);
+ }
+}
+
static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
{
int div;
@@ -1176,6 +1214,7 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
}
sdhci_set_clock(host, ios->clock);
+ sdhci_set_ddr(host, ios->ddr);
if (ios->power_mode == MMC_POWER_OFF)
sdhci_set_power(host, -1);
@@ -1712,7 +1751,7 @@ EXPORT_SYMBOL_GPL(sdhci_alloc_host);
int sdhci_add_host(struct sdhci_host *host)
{
struct mmc_host *mmc;
- unsigned int caps;
+ unsigned int caps, caps_h;
int ret;
WARN_ON(host == NULL);
@@ -1737,6 +1776,13 @@ int sdhci_add_host(struct sdhci_host *host)
caps = (host->quirks & SDHCI_QUIRK_MISSING_CAPS) ? host->caps :
sdhci_readl(host, SDHCI_CAPABILITIES);
+ caps_h = sdhci_readl(host, SDHCI_CAPABILITIES_H);
+
+ if ((caps & SDHCI_CAN_VDD_180) &&
+ ((caps_h & SDHCI_CAN_SDR50) ||
+ (caps_h & SDHCI_CAN_SDR104) ||
+ (caps_h & SDHCI_CAN_DDR50)))
+ mmc->caps |= (MMC_CAP_1_8V_DDR);
if (host->quirks & SDHCI_QUIRK_FORCE_DMA)
host->flags |= SDHCI_USE_SDMA;
@@ -141,7 +141,14 @@
#define SDHCI_ACMD12_ERR 0x3C
-/* 3E-3F reserved */
+#define SDHCI_HOST_CONTROL2 0x3E
+#define SDHCI_CTRL2_UHS_MASK 0x07
+#define SDHCI_CTRL2_SDR12 0x00
+#define SDHCI_CTRL2_SDR25 0x01
+#define SDHCI_CTRL2_SDR50 0x02
+#define SDHCI_CTRL2_SDR104 0x03
+#define SDHCI_CTRL2_DDR50 0x04
+#define SDHCI_CTRL2_1_8V 0x08
#define SDHCI_CAPABILITIES 0x40
#define SDHCI_TIMEOUT_CLK_MASK 0x0000003F
@@ -161,7 +168,10 @@
#define SDHCI_CAN_VDD_180 0x04000000
#define SDHCI_CAN_64BIT 0x10000000
-/* 44-47 reserved for more caps */
+#define SDHCI_CAPABILITIES_H 0x44
+#define SDHCI_CAN_SDR50 0x00000001
+#define SDHCI_CAN_SDR104 0x00000002
+#define SDHCI_CAN_DDR50 0x00000004
#define SDHCI_MAX_CURRENT 0x48
@@ -207,6 +217,7 @@ struct sdhci_ops {
#endif
void (*set_clock)(struct sdhci_host *host, unsigned int clock);
+ void (*set_ddr)(struct sdhci_host *host, unsigned int ddr);
int (*enable_dma)(struct sdhci_host *host);
unsigned int (*get_max_clock)(struct sdhci_host *host);
--
1.7.0.4