cpuidle-mediatek.c 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Copyright (c) 2019 MediaTek Inc.
  4. */
  5. #include <linux/cpuidle.h>
  6. #include <linux/kernel.h>
  7. #include <linux/module.h>
  8. #include <linux/of.h>
  9. #include <linux/slab.h>
  10. #include <linux/cpu_pm.h>
  11. #include <linux/platform_device.h>
  12. #include <linux/of_platform.h>
  13. #include <linux/smp.h>
  14. #include <asm/cpuidle.h>
  15. #include <asm/suspend.h>
  16. #include <dt_idle_states.h>
  17. #define MTK_CPUIDLE_PREPARE (0)
  18. #define MTK_CPUIDLE_RESUME (1)
  19. typedef int (*mtk_pwr_fn)(int type,
  20. struct cpuidle_driver *drv,
  21. int index);
  22. static mtk_pwr_fn mtk_pwr_conservation;
  23. static __always_inline int __mtk_lp_enter(int index)
  24. {
  25. return CPU_PM_CPU_IDLE_ENTER(arm_cpuidle_suspend, index);
  26. }
  27. static int mtk_idle_state_enter(struct cpuidle_device *dev,
  28. struct cpuidle_driver *drv,
  29. int idx)
  30. {
  31. int ret;
  32. if (mtk_pwr_conservation) {
  33. ret = mtk_pwr_conservation(MTK_CPUIDLE_PREPARE, drv, idx);
  34. idx = ret ? 0 : idx;
  35. ret = __mtk_lp_enter(idx);
  36. mtk_pwr_conservation(MTK_CPUIDLE_RESUME, drv, idx);
  37. } else
  38. ret = CPU_PM_CPU_IDLE_ENTER(arm_cpuidle_suspend, 0);
  39. return ret;
  40. }
  41. static int mtk_regular_idle_state_enter(struct cpuidle_device *dev,
  42. struct cpuidle_driver *drv,
  43. int idx)
  44. {
  45. return CPU_PM_CPU_IDLE_ENTER(arm_cpuidle_suspend, idx);
  46. }
  47. static struct cpuidle_driver mtk_cpuidle_driver __initdata = {
  48. .name = "mtk_cpuidle",
  49. .owner = THIS_MODULE,
  50. .states[0] = {
  51. .enter = mtk_regular_idle_state_enter,
  52. .exit_latency = 1,
  53. .target_residency = 1,
  54. .power_usage = UINT_MAX,
  55. .name = "wfi",
  56. .desc = "wfi",
  57. },
  58. .state_count = 1
  59. };
  60. static const struct of_device_id mtk_idle_state_match[] __initconst = {
  61. { .compatible = "mediatek,idle-state",
  62. .data = mtk_idle_state_enter },
  63. { .compatible = "arm,idle-state",
  64. .data = mtk_regular_idle_state_enter },
  65. { },
  66. };
  67. static int mtk_cpuidle_pm_drv_probe(struct platform_device *pdev)
  68. {
  69. if (IS_ERR_OR_NULL(pdev->dev.platform_data))
  70. goto mtk_probe_fail;
  71. mtk_pwr_conservation = *((mtk_pwr_fn *)pdev->dev.platform_data);
  72. return 0;
  73. mtk_probe_fail:
  74. return -ENODATA;
  75. }
  76. int mtk_cpuidle_pm_drv_remove(struct platform_device *pdev)
  77. {
  78. mtk_pwr_conservation = NULL;
  79. return 0;
  80. }
  81. static struct platform_driver mtk_cpuidle_pm_driver = {
  82. .driver = {
  83. .name = "mtk_cpuidle_pm",
  84. .owner = THIS_MODULE,
  85. },
  86. .probe = mtk_cpuidle_pm_drv_probe,
  87. .remove = mtk_cpuidle_pm_drv_remove,
  88. };
  89. static int __init mtk_cpuidle_driver_init(void)
  90. {
  91. int cpu, ret;
  92. struct cpuidle_driver *drv;
  93. struct cpuidle_device *dev;
  94. platform_driver_register(&mtk_cpuidle_pm_driver);
  95. for_each_possible_cpu(cpu) {
  96. drv = kmemdup(&mtk_cpuidle_driver, sizeof(*drv), GFP_KERNEL);
  97. if (!drv) {
  98. ret = -ENOMEM;
  99. goto out_fail;
  100. }
  101. drv->cpumask = (struct cpumask *)cpumask_of(cpu);
  102. #ifdef CONFIG_DT_IDLE_STATES
  103. ret = dt_init_idle_driver(drv, mtk_idle_state_match, 1);
  104. if (ret <= 0) {
  105. ret = ret ? : -ENODEV;
  106. goto out_kfree_drv;
  107. }
  108. #endif
  109. ret = arm_cpuidle_init(cpu);
  110. if (ret) {
  111. pr_err("CPU %d failed to init idle CPU ops\n", cpu);
  112. goto out_kfree_drv;
  113. }
  114. ret = cpuidle_register(drv, NULL);
  115. if (ret)
  116. goto out_kfree_drv;
  117. }
  118. return 0;
  119. out_kfree_drv:
  120. kfree(drv);
  121. out_fail:
  122. while (--cpu >= 0) {
  123. dev = per_cpu(cpuidle_devices, cpu);
  124. drv = cpuidle_get_cpu_driver(dev);
  125. cpuidle_unregister(drv);
  126. kfree(drv);
  127. }
  128. return ret;
  129. }
  130. device_initcall_sync(mtk_cpuidle_driver_init);