root/drivers/mmc/host/sdhci-cadence.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. sdhci_cdns_write_phy_reg
  2. sdhci_cdns_phy_param_count
  3. sdhci_cdns_phy_param_parse
  4. sdhci_cdns_phy_init
  5. sdhci_cdns_priv
  6. sdhci_cdns_get_timeout_clock
  7. sdhci_cdns_set_emmc_mode
  8. sdhci_cdns_get_emmc_mode
  9. sdhci_cdns_set_uhs_signaling
  10. sdhci_cdns_set_tune_val
  11. sdhci_cdns_execute_tuning
  12. sdhci_cdns_hs400_enhanced_strobe
  13. sdhci_cdns_probe
  14. sdhci_cdns_resume

   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 
  62 struct sdhci_cdns_phy_param {
  63         u8 addr;
  64         u8 data;
  65 };
  66 
  67 struct 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 
  74 struct sdhci_cdns_phy_cfg {
  75         const char *property;
  76         u8 addr;
  77 };
  78 
  79 static 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 
  93 static 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 
 117 static 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 
 129 static 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 
 148 static 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 
 162 static inline 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 
 169 static 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 
 178 static 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 
 189 static 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 
 197 static 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 
 231 static 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 
 239 static const struct sdhci_pltfm_data sdhci_cdns_uniphier_pltfm_data = {
 240         .ops = &sdhci_cdns_ops,
 241         .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
 242 };
 243 
 244 static const struct sdhci_pltfm_data sdhci_cdns_pltfm_data = {
 245         .ops = &sdhci_cdns_ops,
 246 };
 247 
 248 static 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 
 281 static 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 
 320 static 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 
 340 static 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;
 403 free:
 404         sdhci_pltfm_free(pdev);
 405 disable_clk:
 406         clk_disable_unprepare(clk);
 407 
 408         return ret;
 409 }
 410 
 411 #ifdef CONFIG_PM_SLEEP
 412 static 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 
 433 disable_clk:
 434         clk_disable_unprepare(pltfm_host->clk);
 435 
 436         return ret;
 437 }
 438 #endif
 439 
 440 static const struct dev_pm_ops sdhci_cdns_pm_ops = {
 441         SET_SYSTEM_SLEEP_PM_OPS(sdhci_pltfm_suspend, sdhci_cdns_resume)
 442 };
 443 
 444 static 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 };
 452 MODULE_DEVICE_TABLE(of, sdhci_cdns_match);
 453 
 454 static 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 };
 463 module_platform_driver(sdhci_cdns_driver);
 464 
 465 MODULE_AUTHOR("Masahiro Yamada <yamada.masahiro@socionext.com>");
 466 MODULE_DESCRIPTION("Cadence SD/SDIO/eMMC Host Controller Driver");
 467 MODULE_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */