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