cpuidle-powernv.c 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. /*
  2. * cpuidle-powernv - idle state cpuidle driver.
  3. * Adapted from drivers/cpuidle/cpuidle-pseries
  4. *
  5. */
  6. #include <linux/kernel.h>
  7. #include <linux/module.h>
  8. #include <linux/init.h>
  9. #include <linux/moduleparam.h>
  10. #include <linux/cpuidle.h>
  11. #include <linux/cpu.h>
  12. #include <linux/notifier.h>
  13. #include <asm/machdep.h>
  14. #include <asm/firmware.h>
  15. struct cpuidle_driver powernv_idle_driver = {
  16. .name = "powernv_idle",
  17. .owner = THIS_MODULE,
  18. };
  19. static int max_idle_state;
  20. static struct cpuidle_state *cpuidle_state_table;
  21. static int snooze_loop(struct cpuidle_device *dev,
  22. struct cpuidle_driver *drv,
  23. int index)
  24. {
  25. local_irq_enable();
  26. set_thread_flag(TIF_POLLING_NRFLAG);
  27. while (!need_resched()) {
  28. HMT_low();
  29. HMT_very_low();
  30. }
  31. HMT_medium();
  32. clear_thread_flag(TIF_POLLING_NRFLAG);
  33. smp_mb();
  34. return index;
  35. }
  36. static int nap_loop(struct cpuidle_device *dev,
  37. struct cpuidle_driver *drv,
  38. int index)
  39. {
  40. power7_idle();
  41. return index;
  42. }
  43. /*
  44. * States for dedicated partition case.
  45. */
  46. static struct cpuidle_state powernv_states[] = {
  47. { /* Snooze */
  48. .name = "snooze",
  49. .desc = "snooze",
  50. .flags = CPUIDLE_FLAG_TIME_VALID,
  51. .exit_latency = 0,
  52. .target_residency = 0,
  53. .enter = &snooze_loop },
  54. { /* NAP */
  55. .name = "NAP",
  56. .desc = "NAP",
  57. .flags = CPUIDLE_FLAG_TIME_VALID,
  58. .exit_latency = 10,
  59. .target_residency = 100,
  60. .enter = &nap_loop },
  61. };
  62. static int powernv_cpuidle_add_cpu_notifier(struct notifier_block *n,
  63. unsigned long action, void *hcpu)
  64. {
  65. int hotcpu = (unsigned long)hcpu;
  66. struct cpuidle_device *dev =
  67. per_cpu(cpuidle_devices, hotcpu);
  68. if (dev && cpuidle_get_driver()) {
  69. switch (action) {
  70. case CPU_ONLINE:
  71. case CPU_ONLINE_FROZEN:
  72. cpuidle_pause_and_lock();
  73. cpuidle_enable_device(dev);
  74. cpuidle_resume_and_unlock();
  75. break;
  76. case CPU_DEAD:
  77. case CPU_DEAD_FROZEN:
  78. cpuidle_pause_and_lock();
  79. cpuidle_disable_device(dev);
  80. cpuidle_resume_and_unlock();
  81. break;
  82. default:
  83. return NOTIFY_DONE;
  84. }
  85. }
  86. return NOTIFY_OK;
  87. }
  88. static struct notifier_block setup_hotplug_notifier = {
  89. .notifier_call = powernv_cpuidle_add_cpu_notifier,
  90. };
  91. /*
  92. * powernv_cpuidle_driver_init()
  93. */
  94. static int powernv_cpuidle_driver_init(void)
  95. {
  96. int idle_state;
  97. struct cpuidle_driver *drv = &powernv_idle_driver;
  98. drv->state_count = 0;
  99. for (idle_state = 0; idle_state < max_idle_state; ++idle_state) {
  100. /* Is the state not enabled? */
  101. if (cpuidle_state_table[idle_state].enter == NULL)
  102. continue;
  103. drv->states[drv->state_count] = /* structure copy */
  104. cpuidle_state_table[idle_state];
  105. drv->state_count += 1;
  106. }
  107. return 0;
  108. }
  109. /*
  110. * powernv_idle_probe()
  111. * Choose state table for shared versus dedicated partition
  112. */
  113. static int powernv_idle_probe(void)
  114. {
  115. if (cpuidle_disable != IDLE_NO_OVERRIDE)
  116. return -ENODEV;
  117. if (firmware_has_feature(FW_FEATURE_OPALv3)) {
  118. cpuidle_state_table = powernv_states;
  119. max_idle_state = ARRAY_SIZE(powernv_states);
  120. } else
  121. return -ENODEV;
  122. return 0;
  123. }
  124. static int __init powernv_processor_idle_init(void)
  125. {
  126. int retval;
  127. retval = powernv_idle_probe();
  128. if (retval)
  129. return retval;
  130. powernv_cpuidle_driver_init();
  131. retval = cpuidle_register(&powernv_idle_driver, NULL);
  132. if (retval) {
  133. printk(KERN_DEBUG "Registration of powernv driver failed.\n");
  134. return retval;
  135. }
  136. register_cpu_notifier(&setup_hotplug_notifier);
  137. printk(KERN_DEBUG "powernv_idle_driver registered\n");
  138. return 0;
  139. }
  140. device_initcall(powernv_processor_idle_init);