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