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 23 struct cpuidle_driver pseries_idle_driver = { 24 .name = "pseries_idle", 25 .owner = THIS_MODULE, 26 }; 27 28 static int max_idle_state; 29 static struct cpuidle_state *cpuidle_state_table; 30 31 static 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 42 static 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 56 static 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 80 static 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 97 static 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 116 static 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 */ 141 static 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 */ 161 static 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 171 static 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 201 static struct notifier_block setup_hotplug_notifier = { 202 .notifier_call = pseries_cpuidle_add_cpu_notifier, 203 }; 204 205 /* 206 * pseries_cpuidle_driver_init() 207 */ 208 static 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 */ 233 static 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 253 static 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 273 device_initcall(pseries_processor_idle_init); 274