On Mon, Jan 10, 2011 at 7:06 PM, Philip Rakity <prakity@xxxxxxxxxxx> wrote: > > On Jan 9, 2011, at 3:50 AM, zhangfei gao wrote: > >> From d65af1e0a51bd4cc6b7a55b48df2bc23ccb84f2d Mon Sep 17 00:00:00 2001 >> From: Zhangfei Gao <zhangfei.gao@xxxxxxxxxxx> >> Date: Sun, 9 Jan 2011 18:09:12 -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_uhs is added, since some controller count >> on external pmic to provide io voltage >> >> Signed-off-by: Zhangfei Gao <zhangfei.gao@xxxxxxxxxxx> >> --- >> drivers/mmc/core/core.c | 17 ++++++++++++ >> drivers/mmc/core/core.h | 1 + >> drivers/mmc/core/mmc.c | 5 +++ >> drivers/mmc/host/sdhci.c | 62 +++++++++++++++++++++++++++++++++++++++++++-- >> drivers/mmc/host/sdhci.h | 14 +++++++++- >> include/linux/mmc/host.h | 2 + >> 6 files changed, 97 insertions(+), 4 deletions(-) >> >> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c >> index a8e89f3..964b44f 100644 >> --- a/drivers/mmc/core/core.c >> +++ b/drivers/mmc/core/core.c >> @@ -623,6 +623,23 @@ static inline void mmc_set_ios(struct mmc_host *host) >> host->ops->set_ios(host, ios); >> } >> >> +int mmc_set_uhs(struct mmc_host *host, unsigned int uhs_mode) >> +{ >> + int ret = -EINVAL; >> + >> + if (host->ops->set_uhs) { >> + ret = host->ops->set_uhs(host, uhs_mode); >> + >> + /* >> + * According to sdhc standard spec v3.0 >> + * 1.8v regulator should be stable withing 5ms >> + */ >> + mmc_delay(5); > > The delay should be set in set_uhs IHMO mmc_delay is defined in core.h, and it already consider schedule case, so it's better to put here. > >> + } >> + >> + return ret; >> +} >> + >> /* >> * Control chip select pin on a host. >> */ >> diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h >> index 026c975..4b1ea7e 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); >> +int mmc_set_uhs(struct mmc_host *host, unsigned int uhs_mode); >> 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..4dac82f 100644 >> --- a/drivers/mmc/core/mmc.c >> +++ b/drivers/mmc/core/mmc.c >> @@ -529,6 +529,11 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, >> ddr = MMC_1_2V_DDR_MODE; >> } >> >> + if (ddr) { >> + if (mmc_set_uhs(host, ddr)) >> + ddr = MMC_SDR_MODE; >> + } >> + > > > This code is setting 1.8v signaling on the host controller before the switch command to ask the card to go to DDR mode and use 1.8v signalling. > Is this valid before the card has moved into that state ? Since time is needed to wait for voltage to be stable, so it should be put before switch. > >> /* >> * Activate wide bus and DDR (if supported). >> */ >> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c >> index d5febe5..906f85b 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; >> @@ -1180,6 +1196,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); >> @@ -1237,6 +1254,35 @@ out: >> spin_unlock_irqrestore(&host->lock, flags); >> } >> >> +static int sdhci_set_uhs(struct mmc_host *mmc, unsigned int uhs_mode) >> +{ >> + struct sdhci_host *host; >> + unsigned long flags; >> + int ret = 0; >> + >> + host = mmc_priv(mmc); >> + >> + spin_lock_irqsave(&host->lock, flags); >> + if (host->mmc->caps & MMC_CAP_1_8V_DDR) { > > since only 1_8V is supported is the if check necessary. Failure would imply the core layer was broken. Typo, here is check uhs_mode, if (uhs_mode & MMC_1_8V_DDR_MODE), since MMC_1_2V_DDR_MODE is not supported. And failure is already considered according to return value, re-use SDR mode > >> + u16 con; >> + >> + con = sdhci_readw(host, SDHCI_HOST_CONTROL2); >> + con |= SDHCI_CTRL2_1_8V; >> + sdhci_writew(host, con, SDHCI_HOST_CONTROL2); >> + } else >> + goto err; >> + >> + spin_unlock_irqrestore(&host->lock, flags); >> + >> + if (host->ops->set_uhs) >> + ret = host->ops->set_uhs(host, uhs_mode); >> + >> + return ret; >> +err: >> + spin_unlock_irqrestore(&host->lock, flags); >> + return -EINVAL; >> +} >> + >> static int sdhci_get_ro(struct mmc_host *mmc) >> { >> struct sdhci_host *host; >> @@ -1287,6 +1333,7 @@ out: >> static const struct mmc_host_ops sdhci_ops = { >> .request = sdhci_request, >> .set_ios = sdhci_set_ios, >> + .set_uhs = sdhci_set_uhs, >> .get_ro = sdhci_get_ro, >> .enable_sdio_irq = sdhci_enable_sdio_irq, >> }; >> @@ -1744,7 +1791,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 +1814,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); >> + } >> > > SDHCI_CAN_ DDR50 - wrong bit being checked. Thanks, mistake here. > missing check for SD 3.0 Controller -- see my patch > see chris comments on the name caps_h I think use caps_h and SDHCI_CAPABILITIES_H is OK, since it is high 32 bits of capabilities register. > >> 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..f8e94b9 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_uhs)(struct sdhci_host *host, >> + unsigned int uhs_mode); >> }; >> >> #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS >> diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h >> index bcb793e..07131b0 100644 >> --- a/include/linux/mmc/host.h >> +++ b/include/linux/mmc/host.h >> @@ -117,6 +117,8 @@ struct mmc_host_ops { >> >> /* optional callback for HC quirks */ >> void (*init_card)(struct mmc_host *host, struct mmc_card *card); >> + >> + int (*set_uhs)(struct mmc_host *host, unsigned int uhs_mode); >> }; >> >> struct mmc_card; >> -- >> 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