power: use C11 atomics for power state
[dpdk.git] / lib / librte_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 rte_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 rte_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 rte_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 rte_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         /* Check if current governor is userspace */
156         if (strncmp(buf, POWER_GOVERNOR_USERSPACE,
157                         sizeof(POWER_GOVERNOR_USERSPACE)) == 0) {
158                 ret = 0;
159                 POWER_DEBUG_TRACE("Power management governor of lcore %u is "
160                                 "already userspace\n", pi->lcore_id);
161                 goto out;
162         }
163         /* Save the original governor */
164         strlcpy(pi->governor_ori, buf, sizeof(pi->governor_ori));
165
166         /* Write 'userspace' to the governor */
167         val = fseek(f, 0, SEEK_SET);
168         FOPS_OR_ERR_GOTO(val, out);
169
170         val = fputs(POWER_GOVERNOR_USERSPACE, f);
171         FOPS_OR_ERR_GOTO(val, out);
172
173         /* We need to flush to see if the fputs succeeds */
174         val = fflush(f);
175         FOPS_OR_ERR_GOTO(val, out);
176
177         ret = 0;
178         RTE_LOG(INFO, POWER, "Power management governor of lcore %u has been "
179                         "set to user space successfully\n", pi->lcore_id);
180 out:
181         fclose(f);
182
183         return ret;
184 }
185
186 /**
187  * It is to get the available frequencies of the specific lcore by reading the
188  * sys file.
189  */
190 static int
191 power_get_available_freqs(struct rte_power_info *pi)
192 {
193         FILE *f;
194         int ret = -1, i, count;
195         char *p;
196         char buf[BUFSIZ];
197         char fullpath[PATH_MAX];
198         char *freqs[RTE_MAX_LCORE_FREQS];
199         char *s;
200
201         snprintf(fullpath, sizeof(fullpath), POWER_SYSFILE_AVAIL_FREQ,
202                         pi->lcore_id);
203         f = fopen(fullpath, "r");
204         FOPEN_OR_ERR_RET(f, ret);
205
206         s = fgets(buf, sizeof(buf), f);
207         FOPS_OR_NULL_GOTO(s, out);
208
209         /* Strip the line break if there is */
210         p = strchr(buf, '\n');
211         if (p != NULL)
212                 *p = 0;
213
214         /* Split string into at most RTE_MAX_LCORE_FREQS frequencies */
215         count = rte_strsplit(buf, sizeof(buf), freqs,
216                         RTE_MAX_LCORE_FREQS, ' ');
217         if (count <= 0) {
218                 RTE_LOG(ERR, POWER, "No available frequency in "
219                                 ""POWER_SYSFILE_AVAIL_FREQ"\n", pi->lcore_id);
220                 goto out;
221         }
222         if (count >= RTE_MAX_LCORE_FREQS) {
223                 RTE_LOG(ERR, POWER, "Too many available frequencies : %d\n",
224                                 count);
225                 goto out;
226         }
227
228         /* Store the available frequncies into power context */
229         for (i = 0, pi->nb_freqs = 0; i < count; i++) {
230                 POWER_DEBUG_TRACE("Lcore %u frequency[%d]: %s\n", pi->lcore_id,
231                                 i, freqs[i]);
232                 pi->freqs[pi->nb_freqs++] = strtoul(freqs[i], &p,
233                                 POWER_CONVERT_TO_DECIMAL);
234         }
235
236         if ((pi->freqs[0]-1000) == pi->freqs[1]) {
237                 pi->turbo_available = 1;
238                 pi->turbo_enable = 1;
239                 POWER_DEBUG_TRACE("Lcore %u Can do Turbo Boost\n",
240                                 pi->lcore_id);
241         } else {
242                 pi->turbo_available = 0;
243                 pi->turbo_enable = 0;
244                 POWER_DEBUG_TRACE("Turbo Boost not available on Lcore %u\n",
245                                 pi->lcore_id);
246         }
247
248         ret = 0;
249         POWER_DEBUG_TRACE("%d frequency(s) of lcore %u are available\n",
250                         count, pi->lcore_id);
251 out:
252         fclose(f);
253
254         return ret;
255 }
256
257 /**
258  * It is to fopen the sys file for the future setting the lcore frequency.
259  */
260 static int
261 power_init_for_setting_freq(struct rte_power_info *pi)
262 {
263         FILE *f;
264         char fullpath[PATH_MAX];
265         char buf[BUFSIZ];
266         uint32_t i, freq;
267         char *s;
268
269         snprintf(fullpath, sizeof(fullpath), POWER_SYSFILE_SETSPEED,
270                         pi->lcore_id);
271         f = fopen(fullpath, "rw+");
272         FOPEN_OR_ERR_RET(f, -1);
273
274         s = fgets(buf, sizeof(buf), f);
275         FOPS_OR_NULL_GOTO(s, out);
276
277         freq = strtoul(buf, NULL, POWER_CONVERT_TO_DECIMAL);
278         for (i = 0; i < pi->nb_freqs; i++) {
279                 if (freq == pi->freqs[i]) {
280                         pi->curr_idx = i;
281                         pi->f = f;
282                         return 0;
283                 }
284         }
285
286 out:
287         fclose(f);
288
289         return -1;
290 }
291
292 int
293 power_acpi_cpufreq_check_supported(void)
294 {
295         return cpufreq_check_scaling_driver(POWER_ACPI_DRIVER);
296 }
297
298 int
299 power_acpi_cpufreq_init(unsigned int lcore_id)
300 {
301         struct rte_power_info *pi;
302         uint32_t exp_state;
303
304         if (lcore_id >= RTE_MAX_LCORE) {
305                 RTE_LOG(ERR, POWER, "Lcore id %u can not exceeds %u\n",
306                                 lcore_id, RTE_MAX_LCORE - 1U);
307                 return -1;
308         }
309
310         pi = &lcore_power_info[lcore_id];
311         exp_state = POWER_IDLE;
312         /* The power in use state works as a guard variable between
313          * the CPU frequency control initialization and exit process.
314          * The ACQUIRE memory ordering here pairs with the RELEASE
315          * ordering below as lock to make sure the frequency operations
316          * in the critical section are done under the correct state.
317          */
318         if (!__atomic_compare_exchange_n(&(pi->state), &exp_state,
319                                         POWER_ONGOING, 0,
320                                         __ATOMIC_ACQUIRE, __ATOMIC_RELAXED)) {
321                 RTE_LOG(INFO, POWER, "Power management of lcore %u is "
322                                 "in use\n", lcore_id);
323                 return -1;
324         }
325
326         pi->lcore_id = lcore_id;
327         /* Check and set the governor */
328         if (power_set_governor_userspace(pi) < 0) {
329                 RTE_LOG(ERR, POWER, "Cannot set governor of lcore %u to "
330                                 "userspace\n", lcore_id);
331                 goto fail;
332         }
333
334         /* Get the available frequencies */
335         if (power_get_available_freqs(pi) < 0) {
336                 RTE_LOG(ERR, POWER, "Cannot get available frequencies of "
337                                 "lcore %u\n", lcore_id);
338                 goto fail;
339         }
340
341         /* Init for setting lcore frequency */
342         if (power_init_for_setting_freq(pi) < 0) {
343                 RTE_LOG(ERR, POWER, "Cannot init for setting frequency for "
344                                 "lcore %u\n", lcore_id);
345                 goto fail;
346         }
347
348         /* Set freq to max by default */
349         if (power_acpi_cpufreq_freq_max(lcore_id) < 0) {
350                 RTE_LOG(ERR, POWER, "Cannot set frequency of lcore %u "
351                                 "to max\n", lcore_id);
352                 goto fail;
353         }
354
355         RTE_LOG(INFO, POWER, "Initialized successfully for lcore %u "
356                         "power management\n", lcore_id);
357         exp_state = POWER_ONGOING;
358         __atomic_compare_exchange_n(&(pi->state), &exp_state, POWER_USED,
359                                     0, __ATOMIC_RELEASE, __ATOMIC_RELAXED);
360
361         return 0;
362
363 fail:
364         exp_state = POWER_ONGOING;
365         __atomic_compare_exchange_n(&(pi->state), &exp_state, POWER_UNKNOWN,
366                                     0, __ATOMIC_RELEASE, __ATOMIC_RELAXED);
367
368         return -1;
369 }
370
371 /**
372  * It is to check the governor and then set the original governor back if
373  * needed by writing the sys file.
374  */
375 static int
376 power_set_governor_original(struct rte_power_info *pi)
377 {
378         FILE *f;
379         int ret = -1;
380         char buf[BUFSIZ];
381         char fullpath[PATH_MAX];
382         char *s;
383         int val;
384
385         snprintf(fullpath, sizeof(fullpath), POWER_SYSFILE_GOVERNOR,
386                         pi->lcore_id);
387         f = fopen(fullpath, "rw+");
388         FOPEN_OR_ERR_RET(f, ret);
389
390         s = fgets(buf, sizeof(buf), f);
391         FOPS_OR_NULL_GOTO(s, out);
392
393         /* Check if the governor to be set is the same as current */
394         if (strncmp(buf, pi->governor_ori, sizeof(pi->governor_ori)) == 0) {
395                 ret = 0;
396                 POWER_DEBUG_TRACE("Power management governor of lcore %u "
397                                 "has already been set to %s\n",
398                                 pi->lcore_id, pi->governor_ori);
399                 goto out;
400         }
401
402         /* Write back the original governor */
403         val = fseek(f, 0, SEEK_SET);
404         FOPS_OR_ERR_GOTO(val, out);
405
406         val = fputs(pi->governor_ori, f);
407         FOPS_OR_ERR_GOTO(val, out);
408
409         ret = 0;
410         RTE_LOG(INFO, POWER, "Power management governor of lcore %u "
411                         "has been set back to %s successfully\n",
412                         pi->lcore_id, pi->governor_ori);
413 out:
414         fclose(f);
415
416         return ret;
417 }
418
419 int
420 power_acpi_cpufreq_exit(unsigned int lcore_id)
421 {
422         struct rte_power_info *pi;
423         uint32_t exp_state;
424
425         if (lcore_id >= RTE_MAX_LCORE) {
426                 RTE_LOG(ERR, POWER, "Lcore id %u can not exceeds %u\n",
427                                 lcore_id, RTE_MAX_LCORE - 1U);
428                 return -1;
429         }
430         pi = &lcore_power_info[lcore_id];
431         exp_state = POWER_USED;
432         /* The power in use state works as a guard variable between
433          * the CPU frequency control initialization and exit process.
434          * The ACQUIRE memory ordering here pairs with the RELEASE
435          * ordering below as lock to make sure the frequency operations
436          * in the critical section are done under the correct state.
437          */
438         if (!__atomic_compare_exchange_n(&(pi->state), &exp_state,
439                                         POWER_ONGOING, 0,
440                                         __ATOMIC_ACQUIRE, __ATOMIC_RELAXED)) {
441                 RTE_LOG(INFO, POWER, "Power management of lcore %u is "
442                                 "not used\n", lcore_id);
443                 return -1;
444         }
445
446         /* Close FD of setting freq */
447         fclose(pi->f);
448         pi->f = NULL;
449
450         /* Set the governor back to the original */
451         if (power_set_governor_original(pi) < 0) {
452                 RTE_LOG(ERR, POWER, "Cannot set the governor of %u back "
453                                 "to the original\n", lcore_id);
454                 goto fail;
455         }
456
457         RTE_LOG(INFO, POWER, "Power management of lcore %u has exited from "
458                         "'userspace' mode and been set back to the "
459                         "original\n", lcore_id);
460         exp_state = POWER_ONGOING;
461         __atomic_compare_exchange_n(&(pi->state), &exp_state, POWER_IDLE,
462                                     0, __ATOMIC_RELEASE, __ATOMIC_RELAXED);
463
464         return 0;
465
466 fail:
467         exp_state = POWER_ONGOING;
468         __atomic_compare_exchange_n(&(pi->state), &exp_state, POWER_UNKNOWN,
469                                     0, __ATOMIC_RELEASE, __ATOMIC_RELAXED);
470
471         return -1;
472 }
473
474 uint32_t
475 power_acpi_cpufreq_freqs(unsigned int lcore_id, uint32_t *freqs, uint32_t num)
476 {
477         struct rte_power_info *pi;
478
479         if (lcore_id >= RTE_MAX_LCORE) {
480                 RTE_LOG(ERR, POWER, "Invalid lcore ID\n");
481                 return 0;
482         }
483
484         if (freqs == NULL) {
485                 RTE_LOG(ERR, POWER, "NULL buffer supplied\n");
486                 return 0;
487         }
488
489         pi = &lcore_power_info[lcore_id];
490         if (num < pi->nb_freqs) {
491                 RTE_LOG(ERR, POWER, "Buffer size is not enough\n");
492                 return 0;
493         }
494         rte_memcpy(freqs, pi->freqs, pi->nb_freqs * sizeof(uint32_t));
495
496         return pi->nb_freqs;
497 }
498
499 uint32_t
500 power_acpi_cpufreq_get_freq(unsigned int lcore_id)
501 {
502         if (lcore_id >= RTE_MAX_LCORE) {
503                 RTE_LOG(ERR, POWER, "Invalid lcore ID\n");
504                 return RTE_POWER_INVALID_FREQ_INDEX;
505         }
506
507         return lcore_power_info[lcore_id].curr_idx;
508 }
509
510 int
511 power_acpi_cpufreq_set_freq(unsigned int lcore_id, uint32_t index)
512 {
513         if (lcore_id >= RTE_MAX_LCORE) {
514                 RTE_LOG(ERR, POWER, "Invalid lcore ID\n");
515                 return -1;
516         }
517
518         return set_freq_internal(&(lcore_power_info[lcore_id]), index);
519 }
520
521 int
522 power_acpi_cpufreq_freq_down(unsigned int lcore_id)
523 {
524         struct rte_power_info *pi;
525
526         if (lcore_id >= RTE_MAX_LCORE) {
527                 RTE_LOG(ERR, POWER, "Invalid lcore ID\n");
528                 return -1;
529         }
530
531         pi = &lcore_power_info[lcore_id];
532         if (pi->curr_idx + 1 == pi->nb_freqs)
533                 return 0;
534
535         /* Frequencies in the array are from high to low. */
536         return set_freq_internal(pi, pi->curr_idx + 1);
537 }
538
539 int
540 power_acpi_cpufreq_freq_up(unsigned int lcore_id)
541 {
542         struct rte_power_info *pi;
543
544         if (lcore_id >= RTE_MAX_LCORE) {
545                 RTE_LOG(ERR, POWER, "Invalid lcore ID\n");
546                 return -1;
547         }
548
549         pi = &lcore_power_info[lcore_id];
550         if (pi->curr_idx == 0 ||
551             (pi->curr_idx == 1 && pi->turbo_available && !pi->turbo_enable))
552                 return 0;
553
554         /* Frequencies in the array are from high to low. */
555         return set_freq_internal(pi, pi->curr_idx - 1);
556 }
557
558 int
559 power_acpi_cpufreq_freq_max(unsigned int lcore_id)
560 {
561         if (lcore_id >= RTE_MAX_LCORE) {
562                 RTE_LOG(ERR, POWER, "Invalid lcore ID\n");
563                 return -1;
564         }
565
566         /* Frequencies in the array are from high to low. */
567         if (lcore_power_info[lcore_id].turbo_available) {
568                 if (lcore_power_info[lcore_id].turbo_enable)
569                         /* Set to Turbo */
570                         return set_freq_internal(
571                                         &lcore_power_info[lcore_id], 0);
572                 else
573                         /* Set to max non-turbo */
574                         return set_freq_internal(
575                                         &lcore_power_info[lcore_id], 1);
576         } else
577                 return set_freq_internal(&lcore_power_info[lcore_id], 0);
578 }
579
580 int
581 power_acpi_cpufreq_freq_min(unsigned int lcore_id)
582 {
583         struct rte_power_info *pi;
584
585         if (lcore_id >= RTE_MAX_LCORE) {
586                 RTE_LOG(ERR, POWER, "Invalid lcore ID\n");
587                 return -1;
588         }
589
590         pi = &lcore_power_info[lcore_id];
591
592         /* Frequencies in the array are from high to low. */
593         return set_freq_internal(pi, pi->nb_freqs - 1);
594 }
595
596
597 int
598 power_acpi_turbo_status(unsigned int lcore_id)
599 {
600         struct rte_power_info *pi;
601
602         if (lcore_id >= RTE_MAX_LCORE) {
603                 RTE_LOG(ERR, POWER, "Invalid lcore ID\n");
604                 return -1;
605         }
606
607         pi = &lcore_power_info[lcore_id];
608
609         return pi->turbo_enable;
610 }
611
612
613 int
614 power_acpi_enable_turbo(unsigned int lcore_id)
615 {
616         struct rte_power_info *pi;
617
618         if (lcore_id >= RTE_MAX_LCORE) {
619                 RTE_LOG(ERR, POWER, "Invalid lcore ID\n");
620                 return -1;
621         }
622
623         pi = &lcore_power_info[lcore_id];
624
625         if (pi->turbo_available)
626                 pi->turbo_enable = 1;
627         else {
628                 pi->turbo_enable = 0;
629                 RTE_LOG(ERR, POWER,
630                         "Failed to enable turbo on lcore %u\n",
631                         lcore_id);
632                         return -1;
633         }
634
635         /* Max may have changed, so call to max function */
636         if (power_acpi_cpufreq_freq_max(lcore_id) < 0) {
637                 RTE_LOG(ERR, POWER,
638                         "Failed to set frequency of lcore %u to max\n",
639                         lcore_id);
640                         return -1;
641         }
642
643         return 0;
644 }
645
646 int
647 power_acpi_disable_turbo(unsigned int lcore_id)
648 {
649         struct rte_power_info *pi;
650
651         if (lcore_id >= RTE_MAX_LCORE) {
652                 RTE_LOG(ERR, POWER, "Invalid lcore ID\n");
653                 return -1;
654         }
655
656         pi = &lcore_power_info[lcore_id];
657
658          pi->turbo_enable = 0;
659
660         if ((pi->turbo_available) && (pi->curr_idx <= 1)) {
661                 /* Try to set freq to max by default coming out of turbo */
662                 if (power_acpi_cpufreq_freq_max(lcore_id) < 0) {
663                         RTE_LOG(ERR, POWER,
664                                 "Failed to set frequency of lcore %u to max\n",
665                                 lcore_id);
666                         return -1;
667                 }
668         }
669
670         return 0;
671 }
672
673 int power_acpi_get_capabilities(unsigned int lcore_id,
674                 struct rte_power_core_capabilities *caps)
675 {
676         struct rte_power_info *pi;
677
678         if (lcore_id >= RTE_MAX_LCORE) {
679                 RTE_LOG(ERR, POWER, "Invalid lcore ID\n");
680                 return -1;
681         }
682         if (caps == NULL) {
683                 RTE_LOG(ERR, POWER, "Invalid argument\n");
684                 return -1;
685         }
686
687         pi = &lcore_power_info[lcore_id];
688         caps->capabilities = 0;
689         caps->turbo = !!(pi->turbo_available);
690
691         return 0;
692 }