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