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 
hsw_ext_get_count(enum intel_hsw_ext_id id,unsigned long long * val,unsigned int cpu)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;
827b0bf99bSZou Wei 	}
837ee767b6SThomas Renninger 	if (read_msr(cpu, msr, val))
847ee767b6SThomas Renninger 		return -1;
857ee767b6SThomas Renninger 	return 0;
867ee767b6SThomas Renninger }
877ee767b6SThomas Renninger 
hsw_ext_get_count_percent(unsigned int id,double * percent,unsigned int cpu)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 
hsw_ext_start(void)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 
hsw_ext_stop(void)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 
hsw_ext_register(void)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 
hsw_ext_unregister(void)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