xref: /illumos-gate/usr/src/uts/i86pc/os/cpupm/speedstep.c (revision 1ed6b69a5ca1ca3ee5e9a4931f74e2237c7e1c9f)
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 void
130 speedstep_pstate_transition(uint32_t req_state)
131 {
132 	cpupm_mach_state_t *mach_state =
133 	    (cpupm_mach_state_t *)CPU->cpu_m.mcpu_pm_mach_state;
134 	cpu_acpi_handle_t handle = mach_state->ms_acpi_handle;
135 	cpu_acpi_pstate_t *req_pstate;
136 	uint32_t ctrl;
137 
138 	req_pstate = (cpu_acpi_pstate_t *)CPU_ACPI_PSTATES(handle);
139 	req_pstate += req_state;
140 
141 	DTRACE_PROBE1(ess_transition, uint32_t, CPU_ACPI_FREQ(req_pstate));
142 
143 	/*
144 	 * Initiate the processor p-state change.
145 	 */
146 	ctrl = CPU_ACPI_PSTATE_CTRL(req_pstate);
147 	write_ctrl(handle, ctrl);
148 
149 	if (mach_state->ms_turbo != NULL)
150 		cpupm_record_turbo_info(mach_state->ms_turbo,
151 		    mach_state->ms_pstate.cma_state.pstate, req_state);
152 
153 	mach_state->ms_pstate.cma_state.pstate = req_state;
154 	cpu_set_curr_clock(((uint64_t)CPU_ACPI_FREQ(req_pstate) * 1000000));
155 }
156 
157 static void
158 speedstep_power(cpuset_t set, uint32_t req_state)
159 {
160 	/*
161 	 * If thread is already running on target CPU then just
162 	 * make the transition request. Otherwise, we'll need to
163 	 * make a cross-call.
164 	 */
165 	kpreempt_disable();
166 	if (CPU_IN_SET(set, CPU->cpu_id)) {
167 		speedstep_pstate_transition(req_state);
168 		CPUSET_DEL(set, CPU->cpu_id);
169 	}
170 	if (!CPUSET_ISNULL(set)) {
171 		xc_call((xc_arg_t)req_state, NULL, NULL, CPUSET2BV(set),
172 		    (xc_func_t)speedstep_pstate_transition);
173 	}
174 	kpreempt_enable();
175 }
176 
177 /*
178  * Validate that this processor supports Speedstep and if so,
179  * get the P-state data from ACPI and cache it.
180  */
181 static int
182 speedstep_init(cpu_t *cp)
183 {
184 	cpupm_mach_state_t *mach_state =
185 	    (cpupm_mach_state_t *)cp->cpu_m.mcpu_pm_mach_state;
186 	cpu_acpi_handle_t handle = mach_state->ms_acpi_handle;
187 	cpu_acpi_pct_t *pct_stat;
188 	static int logged = 0;
189 
190 	ESSDEBUG(("speedstep_init: processor %d\n", cp->cpu_id));
191 
192 	/*
193 	 * Cache the P-state specific ACPI data.
194 	 */
195 	if (cpu_acpi_cache_pstate_data(handle) != 0) {
196 		if (!logged) {
197 			cmn_err(CE_NOTE, "!SpeedStep support is being "
198 			    "disabled due to errors parsing ACPI P-state "
199 			    "objects exported by BIOS.");
200 			logged = 1;
201 		}
202 		speedstep_fini(cp);
203 		return (ESS_RET_NO_PM);
204 	}
205 
206 	pct_stat = CPU_ACPI_PCT_STATUS(handle);
207 	switch (pct_stat->cr_addrspace_id) {
208 	case ACPI_ADR_SPACE_FIXED_HARDWARE:
209 		ESSDEBUG(("Transitions will use fixed hardware\n"));
210 		break;
211 	case ACPI_ADR_SPACE_SYSTEM_IO:
212 		ESSDEBUG(("Transitions will use system IO\n"));
213 		break;
214 	default:
215 		cmn_err(CE_WARN, "!_PCT conifgured for unsupported "
216 		    "addrspace = %d.", pct_stat->cr_addrspace_id);
217 		cmn_err(CE_NOTE, "!CPU power management will not function.");
218 		speedstep_fini(cp);
219 		return (ESS_RET_NO_PM);
220 	}
221 
222 	cpupm_alloc_domains(cp, CPUPM_P_STATES);
223 
224 	if (speedstep_turbo_supported())
225 		mach_state->ms_turbo = cpupm_turbo_init(cp);
226 
227 	ESSDEBUG(("Processor %d succeeded.\n", cp->cpu_id))
228 	return (ESS_RET_SUCCESS);
229 }
230 
231 /*
232  * Free resources allocated by speedstep_init().
233  */
234 static void
235 speedstep_fini(cpu_t *cp)
236 {
237 	cpupm_mach_state_t *mach_state =
238 	    (cpupm_mach_state_t *)(cp->cpu_m.mcpu_pm_mach_state);
239 	cpu_acpi_handle_t handle = mach_state->ms_acpi_handle;
240 
241 	cpupm_free_domains(&cpupm_pstate_domains);
242 	cpu_acpi_free_pstate_data(handle);
243 
244 	if (mach_state->ms_turbo != NULL)
245 		cpupm_turbo_fini(mach_state->ms_turbo);
246 	mach_state->ms_turbo = NULL;
247 }
248 
249 static void
250 speedstep_stop(cpu_t *cp)
251 {
252 	cpupm_mach_state_t *mach_state =
253 	    (cpupm_mach_state_t *)(cp->cpu_m.mcpu_pm_mach_state);
254 	cpu_acpi_handle_t handle = mach_state->ms_acpi_handle;
255 
256 	cpupm_remove_domains(cp, CPUPM_P_STATES, &cpupm_pstate_domains);
257 	cpu_acpi_free_pstate_data(handle);
258 
259 	if (mach_state->ms_turbo != NULL)
260 		cpupm_turbo_fini(mach_state->ms_turbo);
261 	mach_state->ms_turbo = NULL;
262 }
263 
264 boolean_t
265 speedstep_supported(uint_t family, uint_t model)
266 {
267 	struct cpuid_regs cpu_regs;
268 
269 	/* Required features */
270 	if (!is_x86_feature(x86_featureset, X86FSET_CPUID) ||
271 	    !is_x86_feature(x86_featureset, X86FSET_MSR)) {
272 		return (B_FALSE);
273 	}
274 
275 	/*
276 	 * We only support family/model combinations which
277 	 * are P-state TSC invariant.
278 	 */
279 	if (!((family == 0xf && model >= 0x3) ||
280 	    (family == 0x6 && model >= 0xe))) {
281 		return (B_FALSE);
282 	}
283 
284 	/*
285 	 * Enhanced SpeedStep supported?
286 	 */
287 	cpu_regs.cp_eax = 0x1;
288 	(void) __cpuid_insn(&cpu_regs);
289 	if (!(cpu_regs.cp_ecx & CPUID_INTC_ECX_EST)) {
290 		return (B_FALSE);
291 	}
292 
293 	return (B_TRUE);
294 }
295 
296 boolean_t
297 speedstep_turbo_supported(void)
298 {
299 	struct cpuid_regs cpu_regs;
300 
301 	/* Required features */
302 	if (!is_x86_feature(x86_featureset, X86FSET_CPUID) ||
303 	    !is_x86_feature(x86_featureset, X86FSET_MSR)) {
304 		return (B_FALSE);
305 	}
306 
307 	/*
308 	 * turbo mode supported?
309 	 */
310 	cpu_regs.cp_eax = 0x6;
311 	(void) __cpuid_insn(&cpu_regs);
312 	if (!(cpu_regs.cp_eax & CPUID_TURBO_SUPPORT)) {
313 		return (B_FALSE);
314 	}
315 
316 	return (B_TRUE);
317 }
318