dw_mmc-starfive.c 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * StarFive Designware Mobile Storage Host Controller Driver
  4. *
  5. * Copyright (c) 2022 StarFive Technology Co., Ltd.
  6. */
  7. #include <linux/bitfield.h>
  8. #include <linux/clk.h>
  9. #include <linux/delay.h>
  10. #include <linux/mfd/syscon.h>
  11. #include <linux/mmc/host.h>
  12. #include <linux/module.h>
  13. #include <linux/of_address.h>
  14. #include <linux/platform_device.h>
  15. #include <linux/regmap.h>
  16. #include "dw_mmc.h"
  17. #include "dw_mmc-pltfm.h"
  18. #define ALL_INT_CLR 0x1ffff
  19. #define MAX_DELAY_CHAIN 32
  20. #define STARFIVE_SMPL_PHASE GENMASK(20, 16)
  21. static void dw_mci_starfive_set_ios(struct dw_mci *host, struct mmc_ios *ios)
  22. {
  23. int ret;
  24. unsigned int clock;
  25. if (ios->timing == MMC_TIMING_MMC_DDR52 || ios->timing == MMC_TIMING_UHS_DDR50) {
  26. clock = (ios->clock > 50000000 && ios->clock <= 52000000) ? 100000000 : ios->clock;
  27. ret = clk_set_rate(host->ciu_clk, clock);
  28. if (ret)
  29. dev_dbg(host->dev, "Use an external frequency divider %uHz\n", ios->clock);
  30. host->bus_hz = clk_get_rate(host->ciu_clk);
  31. } else {
  32. dev_dbg(host->dev, "Using the internal divider\n");
  33. }
  34. }
  35. static void dw_mci_starfive_set_sample_phase(struct dw_mci *host, u32 smpl_phase)
  36. {
  37. /* change driver phase and sample phase */
  38. u32 reg_value = mci_readl(host, UHS_REG_EXT);
  39. /* In UHS_REG_EXT, only 5 bits valid in DRV_PHASE and SMPL_PHASE */
  40. reg_value &= ~STARFIVE_SMPL_PHASE;
  41. reg_value |= FIELD_PREP(STARFIVE_SMPL_PHASE, smpl_phase);
  42. mci_writel(host, UHS_REG_EXT, reg_value);
  43. /* We should delay 1ms wait for timing setting finished. */
  44. mdelay(1);
  45. }
  46. static int dw_mci_starfive_execute_tuning(struct dw_mci_slot *slot,
  47. u32 opcode)
  48. {
  49. static const int grade = MAX_DELAY_CHAIN;
  50. struct dw_mci *host = slot->host;
  51. int smpl_phase, smpl_raise = -1, smpl_fall = -1;
  52. int ret;
  53. for (smpl_phase = 0; smpl_phase < grade; smpl_phase++) {
  54. dw_mci_starfive_set_sample_phase(host, smpl_phase);
  55. mci_writel(host, RINTSTS, ALL_INT_CLR);
  56. ret = mmc_send_tuning(slot->mmc, opcode, NULL);
  57. if (!ret && smpl_raise < 0) {
  58. smpl_raise = smpl_phase;
  59. } else if (ret && smpl_raise >= 0) {
  60. smpl_fall = smpl_phase - 1;
  61. break;
  62. }
  63. }
  64. if (smpl_phase >= grade)
  65. smpl_fall = grade - 1;
  66. if (smpl_raise < 0) {
  67. smpl_phase = 0;
  68. dev_err(host->dev, "No valid delay chain! use default\n");
  69. ret = -EINVAL;
  70. goto out;
  71. }
  72. smpl_phase = (smpl_raise + smpl_fall) / 2;
  73. dev_dbg(host->dev, "Found valid delay chain! use it [delay=%d]\n", smpl_phase);
  74. ret = 0;
  75. out:
  76. dw_mci_starfive_set_sample_phase(host, smpl_phase);
  77. mci_writel(host, RINTSTS, ALL_INT_CLR);
  78. return ret;
  79. }
  80. static const struct dw_mci_drv_data starfive_data = {
  81. .common_caps = MMC_CAP_CMD23,
  82. .set_ios = dw_mci_starfive_set_ios,
  83. .execute_tuning = dw_mci_starfive_execute_tuning,
  84. };
  85. static const struct of_device_id dw_mci_starfive_match[] = {
  86. { .compatible = "starfive,jh7110-mmc",
  87. .data = &starfive_data },
  88. {},
  89. };
  90. MODULE_DEVICE_TABLE(of, dw_mci_starfive_match);
  91. static int dw_mci_starfive_probe(struct platform_device *pdev)
  92. {
  93. return dw_mci_pltfm_register(pdev, &starfive_data);
  94. }
  95. static struct platform_driver dw_mci_starfive_driver = {
  96. .probe = dw_mci_starfive_probe,
  97. .remove_new = dw_mci_pltfm_remove,
  98. .driver = {
  99. .name = "dwmmc_starfive",
  100. .probe_type = PROBE_PREFER_ASYNCHRONOUS,
  101. .of_match_table = dw_mci_starfive_match,
  102. },
  103. };
  104. module_platform_driver(dw_mci_starfive_driver);
  105. MODULE_DESCRIPTION("StarFive JH7110 Specific DW-MSHC Driver Extension");
  106. MODULE_LICENSE("GPL");
  107. MODULE_ALIAS("platform:dwmmc_starfive");