xref: /dpdk/lib/power/power_cppc_cpufreq.c (revision 3fc2ddff)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2010-2021 Intel Corporation
3  * Copyright(c) 2021 Arm Limited
4  */
5 
6 #include <rte_memcpy.h>
7 #include <rte_memory.h>
8 
9 #include "power_cppc_cpufreq.h"
10 #include "power_common.h"
11 
12 /* macros used for rounding frequency to nearest 100000 */
13 #define FREQ_ROUNDING_DELTA 50000
14 #define ROUND_FREQ_TO_N_100000 100000
15 
16 /* the unit of highest_perf and nominal_perf differs on different arm platforms.
17  * For highest_perf, it maybe 300 or 3000000, both means 3.0GHz.
18  */
19 #define UNIT_DIFF 10000
20 
21 #define POWER_CONVERT_TO_DECIMAL 10
22 
23 #define POWER_GOVERNOR_USERSPACE "userspace"
24 #define POWER_SYSFILE_SETSPEED   \
25 		"/sys/devices/system/cpu/cpu%u/cpufreq/scaling_setspeed"
26 #define POWER_SYSFILE_SCALING_MAX_FREQ \
27 		"/sys/devices/system/cpu/cpu%u/cpufreq/scaling_max_freq"
28 #define POWER_SYSFILE_SCALING_MIN_FREQ  \
29 		"/sys/devices/system/cpu/cpu%u/cpufreq/scaling_min_freq"
30 #define POWER_SYSFILE_HIGHEST_PERF \
31 		"/sys/devices/system/cpu/cpu%u/acpi_cppc/highest_perf"
32 #define POWER_SYSFILE_NOMINAL_PERF \
33 		"/sys/devices/system/cpu/cpu%u/acpi_cppc/nominal_perf"
34 #define POWER_SYSFILE_SYS_MAX \
35 		"/sys/devices/system/cpu/cpu%u/cpufreq/cpuinfo_max_freq"
36 
37 #define POWER_CPPC_DRIVER "cppc-cpufreq"
38 #define BUS_FREQ     100000
39 
40 enum power_state {
41 	POWER_IDLE = 0,
42 	POWER_ONGOING,
43 	POWER_USED,
44 	POWER_UNKNOWN
45 };
46 
47 /**
48  * Power info per lcore.
49  */
50 struct cppc_power_info {
51 	unsigned int lcore_id;                   /**< Logical core id */
52 	uint32_t state;                      /**< Power in use state */
53 	FILE *f;                             /**< FD of scaling_setspeed */
54 	char governor_ori[32];               /**< Original governor name */
55 	uint32_t curr_idx;                   /**< Freq index in freqs array */
56 	uint32_t highest_perf;		     /**< system wide max freq */
57 	uint32_t nominal_perf;		     /**< system wide nominal freq */
58 	uint16_t turbo_available;            /**< Turbo Boost available */
59 	uint16_t turbo_enable;               /**< Turbo Boost enable/disable */
60 	uint32_t nb_freqs;                   /**< number of available freqs */
61 	uint32_t freqs[RTE_MAX_LCORE_FREQS]; /**< Frequency array */
62 } __rte_cache_aligned;
63 
64 static struct cppc_power_info lcore_power_info[RTE_MAX_LCORE];
65 
66 /**
67  * It is to set specific freq for specific logical core, according to the index
68  * of supported frequencies.
69  */
70 static int
71 set_freq_internal(struct cppc_power_info *pi, uint32_t idx)
72 {
73 	if (idx >= RTE_MAX_LCORE_FREQS || idx >= pi->nb_freqs) {
74 		RTE_LOG(ERR, POWER, "Invalid frequency index %u, which "
75 				"should be less than %u\n", idx, pi->nb_freqs);
76 		return -1;
77 	}
78 
79 	/* Check if it is the same as current */
80 	if (idx == pi->curr_idx)
81 		return 0;
82 
83 	POWER_DEBUG_TRACE("Frequency[%u] %u to be set for lcore %u\n",
84 			idx, pi->freqs[idx], pi->lcore_id);
85 	if (fseek(pi->f, 0, SEEK_SET) < 0) {
86 		RTE_LOG(ERR, POWER, "Fail to set file position indicator to 0 "
87 			"for setting frequency for lcore %u\n", pi->lcore_id);
88 		return -1;
89 	}
90 	if (fprintf(pi->f, "%u", pi->freqs[idx]) < 0) {
91 		RTE_LOG(ERR, POWER, "Fail to write new frequency for "
92 				"lcore %u\n", pi->lcore_id);
93 		return -1;
94 	}
95 	fflush(pi->f);
96 	pi->curr_idx = idx;
97 
98 	return 1;
99 }
100 
101 /**
102  * It is to check the current scaling governor by reading sys file, and then
103  * set it into 'userspace' if it is not by writing the sys file. The original
104  * governor will be saved for rolling back.
105  */
106 static int
107 power_set_governor_userspace(struct cppc_power_info *pi)
108 {
109 	return power_set_governor(pi->lcore_id, POWER_GOVERNOR_USERSPACE,
110 			pi->governor_ori, sizeof(pi->governor_ori));
111 }
112 
113 static int
114 power_check_turbo(struct cppc_power_info *pi)
115 {
116 	FILE *f_nom = NULL, *f_max = NULL, *f_cmax = NULL;
117 	int ret = -1;
118 	uint32_t nominal_perf = 0, highest_perf = 0, cpuinfo_max_freq = 0;
119 
120 	open_core_sysfs_file(&f_max, "r", POWER_SYSFILE_HIGHEST_PERF,
121 			pi->lcore_id);
122 	if (f_max == NULL) {
123 		RTE_LOG(ERR, POWER, "failed to open %s\n",
124 				POWER_SYSFILE_HIGHEST_PERF);
125 		goto err;
126 	}
127 
128 	open_core_sysfs_file(&f_nom, "r", POWER_SYSFILE_NOMINAL_PERF,
129 			pi->lcore_id);
130 	if (f_nom == NULL) {
131 		RTE_LOG(ERR, POWER, "failed to open %s\n",
132 				POWER_SYSFILE_NOMINAL_PERF);
133 		goto err;
134 	}
135 
136 	open_core_sysfs_file(&f_cmax, "r", POWER_SYSFILE_SYS_MAX,
137 			pi->lcore_id);
138 	if (f_cmax == NULL) {
139 		RTE_LOG(ERR, POWER, "failed to open %s\n",
140 				POWER_SYSFILE_SYS_MAX);
141 		goto err;
142 	}
143 
144 	ret = read_core_sysfs_u32(f_max, &highest_perf);
145 	if (ret < 0) {
146 		RTE_LOG(ERR, POWER, "Failed to read %s\n",
147 				POWER_SYSFILE_HIGHEST_PERF);
148 		goto err;
149 	}
150 
151 	ret = read_core_sysfs_u32(f_nom, &nominal_perf);
152 	if (ret < 0) {
153 		RTE_LOG(ERR, POWER, "Failed to read %s\n",
154 				POWER_SYSFILE_NOMINAL_PERF);
155 		goto err;
156 	}
157 
158 	ret = read_core_sysfs_u32(f_cmax, &cpuinfo_max_freq);
159 	if (ret < 0) {
160 		RTE_LOG(ERR, POWER, "Failed to read %s\n",
161 				POWER_SYSFILE_SYS_MAX);
162 		goto err;
163 	}
164 
165 	pi->highest_perf = highest_perf;
166 	pi->nominal_perf = nominal_perf;
167 
168 	if ((highest_perf > nominal_perf) && ((cpuinfo_max_freq == highest_perf)
169 			|| cpuinfo_max_freq == highest_perf * UNIT_DIFF)) {
170 		pi->turbo_available = 1;
171 		pi->turbo_enable = 1;
172 		ret = 0;
173 		POWER_DEBUG_TRACE("Lcore %u can do Turbo Boost! highest perf %u, "
174 				"nominal perf %u\n",
175 				pi->lcore_id, highest_perf, nominal_perf);
176 	} else {
177 		pi->turbo_available = 0;
178 		pi->turbo_enable = 0;
179 		POWER_DEBUG_TRACE("Lcore %u Turbo not available! highest perf %u, "
180 				"nominal perf %u\n",
181 				pi->lcore_id, highest_perf, nominal_perf);
182 	}
183 
184 err:
185 	if (f_max != NULL)
186 		fclose(f_max);
187 	if (f_nom != NULL)
188 		fclose(f_nom);
189 	if (f_cmax != NULL)
190 		fclose(f_cmax);
191 
192 	return ret;
193 }
194 
195 /**
196  * It is to get the available frequencies of the specific lcore by reading the
197  * sys file.
198  */
199 static int
200 power_get_available_freqs(struct cppc_power_info *pi)
201 {
202 	FILE *f_min = NULL, *f_max = NULL;
203 	int ret = -1;
204 	uint32_t scaling_min_freq = 0, scaling_max_freq = 0, nominal_perf = 0;
205 	uint32_t i, num_freqs = 0;
206 
207 	open_core_sysfs_file(&f_max, "r", POWER_SYSFILE_SCALING_MAX_FREQ,
208 			pi->lcore_id);
209 	if (f_max == NULL) {
210 		RTE_LOG(ERR, POWER, "failed to open %s\n",
211 				POWER_SYSFILE_SCALING_MAX_FREQ);
212 		goto out;
213 	}
214 
215 	open_core_sysfs_file(&f_min, "r", POWER_SYSFILE_SCALING_MIN_FREQ,
216 			pi->lcore_id);
217 	if (f_min == NULL) {
218 		RTE_LOG(ERR, POWER, "failed to open %s\n",
219 				POWER_SYSFILE_SCALING_MIN_FREQ);
220 		goto out;
221 	}
222 
223 	ret = read_core_sysfs_u32(f_max, &scaling_max_freq);
224 	if (ret < 0) {
225 		RTE_LOG(ERR, POWER, "Failed to read %s\n",
226 				POWER_SYSFILE_SCALING_MAX_FREQ);
227 		goto out;
228 	}
229 
230 	ret = read_core_sysfs_u32(f_min, &scaling_min_freq);
231 	if (ret < 0) {
232 		RTE_LOG(ERR, POWER, "Failed to read %s\n",
233 				POWER_SYSFILE_SCALING_MIN_FREQ);
234 		goto out;
235 	}
236 
237 	power_check_turbo(pi);
238 
239 	if (scaling_max_freq < scaling_min_freq)
240 		goto out;
241 
242 	/* If turbo is available then there is one extra freq bucket
243 	 * to store the sys max freq which value is scaling_max_freq
244 	 */
245 	nominal_perf = (pi->nominal_perf < UNIT_DIFF) ?
246 			pi->nominal_perf * UNIT_DIFF : pi->nominal_perf;
247 	num_freqs = (nominal_perf - scaling_min_freq) / BUS_FREQ + 1 +
248 		pi->turbo_available;
249 
250 	/* Generate the freq bucket array. */
251 	for (i = 0, pi->nb_freqs = 0; i < num_freqs; i++) {
252 		if ((i == 0) && pi->turbo_available)
253 			pi->freqs[pi->nb_freqs++] = scaling_max_freq;
254 		else
255 			pi->freqs[pi->nb_freqs++] =
256 			nominal_perf - (i - pi->turbo_available) * BUS_FREQ;
257 	}
258 
259 	ret = 0;
260 
261 	POWER_DEBUG_TRACE("%d frequency(s) of lcore %u are available\n",
262 			num_freqs, pi->lcore_id);
263 
264 out:
265 	if (f_min != NULL)
266 		fclose(f_min);
267 	if (f_max != NULL)
268 		fclose(f_max);
269 
270 	return ret;
271 }
272 
273 /**
274  * It is to fopen the sys file for the future setting the lcore frequency.
275  */
276 static int
277 power_init_for_setting_freq(struct cppc_power_info *pi)
278 {
279 	FILE *f = NULL;
280 	char buf[BUFSIZ];
281 	uint32_t i, freq;
282 	int ret;
283 
284 	open_core_sysfs_file(&f, "rw+", POWER_SYSFILE_SETSPEED, pi->lcore_id);
285 	if (f == NULL) {
286 		RTE_LOG(ERR, POWER, "failed to open %s\n",
287 				POWER_SYSFILE_SETSPEED);
288 		goto err;
289 	}
290 
291 	ret = read_core_sysfs_s(f, buf, sizeof(buf));
292 	if (ret < 0) {
293 		RTE_LOG(ERR, POWER, "Failed to read %s\n",
294 				POWER_SYSFILE_SETSPEED);
295 		goto err;
296 	}
297 
298 	freq = strtoul(buf, NULL, POWER_CONVERT_TO_DECIMAL);
299 
300 	/* convert the frequency to nearest 100000 value
301 	 * Ex: if freq=1396789 then freq_conv=1400000
302 	 * Ex: if freq=800030 then freq_conv=800000
303 	 */
304 	unsigned int freq_conv = 0;
305 	freq_conv = (freq + FREQ_ROUNDING_DELTA)
306 				/ ROUND_FREQ_TO_N_100000;
307 	freq_conv = freq_conv * ROUND_FREQ_TO_N_100000;
308 
309 	for (i = 0; i < pi->nb_freqs; i++) {
310 		if (freq_conv == pi->freqs[i]) {
311 			pi->curr_idx = i;
312 			pi->f = f;
313 			return 0;
314 		}
315 	}
316 
317 err:
318 	if (f != NULL)
319 		fclose(f);
320 
321 	return -1;
322 }
323 
324 int
325 power_cppc_cpufreq_check_supported(void)
326 {
327 	return cpufreq_check_scaling_driver(POWER_CPPC_DRIVER);
328 }
329 
330 int
331 power_cppc_cpufreq_init(unsigned int lcore_id)
332 {
333 	struct cppc_power_info *pi;
334 	uint32_t exp_state;
335 
336 	if (lcore_id >= RTE_MAX_LCORE) {
337 		RTE_LOG(ERR, POWER, "Lcore id %u can not exceeds %u\n",
338 				lcore_id, RTE_MAX_LCORE - 1U);
339 		return -1;
340 	}
341 
342 	pi = &lcore_power_info[lcore_id];
343 	exp_state = POWER_IDLE;
344 	/* The power in use state works as a guard variable between
345 	 * the CPU frequency control initialization and exit process.
346 	 * The ACQUIRE memory ordering here pairs with the RELEASE
347 	 * ordering below as lock to make sure the frequency operations
348 	 * in the critical section are done under the correct state.
349 	 */
350 	if (!__atomic_compare_exchange_n(&(pi->state), &exp_state,
351 					POWER_ONGOING, 0,
352 					__ATOMIC_ACQUIRE, __ATOMIC_RELAXED)) {
353 		RTE_LOG(INFO, POWER, "Power management of lcore %u is "
354 				"in use\n", lcore_id);
355 		return -1;
356 	}
357 
358 	pi->lcore_id = lcore_id;
359 	/* Check and set the governor */
360 	if (power_set_governor_userspace(pi) < 0) {
361 		RTE_LOG(ERR, POWER, "Cannot set governor of lcore %u to "
362 				"userspace\n", lcore_id);
363 		goto fail;
364 	}
365 
366 	/* Get the available frequencies */
367 	if (power_get_available_freqs(pi) < 0) {
368 		RTE_LOG(ERR, POWER, "Cannot get available frequencies of "
369 				"lcore %u\n", lcore_id);
370 		goto fail;
371 	}
372 
373 	/* Init for setting lcore frequency */
374 	if (power_init_for_setting_freq(pi) < 0) {
375 		RTE_LOG(ERR, POWER, "Cannot init for setting frequency for "
376 				"lcore %u\n", lcore_id);
377 		goto fail;
378 	}
379 
380 	/* Set freq to max by default */
381 	if (power_cppc_cpufreq_freq_max(lcore_id) < 0) {
382 		RTE_LOG(ERR, POWER, "Cannot set frequency of lcore %u "
383 				"to max\n", lcore_id);
384 		goto fail;
385 	}
386 
387 	RTE_LOG(INFO, POWER, "Initialized successfully for lcore %u "
388 			"power management\n", lcore_id);
389 
390 	__atomic_store_n(&(pi->state), POWER_USED, __ATOMIC_RELEASE);
391 
392 	return 0;
393 
394 fail:
395 	__atomic_store_n(&(pi->state), POWER_UNKNOWN, __ATOMIC_RELEASE);
396 	return -1;
397 }
398 
399 /**
400  * It is to check the governor and then set the original governor back if
401  * needed by writing the sys file.
402  */
403 static int
404 power_set_governor_original(struct cppc_power_info *pi)
405 {
406 	return power_set_governor(pi->lcore_id, pi->governor_ori, NULL, 0);
407 }
408 
409 int
410 power_cppc_cpufreq_exit(unsigned int lcore_id)
411 {
412 	struct cppc_power_info *pi;
413 	uint32_t exp_state;
414 
415 	if (lcore_id >= RTE_MAX_LCORE) {
416 		RTE_LOG(ERR, POWER, "Lcore id %u can not exceeds %u\n",
417 				lcore_id, RTE_MAX_LCORE - 1U);
418 		return -1;
419 	}
420 	pi = &lcore_power_info[lcore_id];
421 	exp_state = POWER_USED;
422 	/* The power in use state works as a guard variable between
423 	 * the CPU frequency control initialization and exit process.
424 	 * The ACQUIRE memory ordering here pairs with the RELEASE
425 	 * ordering below as lock to make sure the frequency operations
426 	 * in the critical section are done under the correct state.
427 	 */
428 	if (!__atomic_compare_exchange_n(&(pi->state), &exp_state,
429 					POWER_ONGOING, 0,
430 					__ATOMIC_ACQUIRE, __ATOMIC_RELAXED)) {
431 		RTE_LOG(INFO, POWER, "Power management of lcore %u is "
432 				"not used\n", lcore_id);
433 		return -1;
434 	}
435 
436 	/* Close FD of setting freq */
437 	fclose(pi->f);
438 	pi->f = NULL;
439 
440 	/* Set the governor back to the original */
441 	if (power_set_governor_original(pi) < 0) {
442 		RTE_LOG(ERR, POWER, "Cannot set the governor of %u back "
443 				"to the original\n", lcore_id);
444 		goto fail;
445 	}
446 
447 	RTE_LOG(INFO, POWER, "Power management of lcore %u has exited from "
448 			"'userspace' mode and been set back to the "
449 			"original\n", lcore_id);
450 	__atomic_store_n(&(pi->state), POWER_IDLE, __ATOMIC_RELEASE);
451 
452 	return 0;
453 
454 fail:
455 	__atomic_store_n(&(pi->state), POWER_UNKNOWN, __ATOMIC_RELEASE);
456 
457 	return -1;
458 }
459 
460 uint32_t
461 power_cppc_cpufreq_freqs(unsigned int lcore_id, uint32_t *freqs, uint32_t num)
462 {
463 	struct cppc_power_info *pi;
464 
465 	if (lcore_id >= RTE_MAX_LCORE) {
466 		RTE_LOG(ERR, POWER, "Invalid lcore ID\n");
467 		return 0;
468 	}
469 
470 	if (freqs == NULL) {
471 		RTE_LOG(ERR, POWER, "NULL buffer supplied\n");
472 		return 0;
473 	}
474 
475 	pi = &lcore_power_info[lcore_id];
476 	if (num < pi->nb_freqs) {
477 		RTE_LOG(ERR, POWER, "Buffer size is not enough\n");
478 		return 0;
479 	}
480 	rte_memcpy(freqs, pi->freqs, pi->nb_freqs * sizeof(uint32_t));
481 
482 	return pi->nb_freqs;
483 }
484 
485 uint32_t
486 power_cppc_cpufreq_get_freq(unsigned int lcore_id)
487 {
488 	if (lcore_id >= RTE_MAX_LCORE) {
489 		RTE_LOG(ERR, POWER, "Invalid lcore ID\n");
490 		return RTE_POWER_INVALID_FREQ_INDEX;
491 	}
492 
493 	return lcore_power_info[lcore_id].curr_idx;
494 }
495 
496 int
497 power_cppc_cpufreq_set_freq(unsigned int lcore_id, uint32_t index)
498 {
499 	if (lcore_id >= RTE_MAX_LCORE) {
500 		RTE_LOG(ERR, POWER, "Invalid lcore ID\n");
501 		return -1;
502 	}
503 
504 	return set_freq_internal(&(lcore_power_info[lcore_id]), index);
505 }
506 
507 int
508 power_cppc_cpufreq_freq_down(unsigned int lcore_id)
509 {
510 	struct cppc_power_info *pi;
511 
512 	if (lcore_id >= RTE_MAX_LCORE) {
513 		RTE_LOG(ERR, POWER, "Invalid lcore ID\n");
514 		return -1;
515 	}
516 
517 	pi = &lcore_power_info[lcore_id];
518 	if (pi->curr_idx + 1 == pi->nb_freqs)
519 		return 0;
520 
521 	/* Frequencies in the array are from high to low. */
522 	return set_freq_internal(pi, pi->curr_idx + 1);
523 }
524 
525 int
526 power_cppc_cpufreq_freq_up(unsigned int lcore_id)
527 {
528 	struct cppc_power_info *pi;
529 
530 	if (lcore_id >= RTE_MAX_LCORE) {
531 		RTE_LOG(ERR, POWER, "Invalid lcore ID\n");
532 		return -1;
533 	}
534 
535 	pi = &lcore_power_info[lcore_id];
536 	if (pi->curr_idx == 0 || (pi->curr_idx == 1 &&
537 		pi->turbo_available && !pi->turbo_enable))
538 		return 0;
539 
540 	/* Frequencies in the array are from high to low. */
541 	return set_freq_internal(pi, pi->curr_idx - 1);
542 }
543 
544 int
545 power_cppc_cpufreq_freq_max(unsigned int lcore_id)
546 {
547 	if (lcore_id >= RTE_MAX_LCORE) {
548 		RTE_LOG(ERR, POWER, "Invalid lcore ID\n");
549 		return -1;
550 	}
551 
552 	/* Frequencies in the array are from high to low. */
553 	if (lcore_power_info[lcore_id].turbo_available) {
554 		if (lcore_power_info[lcore_id].turbo_enable)
555 			/* Set to Turbo */
556 			return set_freq_internal(
557 				&lcore_power_info[lcore_id], 0);
558 		else
559 			/* Set to max non-turbo */
560 			return set_freq_internal(
561 				&lcore_power_info[lcore_id], 1);
562 	} else
563 		return set_freq_internal(&lcore_power_info[lcore_id], 0);
564 }
565 
566 int
567 power_cppc_cpufreq_freq_min(unsigned int lcore_id)
568 {
569 	struct cppc_power_info *pi;
570 
571 	if (lcore_id >= RTE_MAX_LCORE) {
572 		RTE_LOG(ERR, POWER, "Invalid lcore ID\n");
573 		return -1;
574 	}
575 
576 	pi = &lcore_power_info[lcore_id];
577 
578 	/* Frequencies in the array are from high to low. */
579 	return set_freq_internal(pi, pi->nb_freqs - 1);
580 }
581 
582 int
583 power_cppc_turbo_status(unsigned int lcore_id)
584 {
585 	struct cppc_power_info *pi;
586 
587 	if (lcore_id >= RTE_MAX_LCORE) {
588 		RTE_LOG(ERR, POWER, "Invalid lcore ID\n");
589 		return -1;
590 	}
591 
592 	pi = &lcore_power_info[lcore_id];
593 
594 	return pi->turbo_enable;
595 }
596 
597 int
598 power_cppc_enable_turbo(unsigned int lcore_id)
599 {
600 	struct cppc_power_info *pi;
601 
602 	if (lcore_id >= RTE_MAX_LCORE) {
603 		RTE_LOG(ERR, POWER, "Invalid lcore ID\n");
604 		return -1;
605 	}
606 
607 	pi = &lcore_power_info[lcore_id];
608 
609 	if (pi->turbo_available)
610 		pi->turbo_enable = 1;
611 	else {
612 		pi->turbo_enable = 0;
613 		RTE_LOG(ERR, POWER,
614 			"Failed to enable turbo on lcore %u\n",
615 			lcore_id);
616 		return -1;
617 	}
618 
619 	/* TODO: must set to max once enbling Turbo? Considering add condition:
620 	 * if ((pi->turbo_available) && (pi->curr_idx <= 1))
621 	 */
622 	/* Max may have changed, so call to max function */
623 	if (power_cppc_cpufreq_freq_max(lcore_id) < 0) {
624 		RTE_LOG(ERR, POWER,
625 			"Failed to set frequency of lcore %u to max\n",
626 			lcore_id);
627 		return -1;
628 	}
629 
630 	return 0;
631 }
632 
633 int
634 power_cppc_disable_turbo(unsigned int lcore_id)
635 {
636 	struct cppc_power_info *pi;
637 
638 	if (lcore_id >= RTE_MAX_LCORE) {
639 		RTE_LOG(ERR, POWER, "Invalid lcore ID\n");
640 		return -1;
641 	}
642 
643 	pi = &lcore_power_info[lcore_id];
644 
645 	pi->turbo_enable = 0;
646 
647 	if ((pi->turbo_available) && (pi->curr_idx <= 1)) {
648 		/* Try to set freq to max by default coming out of turbo */
649 		if (power_cppc_cpufreq_freq_max(lcore_id) < 0) {
650 			RTE_LOG(ERR, POWER,
651 				"Failed to set frequency of lcore %u to max\n",
652 				lcore_id);
653 			return -1;
654 		}
655 	}
656 
657 	return 0;
658 }
659 
660 int
661 power_cppc_get_capabilities(unsigned int lcore_id,
662 		struct rte_power_core_capabilities *caps)
663 {
664 	struct cppc_power_info *pi;
665 
666 	if (lcore_id >= RTE_MAX_LCORE) {
667 		RTE_LOG(ERR, POWER, "Invalid lcore ID\n");
668 		return -1;
669 	}
670 	if (caps == NULL) {
671 		RTE_LOG(ERR, POWER, "Invalid argument\n");
672 		return -1;
673 	}
674 
675 	pi = &lcore_power_info[lcore_id];
676 	caps->capabilities = 0;
677 	caps->turbo = !!(pi->turbo_available);
678 
679 	return 0;
680 }
681