150276Speter// SPDX-License-Identifier: GPL-2.0-only 2262685Sdelphij/* 350276Speter * (C) 2010,2011 Thomas Renninger <trenn@suse.de>, Novell Inc. 450276Speter * 550276Speter * Based on Len Brown's <lenb@kernel.org> turbostat tool. 650276Speter */ 750276Speter 850276Speter#if defined(__i386__) || defined(__x86_64__) 950276Speter 1050276Speter#include <stdio.h> 1150276Speter#include <stdint.h> 1250276Speter#include <stdlib.h> 1350276Speter#include <string.h> 1450276Speter 1550276Speter#include "helpers/helpers.h" 1650276Speter#include "idle_monitor/cpupower-monitor.h" 1750276Speter 1850276Speter#define MSR_PKG_C3_RESIDENCY 0x3F8 1950276Speter#define MSR_PKG_C6_RESIDENCY 0x3F9 2050276Speter#define MSR_CORE_C3_RESIDENCY 0x3FC 2150276Speter#define MSR_CORE_C6_RESIDENCY 0x3FD 2250276Speter 2350276Speter#define MSR_TSC 0x10 2450276Speter 2550276Speter#define NHM_CSTATE_COUNT 4 2650276Speter 2750276Speterenum intel_nhm_id { C3 = 0, C6, PC3, PC6, TSC = 0xFFFF }; 2850276Speter 2950276Speterstatic int nhm_get_count_percent(unsigned int self_id, double *percent, 30166124Srafan unsigned int cpu); 3150276Speter 3250276Speterstatic cstate_t nhm_cstates[NHM_CSTATE_COUNT] = { 3350276Speter { 3450276Speter .name = "C3", 35262685Sdelphij .desc = N_("Processor Core C3"), 3650276Speter .id = C3, 3750276Speter .range = RANGE_CORE, 38166124Srafan .get_count_percent = nhm_get_count_percent, 39166124Srafan }, 40166124Srafan { 41166124Srafan .name = "C6", 42166124Srafan .desc = N_("Processor Core C6"), 43166124Srafan .id = C6, 44166124Srafan .range = RANGE_CORE, 45166124Srafan .get_count_percent = nhm_get_count_percent, 46166124Srafan }, 47166124Srafan 48166124Srafan { 49166124Srafan .name = "PC3", 50166124Srafan .desc = N_("Processor Package C3"), 51166124Srafan .id = PC3, 52166124Srafan .range = RANGE_PACKAGE, 53166124Srafan .get_count_percent = nhm_get_count_percent, 54166124Srafan }, 55166124Srafan { 56166124Srafan .name = "PC6", 57166124Srafan .desc = N_("Processor Package C6"), 58166124Srafan .id = PC6, 59166124Srafan .range = RANGE_PACKAGE, 60166124Srafan .get_count_percent = nhm_get_count_percent, 61166124Srafan }, 62166124Srafan}; 63166124Srafan 64166124Srafanstatic unsigned long long tsc_at_measure_start; 65166124Srafanstatic unsigned long long tsc_at_measure_end; 6650276Speterstatic unsigned long long *previous_count[NHM_CSTATE_COUNT]; 6750276Speterstatic unsigned long long *current_count[NHM_CSTATE_COUNT]; 68262685Sdelphij/* valid flag for all CPUs. If a MSR read failed it will be zero */ 69166124Srafanstatic int *is_valid; 7050276Speter 71166124Srafanstatic int nhm_get_count(enum intel_nhm_id id, unsigned long long *val, 7250276Speter unsigned int cpu) 7350276Speter{ 7450276Speter int msr; 7550276Speter 76166124Srafan switch (id) { 7750276Speter case C3: 78166124Srafan msr = MSR_CORE_C3_RESIDENCY; 7950276Speter break; 8050276Speter case C6: 8150276Speter msr = MSR_CORE_C6_RESIDENCY; 82166124Srafan break; 8350276Speter case PC3: 84166124Srafan msr = MSR_PKG_C3_RESIDENCY; 85166124Srafan break; 8650276Speter case PC6: 87166124Srafan msr = MSR_PKG_C6_RESIDENCY; 8850276Speter break; 8950276Speter case TSC: 90166124Srafan msr = MSR_TSC; 9150276Speter break; 92166124Srafan default: 93166124Srafan return -1; 9450276Speter } 95174993Srafan if (read_msr(cpu, msr, val)) 96166124Srafan return -1; 97166124Srafan 9850276Speter return 0; 99166124Srafan} 100166124Srafan 10150276Speterstatic int nhm_get_count_percent(unsigned int id, double *percent, 10250276Speter unsigned int cpu) 10350276Speter{ 104166124Srafan *percent = 0.0; 105166124Srafan 106166124Srafan if (!is_valid[cpu]) 107166124Srafan return -1; 108166124Srafan 10950276Speter *percent = (100.0 * 110166124Srafan (current_count[id][cpu] - previous_count[id][cpu])) / 111166124Srafan (tsc_at_measure_end - tsc_at_measure_start); 112166124Srafan 113166124Srafan dprint("%s: previous: %llu - current: %llu - (%u)\n", 114166124Srafan nhm_cstates[id].name, previous_count[id][cpu], 11550276Speter current_count[id][cpu], cpu); 11650276Speter 11750276Speter dprint("%s: tsc_diff: %llu - count_diff: %llu - percent: %2.f (%u)\n", 11850276Speter nhm_cstates[id].name, 11950276Speter (unsigned long long) tsc_at_measure_end - tsc_at_measure_start, 12050276Speter current_count[id][cpu] - previous_count[id][cpu], 121166124Srafan *percent, cpu); 12250276Speter 12350276Speter return 0; 12450276Speter} 125166124Srafan 126166124Srafanstatic int nhm_start(void) 12750276Speter{ 12850276Speter int num, cpu; 12950276Speter unsigned long long dbg, val; 130166124Srafan 13150276Speter nhm_get_count(TSC, &tsc_at_measure_start, base_cpu); 132166124Srafan 133166124Srafan for (num = 0; num < NHM_CSTATE_COUNT; num++) { 13450276Speter for (cpu = 0; cpu < cpu_count; cpu++) { 13550276Speter is_valid[cpu] = !nhm_get_count(num, &val, cpu); 13650276Speter previous_count[num][cpu] = val; 13750276Speter } 138166124Srafan } 13950276Speter nhm_get_count(TSC, &dbg, base_cpu); 140166124Srafan dprint("TSC diff: %llu\n", dbg - tsc_at_measure_start); 141166124Srafan return 0; 14250276Speter} 143174993Srafan 144166124Srafanstatic int nhm_stop(void) 145166124Srafan{ 14650276Speter unsigned long long val; 147166124Srafan unsigned long long dbg; 148166124Srafan int num, cpu; 14950276Speter 15050276Speter nhm_get_count(TSC, &tsc_at_measure_end, base_cpu); 15150276Speter 152166124Srafan for (num = 0; num < NHM_CSTATE_COUNT; num++) { 153166124Srafan for (cpu = 0; cpu < cpu_count; cpu++) { 15450276Speter is_valid[cpu] = !nhm_get_count(num, &val, cpu); 155166124Srafan current_count[num][cpu] = val; 15650276Speter } 157166124Srafan } 158166124Srafan nhm_get_count(TSC, &dbg, base_cpu); 159166124Srafan dprint("TSC diff: %llu\n", dbg - tsc_at_measure_end); 160166124Srafan 16150276Speter return 0; 16250276Speter} 163166124Srafan 164166124Srafanstruct cpuidle_monitor intel_nhm_monitor; 165166124Srafan 16650276Speterstruct cpuidle_monitor *intel_nhm_register(void) 16750276Speter{ 16850276Speter int num; 16950276Speter 17050276Speter if (cpupower_cpu_info.vendor != X86_VENDOR_INTEL) 17150276Speter return NULL; 172166124Srafan 17350276Speter if (!(cpupower_cpu_info.caps & CPUPOWER_CAP_INV_TSC)) 17450276Speter return NULL; 175166124Srafan 17650276Speter if (!(cpupower_cpu_info.caps & CPUPOWER_CAP_APERF)) 17750276Speter return NULL; 17850276Speter 17950276Speter /* Free this at program termination */ 18050276Speter is_valid = calloc(cpu_count, sizeof(int)); 18176726Speter for (num = 0; num < NHM_CSTATE_COUNT; num++) { 182166124Srafan previous_count[num] = calloc(cpu_count, 18350276Speter sizeof(unsigned long long)); 184166124Srafan current_count[num] = calloc(cpu_count, 18550276Speter sizeof(unsigned long long)); 186166124Srafan } 187166124Srafan 188262685Sdelphij intel_nhm_monitor.name_len = strlen(intel_nhm_monitor.name); 189262685Sdelphij return &intel_nhm_monitor; 190262685Sdelphij} 191262685Sdelphij 192262685Sdelphijvoid intel_nhm_unregister(void) 193262685Sdelphij{ 194166124Srafan int num; 195166124Srafan 196166124Srafan for (num = 0; num < NHM_CSTATE_COUNT; num++) { 197166124Srafan free(previous_count[num]); 198166124Srafan free(current_count[num]); 199166124Srafan } 200166124Srafan free(is_valid); 201166124Srafan} 20250276Speter 20350276Speterstruct cpuidle_monitor intel_nhm_monitor = { 20450276Speter .name = "Nehalem", 20550276Speter .hw_states_num = NHM_CSTATE_COUNT, 206166124Srafan .hw_states = nhm_cstates, 20750276Speter .start = nhm_start, 208166124Srafan .stop = nhm_stop, 20950276Speter .do_register = intel_nhm_register, 21050276Speter .unregister = intel_nhm_unregister, 21150276Speter .flags.needs_root = 1, 212166124Srafan .overflow_s = 922000000 /* 922337203 seconds TSC overflow 21350276Speter at 20GHz */ 21476726Speter}; 215166124Srafan#endif 21650276Speter