On Fri, Jan 21, 2011 at 11:51 AM, zhangfei gao <zhangfei.gao@xxxxxxxxx> wrote: > On Mon, Jan 10, 2011 at 9:56 PM, zhangfei gao <zhangfei.gao@xxxxxxxxx> wrote: >> Update typo >> >> From 93873d40ea72b1ccd97c3629ffe555080ac4d0e4 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, so sdhci.c >> return error and SDR mode is been reused. >> 2. Call back function set_uhs is added, since some controller count >> on external pmic to provide io voltage >> 3. mmc_set_uhs is added to make host switch io voltage according to uhs_mode >> 4. Verified on mmp2 and toshiba emmc >> >> Signed-off-by: Zhangfei Gao <zhangfei.gao@xxxxxxxxxxx> >> --- >> drivers/mmc/core/core.c | 18 +++++++++++++ >> drivers/mmc/core/core.h | 1 + >> drivers/mmc/core/mmc.c | 9 ++++++ >> drivers/mmc/host/sdhci.c | 64 +++++++++++++++++++++++++++++++++++++++++++--- >> drivers/mmc/host/sdhci.h | 16 ++++++++++- >> include/linux/mmc/host.h | 2 + >> 6 files changed, 104 insertions(+), 6 deletions(-) >> >> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c >> index a8e89f3..1f1f185 100644 >> --- a/drivers/mmc/core/core.c >> +++ b/drivers/mmc/core/core.c >> @@ -623,6 +623,24 @@ 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 >> + */ >> + if (!ret) >> + mmc_delay(5); >> + } >> + >> + 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..657c5ef 100644 >> --- a/drivers/mmc/core/mmc.c >> +++ b/drivers/mmc/core/mmc.c >> @@ -529,6 +529,15 @@ 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)) { >> + printk(KERN_WARNING "%s: DDR mode not accepted by host " >> + "and reuse MMC_SDR_MODE.\n", >> + mmc_hostname(host)); >> + ddr = MMC_SDR_MODE; >> + } >> + } >> + >> /* >> * Activate wide bus and DDR (if supported). >> */ >> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c >> index d5febe5..decc50f 100644 >> --- a/drivers/mmc/host/sdhci.c >> +++ b/drivers/mmc/host/sdhci.c >> @@ -80,7 +80,7 @@ static void sdhci_dumpregs(struct sdhci_host *host) >> sdhci_readw(host, SDHCI_SLOT_INT_STATUS)); >> printk(KERN_DEBUG DRIVER_NAME ": Caps: 0x%08x | Caps_1: 0x%08x\n", >> sdhci_readl(host, SDHCI_CAPABILITIES), >> - sdhci_readl(host, SDHCI_CAPABILITIES_1)); >> + sdhci_readl(host, SDHCI_CAPABILITIES_H)); >> printk(KERN_DEBUG DRIVER_NAME ": Cmd: 0x%08x | Max curr: 0x%08x\n", >> sdhci_readw(host, SDHCI_COMMAND), >> sdhci_readl(host, SDHCI_MAX_CURRENT)); >> @@ -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 (uhs_mode & MMC_1_8V_DDR_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_H); >> + } >> + >> + if (caps & SDHCI_CAN_VDD_180) { >> + if (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 6e0969e..6be28a9 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 >> @@ -166,7 +173,10 @@ >> #define SDHCI_CAN_VDD_180 0x04000000 >> #define SDHCI_CAN_64BIT 0x10000000 >> >> -#define SDHCI_CAPABILITIES_1 0x44 >> +#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 >> >> @@ -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); >> + 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 >> > Include more people, any comments are welcome. 1. mmc_set_uhs is added since 5ms is required by spec to waiting for io voltage to be stable, or waiting for set_voltage from external pmic to be stable, also considered supporting sd uhs mode feature. Since set_ios is protected by spin_lock_save, so mmc_delay(5) can not put inside. If host does not support ddr50 mode, mmc_set_uhs will return error and SDR mode will be re-used. 2. sdhci_set_ddr is added for directly set the DDR50 mode, also could be extended for support SDR mode. 3. sdhci_ops add set_uhs to allow external pmic to provide io voltage, since some controller can not provide voltage itself, in fact, this only is needed for sd uhs mode, in which mode, 1.8v io voltage is must, while emmc ddr50 mode can work at high voltage. Could you help review, thanks in advance. -- 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