rcpm.c 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. // SPDX-License-Identifier: GPL-2.0
  2. //
  3. // rcpm.c - Freescale QorIQ RCPM driver
  4. //
  5. // Copyright 2019 NXP
  6. //
  7. // Author: Ran Wang <ran.wang_1@nxp.com>
  8. #include <linux/init.h>
  9. #include <linux/module.h>
  10. #include <linux/platform_device.h>
  11. #include <linux/of_address.h>
  12. #include <linux/slab.h>
  13. #include <linux/suspend.h>
  14. #include <linux/kernel.h>
  15. #define RCPM_WAKEUP_CELL_MAX_SIZE 7
  16. struct rcpm {
  17. unsigned int wakeup_cells;
  18. void __iomem *ippdexpcr_base;
  19. bool little_endian;
  20. };
  21. /**
  22. * rcpm_pm_prepare - performs device-level tasks associated with power
  23. * management, such as programming related to the wakeup source control.
  24. * @dev: Device to handle.
  25. *
  26. */
  27. static int rcpm_pm_prepare(struct device *dev)
  28. {
  29. int i, ret, idx;
  30. void __iomem *base;
  31. struct wakeup_source *ws;
  32. struct rcpm *rcpm;
  33. struct device_node *np = dev->of_node;
  34. u32 value[RCPM_WAKEUP_CELL_MAX_SIZE + 1];
  35. u32 setting[RCPM_WAKEUP_CELL_MAX_SIZE] = {0};
  36. rcpm = dev_get_drvdata(dev);
  37. if (!rcpm)
  38. return -EINVAL;
  39. base = rcpm->ippdexpcr_base;
  40. idx = wakeup_sources_read_lock();
  41. /* Begin with first registered wakeup source */
  42. for_each_wakeup_source(ws) {
  43. /* skip object which is not attached to device */
  44. if (!ws->dev || !ws->dev->parent)
  45. continue;
  46. ret = device_property_read_u32_array(ws->dev->parent,
  47. "fsl,rcpm-wakeup", value,
  48. rcpm->wakeup_cells + 1);
  49. /* Wakeup source should refer to current rcpm device */
  50. if (ret || (np->phandle != value[0]))
  51. continue;
  52. /* Property "#fsl,rcpm-wakeup-cells" of rcpm node defines the
  53. * number of IPPDEXPCR register cells, and "fsl,rcpm-wakeup"
  54. * of wakeup source IP contains an integer array: <phandle to
  55. * RCPM node, IPPDEXPCR0 setting, IPPDEXPCR1 setting,
  56. * IPPDEXPCR2 setting, etc>.
  57. *
  58. * So we will go thought them to collect setting data.
  59. */
  60. for (i = 0; i < rcpm->wakeup_cells; i++)
  61. setting[i] |= value[i + 1];
  62. }
  63. wakeup_sources_read_unlock(idx);
  64. /* Program all IPPDEXPCRn once */
  65. for (i = 0; i < rcpm->wakeup_cells; i++) {
  66. u32 tmp = setting[i];
  67. void __iomem *address = base + i * 4;
  68. if (!tmp)
  69. continue;
  70. /* We can only OR related bits */
  71. if (rcpm->little_endian) {
  72. tmp |= ioread32(address);
  73. iowrite32(tmp, address);
  74. } else {
  75. tmp |= ioread32be(address);
  76. iowrite32be(tmp, address);
  77. }
  78. }
  79. return 0;
  80. }
  81. static const struct dev_pm_ops rcpm_pm_ops = {
  82. .prepare = rcpm_pm_prepare,
  83. };
  84. static int rcpm_probe(struct platform_device *pdev)
  85. {
  86. struct device *dev = &pdev->dev;
  87. struct resource *r;
  88. struct rcpm *rcpm;
  89. int ret;
  90. rcpm = devm_kzalloc(dev, sizeof(*rcpm), GFP_KERNEL);
  91. if (!rcpm)
  92. return -ENOMEM;
  93. r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  94. if (!r)
  95. return -ENODEV;
  96. rcpm->ippdexpcr_base = devm_ioremap_resource(&pdev->dev, r);
  97. if (IS_ERR(rcpm->ippdexpcr_base)) {
  98. ret = PTR_ERR(rcpm->ippdexpcr_base);
  99. return ret;
  100. }
  101. rcpm->little_endian = device_property_read_bool(
  102. &pdev->dev, "little-endian");
  103. ret = device_property_read_u32(&pdev->dev,
  104. "#fsl,rcpm-wakeup-cells", &rcpm->wakeup_cells);
  105. if (ret)
  106. return ret;
  107. dev_set_drvdata(&pdev->dev, rcpm);
  108. return 0;
  109. }
  110. static const struct of_device_id rcpm_of_match[] = {
  111. { .compatible = "fsl,qoriq-rcpm-2.1+", },
  112. {}
  113. };
  114. MODULE_DEVICE_TABLE(of, rcpm_of_match);
  115. static struct platform_driver rcpm_driver = {
  116. .driver = {
  117. .name = "rcpm",
  118. .of_match_table = rcpm_of_match,
  119. .pm = &rcpm_pm_ops,
  120. },
  121. .probe = rcpm_probe,
  122. };
  123. module_platform_driver(rcpm_driver);