summaryrefslogtreecommitdiff
path: root/src/cpu/intel/turbo/turbo.c
blob: d0b49416d421fbb6c0f2bb7da740c738e2b1f507 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
/*
 * This file is part of the coreboot project.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; version 2 of
 * the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include <console/console.h>
#include <cpu/intel/turbo.h>
#include <cpu/x86/msr.h>
#include <arch/cpu.h>

#if CONFIG(CPU_INTEL_TURBO_NOT_PACKAGE_SCOPED)
static inline int get_global_turbo_state(void)
{
	return TURBO_UNKNOWN;
}

static inline void set_global_turbo_state(int state)
{
}
#else
static int g_turbo_state = TURBO_UNKNOWN;

static inline int get_global_turbo_state(void)
{
	return g_turbo_state;
}

static inline void set_global_turbo_state(int state)
{
	g_turbo_state = state;
}
#endif

static const char *const turbo_state_desc[] = {
	[TURBO_UNKNOWN]		= "unknown",
	[TURBO_UNAVAILABLE]	= "unavailable",
	[TURBO_DISABLED]	= "available but hidden",
	[TURBO_ENABLED]		= "available and visible"
};

/*
 * Try to update the global Turbo state.
 */
static int update_turbo_state(void)
{
	struct cpuid_result cpuid_regs;
	int turbo_en, turbo_cap;
	msr_t msr;
	int turbo_state = get_global_turbo_state();

	cpuid_regs = cpuid(CPUID_LEAF_PM);
	turbo_cap = !!(cpuid_regs.eax & PM_CAP_TURBO_MODE);

	msr = rdmsr(IA32_MISC_ENABLE);
	turbo_en = !(msr.hi & H_MISC_DISABLE_TURBO);

	if (!turbo_cap && turbo_en) {
		/* Unavailable */
		turbo_state = TURBO_UNAVAILABLE;
	} else if (!turbo_cap && !turbo_en) {
		/* Available but disabled */
		turbo_state = TURBO_DISABLED;
	} else if (turbo_cap && turbo_en) {
		/* Available */
		turbo_state = TURBO_ENABLED;
	}

	set_global_turbo_state(turbo_state);
	printk(BIOS_INFO, "Turbo is %s\n", turbo_state_desc[turbo_state]);

	return turbo_state;
}

/*
 * Determine the current state of Turbo and cache it for later. Turbo is package
 * level config so it does not need to be enabled on every core.
 */
int get_turbo_state(void)
{
	int turbo_state = get_global_turbo_state();

	/* Return cached state if available */
	if (turbo_state == TURBO_UNKNOWN)
		turbo_state = update_turbo_state();

	return turbo_state;
}

/*
 * Try to enable Turbo mode.
 */
void enable_turbo(void)
{
	msr_t msr;

	/* Only possible if turbo is available but hidden */
	if (get_turbo_state() == TURBO_DISABLED) {
		/* Clear Turbo Disable bit in Misc Enables */
		msr = rdmsr(IA32_MISC_ENABLE);
		msr.hi &= ~H_MISC_DISABLE_TURBO;
		wrmsr(IA32_MISC_ENABLE, msr);

		/* Update cached turbo state */
		update_turbo_state();
	}
}

/*
 * Try to disable Turbo mode.
 */
void disable_turbo(void)
{
	msr_t msr;

	/* Only possible if turbo is available and visible */
	if (get_turbo_state() == TURBO_ENABLED) {
		/* Set Turbo Disable bit in Misc Enables */
		msr = rdmsr(IA32_MISC_ENABLE);
		msr.hi |= H_MISC_DISABLE_TURBO;
		wrmsr(IA32_MISC_ENABLE, msr);

		/* Update cached turbo state */
		update_turbo_state();
	}
}