linux/tools/power/cpupower/utils/idle_monitor/amd_fam14h_idle.c
<<
>>
Prefs
   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
  42enum amd_fam14h_states {NON_PC0 = 0, PC1, PC6, NBP1,
  43                        AMD_FAM14H_STATE_NUM};
  44
  45static int fam14h_get_count_percent(unsigned int self_id, double *percent,
  46                                    unsigned int cpu);
  47static int fam14h_nbp1_count(unsigned int id, unsigned long long *count,
  48                             unsigned int cpu);
  49
  50static 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
  81static struct pci_access *pci_acc;
  82static struct pci_dev *amd_fam14h_pci_dev;
  83static int nbp1_entered;
  84
  85static struct timespec start_time;
  86static unsigned long long timediff;
  87
  88#ifdef DEBUG
  89struct timespec dbg_time;
  90long dbg_timediff;
  91#endif
  92
  93static unsigned long long *previous_count[AMD_FAM14H_STATE_NUM];
  94static unsigned long long *current_count[AMD_FAM14H_STATE_NUM];
  95
  96static 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
 124static 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
 159static 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
 195static 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}
 207static 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
 228static 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
 245static 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
 269static 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
 276struct 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
 313static 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
 323struct 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__) */
 335