diff options
-rw-r--r-- | drivers/mmc/host/sdhci-esdhc.h | 20 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci-of-esdhc.c | 131 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci-pltfm.c | 3 |
3 files changed, 150 insertions, 4 deletions
diff --git a/drivers/mmc/host/sdhci-esdhc.h b/drivers/mmc/host/sdhci-esdhc.h index b0174db..6bb6c52 100644 --- a/drivers/mmc/host/sdhci-esdhc.h +++ b/drivers/mmc/host/sdhci-esdhc.h @@ -39,6 +39,24 @@ #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_1 0x1 /* eMMC Card Rev4.5 */ +#define ESDHC_ADAPTER_TYPE_2 0x2 /* SD/MMC Legacy Card */ +#define ESDHC_ADAPTER_TYPE_3 0x3 /* eMMC Card Rev4.4 */ +#define ESDHC_ADAPTER_TYPE_4 0x4 /* Reserved */ +#define ESDHC_ADAPTER_TYPE_5 0x5 /* MMC Card */ +#define ESDHC_ADAPTER_TYPE_6 0x6 /* SD Card Rev2.0 Rev3.0 */ +#define ESDHC_NO_ADAPTER 0x7 /* No Card is Present*/ + /* pltfm-specific */ #define ESDHC_HOST_CONTROL_LE 0x20 @@ -52,6 +70,8 @@ /* OF-specific */ #define ESDHC_DMA_SYSCTL 0x40c #define ESDHC_DMA_SNOOP 0x00000040 +#define ESDHC_FLUSH_ASYNC_FIFO 0x00040000 +#define ESDHC_USE_PERICLK 0x00080000 #define ESDHC_INT_DMA_ERROR 0x10000000 #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 6d71e1a..d6799a8 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -33,11 +33,29 @@ static u32 svr; #endif +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_3: + 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 @@ -187,8 +205,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); + } /* * If we have this quirk: @@ -537,6 +558,85 @@ static int esdhc_pltfm_bus_width(struct sdhci_host *host, int width) return 0; } +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 int 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); + return 0; +} + static const struct sdhci_ops sdhci_esdhc_ops = { .read_l = esdhc_readl, .read_w = esdhc_readw, @@ -560,6 +660,7 @@ static const struct sdhci_ops sdhci_esdhc_ops = { #endif .adma_workaround = esdhci_of_adma_workaround, .platform_bus_width = esdhc_pltfm_bus_width, + .set_uhs_signaling = esdhc_set_uhs_signaling, }; static const struct sdhci_pltfm_data sdhci_esdhc_pdata = { @@ -578,6 +679,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); @@ -585,6 +688,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,p2020-esdhc")) { /* * Freescale messed up with P2020 as it has a non-standard @@ -607,13 +722,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_PERICLK; + sdhci_writel(host, value, ESDHC_DMA_SYSCTL); + esdhc_clock_control(host, true); + } + ret = sdhci_add_host(host); if (ret) sdhci_pltfm_free(pdev); diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c index 095c2a6..5c7c168 100644 --- a/drivers/mmc/host/sdhci-pltfm.c +++ b/drivers/mmc/host/sdhci-pltfm.c @@ -104,7 +104,8 @@ void sdhci_get_of_property(struct platform_device *pdev) if (of_device_is_compatible(np, "fsl,p5020-esdhc") || of_device_is_compatible(np, "fsl,p5040-esdhc") || of_device_is_compatible(np, "fsl,t1024-esdhc") || - of_device_is_compatible(np, "fsl,t1040-esdhc")) + of_device_is_compatible(np, "fsl,t1040-esdhc") || + of_device_is_compatible(np, "fsl,t2080-esdhc")) host->quirks2 |= SDHCI_QUIRK2_LONG_TIME_CMD_COMPLETE_IRQ; if (of_device_is_compatible(np, "fsl,p5040-esdhc") || |