1/* 2 * cpuidle-pseries - idle state cpuidle driver. 3 * Adapted from drivers/idle/intel_idle.c and 4 * drivers/acpi/processor_idle.c 5 * 6 */ 7 8#include <linux/kernel.h> 9#include <linux/module.h> 10#include <linux/init.h> 11#include <linux/moduleparam.h> 12#include <linux/cpuidle.h> 13#include <linux/cpu.h> 14#include <linux/notifier.h> 15 16#include <asm/paca.h> 17#include <asm/reg.h> 18#include <asm/machdep.h> 19#include <asm/firmware.h> 20#include <asm/runlatch.h> 21#include <asm/plpar_wrappers.h> 22 23struct cpuidle_driver pseries_idle_driver = { 24 .name = "pseries_idle", 25 .owner = THIS_MODULE, 26}; 27 28static int max_idle_state; 29static struct cpuidle_state *cpuidle_state_table; 30 31static inline void idle_loop_prolog(unsigned long *in_purr) 32{ 33 ppc64_runlatch_off(); 34 *in_purr = mfspr(SPRN_PURR); 35 /* 36 * Indicate to the HV that we are idle. Now would be 37 * a good time to find other work to dispatch. 38 */ 39 get_lppaca()->idle = 1; 40} 41 42static inline void idle_loop_epilog(unsigned long in_purr) 43{ 44 u64 wait_cycles; 45 46 wait_cycles = be64_to_cpu(get_lppaca()->wait_state_cycles); 47 wait_cycles += mfspr(SPRN_PURR) - in_purr; 48 get_lppaca()->wait_state_cycles = cpu_to_be64(wait_cycles); 49 get_lppaca()->idle = 0; 50 51 if (irqs_disabled()) 52 local_irq_enable(); 53 ppc64_runlatch_on(); 54} 55 56static int snooze_loop(struct cpuidle_device *dev, 57 struct cpuidle_driver *drv, 58 int index) 59{ 60 unsigned long in_purr; 61 62 idle_loop_prolog(&in_purr); 63 local_irq_enable(); 64 set_thread_flag(TIF_POLLING_NRFLAG); 65 66 while (!need_resched()) { 67 HMT_low(); 68 HMT_very_low(); 69 } 70 71 HMT_medium(); 72 clear_thread_flag(TIF_POLLING_NRFLAG); 73 smp_mb(); 74 75 idle_loop_epilog(in_purr); 76 77 return index; 78} 79 80static void check_and_cede_processor(void) 81{ 82 /* 83 * Ensure our interrupt state is properly tracked, 84 * also checks if no interrupt has occurred while we 85 * were soft-disabled 86 */ 87 if (prep_irq_for_idle()) { 88 cede_processor(); 89#ifdef CONFIG_TRACE_IRQFLAGS 90 /* Ensure that H_CEDE returns with IRQs on */ 91 if (WARN_ON(!(mfmsr() & MSR_EE))) 92 __hard_irq_enable(); 93#endif 94 } 95} 96 97static int dedicated_cede_loop(struct cpuidle_device *dev, 98 struct cpuidle_driver *drv, 99 int index) 100{ 101 unsigned long in_purr; 102 103 idle_loop_prolog(&in_purr); 104 get_lppaca()->donate_dedicated_cpu = 1; 105 106 HMT_medium(); 107 check_and_cede_processor(); 108 109 get_lppaca()->donate_dedicated_cpu = 0; 110 111 idle_loop_epilog(in_purr); 112 113 return index; 114} 115 116static int shared_cede_loop(struct cpuidle_device *dev, 117 struct cpuidle_driver *drv, 118 int index) 119{ 120 unsigned long in_purr; 121 122 idle_loop_prolog(&in_purr); 123 124 /* 125 * Yield the processor to the hypervisor. We return if 126 * an external interrupt occurs (which are driven prior 127 * to returning here) or if a prod occurs from another 128 * processor. When returning here, external interrupts 129 * are enabled. 130 */ 131 check_and_cede_processor(); 132 133 idle_loop_epilog(in_purr); 134 135 return index; 136} 137 138/* 139 * States for dedicated partition case. 140 */ 141static struct cpuidle_state dedicated_states[] = { 142 { /* Snooze */ 143 .name = "snooze", 144 .desc = "snooze", 145 .flags = CPUIDLE_FLAG_TIME_VALID, 146 .exit_latency = 0, 147 .target_residency = 0, 148 .enter = &snooze_loop }, 149 { /* CEDE */ 150 .name = "CEDE", 151 .desc = "CEDE", 152 .flags = CPUIDLE_FLAG_TIME_VALID, 153 .exit_latency = 10, 154 .target_residency = 100, 155 .enter = &dedicated_cede_loop }, 156}; 157 158/* 159 * States for shared partition case. 160 */ 161static struct cpuidle_state shared_states[] = { 162 { /* Shared Cede */ 163 .name = "Shared Cede", 164 .desc = "Shared Cede", 165 .flags = CPUIDLE_FLAG_TIME_VALID, 166 .exit_latency = 0, 167 .target_residency = 0, 168 .enter = &shared_cede_loop }, 169}; 170 171static int pseries_cpuidle_add_cpu_notifier(struct notifier_block *n, 172 unsigned long action, void *hcpu) 173{ 174 int hotcpu = (unsigned long)hcpu; 175 struct cpuidle_device *dev = 176 per_cpu(cpuidle_devices, hotcpu); 177 178 if (dev && cpuidle_get_driver()) { 179 switch (action) { 180 case CPU_ONLINE: 181 case CPU_ONLINE_FROZEN: 182 cpuidle_pause_and_lock(); 183 cpuidle_enable_device(dev); 184 cpuidle_resume_and_unlock(); 185 break; 186 187 case CPU_DEAD: 188 case CPU_DEAD_FROZEN: 189 cpuidle_pause_and_lock(); 190 cpuidle_disable_device(dev); 191 cpuidle_resume_and_unlock(); 192 break; 193 194 default: 195 return NOTIFY_DONE; 196 } 197 } 198 return NOTIFY_OK; 199} 200 201static struct notifier_block setup_hotplug_notifier = { 202 .notifier_call = pseries_cpuidle_add_cpu_notifier, 203}; 204 205/* 206 * pseries_cpuidle_driver_init() 207 */ 208static int pseries_cpuidle_driver_init(void) 209{ 210 int idle_state; 211 struct cpuidle_driver *drv = &pseries_idle_driver; 212 213 drv->state_count = 0; 214 215 for (idle_state = 0; idle_state < max_idle_state; ++idle_state) { 216 /* Is the state not enabled? */ 217 if (cpuidle_state_table[idle_state].enter == NULL) 218 continue; 219 220 drv->states[drv->state_count] = /* structure copy */ 221 cpuidle_state_table[idle_state]; 222 223 drv->state_count += 1; 224 } 225 226 return 0; 227} 228 229/* 230 * pseries_idle_probe() 231 * Choose state table for shared versus dedicated partition 232 */ 233static int pseries_idle_probe(void) 234{ 235 236 if (cpuidle_disable != IDLE_NO_OVERRIDE) 237 return -ENODEV; 238 239 if (firmware_has_feature(FW_FEATURE_SPLPAR)) { 240 if (lppaca_shared_proc(get_lppaca())) { 241 cpuidle_state_table = shared_states; 242 max_idle_state = ARRAY_SIZE(shared_states); 243 } else { 244 cpuidle_state_table = dedicated_states; 245 max_idle_state = ARRAY_SIZE(dedicated_states); 246 } 247 } else 248 return -ENODEV; 249 250 return 0; 251} 252 253static int __init pseries_processor_idle_init(void) 254{ 255 int retval; 256 257 retval = pseries_idle_probe(); 258 if (retval) 259 return retval; 260 261 pseries_cpuidle_driver_init(); 262 retval = cpuidle_register(&pseries_idle_driver, NULL); 263 if (retval) { 264 printk(KERN_DEBUG "Registration of pseries driver failed.\n"); 265 return retval; 266 } 267 268 register_cpu_notifier(&setup_hotplug_notifier); 269 printk(KERN_DEBUG "pseries_idle_driver registered\n"); 270 return 0; 271} 272 273device_initcall(pseries_processor_idle_init); 274