diff options
-rw-r--r-- | drivers/cpuidle/cpuidle-powernv.c | 82 |
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 | |||
19 | struct cpuidle_driver powernv_idle_driver = { | 26 | struct 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 | */ |
82 | static struct cpuidle_state powernv_states[] = { | 89 | static 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 | ||
106 | static int powernv_cpuidle_add_cpu_notifier(struct notifier_block *n, | 99 | static 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 | ||
157 | static 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 | */ |
168 | static int powernv_idle_probe(void) | 216 | static 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 | ||