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