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