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