cpuidle-imx6q.c 2.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright (C) 2012 Freescale Semiconductor, Inc.
  4. */
  5. #include <linux/context_tracking.h>
  6. #include <linux/cpuidle.h>
  7. #include <linux/module.h>
  8. #include <asm/cpuidle.h>
  9. #include <soc/imx/cpuidle.h>
  10. #include "common.h"
  11. #include "cpuidle.h"
  12. #include "hardware.h"
  13. static int num_idle_cpus = 0;
  14. static DEFINE_RAW_SPINLOCK(cpuidle_lock);
  15. static int imx6q_enter_wait(struct cpuidle_device *dev,
  16. struct cpuidle_driver *drv, int index)
  17. {
  18. raw_spin_lock(&cpuidle_lock);
  19. if (++num_idle_cpus == num_online_cpus())
  20. imx6_set_lpm(WAIT_UNCLOCKED);
  21. raw_spin_unlock(&cpuidle_lock);
  22. ct_idle_enter();
  23. cpu_do_idle();
  24. ct_idle_exit();
  25. raw_spin_lock(&cpuidle_lock);
  26. if (num_idle_cpus-- == num_online_cpus())
  27. imx6_set_lpm(WAIT_CLOCKED);
  28. raw_spin_unlock(&cpuidle_lock);
  29. return index;
  30. }
  31. static struct cpuidle_driver imx6q_cpuidle_driver = {
  32. .name = "imx6q_cpuidle",
  33. .owner = THIS_MODULE,
  34. .states = {
  35. /* WFI */
  36. ARM_CPUIDLE_WFI_STATE,
  37. /* WAIT */
  38. {
  39. .exit_latency = 50,
  40. .target_residency = 75,
  41. .flags = CPUIDLE_FLAG_TIMER_STOP | CPUIDLE_FLAG_RCU_IDLE,
  42. .enter = imx6q_enter_wait,
  43. .name = "WAIT",
  44. .desc = "Clock off",
  45. },
  46. },
  47. .state_count = 2,
  48. .safe_state_index = 0,
  49. };
  50. /*
  51. * i.MX6 Q/DL has an erratum (ERR006687) that prevents the FEC from waking the
  52. * CPUs when they are in wait(unclocked) state. As the hardware workaround isn't
  53. * applicable to all boards, disable the deeper idle state when the workaround
  54. * isn't present and the FEC is in use.
  55. */
  56. void imx6q_cpuidle_fec_irqs_used(void)
  57. {
  58. cpuidle_driver_state_disabled(&imx6q_cpuidle_driver, 1, true);
  59. }
  60. EXPORT_SYMBOL_GPL(imx6q_cpuidle_fec_irqs_used);
  61. void imx6q_cpuidle_fec_irqs_unused(void)
  62. {
  63. cpuidle_driver_state_disabled(&imx6q_cpuidle_driver, 1, false);
  64. }
  65. EXPORT_SYMBOL_GPL(imx6q_cpuidle_fec_irqs_unused);
  66. int __init imx6q_cpuidle_init(void)
  67. {
  68. /* Set INT_MEM_CLK_LPM bit to get a reliable WAIT mode support */
  69. imx6_set_int_mem_clk_lpm(true);
  70. return cpuidle_register(&imx6q_cpuidle_driver, NULL);
  71. }