xref: /illumos-gate/usr/src/uts/i86pc/os/cpupm/speedstep.c (revision 698f4ab6008be205f4362675967638572eef4f21)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
23  */
24 /*
25  * Copyright (c) 2009,  Intel Corporation.
26  * All Rights Reserved.
27  */
28 
29 #include <sys/x86_archext.h>
30 #include <sys/machsystm.h>
31 #include <sys/archsystm.h>
32 #include <sys/x_call.h>
33 #include <sys/acpi/acpi.h>
34 #include <sys/acpica.h>
35 #include <sys/speedstep.h>
36 #include <sys/cpu_acpi.h>
37 #include <sys/cpupm.h>
38 #include <sys/dtrace.h>
39 #include <sys/sdt.h>
40 
41 static int speedstep_init(cpu_t *);
42 static void speedstep_fini(cpu_t *);
43 static void speedstep_power(cpuset_t, uint32_t);
44 static void speedstep_stop(cpu_t *);
45 static boolean_t speedstep_turbo_supported(void);
46 
47 /*
48  * Interfaces for modules implementing Intel's Enhanced SpeedStep.
49  */
50 cpupm_state_ops_t speedstep_ops = {
51 	"Enhanced SpeedStep Technology",
52 	speedstep_init,
53 	speedstep_fini,
54 	speedstep_power,
55 	speedstep_stop
56 };
57 
58 /*
59  * Error returns
60  */
61 #define	ESS_RET_SUCCESS		0x00
62 #define	ESS_RET_NO_PM		0x01
63 #define	ESS_RET_UNSUP_STATE	0x02
64 
65 /*
66  * MSR registers for changing and reading processor power state.
67  */
68 #define	IA32_PERF_STAT_MSR		0x198
69 #define	IA32_PERF_CTL_MSR		0x199
70 
71 #define	IA32_CPUID_TSC_CONSTANT		0xF30
72 #define	IA32_MISC_ENABLE_MSR		0x1A0
73 #define	IA32_MISC_ENABLE_EST		(1<<16)
74 #define	IA32_MISC_ENABLE_CXE		(1<<25)
75 
76 #define	CPUID_TURBO_SUPPORT		(1 << 1)
77 
78 /*
79  * Debugging support
80  */
81 #ifdef	DEBUG
82 volatile int ess_debug = 0;
83 #define	ESSDEBUG(arglist) if (ess_debug) printf arglist;
84 #else
85 #define	ESSDEBUG(arglist)
86 #endif
87 
88 /*
89  * Write the ctrl register. How it is written, depends upon the _PCT
90  * APCI object value.
91  */
92 static void
93 write_ctrl(cpu_acpi_handle_t handle, uint32_t ctrl)
94 {
95 	cpu_acpi_pct_t *pct_ctrl;
96 	uint64_t reg;
97 
98 	pct_ctrl = CPU_ACPI_PCT_CTRL(handle);
99 
100 	switch (pct_ctrl->cr_addrspace_id) {
101 	case ACPI_ADR_SPACE_FIXED_HARDWARE:
102 		/*
103 		 * Read current power state because reserved bits must be
104 		 * preserved, compose new value, and write it.
105 		 */
106 		reg = rdmsr(IA32_PERF_CTL_MSR);
107 		reg &= ~((uint64_t)0xFFFF);
108 		reg |= ctrl;
109 		wrmsr(IA32_PERF_CTL_MSR, reg);
110 		break;
111 
112 	case ACPI_ADR_SPACE_SYSTEM_IO:
113 		(void) cpu_acpi_write_port(pct_ctrl->cr_address, ctrl,
114 		    pct_ctrl->cr_width);
115 		break;
116 
117 	default:
118 		DTRACE_PROBE1(ess_ctrl_unsupported_type, uint8_t,
119 		    pct_ctrl->cr_addrspace_id);
120 		return;
121 	}
122 
123 	DTRACE_PROBE1(ess_ctrl_write, uint32_t, ctrl);
124 }
125 
126 /*
127  * Transition the current processor to the requested state.
128  */
129 int
130 speedstep_pstate_transition(xc_arg_t arg1, xc_arg_t arg2 __unused,
131     xc_arg_t arg3 __unused)
132 {
133 	uint32_t req_state = (uint32_t)arg1;
134 	cpupm_mach_state_t *mach_state =
135 	    (cpupm_mach_state_t *)CPU->cpu_m.mcpu_pm_mach_state;
136 	cpu_acpi_handle_t handle = mach_state->ms_acpi_handle;
137 	cpu_acpi_pstate_t *req_pstate;
138 	uint32_t ctrl;
139 
140 	req_pstate = (cpu_acpi_pstate_t *)CPU_ACPI_PSTATES(handle);
141 	req_pstate += req_state;
142 
143 	DTRACE_PROBE1(ess_transition, uint32_t, CPU_ACPI_FREQ(req_pstate));
144 
145 	/*
146 	 * Initiate the processor p-state change.
147 	 */
148 	ctrl = CPU_ACPI_PSTATE_CTRL(req_pstate);
149 	write_ctrl(handle, ctrl);
150 
151 	if (mach_state->ms_turbo != NULL)
152 		cpupm_record_turbo_info(mach_state->ms_turbo,
153 		    mach_state->ms_pstate.cma_state.pstate, req_state);
154 
155 	mach_state->ms_pstate.cma_state.pstate = req_state;
156 	cpu_set_curr_clock(((uint64_t)CPU_ACPI_FREQ(req_pstate) * 1000000));
157 	return (0);
158 }
159 
160 static void
161 speedstep_power(cpuset_t set, uint32_t req_state)
162 {
163 	/*
164 	 * If thread is already running on target CPU then just
165 	 * make the transition request. Otherwise, we'll need to
166 	 * make a cross-call.
167 	 */
168 	kpreempt_disable();
169 	if (CPU_IN_SET(set, CPU->cpu_id)) {
170 		(void) speedstep_pstate_transition(req_state, 0, 0);
171 		CPUSET_DEL(set, CPU->cpu_id);
172 	}
173 	if (!CPUSET_ISNULL(set)) {
174 		xc_call((xc_arg_t)req_state, 0, 0, CPUSET2BV(set),
175 		    speedstep_pstate_transition);
176 	}
177 	kpreempt_enable();
178 }
179 
180 /*
181  * Validate that this processor supports Speedstep and if so,
182  * get the P-state data from ACPI and cache it.
183  */
184 static int
185 speedstep_init(cpu_t *cp)
186 {
187 	cpupm_mach_state_t *mach_state =
188 	    (cpupm_mach_state_t *)cp->cpu_m.mcpu_pm_mach_state;
189 	cpu_acpi_handle_t handle = mach_state->ms_acpi_handle;
190 	cpu_acpi_pct_t *pct_stat;
191 	static int logged = 0;
192 
193 	ESSDEBUG(("speedstep_init: processor %d\n", cp->cpu_id));
194 
195 	/*
196 	 * Cache the P-state specific ACPI data.
197 	 */
198 	if (cpu_acpi_cache_pstate_data(handle) != 0) {
199 		if (!logged) {
200 			cmn_err(CE_NOTE, "!SpeedStep support is being "
201 			    "disabled due to errors parsing ACPI P-state "
202 			    "objects exported by BIOS.");
203 			logged = 1;
204 		}
205 		speedstep_fini(cp);
206 		return (ESS_RET_NO_PM);
207 	}
208 
209 	pct_stat = CPU_ACPI_PCT_STATUS(handle);
210 	switch (pct_stat->cr_addrspace_id) {
211 	case ACPI_ADR_SPACE_FIXED_HARDWARE:
212 		ESSDEBUG(("Transitions will use fixed hardware\n"));
213 		break;
214 	case ACPI_ADR_SPACE_SYSTEM_IO:
215 		ESSDEBUG(("Transitions will use system IO\n"));
216 		break;
217 	default:
218 		cmn_err(CE_WARN, "!_PCT conifgured for unsupported "
219 		    "addrspace = %d.", pct_stat->cr_addrspace_id);
220 		cmn_err(CE_NOTE, "!CPU power management will not function.");
221 		speedstep_fini(cp);
222 		return (ESS_RET_NO_PM);
223 	}
224 
225 	cpupm_alloc_domains(cp, CPUPM_P_STATES);
226 
227 	if (speedstep_turbo_supported())
228 		mach_state->ms_turbo = cpupm_turbo_init(cp);
229 
230 	ESSDEBUG(("Processor %d succeeded.\n", cp->cpu_id))
231 	return (ESS_RET_SUCCESS);
232 }
233 
234 /*
235  * Free resources allocated by speedstep_init().
236  */
237 static void
238 speedstep_fini(cpu_t *cp)
239 {
240 	cpupm_mach_state_t *mach_state =
241 	    (cpupm_mach_state_t *)(cp->cpu_m.mcpu_pm_mach_state);
242 	cpu_acpi_handle_t handle = mach_state->ms_acpi_handle;
243 
244 	cpupm_free_domains(&cpupm_pstate_domains);
245 	cpu_acpi_free_pstate_data(handle);
246 
247 	if (mach_state->ms_turbo != NULL)
248 		cpupm_turbo_fini(mach_state->ms_turbo);
249 	mach_state->ms_turbo = NULL;
250 }
251 
252 static void
253 speedstep_stop(cpu_t *cp)
254 {
255 	cpupm_mach_state_t *mach_state =
256 	    (cpupm_mach_state_t *)(cp->cpu_m.mcpu_pm_mach_state);
257 	cpu_acpi_handle_t handle = mach_state->ms_acpi_handle;
258 
259 	cpupm_remove_domains(cp, CPUPM_P_STATES, &cpupm_pstate_domains);
260 	cpu_acpi_free_pstate_data(handle);
261 
262 	if (mach_state->ms_turbo != NULL)
263 		cpupm_turbo_fini(mach_state->ms_turbo);
264 	mach_state->ms_turbo = NULL;
265 }
266 
267 boolean_t
268 speedstep_supported(uint_t family, uint_t model)
269 {
270 	struct cpuid_regs cpu_regs;
271 
272 	/* Required features */
273 	if (!is_x86_feature(x86_featureset, X86FSET_CPUID) ||
274 	    !is_x86_feature(x86_featureset, X86FSET_MSR)) {
275 		return (B_FALSE);
276 	}
277 
278 	/*
279 	 * We only support family/model combinations which
280 	 * are P-state TSC invariant.
281 	 */
282 	if (!((family == 0xf && model >= 0x3) ||
283 	    (family == 0x6 && model >= 0xe))) {
284 		return (B_FALSE);
285 	}
286 
287 	/*
288 	 * Enhanced SpeedStep supported?
289 	 */
290 	cpu_regs.cp_eax = 0x1;
291 	(void) __cpuid_insn(&cpu_regs);
292 	if (!(cpu_regs.cp_ecx & CPUID_INTC_ECX_EST)) {
293 		return (B_FALSE);
294 	}
295 
296 	return (B_TRUE);
297 }
298 
299 boolean_t
300 speedstep_turbo_supported(void)
301 {
302 	struct cpuid_regs cpu_regs;
303 
304 	/* Required features */
305 	if (!is_x86_feature(x86_featureset, X86FSET_CPUID) ||
306 	    !is_x86_feature(x86_featureset, X86FSET_MSR)) {
307 		return (B_FALSE);
308 	}
309 
310 	/*
311 	 * turbo mode supported?
312 	 */
313 	cpu_regs.cp_eax = 0x6;
314 	(void) __cpuid_insn(&cpu_regs);
315 	if (!(cpu_regs.cp_eax & CPUID_TURBO_SUPPORT)) {
316 		return (B_FALSE);
317 	}
318 
319 	return (B_TRUE);
320 }
321