Based on the work from Adrian and Hanumath, here is the patch to verify ddr50 for sdhci.c on sdhci-pxa with HYNIX and toshiba eMMC. Only support emmc DDR50 mode now. Would you help review, thanks >From 59072fa8fea50634f01009e51c4b5e16308ab466 Mon Sep 17 00:00:00 2001 From: Zhangfei Gao <zhangfei.gao@xxxxxxxxxxx> 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@xxxxxxxxxxx> --- drivers/mmc/host/sdhci.c | 48 +++++++++++++++++++++++++++++++++++++++++++++- drivers/mmc/host/sdhci.h | 15 ++++++++++++- 2 files changed, 60 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 782c0ee..d9e4ab8 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -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; diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index b7b8a3b..fe87c5f 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -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
From 59072fa8fea50634f01009e51c4b5e16308ab466 Mon Sep 17 00:00:00 2001 From: Zhangfei Gao <zhangfei.gao@xxxxxxxxxxx> 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@xxxxxxxxxxx> --- drivers/mmc/host/sdhci.c | 48 +++++++++++++++++++++++++++++++++++++++++++++- drivers/mmc/host/sdhci.h | 15 ++++++++++++- 2 files changed, 60 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 782c0ee..d9e4ab8 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -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; diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index b7b8a3b..fe87c5f 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -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