speedstep.c revision 8935:5bd81ee07936
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 */
41typedef 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
49typedef 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
55static int speedstep_init(cpu_t *);
56static void speedstep_fini(cpu_t *);
57static void speedstep_power(cpuset_t, uint32_t);
58static boolean_t turbo_supported(void);
59static int turbo_kstat_update(kstat_t *, int);
60static void get_turbo_info(cpupm_turbo_info_t *);
61static void reset_turbo_info(void);
62static void record_turbo_info(cpupm_turbo_info_t *, uint32_t, uint32_t);
63static void update_turbo_info(cpupm_turbo_info_t *);
64
65/*
66 * Interfaces for modules implementing Intel's Enhanced SpeedStep.
67 */
68cpupm_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
109volatile int ess_debug = 0;
110#define	ESSDEBUG(arglist) if (ess_debug) printf arglist;
111#else
112#define	ESSDEBUG(arglist)
113#endif
114
115static kmutex_t turbo_mutex;
116
117turbo_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 */
126static int
127turbo_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 */
153static void
154get_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 */
170static void
171reset_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 */
184static void
185record_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 */
209static void
210update_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 */
229static void
230write_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 */
266void
267speedstep_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
297static void
298speedstep_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, X_CALL_HIPRI, 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 */
321static int
322speedstep_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
386ess_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 */
395static void
396speedstep_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
414boolean_t
415speedstep_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
446boolean_t
447turbo_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