diff options
-rw-r--r-- | drivers/mmc/host/sdhci-esdhc-imx.c | 99 |
1 files changed, 98 insertions, 1 deletions
diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 9362f00623b6..5aec0bfe6fd6 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -39,6 +39,7 @@ #define SDHCI_MIX_CTRL_EXE_TUNE (1 << 22) #define SDHCI_MIX_CTRL_SMPCLK_SEL (1 << 23) +#define SDHCI_MIX_CTRL_AUTOTUNE_EN (1 << 24) #define SDHCI_MIX_CTRL_FBCLK_SEL (1 << 25) #define SDHCI_VENDOR_SPEC_VSELECT (1 << 1) @@ -67,6 +68,8 @@ struct pltfm_imx_data { int flags; u32 scratchpad; + /* uhs mode for sdhc host control2 */ + unsigned char uhs_mode; }; static inline void esdhc_clrset_le(struct sdhci_host *host, u32 mask, u32 val, int reg) @@ -99,7 +102,7 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg) val |= SDHCI_CARD_PRESENT; } - if (reg == SDHCI_INT_STATUS && cpu_is_mx6q()) + if (reg == SDHCI_INT_STATUS && cpu_is_mx6q()) { /* * on mx6q, there is low possibility that * DATA END interrupt comes ealier than DMA @@ -110,6 +113,32 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg) if ((val & SDHCI_INT_DATA_END) && \ !(val & SDHCI_INT_DMA_END)) val = readl(host->ioaddr + reg); + } else if (reg == SDHCI_CAPABILITIES_1 && cpu_is_mx6q()) { + /* + * on mx6q, no cap_1 available, fake one. + */ + val = SDHCI_SUPPORT_DDR50 | SDHCI_SUPPORT_SDR104 | \ + SDHCI_SUPPORT_SDR50; + } else if (reg == SDHCI_MAX_CURRENT && cpu_is_mx6q()) { + /* + * on mx6q, no max current available, fake one. + */ + val = 0; + val |= 0xFF << SDHCI_MAX_CURRENT_330_SHIFT; + val |= 0xFF << SDHCI_MAX_CURRENT_300_SHIFT; + val |= 0xFF << SDHCI_MAX_CURRENT_180_SHIFT; + } + + if (reg == SDHCI_PRESENT_STATE && cpu_is_mx6q()) { + u32 fsl_prss = readl(host->ioaddr + SDHCI_PRESENT_STATE); + /* save the least 20 bits */ + val = fsl_prss & 0x000FFFFF; + /* move dat[0-3] line signal bits */ + val |= (fsl_prss & 0x0F000000) >> 4; + /* move cmd line signal bits */ + val |= (fsl_prss & 0x00800000) << 1; + } + return val; } @@ -174,9 +203,34 @@ static void esdhc_writel_le(struct sdhci_host *host, u32 val, int reg) static u16 esdhc_readw_le(struct sdhci_host *host, int reg) { + u16 ret; + u32 val; + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct pltfm_imx_data *imx_data = pltfm_host->priv; + if (unlikely(reg == SDHCI_HOST_VERSION)) reg ^= 2; + switch (reg) { + case SDHCI_HOST_CONTROL2: + ret = 0; + /* collect bit info from several regs */ + val = readl(host->ioaddr + SDHCI_VENDOR_SPEC); + ret |= (val & SDHCI_VENDOR_SPEC_VSELECT) + ? SDHCI_CTRL_VDD_180 : 0; + + val = readl(host->ioaddr + SDHCI_MIX_CTRL); + ret |= (val & SDHCI_MIX_CTRL_EXE_TUNE) + ? SDHCI_CTRL_EXEC_TUNING : 0; + ret |= (val & SDHCI_MIX_CTRL_SMPCLK_SEL) + ? 0 : SDHCI_CTRL_TUNED_CLK ; + ret |= SDHCI_CTRL_UHS_MASK & imx_data->uhs_mode; + /* no preset enable available */ + ret &= ~SDHCI_CTRL_PRESET_VAL_ENABLE; + + return ret; + } + return readw(host->ioaddr + reg); } @@ -187,6 +241,7 @@ void esdhc_prepare_tuning(struct sdhci_host *host, u32 val) reg = sdhci_readl(host, SDHCI_MIX_CTRL); reg |= SDHCI_MIX_CTRL_EXE_TUNE | \ SDHCI_MIX_CTRL_SMPCLK_SEL | \ + SDHCI_MIX_CTRL_AUTOTUNE_EN | \ SDHCI_MIX_CTRL_FBCLK_SEL; sdhci_writel(host, reg, SDHCI_MIX_CTRL); sdhci_writel(host, (val << 8), SDHCI_TUNE_CTRL_STATUS); @@ -197,8 +252,50 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct pltfm_imx_data *imx_data = pltfm_host->priv; + u32 orig_reg; switch (reg) { + case SDHCI_CLOCK_CONTROL: + orig_reg = readl(host->ioaddr + SDHCI_VENDOR_SPEC); + if (val & SDHCI_CLOCK_CARD_EN) { + writel(orig_reg | SDHCI_VENDOR_SPEC_FRC_SDCLK_ON, \ + host->ioaddr + SDHCI_VENDOR_SPEC); + } else { + writel(orig_reg & ~SDHCI_VENDOR_SPEC_FRC_SDCLK_ON, \ + host->ioaddr + SDHCI_VENDOR_SPEC); + } + + return; + case SDHCI_HOST_CONTROL2: + orig_reg = readl(host->ioaddr + SDHCI_VENDOR_SPEC); + if (val & SDHCI_CTRL_VDD_180) { + orig_reg |= SDHCI_VENDOR_SPEC_VSELECT; + writel(orig_reg, host->ioaddr + SDHCI_VENDOR_SPEC); + } else { + orig_reg &= ~SDHCI_VENDOR_SPEC_VSELECT; + writel(orig_reg, host->ioaddr + SDHCI_VENDOR_SPEC); + } + + /* + * FSL sdhc controls bus and signal voltage via one bit + * VSELECT in VENDOR_SPEC, which has been set in + * SDHCI_POWER_CONTROL. So we skip the SDHCI_CTRL_VDD_180 + * here. + * + * ignore exec_tuning flag written to SDHCI_HOST_CONTROL2, + * tuning will be handled differently for FSL SDHC ip. + */ + orig_reg = readl(host->ioaddr + SDHCI_MIX_CTRL); + orig_reg &= ~SDHCI_MIX_CTRL_SMPCLK_SEL; + + orig_reg |= (val & SDHCI_CTRL_TUNED_CLK) + ? 0 : SDHCI_MIX_CTRL_SMPCLK_SEL; + + writel(orig_reg, host->ioaddr + SDHCI_MIX_CTRL); + + imx_data->uhs_mode = val & SDHCI_CTRL_UHS_MASK; + + return; case SDHCI_TRANSFER_MODE: /* * Postpone this write, we must do it together with a |