Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mmc: sdhci-esdhc-imx: add imx7d support and support HS400

The imx7d usdhc is derived from imx6sx, the difference is that
imx7d support HS400.

So introduce a new compatible string for imx7d and add HS400
support for imx7d usdhc.

Signed-off-by: Haibo Chen <haibo.chen@freescale.com>
Acked-by: Dong Aisheng <aisheng.dong@freescale.com>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>

authored by

Haibo Chen and committed by
Ulf Hansson
28b07674 2a2a7ea7

+83 -3
+83 -3
drivers/mmc/host/sdhci-esdhc-imx.c
··· 44 44 #define ESDHC_MIX_CTRL_EXE_TUNE (1 << 22) 45 45 #define ESDHC_MIX_CTRL_SMPCLK_SEL (1 << 23) 46 46 #define ESDHC_MIX_CTRL_FBCLK_SEL (1 << 25) 47 + #define ESDHC_MIX_CTRL_HS400_EN (1 << 26) 47 48 /* Bits 3 and 6 are not SDHCI standard definitions */ 48 49 #define ESDHC_MIX_CTRL_SDHCI_MASK 0xb7 49 50 /* Tuning bits */ ··· 60 59 #define ESDHC_TUNE_CTRL_STEP 1 61 60 #define ESDHC_TUNE_CTRL_MIN 0 62 61 #define ESDHC_TUNE_CTRL_MAX ((1 << 7) - 1) 62 + 63 + /* strobe dll register */ 64 + #define ESDHC_STROBE_DLL_CTRL 0x70 65 + #define ESDHC_STROBE_DLL_CTRL_ENABLE (1 << 0) 66 + #define ESDHC_STROBE_DLL_CTRL_RESET (1 << 1) 67 + #define ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_SHIFT 3 68 + 69 + #define ESDHC_STROBE_DLL_STATUS 0x74 70 + #define ESDHC_STROBE_DLL_STS_REF_LOCK (1 << 1) 71 + #define ESDHC_STROBE_DLL_STS_SLV_LOCK 0x1 63 72 64 73 #define ESDHC_TUNING_CTRL 0xcc 65 74 #define ESDHC_STD_TUNING_EN (1 << 24) ··· 131 120 #define ESDHC_FLAG_ERR004536 BIT(7) 132 121 /* The IP supports HS200 mode */ 133 122 #define ESDHC_FLAG_HS200 BIT(8) 123 + /* The IP supports HS400 mode */ 124 + #define ESDHC_FLAG_HS400 BIT(9) 125 + 126 + /* A higher clock ferquency than this rate requires strobell dll control */ 127 + #define ESDHC_STROBE_DLL_CLK_FREQ 100000000 134 128 135 129 struct esdhc_soc_data { 136 130 u32 flags; ··· 170 154 static struct esdhc_soc_data usdhc_imx6sx_data = { 171 155 .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING 172 156 | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200, 157 + }; 158 + 159 + static struct esdhc_soc_data usdhc_imx7d_data = { 160 + .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING 161 + | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200 162 + | ESDHC_FLAG_HS400, 173 163 }; 174 164 175 165 struct pltfm_imx_data { ··· 221 199 { .compatible = "fsl,imx6sx-usdhc", .data = &usdhc_imx6sx_data, }, 222 200 { .compatible = "fsl,imx6sl-usdhc", .data = &usdhc_imx6sl_data, }, 223 201 { .compatible = "fsl,imx6q-usdhc", .data = &usdhc_imx6q_data, }, 202 + { .compatible = "fsl,imx7d-usdhc", .data = &usdhc_imx7d_data, }, 224 203 { /* sentinel */ } 225 204 }; 226 205 MODULE_DEVICE_TABLE(of, imx_esdhc_dt_ids); ··· 297 274 val = SDHCI_SUPPORT_DDR50 | SDHCI_SUPPORT_SDR104 298 275 | SDHCI_SUPPORT_SDR50 299 276 | SDHCI_USE_SDR50_TUNING; 277 + 278 + if (imx_data->socdata->flags & ESDHC_FLAG_HS400) 279 + val |= SDHCI_SUPPORT_HS400; 300 280 } 301 281 } 302 282 ··· 800 774 break; 801 775 case MMC_TIMING_UHS_SDR104: 802 776 case MMC_TIMING_MMC_HS200: 777 + case MMC_TIMING_MMC_HS400: 803 778 pinctrl = imx_data->pins_200mhz; 804 779 break; 805 780 default: ··· 811 784 return pinctrl_select_state(imx_data->pinctrl, pinctrl); 812 785 } 813 786 787 + /* 788 + * For HS400 eMMC, there is a data_strobe line, this signal is generated 789 + * by the device and used for data output and CRC status response output 790 + * in HS400 mode. The frequency of this signal follows the frequency of 791 + * CLK generated by host. Host receive the data which is aligned to the 792 + * edge of data_strobe line. Due to the time delay between CLK line and 793 + * data_strobe line, if the delay time is larger than one clock cycle, 794 + * then CLK and data_strobe line will misaligned, read error shows up. 795 + * So when the CLK is higher than 100MHz, each clock cycle is short enough, 796 + * host should config the delay target. 797 + */ 798 + static void esdhc_set_strobe_dll(struct sdhci_host *host) 799 + { 800 + u32 v; 801 + 802 + if (host->mmc->actual_clock > ESDHC_STROBE_DLL_CLK_FREQ) { 803 + /* force a reset on strobe dll */ 804 + writel(ESDHC_STROBE_DLL_CTRL_RESET, 805 + host->ioaddr + ESDHC_STROBE_DLL_CTRL); 806 + /* 807 + * enable strobe dll ctrl and adjust the delay target 808 + * for the uSDHC loopback read clock 809 + */ 810 + v = ESDHC_STROBE_DLL_CTRL_ENABLE | 811 + (7 << ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_SHIFT); 812 + writel(v, host->ioaddr + ESDHC_STROBE_DLL_CTRL); 813 + /* wait 1us to make sure strobe dll status register stable */ 814 + udelay(1); 815 + v = readl(host->ioaddr + ESDHC_STROBE_DLL_STATUS); 816 + if (!(v & ESDHC_STROBE_DLL_STS_REF_LOCK)) 817 + dev_warn(mmc_dev(host->mmc), 818 + "warning! HS400 strobe DLL status REF not lock!\n"); 819 + if (!(v & ESDHC_STROBE_DLL_STS_SLV_LOCK)) 820 + dev_warn(mmc_dev(host->mmc), 821 + "warning! HS400 strobe DLL status SLV not lock!\n"); 822 + } 823 + } 824 + 814 825 static void esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned timing) 815 826 { 827 + u32 m; 816 828 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 817 829 struct pltfm_imx_data *imx_data = pltfm_host->priv; 818 830 struct esdhc_platform_data *boarddata = &imx_data->boarddata; 831 + 832 + /* disable ddr mode and disable HS400 mode */ 833 + m = readl(host->ioaddr + ESDHC_MIX_CTRL); 834 + m &= ~(ESDHC_MIX_CTRL_DDREN | ESDHC_MIX_CTRL_HS400_EN); 835 + imx_data->is_ddr = 0; 819 836 820 837 switch (timing) { 821 838 case MMC_TIMING_UHS_SDR12: ··· 867 796 case MMC_TIMING_UHS_SDR50: 868 797 case MMC_TIMING_UHS_SDR104: 869 798 case MMC_TIMING_MMC_HS200: 799 + writel(m, host->ioaddr + ESDHC_MIX_CTRL); 870 800 break; 871 801 case MMC_TIMING_UHS_DDR50: 872 802 case MMC_TIMING_MMC_DDR52: 873 - writel(readl(host->ioaddr + ESDHC_MIX_CTRL) | 874 - ESDHC_MIX_CTRL_DDREN, 875 - host->ioaddr + ESDHC_MIX_CTRL); 803 + m |= ESDHC_MIX_CTRL_DDREN; 804 + writel(m, host->ioaddr + ESDHC_MIX_CTRL); 876 805 imx_data->is_ddr = 1; 877 806 if (boarddata->delay_line) { 878 807 u32 v; ··· 883 812 v <<= 1; 884 813 writel(v, host->ioaddr + ESDHC_DLL_CTRL); 885 814 } 815 + break; 816 + case MMC_TIMING_MMC_HS400: 817 + m |= ESDHC_MIX_CTRL_DDREN | ESDHC_MIX_CTRL_HS400_EN; 818 + writel(m, host->ioaddr + ESDHC_MIX_CTRL); 819 + imx_data->is_ddr = 1; 820 + esdhc_set_strobe_dll(host); 886 821 break; 887 822 } 888 823 ··· 1176 1099 1177 1100 if (imx_data->socdata->flags & ESDHC_FLAG_ERR004536) 1178 1101 host->quirks |= SDHCI_QUIRK_BROKEN_ADMA; 1102 + 1103 + if (imx_data->socdata->flags & ESDHC_FLAG_HS400) 1104 + host->quirks2 |= SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400; 1179 1105 1180 1106 if (of_id) 1181 1107 err = sdhci_esdhc_imx_probe_dt(pdev, host, imx_data);