14f19048fSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
27fe2f639SDominik Brodowski /*
37fe2f639SDominik Brodowski * (C) 2010,2011 Thomas Renninger <trenn@suse.de>, Novell Inc.
47fe2f639SDominik Brodowski *
57fe2f639SDominik Brodowski * Based on Len Brown's <lenb@kernel.org> turbostat tool.
67fe2f639SDominik Brodowski */
77fe2f639SDominik Brodowski
87fe2f639SDominik Brodowski #if defined(__i386__) || defined(__x86_64__)
97fe2f639SDominik Brodowski
107fe2f639SDominik Brodowski #include <stdio.h>
117fe2f639SDominik Brodowski #include <stdint.h>
127fe2f639SDominik Brodowski #include <stdlib.h>
137fe2f639SDominik Brodowski #include <string.h>
147fe2f639SDominik Brodowski
157fe2f639SDominik Brodowski #include "helpers/helpers.h"
167fe2f639SDominik Brodowski #include "idle_monitor/cpupower-monitor.h"
177fe2f639SDominik Brodowski
187fe2f639SDominik Brodowski #define MSR_PKG_C2_RESIDENCY 0x60D
197fe2f639SDominik Brodowski #define MSR_PKG_C7_RESIDENCY 0x3FA
207fe2f639SDominik Brodowski #define MSR_CORE_C7_RESIDENCY 0x3FE
217fe2f639SDominik Brodowski
227fe2f639SDominik Brodowski #define MSR_TSC 0x10
237fe2f639SDominik Brodowski
247fe2f639SDominik Brodowski enum intel_snb_id { C7 = 0, PC2, PC7, SNB_CSTATE_COUNT, TSC = 0xFFFF };
257fe2f639SDominik Brodowski
267fe2f639SDominik Brodowski static int snb_get_count_percent(unsigned int self_id, double *percent,
277fe2f639SDominik Brodowski unsigned int cpu);
287fe2f639SDominik Brodowski
297fe2f639SDominik Brodowski static cstate_t snb_cstates[SNB_CSTATE_COUNT] = {
307fe2f639SDominik Brodowski {
317fe2f639SDominik Brodowski .name = "C7",
327fe2f639SDominik Brodowski .desc = N_("Processor Core C7"),
337fe2f639SDominik Brodowski .id = C7,
347fe2f639SDominik Brodowski .range = RANGE_CORE,
357fe2f639SDominik Brodowski .get_count_percent = snb_get_count_percent,
367fe2f639SDominik Brodowski },
377fe2f639SDominik Brodowski {
387fe2f639SDominik Brodowski .name = "PC2",
397fe2f639SDominik Brodowski .desc = N_("Processor Package C2"),
407fe2f639SDominik Brodowski .id = PC2,
417fe2f639SDominik Brodowski .range = RANGE_PACKAGE,
427fe2f639SDominik Brodowski .get_count_percent = snb_get_count_percent,
437fe2f639SDominik Brodowski },
447fe2f639SDominik Brodowski {
457fe2f639SDominik Brodowski .name = "PC7",
467fe2f639SDominik Brodowski .desc = N_("Processor Package C7"),
477fe2f639SDominik Brodowski .id = PC7,
487fe2f639SDominik Brodowski .range = RANGE_PACKAGE,
497fe2f639SDominik Brodowski .get_count_percent = snb_get_count_percent,
507fe2f639SDominik Brodowski },
517fe2f639SDominik Brodowski };
527fe2f639SDominik Brodowski
537fe2f639SDominik Brodowski static unsigned long long tsc_at_measure_start;
547fe2f639SDominik Brodowski static unsigned long long tsc_at_measure_end;
557fe2f639SDominik Brodowski static unsigned long long *previous_count[SNB_CSTATE_COUNT];
567fe2f639SDominik Brodowski static unsigned long long *current_count[SNB_CSTATE_COUNT];
577fe2f639SDominik Brodowski /* valid flag for all CPUs. If a MSR read failed it will be zero */
587fe2f639SDominik Brodowski static int *is_valid;
597fe2f639SDominik Brodowski
snb_get_count(enum intel_snb_id id,unsigned long long * val,unsigned int cpu)60b510b541SDominik Brodowski static int snb_get_count(enum intel_snb_id id, unsigned long long *val,
61b510b541SDominik Brodowski unsigned int cpu)
627fe2f639SDominik Brodowski {
637fe2f639SDominik Brodowski int msr;
647fe2f639SDominik Brodowski
657fe2f639SDominik Brodowski switch (id) {
667fe2f639SDominik Brodowski case C7:
677fe2f639SDominik Brodowski msr = MSR_CORE_C7_RESIDENCY;
687fe2f639SDominik Brodowski break;
697fe2f639SDominik Brodowski case PC2:
707fe2f639SDominik Brodowski msr = MSR_PKG_C2_RESIDENCY;
717fe2f639SDominik Brodowski break;
727fe2f639SDominik Brodowski case PC7:
737fe2f639SDominik Brodowski msr = MSR_PKG_C7_RESIDENCY;
747fe2f639SDominik Brodowski break;
757fe2f639SDominik Brodowski case TSC:
767fe2f639SDominik Brodowski msr = MSR_TSC;
777fe2f639SDominik Brodowski break;
787fe2f639SDominik Brodowski default:
797fe2f639SDominik Brodowski return -1;
807b0bf99bSZou Wei }
817fe2f639SDominik Brodowski if (read_msr(cpu, msr, val))
827fe2f639SDominik Brodowski return -1;
837fe2f639SDominik Brodowski return 0;
847fe2f639SDominik Brodowski }
857fe2f639SDominik Brodowski
snb_get_count_percent(unsigned int id,double * percent,unsigned int cpu)867fe2f639SDominik Brodowski static int snb_get_count_percent(unsigned int id, double *percent,
877fe2f639SDominik Brodowski unsigned int cpu)
887fe2f639SDominik Brodowski {
897fe2f639SDominik Brodowski *percent = 0.0;
907fe2f639SDominik Brodowski
917fe2f639SDominik Brodowski if (!is_valid[cpu])
927fe2f639SDominik Brodowski return -1;
937fe2f639SDominik Brodowski
94b510b541SDominik Brodowski *percent = (100.0 *
95b510b541SDominik Brodowski (current_count[id][cpu] - previous_count[id][cpu])) /
967fe2f639SDominik Brodowski (tsc_at_measure_end - tsc_at_measure_start);
977fe2f639SDominik Brodowski
98b510b541SDominik Brodowski dprint("%s: previous: %llu - current: %llu - (%u)\n",
99b510b541SDominik Brodowski snb_cstates[id].name, previous_count[id][cpu],
100b510b541SDominik Brodowski current_count[id][cpu], cpu);
1017fe2f639SDominik Brodowski
1027fe2f639SDominik Brodowski dprint("%s: tsc_diff: %llu - count_diff: %llu - percent: %2.f (%u)\n",
1037fe2f639SDominik Brodowski snb_cstates[id].name,
1047fe2f639SDominik Brodowski (unsigned long long) tsc_at_measure_end - tsc_at_measure_start,
105b510b541SDominik Brodowski current_count[id][cpu] - previous_count[id][cpu],
1067fe2f639SDominik Brodowski *percent, cpu);
1077fe2f639SDominik Brodowski
1087fe2f639SDominik Brodowski return 0;
1097fe2f639SDominik Brodowski }
1107fe2f639SDominik Brodowski
snb_start(void)1117fe2f639SDominik Brodowski static int snb_start(void)
1127fe2f639SDominik Brodowski {
1137fe2f639SDominik Brodowski int num, cpu;
1147fe2f639SDominik Brodowski unsigned long long val;
1157fe2f639SDominik Brodowski
1167fe2f639SDominik Brodowski for (num = 0; num < SNB_CSTATE_COUNT; num++) {
1177fe2f639SDominik Brodowski for (cpu = 0; cpu < cpu_count; cpu++) {
1187fe2f639SDominik Brodowski snb_get_count(num, &val, cpu);
1197fe2f639SDominik Brodowski previous_count[num][cpu] = val;
1207fe2f639SDominik Brodowski }
1217fe2f639SDominik Brodowski }
122d0e4a193SPrarit Bhargava snb_get_count(TSC, &tsc_at_measure_start, base_cpu);
1237fe2f639SDominik Brodowski return 0;
1247fe2f639SDominik Brodowski }
1257fe2f639SDominik Brodowski
snb_stop(void)1267fe2f639SDominik Brodowski static int snb_stop(void)
1277fe2f639SDominik Brodowski {
1287fe2f639SDominik Brodowski unsigned long long val;
1297fe2f639SDominik Brodowski int num, cpu;
1307fe2f639SDominik Brodowski
131d0e4a193SPrarit Bhargava snb_get_count(TSC, &tsc_at_measure_end, base_cpu);
1327fe2f639SDominik Brodowski
1337fe2f639SDominik Brodowski for (num = 0; num < SNB_CSTATE_COUNT; num++) {
1347fe2f639SDominik Brodowski for (cpu = 0; cpu < cpu_count; cpu++) {
1357fe2f639SDominik Brodowski is_valid[cpu] = !snb_get_count(num, &val, cpu);
1367fe2f639SDominik Brodowski current_count[num][cpu] = val;
1377fe2f639SDominik Brodowski }
1387fe2f639SDominik Brodowski }
1397fe2f639SDominik Brodowski return 0;
1407fe2f639SDominik Brodowski }
1417fe2f639SDominik Brodowski
1427fe2f639SDominik Brodowski struct cpuidle_monitor intel_snb_monitor;
1437fe2f639SDominik Brodowski
snb_register(void)144b510b541SDominik Brodowski static struct cpuidle_monitor *snb_register(void)
145b510b541SDominik Brodowski {
1467fe2f639SDominik Brodowski int num;
1477fe2f639SDominik Brodowski
1487fe2f639SDominik Brodowski if (cpupower_cpu_info.vendor != X86_VENDOR_INTEL
1497fe2f639SDominik Brodowski || cpupower_cpu_info.family != 6)
1507fe2f639SDominik Brodowski return NULL;
1517fe2f639SDominik Brodowski
1528d219e36SThomas Renninger switch (cpupower_cpu_info.model) {
1538d219e36SThomas Renninger case 0x2A: /* SNB */
1548d219e36SThomas Renninger case 0x2D: /* SNB Xeon */
1558d219e36SThomas Renninger case 0x3A: /* IVB */
1568d219e36SThomas Renninger case 0x3E: /* IVB Xeon */
1572aa1ca75SThomas Renninger case 0x3C: /* HSW */
1582aa1ca75SThomas Renninger case 0x3F: /* HSW */
1592aa1ca75SThomas Renninger case 0x45: /* HSW */
1602aa1ca75SThomas Renninger case 0x46: /* HSW */
1618d219e36SThomas Renninger break;
1628d219e36SThomas Renninger default:
1637fe2f639SDominik Brodowski return NULL;
1648d219e36SThomas Renninger }
1657fe2f639SDominik Brodowski
1667fe2f639SDominik Brodowski is_valid = calloc(cpu_count, sizeof(int));
1677fe2f639SDominik Brodowski for (num = 0; num < SNB_CSTATE_COUNT; num++) {
1687fe2f639SDominik Brodowski previous_count[num] = calloc(cpu_count,
1697fe2f639SDominik Brodowski sizeof(unsigned long long));
1707fe2f639SDominik Brodowski current_count[num] = calloc(cpu_count,
1717fe2f639SDominik Brodowski sizeof(unsigned long long));
1727fe2f639SDominik Brodowski }
1737fe2f639SDominik Brodowski intel_snb_monitor.name_len = strlen(intel_snb_monitor.name);
1747fe2f639SDominik Brodowski return &intel_snb_monitor;
1757fe2f639SDominik Brodowski }
1767fe2f639SDominik Brodowski
snb_unregister(void)1777fe2f639SDominik Brodowski void snb_unregister(void)
1787fe2f639SDominik Brodowski {
1797fe2f639SDominik Brodowski int num;
1807fe2f639SDominik Brodowski free(is_valid);
1817fe2f639SDominik Brodowski for (num = 0; num < SNB_CSTATE_COUNT; num++) {
1827fe2f639SDominik Brodowski free(previous_count[num]);
1837fe2f639SDominik Brodowski free(current_count[num]);
1847fe2f639SDominik Brodowski }
1857fe2f639SDominik Brodowski }
1867fe2f639SDominik Brodowski
1877fe2f639SDominik Brodowski struct cpuidle_monitor intel_snb_monitor = {
1887fe2f639SDominik Brodowski .name = "SandyBridge",
1897fe2f639SDominik Brodowski .hw_states = snb_cstates,
1907fe2f639SDominik Brodowski .hw_states_num = SNB_CSTATE_COUNT,
1917fe2f639SDominik Brodowski .start = snb_start,
1927fe2f639SDominik Brodowski .stop = snb_stop,
1937fe2f639SDominik Brodowski .do_register = snb_register,
1947fe2f639SDominik Brodowski .unregister = snb_unregister,
195d3f5d2a1SJanakarajan Natarajan .flags.needs_root = 1,
1967fe2f639SDominik Brodowski .overflow_s = 922000000 /* 922337203 seconds TSC overflow
1977fe2f639SDominik Brodowski at 20GHz */
1987fe2f639SDominik Brodowski };
1997fe2f639SDominik Brodowski #endif /* defined(__i386__) || defined(__x86_64__) */
200