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 .exit_latency = 0, 146 .target_residency = 0, 147 .enter = &snooze_loop }, 148 { /* CEDE */ 149 .name = "CEDE", 150 .desc = "CEDE", 151 .exit_latency = 10, 152 .target_residency = 100, 153 .enter = &dedicated_cede_loop }, 154}; 155 156/* 157 * States for shared partition case. 158 */ 159static struct cpuidle_state shared_states[] = { 160 { /* Shared Cede */ 161 .name = "Shared Cede", 162 .desc = "Shared Cede", 163 .exit_latency = 0, 164 .target_residency = 0, 165 .enter = &shared_cede_loop }, 166}; 167 168static int pseries_cpuidle_add_cpu_notifier(struct notifier_block *n, 169 unsigned long action, void *hcpu) 170{ 171 int hotcpu = (unsigned long)hcpu; 172 struct cpuidle_device *dev = 173 per_cpu(cpuidle_devices, hotcpu); 174 175 if (dev && cpuidle_get_driver()) { 176 switch (action) { 177 case CPU_ONLINE: 178 case CPU_ONLINE_FROZEN: 179 cpuidle_pause_and_lock(); 180 cpuidle_enable_device(dev); 181 cpuidle_resume_and_unlock(); 182 break; 183 184 case CPU_DEAD: 185 case CPU_DEAD_FROZEN: 186 cpuidle_pause_and_lock(); 187 cpuidle_disable_device(dev); 188 cpuidle_resume_and_unlock(); 189 break; 190 191 default: 192 return NOTIFY_DONE; 193 } 194 } 195 return NOTIFY_OK; 196} 197 198static struct notifier_block setup_hotplug_notifier = { 199 .notifier_call = pseries_cpuidle_add_cpu_notifier, 200}; 201 202/* 203 * pseries_cpuidle_driver_init() 204 */ 205static int pseries_cpuidle_driver_init(void) 206{ 207 int idle_state; 208 struct cpuidle_driver *drv = &pseries_idle_driver; 209 210 drv->state_count = 0; 211 212 for (idle_state = 0; idle_state < max_idle_state; ++idle_state) { 213 /* Is the state not enabled? */ 214 if (cpuidle_state_table[idle_state].enter == NULL) 215 continue; 216 217 drv->states[drv->state_count] = /* structure copy */ 218 cpuidle_state_table[idle_state]; 219 220 drv->state_count += 1; 221 } 222 223 return 0; 224} 225 226/* 227 * pseries_idle_probe() 228 * Choose state table for shared versus dedicated partition 229 */ 230static int pseries_idle_probe(void) 231{ 232 233 if (cpuidle_disable != IDLE_NO_OVERRIDE) 234 return -ENODEV; 235 236 if (firmware_has_feature(FW_FEATURE_SPLPAR)) { 237 if (lppaca_shared_proc(get_lppaca())) { 238 cpuidle_state_table = shared_states; 239 max_idle_state = ARRAY_SIZE(shared_states); 240 } else { 241 cpuidle_state_table = dedicated_states; 242 max_idle_state = ARRAY_SIZE(dedicated_states); 243 } 244 } else 245 return -ENODEV; 246 247 return 0; 248} 249 250static int __init pseries_processor_idle_init(void) 251{ 252 int retval; 253 254 retval = pseries_idle_probe(); 255 if (retval) 256 return retval; 257 258 pseries_cpuidle_driver_init(); 259 retval = cpuidle_register(&pseries_idle_driver, NULL); 260 if (retval) { 261 printk(KERN_DEBUG "Registration of pseries driver failed.\n"); 262 return retval; 263 } 264 265 register_cpu_notifier(&setup_hotplug_notifier); 266 printk(KERN_DEBUG "pseries_idle_driver registered\n"); 267 return 0; 268} 269 270device_initcall(pseries_processor_idle_init); 271