cpufreq: intel_pstate: Simplify intel_pstate_cpu_init()
[platform/kernel/linux-rpi.git] / tools / power / cpupower / utils / idle_monitor / amd_fam14h_idle.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  *  (C) 2010,2011      Thomas Renninger <trenn@suse.de>, Novell Inc.
4  *
5  *  PCI initialization based on example code from:
6  *  Andreas Herrmann <andreas.herrmann3@amd.com>
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 /* 32 bit register filled at 12500 HZ
40                                           (1 tick per 80ns) */
41
42 enum amd_fam14h_states {NON_PC0 = 0, PC1, PC6, NBP1,
43                         AMD_FAM14H_STATE_NUM};
44
45 static int fam14h_get_count_percent(unsigned int self_id, double *percent,
46                                     unsigned int cpu);
47 static int fam14h_nbp1_count(unsigned int id, unsigned long long *count,
48                              unsigned int cpu);
49
50 static 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
81 static struct pci_access *pci_acc;
82 static struct pci_dev *amd_fam14h_pci_dev;
83 static int nbp1_entered;
84
85 struct timespec start_time;
86 static unsigned long long timediff;
87
88 #ifdef DEBUG
89 struct timespec dbg_time;
90 long dbg_timediff;
91 #endif
92
93 static unsigned long long *previous_count[AMD_FAM14H_STATE_NUM];
94 static unsigned long long *current_count[AMD_FAM14H_STATE_NUM];
95
96 static 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
124 static 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         /* NBP1 needs extra treating -> write 1 to D18F6x98 bit 1 for init */
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         /* Enable monitor */
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         /* Set counter to zero */
153         pci_write_long(amd_fam14h_pci_dev, pci_offset, 0);
154         previous_count[state->id][cpu] = 0;
155
156         return 0;
157 }
158
159 static 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                 /* was the bit whether NBP1 got entered set? */
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
195 static 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 }
207 static 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         /* residency count in 80ns -> divide through 12.5 to get us residency */
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
228 static 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
245 static 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
269 static 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
276 struct 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         /* We do not alloc for nbp1 machine wide counter */
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         /* We need PCI device: Slot 18, Func 6, compare with BKDG
301            for fam 12h/14h */
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
313 static 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
323 struct 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 /* #if defined(__i386__) || defined(__x86_64__) */