12c2e6ecfSDeepthi Dharwar /* 22c2e6ecfSDeepthi Dharwar * cpuidle-powernv - idle state cpuidle driver. 32c2e6ecfSDeepthi Dharwar * Adapted from drivers/cpuidle/cpuidle-pseries 42c2e6ecfSDeepthi Dharwar * 52c2e6ecfSDeepthi Dharwar */ 62c2e6ecfSDeepthi Dharwar 72c2e6ecfSDeepthi Dharwar #include <linux/kernel.h> 82c2e6ecfSDeepthi Dharwar #include <linux/module.h> 92c2e6ecfSDeepthi Dharwar #include <linux/init.h> 102c2e6ecfSDeepthi Dharwar #include <linux/moduleparam.h> 112c2e6ecfSDeepthi Dharwar #include <linux/cpuidle.h> 122c2e6ecfSDeepthi Dharwar #include <linux/cpu.h> 132c2e6ecfSDeepthi Dharwar #include <linux/notifier.h> 140d948730SPreeti U Murthy #include <linux/clockchips.h> 150888839cSPreeti U Murthy #include <linux/of.h> 162c2e6ecfSDeepthi Dharwar 172c2e6ecfSDeepthi Dharwar #include <asm/machdep.h> 182c2e6ecfSDeepthi Dharwar #include <asm/firmware.h> 19591ac0cbSNicolas Pitre #include <asm/runlatch.h> 202c2e6ecfSDeepthi Dharwar 210888839cSPreeti U Murthy /* Flags and constants used in PowerNV platform */ 220888839cSPreeti U Murthy 230888839cSPreeti U Murthy #define MAX_POWERNV_IDLE_STATES 8 240888839cSPreeti U Murthy #define IDLE_USE_INST_NAP 0x00010000 /* Use nap instruction */ 250888839cSPreeti U Murthy #define IDLE_USE_INST_SLEEP 0x00020000 /* Use sleep instruction */ 260888839cSPreeti U Murthy 272c2e6ecfSDeepthi Dharwar struct cpuidle_driver powernv_idle_driver = { 282c2e6ecfSDeepthi Dharwar .name = "powernv_idle", 292c2e6ecfSDeepthi Dharwar .owner = THIS_MODULE, 302c2e6ecfSDeepthi Dharwar }; 312c2e6ecfSDeepthi Dharwar 322c2e6ecfSDeepthi Dharwar static int max_idle_state; 332c2e6ecfSDeepthi Dharwar static struct cpuidle_state *cpuidle_state_table; 342c2e6ecfSDeepthi Dharwar 352c2e6ecfSDeepthi Dharwar static int snooze_loop(struct cpuidle_device *dev, 362c2e6ecfSDeepthi Dharwar struct cpuidle_driver *drv, 372c2e6ecfSDeepthi Dharwar int index) 382c2e6ecfSDeepthi Dharwar { 392c2e6ecfSDeepthi Dharwar local_irq_enable(); 402c2e6ecfSDeepthi Dharwar set_thread_flag(TIF_POLLING_NRFLAG); 412c2e6ecfSDeepthi Dharwar 42591ac0cbSNicolas Pitre ppc64_runlatch_off(); 432c2e6ecfSDeepthi Dharwar while (!need_resched()) { 442c2e6ecfSDeepthi Dharwar HMT_low(); 452c2e6ecfSDeepthi Dharwar HMT_very_low(); 462c2e6ecfSDeepthi Dharwar } 472c2e6ecfSDeepthi Dharwar 482c2e6ecfSDeepthi Dharwar HMT_medium(); 49591ac0cbSNicolas Pitre ppc64_runlatch_on(); 502c2e6ecfSDeepthi Dharwar clear_thread_flag(TIF_POLLING_NRFLAG); 512c2e6ecfSDeepthi Dharwar smp_mb(); 522c2e6ecfSDeepthi Dharwar return index; 532c2e6ecfSDeepthi Dharwar } 542c2e6ecfSDeepthi Dharwar 552c2e6ecfSDeepthi Dharwar static int nap_loop(struct cpuidle_device *dev, 562c2e6ecfSDeepthi Dharwar struct cpuidle_driver *drv, 572c2e6ecfSDeepthi Dharwar int index) 582c2e6ecfSDeepthi Dharwar { 59591ac0cbSNicolas Pitre ppc64_runlatch_off(); 602c2e6ecfSDeepthi Dharwar power7_idle(); 61591ac0cbSNicolas Pitre ppc64_runlatch_on(); 622c2e6ecfSDeepthi Dharwar return index; 632c2e6ecfSDeepthi Dharwar } 642c2e6ecfSDeepthi Dharwar 650d948730SPreeti U Murthy static int fastsleep_loop(struct cpuidle_device *dev, 660d948730SPreeti U Murthy struct cpuidle_driver *drv, 670d948730SPreeti U Murthy int index) 680d948730SPreeti U Murthy { 690d948730SPreeti U Murthy unsigned long old_lpcr = mfspr(SPRN_LPCR); 700d948730SPreeti U Murthy unsigned long new_lpcr; 710d948730SPreeti U Murthy 720d948730SPreeti U Murthy if (unlikely(system_state < SYSTEM_RUNNING)) 730d948730SPreeti U Murthy return index; 740d948730SPreeti U Murthy 750d948730SPreeti U Murthy new_lpcr = old_lpcr; 769b6a68d9SMichael Neuling /* Do not exit powersave upon decrementer as we've setup the timer 779b6a68d9SMichael Neuling * offload. 780d948730SPreeti U Murthy */ 799b6a68d9SMichael Neuling new_lpcr &= ~LPCR_PECE1; 800d948730SPreeti U Murthy 810d948730SPreeti U Murthy mtspr(SPRN_LPCR, new_lpcr); 820d948730SPreeti U Murthy power7_sleep(); 830d948730SPreeti U Murthy 840d948730SPreeti U Murthy mtspr(SPRN_LPCR, old_lpcr); 850d948730SPreeti U Murthy 860d948730SPreeti U Murthy return index; 870d948730SPreeti U Murthy } 880d948730SPreeti U Murthy 892c2e6ecfSDeepthi Dharwar /* 902c2e6ecfSDeepthi Dharwar * States for dedicated partition case. 912c2e6ecfSDeepthi Dharwar */ 920888839cSPreeti U Murthy static struct cpuidle_state powernv_states[MAX_POWERNV_IDLE_STATES] = { 932c2e6ecfSDeepthi Dharwar { /* Snooze */ 942c2e6ecfSDeepthi Dharwar .name = "snooze", 952c2e6ecfSDeepthi Dharwar .desc = "snooze", 962c2e6ecfSDeepthi Dharwar .exit_latency = 0, 972c2e6ecfSDeepthi Dharwar .target_residency = 0, 982c2e6ecfSDeepthi Dharwar .enter = &snooze_loop }, 992c2e6ecfSDeepthi Dharwar }; 1002c2e6ecfSDeepthi Dharwar 1012c2e6ecfSDeepthi Dharwar static int powernv_cpuidle_add_cpu_notifier(struct notifier_block *n, 1022c2e6ecfSDeepthi Dharwar unsigned long action, void *hcpu) 1032c2e6ecfSDeepthi Dharwar { 1042c2e6ecfSDeepthi Dharwar int hotcpu = (unsigned long)hcpu; 1052c2e6ecfSDeepthi Dharwar struct cpuidle_device *dev = 1062c2e6ecfSDeepthi Dharwar per_cpu(cpuidle_devices, hotcpu); 1072c2e6ecfSDeepthi Dharwar 1082c2e6ecfSDeepthi Dharwar if (dev && cpuidle_get_driver()) { 1092c2e6ecfSDeepthi Dharwar switch (action) { 1102c2e6ecfSDeepthi Dharwar case CPU_ONLINE: 1112c2e6ecfSDeepthi Dharwar case CPU_ONLINE_FROZEN: 1122c2e6ecfSDeepthi Dharwar cpuidle_pause_and_lock(); 1132c2e6ecfSDeepthi Dharwar cpuidle_enable_device(dev); 1142c2e6ecfSDeepthi Dharwar cpuidle_resume_and_unlock(); 1152c2e6ecfSDeepthi Dharwar break; 1162c2e6ecfSDeepthi Dharwar 1172c2e6ecfSDeepthi Dharwar case CPU_DEAD: 1182c2e6ecfSDeepthi Dharwar case CPU_DEAD_FROZEN: 1192c2e6ecfSDeepthi Dharwar cpuidle_pause_and_lock(); 1202c2e6ecfSDeepthi Dharwar cpuidle_disable_device(dev); 1212c2e6ecfSDeepthi Dharwar cpuidle_resume_and_unlock(); 1222c2e6ecfSDeepthi Dharwar break; 1232c2e6ecfSDeepthi Dharwar 1242c2e6ecfSDeepthi Dharwar default: 1252c2e6ecfSDeepthi Dharwar return NOTIFY_DONE; 1262c2e6ecfSDeepthi Dharwar } 1272c2e6ecfSDeepthi Dharwar } 1282c2e6ecfSDeepthi Dharwar return NOTIFY_OK; 1292c2e6ecfSDeepthi Dharwar } 1302c2e6ecfSDeepthi Dharwar 1312c2e6ecfSDeepthi Dharwar static struct notifier_block setup_hotplug_notifier = { 1322c2e6ecfSDeepthi Dharwar .notifier_call = powernv_cpuidle_add_cpu_notifier, 1332c2e6ecfSDeepthi Dharwar }; 1342c2e6ecfSDeepthi Dharwar 1352c2e6ecfSDeepthi Dharwar /* 1362c2e6ecfSDeepthi Dharwar * powernv_cpuidle_driver_init() 1372c2e6ecfSDeepthi Dharwar */ 1382c2e6ecfSDeepthi Dharwar static int powernv_cpuidle_driver_init(void) 1392c2e6ecfSDeepthi Dharwar { 1402c2e6ecfSDeepthi Dharwar int idle_state; 1412c2e6ecfSDeepthi Dharwar struct cpuidle_driver *drv = &powernv_idle_driver; 1422c2e6ecfSDeepthi Dharwar 1432c2e6ecfSDeepthi Dharwar drv->state_count = 0; 1442c2e6ecfSDeepthi Dharwar 1452c2e6ecfSDeepthi Dharwar for (idle_state = 0; idle_state < max_idle_state; ++idle_state) { 1462c2e6ecfSDeepthi Dharwar /* Is the state not enabled? */ 1472c2e6ecfSDeepthi Dharwar if (cpuidle_state_table[idle_state].enter == NULL) 1482c2e6ecfSDeepthi Dharwar continue; 1492c2e6ecfSDeepthi Dharwar 1502c2e6ecfSDeepthi Dharwar drv->states[drv->state_count] = /* structure copy */ 1512c2e6ecfSDeepthi Dharwar cpuidle_state_table[idle_state]; 1522c2e6ecfSDeepthi Dharwar 1532c2e6ecfSDeepthi Dharwar drv->state_count += 1; 1542c2e6ecfSDeepthi Dharwar } 1552c2e6ecfSDeepthi Dharwar 1562c2e6ecfSDeepthi Dharwar return 0; 1572c2e6ecfSDeepthi Dharwar } 1582c2e6ecfSDeepthi Dharwar 1590888839cSPreeti U Murthy static int powernv_add_idle_states(void) 1600888839cSPreeti U Murthy { 1610888839cSPreeti U Murthy struct device_node *power_mgt; 1620888839cSPreeti U Murthy int nr_idle_states = 1; /* Snooze */ 1630888839cSPreeti U Murthy int dt_idle_states; 16495707d85SVaidyanathan Srinivasan const __be32 *idle_state_flags; 16574aa51b5SPreeti U. Murthy const __be32 *idle_state_latency; 16674aa51b5SPreeti U. Murthy u32 len_flags, flags, latency_ns; 1670888839cSPreeti U Murthy int i; 1680888839cSPreeti U Murthy 1690888839cSPreeti U Murthy /* Currently we have snooze statically defined */ 1700888839cSPreeti U Murthy 1710888839cSPreeti U Murthy power_mgt = of_find_node_by_path("/ibm,opal/power-mgt"); 1720888839cSPreeti U Murthy if (!power_mgt) { 1730888839cSPreeti U Murthy pr_warn("opal: PowerMgmt Node not found\n"); 1740888839cSPreeti U Murthy return nr_idle_states; 1750888839cSPreeti U Murthy } 1760888839cSPreeti U Murthy 17795707d85SVaidyanathan Srinivasan idle_state_flags = of_get_property(power_mgt, "ibm,cpu-idle-state-flags", &len_flags); 17895707d85SVaidyanathan Srinivasan if (!idle_state_flags) { 1790888839cSPreeti U Murthy pr_warn("DT-PowerMgmt: missing ibm,cpu-idle-state-flags\n"); 1800888839cSPreeti U Murthy return nr_idle_states; 1810888839cSPreeti U Murthy } 1820888839cSPreeti U Murthy 18374aa51b5SPreeti U. Murthy idle_state_latency = of_get_property(power_mgt, 18474aa51b5SPreeti U. Murthy "ibm,cpu-idle-state-latencies-ns", NULL); 18574aa51b5SPreeti U. Murthy if (!idle_state_latency) { 18674aa51b5SPreeti U. Murthy pr_warn("DT-PowerMgmt: missing ibm,cpu-idle-state-latencies-ns\n"); 18774aa51b5SPreeti U. Murthy return nr_idle_states; 18874aa51b5SPreeti U. Murthy } 18974aa51b5SPreeti U. Murthy 19095707d85SVaidyanathan Srinivasan dt_idle_states = len_flags / sizeof(u32); 1910888839cSPreeti U Murthy 1920888839cSPreeti U Murthy for (i = 0; i < dt_idle_states; i++) { 1930888839cSPreeti U Murthy 19495707d85SVaidyanathan Srinivasan flags = be32_to_cpu(idle_state_flags[i]); 19574aa51b5SPreeti U. Murthy 19674aa51b5SPreeti U. Murthy /* Cpuidle accepts exit_latency in us and we estimate 19774aa51b5SPreeti U. Murthy * target residency to be 10x exit_latency 19874aa51b5SPreeti U. Murthy */ 19974aa51b5SPreeti U. Murthy latency_ns = be32_to_cpu(idle_state_latency[i]); 20095707d85SVaidyanathan Srinivasan if (flags & IDLE_USE_INST_NAP) { 2010888839cSPreeti U Murthy /* Add NAP state */ 2020888839cSPreeti U Murthy strcpy(powernv_states[nr_idle_states].name, "Nap"); 2030888839cSPreeti U Murthy strcpy(powernv_states[nr_idle_states].desc, "Nap"); 204b82b6ccaSDaniel Lezcano powernv_states[nr_idle_states].flags = 0; 20574aa51b5SPreeti U. Murthy powernv_states[nr_idle_states].exit_latency = 20674aa51b5SPreeti U. Murthy ((unsigned int)latency_ns) / 1000; 20774aa51b5SPreeti U. Murthy powernv_states[nr_idle_states].target_residency = 20874aa51b5SPreeti U. Murthy ((unsigned int)latency_ns / 100); 2090888839cSPreeti U Murthy powernv_states[nr_idle_states].enter = &nap_loop; 2100888839cSPreeti U Murthy nr_idle_states++; 2110888839cSPreeti U Murthy } 2120888839cSPreeti U Murthy 21395707d85SVaidyanathan Srinivasan if (flags & IDLE_USE_INST_SLEEP) { 2140888839cSPreeti U Murthy /* Add FASTSLEEP state */ 2150888839cSPreeti U Murthy strcpy(powernv_states[nr_idle_states].name, "FastSleep"); 2160888839cSPreeti U Murthy strcpy(powernv_states[nr_idle_states].desc, "FastSleep"); 217b82b6ccaSDaniel Lezcano powernv_states[nr_idle_states].flags = CPUIDLE_FLAG_TIMER_STOP; 21874aa51b5SPreeti U. Murthy powernv_states[nr_idle_states].exit_latency = 21974aa51b5SPreeti U. Murthy ((unsigned int)latency_ns) / 1000; 22074aa51b5SPreeti U. Murthy powernv_states[nr_idle_states].target_residency = 22174aa51b5SPreeti U. Murthy ((unsigned int)latency_ns / 100); 2220888839cSPreeti U Murthy powernv_states[nr_idle_states].enter = &fastsleep_loop; 2230888839cSPreeti U Murthy nr_idle_states++; 2240888839cSPreeti U Murthy } 2250888839cSPreeti U Murthy } 2260888839cSPreeti U Murthy 2270888839cSPreeti U Murthy return nr_idle_states; 2280888839cSPreeti U Murthy } 2290888839cSPreeti U Murthy 2302c2e6ecfSDeepthi Dharwar /* 2312c2e6ecfSDeepthi Dharwar * powernv_idle_probe() 2322c2e6ecfSDeepthi Dharwar * Choose state table for shared versus dedicated partition 2332c2e6ecfSDeepthi Dharwar */ 2342c2e6ecfSDeepthi Dharwar static int powernv_idle_probe(void) 2352c2e6ecfSDeepthi Dharwar { 2362c2e6ecfSDeepthi Dharwar if (cpuidle_disable != IDLE_NO_OVERRIDE) 2372c2e6ecfSDeepthi Dharwar return -ENODEV; 2382c2e6ecfSDeepthi Dharwar 2392c2e6ecfSDeepthi Dharwar if (firmware_has_feature(FW_FEATURE_OPALv3)) { 2402c2e6ecfSDeepthi Dharwar cpuidle_state_table = powernv_states; 2410888839cSPreeti U Murthy /* Device tree can indicate more idle states */ 2420888839cSPreeti U Murthy max_idle_state = powernv_add_idle_states(); 2432c2e6ecfSDeepthi Dharwar } else 2442c2e6ecfSDeepthi Dharwar return -ENODEV; 2452c2e6ecfSDeepthi Dharwar 2462c2e6ecfSDeepthi Dharwar return 0; 2472c2e6ecfSDeepthi Dharwar } 2482c2e6ecfSDeepthi Dharwar 2492c2e6ecfSDeepthi Dharwar static int __init powernv_processor_idle_init(void) 2502c2e6ecfSDeepthi Dharwar { 2512c2e6ecfSDeepthi Dharwar int retval; 2522c2e6ecfSDeepthi Dharwar 2532c2e6ecfSDeepthi Dharwar retval = powernv_idle_probe(); 2542c2e6ecfSDeepthi Dharwar if (retval) 2552c2e6ecfSDeepthi Dharwar return retval; 2562c2e6ecfSDeepthi Dharwar 2572c2e6ecfSDeepthi Dharwar powernv_cpuidle_driver_init(); 2582c2e6ecfSDeepthi Dharwar retval = cpuidle_register(&powernv_idle_driver, NULL); 2592c2e6ecfSDeepthi Dharwar if (retval) { 2602c2e6ecfSDeepthi Dharwar printk(KERN_DEBUG "Registration of powernv driver failed.\n"); 2612c2e6ecfSDeepthi Dharwar return retval; 2622c2e6ecfSDeepthi Dharwar } 2632c2e6ecfSDeepthi Dharwar 2642c2e6ecfSDeepthi Dharwar register_cpu_notifier(&setup_hotplug_notifier); 2652c2e6ecfSDeepthi Dharwar printk(KERN_DEBUG "powernv_idle_driver registered\n"); 2662c2e6ecfSDeepthi Dharwar return 0; 2672c2e6ecfSDeepthi Dharwar } 2682c2e6ecfSDeepthi Dharwar 2692c2e6ecfSDeepthi Dharwar device_initcall(powernv_processor_idle_init); 270