linux/tools/power/cpupower/utils/idle_monitor/amd_fam14h_idle.c
<<
>>
Prefs
   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
  43enum amd_fam14h_states {NON_PC0 = 0, PC1, PC6, NBP1,
  44                        AMD_FAM14H_STATE_NUM};
  45
  46static int fam14h_get_count_percent(unsigned int self_id, double *percent,
  47                                    unsigned int cpu);
  48static int fam14h_nbp1_count(unsigned int id, unsigned long long *count,
  49                             unsigned int cpu);
  50
  51static 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
  82static struct pci_access *pci_acc;
  83static struct pci_dev *amd_fam14h_pci_dev;
  84static int nbp1_entered;
  85
  86struct timespec start_time;
  87static unsigned long long timediff;
  88
  89#ifdef DEBUG
  90struct timespec dbg_time;
  91long dbg_timediff;
  92#endif
  93
  94static unsigned long long *previous_count[AMD_FAM14H_STATE_NUM];
  95static unsigned long long *current_count[AMD_FAM14H_STATE_NUM];
  96
  97static 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
 125static 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
 160static 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
 196static 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}
 208static 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
 229static 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
 246static 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
 270static 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
 277struct 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
 314static 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
 324struct 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