aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/cpuidle/cpuidle-powernv.c82
1 files changed, 65 insertions, 17 deletions
diff --git a/drivers/cpuidle/cpuidle-powernv.c b/drivers/cpuidle/cpuidle-powernv.c
index 4fb97cef82fd..fdae1c476e27 100644
--- a/drivers/cpuidle/cpuidle-powernv.c
+++ b/drivers/cpuidle/cpuidle-powernv.c
@@ -12,10 +12,17 @@
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> 14#include <linux/clockchips.h>
15#include <linux/of.h>
15 16
16#include <asm/machdep.h> 17#include <asm/machdep.h>
17#include <asm/firmware.h> 18#include <asm/firmware.h>
18 19
20/* Flags and constants used in PowerNV platform */
21
22#define MAX_POWERNV_IDLE_STATES 8
23#define IDLE_USE_INST_NAP 0x00010000 /* Use nap instruction */
24#define IDLE_USE_INST_SLEEP 0x00020000 /* Use sleep instruction */
25
19struct cpuidle_driver powernv_idle_driver = { 26struct cpuidle_driver powernv_idle_driver = {
20 .name = "powernv_idle", 27 .name = "powernv_idle",
21 .owner = THIS_MODULE, 28 .owner = THIS_MODULE,
@@ -79,7 +86,7 @@ static int fastsleep_loop(struct cpuidle_device *dev,
79/* 86/*
80 * States for dedicated partition case. 87 * States for dedicated partition case.
81 */ 88 */
82static struct cpuidle_state powernv_states[] = { 89static struct cpuidle_state powernv_states[MAX_POWERNV_IDLE_STATES] = {
83 { /* Snooze */ 90 { /* Snooze */
84 .name = "snooze", 91 .name = "snooze",
85 .desc = "snooze", 92 .desc = "snooze",
@@ -87,20 +94,6 @@ static struct cpuidle_state powernv_states[] = {
87 .exit_latency = 0, 94 .exit_latency = 0,
88 .target_residency = 0, 95 .target_residency = 0,
89 .enter = &snooze_loop }, 96 .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 },
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 },
104}; 97};
105 98
106static int powernv_cpuidle_add_cpu_notifier(struct notifier_block *n, 99static int powernv_cpuidle_add_cpu_notifier(struct notifier_block *n,
@@ -161,19 +154,74 @@ static int powernv_cpuidle_driver_init(void)
161 return 0; 154 return 0;
162} 155}
163 156
157static int powernv_add_idle_states(void)
158{
159 struct device_node *power_mgt;
160 struct property *prop;
161 int nr_idle_states = 1; /* Snooze */
162 int dt_idle_states;
163 u32 *flags;
164 int i;
165
166 /* Currently we have snooze statically defined */
167
168 power_mgt = of_find_node_by_path("/ibm,opal/power-mgt");
169 if (!power_mgt) {
170 pr_warn("opal: PowerMgmt Node not found\n");
171 return nr_idle_states;
172 }
173
174 prop = of_find_property(power_mgt, "ibm,cpu-idle-state-flags", NULL);
175 if (!prop) {
176 pr_warn("DT-PowerMgmt: missing ibm,cpu-idle-state-flags\n");
177 return nr_idle_states;
178 }
179
180 dt_idle_states = prop->length / sizeof(u32);
181 flags = (u32 *) prop->value;
182
183 for (i = 0; i < dt_idle_states; i++) {
184
185 if (flags[i] & IDLE_USE_INST_NAP) {
186 /* Add NAP state */
187 strcpy(powernv_states[nr_idle_states].name, "Nap");
188 strcpy(powernv_states[nr_idle_states].desc, "Nap");
189 powernv_states[nr_idle_states].flags = CPUIDLE_FLAG_TIME_VALID;
190 powernv_states[nr_idle_states].exit_latency = 10;
191 powernv_states[nr_idle_states].target_residency = 100;
192 powernv_states[nr_idle_states].enter = &nap_loop;
193 nr_idle_states++;
194 }
195
196 if (flags[i] & IDLE_USE_INST_SLEEP) {
197 /* Add FASTSLEEP state */
198 strcpy(powernv_states[nr_idle_states].name, "FastSleep");
199 strcpy(powernv_states[nr_idle_states].desc, "FastSleep");
200 powernv_states[nr_idle_states].flags =
201 CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TIMER_STOP;
202 powernv_states[nr_idle_states].exit_latency = 300;
203 powernv_states[nr_idle_states].target_residency = 1000000;
204 powernv_states[nr_idle_states].enter = &fastsleep_loop;
205 nr_idle_states++;
206 }
207 }
208
209 return nr_idle_states;
210}
211
164/* 212/*
165 * powernv_idle_probe() 213 * powernv_idle_probe()
166 * Choose state table for shared versus dedicated partition 214 * Choose state table for shared versus dedicated partition
167 */ 215 */
168static int powernv_idle_probe(void) 216static int powernv_idle_probe(void)
169{ 217{
170
171 if (cpuidle_disable != IDLE_NO_OVERRIDE) 218 if (cpuidle_disable != IDLE_NO_OVERRIDE)
172 return -ENODEV; 219 return -ENODEV;
173 220
174 if (firmware_has_feature(FW_FEATURE_OPALv3)) { 221 if (firmware_has_feature(FW_FEATURE_OPALv3)) {
175 cpuidle_state_table = powernv_states; 222 cpuidle_state_table = powernv_states;
176 max_idle_state = ARRAY_SIZE(powernv_states); 223 /* Device tree can indicate more idle states */
224 max_idle_state = powernv_add_idle_states();
177 } else 225 } else
178 return -ENODEV; 226 return -ENODEV;
179 227