dw_mmc-hi3798cv200.c 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Copyright (c) 2018 HiSilicon Technologies Co., Ltd.
  4. */
  5. #include <linux/clk.h>
  6. #include <linux/mfd/syscon.h>
  7. #include <linux/mmc/host.h>
  8. #include <linux/module.h>
  9. #include <linux/of_address.h>
  10. #include <linux/platform_device.h>
  11. #include <linux/pm_runtime.h>
  12. #include <linux/regmap.h>
  13. #include <linux/regulator/consumer.h>
  14. #include "dw_mmc.h"
  15. #include "dw_mmc-pltfm.h"
  16. #define ALL_INT_CLR 0x1ffff
  17. struct hi3798cv200_priv {
  18. struct clk *sample_clk;
  19. struct clk *drive_clk;
  20. };
  21. static void dw_mci_hi3798cv200_set_ios(struct dw_mci *host, struct mmc_ios *ios)
  22. {
  23. struct hi3798cv200_priv *priv = host->priv;
  24. u32 val;
  25. val = mci_readl(host, UHS_REG);
  26. if (ios->timing == MMC_TIMING_MMC_DDR52 ||
  27. ios->timing == MMC_TIMING_UHS_DDR50)
  28. val |= SDMMC_UHS_DDR;
  29. else
  30. val &= ~SDMMC_UHS_DDR;
  31. mci_writel(host, UHS_REG, val);
  32. val = mci_readl(host, ENABLE_SHIFT);
  33. if (ios->timing == MMC_TIMING_MMC_DDR52)
  34. val |= SDMMC_ENABLE_PHASE;
  35. else
  36. val &= ~SDMMC_ENABLE_PHASE;
  37. mci_writel(host, ENABLE_SHIFT, val);
  38. val = mci_readl(host, DDR_REG);
  39. if (ios->timing == MMC_TIMING_MMC_HS400)
  40. val |= SDMMC_DDR_HS400;
  41. else
  42. val &= ~SDMMC_DDR_HS400;
  43. mci_writel(host, DDR_REG, val);
  44. if (ios->timing == MMC_TIMING_MMC_HS ||
  45. ios->timing == MMC_TIMING_LEGACY)
  46. clk_set_phase(priv->drive_clk, 180);
  47. else if (ios->timing == MMC_TIMING_MMC_HS200)
  48. clk_set_phase(priv->drive_clk, 135);
  49. }
  50. static int dw_mci_hi3798cv200_execute_tuning(struct dw_mci_slot *slot,
  51. u32 opcode)
  52. {
  53. static const int degrees[] = { 0, 45, 90, 135, 180, 225, 270, 315 };
  54. struct dw_mci *host = slot->host;
  55. struct hi3798cv200_priv *priv = host->priv;
  56. int raise_point = -1, fall_point = -1;
  57. int err, prev_err = -1;
  58. int found = 0;
  59. int i;
  60. for (i = 0; i < ARRAY_SIZE(degrees); i++) {
  61. clk_set_phase(priv->sample_clk, degrees[i]);
  62. mci_writel(host, RINTSTS, ALL_INT_CLR);
  63. err = mmc_send_tuning(slot->mmc, opcode, NULL);
  64. if (!err)
  65. found = 1;
  66. if (i > 0) {
  67. if (err && !prev_err)
  68. fall_point = i - 1;
  69. if (!err && prev_err)
  70. raise_point = i;
  71. }
  72. if (raise_point != -1 && fall_point != -1)
  73. goto tuning_out;
  74. prev_err = err;
  75. err = 0;
  76. }
  77. tuning_out:
  78. if (found) {
  79. if (raise_point == -1)
  80. raise_point = 0;
  81. if (fall_point == -1)
  82. fall_point = ARRAY_SIZE(degrees) - 1;
  83. if (fall_point < raise_point) {
  84. if ((raise_point + fall_point) >
  85. (ARRAY_SIZE(degrees) - 1))
  86. i = fall_point / 2;
  87. else
  88. i = (raise_point + ARRAY_SIZE(degrees) - 1) / 2;
  89. } else {
  90. i = (raise_point + fall_point) / 2;
  91. }
  92. clk_set_phase(priv->sample_clk, degrees[i]);
  93. dev_dbg(host->dev, "Tuning clk_sample[%d, %d], set[%d]\n",
  94. raise_point, fall_point, degrees[i]);
  95. } else {
  96. dev_err(host->dev, "No valid clk_sample shift! use default\n");
  97. err = -EINVAL;
  98. }
  99. mci_writel(host, RINTSTS, ALL_INT_CLR);
  100. return err;
  101. }
  102. static int dw_mci_hi3798cv200_init(struct dw_mci *host)
  103. {
  104. struct hi3798cv200_priv *priv;
  105. int ret;
  106. priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
  107. if (!priv)
  108. return -ENOMEM;
  109. priv->sample_clk = devm_clk_get(host->dev, "ciu-sample");
  110. if (IS_ERR(priv->sample_clk)) {
  111. dev_err(host->dev, "failed to get ciu-sample clock\n");
  112. return PTR_ERR(priv->sample_clk);
  113. }
  114. priv->drive_clk = devm_clk_get(host->dev, "ciu-drive");
  115. if (IS_ERR(priv->drive_clk)) {
  116. dev_err(host->dev, "failed to get ciu-drive clock\n");
  117. return PTR_ERR(priv->drive_clk);
  118. }
  119. ret = clk_prepare_enable(priv->sample_clk);
  120. if (ret) {
  121. dev_err(host->dev, "failed to enable ciu-sample clock\n");
  122. return ret;
  123. }
  124. ret = clk_prepare_enable(priv->drive_clk);
  125. if (ret) {
  126. dev_err(host->dev, "failed to enable ciu-drive clock\n");
  127. goto disable_sample_clk;
  128. }
  129. host->priv = priv;
  130. return 0;
  131. disable_sample_clk:
  132. clk_disable_unprepare(priv->sample_clk);
  133. return ret;
  134. }
  135. static const struct dw_mci_drv_data hi3798cv200_data = {
  136. .common_caps = MMC_CAP_CMD23,
  137. .init = dw_mci_hi3798cv200_init,
  138. .set_ios = dw_mci_hi3798cv200_set_ios,
  139. .execute_tuning = dw_mci_hi3798cv200_execute_tuning,
  140. };
  141. static int dw_mci_hi3798cv200_probe(struct platform_device *pdev)
  142. {
  143. return dw_mci_pltfm_register(pdev, &hi3798cv200_data);
  144. }
  145. static int dw_mci_hi3798cv200_remove(struct platform_device *pdev)
  146. {
  147. struct dw_mci *host = platform_get_drvdata(pdev);
  148. struct hi3798cv200_priv *priv = host->priv;
  149. clk_disable_unprepare(priv->drive_clk);
  150. clk_disable_unprepare(priv->sample_clk);
  151. dw_mci_pltfm_remove(pdev);
  152. return 0;
  153. }
  154. static const struct of_device_id dw_mci_hi3798cv200_match[] = {
  155. { .compatible = "hisilicon,hi3798cv200-dw-mshc", },
  156. {},
  157. };
  158. MODULE_DEVICE_TABLE(of, dw_mci_hi3798cv200_match);
  159. static struct platform_driver dw_mci_hi3798cv200_driver = {
  160. .probe = dw_mci_hi3798cv200_probe,
  161. .remove = dw_mci_hi3798cv200_remove,
  162. .driver = {
  163. .name = "dwmmc_hi3798cv200",
  164. .probe_type = PROBE_PREFER_ASYNCHRONOUS,
  165. .of_match_table = dw_mci_hi3798cv200_match,
  166. },
  167. };
  168. module_platform_driver(dw_mci_hi3798cv200_driver);
  169. MODULE_DESCRIPTION("HiSilicon Hi3798CV200 Specific DW-MSHC Driver Extension");
  170. MODULE_LICENSE("GPL v2");
  171. MODULE_ALIAS("platform:dwmmc_hi3798cv200");