On Fri, Jul 31, 2015 at 11:55 AM, Yangbo Lu <[email protected]> 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 <[email protected]>
> ---
> 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 [email protected]
> 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 [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html