sdhci-cadence.c 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. /*
  2. * Copyright (C) 2016 Socionext Inc.
  3. * Author: Masahiro Yamada <yamada.masahiro@socionext.com>
  4. *
  5. * SPDX-License-Identifier: GPL-2.0+
  6. */
  7. #include <common.h>
  8. #include <linux/io.h>
  9. #include <linux/sizes.h>
  10. #include <dm/device.h>
  11. #include <mmc.h>
  12. #include <sdhci.h>
  13. /* HRS - Host Register Set (specific to Cadence) */
  14. #define SDHCI_CDNS_HRS04 0x10 /* PHY access port */
  15. #define SDHCI_CDNS_HRS04_ACK BIT(26)
  16. #define SDHCI_CDNS_HRS04_RD BIT(25)
  17. #define SDHCI_CDNS_HRS04_WR BIT(24)
  18. #define SDHCI_CDNS_HRS04_RDATA_SHIFT 12
  19. #define SDHCI_CDNS_HRS04_WDATA_SHIFT 8
  20. #define SDHCI_CDNS_HRS04_ADDR_SHIFT 0
  21. /* SRS - Slot Register Set (SDHCI-compatible) */
  22. #define SDHCI_CDNS_SRS_BASE 0x200
  23. /* PHY */
  24. #define SDHCI_CDNS_PHY_DLY_SD_HS 0x00
  25. #define SDHCI_CDNS_PHY_DLY_SD_DEFAULT 0x01
  26. #define SDHCI_CDNS_PHY_DLY_UHS_SDR12 0x02
  27. #define SDHCI_CDNS_PHY_DLY_UHS_SDR25 0x03
  28. #define SDHCI_CDNS_PHY_DLY_UHS_SDR50 0x04
  29. #define SDHCI_CDNS_PHY_DLY_UHS_DDR50 0x05
  30. #define SDHCI_CDNS_PHY_DLY_EMMC_LEGACY 0x06
  31. #define SDHCI_CDNS_PHY_DLY_EMMC_SDR 0x07
  32. #define SDHCI_CDNS_PHY_DLY_EMMC_DDR 0x08
  33. struct sdhci_cdns_plat {
  34. struct mmc_config cfg;
  35. struct mmc mmc;
  36. void __iomem *hrs_addr;
  37. };
  38. static void sdhci_cdns_write_phy_reg(struct sdhci_cdns_plat *plat,
  39. u8 addr, u8 data)
  40. {
  41. void __iomem *reg = plat->hrs_addr + SDHCI_CDNS_HRS04;
  42. u32 tmp;
  43. tmp = (data << SDHCI_CDNS_HRS04_WDATA_SHIFT) |
  44. (addr << SDHCI_CDNS_HRS04_ADDR_SHIFT);
  45. writel(tmp, reg);
  46. tmp |= SDHCI_CDNS_HRS04_WR;
  47. writel(tmp, reg);
  48. tmp &= ~SDHCI_CDNS_HRS04_WR;
  49. writel(tmp, reg);
  50. }
  51. static void sdhci_cdns_phy_init(struct sdhci_cdns_plat *plat)
  52. {
  53. sdhci_cdns_write_phy_reg(plat, SDHCI_CDNS_PHY_DLY_SD_HS, 4);
  54. sdhci_cdns_write_phy_reg(plat, SDHCI_CDNS_PHY_DLY_SD_DEFAULT, 4);
  55. sdhci_cdns_write_phy_reg(plat, SDHCI_CDNS_PHY_DLY_EMMC_LEGACY, 9);
  56. sdhci_cdns_write_phy_reg(plat, SDHCI_CDNS_PHY_DLY_EMMC_SDR, 2);
  57. sdhci_cdns_write_phy_reg(plat, SDHCI_CDNS_PHY_DLY_EMMC_DDR, 3);
  58. }
  59. static int sdhci_cdns_bind(struct udevice *dev)
  60. {
  61. struct sdhci_cdns_plat *plat = dev_get_platdata(dev);
  62. return sdhci_bind(dev, &plat->mmc, &plat->cfg);
  63. }
  64. static int sdhci_cdns_probe(struct udevice *dev)
  65. {
  66. struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
  67. struct sdhci_cdns_plat *plat = dev_get_platdata(dev);
  68. struct sdhci_host *host = dev_get_priv(dev);
  69. fdt_addr_t base;
  70. int ret;
  71. base = dev_get_addr(dev);
  72. if (base == FDT_ADDR_T_NONE)
  73. return -EINVAL;
  74. plat->hrs_addr = devm_ioremap(dev, base, SZ_1K);
  75. if (!plat->hrs_addr)
  76. return -ENOMEM;
  77. host->name = dev->name;
  78. host->ioaddr = plat->hrs_addr + SDHCI_CDNS_SRS_BASE;
  79. host->quirks |= SDHCI_QUIRK_WAIT_SEND_CMD;
  80. sdhci_cdns_phy_init(plat);
  81. ret = sdhci_setup_cfg(&plat->cfg, host, 0, 0);
  82. if (ret)
  83. return ret;
  84. upriv->mmc = &plat->mmc;
  85. host->mmc = &plat->mmc;
  86. host->mmc->priv = host;
  87. return sdhci_probe(dev);
  88. }
  89. static const struct udevice_id sdhci_cdns_match[] = {
  90. { .compatible = "socionext,uniphier-sd4hc" },
  91. { .compatible = "cdns,sd4hc" },
  92. { /* sentinel */ }
  93. };
  94. U_BOOT_DRIVER(sdhci_cdns) = {
  95. .name = "sdhci-cdns",
  96. .id = UCLASS_MMC,
  97. .of_match = sdhci_cdns_match,
  98. .bind = sdhci_cdns_bind,
  99. .probe = sdhci_cdns_probe,
  100. .priv_auto_alloc_size = sizeof(struct sdhci_host),
  101. .platdata_auto_alloc_size = sizeof(struct sdhci_cdns_plat),
  102. .ops = &sdhci_ops,
  103. };