On Monday 31 August 2020 05:25:37 Andre Heider wrote:
> From: Wojciech Macek <w...@semihalf.com>
> 
> Add support for Marvell Xenon SDHCI HS200 mode.
> 
> Changes focue mostly on correct PHY initialization.
> All procedure is similar to the one done by Linux
> driver, but simplified.
> 
> Change-Id: I5e2396eeb23784f495abc18ea5a2eb7a92390730
> Signed-off-by: Wojciech Macek <w...@semihalf.com>
> Reviewed-on: http://vgitil04.il.marvell.com:8080/59230
> Tested-by: iSoC Platform CI <ykj...@marvell.com>
> Reviewed-by: Grzegorz Jaszczyk <j...@semihalf.com>
> Reviewed-by: Kostya Porotchkin <kos...@marvell.com>
> Reviewed-by: Igal Liberman <ig...@marvell.com>
> [a.heider: adapt to mainline]
> Signed-off-by: Andre Heider <a.hei...@gmail.com>
> ---
> Missing downstream patch, noticed while diffing branches:
> https://github.com/MarvellEmbeddedProcessors/u-boot-marvell/commit/387232507a0d9dda3990284221eaf87d7541dd02

Hello Andre! Why is this patch needed? Or what it is fixing?
I tested upstream U-Boot with all previous patches on Turris MOX and
Espressobin and SD cards worked fine.

>  drivers/mmc/xenon_sdhci.c | 335 ++++++++++++++++++++++++++++++++++++--
>  1 file changed, 323 insertions(+), 12 deletions(-)
> 
> diff --git a/drivers/mmc/xenon_sdhci.c b/drivers/mmc/xenon_sdhci.c
> index 7f9a579c83..ec255d30b0 100644
> --- a/drivers/mmc/xenon_sdhci.c
> +++ b/drivers/mmc/xenon_sdhci.c
> @@ -16,6 +16,7 @@
>  
>  #include <common.h>
>  #include <dm.h>
> +#include <dm/device_compat.h>
>  #include <fdtdec.h>
>  #include <linux/bitops.h>
>  #include <linux/delay.h>
> @@ -45,6 +46,7 @@ DECLARE_GLOBAL_DATA_PTR;
>  
>  #define SDHC_SLOT_EMMC_CTRL                  0x0130
>  #define ENABLE_DATA_STROBE_SHIFT             24
> +#define ENABLE_DATA_STROBE                   BIT(ENABLE_DATA_STROBE_SHIFT)
>  #define SET_EMMC_RSTN_SHIFT                  16
>  #define EMMC_VCCQ_MASK                               0x3
>  #define EMMC_VCCQ_1_8V                               0x1
> @@ -64,6 +66,7 @@ DECLARE_GLOBAL_DATA_PTR;
>  #define OUTPUT_QSN_PHASE_SELECT                      BIT(17)
>  #define SAMPL_INV_QSP_PHASE_SELECT           BIT(18)
>  #define SAMPL_INV_QSP_PHASE_SELECT_SHIFT     18
> +#define EMMC_PHY_SDIO_MODE                   BIT(28)
>  #define EMMC_PHY_SLOW_MODE                   BIT(29)
>  #define PHY_INITIALIZAION                    BIT(31)
>  #define WAIT_CYCLE_BEFORE_USING_MASK         0xf
> @@ -90,6 +93,7 @@ DECLARE_GLOBAL_DATA_PTR;
>  #define FC_QSN_RECEN                         BIT(27)
>  #define OEN_QSN                                      BIT(28)
>  #define AUTO_RECEN_CTRL                              BIT(30)
> +#define FC_ALL_CMOS_RECEIVER                 (REC_EN_MASK << REC_EN_SHIFT)
>  
>  #define EMMC_PHY_PAD_CONTROL1                        (EMMC_PHY_REG_BASE + 
> 0xc)
>  #define EMMC5_1_FC_QSP_PD                    BIT(9)
> @@ -99,10 +103,71 @@ DECLARE_GLOBAL_DATA_PTR;
>  #define EMMC5_1_FC_DQ_PD                     0xff
>  #define EMMC5_1_FC_DQ_PU                     (0xff << 16)
>  
> +#define EMMC_PHY_PAD_CONTROL2                        (EMMC_PHY_REG_BASE + 
> 0x10)
> +#define ZNR_MASK                             0x1F
> +#define ZNR_SHIFT                            8
> +#define ZPR_MASK                             0x1F
> +
>  #define SDHCI_RETUNE_EVT_INTSIG                      0x00001000
>  
> +#define SDHCI_HOST_CONTROL2          0x3E
> +#define  SDHCI_CTRL_UHS_MASK         0x0007
> +#define   SDHCI_CTRL_UHS_SDR12               0x0000
> +#define   SDHCI_CTRL_UHS_SDR25               0x0001
> +#define   SDHCI_CTRL_UHS_SDR50               0x0002
> +#define   SDHCI_CTRL_UHS_SDR104              0x0003
> +#define   SDHCI_CTRL_UHS_DDR50               0x0004
> +#define   SDHCI_CTRL_HS400           0x0005 /* Non-standard */
> +#define   SDHCI_CTRL_HS200_ONLY              0x0005 /* Non-standard */
> +#define   SDHCI_CTRL_HS400_ONLY              0x0006 /* Non-standard */
> +#define  SDHCI_CTRL_VDD_180          0x0008
> +#define  SDHCI_CTRL_DRV_TYPE_MASK    0x0030
> +#define   SDHCI_CTRL_DRV_TYPE_B              0x0000
> +#define   SDHCI_CTRL_DRV_TYPE_A              0x0010
> +#define   SDHCI_CTRL_DRV_TYPE_C              0x0020
> +#define   SDHCI_CTRL_DRV_TYPE_D              0x0030
> +#define  SDHCI_CTRL_EXEC_TUNING              0x0040
> +#define  SDHCI_CTRL_TUNED_CLK                0x0080
> +#define  SDHCI_CTRL_PRESET_VAL_ENABLE        0x8000
> +
> +/*
> + * Config to eMMC PHY to prepare for tuning.
> + * Enable HW DLL and set the TUNING_STEP
> + */
> +#define XENON_SLOT_DLL_CUR_DLY_VAL           0x0150
> +
> +#define XENON_SLOT_OP_STATUS_CTRL            0x0128
> +#define XENON_TUN_CONSECUTIVE_TIMES_SHIFT    16
> +#define XENON_TUN_CONSECUTIVE_TIMES_MASK     0x7
> +#define XENON_TUN_CONSECUTIVE_TIMES          0x4
> +#define XENON_TUNING_STEP_SHIFT                      12
> +#define XENON_TUNING_STEP_MASK                       0xF
> +#define XENON_TUNING_STEP_DIVIDER            BIT(6)
> +
> +#define XENON_EMMC_PHY_DLL_CONTROL           (EMMC_PHY_REG_BASE + 0x14)
> +#define XENON_EMMC_5_0_PHY_DLL_CONTROL               \
> +     (XENON_EMMC_5_0_PHY_REG_BASE + 0x10)
> +#define XENON_DLL_ENABLE                     BIT(31)
> +#define XENON_DLL_UPDATE_STROBE_5_0          BIT(30)
> +#define XENON_DLL_REFCLK_SEL                 BIT(30)
> +#define XENON_DLL_UPDATE                     BIT(23)
> +#define XENON_DLL_PHSEL1_SHIFT                       24
> +#define XENON_DLL_PHSEL0_SHIFT                       16
> +#define XENON_DLL_PHASE_MASK                 0x3F
> +#define XENON_DLL_PHASE_90_DEGREE            0x1F
> +#define XENON_DLL_FAST_LOCK                  BIT(5)
> +#define XENON_DLL_GAIN2X                     BIT(3)
> +#define XENON_DLL_BYPASS_EN                  BIT(0)
> +
> +#define XENON_SLOT_EXT_PRESENT_STATE         0x014C
> +#define XENON_DLL_LOCK_STATE                 0x1
> +
>  /* Hyperion only have one slot 0 */
>  #define XENON_MMC_SLOT_ID_HYPERION           0
> +#define SLOT_MASK(slot)                              BIT(slot)
> +
> +#define XENON_EMMC_PHY_LOGIC_TIMING_ADJUST   (EMMC_PHY_REG_BASE + 0x18)
> +#define XENON_LOGIC_TIMING_VALUE             0x00AA8977
>  
>  #define MMC_TIMING_LEGACY    0
>  #define MMC_TIMING_MMC_HS    1
> @@ -266,6 +331,176 @@ static int xenon_mmc_start_signal_voltage_switch(struct 
> sdhci_host *host)
>       return ret;
>  }
>  
> +/*
> + * Xenon defines different values for HS200 and HS400
> + * in Host_Control_2
> + */
> +static void xenon_set_uhs_signaling(struct sdhci_host *host,
> +                                 unsigned int timing)
> +{
> +     u16 ctrl_2;
> +
> +     ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> +     /* Select Bus Speed Mode for host */
> +     ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
> +     if (timing == MMC_TIMING_MMC_HS200)
> +             ctrl_2 |= SDHCI_CTRL_HS200_ONLY;
> +     else if (timing == MMC_TIMING_UHS_SDR104)
> +             ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
> +     else if (timing == MMC_TIMING_UHS_SDR12)
> +             ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
> +     else if (timing == MMC_TIMING_UHS_SDR25)
> +             ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
> +     else if (timing == MMC_TIMING_UHS_SDR50)
> +             ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
> +     else if ((timing == MMC_TIMING_UHS_DDR50) ||
> +              (timing == MMC_TIMING_MMC_DDR52))
> +             ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
> +     else if (timing == MMC_TIMING_MMC_HS400)
> +             ctrl_2 |= SDHCI_CTRL_HS400_ONLY;
> +     sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
> +}
> +
> +/*
> + * If eMMC PHY Slow Mode is required in lower speed mode (SDCLK < 55MHz)
> + * in SDR mode, enable Slow Mode to bypass eMMC PHY.
> + * SDIO slower SDR mode also requires Slow Mode.
> + *
> + * If Slow Mode is enabled, return 0.
> + * Otherwise, return -EINVAL.
> + */
> +static int xenon_emmc_phy_slow_mode(struct sdhci_host *host,
> +                                 unsigned char timing)
> +{
> +     u32 reg;
> +     int ret = -EINVAL;
> +
> +     if (host->mmc->tran_speed > 52000000)
> +             return -EINVAL;
> +
> +     reg = sdhci_readl(host, EMMC_PHY_TIMING_ADJUST);
> +     /* When in slower SDR mode, enable Slow Mode for SDIO */
> +     switch (timing) {
> +     case MMC_TIMING_LEGACY:
> +             /*
> +              * If Slow Mode is required, enable Slow Mode by default
> +              * in early init phase to avoid any potential issue.
> +              */
> +             reg |= EMMC_PHY_SLOW_MODE;
> +             ret = 0;
> +             break;
> +     case MMC_TIMING_UHS_SDR25:
> +     case MMC_TIMING_UHS_SDR12:
> +     case MMC_TIMING_SD_HS:
> +     case MMC_TIMING_MMC_HS:
> +             if (IS_SD(host->mmc)) {
> +                     reg |= EMMC_PHY_SLOW_MODE;
> +                     ret = 0;
> +                     break;
> +             }
> +     default:
> +             reg &= ~EMMC_PHY_SLOW_MODE;
> +             ret = -EINVAL;
> +     }
> +
> +     sdhci_writel(host, reg, EMMC_PHY_TIMING_ADJUST);
> +     return ret;
> +}
> +
> +static void xenon_emmc_phy_disable_data_strobe(struct sdhci_host *host)
> +{
> +     u32 reg;
> +
> +     /* Disable SDHC Data Strobe */
> +     reg = sdhci_readl(host, SDHC_SLOT_EMMC_CTRL);
> +     reg &= ~ENABLE_DATA_STROBE;
> +     sdhci_writel(host, reg, SDHC_SLOT_EMMC_CTRL);
> +}
> +
> +/*
> + * Enable eMMC PHY HW DLL
> + * DLL should be enabled and stable before HS200/SDR104 tuning,
> + * and before HS400 data strobe setting.
> + */
> +static int xenon_emmc_phy_enable_dll(struct sdhci_host *host)
> +{
> +     u32 reg;
> +     u32 timeout;
> +
> +     if (host->mmc->tran_speed <= 52000000)
> +             return -EINVAL;
> +
> +     reg = sdhci_readl(host, XENON_EMMC_PHY_DLL_CONTROL);
> +     if (reg & XENON_DLL_ENABLE)
> +             return 0;
> +
> +     /* Enable DLL */
> +     reg = sdhci_readl(host, XENON_EMMC_PHY_DLL_CONTROL);
> +     reg |= (XENON_DLL_ENABLE | XENON_DLL_FAST_LOCK);
> +
> +     /*
> +      * Set Phase as 90 degree, which is most common value.
> +      * Might set another value if necessary.
> +      * The granularity is 1 degree.
> +      */
> +     reg &= ~((XENON_DLL_PHASE_MASK << XENON_DLL_PHSEL0_SHIFT) |
> +              (XENON_DLL_PHASE_MASK << XENON_DLL_PHSEL1_SHIFT));
> +     reg |= ((XENON_DLL_PHASE_90_DEGREE << XENON_DLL_PHSEL0_SHIFT) |
> +             (XENON_DLL_PHASE_90_DEGREE << XENON_DLL_PHSEL1_SHIFT));
> +
> +     reg &= ~(XENON_DLL_BYPASS_EN | XENON_DLL_REFCLK_SEL);
> +     reg |= XENON_DLL_UPDATE;
> +     sdhci_writel(host, reg, XENON_EMMC_PHY_DLL_CONTROL);
> +
> +     /* Wait max 32 ms */
> +     timeout = 32;
> +     while (!(sdhci_readw(host, XENON_SLOT_EXT_PRESENT_STATE) &
> +             XENON_DLL_LOCK_STATE)) {
> +             if (timeout > 32) {
> +                     printf("Wait for DLL Lock time-out\n");
> +                     return -ETIMEDOUT;
> +             }
> +             udelay(1000);
> +             timeout++;
> +     }
> +     return 0;
> +}
> +
> +static int xenon_emmc_phy_config_tuning(struct sdhci_host *host)
> +{
> +     u32 reg, tuning_step;
> +     int ret;
> +
> +     if (host->mmc->tran_speed <= 52000000)
> +             return -EINVAL;
> +
> +     ret = xenon_emmc_phy_enable_dll(host);
> +     if (ret)
> +             return ret;
> +
> +     /* Achieve TUNING_STEP with HW DLL help */
> +     reg = sdhci_readl(host, XENON_SLOT_DLL_CUR_DLY_VAL);
> +     tuning_step = reg / XENON_TUNING_STEP_DIVIDER;
> +     if (unlikely(tuning_step > XENON_TUNING_STEP_MASK)) {
> +             dev_warn(mmc_dev(host->mmc),
> +                      "HS200 TUNING_STEP %d is larger than MAX value\n",
> +                      tuning_step);
> +             tuning_step = XENON_TUNING_STEP_MASK;
> +     }
> +
> +     /* Set TUNING_STEP for later tuning */
> +     reg = sdhci_readl(host, XENON_SLOT_OP_STATUS_CTRL);
> +     reg &= ~(XENON_TUN_CONSECUTIVE_TIMES_MASK <<
> +              XENON_TUN_CONSECUTIVE_TIMES_SHIFT);
> +     reg |= (XENON_TUN_CONSECUTIVE_TIMES <<
> +             XENON_TUN_CONSECUTIVE_TIMES_SHIFT);
> +     reg &= ~(XENON_TUNING_STEP_MASK << XENON_TUNING_STEP_SHIFT);
> +     reg |= (tuning_step << XENON_TUNING_STEP_SHIFT);
> +     sdhci_writel(host, reg, XENON_SLOT_OP_STATUS_CTRL);
> +
> +     return 0;
> +}
> +
>  static void xenon_mmc_phy_set(struct sdhci_host *host)
>  {
>       struct xenon_sdhci_priv *priv = host->mmc->priv;
> @@ -273,8 +508,8 @@ static void xenon_mmc_phy_set(struct sdhci_host *host)
>  
>       /* Setup pad, set bit[30], bit[28] and bits[26:24] */
>       var = sdhci_readl(host, EMMC_PHY_PAD_CONTROL);
> -     var |= AUTO_RECEN_CTRL | OEN_QSN | FC_QSP_RECEN |
> -             FC_CMD_RECEN | FC_DQ_RECEN;
> +     var |= OEN_QSN | FC_QSP_RECEN | FC_CMD_RECEN | FC_DQ_RECEN |
> +             FC_ALL_CMOS_RECEIVER;
>       sdhci_writel(host, var, EMMC_PHY_PAD_CONTROL);
>  
>       /* Set CMD and DQ Pull Up */
> @@ -284,20 +519,45 @@ static void xenon_mmc_phy_set(struct sdhci_host *host)
>       sdhci_writel(host, var, EMMC_PHY_PAD_CONTROL1);
>  
>       /*
> -      * If timing belongs to high speed, set bit[17] of
> +      * If Timing belongs to high speed, clear bit[17] of
>        * EMMC_PHY_TIMING_ADJUST register
>        */
> +     var = sdhci_readl(host, EMMC_PHY_TIMING_ADJUST);
>       if ((priv->timing == MMC_TIMING_MMC_HS400) ||
>           (priv->timing == MMC_TIMING_MMC_HS200) ||
> +         (priv->timing == MMC_TIMING_MMC_DDR52) ||
>           (priv->timing == MMC_TIMING_UHS_SDR50) ||
>           (priv->timing == MMC_TIMING_UHS_SDR104) ||
>           (priv->timing == MMC_TIMING_UHS_DDR50) ||
> -         (priv->timing == MMC_TIMING_UHS_SDR25) ||
> -         (priv->timing == MMC_TIMING_MMC_DDR52)) {
> -             var = sdhci_readl(host, EMMC_PHY_TIMING_ADJUST);
> -             var |= OUTPUT_QSN_PHASE_SELECT;
> +         (priv->timing == MMC_TIMING_UHS_SDR25)) {
> +             var &= ~OUTPUT_QSN_PHASE_SELECT;
>               sdhci_writel(host, var, EMMC_PHY_TIMING_ADJUST);
>       }
> +     if (priv->timing == MMC_TIMING_LEGACY) {
> +             xenon_emmc_phy_slow_mode(host, priv->timing);
> +             goto phy_init;
> +     }
> +
> +     /*
> +      * If SDIO card, set SDIO Mode
> +      * Otherwise, clear SDIO Mode
> +      */
> +     var = sdhci_readl(host, EMMC_PHY_TIMING_ADJUST);
> +     if (IS_SD(host->mmc))
> +             var |= EMMC_PHY_SDIO_MODE;
> +     else
> +             var &= ~EMMC_PHY_SDIO_MODE;
> +     sdhci_writel(host, var, EMMC_PHY_TIMING_ADJUST);
> +
> +     /*
> +      * Set preferred ZNR and ZPR value
> +      * The ZNR and ZPR value vary between different boards.
> +      * Define them both in sdhci-xenon-emmc-phy.h.
> +      */
> +     var = sdhci_readl(host, EMMC_PHY_PAD_CONTROL2);
> +     var &= ~((ZNR_MASK << ZNR_SHIFT) | ZPR_MASK);
> +     var |= ((0xf << ZNR_SHIFT) | 0xf);
> +     sdhci_writel(host, var, EMMC_PHY_PAD_CONTROL2);
>  
>       /*
>        * When setting EMMC_PHY_FUNC_CONTROL register,
> @@ -308,11 +568,21 @@ static void xenon_mmc_phy_set(struct sdhci_host *host)
>       sdhci_writew(host, var, SDHCI_CLOCK_CONTROL);
>  
>       var = sdhci_readl(host, EMMC_PHY_FUNC_CONTROL);
> -     if (host->mmc->ddr_mode) {
> -             var |= (DQ_DDR_MODE_MASK << DQ_DDR_MODE_SHIFT) | CMD_DDR_MODE;
> -     } else {
> +     switch (priv->timing) {
> +     case MMC_TIMING_MMC_HS400:
> +             var |= (DQ_DDR_MODE_MASK << DQ_DDR_MODE_SHIFT) |
> +                    CMD_DDR_MODE;
> +             var &= ~DQ_ASYNC_MODE;
> +             break;
> +     case MMC_TIMING_UHS_DDR50:
> +     case MMC_TIMING_MMC_DDR52:
> +             var |= (DQ_DDR_MODE_MASK << DQ_DDR_MODE_SHIFT) |
> +                    CMD_DDR_MODE | DQ_ASYNC_MODE;
> +             break;
> +     default:
>               var &= ~((DQ_DDR_MODE_MASK << DQ_DDR_MODE_SHIFT) |
>                        CMD_DDR_MODE);
> +             var |= DQ_ASYNC_MODE;
>       }
>       sdhci_writel(host, var, EMMC_PHY_FUNC_CONTROL);
>  
> @@ -321,7 +591,26 @@ static void xenon_mmc_phy_set(struct sdhci_host *host)
>       var |= SDHCI_CLOCK_CARD_EN;
>       sdhci_writew(host, var, SDHCI_CLOCK_CONTROL);
>  
> +     udelay(1000);
> +
> +     /* Quirk, value suggested by hardware team */
> +     if (priv->timing == MMC_TIMING_MMC_HS400)
> +             /* Hardware team recommend a value for HS400 */
> +             sdhci_writel(host, XENON_EMMC_PHY_LOGIC_TIMING_ADJUST,
> +                          XENON_LOGIC_TIMING_VALUE);
> +     else
> +             xenon_emmc_phy_disable_data_strobe(host);
> +
> +phy_init:
> +
> +     xenon_set_uhs_signaling(host, priv->timing);
>       xenon_mmc_phy_init(host);
> +
> +     if ((priv->timing == MMC_TIMING_MMC_HS400) ||
> +         (priv->timing == MMC_TIMING_MMC_HS200)) {
> +             if (xenon_emmc_phy_config_tuning(host) != 0)
> +                     printf("Error, failed to tune MMC PHY\n");
> +     }
>  }
>  
>  /* Enable/Disable the Auto Clock Gating function of this slot */
> @@ -338,8 +627,6 @@ static void xenon_mmc_set_acg(struct sdhci_host *host, 
> bool enable)
>       sdhci_writel(host, var, SDHC_SYS_OP_CTRL);
>  }
>  
> -#define SLOT_MASK(slot)              BIT(slot)
> -
>  /* Enable specific slot */
>  static void xenon_mmc_enable_slot(struct sdhci_host *host, u8 slot)
>  {
> @@ -391,6 +678,7 @@ static int xenon_sdhci_set_ios_post(struct sdhci_host 
> *host)
>       struct xenon_sdhci_priv *priv = host->mmc->priv;
>       uint speed = host->mmc->tran_speed;
>       int pwr_18v = 0;
> +     u32 reg;
>  
>       /*
>        * Signal Voltage Switching is only applicable for Host Controllers
> @@ -423,12 +711,22 @@ static int xenon_sdhci_set_ios_post(struct sdhci_host 
> *host)
>               /* eMMC */
>               if (host->mmc->ddr_mode)
>                       priv->timing = MMC_TIMING_MMC_DDR52;
> +             else if (speed == 200000000)
> +                     priv->timing = MMC_TIMING_MMC_HS200;
>               else if (speed <= 26000000)
>                       priv->timing = MMC_TIMING_LEGACY;
>               else
>                       priv->timing = MMC_TIMING_MMC_HS;
>       }
>  
> +     if ((priv->timing == MMC_TIMING_MMC_HS400) ||
> +         (priv->timing == MMC_TIMING_MMC_HS200) ||
> +         (priv->timing == MMC_TIMING_MMC_HS)) {
> +             reg = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> +             reg &= ~SDHCI_CTRL_PRESET_VAL_ENABLE;
> +             sdhci_writew(host, reg, SDHCI_HOST_CONTROL2);
> +     }
> +
>       /* Re-init the PHY */
>       xenon_mmc_phy_set(host);
>  
> @@ -447,6 +745,7 @@ static int xenon_sdhci_probe(struct udevice *dev)
>       struct xenon_sdhci_priv *priv = dev_get_priv(dev);
>       struct sdhci_host *host = dev_get_priv(dev);
>       int ret;
> +     int len;
>  
>       host->mmc = &plat->mmc;
>       host->mmc->priv = host;
> @@ -500,6 +799,18 @@ static int xenon_sdhci_probe(struct udevice *dev)
>               return -EINVAL;
>       }
>  
> +     /* Support for High Speed modes */
> +     if (fdt_getprop(gd->fdt_blob,
> +                     dev_of_offset(dev), "mmc-hs400-1_8v", &len) != NULL) {
> +             host->host_caps |= (MMC_MODE_HS400 | MMC_MODE_HS200);
> +             sdhci_writeb(host,  SDHCI_POWER_180 |
> +                          SDHCI_POWER_ON, SDHCI_POWER_CONTROL);
> +     }
> +     if (fdt_getprop(gd->fdt_blob,
> +                     dev_of_offset(dev), "mmc-hs200-1_8v", &len) != NULL) {
> +             host->host_caps |= MMC_MODE_HS200;
> +     }
> +
>       host->ops = &xenon_sdhci_ops;
>  
>       host->max_clk = XENON_MMC_MAX_CLK;
> -- 
> 2.28.0
> 

Reply via email to