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