xref: /illumos-gate/usr/src/uts/i86pc/os/cpupm/speedstep.c (revision 7417cfdecea1902cef03c0d61a72df97d945925d)
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 /*
42  * turbo related structure definitions
43  */
44 typedef struct cpupm_turbo_info {
45 	kstat_t		*turbo_ksp;		/* turbo kstat */
46 	int		in_turbo;		/* in turbo? */
47 	int		turbo_supported;	/* turbo flag */
48 	uint64_t	t_mcnt;			/* turbo mcnt */
49 	uint64_t	t_acnt;			/* turbo acnt */
50 } cpupm_turbo_info_t;
51 
52 typedef struct turbo_kstat_s {
53 	struct kstat_named	turbo_supported;	/* turbo flag */
54 	struct kstat_named	t_mcnt;			/* IA32_MPERF_MSR */
55 	struct kstat_named	t_acnt;			/* IA32_APERF_MSR */
56 } turbo_kstat_t;
57 
58 static int speedstep_init(cpu_t *);
59 static void speedstep_fini(cpu_t *);
60 static void speedstep_power(cpuset_t, uint32_t);
61 static void speedstep_stop(cpu_t *);
62 static boolean_t turbo_supported(void);
63 static int turbo_kstat_update(kstat_t *, int);
64 static void get_turbo_info(cpupm_turbo_info_t *);
65 static void reset_turbo_info(void);
66 static void record_turbo_info(cpupm_turbo_info_t *, uint32_t, uint32_t);
67 static void update_turbo_info(cpupm_turbo_info_t *);
68 
69 /*
70  * Interfaces for modules implementing Intel's Enhanced SpeedStep.
71  */
72 cpupm_state_ops_t speedstep_ops = {
73 	"Enhanced SpeedStep Technology",
74 	speedstep_init,
75 	speedstep_fini,
76 	speedstep_power,
77 	speedstep_stop
78 };
79 
80 /*
81  * Error returns
82  */
83 #define	ESS_RET_SUCCESS		0x00
84 #define	ESS_RET_NO_PM		0x01
85 #define	ESS_RET_UNSUP_STATE	0x02
86 
87 /*
88  * MSR registers for changing and reading processor power state.
89  */
90 #define	IA32_PERF_STAT_MSR		0x198
91 #define	IA32_PERF_CTL_MSR		0x199
92 
93 #define	IA32_CPUID_TSC_CONSTANT		0xF30
94 #define	IA32_MISC_ENABLE_MSR		0x1A0
95 #define	IA32_MISC_ENABLE_EST		(1<<16)
96 #define	IA32_MISC_ENABLE_CXE		(1<<25)
97 
98 #define	CPUID_TURBO_SUPPORT		(1 << 1)
99 #define	CPU_ACPI_P0			0
100 #define	CPU_IN_TURBO			1
101 
102 /*
103  * MSR for hardware coordination feedback mechanism
104  *   - IA32_MPERF: increments in proportion to a fixed frequency
105  *   - IA32_APERF: increments in proportion to actual performance
106  */
107 #define	IA32_MPERF_MSR			0xE7
108 #define	IA32_APERF_MSR			0xE8
109 
110 /*
111  * Debugging support
112  */
113 #ifdef	DEBUG
114 volatile int ess_debug = 0;
115 #define	ESSDEBUG(arglist) if (ess_debug) printf arglist;
116 #else
117 #define	ESSDEBUG(arglist)
118 #endif
119 
120 static kmutex_t turbo_mutex;
121 
122 turbo_kstat_t turbo_kstat = {
123 	{ "turbo_supported",	KSTAT_DATA_UINT32 },
124 	{ "turbo_mcnt",		KSTAT_DATA_UINT64 },
125 	{ "turbo_acnt",		KSTAT_DATA_UINT64 },
126 };
127 
128 /*
129  * kstat update function of the turbo mode info
130  */
131 static int
132 turbo_kstat_update(kstat_t *ksp, int flag)
133 {
134 	cpupm_turbo_info_t *turbo_info = ksp->ks_private;
135 
136 	if (flag == KSTAT_WRITE) {
137 		return (EACCES);
138 	}
139 
140 	/*
141 	 * update the count in case CPU is in the turbo
142 	 * mode for a long time
143 	 */
144 	if (turbo_info->in_turbo == CPU_IN_TURBO)
145 		update_turbo_info(turbo_info);
146 
147 	turbo_kstat.turbo_supported.value.ui32 =
148 	    turbo_info->turbo_supported;
149 	turbo_kstat.t_mcnt.value.ui64 = turbo_info->t_mcnt;
150 	turbo_kstat.t_acnt.value.ui64 = turbo_info->t_acnt;
151 
152 	return (0);
153 }
154 
155 /*
156  * Get count of MPERF/APERF MSR
157  */
158 static void
159 get_turbo_info(cpupm_turbo_info_t *turbo_info)
160 {
161 	ulong_t		iflag;
162 	uint64_t	mcnt, acnt;
163 
164 	iflag = intr_clear();
165 	mcnt = rdmsr(IA32_MPERF_MSR);
166 	acnt = rdmsr(IA32_APERF_MSR);
167 	turbo_info->t_mcnt += mcnt;
168 	turbo_info->t_acnt += acnt;
169 	intr_restore(iflag);
170 }
171 
172 /*
173  * Clear MPERF/APERF MSR
174  */
175 static void
176 reset_turbo_info(void)
177 {
178 	ulong_t		iflag;
179 
180 	iflag = intr_clear();
181 	wrmsr(IA32_MPERF_MSR, 0);
182 	wrmsr(IA32_APERF_MSR, 0);
183 	intr_restore(iflag);
184 }
185 
186 /*
187  * sum up the count of one CPU_ACPI_P0 transition
188  */
189 static void
190 record_turbo_info(cpupm_turbo_info_t *turbo_info,
191     uint32_t cur_state, uint32_t req_state)
192 {
193 	if (!turbo_info->turbo_supported)
194 		return;
195 	/*
196 	 * enter P0 state
197 	 */
198 	if (req_state == CPU_ACPI_P0) {
199 		reset_turbo_info();
200 		turbo_info->in_turbo = CPU_IN_TURBO;
201 	}
202 	/*
203 	 * Leave P0 state
204 	 */
205 	else if (cur_state == CPU_ACPI_P0) {
206 		turbo_info->in_turbo = 0;
207 		get_turbo_info(turbo_info);
208 	}
209 }
210 
211 /*
212  * update the sum of counts and clear MSRs
213  */
214 static void
215 update_turbo_info(cpupm_turbo_info_t *turbo_info)
216 {
217 	ulong_t		iflag;
218 	uint64_t	mcnt, acnt;
219 
220 	iflag = intr_clear();
221 	mcnt = rdmsr(IA32_MPERF_MSR);
222 	acnt = rdmsr(IA32_APERF_MSR);
223 	wrmsr(IA32_MPERF_MSR, 0);
224 	wrmsr(IA32_APERF_MSR, 0);
225 	turbo_info->t_mcnt += mcnt;
226 	turbo_info->t_acnt += acnt;
227 	intr_restore(iflag);
228 }
229 
230 /*
231  * Write the ctrl register. How it is written, depends upon the _PCT
232  * APCI object value.
233  */
234 static void
235 write_ctrl(cpu_acpi_handle_t handle, uint32_t ctrl)
236 {
237 	cpu_acpi_pct_t *pct_ctrl;
238 	uint64_t reg;
239 
240 	pct_ctrl = CPU_ACPI_PCT_CTRL(handle);
241 
242 	switch (pct_ctrl->cr_addrspace_id) {
243 	case ACPI_ADR_SPACE_FIXED_HARDWARE:
244 		/*
245 		 * Read current power state because reserved bits must be
246 		 * preserved, compose new value, and write it.
247 		 */
248 		reg = rdmsr(IA32_PERF_CTL_MSR);
249 		reg &= ~((uint64_t)0xFFFF);
250 		reg |= ctrl;
251 		wrmsr(IA32_PERF_CTL_MSR, reg);
252 		break;
253 
254 	case ACPI_ADR_SPACE_SYSTEM_IO:
255 		(void) cpu_acpi_write_port(pct_ctrl->cr_address, ctrl,
256 		    pct_ctrl->cr_width);
257 		break;
258 
259 	default:
260 		DTRACE_PROBE1(ess_ctrl_unsupported_type, uint8_t,
261 		    pct_ctrl->cr_addrspace_id);
262 		return;
263 	}
264 
265 	DTRACE_PROBE1(ess_ctrl_write, uint32_t, ctrl);
266 }
267 
268 /*
269  * Transition the current processor to the requested state.
270  */
271 void
272 speedstep_pstate_transition(uint32_t req_state)
273 {
274 	cpupm_mach_state_t *mach_state =
275 	    (cpupm_mach_state_t *)CPU->cpu_m.mcpu_pm_mach_state;
276 	cpu_acpi_handle_t handle = mach_state->ms_acpi_handle;
277 	cpu_acpi_pstate_t *req_pstate;
278 	uint32_t ctrl;
279 	cpupm_turbo_info_t *turbo_info =
280 	    (cpupm_turbo_info_t *)(mach_state->ms_vendor);
281 
282 	req_pstate = (cpu_acpi_pstate_t *)CPU_ACPI_PSTATES(handle);
283 	req_pstate += req_state;
284 
285 	DTRACE_PROBE1(ess_transition, uint32_t, CPU_ACPI_FREQ(req_pstate));
286 
287 	/*
288 	 * Initiate the processor p-state change.
289 	 */
290 	ctrl = CPU_ACPI_PSTATE_CTRL(req_pstate);
291 	write_ctrl(handle, ctrl);
292 
293 	if (turbo_info)
294 		record_turbo_info(turbo_info,
295 		    mach_state->ms_pstate.cma_state.pstate, req_state);
296 
297 
298 	mach_state->ms_pstate.cma_state.pstate = req_state;
299 	cpu_set_curr_clock(((uint64_t)CPU_ACPI_FREQ(req_pstate) * 1000000));
300 }
301 
302 static void
303 speedstep_power(cpuset_t set, uint32_t req_state)
304 {
305 	/*
306 	 * If thread is already running on target CPU then just
307 	 * make the transition request. Otherwise, we'll need to
308 	 * make a cross-call.
309 	 */
310 	kpreempt_disable();
311 	if (CPU_IN_SET(set, CPU->cpu_id)) {
312 		speedstep_pstate_transition(req_state);
313 		CPUSET_DEL(set, CPU->cpu_id);
314 	}
315 	if (!CPUSET_ISNULL(set)) {
316 		xc_call((xc_arg_t)req_state, NULL, NULL, CPUSET2BV(set),
317 		    (xc_func_t)speedstep_pstate_transition);
318 	}
319 	kpreempt_enable();
320 }
321 
322 /*
323  * Validate that this processor supports Speedstep and if so,
324  * get the P-state data from ACPI and cache it.
325  */
326 static int
327 speedstep_init(cpu_t *cp)
328 {
329 	cpupm_mach_state_t *mach_state =
330 	    (cpupm_mach_state_t *)cp->cpu_m.mcpu_pm_mach_state;
331 	cpu_acpi_handle_t handle = mach_state->ms_acpi_handle;
332 	cpu_acpi_pct_t *pct_stat;
333 	cpupm_turbo_info_t *turbo_info;
334 
335 	ESSDEBUG(("speedstep_init: processor %d\n", cp->cpu_id));
336 
337 	/*
338 	 * Cache the P-state specific ACPI data.
339 	 */
340 	if (cpu_acpi_cache_pstate_data(handle) != 0) {
341 		cmn_err(CE_NOTE, "!SpeedStep support is being "
342 		    "disabled due to errors parsing ACPI P-state objects "
343 		    "exported by BIOS.");
344 		speedstep_fini(cp);
345 		return (ESS_RET_NO_PM);
346 	}
347 
348 	pct_stat = CPU_ACPI_PCT_STATUS(handle);
349 	switch (pct_stat->cr_addrspace_id) {
350 	case ACPI_ADR_SPACE_FIXED_HARDWARE:
351 		ESSDEBUG(("Transitions will use fixed hardware\n"));
352 		break;
353 	case ACPI_ADR_SPACE_SYSTEM_IO:
354 		ESSDEBUG(("Transitions will use system IO\n"));
355 		break;
356 	default:
357 		cmn_err(CE_WARN, "!_PCT conifgured for unsupported "
358 		    "addrspace = %d.", pct_stat->cr_addrspace_id);
359 		cmn_err(CE_NOTE, "!CPU power management will not function.");
360 		speedstep_fini(cp);
361 		return (ESS_RET_NO_PM);
362 	}
363 
364 	cpupm_alloc_domains(cp, CPUPM_P_STATES);
365 
366 	if (!turbo_supported()) {
367 		mach_state->ms_vendor = NULL;
368 		goto ess_ret_success;
369 	}
370 	/*
371 	 * turbo mode supported
372 	 */
373 	turbo_info = mach_state->ms_vendor =
374 	    kmem_zalloc(sizeof (cpupm_turbo_info_t), KM_SLEEP);
375 	turbo_info->turbo_supported = 1;
376 	turbo_info->turbo_ksp = kstat_create("turbo", cp->cpu_id,
377 	    "turbo", "misc", KSTAT_TYPE_NAMED,
378 	    sizeof (turbo_kstat) / sizeof (kstat_named_t),
379 	    KSTAT_FLAG_VIRTUAL);
380 
381 	if (turbo_info->turbo_ksp == NULL) {
382 		cmn_err(CE_NOTE, "kstat_create(turbo) fail");
383 	} else {
384 		turbo_info->turbo_ksp->ks_data = &turbo_kstat;
385 		turbo_info->turbo_ksp->ks_lock = &turbo_mutex;
386 		turbo_info->turbo_ksp->ks_update = turbo_kstat_update;
387 		turbo_info->turbo_ksp->ks_data_size += MAXNAMELEN;
388 		turbo_info->turbo_ksp->ks_private = turbo_info;
389 
390 		kstat_install(turbo_info->turbo_ksp);
391 	}
392 
393 ess_ret_success:
394 
395 	ESSDEBUG(("Processor %d succeeded.\n", cp->cpu_id))
396 	return (ESS_RET_SUCCESS);
397 }
398 
399 /*
400  * Free resources allocated by speedstep_init().
401  */
402 static void
403 speedstep_fini(cpu_t *cp)
404 {
405 	cpupm_mach_state_t *mach_state =
406 	    (cpupm_mach_state_t *)(cp->cpu_m.mcpu_pm_mach_state);
407 	cpu_acpi_handle_t handle = mach_state->ms_acpi_handle;
408 	cpupm_turbo_info_t *turbo_info =
409 	    (cpupm_turbo_info_t *)(mach_state->ms_vendor);
410 
411 	cpupm_free_domains(&cpupm_pstate_domains);
412 	cpu_acpi_free_pstate_data(handle);
413 
414 	if (turbo_info) {
415 		if (turbo_info->turbo_ksp != NULL)
416 			kstat_delete(turbo_info->turbo_ksp);
417 		kmem_free(turbo_info, sizeof (cpupm_turbo_info_t));
418 	}
419 }
420 
421 static void
422 speedstep_stop(cpu_t *cp)
423 {
424 	cpupm_mach_state_t *mach_state =
425 	    (cpupm_mach_state_t *)(cp->cpu_m.mcpu_pm_mach_state);
426 	cpu_acpi_handle_t handle = mach_state->ms_acpi_handle;
427 	cpupm_turbo_info_t *turbo_info =
428 	    (cpupm_turbo_info_t *)(mach_state->ms_vendor);
429 
430 	cpupm_remove_domains(cp, CPUPM_P_STATES, &cpupm_pstate_domains);
431 	cpu_acpi_free_pstate_data(handle);
432 
433 	if (turbo_info) {
434 		if (turbo_info->turbo_ksp != NULL)
435 			kstat_delete(turbo_info->turbo_ksp);
436 		kmem_free(turbo_info, sizeof (cpupm_turbo_info_t));
437 	}
438 }
439 
440 boolean_t
441 speedstep_supported(uint_t family, uint_t model)
442 {
443 	struct cpuid_regs cpu_regs;
444 
445 	/* Required features */
446 	if (!is_x86_feature(x86_featureset, X86FSET_CPUID) ||
447 	    !is_x86_feature(x86_featureset, X86FSET_MSR)) {
448 		return (B_FALSE);
449 	}
450 
451 	/*
452 	 * We only support family/model combinations which
453 	 * are P-state TSC invariant.
454 	 */
455 	if (!((family == 0xf && model >= 0x3) ||
456 	    (family == 0x6 && model >= 0xe))) {
457 		return (B_FALSE);
458 	}
459 
460 	/*
461 	 * Enhanced SpeedStep supported?
462 	 */
463 	cpu_regs.cp_eax = 0x1;
464 	(void) __cpuid_insn(&cpu_regs);
465 	if (!(cpu_regs.cp_ecx & CPUID_INTC_ECX_EST)) {
466 		return (B_FALSE);
467 	}
468 
469 	return (B_TRUE);
470 }
471 
472 boolean_t
473 turbo_supported(void)
474 {
475 	struct cpuid_regs cpu_regs;
476 
477 	/* Required features */
478 	if (!is_x86_feature(x86_featureset, X86FSET_CPUID) ||
479 	    !is_x86_feature(x86_featureset, X86FSET_MSR)) {
480 		return (B_FALSE);
481 	}
482 
483 	/*
484 	 * turbo mode supported?
485 	 */
486 	cpu_regs.cp_eax = 0x6;
487 	(void) __cpuid_insn(&cpu_regs);
488 	if (!(cpu_regs.cp_eax & CPUID_TURBO_SUPPORT)) {
489 		return (B_FALSE);
490 	}
491 
492 	return (B_TRUE);
493 }
494