aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/cpuidle
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2014-04-02 16:47:29 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2014-04-02 16:47:29 -0400
commite6d9bfc63813882c896bf7ea6f6b14ca7b50b755 (patch)
tree68decf00726f6f415cee04a62e68cc60b37f380b /drivers/cpuidle
parent235c7b9feb8779c7c289ed614324baebf3651bf9 (diff)
parent0888839c5b62c44a55ac9d28acc273ba663c65ea (diff)
Merge branch 'powernv-cpuidle' of git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc
Pull powerpc non-virtualized cpuidle from Ben Herrenschmidt: "This is the branch I mentioned in my other pull request which contains our improved cpuidle support for the "powernv" platform (non-virtualized). It adds support for the "fast sleep" feature of the processor which provides higher power savings than our usual "nap" mode but at the cost of losing the timers while asleep, and thus exploits the new timer broadcast framework to work around that limitation. It's based on a tip timer tree that you seem to have already merged" * 'powernv-cpuidle' of git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc: cpuidle/powernv: Parse device tree to setup idle states cpuidle/powernv: Add "Fast-Sleep" CPU idle state powerpc/powernv: Add OPAL call to resync timebase on wakeup powerpc/powernv: Add context management for Fast Sleep powerpc: Split timer_interrupt() into timer handling and interrupt handling routines powerpc: Implement tick broadcast IPI as a fixed IPI message powerpc: Free up the slot of PPC_MSG_CALL_FUNC_SINGLE IPI message
Diffstat (limited to 'drivers/cpuidle')
-rw-r--r--drivers/cpuidle/cpuidle-powernv.c102
1 files changed, 92 insertions, 10 deletions
diff --git a/drivers/cpuidle/cpuidle-powernv.c b/drivers/cpuidle/cpuidle-powernv.c
index f48607cd2540..719f6fb5b1c3 100644
--- a/drivers/cpuidle/cpuidle-powernv.c
+++ b/drivers/cpuidle/cpuidle-powernv.c
@@ -11,11 +11,19 @@
11#include <linux/cpuidle.h> 11#include <linux/cpuidle.h>
12#include <linux/cpu.h> 12#include <linux/cpu.h>
13#include <linux/notifier.h> 13#include <linux/notifier.h>
14#include <linux/clockchips.h>
15#include <linux/of.h>
14 16
15#include <asm/machdep.h> 17#include <asm/machdep.h>
16#include <asm/firmware.h> 18#include <asm/firmware.h>
17#include <asm/runlatch.h> 19#include <asm/runlatch.h>
18 20
21/* Flags and constants used in PowerNV platform */
22
23#define MAX_POWERNV_IDLE_STATES 8
24#define IDLE_USE_INST_NAP 0x00010000 /* Use nap instruction */
25#define IDLE_USE_INST_SLEEP 0x00020000 /* Use sleep instruction */
26
19struct cpuidle_driver powernv_idle_driver = { 27struct cpuidle_driver powernv_idle_driver = {
20 .name = "powernv_idle", 28 .name = "powernv_idle",
21 .owner = THIS_MODULE, 29 .owner = THIS_MODULE,
@@ -54,10 +62,36 @@ static int nap_loop(struct cpuidle_device *dev,
54 return index; 62 return index;
55} 63}
56 64
65static int fastsleep_loop(struct cpuidle_device *dev,
66 struct cpuidle_driver *drv,
67 int index)
68{
69 unsigned long old_lpcr = mfspr(SPRN_LPCR);
70 unsigned long new_lpcr;
71
72 if (unlikely(system_state < SYSTEM_RUNNING))
73 return index;
74
75 new_lpcr = old_lpcr;
76 new_lpcr &= ~(LPCR_MER | LPCR_PECE); /* lpcr[mer] must be 0 */
77
78 /* exit powersave upon external interrupt, but not decrementer
79 * interrupt.
80 */
81 new_lpcr |= LPCR_PECE0;
82
83 mtspr(SPRN_LPCR, new_lpcr);
84 power7_sleep();
85
86 mtspr(SPRN_LPCR, old_lpcr);
87
88 return index;
89}
90
57/* 91/*
58 * States for dedicated partition case. 92 * States for dedicated partition case.
59 */ 93 */
60static struct cpuidle_state powernv_states[] = { 94static struct cpuidle_state powernv_states[MAX_POWERNV_IDLE_STATES] = {
61 { /* Snooze */ 95 { /* Snooze */
62 .name = "snooze", 96 .name = "snooze",
63 .desc = "snooze", 97 .desc = "snooze",
@@ -65,13 +99,6 @@ static struct cpuidle_state powernv_states[] = {
65 .exit_latency = 0, 99 .exit_latency = 0,
66 .target_residency = 0, 100 .target_residency = 0,
67 .enter = &snooze_loop }, 101 .enter = &snooze_loop },
68 { /* NAP */
69 .name = "NAP",
70 .desc = "NAP",
71 .flags = CPUIDLE_FLAG_TIME_VALID,
72 .exit_latency = 10,
73 .target_residency = 100,
74 .enter = &nap_loop },
75}; 102};
76 103
77static int powernv_cpuidle_add_cpu_notifier(struct notifier_block *n, 104static int powernv_cpuidle_add_cpu_notifier(struct notifier_block *n,
@@ -132,19 +159,74 @@ static int powernv_cpuidle_driver_init(void)
132 return 0; 159 return 0;
133} 160}
134 161
162static int powernv_add_idle_states(void)
163{
164 struct device_node *power_mgt;
165 struct property *prop;
166 int nr_idle_states = 1; /* Snooze */
167 int dt_idle_states;
168 u32 *flags;
169 int i;
170
171 /* Currently we have snooze statically defined */
172
173 power_mgt = of_find_node_by_path("/ibm,opal/power-mgt");
174 if (!power_mgt) {
175 pr_warn("opal: PowerMgmt Node not found\n");
176 return nr_idle_states;
177 }
178
179 prop = of_find_property(power_mgt, "ibm,cpu-idle-state-flags", NULL);
180 if (!prop) {
181 pr_warn("DT-PowerMgmt: missing ibm,cpu-idle-state-flags\n");
182 return nr_idle_states;
183 }
184
185 dt_idle_states = prop->length / sizeof(u32);
186 flags = (u32 *) prop->value;
187
188 for (i = 0; i < dt_idle_states; i++) {
189
190 if (flags[i] & IDLE_USE_INST_NAP) {
191 /* Add NAP state */
192 strcpy(powernv_states[nr_idle_states].name, "Nap");
193 strcpy(powernv_states[nr_idle_states].desc, "Nap");
194 powernv_states[nr_idle_states].flags = CPUIDLE_FLAG_TIME_VALID;
195 powernv_states[nr_idle_states].exit_latency = 10;
196 powernv_states[nr_idle_states].target_residency = 100;
197 powernv_states[nr_idle_states].enter = &nap_loop;
198 nr_idle_states++;
199 }
200
201 if (flags[i] & IDLE_USE_INST_SLEEP) {
202 /* Add FASTSLEEP state */
203 strcpy(powernv_states[nr_idle_states].name, "FastSleep");
204 strcpy(powernv_states[nr_idle_states].desc, "FastSleep");
205 powernv_states[nr_idle_states].flags =
206 CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TIMER_STOP;
207 powernv_states[nr_idle_states].exit_latency = 300;
208 powernv_states[nr_idle_states].target_residency = 1000000;
209 powernv_states[nr_idle_states].enter = &fastsleep_loop;
210 nr_idle_states++;
211 }
212 }
213
214 return nr_idle_states;
215}
216
135/* 217/*
136 * powernv_idle_probe() 218 * powernv_idle_probe()
137 * Choose state table for shared versus dedicated partition 219 * Choose state table for shared versus dedicated partition
138 */ 220 */
139static int powernv_idle_probe(void) 221static int powernv_idle_probe(void)
140{ 222{
141
142 if (cpuidle_disable != IDLE_NO_OVERRIDE) 223 if (cpuidle_disable != IDLE_NO_OVERRIDE)
143 return -ENODEV; 224 return -ENODEV;
144 225
145 if (firmware_has_feature(FW_FEATURE_OPALv3)) { 226 if (firmware_has_feature(FW_FEATURE_OPALv3)) {
146 cpuidle_state_table = powernv_states; 227 cpuidle_state_table = powernv_states;
147 max_idle_state = ARRAY_SIZE(powernv_states); 228 /* Device tree can indicate more idle states */
229 max_idle_state = powernv_add_idle_states();
148 } else 230 } else
149 return -ENODEV; 231 return -ENODEV;
150 232