14f19048fSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 27ee767b6SThomas Renninger /* 37ee767b6SThomas Renninger * (C) 2010,2011 Thomas Renninger <trenn@suse.de>, Novell Inc. 47ee767b6SThomas Renninger * 57ee767b6SThomas Renninger * Based on SandyBridge monitor. Implements the new package C-states 67ee767b6SThomas Renninger * (PC8, PC9, PC10) coming with a specific Haswell (family 0x45) CPU. 77ee767b6SThomas Renninger */ 87ee767b6SThomas Renninger 97ee767b6SThomas Renninger #if defined(__i386__) || defined(__x86_64__) 107ee767b6SThomas Renninger 117ee767b6SThomas Renninger #include <stdio.h> 127ee767b6SThomas Renninger #include <stdint.h> 137ee767b6SThomas Renninger #include <stdlib.h> 147ee767b6SThomas Renninger #include <string.h> 157ee767b6SThomas Renninger 167ee767b6SThomas Renninger #include "helpers/helpers.h" 177ee767b6SThomas Renninger #include "idle_monitor/cpupower-monitor.h" 187ee767b6SThomas Renninger 197ee767b6SThomas Renninger #define MSR_PKG_C8_RESIDENCY 0x00000630 207ee767b6SThomas Renninger #define MSR_PKG_C9_RESIDENCY 0x00000631 217ee767b6SThomas Renninger #define MSR_PKG_C10_RESIDENCY 0x00000632 227ee767b6SThomas Renninger 237ee767b6SThomas Renninger #define MSR_TSC 0x10 247ee767b6SThomas Renninger 257ee767b6SThomas Renninger enum intel_hsw_ext_id { PC8 = 0, PC9, PC10, HSW_EXT_CSTATE_COUNT, 267ee767b6SThomas Renninger TSC = 0xFFFF }; 277ee767b6SThomas Renninger 287ee767b6SThomas Renninger static int hsw_ext_get_count_percent(unsigned int self_id, double *percent, 297ee767b6SThomas Renninger unsigned int cpu); 307ee767b6SThomas Renninger 317ee767b6SThomas Renninger static cstate_t hsw_ext_cstates[HSW_EXT_CSTATE_COUNT] = { 327ee767b6SThomas Renninger { 337ee767b6SThomas Renninger .name = "PC8", 347ee767b6SThomas Renninger .desc = N_("Processor Package C8"), 357ee767b6SThomas Renninger .id = PC8, 367ee767b6SThomas Renninger .range = RANGE_PACKAGE, 377ee767b6SThomas Renninger .get_count_percent = hsw_ext_get_count_percent, 387ee767b6SThomas Renninger }, 397ee767b6SThomas Renninger { 407ee767b6SThomas Renninger .name = "PC9", 417ee767b6SThomas Renninger .desc = N_("Processor Package C9"), 427ee767b6SThomas Renninger .id = PC9, 437ee767b6SThomas Renninger .range = RANGE_PACKAGE, 447ee767b6SThomas Renninger .get_count_percent = hsw_ext_get_count_percent, 457ee767b6SThomas Renninger }, 467ee767b6SThomas Renninger { 477ee767b6SThomas Renninger .name = "PC10", 487ee767b6SThomas Renninger .desc = N_("Processor Package C10"), 497ee767b6SThomas Renninger .id = PC10, 507ee767b6SThomas Renninger .range = RANGE_PACKAGE, 517ee767b6SThomas Renninger .get_count_percent = hsw_ext_get_count_percent, 527ee767b6SThomas Renninger }, 537ee767b6SThomas Renninger }; 547ee767b6SThomas Renninger 557ee767b6SThomas Renninger static unsigned long long tsc_at_measure_start; 567ee767b6SThomas Renninger static unsigned long long tsc_at_measure_end; 577ee767b6SThomas Renninger static unsigned long long *previous_count[HSW_EXT_CSTATE_COUNT]; 587ee767b6SThomas Renninger static unsigned long long *current_count[HSW_EXT_CSTATE_COUNT]; 597ee767b6SThomas Renninger /* valid flag for all CPUs. If a MSR read failed it will be zero */ 607ee767b6SThomas Renninger static int *is_valid; 617ee767b6SThomas Renninger 627ee767b6SThomas Renninger static int hsw_ext_get_count(enum intel_hsw_ext_id id, unsigned long long *val, 637ee767b6SThomas Renninger unsigned int cpu) 647ee767b6SThomas Renninger { 657ee767b6SThomas Renninger int msr; 667ee767b6SThomas Renninger 677ee767b6SThomas Renninger switch (id) { 687ee767b6SThomas Renninger case PC8: 697ee767b6SThomas Renninger msr = MSR_PKG_C8_RESIDENCY; 707ee767b6SThomas Renninger break; 717ee767b6SThomas Renninger case PC9: 727ee767b6SThomas Renninger msr = MSR_PKG_C9_RESIDENCY; 737ee767b6SThomas Renninger break; 747ee767b6SThomas Renninger case PC10: 757ee767b6SThomas Renninger msr = MSR_PKG_C10_RESIDENCY; 767ee767b6SThomas Renninger break; 777ee767b6SThomas Renninger case TSC: 787ee767b6SThomas Renninger msr = MSR_TSC; 797ee767b6SThomas Renninger break; 807ee767b6SThomas Renninger default: 817ee767b6SThomas Renninger return -1; 827ee767b6SThomas Renninger }; 837ee767b6SThomas Renninger if (read_msr(cpu, msr, val)) 847ee767b6SThomas Renninger return -1; 857ee767b6SThomas Renninger return 0; 867ee767b6SThomas Renninger } 877ee767b6SThomas Renninger 887ee767b6SThomas Renninger static int hsw_ext_get_count_percent(unsigned int id, double *percent, 897ee767b6SThomas Renninger unsigned int cpu) 907ee767b6SThomas Renninger { 917ee767b6SThomas Renninger *percent = 0.0; 927ee767b6SThomas Renninger 937ee767b6SThomas Renninger if (!is_valid[cpu]) 947ee767b6SThomas Renninger return -1; 957ee767b6SThomas Renninger 967ee767b6SThomas Renninger *percent = (100.0 * 977ee767b6SThomas Renninger (current_count[id][cpu] - previous_count[id][cpu])) / 987ee767b6SThomas Renninger (tsc_at_measure_end - tsc_at_measure_start); 997ee767b6SThomas Renninger 1007ee767b6SThomas Renninger dprint("%s: previous: %llu - current: %llu - (%u)\n", 1017ee767b6SThomas Renninger hsw_ext_cstates[id].name, previous_count[id][cpu], 1027ee767b6SThomas Renninger current_count[id][cpu], cpu); 1037ee767b6SThomas Renninger 1047ee767b6SThomas Renninger dprint("%s: tsc_diff: %llu - count_diff: %llu - percent: %2.f (%u)\n", 1057ee767b6SThomas Renninger hsw_ext_cstates[id].name, 1067ee767b6SThomas Renninger (unsigned long long) tsc_at_measure_end - tsc_at_measure_start, 1077ee767b6SThomas Renninger current_count[id][cpu] - previous_count[id][cpu], 1087ee767b6SThomas Renninger *percent, cpu); 1097ee767b6SThomas Renninger 1107ee767b6SThomas Renninger return 0; 1117ee767b6SThomas Renninger } 1127ee767b6SThomas Renninger 1137ee767b6SThomas Renninger static int hsw_ext_start(void) 1147ee767b6SThomas Renninger { 1157ee767b6SThomas Renninger int num, cpu; 1167ee767b6SThomas Renninger unsigned long long val; 1177ee767b6SThomas Renninger 1187ee767b6SThomas Renninger for (num = 0; num < HSW_EXT_CSTATE_COUNT; num++) { 1197ee767b6SThomas Renninger for (cpu = 0; cpu < cpu_count; cpu++) { 1207ee767b6SThomas Renninger hsw_ext_get_count(num, &val, cpu); 1217ee767b6SThomas Renninger previous_count[num][cpu] = val; 1227ee767b6SThomas Renninger } 1237ee767b6SThomas Renninger } 124d0e4a193SPrarit Bhargava hsw_ext_get_count(TSC, &tsc_at_measure_start, base_cpu); 1257ee767b6SThomas Renninger return 0; 1267ee767b6SThomas Renninger } 1277ee767b6SThomas Renninger 1287ee767b6SThomas Renninger static int hsw_ext_stop(void) 1297ee767b6SThomas Renninger { 1307ee767b6SThomas Renninger unsigned long long val; 1317ee767b6SThomas Renninger int num, cpu; 1327ee767b6SThomas Renninger 133d0e4a193SPrarit Bhargava hsw_ext_get_count(TSC, &tsc_at_measure_end, base_cpu); 1347ee767b6SThomas Renninger 1357ee767b6SThomas Renninger for (num = 0; num < HSW_EXT_CSTATE_COUNT; num++) { 1367ee767b6SThomas Renninger for (cpu = 0; cpu < cpu_count; cpu++) { 1377ee767b6SThomas Renninger is_valid[cpu] = !hsw_ext_get_count(num, &val, cpu); 1387ee767b6SThomas Renninger current_count[num][cpu] = val; 1397ee767b6SThomas Renninger } 1407ee767b6SThomas Renninger } 1417ee767b6SThomas Renninger return 0; 1427ee767b6SThomas Renninger } 1437ee767b6SThomas Renninger 1447ee767b6SThomas Renninger struct cpuidle_monitor intel_hsw_ext_monitor; 1457ee767b6SThomas Renninger 1467ee767b6SThomas Renninger static struct cpuidle_monitor *hsw_ext_register(void) 1477ee767b6SThomas Renninger { 1487ee767b6SThomas Renninger int num; 1497ee767b6SThomas Renninger 1507ee767b6SThomas Renninger if (cpupower_cpu_info.vendor != X86_VENDOR_INTEL 1517ee767b6SThomas Renninger || cpupower_cpu_info.family != 6) 1527ee767b6SThomas Renninger return NULL; 1537ee767b6SThomas Renninger 1547ee767b6SThomas Renninger switch (cpupower_cpu_info.model) { 1557ee767b6SThomas Renninger case 0x45: /* HSW */ 1567ee767b6SThomas Renninger break; 1577ee767b6SThomas Renninger default: 1587ee767b6SThomas Renninger return NULL; 1597ee767b6SThomas Renninger } 1607ee767b6SThomas Renninger 1617ee767b6SThomas Renninger is_valid = calloc(cpu_count, sizeof(int)); 1627ee767b6SThomas Renninger for (num = 0; num < HSW_EXT_CSTATE_COUNT; num++) { 1637ee767b6SThomas Renninger previous_count[num] = calloc(cpu_count, 1647ee767b6SThomas Renninger sizeof(unsigned long long)); 1657ee767b6SThomas Renninger current_count[num] = calloc(cpu_count, 1667ee767b6SThomas Renninger sizeof(unsigned long long)); 1677ee767b6SThomas Renninger } 1687ee767b6SThomas Renninger intel_hsw_ext_monitor.name_len = strlen(intel_hsw_ext_monitor.name); 1697ee767b6SThomas Renninger return &intel_hsw_ext_monitor; 1707ee767b6SThomas Renninger } 1717ee767b6SThomas Renninger 1727ee767b6SThomas Renninger void hsw_ext_unregister(void) 1737ee767b6SThomas Renninger { 1747ee767b6SThomas Renninger int num; 1757ee767b6SThomas Renninger free(is_valid); 1767ee767b6SThomas Renninger for (num = 0; num < HSW_EXT_CSTATE_COUNT; num++) { 1777ee767b6SThomas Renninger free(previous_count[num]); 1787ee767b6SThomas Renninger free(current_count[num]); 1797ee767b6SThomas Renninger } 1807ee767b6SThomas Renninger } 1817ee767b6SThomas Renninger 1827ee767b6SThomas Renninger struct cpuidle_monitor intel_hsw_ext_monitor = { 1837ee767b6SThomas Renninger .name = "HaswellExtended", 1847ee767b6SThomas Renninger .hw_states = hsw_ext_cstates, 1857ee767b6SThomas Renninger .hw_states_num = HSW_EXT_CSTATE_COUNT, 1867ee767b6SThomas Renninger .start = hsw_ext_start, 1877ee767b6SThomas Renninger .stop = hsw_ext_stop, 1887ee767b6SThomas Renninger .do_register = hsw_ext_register, 1897ee767b6SThomas Renninger .unregister = hsw_ext_unregister, 190d3f5d2a1SJanakarajan Natarajan .flags.needs_root = 1, 1917ee767b6SThomas Renninger .overflow_s = 922000000 /* 922337203 seconds TSC overflow 1927ee767b6SThomas Renninger at 20GHz */ 1937ee767b6SThomas Renninger }; 1947ee767b6SThomas Renninger #endif /* defined(__i386__) || defined(__x86_64__) */ 195