~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/tools/power/cpupower/utils/idle_monitor/amd_fam14h_idle.c

Version: ~ [ linux-5.6 ] ~ [ linux-5.5.13 ] ~ [ linux-5.4.28 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.113 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.174 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.217 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.217 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.140 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.82 ] ~ [ linux-3.15.10 ] ~ [ linux-3.14.79 ] ~ [ linux-3.13.11 ] ~ [ linux-3.12.74 ] ~ [ linux-3.11.10 ] ~ [ linux-3.10.108 ] ~ [ linux-3.9.11 ] ~ [ linux-3.8.13 ] ~ [ linux-3.7.10 ] ~ [ linux-3.6.11 ] ~ [ linux-3.5.7 ] ~ [ linux-3.4.113 ] ~ [ linux-3.3.8 ] ~ [ linux-3.2.102 ] ~ [ linux-3.1.10 ] ~ [ linux-3.0.101 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

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

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp