cpuidle/powernv: Add "Fast-Sleep" CPU idle state
[deliverable/linux.git] / drivers / cpuidle / cpuidle-powernv.c
CommitLineData
2c2e6ecf
DD
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>
0d948730 14#include <linux/clockchips.h>
2c2e6ecf
DD
15
16#include <asm/machdep.h>
17#include <asm/firmware.h>
18
19struct cpuidle_driver powernv_idle_driver = {
20 .name = "powernv_idle",
21 .owner = THIS_MODULE,
22};
23
24static int max_idle_state;
25static struct cpuidle_state *cpuidle_state_table;
26
27static int snooze_loop(struct cpuidle_device *dev,
28 struct cpuidle_driver *drv,
29 int index)
30{
31 local_irq_enable();
32 set_thread_flag(TIF_POLLING_NRFLAG);
33
34 while (!need_resched()) {
35 HMT_low();
36 HMT_very_low();
37 }
38
39 HMT_medium();
40 clear_thread_flag(TIF_POLLING_NRFLAG);
41 smp_mb();
42 return index;
43}
44
45static int nap_loop(struct cpuidle_device *dev,
46 struct cpuidle_driver *drv,
47 int index)
48{
49 power7_idle();
50 return index;
51}
52
0d948730
PM
53static int fastsleep_loop(struct cpuidle_device *dev,
54 struct cpuidle_driver *drv,
55 int index)
56{
57 unsigned long old_lpcr = mfspr(SPRN_LPCR);
58 unsigned long new_lpcr;
59
60 if (unlikely(system_state < SYSTEM_RUNNING))
61 return index;
62
63 new_lpcr = old_lpcr;
64 new_lpcr &= ~(LPCR_MER | LPCR_PECE); /* lpcr[mer] must be 0 */
65
66 /* exit powersave upon external interrupt, but not decrementer
67 * interrupt.
68 */
69 new_lpcr |= LPCR_PECE0;
70
71 mtspr(SPRN_LPCR, new_lpcr);
72 power7_sleep();
73
74 mtspr(SPRN_LPCR, old_lpcr);
75
76 return index;
77}
78
2c2e6ecf
DD
79/*
80 * States for dedicated partition case.
81 */
82static struct cpuidle_state powernv_states[] = {
83 { /* Snooze */
84 .name = "snooze",
85 .desc = "snooze",
86 .flags = CPUIDLE_FLAG_TIME_VALID,
87 .exit_latency = 0,
88 .target_residency = 0,
89 .enter = &snooze_loop },
90 { /* NAP */
91 .name = "NAP",
92 .desc = "NAP",
93 .flags = CPUIDLE_FLAG_TIME_VALID,
94 .exit_latency = 10,
95 .target_residency = 100,
96 .enter = &nap_loop },
0d948730
PM
97 { /* Fastsleep */
98 .name = "fastsleep",
99 .desc = "fastsleep",
100 .flags = CPUIDLE_FLAG_TIME_VALID,
101 .exit_latency = 10,
102 .target_residency = 100,
103 .enter = &fastsleep_loop },
2c2e6ecf
DD
104};
105
106static int powernv_cpuidle_add_cpu_notifier(struct notifier_block *n,
107 unsigned long action, void *hcpu)
108{
109 int hotcpu = (unsigned long)hcpu;
110 struct cpuidle_device *dev =
111 per_cpu(cpuidle_devices, hotcpu);
112
113 if (dev && cpuidle_get_driver()) {
114 switch (action) {
115 case CPU_ONLINE:
116 case CPU_ONLINE_FROZEN:
117 cpuidle_pause_and_lock();
118 cpuidle_enable_device(dev);
119 cpuidle_resume_and_unlock();
120 break;
121
122 case CPU_DEAD:
123 case CPU_DEAD_FROZEN:
124 cpuidle_pause_and_lock();
125 cpuidle_disable_device(dev);
126 cpuidle_resume_and_unlock();
127 break;
128
129 default:
130 return NOTIFY_DONE;
131 }
132 }
133 return NOTIFY_OK;
134}
135
136static struct notifier_block setup_hotplug_notifier = {
137 .notifier_call = powernv_cpuidle_add_cpu_notifier,
138};
139
140/*
141 * powernv_cpuidle_driver_init()
142 */
143static int powernv_cpuidle_driver_init(void)
144{
145 int idle_state;
146 struct cpuidle_driver *drv = &powernv_idle_driver;
147
148 drv->state_count = 0;
149
150 for (idle_state = 0; idle_state < max_idle_state; ++idle_state) {
151 /* Is the state not enabled? */
152 if (cpuidle_state_table[idle_state].enter == NULL)
153 continue;
154
155 drv->states[drv->state_count] = /* structure copy */
156 cpuidle_state_table[idle_state];
157
158 drv->state_count += 1;
159 }
160
161 return 0;
162}
163
164/*
165 * powernv_idle_probe()
166 * Choose state table for shared versus dedicated partition
167 */
168static int powernv_idle_probe(void)
169{
170
171 if (cpuidle_disable != IDLE_NO_OVERRIDE)
172 return -ENODEV;
173
174 if (firmware_has_feature(FW_FEATURE_OPALv3)) {
175 cpuidle_state_table = powernv_states;
176 max_idle_state = ARRAY_SIZE(powernv_states);
177 } else
178 return -ENODEV;
179
180 return 0;
181}
182
183static int __init powernv_processor_idle_init(void)
184{
185 int retval;
186
187 retval = powernv_idle_probe();
188 if (retval)
189 return retval;
190
191 powernv_cpuidle_driver_init();
192 retval = cpuidle_register(&powernv_idle_driver, NULL);
193 if (retval) {
194 printk(KERN_DEBUG "Registration of powernv driver failed.\n");
195 return retval;
196 }
197
198 register_cpu_notifier(&setup_hotplug_notifier);
199 printk(KERN_DEBUG "powernv_idle_driver registered\n");
200 return 0;
201}
202
203device_initcall(powernv_processor_idle_init);
This page took 0.033676 seconds and 5 git commands to generate.