Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright(c) 2010-2014 Intel Corporation
3 : : */
4 : :
5 : : #include <stdio.h>
6 : : #include <fcntl.h>
7 : : #include <stdlib.h>
8 : :
9 : : #include <rte_memcpy.h>
10 : : #include <rte_stdatomic.h>
11 : : #include <rte_string_fns.h>
12 : :
13 : : #include "power_acpi_cpufreq.h"
14 : : #include "power_common.h"
15 : :
16 : : #define STR_SIZE 1024
17 : : #define POWER_CONVERT_TO_DECIMAL 10
18 : :
19 : : #define POWER_GOVERNOR_USERSPACE "userspace"
20 : : #define POWER_SYSFILE_AVAIL_FREQ \
21 : : "/sys/devices/system/cpu/cpu%u/cpufreq/scaling_available_frequencies"
22 : : #define POWER_SYSFILE_SETSPEED \
23 : : "/sys/devices/system/cpu/cpu%u/cpufreq/scaling_setspeed"
24 : : #define POWER_ACPI_DRIVER "acpi-cpufreq"
25 : :
26 : : /*
27 : : * MSR related
28 : : */
29 : : #define PLATFORM_INFO 0x0CE
30 : : #define TURBO_RATIO_LIMIT 0x1AD
31 : : #define IA32_PERF_CTL 0x199
32 : : #define CORE_TURBO_DISABLE_BIT ((uint64_t)1<<32)
33 : :
34 : : enum power_state {
35 : : POWER_IDLE = 0,
36 : : POWER_ONGOING,
37 : : POWER_USED,
38 : : POWER_UNKNOWN
39 : : };
40 : :
41 : : /**
42 : : * Power info per lcore.
43 : : */
44 : : struct __rte_cache_aligned acpi_power_info {
45 : : unsigned int lcore_id; /**< Logical core id */
46 : : uint32_t freqs[RTE_MAX_LCORE_FREQS]; /**< Frequency array */
47 : : uint32_t nb_freqs; /**< number of available freqs */
48 : : FILE *f; /**< FD of scaling_setspeed */
49 : : char governor_ori[32]; /**< Original governor name */
50 : : uint32_t curr_idx; /**< Freq index in freqs array */
51 : : RTE_ATOMIC(uint32_t) state; /**< Power in use state */
52 : : uint16_t turbo_available; /**< Turbo Boost available */
53 : : uint16_t turbo_enable; /**< Turbo Boost enable/disable */
54 : : };
55 : :
56 : : static struct acpi_power_info lcore_power_info[RTE_MAX_LCORE];
57 : :
58 : : /**
59 : : * It is to set specific freq for specific logical core, according to the index
60 : : * of supported frequencies.
61 : : */
62 : : static int
63 : 0 : set_freq_internal(struct acpi_power_info *pi, uint32_t idx)
64 : : {
65 [ # # # # ]: 0 : if (idx >= RTE_MAX_LCORE_FREQS || idx >= pi->nb_freqs) {
66 : 0 : POWER_LOG(ERR, "Invalid frequency index %u, which "
67 : : "should be less than %u", idx, pi->nb_freqs);
68 : 0 : return -1;
69 : : }
70 : :
71 : : /* Check if it is the same as current */
72 [ # # ]: 0 : if (idx == pi->curr_idx)
73 : : return 0;
74 : :
75 : : POWER_DEBUG_LOG("Frequency[%u] %u to be set for lcore %u",
76 : : idx, pi->freqs[idx], pi->lcore_id);
77 [ # # ]: 0 : if (fseek(pi->f, 0, SEEK_SET) < 0) {
78 : 0 : POWER_LOG(ERR, "Fail to set file position indicator to 0 "
79 : : "for setting frequency for lcore %u", pi->lcore_id);
80 : 0 : return -1;
81 : : }
82 [ # # ]: 0 : if (fprintf(pi->f, "%u", pi->freqs[idx]) < 0) {
83 : 0 : POWER_LOG(ERR, "Fail to write new frequency for "
84 : : "lcore %u", pi->lcore_id);
85 : 0 : return -1;
86 : : }
87 : 0 : fflush(pi->f);
88 : 0 : pi->curr_idx = idx;
89 : :
90 : 0 : return 1;
91 : : }
92 : :
93 : : /**
94 : : * It is to check the current scaling governor by reading sys file, and then
95 : : * set it into 'userspace' if it is not by writing the sys file. The original
96 : : * governor will be saved for rolling back.
97 : : */
98 : : static int
99 : : power_set_governor_userspace(struct acpi_power_info *pi)
100 : : {
101 : 1 : return power_set_governor(pi->lcore_id, POWER_GOVERNOR_USERSPACE,
102 : 1 : pi->governor_ori, sizeof(pi->governor_ori));
103 : : }
104 : :
105 : : /**
106 : : * It is to check the governor and then set the original governor back if
107 : : * needed by writing the sys file.
108 : : */
109 : : static int
110 : : power_set_governor_original(struct acpi_power_info *pi)
111 : : {
112 : 0 : return power_set_governor(pi->lcore_id, pi->governor_ori, NULL, 0);
113 : : }
114 : :
115 : : /**
116 : : * It is to get the available frequencies of the specific lcore by reading the
117 : : * sys file.
118 : : */
119 : : static int
120 : 0 : power_get_available_freqs(struct acpi_power_info *pi)
121 : : {
122 : : FILE *f;
123 : : int ret = -1, i, count;
124 : : char *p;
125 : : char buf[BUFSIZ];
126 : : char *freqs[RTE_MAX_LCORE_FREQS];
127 : :
128 : 0 : open_core_sysfs_file(&f, "r", POWER_SYSFILE_AVAIL_FREQ, pi->lcore_id);
129 [ # # ]: 0 : if (f == NULL) {
130 : 0 : POWER_LOG(ERR, "failed to open %s",
131 : : POWER_SYSFILE_AVAIL_FREQ);
132 : 0 : goto out;
133 : : }
134 : :
135 : 0 : ret = read_core_sysfs_s(f, buf, sizeof(buf));
136 [ # # ]: 0 : if ((ret) < 0) {
137 : 0 : POWER_LOG(ERR, "Failed to read %s",
138 : : POWER_SYSFILE_AVAIL_FREQ);
139 : 0 : goto out;
140 : : }
141 : :
142 : : /* Split string into at most RTE_MAX_LCORE_FREQS frequencies */
143 : 0 : count = rte_strsplit(buf, sizeof(buf), freqs,
144 : : RTE_MAX_LCORE_FREQS, ' ');
145 [ # # ]: 0 : if (count <= 0) {
146 : 0 : POWER_LOG(ERR, "No available frequency in "
147 : : POWER_SYSFILE_AVAIL_FREQ, pi->lcore_id);
148 : 0 : goto out;
149 : : }
150 [ # # ]: 0 : if (count >= RTE_MAX_LCORE_FREQS) {
151 : 0 : POWER_LOG(ERR, "Too many available frequencies : %d",
152 : : count);
153 : 0 : goto out;
154 : : }
155 : :
156 : : /* Store the available frequencies into power context */
157 [ # # ]: 0 : for (i = 0, pi->nb_freqs = 0; i < count; i++) {
158 : : POWER_DEBUG_LOG("Lcore %u frequency[%d]: %s", pi->lcore_id,
159 : : i, freqs[i]);
160 : 0 : pi->freqs[pi->nb_freqs++] = strtoul(freqs[i], &p,
161 : : POWER_CONVERT_TO_DECIMAL);
162 : : }
163 : :
164 [ # # ]: 0 : if ((pi->freqs[0]-1000) == pi->freqs[1]) {
165 : 0 : pi->turbo_available = 1;
166 : 0 : pi->turbo_enable = 1;
167 : : POWER_DEBUG_LOG("Lcore %u Can do Turbo Boost",
168 : : pi->lcore_id);
169 : : } else {
170 : 0 : pi->turbo_available = 0;
171 : 0 : pi->turbo_enable = 0;
172 : : POWER_DEBUG_LOG("Turbo Boost not available on Lcore %u",
173 : : pi->lcore_id);
174 : : }
175 : :
176 : : ret = 0;
177 : : POWER_DEBUG_LOG("%d frequency(s) of lcore %u are available",
178 : : count, pi->lcore_id);
179 : 0 : out:
180 [ # # ]: 0 : if (f != NULL)
181 : 0 : fclose(f);
182 : :
183 : 0 : return ret;
184 : : }
185 : :
186 : : /**
187 : : * It is to fopen the sys file for the future setting the lcore frequency.
188 : : */
189 : : static int
190 : 0 : power_init_for_setting_freq(struct acpi_power_info *pi)
191 : : {
192 : : FILE *f;
193 : : char buf[BUFSIZ];
194 : : uint32_t i, freq;
195 : : int ret;
196 : :
197 : 0 : open_core_sysfs_file(&f, "rw+", POWER_SYSFILE_SETSPEED, pi->lcore_id);
198 [ # # ]: 0 : if (f == NULL) {
199 : 0 : POWER_LOG(ERR, "Failed to open %s",
200 : : POWER_SYSFILE_SETSPEED);
201 : 0 : goto err;
202 : : }
203 : :
204 : 0 : ret = read_core_sysfs_s(f, buf, sizeof(buf));
205 [ # # ]: 0 : if ((ret) < 0) {
206 : 0 : POWER_LOG(ERR, "Failed to read %s",
207 : : POWER_SYSFILE_SETSPEED);
208 : 0 : goto err;
209 : : }
210 : :
211 : 0 : freq = strtoul(buf, NULL, POWER_CONVERT_TO_DECIMAL);
212 [ # # ]: 0 : for (i = 0; i < pi->nb_freqs; i++) {
213 [ # # ]: 0 : if (freq == pi->freqs[i]) {
214 : 0 : pi->curr_idx = i;
215 : 0 : pi->f = f;
216 : 0 : return 0;
217 : : }
218 : : }
219 : :
220 : 0 : err:
221 [ # # ]: 0 : if (f != NULL)
222 : 0 : fclose(f);
223 : :
224 : : return -1;
225 : : }
226 : :
227 : : int
228 : 0 : power_acpi_cpufreq_check_supported(void)
229 : : {
230 : 0 : return cpufreq_check_scaling_driver(POWER_ACPI_DRIVER);
231 : : }
232 : :
233 : : int
234 : 1 : power_acpi_cpufreq_init(unsigned int lcore_id)
235 : : {
236 : : struct acpi_power_info *pi;
237 : : uint32_t exp_state;
238 : :
239 [ - + ]: 1 : if (lcore_id >= RTE_MAX_LCORE) {
240 : 0 : POWER_LOG(ERR, "Lcore id %u can not exceeds %u",
241 : : lcore_id, RTE_MAX_LCORE - 1U);
242 : 0 : return -1;
243 : : }
244 : :
245 : 1 : pi = &lcore_power_info[lcore_id];
246 : : exp_state = POWER_IDLE;
247 : : /* The power in use state works as a guard variable between
248 : : * the CPU frequency control initialization and exit process.
249 : : * The ACQUIRE memory ordering here pairs with the RELEASE
250 : : * ordering below as lock to make sure the frequency operations
251 : : * in the critical section are done under the correct state.
252 : : */
253 [ - + ]: 1 : if (!rte_atomic_compare_exchange_strong_explicit(&(pi->state), &exp_state,
254 : : POWER_ONGOING,
255 : : rte_memory_order_acquire, rte_memory_order_relaxed)) {
256 : 0 : POWER_LOG(INFO, "Power management of lcore %u is "
257 : : "in use", lcore_id);
258 : 0 : return -1;
259 : : }
260 : :
261 : 1 : pi->lcore_id = lcore_id;
262 : : /* Check and set the governor */
263 [ + - ]: 1 : if (power_set_governor_userspace(pi) < 0) {
264 : 1 : POWER_LOG(ERR, "Cannot set governor of lcore %u to "
265 : : "userspace", lcore_id);
266 : 1 : goto fail;
267 : : }
268 : :
269 : : /* Get the available frequencies */
270 [ # # ]: 0 : if (power_get_available_freqs(pi) < 0) {
271 : 0 : POWER_LOG(ERR, "Cannot get available frequencies of "
272 : : "lcore %u", lcore_id);
273 : 0 : goto fail;
274 : : }
275 : :
276 : : /* Init for setting lcore frequency */
277 [ # # ]: 0 : if (power_init_for_setting_freq(pi) < 0) {
278 : 0 : POWER_LOG(ERR, "Cannot init for setting frequency for "
279 : : "lcore %u", lcore_id);
280 : 0 : goto fail;
281 : : }
282 : :
283 : : /* Set freq to max by default */
284 [ # # ]: 0 : if (power_acpi_cpufreq_freq_max(lcore_id) < 0) {
285 : 0 : POWER_LOG(ERR, "Cannot set frequency of lcore %u "
286 : : "to max", lcore_id);
287 : 0 : goto fail;
288 : : }
289 : :
290 : 0 : POWER_LOG(INFO, "Initialized successfully for lcore %u "
291 : : "power management", lcore_id);
292 : : exp_state = POWER_ONGOING;
293 : 0 : rte_atomic_compare_exchange_strong_explicit(&(pi->state), &exp_state, POWER_USED,
294 : : rte_memory_order_release, rte_memory_order_relaxed);
295 : :
296 : 0 : return 0;
297 : :
298 : 1 : fail:
299 : : exp_state = POWER_ONGOING;
300 : 1 : rte_atomic_compare_exchange_strong_explicit(&(pi->state), &exp_state, POWER_UNKNOWN,
301 : : rte_memory_order_release, rte_memory_order_relaxed);
302 : :
303 : 1 : return -1;
304 : : }
305 : :
306 : : int
307 : 0 : power_acpi_cpufreq_exit(unsigned int lcore_id)
308 : : {
309 : : struct acpi_power_info *pi;
310 : : uint32_t exp_state;
311 : :
312 [ # # ]: 0 : if (lcore_id >= RTE_MAX_LCORE) {
313 : 0 : POWER_LOG(ERR, "Lcore id %u can not exceeds %u",
314 : : lcore_id, RTE_MAX_LCORE - 1U);
315 : 0 : return -1;
316 : : }
317 : : pi = &lcore_power_info[lcore_id];
318 : : exp_state = POWER_USED;
319 : : /* The power in use state works as a guard variable between
320 : : * the CPU frequency control initialization and exit process.
321 : : * The ACQUIRE memory ordering here pairs with the RELEASE
322 : : * ordering below as lock to make sure the frequency operations
323 : : * in the critical section are done under the correct state.
324 : : */
325 [ # # ]: 0 : if (!rte_atomic_compare_exchange_strong_explicit(&(pi->state), &exp_state,
326 : : POWER_ONGOING,
327 : : rte_memory_order_acquire, rte_memory_order_relaxed)) {
328 : 0 : POWER_LOG(INFO, "Power management of lcore %u is "
329 : : "not used", lcore_id);
330 : 0 : return -1;
331 : : }
332 : :
333 : : /* Close FD of setting freq */
334 : 0 : fclose(pi->f);
335 : 0 : pi->f = NULL;
336 : :
337 : : /* Set the governor back to the original */
338 [ # # ]: 0 : if (power_set_governor_original(pi) < 0) {
339 : 0 : POWER_LOG(ERR, "Cannot set the governor of %u back "
340 : : "to the original", lcore_id);
341 : 0 : goto fail;
342 : : }
343 : :
344 : 0 : POWER_LOG(INFO, "Power management of lcore %u has exited from "
345 : : "'userspace' mode and been set back to the "
346 : : "original", lcore_id);
347 : : exp_state = POWER_ONGOING;
348 : 0 : rte_atomic_compare_exchange_strong_explicit(&(pi->state), &exp_state, POWER_IDLE,
349 : : rte_memory_order_release, rte_memory_order_relaxed);
350 : :
351 : 0 : return 0;
352 : :
353 : : fail:
354 : : exp_state = POWER_ONGOING;
355 : 0 : rte_atomic_compare_exchange_strong_explicit(&(pi->state), &exp_state, POWER_UNKNOWN,
356 : : rte_memory_order_release, rte_memory_order_relaxed);
357 : :
358 : 0 : return -1;
359 : : }
360 : :
361 : : uint32_t
362 : 0 : power_acpi_cpufreq_freqs(unsigned int lcore_id, uint32_t *freqs, uint32_t num)
363 : : {
364 : : struct acpi_power_info *pi;
365 : :
366 [ # # ]: 0 : if (lcore_id >= RTE_MAX_LCORE) {
367 : 0 : POWER_LOG(ERR, "Invalid lcore ID");
368 : 0 : return 0;
369 : : }
370 : :
371 [ # # ]: 0 : if (freqs == NULL) {
372 : 0 : POWER_LOG(ERR, "NULL buffer supplied");
373 : 0 : return 0;
374 : : }
375 : :
376 : : pi = &lcore_power_info[lcore_id];
377 [ # # ]: 0 : if (num < pi->nb_freqs) {
378 : 0 : POWER_LOG(ERR, "Buffer size is not enough");
379 : 0 : return 0;
380 : : }
381 [ # # ]: 0 : rte_memcpy(freqs, pi->freqs, pi->nb_freqs * sizeof(uint32_t));
382 : :
383 : 0 : return pi->nb_freqs;
384 : : }
385 : :
386 : : uint32_t
387 : 0 : power_acpi_cpufreq_get_freq(unsigned int lcore_id)
388 : : {
389 [ # # ]: 0 : if (lcore_id >= RTE_MAX_LCORE) {
390 : 0 : POWER_LOG(ERR, "Invalid lcore ID");
391 : 0 : return RTE_POWER_INVALID_FREQ_INDEX;
392 : : }
393 : :
394 : 0 : return lcore_power_info[lcore_id].curr_idx;
395 : : }
396 : :
397 : : int
398 : 0 : power_acpi_cpufreq_set_freq(unsigned int lcore_id, uint32_t index)
399 : : {
400 [ # # ]: 0 : if (lcore_id >= RTE_MAX_LCORE) {
401 : 0 : POWER_LOG(ERR, "Invalid lcore ID");
402 : 0 : return -1;
403 : : }
404 : :
405 : 0 : return set_freq_internal(&(lcore_power_info[lcore_id]), index);
406 : : }
407 : :
408 : : int
409 : 0 : power_acpi_cpufreq_freq_down(unsigned int lcore_id)
410 : : {
411 : : struct acpi_power_info *pi;
412 : :
413 [ # # ]: 0 : if (lcore_id >= RTE_MAX_LCORE) {
414 : 0 : POWER_LOG(ERR, "Invalid lcore ID");
415 : 0 : return -1;
416 : : }
417 : :
418 : 0 : pi = &lcore_power_info[lcore_id];
419 [ # # ]: 0 : if (pi->curr_idx + 1 == pi->nb_freqs)
420 : : return 0;
421 : :
422 : : /* Frequencies in the array are from high to low. */
423 : 0 : return set_freq_internal(pi, pi->curr_idx + 1);
424 : : }
425 : :
426 : : int
427 : 0 : power_acpi_cpufreq_freq_up(unsigned int lcore_id)
428 : : {
429 : : struct acpi_power_info *pi;
430 : :
431 [ # # ]: 0 : if (lcore_id >= RTE_MAX_LCORE) {
432 : 0 : POWER_LOG(ERR, "Invalid lcore ID");
433 : 0 : return -1;
434 : : }
435 : :
436 : 0 : pi = &lcore_power_info[lcore_id];
437 [ # # # # ]: 0 : if (pi->curr_idx == 0 ||
438 [ # # # # ]: 0 : (pi->curr_idx == 1 && pi->turbo_available && !pi->turbo_enable))
439 : : return 0;
440 : :
441 : : /* Frequencies in the array are from high to low. */
442 : 0 : return set_freq_internal(pi, pi->curr_idx - 1);
443 : : }
444 : :
445 : : int
446 : 0 : power_acpi_cpufreq_freq_max(unsigned int lcore_id)
447 : : {
448 [ # # ]: 0 : if (lcore_id >= RTE_MAX_LCORE) {
449 : 0 : POWER_LOG(ERR, "Invalid lcore ID");
450 : 0 : return -1;
451 : : }
452 : :
453 : : /* Frequencies in the array are from high to low. */
454 [ # # ]: 0 : if (lcore_power_info[lcore_id].turbo_available) {
455 [ # # ]: 0 : if (lcore_power_info[lcore_id].turbo_enable)
456 : : /* Set to Turbo */
457 : 0 : return set_freq_internal(
458 : : &lcore_power_info[lcore_id], 0);
459 : : else
460 : : /* Set to max non-turbo */
461 : 0 : return set_freq_internal(
462 : : &lcore_power_info[lcore_id], 1);
463 : : } else
464 : 0 : return set_freq_internal(&lcore_power_info[lcore_id], 0);
465 : : }
466 : :
467 : : int
468 : 0 : power_acpi_cpufreq_freq_min(unsigned int lcore_id)
469 : : {
470 : : struct acpi_power_info *pi;
471 : :
472 [ # # ]: 0 : if (lcore_id >= RTE_MAX_LCORE) {
473 : 0 : POWER_LOG(ERR, "Invalid lcore ID");
474 : 0 : return -1;
475 : : }
476 : :
477 : 0 : pi = &lcore_power_info[lcore_id];
478 : :
479 : : /* Frequencies in the array are from high to low. */
480 : 0 : return set_freq_internal(pi, pi->nb_freqs - 1);
481 : : }
482 : :
483 : :
484 : : int
485 : 0 : power_acpi_turbo_status(unsigned int lcore_id)
486 : : {
487 : : struct acpi_power_info *pi;
488 : :
489 [ # # ]: 0 : if (lcore_id >= RTE_MAX_LCORE) {
490 : 0 : POWER_LOG(ERR, "Invalid lcore ID");
491 : 0 : return -1;
492 : : }
493 : :
494 : : pi = &lcore_power_info[lcore_id];
495 : :
496 : 0 : return pi->turbo_enable;
497 : : }
498 : :
499 : :
500 : : int
501 : 0 : power_acpi_enable_turbo(unsigned int lcore_id)
502 : : {
503 : : struct acpi_power_info *pi;
504 : :
505 [ # # ]: 0 : if (lcore_id >= RTE_MAX_LCORE) {
506 : 0 : POWER_LOG(ERR, "Invalid lcore ID");
507 : 0 : return -1;
508 : : }
509 : :
510 : : pi = &lcore_power_info[lcore_id];
511 : :
512 [ # # ]: 0 : if (pi->turbo_available)
513 : 0 : pi->turbo_enable = 1;
514 : : else {
515 : 0 : pi->turbo_enable = 0;
516 : 0 : POWER_LOG(ERR,
517 : : "Failed to enable turbo on lcore %u",
518 : : lcore_id);
519 : 0 : return -1;
520 : : }
521 : :
522 : : /* Max may have changed, so call to max function */
523 [ # # ]: 0 : if (power_acpi_cpufreq_freq_max(lcore_id) < 0) {
524 : 0 : POWER_LOG(ERR,
525 : : "Failed to set frequency of lcore %u to max",
526 : : lcore_id);
527 : 0 : return -1;
528 : : }
529 : :
530 : : return 0;
531 : : }
532 : :
533 : : int
534 : 0 : power_acpi_disable_turbo(unsigned int lcore_id)
535 : : {
536 : : struct acpi_power_info *pi;
537 : :
538 [ # # ]: 0 : if (lcore_id >= RTE_MAX_LCORE) {
539 : 0 : POWER_LOG(ERR, "Invalid lcore ID");
540 : 0 : return -1;
541 : : }
542 : :
543 : : pi = &lcore_power_info[lcore_id];
544 : :
545 : 0 : pi->turbo_enable = 0;
546 : :
547 [ # # # # ]: 0 : if ((pi->turbo_available) && (pi->curr_idx <= 1)) {
548 : : /* Try to set freq to max by default coming out of turbo */
549 [ # # ]: 0 : if (power_acpi_cpufreq_freq_max(lcore_id) < 0) {
550 : 0 : POWER_LOG(ERR,
551 : : "Failed to set frequency of lcore %u to max",
552 : : lcore_id);
553 : 0 : return -1;
554 : : }
555 : : }
556 : :
557 : : return 0;
558 : : }
559 : :
560 : 0 : int power_acpi_get_capabilities(unsigned int lcore_id,
561 : : struct rte_power_core_capabilities *caps)
562 : : {
563 : : struct acpi_power_info *pi;
564 : :
565 [ # # ]: 0 : if (lcore_id >= RTE_MAX_LCORE) {
566 : 0 : POWER_LOG(ERR, "Invalid lcore ID");
567 : 0 : return -1;
568 : : }
569 [ # # ]: 0 : if (caps == NULL) {
570 : 0 : POWER_LOG(ERR, "Invalid argument");
571 : 0 : return -1;
572 : : }
573 : :
574 : : pi = &lcore_power_info[lcore_id];
575 : 0 : caps->capabilities = 0;
576 : 0 : caps->turbo = !!(pi->turbo_available);
577 : :
578 : 0 : return 0;
579 : : }
|