linux/drivers/mmc/host/sdhci-cadence.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Copyright (C) 2016 Socionext Inc.
   4 *   Author: Masahiro Yamada <yamada.masahiro@socionext.com>
   5 */
   6
   7#include <linux/bitfield.h>
   8#include <linux/bits.h>
   9#include <linux/iopoll.h>
  10#include <linux/module.h>
  11#include <linux/mmc/host.h>
  12#include <linux/mmc/mmc.h>
  13#include <linux/of.h>
  14#include <linux/of_device.h>
  15
  16#include "sdhci-pltfm.h"
  17
  18/* HRS - Host Register Set (specific to Cadence) */
  19#define SDHCI_CDNS_HRS04                0x10            /* PHY access port */
  20#define   SDHCI_CDNS_HRS04_ACK                  BIT(26)
  21#define   SDHCI_CDNS_HRS04_RD                   BIT(25)
  22#define   SDHCI_CDNS_HRS04_WR                   BIT(24)
  23#define   SDHCI_CDNS_HRS04_RDATA                GENMASK(23, 16)
  24#define   SDHCI_CDNS_HRS04_WDATA                GENMASK(15, 8)
  25#define   SDHCI_CDNS_HRS04_ADDR                 GENMASK(5, 0)
  26
  27#define SDHCI_CDNS_HRS06                0x18            /* eMMC control */
  28#define   SDHCI_CDNS_HRS06_TUNE_UP              BIT(15)
  29#define   SDHCI_CDNS_HRS06_TUNE                 GENMASK(13, 8)
  30#define   SDHCI_CDNS_HRS06_MODE                 GENMASK(2, 0)
  31#define   SDHCI_CDNS_HRS06_MODE_SD              0x0
  32#define   SDHCI_CDNS_HRS06_MODE_MMC_SDR         0x2
  33#define   SDHCI_CDNS_HRS06_MODE_MMC_DDR         0x3
  34#define   SDHCI_CDNS_HRS06_MODE_MMC_HS200       0x4
  35#define   SDHCI_CDNS_HRS06_MODE_MMC_HS400       0x5
  36#define   SDHCI_CDNS_HRS06_MODE_MMC_HS400ES     0x6
  37
  38/* SRS - Slot Register Set (SDHCI-compatible) */
  39#define SDHCI_CDNS_SRS_BASE             0x200
  40
  41/* PHY */
  42#define SDHCI_CDNS_PHY_DLY_SD_HS        0x00
  43#define SDHCI_CDNS_PHY_DLY_SD_DEFAULT   0x01
  44#define SDHCI_CDNS_PHY_DLY_UHS_SDR12    0x02
  45#define SDHCI_CDNS_PHY_DLY_UHS_SDR25    0x03
  46#define SDHCI_CDNS_PHY_DLY_UHS_SDR50    0x04
  47#define SDHCI_CDNS_PHY_DLY_UHS_DDR50    0x05
  48#define SDHCI_CDNS_PHY_DLY_EMMC_LEGACY  0x06
  49#define SDHCI_CDNS_PHY_DLY_EMMC_SDR     0x07
  50#define SDHCI_CDNS_PHY_DLY_EMMC_DDR     0x08
  51#define SDHCI_CDNS_PHY_DLY_SDCLK        0x0b
  52#define SDHCI_CDNS_PHY_DLY_HSMMC        0x0c
  53#define SDHCI_CDNS_PHY_DLY_STROBE       0x0d
  54
  55/*
  56 * The tuned val register is 6 bit-wide, but not the whole of the range is
  57 * available.  The range 0-42 seems to be available (then 43 wraps around to 0)
  58 * but I am not quite sure if it is official.  Use only 0 to 39 for safety.
  59 */
  60#define SDHCI_CDNS_MAX_TUNING_LOOP      40
  61
  62struct sdhci_cdns_phy_param {
  63        u8 addr;
  64        u8 data;
  65};
  66
  67struct sdhci_cdns_priv {
  68        void __iomem *hrs_addr;
  69        bool enhanced_strobe;
  70        unsigned int nr_phy_params;
  71        struct sdhci_cdns_phy_param phy_params[];
  72};
  73
  74struct sdhci_cdns_phy_cfg {
  75        const char *property;
  76        u8 addr;
  77};
  78
  79static const struct sdhci_cdns_phy_cfg sdhci_cdns_phy_cfgs[] = {
  80        { "cdns,phy-input-delay-sd-highspeed", SDHCI_CDNS_PHY_DLY_SD_HS, },
  81        { "cdns,phy-input-delay-legacy", SDHCI_CDNS_PHY_DLY_SD_DEFAULT, },
  82        { "cdns,phy-input-delay-sd-uhs-sdr12", SDHCI_CDNS_PHY_DLY_UHS_SDR12, },
  83        { "cdns,phy-input-delay-sd-uhs-sdr25", SDHCI_CDNS_PHY_DLY_UHS_SDR25, },
  84        { "cdns,phy-input-delay-sd-uhs-sdr50", SDHCI_CDNS_PHY_DLY_UHS_SDR50, },
  85        { "cdns,phy-input-delay-sd-uhs-ddr50", SDHCI_CDNS_PHY_DLY_UHS_DDR50, },
  86        { "cdns,phy-input-delay-mmc-highspeed", SDHCI_CDNS_PHY_DLY_EMMC_SDR, },
  87        { "cdns,phy-input-delay-mmc-ddr", SDHCI_CDNS_PHY_DLY_EMMC_DDR, },
  88        { "cdns,phy-dll-delay-sdclk", SDHCI_CDNS_PHY_DLY_SDCLK, },
  89        { "cdns,phy-dll-delay-sdclk-hsmmc", SDHCI_CDNS_PHY_DLY_HSMMC, },
  90        { "cdns,phy-dll-delay-strobe", SDHCI_CDNS_PHY_DLY_STROBE, },
  91};
  92
  93static int sdhci_cdns_write_phy_reg(struct sdhci_cdns_priv *priv,
  94                                    u8 addr, u8 data)
  95{
  96        void __iomem *reg = priv->hrs_addr + SDHCI_CDNS_HRS04;
  97        u32 tmp;
  98        int ret;
  99
 100        ret = readl_poll_timeout(reg, tmp, !(tmp & SDHCI_CDNS_HRS04_ACK),
 101                                 0, 10);
 102        if (ret)
 103                return ret;
 104
 105        tmp = FIELD_PREP(SDHCI_CDNS_HRS04_WDATA, data) |
 106              FIELD_PREP(SDHCI_CDNS_HRS04_ADDR, addr);
 107        writel(tmp, reg);
 108
 109        tmp |= SDHCI_CDNS_HRS04_WR;
 110        writel(tmp, reg);
 111
 112        ret = readl_poll_timeout(reg, tmp, tmp & SDHCI_CDNS_HRS04_ACK, 0, 10);
 113        if (ret)
 114                return ret;
 115
 116        tmp &= ~SDHCI_CDNS_HRS04_WR;
 117        writel(tmp, reg);
 118
 119        ret = readl_poll_timeout(reg, tmp, !(tmp & SDHCI_CDNS_HRS04_ACK),
 120                                 0, 10);
 121
 122        return ret;
 123}
 124
 125static unsigned int sdhci_cdns_phy_param_count(struct device_node *np)
 126{
 127        unsigned int count = 0;
 128        int i;
 129
 130        for (i = 0; i < ARRAY_SIZE(sdhci_cdns_phy_cfgs); i++)
 131                if (of_property_read_bool(np, sdhci_cdns_phy_cfgs[i].property))
 132                        count++;
 133
 134        return count;
 135}
 136
 137static void sdhci_cdns_phy_param_parse(struct device_node *np,
 138                                       struct sdhci_cdns_priv *priv)
 139{
 140        struct sdhci_cdns_phy_param *p = priv->phy_params;
 141        u32 val;
 142        int ret, i;
 143
 144        for (i = 0; i < ARRAY_SIZE(sdhci_cdns_phy_cfgs); i++) {
 145                ret = of_property_read_u32(np, sdhci_cdns_phy_cfgs[i].property,
 146                                           &val);
 147                if (ret)
 148                        continue;
 149
 150                p->addr = sdhci_cdns_phy_cfgs[i].addr;
 151                p->data = val;
 152                p++;
 153        }
 154}
 155
 156static int sdhci_cdns_phy_init(struct sdhci_cdns_priv *priv)
 157{
 158        int ret, i;
 159
 160        for (i = 0; i < priv->nr_phy_params; i++) {
 161                ret = sdhci_cdns_write_phy_reg(priv, priv->phy_params[i].addr,
 162                                               priv->phy_params[i].data);
 163                if (ret)
 164                        return ret;
 165        }
 166
 167        return 0;
 168}
 169
 170static void *sdhci_cdns_priv(struct sdhci_host *host)
 171{
 172        struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
 173
 174        return sdhci_pltfm_priv(pltfm_host);
 175}
 176
 177static unsigned int sdhci_cdns_get_timeout_clock(struct sdhci_host *host)
 178{
 179        /*
 180         * Cadence's spec says the Timeout Clock Frequency is the same as the
 181         * Base Clock Frequency.
 182         */
 183        return host->max_clk;
 184}
 185
 186static void sdhci_cdns_set_emmc_mode(struct sdhci_cdns_priv *priv, u32 mode)
 187{
 188        u32 tmp;
 189
 190        /* The speed mode for eMMC is selected by HRS06 register */
 191        tmp = readl(priv->hrs_addr + SDHCI_CDNS_HRS06);
 192        tmp &= ~SDHCI_CDNS_HRS06_MODE;
 193        tmp |= FIELD_PREP(SDHCI_CDNS_HRS06_MODE, mode);
 194        writel(tmp, priv->hrs_addr + SDHCI_CDNS_HRS06);
 195}
 196
 197static u32 sdhci_cdns_get_emmc_mode(struct sdhci_cdns_priv *priv)
 198{
 199        u32 tmp;
 200
 201        tmp = readl(priv->hrs_addr + SDHCI_CDNS_HRS06);
 202        return FIELD_GET(SDHCI_CDNS_HRS06_MODE, tmp);
 203}
 204
 205static int sdhci_cdns_set_tune_val(struct sdhci_host *host, unsigned int val)
 206{
 207        struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host);
 208        void __iomem *reg = priv->hrs_addr + SDHCI_CDNS_HRS06;
 209        u32 tmp;
 210        int i, ret;
 211
 212        if (WARN_ON(!FIELD_FIT(SDHCI_CDNS_HRS06_TUNE, val)))
 213                return -EINVAL;
 214
 215        tmp = readl(reg);
 216        tmp &= ~SDHCI_CDNS_HRS06_TUNE;
 217        tmp |= FIELD_PREP(SDHCI_CDNS_HRS06_TUNE, val);
 218
 219        /*
 220         * Workaround for IP errata:
 221         * The IP6116 SD/eMMC PHY design has a timing issue on receive data
 222         * path. Send tune request twice.
 223         */
 224        for (i = 0; i < 2; i++) {
 225                tmp |= SDHCI_CDNS_HRS06_TUNE_UP;
 226                writel(tmp, reg);
 227
 228                ret = readl_poll_timeout(reg, tmp,
 229                                         !(tmp & SDHCI_CDNS_HRS06_TUNE_UP),
 230                                         0, 1);
 231                if (ret)
 232                        return ret;
 233        }
 234
 235        return 0;
 236}
 237
 238/*
 239 * In SD mode, software must not use the hardware tuning and instead perform
 240 * an almost identical procedure to eMMC.
 241 */
 242static int sdhci_cdns_execute_tuning(struct sdhci_host *host, u32 opcode)
 243{
 244        int cur_streak = 0;
 245        int max_streak = 0;
 246        int end_of_streak = 0;
 247        int i;
 248
 249        /*
 250         * Do not execute tuning for UHS_SDR50 or UHS_DDR50.
 251         * The delay is set by probe, based on the DT properties.
 252         */
 253        if (host->timing != MMC_TIMING_MMC_HS200 &&
 254            host->timing != MMC_TIMING_UHS_SDR104)
 255                return 0;
 256
 257        for (i = 0; i < SDHCI_CDNS_MAX_TUNING_LOOP; i++) {
 258                if (sdhci_cdns_set_tune_val(host, i) ||
 259                    mmc_send_tuning(host->mmc, opcode, NULL)) { /* bad */
 260                        cur_streak = 0;
 261                } else { /* good */
 262                        cur_streak++;
 263                        if (cur_streak > max_streak) {
 264                                max_streak = cur_streak;
 265                                end_of_streak = i;
 266                        }
 267                }
 268        }
 269
 270        if (!max_streak) {
 271                dev_err(mmc_dev(host->mmc), "no tuning point found\n");
 272                return -EIO;
 273        }
 274
 275        return sdhci_cdns_set_tune_val(host, end_of_streak - max_streak / 2);
 276}
 277
 278static void sdhci_cdns_set_uhs_signaling(struct sdhci_host *host,
 279                                         unsigned int timing)
 280{
 281        struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host);
 282        u32 mode;
 283
 284        switch (timing) {
 285        case MMC_TIMING_MMC_HS:
 286                mode = SDHCI_CDNS_HRS06_MODE_MMC_SDR;
 287                break;
 288        case MMC_TIMING_MMC_DDR52:
 289                mode = SDHCI_CDNS_HRS06_MODE_MMC_DDR;
 290                break;
 291        case MMC_TIMING_MMC_HS200:
 292                mode = SDHCI_CDNS_HRS06_MODE_MMC_HS200;
 293                break;
 294        case MMC_TIMING_MMC_HS400:
 295                if (priv->enhanced_strobe)
 296                        mode = SDHCI_CDNS_HRS06_MODE_MMC_HS400ES;
 297                else
 298                        mode = SDHCI_CDNS_HRS06_MODE_MMC_HS400;
 299                break;
 300        default:
 301                mode = SDHCI_CDNS_HRS06_MODE_SD;
 302                break;
 303        }
 304
 305        sdhci_cdns_set_emmc_mode(priv, mode);
 306
 307        /* For SD, fall back to the default handler */
 308        if (mode == SDHCI_CDNS_HRS06_MODE_SD)
 309                sdhci_set_uhs_signaling(host, timing);
 310}
 311
 312static const struct sdhci_ops sdhci_cdns_ops = {
 313        .set_clock = sdhci_set_clock,
 314        .get_timeout_clock = sdhci_cdns_get_timeout_clock,
 315        .set_bus_width = sdhci_set_bus_width,
 316        .reset = sdhci_reset,
 317        .platform_execute_tuning = sdhci_cdns_execute_tuning,
 318        .set_uhs_signaling = sdhci_cdns_set_uhs_signaling,
 319};
 320
 321static const struct sdhci_pltfm_data sdhci_cdns_uniphier_pltfm_data = {
 322        .ops = &sdhci_cdns_ops,
 323        .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
 324};
 325
 326static const struct sdhci_pltfm_data sdhci_cdns_pltfm_data = {
 327        .ops = &sdhci_cdns_ops,
 328};
 329
 330static void sdhci_cdns_hs400_enhanced_strobe(struct mmc_host *mmc,
 331                                             struct mmc_ios *ios)
 332{
 333        struct sdhci_host *host = mmc_priv(mmc);
 334        struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host);
 335        u32 mode;
 336
 337        priv->enhanced_strobe = ios->enhanced_strobe;
 338
 339        mode = sdhci_cdns_get_emmc_mode(priv);
 340
 341        if (mode == SDHCI_CDNS_HRS06_MODE_MMC_HS400 && ios->enhanced_strobe)
 342                sdhci_cdns_set_emmc_mode(priv,
 343                                         SDHCI_CDNS_HRS06_MODE_MMC_HS400ES);
 344
 345        if (mode == SDHCI_CDNS_HRS06_MODE_MMC_HS400ES && !ios->enhanced_strobe)
 346                sdhci_cdns_set_emmc_mode(priv,
 347                                         SDHCI_CDNS_HRS06_MODE_MMC_HS400);
 348}
 349
 350static int sdhci_cdns_probe(struct platform_device *pdev)
 351{
 352        struct sdhci_host *host;
 353        const struct sdhci_pltfm_data *data;
 354        struct sdhci_pltfm_host *pltfm_host;
 355        struct sdhci_cdns_priv *priv;
 356        struct clk *clk;
 357        unsigned int nr_phy_params;
 358        int ret;
 359        struct device *dev = &pdev->dev;
 360        static const u16 version = SDHCI_SPEC_400 << SDHCI_SPEC_VER_SHIFT;
 361
 362        clk = devm_clk_get(dev, NULL);
 363        if (IS_ERR(clk))
 364                return PTR_ERR(clk);
 365
 366        ret = clk_prepare_enable(clk);
 367        if (ret)
 368                return ret;
 369
 370        data = of_device_get_match_data(dev);
 371        if (!data)
 372                data = &sdhci_cdns_pltfm_data;
 373
 374        nr_phy_params = sdhci_cdns_phy_param_count(dev->of_node);
 375        host = sdhci_pltfm_init(pdev, data,
 376                                struct_size(priv, phy_params, nr_phy_params));
 377        if (IS_ERR(host)) {
 378                ret = PTR_ERR(host);
 379                goto disable_clk;
 380        }
 381
 382        pltfm_host = sdhci_priv(host);
 383        pltfm_host->clk = clk;
 384
 385        priv = sdhci_pltfm_priv(pltfm_host);
 386        priv->nr_phy_params = nr_phy_params;
 387        priv->hrs_addr = host->ioaddr;
 388        priv->enhanced_strobe = false;
 389        host->ioaddr += SDHCI_CDNS_SRS_BASE;
 390        host->mmc_host_ops.hs400_enhanced_strobe =
 391                                sdhci_cdns_hs400_enhanced_strobe;
 392        sdhci_enable_v4_mode(host);
 393        __sdhci_read_caps(host, &version, NULL, NULL);
 394
 395        sdhci_get_of_property(pdev);
 396
 397        ret = mmc_of_parse(host->mmc);
 398        if (ret)
 399                goto free;
 400
 401        sdhci_cdns_phy_param_parse(dev->of_node, priv);
 402
 403        ret = sdhci_cdns_phy_init(priv);
 404        if (ret)
 405                goto free;
 406
 407        ret = sdhci_add_host(host);
 408        if (ret)
 409                goto free;
 410
 411        return 0;
 412free:
 413        sdhci_pltfm_free(pdev);
 414disable_clk:
 415        clk_disable_unprepare(clk);
 416
 417        return ret;
 418}
 419
 420#ifdef CONFIG_PM_SLEEP
 421static int sdhci_cdns_resume(struct device *dev)
 422{
 423        struct sdhci_host *host = dev_get_drvdata(dev);
 424        struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
 425        struct sdhci_cdns_priv *priv = sdhci_pltfm_priv(pltfm_host);
 426        int ret;
 427
 428        ret = clk_prepare_enable(pltfm_host->clk);
 429        if (ret)
 430                return ret;
 431
 432        ret = sdhci_cdns_phy_init(priv);
 433        if (ret)
 434                goto disable_clk;
 435
 436        ret = sdhci_resume_host(host);
 437        if (ret)
 438                goto disable_clk;
 439
 440        return 0;
 441
 442disable_clk:
 443        clk_disable_unprepare(pltfm_host->clk);
 444
 445        return ret;
 446}
 447#endif
 448
 449static const struct dev_pm_ops sdhci_cdns_pm_ops = {
 450        SET_SYSTEM_SLEEP_PM_OPS(sdhci_pltfm_suspend, sdhci_cdns_resume)
 451};
 452
 453static const struct of_device_id sdhci_cdns_match[] = {
 454        {
 455                .compatible = "socionext,uniphier-sd4hc",
 456                .data = &sdhci_cdns_uniphier_pltfm_data,
 457        },
 458        { .compatible = "cdns,sd4hc" },
 459        { /* sentinel */ }
 460};
 461MODULE_DEVICE_TABLE(of, sdhci_cdns_match);
 462
 463static struct platform_driver sdhci_cdns_driver = {
 464        .driver = {
 465                .name = "sdhci-cdns",
 466                .probe_type = PROBE_PREFER_ASYNCHRONOUS,
 467                .pm = &sdhci_cdns_pm_ops,
 468                .of_match_table = sdhci_cdns_match,
 469        },
 470        .probe = sdhci_cdns_probe,
 471        .remove = sdhci_pltfm_unregister,
 472};
 473module_platform_driver(sdhci_cdns_driver);
 474
 475MODULE_AUTHOR("Masahiro Yamada <yamada.masahiro@socionext.com>");
 476MODULE_DESCRIPTION("Cadence SD/SDIO/eMMC Host Controller Driver");
 477MODULE_LICENSE("GPL");
 478