Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

powerpc/powernv/cpuidle: Back-end cpuidle driver for powernv platform.

Following patch ports the cpuidle framework for powernv
platform and also implements a cpuidle back-end powernv
idle driver calling on to power7_nap and snooze idle states.

Signed-off-by: Deepthi Dharwar <deepthi@linux.vnet.ibm.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>

authored by

Deepthi Dharwar and committed by
Benjamin Herrenschmidt
2c2e6ecf 3fa8cad8

+191 -1
+12 -1
arch/powerpc/platforms/powernv/setup.c
··· 26 26 #include <linux/of_fdt.h> 27 27 #include <linux/interrupt.h> 28 28 #include <linux/bug.h> 29 + #include <linux/cpuidle.h> 29 30 30 31 #include <asm/machdep.h> 31 32 #include <asm/firmware.h> ··· 217 216 return 1; 218 217 } 219 218 219 + void powernv_idle(void) 220 + { 221 + /* Hook to cpuidle framework if available, else 222 + * call on default platform idle code 223 + */ 224 + if (cpuidle_idle_call()) { 225 + power7_idle(); 226 + } 227 + } 228 + 220 229 define_machine(powernv) { 221 230 .name = "PowerNV", 222 231 .probe = pnv_probe, ··· 236 225 .show_cpuinfo = pnv_show_cpuinfo, 237 226 .progress = pnv_progress, 238 227 .machine_shutdown = pnv_shutdown, 239 - .power_save = power7_idle, 228 + .power_save = powernv_idle, 240 229 .calibrate_decr = generic_calibrate_decr, 241 230 #ifdef CONFIG_KEXEC 242 231 .kexec_cpu_down = pnv_kexec_cpu_down,
+9
drivers/cpuidle/Kconfig.powerpc
··· 9 9 help 10 10 Select this option to enable processor idle state management 11 11 through cpuidle subsystem. 12 + 13 + config POWERNV_CPUIDLE 14 + bool "Cpuidle driver for powernv platforms" 15 + depends on CPU_IDLE 16 + depends on PPC_POWERNV 17 + default y 18 + help 19 + Select this option to enable processor idle state management 20 + through cpuidle subsystem.
+1
drivers/cpuidle/Makefile
··· 17 17 ############################################################################### 18 18 # POWERPC drivers 19 19 obj-$(CONFIG_PSERIES_CPUIDLE) += cpuidle-pseries.o 20 + obj-$(CONFIG_POWERNV_CPUIDLE) += cpuidle-powernv.o
+169
drivers/cpuidle/cpuidle-powernv.c
··· 1 + /* 2 + * cpuidle-powernv - idle state cpuidle driver. 3 + * Adapted from drivers/cpuidle/cpuidle-pseries 4 + * 5 + */ 6 + 7 + #include <linux/kernel.h> 8 + #include <linux/module.h> 9 + #include <linux/init.h> 10 + #include <linux/moduleparam.h> 11 + #include <linux/cpuidle.h> 12 + #include <linux/cpu.h> 13 + #include <linux/notifier.h> 14 + 15 + #include <asm/machdep.h> 16 + #include <asm/firmware.h> 17 + 18 + struct cpuidle_driver powernv_idle_driver = { 19 + .name = "powernv_idle", 20 + .owner = THIS_MODULE, 21 + }; 22 + 23 + static int max_idle_state; 24 + static struct cpuidle_state *cpuidle_state_table; 25 + 26 + static int snooze_loop(struct cpuidle_device *dev, 27 + struct cpuidle_driver *drv, 28 + int index) 29 + { 30 + local_irq_enable(); 31 + set_thread_flag(TIF_POLLING_NRFLAG); 32 + 33 + while (!need_resched()) { 34 + HMT_low(); 35 + HMT_very_low(); 36 + } 37 + 38 + HMT_medium(); 39 + clear_thread_flag(TIF_POLLING_NRFLAG); 40 + smp_mb(); 41 + return index; 42 + } 43 + 44 + static int nap_loop(struct cpuidle_device *dev, 45 + struct cpuidle_driver *drv, 46 + int index) 47 + { 48 + power7_idle(); 49 + return index; 50 + } 51 + 52 + /* 53 + * States for dedicated partition case. 54 + */ 55 + static struct cpuidle_state powernv_states[] = { 56 + { /* Snooze */ 57 + .name = "snooze", 58 + .desc = "snooze", 59 + .flags = CPUIDLE_FLAG_TIME_VALID, 60 + .exit_latency = 0, 61 + .target_residency = 0, 62 + .enter = &snooze_loop }, 63 + { /* NAP */ 64 + .name = "NAP", 65 + .desc = "NAP", 66 + .flags = CPUIDLE_FLAG_TIME_VALID, 67 + .exit_latency = 10, 68 + .target_residency = 100, 69 + .enter = &nap_loop }, 70 + }; 71 + 72 + static int powernv_cpuidle_add_cpu_notifier(struct notifier_block *n, 73 + unsigned long action, void *hcpu) 74 + { 75 + int hotcpu = (unsigned long)hcpu; 76 + struct cpuidle_device *dev = 77 + per_cpu(cpuidle_devices, hotcpu); 78 + 79 + if (dev && cpuidle_get_driver()) { 80 + switch (action) { 81 + case CPU_ONLINE: 82 + case CPU_ONLINE_FROZEN: 83 + cpuidle_pause_and_lock(); 84 + cpuidle_enable_device(dev); 85 + cpuidle_resume_and_unlock(); 86 + break; 87 + 88 + case CPU_DEAD: 89 + case CPU_DEAD_FROZEN: 90 + cpuidle_pause_and_lock(); 91 + cpuidle_disable_device(dev); 92 + cpuidle_resume_and_unlock(); 93 + break; 94 + 95 + default: 96 + return NOTIFY_DONE; 97 + } 98 + } 99 + return NOTIFY_OK; 100 + } 101 + 102 + static struct notifier_block setup_hotplug_notifier = { 103 + .notifier_call = powernv_cpuidle_add_cpu_notifier, 104 + }; 105 + 106 + /* 107 + * powernv_cpuidle_driver_init() 108 + */ 109 + static int powernv_cpuidle_driver_init(void) 110 + { 111 + int idle_state; 112 + struct cpuidle_driver *drv = &powernv_idle_driver; 113 + 114 + drv->state_count = 0; 115 + 116 + for (idle_state = 0; idle_state < max_idle_state; ++idle_state) { 117 + /* Is the state not enabled? */ 118 + if (cpuidle_state_table[idle_state].enter == NULL) 119 + continue; 120 + 121 + drv->states[drv->state_count] = /* structure copy */ 122 + cpuidle_state_table[idle_state]; 123 + 124 + drv->state_count += 1; 125 + } 126 + 127 + return 0; 128 + } 129 + 130 + /* 131 + * powernv_idle_probe() 132 + * Choose state table for shared versus dedicated partition 133 + */ 134 + static int powernv_idle_probe(void) 135 + { 136 + 137 + if (cpuidle_disable != IDLE_NO_OVERRIDE) 138 + return -ENODEV; 139 + 140 + if (firmware_has_feature(FW_FEATURE_OPALv3)) { 141 + cpuidle_state_table = powernv_states; 142 + max_idle_state = ARRAY_SIZE(powernv_states); 143 + } else 144 + return -ENODEV; 145 + 146 + return 0; 147 + } 148 + 149 + static int __init powernv_processor_idle_init(void) 150 + { 151 + int retval; 152 + 153 + retval = powernv_idle_probe(); 154 + if (retval) 155 + return retval; 156 + 157 + powernv_cpuidle_driver_init(); 158 + retval = cpuidle_register(&powernv_idle_driver, NULL); 159 + if (retval) { 160 + printk(KERN_DEBUG "Registration of powernv driver failed.\n"); 161 + return retval; 162 + } 163 + 164 + register_cpu_notifier(&setup_hotplug_notifier); 165 + printk(KERN_DEBUG "powernv_idle_driver registered\n"); 166 + return 0; 167 + } 168 + 169 + device_initcall(powernv_processor_idle_init);