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