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