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