root/tools/power/cpupower/utils/idle_monitor/amd_fam14h_idle.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. amd_fam14h_get_pci_info
  2. amd_fam14h_init
  3. amd_fam14h_disable
  4. fam14h_nbp1_count
  5. fam14h_get_count_percent
  6. amd_fam14h_start
  7. amd_fam14h_stop
  8. is_nbp1_capable
  9. amd_fam14h_register
  10. amd_fam14h_unregister

   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 static 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         .needs_root             = 1,
 332         .overflow_s             = OVERFLOW_MS / 1000,
 333 };
 334 #endif /* #if defined(__i386__) || defined(__x86_64__) */

/* [<][>][^][v][top][bottom][index][help] */