xref: /dpdk/lib/power/power_cppc_cpufreq.c (revision 33e71acf)
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 	if (num_freqs >= RTE_MAX_LCORE_FREQS) {
250 		RTE_LOG(ERR, POWER, "Too many available frequencies: %d\n",
251 				num_freqs);
252 		goto out;
253 	}
254 
255 	/* Generate the freq bucket array. */
256 	for (i = 0, pi->nb_freqs = 0; i < num_freqs; i++) {
257 		if ((i == 0) && pi->turbo_available)
258 			pi->freqs[pi->nb_freqs++] = scaling_max_freq;
259 		else
260 			pi->freqs[pi->nb_freqs++] =
261 			nominal_perf - (i - pi->turbo_available) * BUS_FREQ;
262 	}
263 
264 	ret = 0;
265 
266 	POWER_DEBUG_TRACE("%d frequency(s) of lcore %u are available\n",
267 			num_freqs, pi->lcore_id);
268 
269 out:
270 	if (f_min != NULL)
271 		fclose(f_min);
272 	if (f_max != NULL)
273 		fclose(f_max);
274 
275 	return ret;
276 }
277 
278 /**
279  * It is to fopen the sys file for the future setting the lcore frequency.
280  */
281 static int
282 power_init_for_setting_freq(struct cppc_power_info *pi)
283 {
284 	FILE *f = NULL;
285 	char buf[BUFSIZ];
286 	uint32_t i, freq;
287 	int ret;
288 
289 	open_core_sysfs_file(&f, "rw+", POWER_SYSFILE_SETSPEED, pi->lcore_id);
290 	if (f == NULL) {
291 		RTE_LOG(ERR, POWER, "failed to open %s\n",
292 				POWER_SYSFILE_SETSPEED);
293 		goto err;
294 	}
295 
296 	ret = read_core_sysfs_s(f, buf, sizeof(buf));
297 	if (ret < 0) {
298 		RTE_LOG(ERR, POWER, "Failed to read %s\n",
299 				POWER_SYSFILE_SETSPEED);
300 		goto err;
301 	}
302 
303 	freq = strtoul(buf, NULL, POWER_CONVERT_TO_DECIMAL);
304 
305 	/* convert the frequency to nearest 100000 value
306 	 * Ex: if freq=1396789 then freq_conv=1400000
307 	 * Ex: if freq=800030 then freq_conv=800000
308 	 */
309 	unsigned int freq_conv = 0;
310 	freq_conv = (freq + FREQ_ROUNDING_DELTA)
311 				/ ROUND_FREQ_TO_N_100000;
312 	freq_conv = freq_conv * ROUND_FREQ_TO_N_100000;
313 
314 	for (i = 0; i < pi->nb_freqs; i++) {
315 		if (freq_conv == pi->freqs[i]) {
316 			pi->curr_idx = i;
317 			pi->f = f;
318 			return 0;
319 		}
320 	}
321 
322 err:
323 	if (f != NULL)
324 		fclose(f);
325 
326 	return -1;
327 }
328 
329 int
330 power_cppc_cpufreq_check_supported(void)
331 {
332 	return cpufreq_check_scaling_driver(POWER_CPPC_DRIVER);
333 }
334 
335 int
336 power_cppc_cpufreq_init(unsigned int lcore_id)
337 {
338 	struct cppc_power_info *pi;
339 	uint32_t exp_state;
340 
341 	if (lcore_id >= RTE_MAX_LCORE) {
342 		RTE_LOG(ERR, POWER, "Lcore id %u can not exceeds %u\n",
343 				lcore_id, RTE_MAX_LCORE - 1U);
344 		return -1;
345 	}
346 
347 	pi = &lcore_power_info[lcore_id];
348 	exp_state = POWER_IDLE;
349 	/* The power in use state works as a guard variable between
350 	 * the CPU frequency control initialization and exit process.
351 	 * The ACQUIRE memory ordering here pairs with the RELEASE
352 	 * ordering below as lock to make sure the frequency operations
353 	 * in the critical section are done under the correct state.
354 	 */
355 	if (!__atomic_compare_exchange_n(&(pi->state), &exp_state,
356 					POWER_ONGOING, 0,
357 					__ATOMIC_ACQUIRE, __ATOMIC_RELAXED)) {
358 		RTE_LOG(INFO, POWER, "Power management of lcore %u is "
359 				"in use\n", lcore_id);
360 		return -1;
361 	}
362 
363 	pi->lcore_id = lcore_id;
364 	/* Check and set the governor */
365 	if (power_set_governor_userspace(pi) < 0) {
366 		RTE_LOG(ERR, POWER, "Cannot set governor of lcore %u to "
367 				"userspace\n", lcore_id);
368 		goto fail;
369 	}
370 
371 	/* Get the available frequencies */
372 	if (power_get_available_freqs(pi) < 0) {
373 		RTE_LOG(ERR, POWER, "Cannot get available frequencies of "
374 				"lcore %u\n", lcore_id);
375 		goto fail;
376 	}
377 
378 	/* Init for setting lcore frequency */
379 	if (power_init_for_setting_freq(pi) < 0) {
380 		RTE_LOG(ERR, POWER, "Cannot init for setting frequency for "
381 				"lcore %u\n", lcore_id);
382 		goto fail;
383 	}
384 
385 	/* Set freq to max by default */
386 	if (power_cppc_cpufreq_freq_max(lcore_id) < 0) {
387 		RTE_LOG(ERR, POWER, "Cannot set frequency of lcore %u "
388 				"to max\n", lcore_id);
389 		goto fail;
390 	}
391 
392 	RTE_LOG(INFO, POWER, "Initialized successfully for lcore %u "
393 			"power management\n", lcore_id);
394 
395 	__atomic_store_n(&(pi->state), POWER_USED, __ATOMIC_RELEASE);
396 
397 	return 0;
398 
399 fail:
400 	__atomic_store_n(&(pi->state), POWER_UNKNOWN, __ATOMIC_RELEASE);
401 	return -1;
402 }
403 
404 /**
405  * It is to check the governor and then set the original governor back if
406  * needed by writing the sys file.
407  */
408 static int
409 power_set_governor_original(struct cppc_power_info *pi)
410 {
411 	return power_set_governor(pi->lcore_id, pi->governor_ori, NULL, 0);
412 }
413 
414 int
415 power_cppc_cpufreq_exit(unsigned int lcore_id)
416 {
417 	struct cppc_power_info *pi;
418 	uint32_t exp_state;
419 
420 	if (lcore_id >= RTE_MAX_LCORE) {
421 		RTE_LOG(ERR, POWER, "Lcore id %u can not exceeds %u\n",
422 				lcore_id, RTE_MAX_LCORE - 1U);
423 		return -1;
424 	}
425 	pi = &lcore_power_info[lcore_id];
426 	exp_state = POWER_USED;
427 	/* The power in use state works as a guard variable between
428 	 * the CPU frequency control initialization and exit process.
429 	 * The ACQUIRE memory ordering here pairs with the RELEASE
430 	 * ordering below as lock to make sure the frequency operations
431 	 * in the critical section are done under the correct state.
432 	 */
433 	if (!__atomic_compare_exchange_n(&(pi->state), &exp_state,
434 					POWER_ONGOING, 0,
435 					__ATOMIC_ACQUIRE, __ATOMIC_RELAXED)) {
436 		RTE_LOG(INFO, POWER, "Power management of lcore %u is "
437 				"not used\n", lcore_id);
438 		return -1;
439 	}
440 
441 	/* Close FD of setting freq */
442 	fclose(pi->f);
443 	pi->f = NULL;
444 
445 	/* Set the governor back to the original */
446 	if (power_set_governor_original(pi) < 0) {
447 		RTE_LOG(ERR, POWER, "Cannot set the governor of %u back "
448 				"to the original\n", lcore_id);
449 		goto fail;
450 	}
451 
452 	RTE_LOG(INFO, POWER, "Power management of lcore %u has exited from "
453 			"'userspace' mode and been set back to the "
454 			"original\n", lcore_id);
455 	__atomic_store_n(&(pi->state), POWER_IDLE, __ATOMIC_RELEASE);
456 
457 	return 0;
458 
459 fail:
460 	__atomic_store_n(&(pi->state), POWER_UNKNOWN, __ATOMIC_RELEASE);
461 
462 	return -1;
463 }
464 
465 uint32_t
466 power_cppc_cpufreq_freqs(unsigned int lcore_id, uint32_t *freqs, uint32_t num)
467 {
468 	struct cppc_power_info *pi;
469 
470 	if (lcore_id >= RTE_MAX_LCORE) {
471 		RTE_LOG(ERR, POWER, "Invalid lcore ID\n");
472 		return 0;
473 	}
474 
475 	if (freqs == NULL) {
476 		RTE_LOG(ERR, POWER, "NULL buffer supplied\n");
477 		return 0;
478 	}
479 
480 	pi = &lcore_power_info[lcore_id];
481 	if (num < pi->nb_freqs) {
482 		RTE_LOG(ERR, POWER, "Buffer size is not enough\n");
483 		return 0;
484 	}
485 	rte_memcpy(freqs, pi->freqs, pi->nb_freqs * sizeof(uint32_t));
486 
487 	return pi->nb_freqs;
488 }
489 
490 uint32_t
491 power_cppc_cpufreq_get_freq(unsigned int lcore_id)
492 {
493 	if (lcore_id >= RTE_MAX_LCORE) {
494 		RTE_LOG(ERR, POWER, "Invalid lcore ID\n");
495 		return RTE_POWER_INVALID_FREQ_INDEX;
496 	}
497 
498 	return lcore_power_info[lcore_id].curr_idx;
499 }
500 
501 int
502 power_cppc_cpufreq_set_freq(unsigned int lcore_id, uint32_t index)
503 {
504 	if (lcore_id >= RTE_MAX_LCORE) {
505 		RTE_LOG(ERR, POWER, "Invalid lcore ID\n");
506 		return -1;
507 	}
508 
509 	return set_freq_internal(&(lcore_power_info[lcore_id]), index);
510 }
511 
512 int
513 power_cppc_cpufreq_freq_down(unsigned int lcore_id)
514 {
515 	struct cppc_power_info *pi;
516 
517 	if (lcore_id >= RTE_MAX_LCORE) {
518 		RTE_LOG(ERR, POWER, "Invalid lcore ID\n");
519 		return -1;
520 	}
521 
522 	pi = &lcore_power_info[lcore_id];
523 	if (pi->curr_idx + 1 == pi->nb_freqs)
524 		return 0;
525 
526 	/* Frequencies in the array are from high to low. */
527 	return set_freq_internal(pi, pi->curr_idx + 1);
528 }
529 
530 int
531 power_cppc_cpufreq_freq_up(unsigned int lcore_id)
532 {
533 	struct cppc_power_info *pi;
534 
535 	if (lcore_id >= RTE_MAX_LCORE) {
536 		RTE_LOG(ERR, POWER, "Invalid lcore ID\n");
537 		return -1;
538 	}
539 
540 	pi = &lcore_power_info[lcore_id];
541 	if (pi->curr_idx == 0 || (pi->curr_idx == 1 &&
542 		pi->turbo_available && !pi->turbo_enable))
543 		return 0;
544 
545 	/* Frequencies in the array are from high to low. */
546 	return set_freq_internal(pi, pi->curr_idx - 1);
547 }
548 
549 int
550 power_cppc_cpufreq_freq_max(unsigned int lcore_id)
551 {
552 	if (lcore_id >= RTE_MAX_LCORE) {
553 		RTE_LOG(ERR, POWER, "Invalid lcore ID\n");
554 		return -1;
555 	}
556 
557 	/* Frequencies in the array are from high to low. */
558 	if (lcore_power_info[lcore_id].turbo_available) {
559 		if (lcore_power_info[lcore_id].turbo_enable)
560 			/* Set to Turbo */
561 			return set_freq_internal(
562 				&lcore_power_info[lcore_id], 0);
563 		else
564 			/* Set to max non-turbo */
565 			return set_freq_internal(
566 				&lcore_power_info[lcore_id], 1);
567 	} else
568 		return set_freq_internal(&lcore_power_info[lcore_id], 0);
569 }
570 
571 int
572 power_cppc_cpufreq_freq_min(unsigned int lcore_id)
573 {
574 	struct cppc_power_info *pi;
575 
576 	if (lcore_id >= RTE_MAX_LCORE) {
577 		RTE_LOG(ERR, POWER, "Invalid lcore ID\n");
578 		return -1;
579 	}
580 
581 	pi = &lcore_power_info[lcore_id];
582 
583 	/* Frequencies in the array are from high to low. */
584 	return set_freq_internal(pi, pi->nb_freqs - 1);
585 }
586 
587 int
588 power_cppc_turbo_status(unsigned int lcore_id)
589 {
590 	struct cppc_power_info *pi;
591 
592 	if (lcore_id >= RTE_MAX_LCORE) {
593 		RTE_LOG(ERR, POWER, "Invalid lcore ID\n");
594 		return -1;
595 	}
596 
597 	pi = &lcore_power_info[lcore_id];
598 
599 	return pi->turbo_enable;
600 }
601 
602 int
603 power_cppc_enable_turbo(unsigned int lcore_id)
604 {
605 	struct cppc_power_info *pi;
606 
607 	if (lcore_id >= RTE_MAX_LCORE) {
608 		RTE_LOG(ERR, POWER, "Invalid lcore ID\n");
609 		return -1;
610 	}
611 
612 	pi = &lcore_power_info[lcore_id];
613 
614 	if (pi->turbo_available)
615 		pi->turbo_enable = 1;
616 	else {
617 		pi->turbo_enable = 0;
618 		RTE_LOG(ERR, POWER,
619 			"Failed to enable turbo on lcore %u\n",
620 			lcore_id);
621 		return -1;
622 	}
623 
624 	/* TODO: must set to max once enabling Turbo? Considering add condition:
625 	 * if ((pi->turbo_available) && (pi->curr_idx <= 1))
626 	 */
627 	/* Max may have changed, so call to max function */
628 	if (power_cppc_cpufreq_freq_max(lcore_id) < 0) {
629 		RTE_LOG(ERR, POWER,
630 			"Failed to set frequency of lcore %u to max\n",
631 			lcore_id);
632 		return -1;
633 	}
634 
635 	return 0;
636 }
637 
638 int
639 power_cppc_disable_turbo(unsigned int lcore_id)
640 {
641 	struct cppc_power_info *pi;
642 
643 	if (lcore_id >= RTE_MAX_LCORE) {
644 		RTE_LOG(ERR, POWER, "Invalid lcore ID\n");
645 		return -1;
646 	}
647 
648 	pi = &lcore_power_info[lcore_id];
649 
650 	pi->turbo_enable = 0;
651 
652 	if ((pi->turbo_available) && (pi->curr_idx <= 1)) {
653 		/* Try to set freq to max by default coming out of turbo */
654 		if (power_cppc_cpufreq_freq_max(lcore_id) < 0) {
655 			RTE_LOG(ERR, POWER,
656 				"Failed to set frequency of lcore %u to max\n",
657 				lcore_id);
658 			return -1;
659 		}
660 	}
661 
662 	return 0;
663 }
664 
665 int
666 power_cppc_get_capabilities(unsigned int lcore_id,
667 		struct rte_power_core_capabilities *caps)
668 {
669 	struct cppc_power_info *pi;
670 
671 	if (lcore_id >= RTE_MAX_LCORE) {
672 		RTE_LOG(ERR, POWER, "Invalid lcore ID\n");
673 		return -1;
674 	}
675 	if (caps == NULL) {
676 		RTE_LOG(ERR, POWER, "Invalid argument\n");
677 		return -1;
678 	}
679 
680 	pi = &lcore_power_info[lcore_id];
681 	caps->capabilities = 0;
682 	caps->turbo = !!(pi->turbo_available);
683 
684 	return 0;
685 }
686