On Jan 3, 2011, at 8:25 PM, Philip Rakity wrote: > > On Jan 3, 2011, at 7:39 PM, zhangfei gao wrote: > >> On Sun, Jan 2, 2011 at 11:45 AM, Philip Rakity <prakity@xxxxxxxxxxx> wrote: >>> >>> mmc->caps MMC_CAP_1_8V_DDR is set by looking at cabability_1 >>> register if sd 3.0 controller. >>> >>> Support for dual data rate (DDR50) with 1_8V signalling added >>> with optional call back to enable external control of signalling >>> voltage. >>> >>> no support for 1.2V core voltage since SD 3.0 does not support >>> this. >>> >>> **** QUESTION **** >>> should this be part of regulator framework ? >>> >>> delay for signaling voltage to settle set to 5ms (per spec). >>> >>> this code does not change the voltage supplied to the card. >>> It assumes that this is correctly handled in the core/ layer. >>> >>> There is no requirement that the card voltage be at 1.8v >>> for DDR to work. This violates DDR specification for >>> SD Host Controller 3.0 since explicitly states card voltage >>> is 3.3v while signalling voltage is set to 1.8v. >>> >>> tested on mmp2 -- brownstone -- under linux-next. >>> >>> Note: this board design runs a fixed voltage to the card and >>> signaling voltage cannot be changed. >>> >>> Signed-off-by: Philip Rakity <prakity@xxxxxxxxxxx> >>> --- >>> drivers/mmc/host/sdhci.c | 53 ++++++++++++++++++++++++++++++++++++++++++--- >>> drivers/mmc/host/sdhci.h | 5 ++++ >>> 2 files changed, 54 insertions(+), 4 deletions(-) >>> >>> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c >>> index d5febe5..805f850 100644 >>> --- a/drivers/mmc/host/sdhci.c >>> +++ b/drivers/mmc/host/sdhci.c >>> @@ -84,6 +84,8 @@ static void sdhci_dumpregs(struct sdhci_host *host) >>> printk(KERN_DEBUG DRIVER_NAME ": Cmd: 0x%08x | Max curr: 0x%08x\n", >>> sdhci_readw(host, SDHCI_COMMAND), >>> sdhci_readl(host, SDHCI_MAX_CURRENT)); >>> + printk(KERN_DEBUG DRIVER_NAME ": HostCtrl2:0x%08x\n", >>> + sdhci_readw(host, SDHCI_HOST_CONTROL_2)); >>> >>> if (host->flags & SDHCI_USE_ADMA) >>> printk(KERN_DEBUG DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x\n", >>> @@ -986,6 +988,38 @@ static void sdhci_finish_command(struct sdhci_host *host) >>> host->cmd = NULL; >>> } >>> >>> +/* >>> + * Handle 1.8V signaling for DDR. No need to check for other >>> + * DDR values since driver supports ONLY 1_8V DDR. This is >>> + * set by the MMC_CAP_1_8V_DDR. 1_2V DDR is not supported. >>> + */ >>> +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_CONTROL_2); >>> + con |= SDCTRL_2_SDH_V18_EN; >>> + sdhci_writew(host, con, SDHCI_HOST_CONTROL_2); >>> + >>> + /* Change sigalling voltage and wait for it to be stable */ >>> + if (host->ops->set_signaling_voltage) >>> + host->ops->set_signaling_voltage(host, 18); >>> + else >>> + mdelay(5); >> >> In fact, have considered this method before, mdelay(5) in >> spin_lock_irqsave is terrible, since timer can not update system time, >> and android must die. > > where is the spin_lock set ? in the set_ios routine on entry > maybe we need a check to not do this twice by saving the current ddr state Forgot to mention -- set clock can wait up to 20ms and is called with the spin_lock being held. so why the objection to the 5ms ? <snip> static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { struct sdhci_host *host; unsigned long flags; u8 ctrl; host = mmc_priv(mmc); spin_lock_irqsave(&host->lock, flags); if (host->flags & SDHCI_DEVICE_DEAD) goto out; /* * Reset the chip on each power off. * Should clear out any weird states. */ if (ios->power_mode == MMC_POWER_OFF) { sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE); sdhci_reinit(host); } sdhci_set_clock(host, ios->clock); sdhci_set_ddr(host, ios->ddr); ===== static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) { int div; u16 clk; unsigned long timeout; if (clock == host->clock) return; if (host->ops->set_clock) { host->ops->set_clock(host, clock); if (host->quirks & SDHCI_QUIRK_NONSTANDARD_CLOCK) return; } sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); if (clock == 0) goto out; if (host->version >= SDHCI_SPEC_300) { /* Version 3.00 divisors must be a multiple of 2. */ if (host->max_clk <= clock) div = 1; else { for (div = 2; div < SDHCI_MAX_DIV_SPEC_300; div += 2) { if ((host->max_clk / div) <= clock) break; } } } else { /* Version 2.00 divisors must be a power of 2. */ for (div = 1; div < SDHCI_MAX_DIV_SPEC_200; div *= 2) { if ((host->max_clk / div) <= clock) break; } } div >>= 1; clk = (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT; clk |= ((div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN) << SDHCI_DIVIDER_HI_SHIFT; clk |= SDHCI_CLOCK_INT_EN; sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); /* Wait max 20 ms */ timeout = 20; while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL)) & SDHCI_CLOCK_INT_STABLE)) { if (timeout == 0) { printk(KERN_ERR "%s: Internal clock never " "stabilised.\n", mmc_hostname(host->mmc)); sdhci_dumpregs(host); return; } timeout--; mdelay(1); } clk |= SDHCI_CLOCK_CARD_EN; sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); out: host->clock = clock; } >> >>> + >>> + /* >>> + * We can fail here but there is no higher level recovery >>> + * since the card is already past the switch to ddr >>> + */ >>> + con = sdhci_readw(host, SDHCI_HOST_CONTROL_2); >>> + con &= ~SDCTRL_2_UHS_MODE_MASK; >>> + con |= SDCTRL_2_UHS_MODE_SEL_DDR50; >>> + sdhci_writew(host, con, SDHCI_HOST_CONTROL_2); >>> +} >>> + >>> static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) >>> { >>> int div; >>> @@ -1180,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); >>> @@ -1744,7 +1779,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, ocr_avail; >>> int ret; >>> >>> WARN_ON(host == NULL); >>> @@ -1767,8 +1802,19 @@ 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; >>> + caps_h = 0; >>> + } else { >>> + caps = sdhci_readl(host, SDHCI_CAPABILITIES); >>> + if (host->version >= SDHCI_SPEC_300) >>> + caps_h = sdhci_readl(host, SDHCI_CAPABILITIES_1); >>> + else >>> + caps_h = 0; >>> + } >>> + >>> + 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; >>> @@ -1905,7 +1951,6 @@ int sdhci_add_host(struct sdhci_host *host) >>> ocr_avail |= MMC_VDD_29_30 | MMC_VDD_30_31; >>> if (caps & SDHCI_CAN_VDD_180) >>> ocr_avail |= MMC_VDD_165_195; >>> - >>> mmc->ocr_avail = ocr_avail; >>> mmc->ocr_avail_sdio = ocr_avail; >>> if (host->ocr_avail_sdio) >>> diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h >>> index 36f3861..d976e4f 100644 >>> --- a/drivers/mmc/host/sdhci.h >>> +++ b/drivers/mmc/host/sdhci.h >>> @@ -182,6 +182,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 >>> >>> @@ -237,6 +240,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_signaling_voltage)(struct sdhci_host *host, >>> + unsigned short voltage); >>> }; >>> >>> #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS >>> -- >>> 1.7.0.4 >>> >>> >>> -- >>> 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 >>> > -- 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