1
2
3
4
5
6
7
8
9#if defined(__i386__) || defined(__x86_64__)
10
11#include <stdio.h>
12#include <stdlib.h>
13#include <stdint.h>
14#include <time.h>
15#include <string.h>
16
17#include <pci/pci.h>
18
19#include "idle_monitor/cpupower-monitor.h"
20#include "helpers/helpers.h"
21
22#define PCI_NON_PC0_OFFSET 0xb0
23#define PCI_PC1_OFFSET 0xb4
24#define PCI_PC6_OFFSET 0xb8
25
26#define PCI_MONITOR_ENABLE_REG 0xe0
27
28#define PCI_NON_PC0_ENABLE_BIT 0
29#define PCI_PC1_ENABLE_BIT 1
30#define PCI_PC6_ENABLE_BIT 2
31
32#define PCI_NBP1_STAT_OFFSET 0x98
33#define PCI_NBP1_ACTIVE_BIT 2
34#define PCI_NBP1_ENTERED_BIT 1
35
36#define PCI_NBP1_CAP_OFFSET 0x90
37#define PCI_NBP1_CAPABLE_BIT 31
38
39#define OVERFLOW_MS 343597
40
41
42enum amd_fam14h_states {NON_PC0 = 0, PC1, PC6, NBP1,
43 AMD_FAM14H_STATE_NUM};
44
45static int fam14h_get_count_percent(unsigned int self_id, double *percent,
46 unsigned int cpu);
47static int fam14h_nbp1_count(unsigned int id, unsigned long long *count,
48 unsigned int cpu);
49
50static cstate_t amd_fam14h_cstates[AMD_FAM14H_STATE_NUM] = {
51 {
52 .name = "!PC0",
53 .desc = N_("Package in sleep state (PC1 or deeper)"),
54 .id = NON_PC0,
55 .range = RANGE_PACKAGE,
56 .get_count_percent = fam14h_get_count_percent,
57 },
58 {
59 .name = "PC1",
60 .desc = N_("Processor Package C1"),
61 .id = PC1,
62 .range = RANGE_PACKAGE,
63 .get_count_percent = fam14h_get_count_percent,
64 },
65 {
66 .name = "PC6",
67 .desc = N_("Processor Package C6"),
68 .id = PC6,
69 .range = RANGE_PACKAGE,
70 .get_count_percent = fam14h_get_count_percent,
71 },
72 {
73 .name = "NBP1",
74 .desc = N_("North Bridge P1 boolean counter (returns 0 or 1)"),
75 .id = NBP1,
76 .range = RANGE_PACKAGE,
77 .get_count = fam14h_nbp1_count,
78 },
79};
80
81static struct pci_access *pci_acc;
82static struct pci_dev *amd_fam14h_pci_dev;
83static int nbp1_entered;
84
85static struct timespec start_time;
86static unsigned long long timediff;
87
88#ifdef DEBUG
89struct timespec dbg_time;
90long dbg_timediff;
91#endif
92
93static unsigned long long *previous_count[AMD_FAM14H_STATE_NUM];
94static unsigned long long *current_count[AMD_FAM14H_STATE_NUM];
95
96static int amd_fam14h_get_pci_info(struct cstate *state,
97 unsigned int *pci_offset,
98 unsigned int *enable_bit,
99 unsigned int cpu)
100{
101 switch (state->id) {
102 case NON_PC0:
103 *enable_bit = PCI_NON_PC0_ENABLE_BIT;
104 *pci_offset = PCI_NON_PC0_OFFSET;
105 break;
106 case PC1:
107 *enable_bit = PCI_PC1_ENABLE_BIT;
108 *pci_offset = PCI_PC1_OFFSET;
109 break;
110 case PC6:
111 *enable_bit = PCI_PC6_ENABLE_BIT;
112 *pci_offset = PCI_PC6_OFFSET;
113 break;
114 case NBP1:
115 *enable_bit = PCI_NBP1_ENTERED_BIT;
116 *pci_offset = PCI_NBP1_STAT_OFFSET;
117 break;
118 default:
119 return -1;
120 }
121 return 0;
122}
123
124static int amd_fam14h_init(cstate_t *state, unsigned int cpu)
125{
126 int enable_bit, pci_offset, ret;
127 uint32_t val;
128
129 ret = amd_fam14h_get_pci_info(state, &pci_offset, &enable_bit, cpu);
130 if (ret)
131 return ret;
132
133
134 if (state->id == NBP1) {
135 val = pci_read_long(amd_fam14h_pci_dev, pci_offset);
136 val |= 1 << enable_bit;
137 val = pci_write_long(amd_fam14h_pci_dev, pci_offset, val);
138 return ret;
139 }
140
141
142 val = pci_read_long(amd_fam14h_pci_dev, PCI_MONITOR_ENABLE_REG);
143 dprint("Init %s: read at offset: 0x%x val: %u\n", state->name,
144 PCI_MONITOR_ENABLE_REG, (unsigned int) val);
145 val |= 1 << enable_bit;
146 pci_write_long(amd_fam14h_pci_dev, PCI_MONITOR_ENABLE_REG, val);
147
148 dprint("Init %s: offset: 0x%x enable_bit: %d - val: %u (%u)\n",
149 state->name, PCI_MONITOR_ENABLE_REG, enable_bit,
150 (unsigned int) val, cpu);
151
152
153 pci_write_long(amd_fam14h_pci_dev, pci_offset, 0);
154 previous_count[state->id][cpu] = 0;
155
156 return 0;
157}
158
159static int amd_fam14h_disable(cstate_t *state, unsigned int cpu)
160{
161 int enable_bit, pci_offset, ret;
162 uint32_t val;
163
164 ret = amd_fam14h_get_pci_info(state, &pci_offset, &enable_bit, cpu);
165 if (ret)
166 return ret;
167
168 val = pci_read_long(amd_fam14h_pci_dev, pci_offset);
169 dprint("%s: offset: 0x%x %u\n", state->name, pci_offset, val);
170 if (state->id == NBP1) {
171
172 nbp1_entered = (val & (1 << PCI_NBP1_ACTIVE_BIT)) |
173 (val & (1 << PCI_NBP1_ENTERED_BIT));
174
175 dprint("NBP1 was %sentered - 0x%x - enable_bit: "
176 "%d - pci_offset: 0x%x\n",
177 nbp1_entered ? "" : "not ",
178 val, enable_bit, pci_offset);
179 return ret;
180 }
181 current_count[state->id][cpu] = val;
182
183 dprint("%s: Current - %llu (%u)\n", state->name,
184 current_count[state->id][cpu], cpu);
185 dprint("%s: Previous - %llu (%u)\n", state->name,
186 previous_count[state->id][cpu], cpu);
187
188 val = pci_read_long(amd_fam14h_pci_dev, PCI_MONITOR_ENABLE_REG);
189 val &= ~(1 << enable_bit);
190 pci_write_long(amd_fam14h_pci_dev, PCI_MONITOR_ENABLE_REG, val);
191
192 return 0;
193}
194
195static int fam14h_nbp1_count(unsigned int id, unsigned long long *count,
196 unsigned int cpu)
197{
198 if (id == NBP1) {
199 if (nbp1_entered)
200 *count = 1;
201 else
202 *count = 0;
203 return 0;
204 }
205 return -1;
206}
207static int fam14h_get_count_percent(unsigned int id, double *percent,
208 unsigned int cpu)
209{
210 unsigned long diff;
211
212 if (id >= AMD_FAM14H_STATE_NUM)
213 return -1;
214
215 diff = current_count[id][cpu] - previous_count[id][cpu];
216
217 if (timediff == 0)
218 *percent = 0.0;
219 else
220 *percent = 100.0 * diff / timediff / 12.5;
221
222 dprint("Timediff: %llu - res~: %lu us - percent: %.2f %%\n",
223 timediff, diff * 10 / 125, *percent);
224
225 return 0;
226}
227
228static int amd_fam14h_start(void)
229{
230 int num, cpu;
231 clock_gettime(CLOCK_REALTIME, &start_time);
232 for (num = 0; num < AMD_FAM14H_STATE_NUM; num++) {
233 for (cpu = 0; cpu < cpu_count; cpu++)
234 amd_fam14h_init(&amd_fam14h_cstates[num], cpu);
235 }
236#ifdef DEBUG
237 clock_gettime(CLOCK_REALTIME, &dbg_time);
238 dbg_timediff = timespec_diff_us(start_time, dbg_time);
239 dprint("Enabling counters took: %lu us\n",
240 dbg_timediff);
241#endif
242 return 0;
243}
244
245static int amd_fam14h_stop(void)
246{
247 int num, cpu;
248 struct timespec end_time;
249
250 clock_gettime(CLOCK_REALTIME, &end_time);
251
252 for (num = 0; num < AMD_FAM14H_STATE_NUM; num++) {
253 for (cpu = 0; cpu < cpu_count; cpu++)
254 amd_fam14h_disable(&amd_fam14h_cstates[num], cpu);
255 }
256#ifdef DEBUG
257 clock_gettime(CLOCK_REALTIME, &dbg_time);
258 dbg_timediff = timespec_diff_us(end_time, dbg_time);
259 dprint("Disabling counters took: %lu ns\n", dbg_timediff);
260#endif
261 timediff = timespec_diff_us(start_time, end_time);
262 if (timediff / 1000 > OVERFLOW_MS)
263 print_overflow_err((unsigned int)timediff / 1000000,
264 OVERFLOW_MS / 1000);
265
266 return 0;
267}
268
269static int is_nbp1_capable(void)
270{
271 uint32_t val;
272 val = pci_read_long(amd_fam14h_pci_dev, PCI_NBP1_CAP_OFFSET);
273 return val & (1 << 31);
274}
275
276struct cpuidle_monitor *amd_fam14h_register(void)
277{
278 int num;
279
280 if (cpupower_cpu_info.vendor != X86_VENDOR_AMD)
281 return NULL;
282
283 if (cpupower_cpu_info.family == 0x14)
284 strncpy(amd_fam14h_monitor.name, "Fam_14h",
285 MONITOR_NAME_LEN - 1);
286 else if (cpupower_cpu_info.family == 0x12)
287 strncpy(amd_fam14h_monitor.name, "Fam_12h",
288 MONITOR_NAME_LEN - 1);
289 else
290 return NULL;
291
292
293 for (num = 0; num < AMD_FAM14H_STATE_NUM - 1; num++) {
294 previous_count[num] = calloc(cpu_count,
295 sizeof(unsigned long long));
296 current_count[num] = calloc(cpu_count,
297 sizeof(unsigned long long));
298 }
299
300
301
302 amd_fam14h_pci_dev = pci_slot_func_init(&pci_acc, 0x18, 6);
303 if (amd_fam14h_pci_dev == NULL || pci_acc == NULL)
304 return NULL;
305
306 if (!is_nbp1_capable())
307 amd_fam14h_monitor.hw_states_num = AMD_FAM14H_STATE_NUM - 1;
308
309 amd_fam14h_monitor.name_len = strlen(amd_fam14h_monitor.name);
310 return &amd_fam14h_monitor;
311}
312
313static void amd_fam14h_unregister(void)
314{
315 int num;
316 for (num = 0; num < AMD_FAM14H_STATE_NUM - 1; num++) {
317 free(previous_count[num]);
318 free(current_count[num]);
319 }
320 pci_cleanup(pci_acc);
321}
322
323struct cpuidle_monitor amd_fam14h_monitor = {
324 .name = "",
325 .hw_states = amd_fam14h_cstates,
326 .hw_states_num = AMD_FAM14H_STATE_NUM,
327 .start = amd_fam14h_start,
328 .stop = amd_fam14h_stop,
329 .do_register = amd_fam14h_register,
330 .unregister = amd_fam14h_unregister,
331 .flags.needs_root = 1,
332 .overflow_s = OVERFLOW_MS / 1000,
333};
334#endif
335