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