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