uboot/drivers/mmc/xenon_sdhci.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Driver for Marvell SOC Platform Group Xenon SDHC as a platform device
   4 *
   5 * Copyright (C) 2016 Marvell, All Rights Reserved.
   6 *
   7 * Author:      Victor Gu <xigu@marvell.com>
   8 * Date:        2016-8-24
   9 *
  10 * Included parts of the Linux driver version which was written by:
  11 * Hu Ziji <huziji@marvell.com>
  12 *
  13 * Ported to from Marvell 2015.01 to mainline U-Boot 2017.01:
  14 * Stefan Roese <sr@denx.de>
  15 */
  16
  17#include <common.h>
  18#include <dm.h>
  19#include <fdtdec.h>
  20#include <asm/global_data.h>
  21#include <linux/bitops.h>
  22#include <linux/delay.h>
  23#include <linux/libfdt.h>
  24#include <malloc.h>
  25#include <sdhci.h>
  26#include <power/regulator.h>
  27
  28DECLARE_GLOBAL_DATA_PTR;
  29
  30/* Register Offset of SD Host Controller SOCP self-defined register */
  31#define SDHC_SYS_CFG_INFO                       0x0104
  32#define SLOT_TYPE_SDIO_SHIFT                    24
  33#define SLOT_TYPE_EMMC_MASK                     0xFF
  34#define SLOT_TYPE_EMMC_SHIFT                    16
  35#define SLOT_TYPE_SD_SDIO_MMC_MASK              0xFF
  36#define SLOT_TYPE_SD_SDIO_MMC_SHIFT             8
  37#define NR_SUPPORTED_SLOT_MASK                  0x7
  38
  39#define SDHC_SYS_OP_CTRL                        0x0108
  40#define AUTO_CLKGATE_DISABLE_MASK               BIT(20)
  41#define SDCLK_IDLEOFF_ENABLE_SHIFT              8
  42#define SLOT_ENABLE_SHIFT                       0
  43
  44#define SDHC_SYS_EXT_OP_CTRL                    0x010C
  45#define MASK_CMD_CONFLICT_ERROR                 BIT(8)
  46
  47#define SDHC_SLOT_EMMC_CTRL                     0x0130
  48#define ENABLE_DATA_STROBE_SHIFT                24
  49#define SET_EMMC_RSTN_SHIFT                     16
  50#define EMMC_VCCQ_MASK                          0x3
  51#define EMMC_VCCQ_1_8V                          0x1
  52#define EMMC_VCCQ_1_2V                          0x2
  53#define EMMC_VCCQ_3_3V                          0x3
  54
  55#define SDHC_SLOT_RETUNING_REQ_CTRL             0x0144
  56/* retuning compatible */
  57#define RETUNING_COMPATIBLE                     0x1
  58
  59/* Xenon specific Mode Select value */
  60#define XENON_SDHCI_CTRL_HS200                  0x5
  61#define XENON_SDHCI_CTRL_HS400                  0x6
  62
  63#define EMMC_PHY_REG_BASE                       0x170
  64#define EMMC_PHY_TIMING_ADJUST                  EMMC_PHY_REG_BASE
  65#define OUTPUT_QSN_PHASE_SELECT                 BIT(17)
  66#define SAMPL_INV_QSP_PHASE_SELECT              BIT(18)
  67#define SAMPL_INV_QSP_PHASE_SELECT_SHIFT        18
  68#define EMMC_PHY_SLOW_MODE                      BIT(29)
  69#define PHY_INITIALIZAION                       BIT(31)
  70#define WAIT_CYCLE_BEFORE_USING_MASK            0xf
  71#define WAIT_CYCLE_BEFORE_USING_SHIFT           12
  72#define FC_SYNC_EN_DURATION_MASK                0xf
  73#define FC_SYNC_EN_DURATION_SHIFT               8
  74#define FC_SYNC_RST_EN_DURATION_MASK            0xf
  75#define FC_SYNC_RST_EN_DURATION_SHIFT           4
  76#define FC_SYNC_RST_DURATION_MASK               0xf
  77#define FC_SYNC_RST_DURATION_SHIFT              0
  78
  79#define EMMC_PHY_FUNC_CONTROL                   (EMMC_PHY_REG_BASE + 0x4)
  80#define DQ_ASYNC_MODE                           BIT(4)
  81#define DQ_DDR_MODE_SHIFT                       8
  82#define DQ_DDR_MODE_MASK                        0xff
  83#define CMD_DDR_MODE                            BIT(16)
  84
  85#define EMMC_PHY_PAD_CONTROL                    (EMMC_PHY_REG_BASE + 0x8)
  86#define REC_EN_SHIFT                            24
  87#define REC_EN_MASK                             0xf
  88#define FC_DQ_RECEN                             BIT(24)
  89#define FC_CMD_RECEN                            BIT(25)
  90#define FC_QSP_RECEN                            BIT(26)
  91#define FC_QSN_RECEN                            BIT(27)
  92#define OEN_QSN                                 BIT(28)
  93#define AUTO_RECEN_CTRL                         BIT(30)
  94
  95#define EMMC_PHY_PAD_CONTROL1                   (EMMC_PHY_REG_BASE + 0xc)
  96#define EMMC5_1_FC_QSP_PD                       BIT(9)
  97#define EMMC5_1_FC_QSP_PU                       BIT(25)
  98#define EMMC5_1_FC_CMD_PD                       BIT(8)
  99#define EMMC5_1_FC_CMD_PU                       BIT(24)
 100#define EMMC5_1_FC_DQ_PD                        0xff
 101#define EMMC5_1_FC_DQ_PU                        (0xff << 16)
 102
 103#define SDHCI_RETUNE_EVT_INTSIG                 0x00001000
 104
 105/* Hyperion only have one slot 0 */
 106#define XENON_MMC_SLOT_ID_HYPERION              0
 107
 108#define XENON_MMC_MAX_CLK       400000000
 109#define XENON_MMC_3V3_UV        3300000
 110#define XENON_MMC_1V8_UV        1800000
 111
 112enum soc_pad_ctrl_type {
 113        SOC_PAD_SD,
 114        SOC_PAD_FIXED_1_8V,
 115};
 116
 117struct xenon_sdhci_plat {
 118        struct mmc_config cfg;
 119        struct mmc mmc;
 120};
 121
 122struct xenon_sdhci_priv {
 123        struct sdhci_host host;
 124
 125        u8 timing;
 126
 127        unsigned int clock;
 128
 129        void *pad_ctrl_reg;
 130        int pad_type;
 131
 132        struct udevice *vqmmc;
 133};
 134
 135static int xenon_mmc_phy_init(struct sdhci_host *host)
 136{
 137        struct xenon_sdhci_priv *priv = host->mmc->priv;
 138        u32 clock = priv->clock;
 139        u32 time;
 140        u32 var;
 141
 142        /* Enable QSP PHASE SELECT */
 143        var = sdhci_readl(host, EMMC_PHY_TIMING_ADJUST);
 144        var |= SAMPL_INV_QSP_PHASE_SELECT;
 145        if ((priv->timing == MMC_TIMING_UHS_SDR50) ||
 146            (priv->timing == MMC_TIMING_UHS_SDR25) ||
 147            (priv->timing == MMC_TIMING_UHS_SDR12) ||
 148            (priv->timing == MMC_TIMING_SD_HS) ||
 149            (priv->timing == MMC_TIMING_LEGACY))
 150                var |= EMMC_PHY_SLOW_MODE;
 151        sdhci_writel(host, var, EMMC_PHY_TIMING_ADJUST);
 152
 153        /* Poll for host MMC PHY clock init to be stable */
 154        /* Wait up to 10ms */
 155        time = 100;
 156        while (time--) {
 157                var = sdhci_readl(host, SDHCI_CLOCK_CONTROL);
 158                if (var & SDHCI_CLOCK_INT_STABLE)
 159                        break;
 160
 161                udelay(100);
 162        }
 163
 164        if (time <= 0) {
 165                pr_err("Failed to enable MMC internal clock in time\n");
 166                return -ETIMEDOUT;
 167        }
 168
 169        /* Init PHY */
 170        var = sdhci_readl(host, EMMC_PHY_TIMING_ADJUST);
 171        var |= PHY_INITIALIZAION;
 172        sdhci_writel(host, var, EMMC_PHY_TIMING_ADJUST);
 173
 174        if (clock == 0) {
 175                /* Use the possibly slowest bus frequency value */
 176                clock = 100000;
 177        }
 178
 179        /* Poll for host eMMC PHY init to complete */
 180        /* Wait up to 10ms */
 181        time = 100;
 182        while (time--) {
 183                var = sdhci_readl(host, EMMC_PHY_TIMING_ADJUST);
 184                var &= PHY_INITIALIZAION;
 185                if (!var)
 186                        break;
 187
 188                /* wait for host eMMC PHY init to complete */
 189                udelay(100);
 190        }
 191
 192        if (time <= 0) {
 193                pr_err("Failed to init MMC PHY in time\n");
 194                return -ETIMEDOUT;
 195        }
 196
 197        return 0;
 198}
 199
 200#define ARMADA_3700_SOC_PAD_1_8V        0x1
 201#define ARMADA_3700_SOC_PAD_3_3V        0x0
 202
 203static void armada_3700_soc_pad_voltage_set(struct sdhci_host *host)
 204{
 205        struct xenon_sdhci_priv *priv = host->mmc->priv;
 206
 207        if (priv->pad_type == SOC_PAD_FIXED_1_8V)
 208                writel(ARMADA_3700_SOC_PAD_1_8V, priv->pad_ctrl_reg);
 209        else if (priv->pad_type == SOC_PAD_SD)
 210                writel(ARMADA_3700_SOC_PAD_3_3V, priv->pad_ctrl_reg);
 211}
 212
 213static int xenon_mmc_start_signal_voltage_switch(struct sdhci_host *host)
 214{
 215        struct xenon_sdhci_priv *priv = host->mmc->priv;
 216        u8 voltage;
 217        u32 ctrl;
 218        int ret = 0;
 219
 220        /* If there is no vqmmc regulator, return */
 221        if (!priv->vqmmc)
 222                return 0;
 223
 224        if (priv->pad_type == SOC_PAD_FIXED_1_8V) {
 225                /* Switch to 1.8v */
 226                ret = regulator_set_value(priv->vqmmc,
 227                                          XENON_MMC_1V8_UV);
 228        } else if (priv->pad_type == SOC_PAD_SD) {
 229                /* Get voltage info */
 230                voltage = sdhci_readb(host, SDHCI_POWER_CONTROL);
 231                voltage &= ~SDHCI_POWER_ON;
 232
 233                if (voltage == SDHCI_POWER_330) {
 234                        /* Switch to 3.3v */
 235                        ret = regulator_set_value(priv->vqmmc,
 236                                                  XENON_MMC_3V3_UV);
 237                } else {
 238                        /* Switch to 1.8v */
 239                        ret = regulator_set_value(priv->vqmmc,
 240                                                  XENON_MMC_1V8_UV);
 241                }
 242        }
 243
 244        /* Set VCCQ, eMMC mode: 1.8V; SD/SDIO mode: 3.3V */
 245        ctrl = sdhci_readl(host, SDHC_SLOT_EMMC_CTRL);
 246        if (IS_SD(host->mmc))
 247                ctrl |= EMMC_VCCQ_3_3V;
 248        else
 249                ctrl |= EMMC_VCCQ_1_8V;
 250        sdhci_writel(host, ctrl, SDHC_SLOT_EMMC_CTRL);
 251
 252        if (ret)
 253                printf("Signal voltage switch fail\n");
 254
 255        return ret;
 256}
 257
 258static void xenon_mmc_phy_set(struct sdhci_host *host)
 259{
 260        struct xenon_sdhci_priv *priv = host->mmc->priv;
 261        u32 var;
 262
 263        /* Setup pad, set bit[30], bit[28] and bits[26:24] */
 264        var = sdhci_readl(host, EMMC_PHY_PAD_CONTROL);
 265        var |= AUTO_RECEN_CTRL | OEN_QSN | FC_QSP_RECEN |
 266                FC_CMD_RECEN | FC_DQ_RECEN;
 267        sdhci_writel(host, var, EMMC_PHY_PAD_CONTROL);
 268
 269        /* Set CMD and DQ Pull Up */
 270        var = sdhci_readl(host, EMMC_PHY_PAD_CONTROL1);
 271        var |= (EMMC5_1_FC_CMD_PU | EMMC5_1_FC_DQ_PU);
 272        var &= ~(EMMC5_1_FC_CMD_PD | EMMC5_1_FC_DQ_PD);
 273        sdhci_writel(host, var, EMMC_PHY_PAD_CONTROL1);
 274
 275        /*
 276         * If timing belongs to high speed, set bit[17] of
 277         * EMMC_PHY_TIMING_ADJUST register
 278         */
 279        if ((priv->timing == MMC_TIMING_MMC_HS400) ||
 280            (priv->timing == MMC_TIMING_MMC_HS200) ||
 281            (priv->timing == MMC_TIMING_UHS_SDR50) ||
 282            (priv->timing == MMC_TIMING_UHS_SDR104) ||
 283            (priv->timing == MMC_TIMING_UHS_DDR50) ||
 284            (priv->timing == MMC_TIMING_UHS_SDR25) ||
 285            (priv->timing == MMC_TIMING_MMC_DDR52)) {
 286                var = sdhci_readl(host, EMMC_PHY_TIMING_ADJUST);
 287                var |= OUTPUT_QSN_PHASE_SELECT;
 288                sdhci_writel(host, var, EMMC_PHY_TIMING_ADJUST);
 289        }
 290
 291        /*
 292         * When setting EMMC_PHY_FUNC_CONTROL register,
 293         * SD clock should be disabled
 294         */
 295        var = sdhci_readl(host, SDHCI_CLOCK_CONTROL);
 296        var &= ~SDHCI_CLOCK_CARD_EN;
 297        sdhci_writew(host, var, SDHCI_CLOCK_CONTROL);
 298
 299        var = sdhci_readl(host, EMMC_PHY_FUNC_CONTROL);
 300        if (host->mmc->ddr_mode) {
 301                var |= (DQ_DDR_MODE_MASK << DQ_DDR_MODE_SHIFT) | CMD_DDR_MODE;
 302        } else {
 303                var &= ~((DQ_DDR_MODE_MASK << DQ_DDR_MODE_SHIFT) |
 304                         CMD_DDR_MODE);
 305        }
 306        sdhci_writel(host, var, EMMC_PHY_FUNC_CONTROL);
 307
 308        /* Enable bus clock */
 309        var = sdhci_readl(host, SDHCI_CLOCK_CONTROL);
 310        var |= SDHCI_CLOCK_CARD_EN;
 311        sdhci_writew(host, var, SDHCI_CLOCK_CONTROL);
 312
 313        xenon_mmc_phy_init(host);
 314}
 315
 316/* Enable/Disable the Auto Clock Gating function of this slot */
 317static void xenon_mmc_set_acg(struct sdhci_host *host, bool enable)
 318{
 319        u32 var;
 320
 321        var = sdhci_readl(host, SDHC_SYS_OP_CTRL);
 322        if (enable)
 323                var &= ~AUTO_CLKGATE_DISABLE_MASK;
 324        else
 325                var |= AUTO_CLKGATE_DISABLE_MASK;
 326
 327        sdhci_writel(host, var, SDHC_SYS_OP_CTRL);
 328}
 329
 330#define SLOT_MASK(slot)         BIT(slot)
 331
 332/* Enable specific slot */
 333static void xenon_mmc_enable_slot(struct sdhci_host *host, u8 slot)
 334{
 335        u32 var;
 336
 337        var = sdhci_readl(host, SDHC_SYS_OP_CTRL);
 338        var |= SLOT_MASK(slot) << SLOT_ENABLE_SHIFT;
 339        sdhci_writel(host, var, SDHC_SYS_OP_CTRL);
 340}
 341
 342/* Disable specific slot */
 343static void xenon_mmc_disable_slot(struct sdhci_host *host, u8 slot)
 344{
 345        u32 var;
 346
 347        var = sdhci_readl(host, SDHC_SYS_OP_CTRL);
 348        var &= ~(SLOT_MASK(slot) << SLOT_ENABLE_SHIFT);
 349        sdhci_writel(host, var, SDHC_SYS_OP_CTRL);
 350}
 351
 352/* Enable Parallel Transfer Mode */
 353static void xenon_mmc_enable_parallel_tran(struct sdhci_host *host, u8 slot)
 354{
 355        u32 var;
 356
 357        var = sdhci_readl(host, SDHC_SYS_EXT_OP_CTRL);
 358        var |= SLOT_MASK(slot);
 359        sdhci_writel(host, var, SDHC_SYS_EXT_OP_CTRL);
 360}
 361
 362static void xenon_mmc_disable_tuning(struct sdhci_host *host, u8 slot)
 363{
 364        u32 var;
 365
 366        /* Clear the Re-Tuning Request functionality */
 367        var = sdhci_readl(host, SDHC_SLOT_RETUNING_REQ_CTRL);
 368        var &= ~RETUNING_COMPATIBLE;
 369        sdhci_writel(host, var, SDHC_SLOT_RETUNING_REQ_CTRL);
 370
 371        /* Clear the Re-tuning Event Signal Enable */
 372        var = sdhci_readl(host, SDHCI_SIGNAL_ENABLE);
 373        var &= ~SDHCI_RETUNE_EVT_INTSIG;
 374        sdhci_writel(host, var, SDHCI_SIGNAL_ENABLE);
 375}
 376
 377/* Mask command conflict error */
 378static void xenon_mask_cmd_conflict_err(struct sdhci_host *host)
 379{
 380        u32  reg;
 381
 382        reg = sdhci_readl(host, SDHC_SYS_EXT_OP_CTRL);
 383        reg |= MASK_CMD_CONFLICT_ERROR;
 384        sdhci_writel(host, reg, SDHC_SYS_EXT_OP_CTRL);
 385}
 386
 387/* Platform specific function for post set_ios configuration */
 388static int xenon_sdhci_set_ios_post(struct sdhci_host *host)
 389{
 390        struct xenon_sdhci_priv *priv = host->mmc->priv;
 391        uint speed = host->mmc->tran_speed;
 392        int pwr_18v = 0;
 393
 394        /*
 395         * Signal Voltage Switching is only applicable for Host Controllers
 396         * v3.00 and above.
 397         */
 398        if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300)
 399                xenon_mmc_start_signal_voltage_switch(host);
 400
 401        if ((sdhci_readb(host, SDHCI_POWER_CONTROL) & ~SDHCI_POWER_ON) ==
 402            SDHCI_POWER_180)
 403                pwr_18v = 1;
 404
 405        /* Set timing variable according to the configured speed */
 406        if (IS_SD(host->mmc)) {
 407                /* SD/SDIO */
 408                if (pwr_18v) {
 409                        if (host->mmc->ddr_mode)
 410                                priv->timing = MMC_TIMING_UHS_DDR50;
 411                        else if (speed <= 25000000)
 412                                priv->timing = MMC_TIMING_UHS_SDR25;
 413                        else
 414                                priv->timing = MMC_TIMING_UHS_SDR50;
 415                } else {
 416                        if (speed <= 25000000)
 417                                priv->timing = MMC_TIMING_LEGACY;
 418                        else
 419                                priv->timing = MMC_TIMING_SD_HS;
 420                }
 421        } else {
 422                /* eMMC */
 423                if (host->mmc->ddr_mode)
 424                        priv->timing = MMC_TIMING_MMC_DDR52;
 425                else if (speed <= 26000000)
 426                        priv->timing = MMC_TIMING_LEGACY;
 427                else
 428                        priv->timing = MMC_TIMING_MMC_HS;
 429        }
 430
 431        /* Re-init the PHY */
 432        xenon_mmc_phy_set(host);
 433
 434        return 0;
 435}
 436
 437/* Install a driver specific handler for post set_ios configuration */
 438static const struct sdhci_ops xenon_sdhci_ops = {
 439        .set_ios_post = xenon_sdhci_set_ios_post
 440};
 441
 442static int xenon_sdhci_probe(struct udevice *dev)
 443{
 444        struct xenon_sdhci_plat *plat = dev_get_plat(dev);
 445        struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
 446        struct xenon_sdhci_priv *priv = dev_get_priv(dev);
 447        struct sdhci_host *host = dev_get_priv(dev);
 448        int ret;
 449
 450        host->mmc = &plat->mmc;
 451        host->mmc->priv = host;
 452        host->mmc->dev = dev;
 453        upriv->mmc = host->mmc;
 454
 455        /* Set quirks */
 456        host->quirks = SDHCI_QUIRK_WAIT_SEND_CMD | SDHCI_QUIRK_32BIT_DMA_ADDR;
 457
 458        /* Set default timing */
 459        priv->timing = MMC_TIMING_LEGACY;
 460
 461        /* Get the vqmmc regulator if there is */
 462        device_get_supply_regulator(dev, "vqmmc-supply", &priv->vqmmc);
 463        /* Set the initial voltage value to 3.3V if there is regulator */
 464        if (priv->vqmmc) {
 465                ret = regulator_set_value(priv->vqmmc,
 466                                          XENON_MMC_3V3_UV);
 467                if (ret) {
 468                        printf("Failed to set VQMMC regulator to 3.3V\n");
 469                        return ret;
 470                }
 471        }
 472
 473        /* Disable auto clock gating during init */
 474        xenon_mmc_set_acg(host, false);
 475
 476        /* Enable slot */
 477        xenon_mmc_enable_slot(host, XENON_MMC_SLOT_ID_HYPERION);
 478
 479        /*
 480         * Set default power on SoC PHY PAD register (currently only
 481         * available on the Armada 3700)
 482         */
 483        if (priv->pad_ctrl_reg)
 484                armada_3700_soc_pad_voltage_set(host);
 485
 486        host->host_caps = MMC_MODE_HS | MMC_MODE_HS_52MHz | MMC_MODE_DDR_52MHz;
 487
 488        ret = mmc_of_parse(dev, &plat->cfg);
 489        if (ret)
 490                return ret;
 491
 492        host->ops = &xenon_sdhci_ops;
 493
 494        host->max_clk = XENON_MMC_MAX_CLK;
 495        ret = sdhci_setup_cfg(&plat->cfg, host, XENON_MMC_MAX_CLK, 0);
 496        if (ret)
 497                return ret;
 498
 499        ret = sdhci_probe(dev);
 500        if (ret)
 501                return ret;
 502
 503        /* Enable parallel transfer */
 504        xenon_mmc_enable_parallel_tran(host, XENON_MMC_SLOT_ID_HYPERION);
 505
 506        /* Disable tuning functionality of this slot */
 507        xenon_mmc_disable_tuning(host, XENON_MMC_SLOT_ID_HYPERION);
 508
 509        /* Enable auto clock gating after init */
 510        xenon_mmc_set_acg(host, true);
 511
 512        xenon_mask_cmd_conflict_err(host);
 513
 514        return ret;
 515}
 516
 517static int xenon_sdhci_remove(struct udevice *dev)
 518{
 519        struct sdhci_host *host = dev_get_priv(dev);
 520
 521        xenon_mmc_disable_slot(host, XENON_MMC_SLOT_ID_HYPERION);
 522        return 0;
 523}
 524
 525static int xenon_sdhci_of_to_plat(struct udevice *dev)
 526{
 527        struct sdhci_host *host = dev_get_priv(dev);
 528        struct xenon_sdhci_priv *priv = dev_get_priv(dev);
 529        const char *name;
 530
 531        host->name = dev->name;
 532        host->ioaddr = dev_read_addr_ptr(dev);
 533
 534        if (device_is_compatible(dev, "marvell,armada-3700-sdhci"))
 535                priv->pad_ctrl_reg = (void *)devfdt_get_addr_index(dev, 1);
 536
 537        name = fdt_getprop(gd->fdt_blob, dev_of_offset(dev), "marvell,pad-type",
 538                           NULL);
 539        if (name) {
 540                if (0 == strncmp(name, "sd", 2)) {
 541                        priv->pad_type = SOC_PAD_SD;
 542                } else if (0 == strncmp(name, "fixed-1-8v", 10)) {
 543                        priv->pad_type = SOC_PAD_FIXED_1_8V;
 544                } else {
 545                        printf("Unsupported SOC PHY PAD ctrl type %s\n", name);
 546                        return -EINVAL;
 547                }
 548        }
 549
 550        return 0;
 551}
 552
 553static int xenon_sdhci_bind(struct udevice *dev)
 554{
 555        struct xenon_sdhci_plat *plat = dev_get_plat(dev);
 556
 557        return sdhci_bind(dev, &plat->mmc, &plat->cfg);
 558}
 559
 560static const struct udevice_id xenon_sdhci_ids[] = {
 561        { .compatible = "marvell,armada-8k-sdhci",},
 562        { .compatible = "marvell,armada-3700-sdhci",},
 563        { }
 564};
 565
 566U_BOOT_DRIVER(xenon_sdhci_drv) = {
 567        .name           = "xenon_sdhci",
 568        .id             = UCLASS_MMC,
 569        .of_match       = xenon_sdhci_ids,
 570        .of_to_plat = xenon_sdhci_of_to_plat,
 571        .ops            = &sdhci_ops,
 572        .bind           = xenon_sdhci_bind,
 573        .probe          = xenon_sdhci_probe,
 574        .remove         = xenon_sdhci_remove,
 575        .priv_auto      = sizeof(struct xenon_sdhci_priv),
 576        .plat_auto      = sizeof(struct xenon_sdhci_plat),
 577};
 578