dw_mmc-pltfm.c 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * Synopsys DesignWare Multimedia Card Interface driver
  4. *
  5. * Copyright (C) 2009 NXP Semiconductors
  6. * Copyright (C) 2009, 2010 Imagination Technologies Ltd.
  7. */
  8. #include <linux/err.h>
  9. #include <linux/interrupt.h>
  10. #include <linux/module.h>
  11. #include <linux/io.h>
  12. #include <linux/irq.h>
  13. #include <linux/platform_device.h>
  14. #include <linux/pm_runtime.h>
  15. #include <linux/slab.h>
  16. #include <linux/mmc/host.h>
  17. #include <linux/mmc/mmc.h>
  18. #include <linux/of.h>
  19. #include <linux/mfd/altera-sysmgr.h>
  20. #include <linux/regmap.h>
  21. #include "dw_mmc.h"
  22. #include "dw_mmc-pltfm.h"
  23. #define SOCFPGA_DW_MMC_CLK_PHASE_STEP 45
  24. #define SYSMGR_SDMMC_CTRL_SET(smplsel, drvsel, reg_shift) \
  25. ((((smplsel) & 0x7) << reg_shift) | (((drvsel) & 0x7) << 0))
  26. int dw_mci_pltfm_register(struct platform_device *pdev,
  27. const struct dw_mci_drv_data *drv_data)
  28. {
  29. struct dw_mci *host;
  30. struct resource *regs;
  31. host = devm_kzalloc(&pdev->dev, sizeof(struct dw_mci), GFP_KERNEL);
  32. if (!host)
  33. return -ENOMEM;
  34. host->irq = platform_get_irq(pdev, 0);
  35. if (host->irq < 0)
  36. return host->irq;
  37. host->drv_data = drv_data;
  38. host->dev = &pdev->dev;
  39. host->irq_flags = 0;
  40. host->pdata = pdev->dev.platform_data;
  41. host->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &regs);
  42. if (IS_ERR(host->regs))
  43. return PTR_ERR(host->regs);
  44. /* Get registers' physical base address */
  45. host->phy_regs = regs->start;
  46. platform_set_drvdata(pdev, host);
  47. return dw_mci_probe(host);
  48. }
  49. EXPORT_SYMBOL_GPL(dw_mci_pltfm_register);
  50. const struct dev_pm_ops dw_mci_pltfm_pmops = {
  51. SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
  52. pm_runtime_force_resume)
  53. SET_RUNTIME_PM_OPS(dw_mci_runtime_suspend,
  54. dw_mci_runtime_resume,
  55. NULL)
  56. };
  57. EXPORT_SYMBOL_GPL(dw_mci_pltfm_pmops);
  58. #ifdef CONFIG_SOC_ARK1668ED
  59. static unsigned long ark_dwmmc_caps[3] = {
  60. MMC_CAP_CMD23,
  61. MMC_CAP_CMD23,
  62. MMC_CAP_CMD23,
  63. };
  64. static const struct dw_mci_drv_data ark_drv_data = {
  65. .caps = ark_dwmmc_caps,
  66. .num_caps = ARRAY_SIZE(ark_dwmmc_caps),
  67. };
  68. #endif
  69. static int dw_mci_socfpga_priv_init(struct dw_mci *host)
  70. {
  71. struct device_node *np = host->dev->of_node;
  72. struct regmap *sys_mgr_base_addr;
  73. u32 clk_phase[2] = {0}, reg_offset, reg_shift;
  74. int i, rc, hs_timing;
  75. rc = of_property_read_variable_u32_array(np, "clk-phase-sd-hs", &clk_phase[0], 2, 0);
  76. if (rc < 0)
  77. return 0;
  78. sys_mgr_base_addr = altr_sysmgr_regmap_lookup_by_phandle(np, "altr,sysmgr-syscon");
  79. if (IS_ERR(sys_mgr_base_addr)) {
  80. dev_warn(host->dev, "clk-phase-sd-hs was specified, but failed to find altr,sys-mgr regmap!\n");
  81. return 0;
  82. }
  83. of_property_read_u32_index(np, "altr,sysmgr-syscon", 1, &reg_offset);
  84. of_property_read_u32_index(np, "altr,sysmgr-syscon", 2, &reg_shift);
  85. for (i = 0; i < ARRAY_SIZE(clk_phase); i++)
  86. clk_phase[i] /= SOCFPGA_DW_MMC_CLK_PHASE_STEP;
  87. hs_timing = SYSMGR_SDMMC_CTRL_SET(clk_phase[0], clk_phase[1], reg_shift);
  88. regmap_write(sys_mgr_base_addr, reg_offset, hs_timing);
  89. return 0;
  90. }
  91. static const struct dw_mci_drv_data socfpga_drv_data = {
  92. .init = dw_mci_socfpga_priv_init,
  93. };
  94. static const struct of_device_id dw_mci_pltfm_match[] = {
  95. #ifdef CONFIG_SOC_ARK1668ED
  96. { .compatible = "snps,dw-mshc", .data = &ark_drv_data},
  97. #else
  98. { .compatible = "snps,dw-mshc", },
  99. #endif
  100. { .compatible = "altr,socfpga-dw-mshc", .data = &socfpga_drv_data, },
  101. { .compatible = "img,pistachio-dw-mshc", },
  102. {},
  103. };
  104. MODULE_DEVICE_TABLE(of, dw_mci_pltfm_match);
  105. static int dw_mci_pltfm_probe(struct platform_device *pdev)
  106. {
  107. const struct dw_mci_drv_data *drv_data = NULL;
  108. const struct of_device_id *match;
  109. if (pdev->dev.of_node) {
  110. match = of_match_node(dw_mci_pltfm_match, pdev->dev.of_node);
  111. drv_data = match->data;
  112. }
  113. return dw_mci_pltfm_register(pdev, drv_data);
  114. }
  115. void dw_mci_pltfm_remove(struct platform_device *pdev)
  116. {
  117. struct dw_mci *host = platform_get_drvdata(pdev);
  118. dw_mci_remove(host);
  119. }
  120. EXPORT_SYMBOL_GPL(dw_mci_pltfm_remove);
  121. static struct platform_driver dw_mci_pltfm_driver = {
  122. .probe = dw_mci_pltfm_probe,
  123. .remove_new = dw_mci_pltfm_remove,
  124. .driver = {
  125. .name = "dw_mmc",
  126. .probe_type = PROBE_PREFER_ASYNCHRONOUS,
  127. .of_match_table = dw_mci_pltfm_match,
  128. .pm = &dw_mci_pltfm_pmops,
  129. },
  130. };
  131. module_platform_driver(dw_mci_pltfm_driver);
  132. MODULE_DESCRIPTION("DW Multimedia Card Interface driver");
  133. MODULE_AUTHOR("NXP Semiconductor VietNam");
  134. MODULE_AUTHOR("Imagination Technologies Ltd");
  135. MODULE_LICENSE("GPL v2");