sdhci-st.c 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. /*
  2. * Support for SDHCI on STMicroelectronics SoCs
  3. *
  4. * Copyright (C) 2014 STMicroelectronics Ltd
  5. * Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
  6. * Contributors: Peter Griffin <peter.griffin@linaro.org>
  7. *
  8. * Based on sdhci-cns3xxx.c
  9. *
  10. * This program is free software; you can redistribute it and/or modify
  11. * it under the terms of the GNU General Public License version 2 as
  12. * published by the Free Software Foundation.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU General Public License for more details.
  18. *
  19. */
  20. #include <linux/io.h>
  21. #include <linux/of.h>
  22. #include <linux/module.h>
  23. #include <linux/err.h>
  24. #include <linux/mmc/host.h>
  25. #include "sdhci-pltfm.h"
  26. static u32 sdhci_st_readl(struct sdhci_host *host, int reg)
  27. {
  28. u32 ret;
  29. switch (reg) {
  30. case SDHCI_CAPABILITIES:
  31. ret = readl_relaxed(host->ioaddr + reg);
  32. /* Support 3.3V and 1.8V */
  33. ret &= ~SDHCI_CAN_VDD_300;
  34. break;
  35. default:
  36. ret = readl_relaxed(host->ioaddr + reg);
  37. }
  38. return ret;
  39. }
  40. static const struct sdhci_ops sdhci_st_ops = {
  41. .get_max_clock = sdhci_pltfm_clk_get_max_clock,
  42. .set_clock = sdhci_set_clock,
  43. .set_bus_width = sdhci_set_bus_width,
  44. .read_l = sdhci_st_readl,
  45. .reset = sdhci_reset,
  46. };
  47. static const struct sdhci_pltfm_data sdhci_st_pdata = {
  48. .ops = &sdhci_st_ops,
  49. .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC |
  50. SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
  51. };
  52. static int sdhci_st_probe(struct platform_device *pdev)
  53. {
  54. struct sdhci_host *host;
  55. struct sdhci_pltfm_host *pltfm_host;
  56. struct clk *clk;
  57. int ret = 0;
  58. u16 host_version;
  59. clk = devm_clk_get(&pdev->dev, "mmc");
  60. if (IS_ERR(clk)) {
  61. dev_err(&pdev->dev, "Peripheral clk not found\n");
  62. return PTR_ERR(clk);
  63. }
  64. host = sdhci_pltfm_init(pdev, &sdhci_st_pdata, 0);
  65. if (IS_ERR(host)) {
  66. dev_err(&pdev->dev, "Failed sdhci_pltfm_init\n");
  67. return PTR_ERR(host);
  68. }
  69. ret = mmc_of_parse(host->mmc);
  70. if (ret) {
  71. dev_err(&pdev->dev, "Failed mmc_of_parse\n");
  72. return ret;
  73. }
  74. clk_prepare_enable(clk);
  75. pltfm_host = sdhci_priv(host);
  76. pltfm_host->clk = clk;
  77. ret = sdhci_add_host(host);
  78. if (ret) {
  79. dev_err(&pdev->dev, "Failed sdhci_add_host\n");
  80. goto err_out;
  81. }
  82. platform_set_drvdata(pdev, host);
  83. host_version = readw_relaxed((host->ioaddr + SDHCI_HOST_VERSION));
  84. dev_info(&pdev->dev, "SDHCI ST Initialised: Host Version: 0x%x Vendor Version 0x%x\n",
  85. ((host_version & SDHCI_SPEC_VER_MASK) >> SDHCI_SPEC_VER_SHIFT),
  86. ((host_version & SDHCI_VENDOR_VER_MASK) >>
  87. SDHCI_VENDOR_VER_SHIFT));
  88. return 0;
  89. err_out:
  90. clk_disable_unprepare(clk);
  91. sdhci_pltfm_free(pdev);
  92. return ret;
  93. }
  94. static int sdhci_st_remove(struct platform_device *pdev)
  95. {
  96. struct sdhci_host *host = platform_get_drvdata(pdev);
  97. struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
  98. clk_disable_unprepare(pltfm_host->clk);
  99. return sdhci_pltfm_unregister(pdev);
  100. }
  101. #ifdef CONFIG_PM_SLEEP
  102. static int sdhci_st_suspend(struct device *dev)
  103. {
  104. struct sdhci_host *host = dev_get_drvdata(dev);
  105. struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
  106. int ret = sdhci_suspend_host(host);
  107. if (ret)
  108. goto out;
  109. clk_disable_unprepare(pltfm_host->clk);
  110. out:
  111. return ret;
  112. }
  113. static int sdhci_st_resume(struct device *dev)
  114. {
  115. struct sdhci_host *host = dev_get_drvdata(dev);
  116. struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
  117. clk_prepare_enable(pltfm_host->clk);
  118. return sdhci_resume_host(host);
  119. }
  120. #endif
  121. static SIMPLE_DEV_PM_OPS(sdhci_st_pmops, sdhci_st_suspend, sdhci_st_resume);
  122. static const struct of_device_id st_sdhci_match[] = {
  123. { .compatible = "st,sdhci" },
  124. {},
  125. };
  126. MODULE_DEVICE_TABLE(of, st_sdhci_match);
  127. static struct platform_driver sdhci_st_driver = {
  128. .probe = sdhci_st_probe,
  129. .remove = sdhci_st_remove,
  130. .driver = {
  131. .name = "sdhci-st",
  132. .pm = &sdhci_st_pmops,
  133. .of_match_table = of_match_ptr(st_sdhci_match),
  134. },
  135. };
  136. module_platform_driver(sdhci_st_driver);
  137. MODULE_DESCRIPTION("SDHCI driver for STMicroelectronics SoCs");
  138. MODULE_AUTHOR("Giuseppe Cavallaro <peppe.cavallaro@st.com>");
  139. MODULE_LICENSE("GPL v2");
  140. MODULE_ALIAS("platform:st-sdhci");