diff options
Diffstat (limited to 'drivers/cpuidle/cpuidle-powernv.c')
-rw-r--r-- | drivers/cpuidle/cpuidle-powernv.c | 84 |
1 files changed, 49 insertions, 35 deletions
diff --git a/drivers/cpuidle/cpuidle-powernv.c b/drivers/cpuidle/cpuidle-powernv.c index aedec0957934..59372077ec7c 100644 --- a/drivers/cpuidle/cpuidle-powernv.c +++ b/drivers/cpuidle/cpuidle-powernv.c | |||
@@ -13,6 +13,7 @@ | |||
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 | #include <linux/of.h> |
16 | #include <linux/slab.h> | ||
16 | 17 | ||
17 | #include <asm/machdep.h> | 18 | #include <asm/machdep.h> |
18 | #include <asm/firmware.h> | 19 | #include <asm/firmware.h> |
@@ -158,70 +159,83 @@ static int powernv_add_idle_states(void) | |||
158 | struct device_node *power_mgt; | 159 | struct device_node *power_mgt; |
159 | int nr_idle_states = 1; /* Snooze */ | 160 | int nr_idle_states = 1; /* Snooze */ |
160 | int dt_idle_states; | 161 | int dt_idle_states; |
161 | const __be32 *idle_state_flags; | 162 | u32 *latency_ns, *residency_ns, *flags; |
162 | const __be32 *idle_state_latency; | 163 | int i, rc; |
163 | u32 len_flags, flags, latency_ns; | ||
164 | int i; | ||
165 | 164 | ||
166 | /* Currently we have snooze statically defined */ | 165 | /* Currently we have snooze statically defined */ |
167 | 166 | ||
168 | power_mgt = of_find_node_by_path("/ibm,opal/power-mgt"); | 167 | power_mgt = of_find_node_by_path("/ibm,opal/power-mgt"); |
169 | if (!power_mgt) { | 168 | if (!power_mgt) { |
170 | pr_warn("opal: PowerMgmt Node not found\n"); | 169 | pr_warn("opal: PowerMgmt Node not found\n"); |
171 | return nr_idle_states; | 170 | goto out; |
172 | } | 171 | } |
173 | 172 | ||
174 | idle_state_flags = of_get_property(power_mgt, "ibm,cpu-idle-state-flags", &len_flags); | 173 | /* Read values of any property to determine the num of idle states */ |
175 | if (!idle_state_flags) { | 174 | dt_idle_states = of_property_count_u32_elems(power_mgt, "ibm,cpu-idle-state-flags"); |
176 | pr_warn("DT-PowerMgmt: missing ibm,cpu-idle-state-flags\n"); | 175 | if (dt_idle_states < 0) { |
177 | return nr_idle_states; | 176 | pr_warn("cpuidle-powernv: no idle states found in the DT\n"); |
177 | goto out; | ||
178 | } | 178 | } |
179 | 179 | ||
180 | idle_state_latency = of_get_property(power_mgt, | 180 | flags = kzalloc(sizeof(*flags) * dt_idle_states, GFP_KERNEL); |
181 | "ibm,cpu-idle-state-latencies-ns", NULL); | 181 | if (of_property_read_u32_array(power_mgt, |
182 | if (!idle_state_latency) { | 182 | "ibm,cpu-idle-state-flags", flags, dt_idle_states)) { |
183 | pr_warn("DT-PowerMgmt: missing ibm,cpu-idle-state-latencies-ns\n"); | 183 | pr_warn("cpuidle-powernv : missing ibm,cpu-idle-state-flags in DT\n"); |
184 | return nr_idle_states; | 184 | goto out_free_flags; |
185 | } | 185 | } |
186 | 186 | ||
187 | dt_idle_states = len_flags / sizeof(u32); | 187 | latency_ns = kzalloc(sizeof(*latency_ns) * dt_idle_states, GFP_KERNEL); |
188 | rc = of_property_read_u32_array(power_mgt, | ||
189 | "ibm,cpu-idle-state-latencies-ns", latency_ns, dt_idle_states); | ||
190 | if (rc) { | ||
191 | pr_warn("cpuidle-powernv: missing ibm,cpu-idle-state-latencies-ns in DT\n"); | ||
192 | goto out_free_latency; | ||
193 | } | ||
188 | 194 | ||
189 | for (i = 0; i < dt_idle_states; i++) { | 195 | residency_ns = kzalloc(sizeof(*residency_ns) * dt_idle_states, GFP_KERNEL); |
196 | rc = of_property_read_u32_array(power_mgt, | ||
197 | "ibm,cpu-idle-state-residency-ns", residency_ns, dt_idle_states); | ||
190 | 198 | ||
191 | flags = be32_to_cpu(idle_state_flags[i]); | 199 | for (i = 0; i < dt_idle_states; i++) { |
192 | 200 | ||
193 | /* Cpuidle accepts exit_latency in us and we estimate | 201 | /* |
194 | * target residency to be 10x exit_latency | 202 | * Cpuidle accepts exit_latency and target_residency in us. |
203 | * Use default target_residency values if f/w does not expose it. | ||
195 | */ | 204 | */ |
196 | latency_ns = be32_to_cpu(idle_state_latency[i]); | 205 | if (flags[i] & OPAL_PM_NAP_ENABLED) { |
197 | if (flags & OPAL_PM_NAP_ENABLED) { | ||
198 | /* Add NAP state */ | 206 | /* Add NAP state */ |
199 | strcpy(powernv_states[nr_idle_states].name, "Nap"); | 207 | strcpy(powernv_states[nr_idle_states].name, "Nap"); |
200 | strcpy(powernv_states[nr_idle_states].desc, "Nap"); | 208 | strcpy(powernv_states[nr_idle_states].desc, "Nap"); |
201 | powernv_states[nr_idle_states].flags = 0; | 209 | powernv_states[nr_idle_states].flags = 0; |
202 | powernv_states[nr_idle_states].exit_latency = | 210 | powernv_states[nr_idle_states].target_residency = 100; |
203 | ((unsigned int)latency_ns) / 1000; | ||
204 | powernv_states[nr_idle_states].target_residency = | ||
205 | ((unsigned int)latency_ns / 100); | ||
206 | powernv_states[nr_idle_states].enter = &nap_loop; | 211 | powernv_states[nr_idle_states].enter = &nap_loop; |
207 | nr_idle_states++; | 212 | } else if (flags[i] & OPAL_PM_SLEEP_ENABLED || |
208 | } | 213 | flags[i] & OPAL_PM_SLEEP_ENABLED_ER1) { |
209 | |||
210 | if (flags & OPAL_PM_SLEEP_ENABLED || | ||
211 | flags & OPAL_PM_SLEEP_ENABLED_ER1) { | ||
212 | /* Add FASTSLEEP state */ | 214 | /* Add FASTSLEEP state */ |
213 | strcpy(powernv_states[nr_idle_states].name, "FastSleep"); | 215 | strcpy(powernv_states[nr_idle_states].name, "FastSleep"); |
214 | strcpy(powernv_states[nr_idle_states].desc, "FastSleep"); | 216 | strcpy(powernv_states[nr_idle_states].desc, "FastSleep"); |
215 | powernv_states[nr_idle_states].flags = CPUIDLE_FLAG_TIMER_STOP; | 217 | powernv_states[nr_idle_states].flags = CPUIDLE_FLAG_TIMER_STOP; |
216 | powernv_states[nr_idle_states].exit_latency = | 218 | powernv_states[nr_idle_states].target_residency = 300000; |
217 | ((unsigned int)latency_ns) / 1000; | ||
218 | powernv_states[nr_idle_states].target_residency = | ||
219 | ((unsigned int)latency_ns / 100); | ||
220 | powernv_states[nr_idle_states].enter = &fastsleep_loop; | 219 | powernv_states[nr_idle_states].enter = &fastsleep_loop; |
221 | nr_idle_states++; | ||
222 | } | 220 | } |
221 | |||
222 | powernv_states[nr_idle_states].exit_latency = | ||
223 | ((unsigned int)latency_ns[i]) / 1000; | ||
224 | |||
225 | if (!rc) { | ||
226 | powernv_states[nr_idle_states].target_residency = | ||
227 | ((unsigned int)residency_ns[i]) / 1000; | ||
228 | } | ||
229 | |||
230 | nr_idle_states++; | ||
223 | } | 231 | } |
224 | 232 | ||
233 | kfree(residency_ns); | ||
234 | out_free_latency: | ||
235 | kfree(latency_ns); | ||
236 | out_free_flags: | ||
237 | kfree(flags); | ||
238 | out: | ||
225 | return nr_idle_states; | 239 | return nr_idle_states; |
226 | } | 240 | } |
227 | 241 | ||