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