Linux-libre 5.4.47-gnu
[librecmc/linux-libre.git] / tools / power / cpupower / utils / idle_monitor / hsw_ext_idle.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  *  (C) 2010,2011       Thomas Renninger <trenn@suse.de>, Novell Inc.
4  *
5  *  Based on SandyBridge monitor. Implements the new package C-states
6  *  (PC8, PC9, PC10) coming with a specific Haswell (family 0x45) CPU.
7  */
8
9 #if defined(__i386__) || defined(__x86_64__)
10
11 #include <stdio.h>
12 #include <stdint.h>
13 #include <stdlib.h>
14 #include <string.h>
15
16 #include "helpers/helpers.h"
17 #include "idle_monitor/cpupower-monitor.h"
18
19 #define MSR_PKG_C8_RESIDENCY           0x00000630
20 #define MSR_PKG_C9_RESIDENCY           0x00000631
21 #define MSR_PKG_C10_RESIDENCY          0x00000632
22
23 #define MSR_TSC 0x10
24
25 enum intel_hsw_ext_id { PC8 = 0, PC9, PC10, HSW_EXT_CSTATE_COUNT,
26                         TSC = 0xFFFF };
27
28 static int hsw_ext_get_count_percent(unsigned int self_id, double *percent,
29                                  unsigned int cpu);
30
31 static cstate_t hsw_ext_cstates[HSW_EXT_CSTATE_COUNT] = {
32         {
33                 .name                   = "PC8",
34                 .desc                   = N_("Processor Package C8"),
35                 .id                     = PC8,
36                 .range                  = RANGE_PACKAGE,
37                 .get_count_percent      = hsw_ext_get_count_percent,
38         },
39         {
40                 .name                   = "PC9",
41                 .desc                   = N_("Processor Package C9"),
42                 .id                     = PC9,
43                 .range                  = RANGE_PACKAGE,
44                 .get_count_percent      = hsw_ext_get_count_percent,
45         },
46         {
47                 .name                   = "PC10",
48                 .desc                   = N_("Processor Package C10"),
49                 .id                     = PC10,
50                 .range                  = RANGE_PACKAGE,
51                 .get_count_percent      = hsw_ext_get_count_percent,
52         },
53 };
54
55 static unsigned long long tsc_at_measure_start;
56 static unsigned long long tsc_at_measure_end;
57 static unsigned long long *previous_count[HSW_EXT_CSTATE_COUNT];
58 static unsigned long long *current_count[HSW_EXT_CSTATE_COUNT];
59 /* valid flag for all CPUs. If a MSR read failed it will be zero */
60 static int *is_valid;
61
62 static int hsw_ext_get_count(enum intel_hsw_ext_id id, unsigned long long *val,
63                         unsigned int cpu)
64 {
65         int msr;
66
67         switch (id) {
68         case PC8:
69                 msr = MSR_PKG_C8_RESIDENCY;
70                 break;
71         case PC9:
72                 msr = MSR_PKG_C9_RESIDENCY;
73                 break;
74         case PC10:
75                 msr = MSR_PKG_C10_RESIDENCY;
76                 break;
77         case TSC:
78                 msr = MSR_TSC;
79                 break;
80         default:
81                 return -1;
82         };
83         if (read_msr(cpu, msr, val))
84                 return -1;
85         return 0;
86 }
87
88 static int hsw_ext_get_count_percent(unsigned int id, double *percent,
89                                  unsigned int cpu)
90 {
91         *percent = 0.0;
92
93         if (!is_valid[cpu])
94                 return -1;
95
96         *percent = (100.0 *
97                 (current_count[id][cpu] - previous_count[id][cpu])) /
98                 (tsc_at_measure_end - tsc_at_measure_start);
99
100         dprint("%s: previous: %llu - current: %llu - (%u)\n",
101                 hsw_ext_cstates[id].name, previous_count[id][cpu],
102                 current_count[id][cpu], cpu);
103
104         dprint("%s: tsc_diff: %llu - count_diff: %llu - percent: %2.f (%u)\n",
105                hsw_ext_cstates[id].name,
106                (unsigned long long) tsc_at_measure_end - tsc_at_measure_start,
107                current_count[id][cpu] - previous_count[id][cpu],
108                *percent, cpu);
109
110         return 0;
111 }
112
113 static int hsw_ext_start(void)
114 {
115         int num, cpu;
116         unsigned long long val;
117
118         for (num = 0; num < HSW_EXT_CSTATE_COUNT; num++) {
119                 for (cpu = 0; cpu < cpu_count; cpu++) {
120                         hsw_ext_get_count(num, &val, cpu);
121                         previous_count[num][cpu] = val;
122                 }
123         }
124         hsw_ext_get_count(TSC, &tsc_at_measure_start, base_cpu);
125         return 0;
126 }
127
128 static int hsw_ext_stop(void)
129 {
130         unsigned long long val;
131         int num, cpu;
132
133         hsw_ext_get_count(TSC, &tsc_at_measure_end, base_cpu);
134
135         for (num = 0; num < HSW_EXT_CSTATE_COUNT; num++) {
136                 for (cpu = 0; cpu < cpu_count; cpu++) {
137                         is_valid[cpu] = !hsw_ext_get_count(num, &val, cpu);
138                         current_count[num][cpu] = val;
139                 }
140         }
141         return 0;
142 }
143
144 struct cpuidle_monitor intel_hsw_ext_monitor;
145
146 static struct cpuidle_monitor *hsw_ext_register(void)
147 {
148         int num;
149
150         if (cpupower_cpu_info.vendor != X86_VENDOR_INTEL
151             || cpupower_cpu_info.family != 6)
152                 return NULL;
153
154         switch (cpupower_cpu_info.model) {
155         case 0x45: /* HSW */
156                 break;
157         default:
158                 return NULL;
159         }
160
161         is_valid = calloc(cpu_count, sizeof(int));
162         for (num = 0; num < HSW_EXT_CSTATE_COUNT; num++) {
163                 previous_count[num] = calloc(cpu_count,
164                                         sizeof(unsigned long long));
165                 current_count[num]  = calloc(cpu_count,
166                                         sizeof(unsigned long long));
167         }
168         intel_hsw_ext_monitor.name_len = strlen(intel_hsw_ext_monitor.name);
169         return &intel_hsw_ext_monitor;
170 }
171
172 void hsw_ext_unregister(void)
173 {
174         int num;
175         free(is_valid);
176         for (num = 0; num < HSW_EXT_CSTATE_COUNT; num++) {
177                 free(previous_count[num]);
178                 free(current_count[num]);
179         }
180 }
181
182 struct cpuidle_monitor intel_hsw_ext_monitor = {
183         .name                   = "HaswellExtended",
184         .hw_states              = hsw_ext_cstates,
185         .hw_states_num          = HSW_EXT_CSTATE_COUNT,
186         .start                  = hsw_ext_start,
187         .stop                   = hsw_ext_stop,
188         .do_register            = hsw_ext_register,
189         .unregister             = hsw_ext_unregister,
190         .needs_root             = 1,
191         .overflow_s             = 922000000 /* 922337203 seconds TSC overflow
192                                                at 20GHz */
193 };
194 #endif /* defined(__i386__) || defined(__x86_64__) */