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[0];
  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        tmp = FIELD_PREP(SDHCI_CDNS_HRS04_WDATA, data) |
 101              FIELD_PREP(SDHCI_CDNS_HRS04_ADDR, addr);
 102        writel(tmp, reg);
 103
 104        tmp |= SDHCI_CDNS_HRS04_WR;
 105        writel(tmp, reg);
 106
 107        ret = readl_poll_timeout(reg, tmp, tmp & SDHCI_CDNS_HRS04_ACK, 0, 10);
 108        if (ret)
 109                return ret;
 110
 111        tmp &= ~SDHCI_CDNS_HRS04_WR;
 112        writel(tmp, reg);
 113
 114        return 0;
 115}
 116
 117static unsigned int sdhci_cdns_phy_param_count(struct device_node *np)
 118{
 119        unsigned int count = 0;
 120        int i;
 121
 122        for (i = 0; i < ARRAY_SIZE(sdhci_cdns_phy_cfgs); i++)
 123                if (of_property_read_bool(np, sdhci_cdns_phy_cfgs[i].property))
 124                        count++;
 125
 126        return count;
 127}
 128
 129static void sdhci_cdns_phy_param_parse(struct device_node *np,
 130                                       struct sdhci_cdns_priv *priv)
 131{
 132        struct sdhci_cdns_phy_param *p = priv->phy_params;
 133        u32 val;
 134        int ret, i;
 135
 136        for (i = 0; i < ARRAY_SIZE(sdhci_cdns_phy_cfgs); i++) {
 137                ret = of_property_read_u32(np, sdhci_cdns_phy_cfgs[i].property,
 138                                           &val);
 139                if (ret)
 140                        continue;
 141
 142                p->addr = sdhci_cdns_phy_cfgs[i].addr;
 143                p->data = val;
 144                p++;
 145        }
 146}
 147
 148static int sdhci_cdns_phy_init(struct sdhci_cdns_priv *priv)
 149{
 150        int ret, i;
 151
 152        for (i = 0; i < priv->nr_phy_params; i++) {
 153                ret = sdhci_cdns_write_phy_reg(priv, priv->phy_params[i].addr,
 154                                               priv->phy_params[i].data);
 155                if (ret)
 156                        return ret;
 157        }
 158
 159        return 0;
 160}
 161
 162static void *sdhci_cdns_priv(struct sdhci_host *host)
 163{
 164        struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
 165
 166        return sdhci_pltfm_priv(pltfm_host);
 167}
 168
 169static unsigned int sdhci_cdns_get_timeout_clock(struct sdhci_host *host)
 170{
 171        /*
 172         * Cadence's spec says the Timeout Clock Frequency is the same as the
 173         * Base Clock Frequency.
 174         */
 175        return host->max_clk;
 176}
 177
 178static void sdhci_cdns_set_emmc_mode(struct sdhci_cdns_priv *priv, u32 mode)
 179{
 180        u32 tmp;
 181
 182        /* The speed mode for eMMC is selected by HRS06 register */
 183        tmp = readl(priv->hrs_addr + SDHCI_CDNS_HRS06);
 184        tmp &= ~SDHCI_CDNS_HRS06_MODE;
 185        tmp |= FIELD_PREP(SDHCI_CDNS_HRS06_MODE, mode);
 186        writel(tmp, priv->hrs_addr + SDHCI_CDNS_HRS06);
 187}
 188
 189static u32 sdhci_cdns_get_emmc_mode(struct sdhci_cdns_priv *priv)
 190{
 191        u32 tmp;
 192
 193        tmp = readl(priv->hrs_addr + SDHCI_CDNS_HRS06);
 194        return FIELD_GET(SDHCI_CDNS_HRS06_MODE, tmp);
 195}
 196
 197static void sdhci_cdns_set_uhs_signaling(struct sdhci_host *host,
 198                                         unsigned int timing)
 199{
 200        struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host);
 201        u32 mode;
 202
 203        switch (timing) {
 204        case MMC_TIMING_MMC_HS:
 205                mode = SDHCI_CDNS_HRS06_MODE_MMC_SDR;
 206                break;
 207        case MMC_TIMING_MMC_DDR52:
 208                mode = SDHCI_CDNS_HRS06_MODE_MMC_DDR;
 209                break;
 210        case MMC_TIMING_MMC_HS200:
 211                mode = SDHCI_CDNS_HRS06_MODE_MMC_HS200;
 212                break;
 213        case MMC_TIMING_MMC_HS400:
 214                if (priv->enhanced_strobe)
 215                        mode = SDHCI_CDNS_HRS06_MODE_MMC_HS400ES;
 216                else
 217                        mode = SDHCI_CDNS_HRS06_MODE_MMC_HS400;
 218                break;
 219        default:
 220                mode = SDHCI_CDNS_HRS06_MODE_SD;
 221                break;
 222        }
 223
 224        sdhci_cdns_set_emmc_mode(priv, mode);
 225
 226        /* For SD, fall back to the default handler */
 227        if (mode == SDHCI_CDNS_HRS06_MODE_SD)
 228                sdhci_set_uhs_signaling(host, timing);
 229}
 230
 231static const struct sdhci_ops sdhci_cdns_ops = {
 232        .set_clock = sdhci_set_clock,
 233        .get_timeout_clock = sdhci_cdns_get_timeout_clock,
 234        .set_bus_width = sdhci_set_bus_width,
 235        .reset = sdhci_reset,
 236        .set_uhs_signaling = sdhci_cdns_set_uhs_signaling,
 237};
 238
 239static const struct sdhci_pltfm_data sdhci_cdns_uniphier_pltfm_data = {
 240        .ops = &sdhci_cdns_ops,
 241        .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
 242};
 243
 244static const struct sdhci_pltfm_data sdhci_cdns_pltfm_data = {
 245        .ops = &sdhci_cdns_ops,
 246};
 247
 248static int sdhci_cdns_set_tune_val(struct sdhci_host *host, unsigned int val)
 249{
 250        struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host);
 251        void __iomem *reg = priv->hrs_addr + SDHCI_CDNS_HRS06;
 252        u32 tmp;
 253        int i, ret;
 254
 255        if (WARN_ON(!FIELD_FIT(SDHCI_CDNS_HRS06_TUNE, val)))
 256                return -EINVAL;
 257
 258        tmp = readl(reg);
 259        tmp &= ~SDHCI_CDNS_HRS06_TUNE;
 260        tmp |= FIELD_PREP(SDHCI_CDNS_HRS06_TUNE, val);
 261
 262        /*
 263         * Workaround for IP errata:
 264         * The IP6116 SD/eMMC PHY design has a timing issue on receive data
 265         * path. Send tune request twice.
 266         */
 267        for (i = 0; i < 2; i++) {
 268                tmp |= SDHCI_CDNS_HRS06_TUNE_UP;
 269                writel(tmp, reg);
 270
 271                ret = readl_poll_timeout(reg, tmp,
 272                                         !(tmp & SDHCI_CDNS_HRS06_TUNE_UP),
 273                                         0, 1);
 274                if (ret)
 275                        return ret;
 276        }
 277
 278        return 0;
 279}
 280
 281static int sdhci_cdns_execute_tuning(struct mmc_host *mmc, u32 opcode)
 282{
 283        struct sdhci_host *host = mmc_priv(mmc);
 284        int cur_streak = 0;
 285        int max_streak = 0;
 286        int end_of_streak = 0;
 287        int i;
 288
 289        /*
 290         * This handler only implements the eMMC tuning that is specific to
 291         * this controller.  Fall back to the standard method for SD timing.
 292         */
 293        if (host->timing != MMC_TIMING_MMC_HS200)
 294                return sdhci_execute_tuning(mmc, opcode);
 295
 296        if (WARN_ON(opcode != MMC_SEND_TUNING_BLOCK_HS200))
 297                return -EINVAL;
 298
 299        for (i = 0; i < SDHCI_CDNS_MAX_TUNING_LOOP; i++) {
 300                if (sdhci_cdns_set_tune_val(host, i) ||
 301                    mmc_send_tuning(host->mmc, opcode, NULL)) { /* bad */
 302                        cur_streak = 0;
 303                } else { /* good */
 304                        cur_streak++;
 305                        if (cur_streak > max_streak) {
 306                                max_streak = cur_streak;
 307                                end_of_streak = i;
 308                        }
 309                }
 310        }
 311
 312        if (!max_streak) {
 313                dev_err(mmc_dev(host->mmc), "no tuning point found\n");
 314                return -EIO;
 315        }
 316
 317        return sdhci_cdns_set_tune_val(host, end_of_streak - max_streak / 2);
 318}
 319
 320static void sdhci_cdns_hs400_enhanced_strobe(struct mmc_host *mmc,
 321                                             struct mmc_ios *ios)
 322{
 323        struct sdhci_host *host = mmc_priv(mmc);
 324        struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host);
 325        u32 mode;
 326
 327        priv->enhanced_strobe = ios->enhanced_strobe;
 328
 329        mode = sdhci_cdns_get_emmc_mode(priv);
 330
 331        if (mode == SDHCI_CDNS_HRS06_MODE_MMC_HS400 && ios->enhanced_strobe)
 332                sdhci_cdns_set_emmc_mode(priv,
 333                                         SDHCI_CDNS_HRS06_MODE_MMC_HS400ES);
 334
 335        if (mode == SDHCI_CDNS_HRS06_MODE_MMC_HS400ES && !ios->enhanced_strobe)
 336                sdhci_cdns_set_emmc_mode(priv,
 337                                         SDHCI_CDNS_HRS06_MODE_MMC_HS400);
 338}
 339
 340static int sdhci_cdns_probe(struct platform_device *pdev)
 341{
 342        struct sdhci_host *host;
 343        const struct sdhci_pltfm_data *data;
 344        struct sdhci_pltfm_host *pltfm_host;
 345        struct sdhci_cdns_priv *priv;
 346        struct clk *clk;
 347        unsigned int nr_phy_params;
 348        int ret;
 349        struct device *dev = &pdev->dev;
 350        static const u16 version = SDHCI_SPEC_400 << SDHCI_SPEC_VER_SHIFT;
 351
 352        clk = devm_clk_get(dev, NULL);
 353        if (IS_ERR(clk))
 354                return PTR_ERR(clk);
 355
 356        ret = clk_prepare_enable(clk);
 357        if (ret)
 358                return ret;
 359
 360        data = of_device_get_match_data(dev);
 361        if (!data)
 362                data = &sdhci_cdns_pltfm_data;
 363
 364        nr_phy_params = sdhci_cdns_phy_param_count(dev->of_node);
 365        host = sdhci_pltfm_init(pdev, data,
 366                                struct_size(priv, phy_params, nr_phy_params));
 367        if (IS_ERR(host)) {
 368                ret = PTR_ERR(host);
 369                goto disable_clk;
 370        }
 371
 372        pltfm_host = sdhci_priv(host);
 373        pltfm_host->clk = clk;
 374
 375        priv = sdhci_pltfm_priv(pltfm_host);
 376        priv->nr_phy_params = nr_phy_params;
 377        priv->hrs_addr = host->ioaddr;
 378        priv->enhanced_strobe = false;
 379        host->ioaddr += SDHCI_CDNS_SRS_BASE;
 380        host->mmc_host_ops.execute_tuning = sdhci_cdns_execute_tuning;
 381        host->mmc_host_ops.hs400_enhanced_strobe =
 382                                sdhci_cdns_hs400_enhanced_strobe;
 383        sdhci_enable_v4_mode(host);
 384        __sdhci_read_caps(host, &version, NULL, NULL);
 385
 386        sdhci_get_of_property(pdev);
 387
 388        ret = mmc_of_parse(host->mmc);
 389        if (ret)
 390                goto free;
 391
 392        sdhci_cdns_phy_param_parse(dev->of_node, priv);
 393
 394        ret = sdhci_cdns_phy_init(priv);
 395        if (ret)
 396                goto free;
 397
 398        ret = sdhci_add_host(host);
 399        if (ret)
 400                goto free;
 401
 402        return 0;
 403free:
 404        sdhci_pltfm_free(pdev);
 405disable_clk:
 406        clk_disable_unprepare(clk);
 407
 408        return ret;
 409}
 410
 411#ifdef CONFIG_PM_SLEEP
 412static int sdhci_cdns_resume(struct device *dev)
 413{
 414        struct sdhci_host *host = dev_get_drvdata(dev);
 415        struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
 416        struct sdhci_cdns_priv *priv = sdhci_pltfm_priv(pltfm_host);
 417        int ret;
 418
 419        ret = clk_prepare_enable(pltfm_host->clk);
 420        if (ret)
 421                return ret;
 422
 423        ret = sdhci_cdns_phy_init(priv);
 424        if (ret)
 425                goto disable_clk;
 426
 427        ret = sdhci_resume_host(host);
 428        if (ret)
 429                goto disable_clk;
 430
 431        return 0;
 432
 433disable_clk:
 434        clk_disable_unprepare(pltfm_host->clk);
 435
 436        return ret;
 437}
 438#endif
 439
 440static const struct dev_pm_ops sdhci_cdns_pm_ops = {
 441        SET_SYSTEM_SLEEP_PM_OPS(sdhci_pltfm_suspend, sdhci_cdns_resume)
 442};
 443
 444static const struct of_device_id sdhci_cdns_match[] = {
 445        {
 446                .compatible = "socionext,uniphier-sd4hc",
 447                .data = &sdhci_cdns_uniphier_pltfm_data,
 448        },
 449        { .compatible = "cdns,sd4hc" },
 450        { /* sentinel */ }
 451};
 452MODULE_DEVICE_TABLE(of, sdhci_cdns_match);
 453
 454static struct platform_driver sdhci_cdns_driver = {
 455        .driver = {
 456                .name = "sdhci-cdns",
 457                .pm = &sdhci_cdns_pm_ops,
 458                .of_match_table = sdhci_cdns_match,
 459        },
 460        .probe = sdhci_cdns_probe,
 461        .remove = sdhci_pltfm_unregister,
 462};
 463module_platform_driver(sdhci_cdns_driver);
 464
 465MODULE_AUTHOR("Masahiro Yamada <yamada.masahiro@socionext.com>");
 466MODULE_DESCRIPTION("Cadence SD/SDIO/eMMC Host Controller Driver");
 467MODULE_LICENSE("GPL");
 468