On Fri, Jul 31, 2015 at 11:55 AM, Yangbo Lu <yangbo.lu@xxxxxxxxxxxxx> wrote: > Add eMMC DDR mode support for Freescale SDHC adapter card. > The u-boot should provide device tree properties 'adapter-type' > and 'periperal-frequency' for this feature, if not, the card > would not use DDR mode. > > Signed-off-by: Yangbo Lu <yangbo.lu@xxxxxxxxxxxxx> > --- > drivers/mmc/host/sdhci-esdhc.h | 24 +++++++ > drivers/mmc/host/sdhci-of-esdhc.c | 132 ++++++++++++++++++++++++++++++++++++-- > 2 files changed, 152 insertions(+), 4 deletions(-) > > diff --git a/drivers/mmc/host/sdhci-esdhc.h b/drivers/mmc/host/sdhci-esdhc.h > index 163ac99..015ec01 100644 > --- a/drivers/mmc/host/sdhci-esdhc.h > +++ b/drivers/mmc/host/sdhci-esdhc.h > @@ -28,10 +28,32 @@ > #define ESDHC_CLOCK_MASK 0x0000fff0 > #define ESDHC_PREDIV_SHIFT 8 > #define ESDHC_DIVIDER_SHIFT 4 > +#define ESDHC_CLOCK_CRDEN 0x00000008 > #define ESDHC_CLOCK_PEREN 0x00000004 > #define ESDHC_CLOCK_HCKEN 0x00000002 > #define ESDHC_CLOCK_IPGEN 0x00000001 > > +#define ESDHCI_PRESENT_STATE 0x24 > +#define ESDHC_CLK_STABLE 0x00000008 > + > +#define ESDHC_CAPABILITIES_1 0x114 > +#define ESDHC_MODE_MASK 0x00000007 > +#define ESDHC_MODE_DDR50_SEL 0xfffffffc > +#define ESDHC_MODE_DDR50 0x00000004 > + > +#define ESDHC_CLOCK_CONTROL 0x144 > +#define ESDHC_CLKLPBK_EXTPIN 0x80000000 > +#define ESDHC_CMDCLK_SHIFTED 0x00008000 > + > +/* SDHC Adapter Card Type */ > +#define ESDHC_ADAPTER_TYPE_EMMC45 0x1 /* eMMC Card Rev4.5 */ > +#define ESDHC_ADAPTER_TYPE_SDMMC_LEGACY 0x2 /* SD/MMC Legacy Card */ > +#define ESDHC_ADAPTER_TYPE_EMMC44 0x3 /* eMMC Card Rev4.4 */ > +#define ESDHC_ADAPTER_TYPE_RSV 0x4 /* Reserved */ > +#define ESDHC_ADAPTER_TYPE_MMC 0x5 /* MMC Card */ > +#define ESDHC_ADAPTER_TYPE_SD 0x6 /* SD Card Rev2.0 Rev3.0 */ > +#define ESDHC_NO_ADAPTER 0x7 /* No Card is Present*/ > + Hi Yangbo, You could put those of_esdhc specific defines in of_esdhc driver since sdhci-esdhc.h is shared by both sdhci-imx-esdhc and sdhci-of-esdhc driver. Or it may be time to remove such dependency that each driver uses its own head file separately since there's already a lot difference between them and no reason to share the headfile anymore. The later one might be a more reasonable way to me. Regards Dong Aisheng > /* pltfm-specific */ > #define ESDHC_HOST_CONTROL_LE 0x20 > > @@ -45,6 +67,8 @@ > /* OF-specific */ > #define ESDHC_DMA_SYSCTL 0x40c > #define ESDHC_DMA_SNOOP 0x00000040 > +#define ESDHC_FLUSH_ASYNC_FIFO 0x00040000 > +#define ESDHC_USE_PERIPHERAL_CLK 0x00080000 > > #define ESDHC_HOST_CONTROL_RES 0x01 > > diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c > index f1021d8..6d7e3f9 100644 > --- a/drivers/mmc/host/sdhci-of-esdhc.c > +++ b/drivers/mmc/host/sdhci-of-esdhc.c > @@ -24,11 +24,30 @@ > > #define VENDOR_V_22 0x12 > #define VENDOR_V_23 0x13 > + > +static u32 adapter_type; > +static bool peripheral_clk_available; > + > static u32 esdhc_readl(struct sdhci_host *host, int reg) > { > u32 ret; > > - ret = sdhci_32bs_readl(host, reg); > + if (reg == SDHCI_CAPABILITIES_1) { > + ret = sdhci_32bs_readl(host, ESDHC_CAPABILITIES_1); > + switch (adapter_type) { > + case ESDHC_ADAPTER_TYPE_EMMC44: > + if (ret & ESDHC_MODE_DDR50) { > + ret &= ESDHC_MODE_DDR50_SEL; > + /* enable 1/8V DDR capable */ > + host->mmc->caps |= MMC_CAP_1_8V_DDR; > + } else > + ret &= ~ESDHC_MODE_MASK; > + break; > + default: > + ret &= ~ESDHC_MODE_MASK; > + } > + } else > + ret = sdhci_32bs_readl(host, reg); > /* > * The bit of ADMA flag in eSDHC is not compatible with standard > * SDHC register, so set fake flag SDHCI_CAN_DO_ADMA2 when ADMA is > @@ -159,8 +178,11 @@ static void esdhc_writeb(struct sdhci_host *host, u8 val, int reg) > } > > /* Prevent SDHCI core from writing reserved bits (e.g. HISPD). */ > - if (reg == SDHCI_HOST_CONTROL) > + if (reg == SDHCI_HOST_CONTROL) { > val &= ~ESDHC_HOST_CONTROL_RES; > + val &= ~SDHCI_CTRL_HISPD; > + val |= (sdhci_32bs_readl(host, reg) & SDHCI_CTRL_HISPD); > + } > sdhci_clrsetbits(host, 0xff, val, reg); > } > > @@ -307,6 +329,84 @@ static void esdhc_reset(struct sdhci_host *host, u8 mask) > sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); > } > > +static void esdhc_clock_control(struct sdhci_host *host, bool enable) > +{ > + u32 value; > + u32 time_out; > + > + value = sdhci_readl(host, ESDHC_SYSTEM_CONTROL); > + > + if (enable) > + value |= ESDHC_CLOCK_CRDEN; > + else > + value &= ~ESDHC_CLOCK_CRDEN; > + > + sdhci_writel(host, value, ESDHC_SYSTEM_CONTROL); > + > + time_out = 20; > + value = ESDHC_CLK_STABLE; > + while (!(sdhci_readl(host, ESDHCI_PRESENT_STATE) & value)) { > + if (time_out == 0) { > + pr_err("%s: Internal clock never stabilised.\n", > + mmc_hostname(host->mmc)); > + break; > + } > + time_out--; > + mdelay(1); > + } > +} > + > +static void esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs) > +{ > + u16 ctrl_2; > + u32 time_out; > + u32 value; > + > + ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); > + /* Select Bus Speed Mode for host */ > + ctrl_2 &= ~SDHCI_CTRL_UHS_MASK; > + if ((uhs == MMC_TIMING_MMC_HS200) || > + (uhs == MMC_TIMING_UHS_SDR104)) > + ctrl_2 |= SDHCI_CTRL_UHS_SDR104; > + else if (uhs == MMC_TIMING_UHS_SDR12) > + ctrl_2 |= SDHCI_CTRL_UHS_SDR12; > + else if (uhs == MMC_TIMING_UHS_SDR25) > + ctrl_2 |= SDHCI_CTRL_UHS_SDR25; > + else if (uhs == MMC_TIMING_UHS_SDR50) > + ctrl_2 |= SDHCI_CTRL_UHS_SDR50; > + else if (uhs == MMC_TIMING_UHS_DDR50) > + ctrl_2 |= SDHCI_CTRL_UHS_DDR50; > + > + if (uhs == MMC_TIMING_UHS_DDR50) { > + esdhc_clock_control(host, false); > + sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); > + value = sdhci_readl(host, ESDHC_CLOCK_CONTROL); > + value |= (ESDHC_CLKLPBK_EXTPIN | ESDHC_CMDCLK_SHIFTED); > + sdhci_writel(host, value, ESDHC_CLOCK_CONTROL); > + esdhc_clock_control(host, true); > + > + esdhc_clock_control(host, false); > + value = sdhci_readl(host, ESDHC_DMA_SYSCTL); > + value |= ESDHC_FLUSH_ASYNC_FIFO; > + sdhci_writel(host, value, ESDHC_DMA_SYSCTL); > + /* Wait max 20 ms */ > + time_out = 20; > + value = ESDHC_FLUSH_ASYNC_FIFO; > + while (sdhci_readl(host, ESDHC_DMA_SYSCTL) & value) { > + if (time_out == 0) { > + pr_err("%s: FAF bit is auto cleaned failed.\n", > + mmc_hostname(host->mmc)); > + > + break; > + } > + time_out--; > + mdelay(1); > + } > + esdhc_clock_control(host, true); > + } else > + sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); > +} > + > static const struct sdhci_ops sdhci_esdhc_ops = { > .read_l = esdhc_readl, > .read_w = esdhc_readw, > @@ -322,7 +422,7 @@ static const struct sdhci_ops sdhci_esdhc_ops = { > .adma_workaround = esdhci_of_adma_workaround, > .set_bus_width = esdhc_pltfm_set_bus_width, > .reset = esdhc_reset, > - .set_uhs_signaling = sdhci_set_uhs_signaling, > + .set_uhs_signaling = esdhc_set_uhs_signaling, > }; > > #ifdef CONFIG_PM > @@ -376,6 +476,8 @@ static void esdhc_get_property(struct platform_device *pdev) > struct device_node *np = pdev->dev.of_node; > struct sdhci_host *host = platform_get_drvdata(pdev); > struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); > + const __be32 *value; > + int size; > > sdhci_get_of_property(pdev); > > @@ -383,6 +485,18 @@ static void esdhc_get_property(struct platform_device *pdev) > mmc_of_parse(host->mmc); > mmc_of_parse_voltage(np, &host->ocr_mask); > > + value = of_get_property(np, "adapter-type", &size); > + if (value && size == sizeof(*value) && *value) > + adapter_type = be32_to_cpup(value); > + > + /* If getting a peripheral-frequency, use it instead */ > + value = of_get_property(np, "peripheral-frequency", &size); > + if (value && size == sizeof(*value) && *value) { > + pltfm_host->clock = be32_to_cpup(value); > + peripheral_clk_available = true; > + } else > + peripheral_clk_available = false; > + > if (of_device_is_compatible(np, "fsl,p5040-esdhc") || > of_device_is_compatible(np, "fsl,p5020-esdhc") || > of_device_is_compatible(np, "fsl,p4080-esdhc") || > @@ -409,13 +523,23 @@ static int sdhci_esdhc_probe(struct platform_device *pdev) > { > struct sdhci_host *host; > int ret; > - > + u32 value; > > host = sdhci_pltfm_init(pdev, &sdhci_esdhc_pdata, 0); > if (IS_ERR(host)) > return PTR_ERR(host); > > esdhc_get_property(pdev); > + > + /* Select peripheral clock as the eSDHC clock */ > + if (peripheral_clk_available) { > + esdhc_clock_control(host, false); > + value = sdhci_readl(host, ESDHC_DMA_SYSCTL); > + value |= ESDHC_USE_PERIPHERAL_CLK; > + sdhci_writel(host, value, ESDHC_DMA_SYSCTL); > + esdhc_clock_control(host, true); > + } > + > ret = sdhci_add_host(host); > if (ret) > goto err; > -- > 2.1.0.27.g96db324 > > -- > 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