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