Merge branch 'slab/urgent' into slab/next
[pandora-kernel.git] / tools / power / cpupower / utils / idle_monitor / amd_fam14h_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  *  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
45 enum amd_fam14h_states {NON_PC0 = 0, PC1, PC6, NBP1,
46                         AMD_FAM14H_STATE_NUM};
47
48 static int fam14h_get_count_percent(unsigned int self_id, double *percent,
49                                     unsigned int cpu);
50 static int fam14h_nbp1_count(unsigned int id, unsigned long long *count,
51                              unsigned int cpu);
52
53 static 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
84 static struct pci_access *pci_acc;
85 static int pci_vendor_id = 0x1022;
86 static int pci_dev_ids[2] = {0x1716, 0};
87 static struct pci_dev *amd_fam14h_pci_dev;
88
89 static int nbp1_entered;
90
91 struct timespec start_time;
92 static unsigned long long timediff;
93
94 #ifdef DEBUG
95 struct timespec dbg_time;
96 long dbg_timediff;
97 #endif
98
99 static unsigned long long *previous_count[AMD_FAM14H_STATE_NUM];
100 static unsigned long long *current_count[AMD_FAM14H_STATE_NUM];
101
102 static 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
130 static 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
165 static 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
201 static 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 }
213 static 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
234 static 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 #ifdef DEBUG
243         clock_gettime(CLOCK_REALTIME, &dbg_time);
244         dbg_timediff = timespec_diff_us(start_time, dbg_time);
245         dprint("Enabling counters took: %lu us\n",
246                dbg_timediff);
247 #endif
248         return 0;
249 }
250
251 static int amd_fam14h_stop(void)
252 {
253         int num, cpu;
254         struct timespec end_time;
255
256         clock_gettime(CLOCK_REALTIME, &end_time);
257
258         for (num = 0; num < AMD_FAM14H_STATE_NUM; num++) {
259                 for (cpu = 0; cpu < cpu_count; cpu++)
260                         amd_fam14h_disable(&amd_fam14h_cstates[num], cpu);
261         }
262 #ifdef DEBUG
263         clock_gettime(CLOCK_REALTIME, &dbg_time);
264         dbg_timediff = timespec_diff_us(end_time, dbg_time);
265         dprint("Disabling counters took: %lu ns\n", dbg_timediff);
266 #endif
267         timediff = timespec_diff_us(start_time, end_time);
268         if (timediff / 1000 > OVERFLOW_MS)
269                 print_overflow_err((unsigned int)timediff / 1000000,
270                                    OVERFLOW_MS / 1000);
271
272         return 0;
273 }
274
275 static int is_nbp1_capable(void)
276 {
277         uint32_t val;
278         val = pci_read_long(amd_fam14h_pci_dev, PCI_NBP1_CAP_OFFSET);
279         return val & (1 << 31);
280 }
281
282 struct cpuidle_monitor *amd_fam14h_register(void)
283 {
284         int num;
285
286         if (cpupower_cpu_info.vendor != X86_VENDOR_AMD)
287                 return NULL;
288
289         if (cpupower_cpu_info.family == 0x14) {
290                 if (cpu_count <= 0 || cpu_count > 2) {
291                         fprintf(stderr, "AMD fam14h: Invalid cpu count: %d\n",
292                                 cpu_count);
293                         return NULL;
294                 }
295         } else
296                 return NULL;
297
298         /* We do not alloc for nbp1 machine wide counter */
299         for (num = 0; num < AMD_FAM14H_STATE_NUM - 1; num++) {
300                 previous_count[num] = calloc(cpu_count,
301                                               sizeof(unsigned long long));
302                 current_count[num]  = calloc(cpu_count,
303                                               sizeof(unsigned long long));
304         }
305
306         amd_fam14h_pci_dev = pci_acc_init(&pci_acc, pci_vendor_id, pci_dev_ids);
307         if (amd_fam14h_pci_dev == NULL || pci_acc == NULL)
308                 return NULL;
309
310         if (!is_nbp1_capable())
311                 amd_fam14h_monitor.hw_states_num = AMD_FAM14H_STATE_NUM - 1;
312
313         amd_fam14h_monitor.name_len = strlen(amd_fam14h_monitor.name);
314         return &amd_fam14h_monitor;
315 }
316
317 static void amd_fam14h_unregister(void)
318 {
319         int num;
320         for (num = 0; num < AMD_FAM14H_STATE_NUM - 1; num++) {
321                 free(previous_count[num]);
322                 free(current_count[num]);
323         }
324         pci_cleanup(pci_acc);
325 }
326
327 struct cpuidle_monitor amd_fam14h_monitor = {
328         .name                   = "Ontario",
329         .hw_states              = amd_fam14h_cstates,
330         .hw_states_num          = AMD_FAM14H_STATE_NUM,
331         .start                  = amd_fam14h_start,
332         .stop                   = amd_fam14h_stop,
333         .do_register            = amd_fam14h_register,
334         .unregister             = amd_fam14h_unregister,
335         .needs_root             = 1,
336         .overflow_s             = OVERFLOW_MS / 1000,
337 };
338 #endif /* #if defined(__i386__) || defined(__x86_64__) */