Resend as plain test -- with additional comments. On Dec 21, 2010, at 4:37 AM, zhangfei gao wrote: > v3: sync to mmc-next > > Emmc speed could double if using ddr50 mode, help check > > From 895c3d15a200d5f5803f992dab46ff114ad26f90 Mon Sep 17 00:00:00 2001 > From: Zhangfei Gao <zhangfei.gao@xxxxxxxxxxx> > Date: Tue, 21 Dec 2010 19:51:38 -0500 > Subject: [PATCH] mmc: sdhci support emmc ddr50 mode > > 1. spec sdhc 3.0 does not claim support 1.2v ddr mode > 2. Call back function set_power is added, since some controller count > on external pmic to provide power > 3. According to spec sdhc 3.0, uhs mode, including emmc ddr50 takes > effect only when 1.8v Signaling Enable bit, which used for providing > 1.8v. > So emmc ddr50 mode works after 1.8v switching process, though emmc > ddr50 could work at high voltage such as 3.3v if external pmic provide > voltage. > Limitation: emmc ddr50 mode only workable when both host and emmc > card support 1.70-1.90v signaling voltage is not the same as card voltage. card voltage can be 3.3v and signaling voltage 1.8v. > 4. According to JESD84, power down and power up is required to > provide low voltage 1.70-1.90v to mmc. > > Verified: toshiba emmc on mmp2, with io voltage at 1.8v provided by > external pmic. > > Signed-off-by: Zhangfei Gao <zhangfei.gao@xxxxxxxxxxx> > --- > drivers/mmc/core/core.c | 13 +++++++++++++ > drivers/mmc/core/core.h | 1 + > drivers/mmc/core/mmc.c | 6 +++++- > drivers/mmc/host/sdhci.c | 44 +++++++++++++++++++++++++++++++++++++++++--- > drivers/mmc/host/sdhci.h | 14 +++++++++++++- > 5 files changed, 73 insertions(+), 5 deletions(-) > > diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c > index a8e89f3..fd657f1 100644 > --- a/drivers/mmc/core/core.c > +++ b/drivers/mmc/core/core.c > @@ -1012,6 +1012,19 @@ static void mmc_power_off(struct mmc_host *host) > } > > /* > + * mmc select low voltage 1.70-1.95v > + */ > +void mmc_select_low_voltage(struct mmc_host *host, u32 ocr) > +{ > + if (!(ocr & MMC_VDD_165_195)) > + return; > + > + mmc_power_off(host); > + host->ocr = ocr & host->ocr_avail; > + mmc_power_up(host); > +} > + > +/* > * Cleanup when the last reference to the bus operator is dropped. > */ > static void __mmc_release_bus(struct mmc_host *host) > diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h > index 026c975..b05c20a 100644 > --- a/drivers/mmc/core/core.h > +++ b/drivers/mmc/core/core.h > @@ -41,6 +41,7 @@ void mmc_set_bus_width(struct mmc_host *host, > unsigned int width); > void mmc_set_bus_width_ddr(struct mmc_host *host, unsigned int width, > unsigned int ddr); > u32 mmc_select_voltage(struct mmc_host *host, u32 ocr); > +void mmc_select_low_voltage(struct mmc_host *host, u32 ocr); > void mmc_set_timing(struct mmc_host *host, unsigned int timing); > > static inline void mmc_delay(unsigned int ms) > diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c > index 86cac0d..8779339 100644 > --- a/drivers/mmc/core/mmc.c > +++ b/drivers/mmc/core/mmc.c > @@ -790,7 +790,11 @@ int mmc_attach_mmc(struct mmc_host *host, u32 ocr) > ocr &= ~0x7F; > } > > - host->ocr = mmc_select_voltage(host, ocr); > + if ((ocr & MMC_VDD_165_195) > + && (host->ocr_avail & MMC_VDD_165_195)) > + mmc_select_low_voltage(host, ocr); > + else > + host->ocr = mmc_select_voltage(host, ocr); > The patch for dual voltage cards should be a separate patch. It is independent of support for dual data rate. > /* > * Can we support the voltage of the card? > diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c > index d5febe5..aafbb42 100644 > --- a/drivers/mmc/host/sdhci.c > +++ b/drivers/mmc/host/sdhci.c > @@ -986,6 +986,22 @@ 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; > + > + if (ddr == MMC_SDR_MODE) > + return; > + > + con = sdhci_readw(host, SDHCI_HOST_CONTROL2); > + if (con & SDHCI_CTRL2_1_8V) { > + con &= ~SDHCI_CTRL2_UHS_MASK; > + if (ddr & MMC_1_8V_DDR_MODE) > + 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; > @@ -1084,6 +1100,18 @@ static void sdhci_set_power(struct sdhci_host > *host, unsigned short power) > return; > } > > + if ((pwr == SDHCI_POWER_180) && > + (host->mmc->caps & MMC_CAP_1_8V_DDR)) { > + u16 con; > + > + con = sdhci_readw(host, SDHCI_HOST_CONTROL2); > + con |= SDHCI_CTRL2_1_8V; > + sdhci_writew(host, con, SDHCI_HOST_CONTROL2); > + > + if (host->ops->set_power) > + host->ops->set_power(host, pwr); > + } > + power to the card can be at 3.3v and signaling at 1.8v. This is a legal combination. The > + if ((pwr == SDHCI_POWER_180) && > + (host->mmc->caps & MMC_CAP_1_8V_DDR)) { should be reworked. > /* > * Spec says that we should clear the power reg before setting > * a new value. Some controllers don't seem to like this though. > @@ -1180,6 +1208,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); > @@ -1744,7 +1773,7 @@ EXPORT_SYMBOL_GPL(sdhci_alloc_host); > int sdhci_add_host(struct sdhci_host *host) > { > struct mmc_host *mmc; > - unsigned int caps, ocr_avail; > + unsigned int caps, caps_h = 0, ocr_avail; > int ret; > > WARN_ON(host == NULL); > @@ -1767,8 +1796,17 @@ int sdhci_add_host(struct sdhci_host *host) > host->version); > } > > - caps = (host->quirks & SDHCI_QUIRK_MISSING_CAPS) ? host->caps : > - sdhci_readl(host, SDHCI_CAPABILITIES); > + if (host->quirks & SDHCI_QUIRK_MISSING_CAPS) > + caps = host->caps; > + else { > + caps = sdhci_readl(host, SDHCI_CAPABILITIES); > + caps_h = sdhci_readl(host, SDHCI_CAPABILITIES_1); > + } > + > + if (caps & SDHCI_CAN_VDD_180) { > + if (caps_h & SDHCI_CAN_SDR50) > + 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 6e0969e..c4bd5dd 100644 > --- a/drivers/mmc/host/sdhci.h > +++ b/drivers/mmc/host/sdhci.h > @@ -145,7 +145,14 @@ > > #define SDHCI_ACMD12_ERR 0x3C > > -/* 3E-3F reserved */ > +#define SDHCI_HOST_CONTROL2 0x3E > +#define SDHCI_CTRL2_UHS_MASK 0x0007 > +#define SDHCI_CTRL2_SDR12 0x0000 > +#define SDHCI_CTRL2_SDR25 0x0001 > +#define SDHCI_CTRL2_SDR50 0x0002 > +#define SDHCI_CTRL2_SDR104 0x0003 > +#define SDHCI_CTRL2_DDR50 0x0004 > +#define SDHCI_CTRL2_1_8V 0x0008 > > #define SDHCI_CAPABILITIES 0x40 > #define SDHCI_TIMEOUT_CLK_MASK 0x0000003F > @@ -167,6 +174,9 @@ > #define SDHCI_CAN_64BIT 0x10000000 > > #define SDHCI_CAPABILITIES_1 0x44 > +#define SDHCI_CAN_SDR50 0x00000001 > +#define SDHCI_CAN_SDR104 0x00000002 > +#define SDHCI_CAN_DDR50 0x00000004 > > #define SDHCI_MAX_CURRENT 0x48 > > @@ -222,6 +232,8 @@ struct sdhci_ops { > void (*platform_send_init_74_clocks)(struct sdhci_host *host, > u8 power_mode); > unsigned int (*get_ro)(struct sdhci_host *host); > + unsigned int (*set_power)(struct sdhci_host *host, > + unsigned short power); > }; > > #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS > -- > 1.7.0.4 > <0001-mmc-sdhci-support-emmc-ddr50-mode.patch> -- To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html