speedstep.c revision 8906:e559381f1e2b
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/x_call.h>
29#include <sys/acpi/acpi.h>
30#include <sys/acpica.h>
31#include <sys/speedstep.h>
32#include <sys/cpu_acpi.h>
33#include <sys/cpupm.h>
34#include <sys/dtrace.h>
35#include <sys/sdt.h>
36
37static int speedstep_init(cpu_t *);
38static void speedstep_fini(cpu_t *);
39static void speedstep_power(cpuset_t, uint32_t);
40
41/*
42 * Interfaces for modules implementing Intel's Enhanced SpeedStep.
43 */
44cpupm_state_ops_t speedstep_ops = {
45	"Enhanced SpeedStep Technology",
46	speedstep_init,
47	speedstep_fini,
48	speedstep_power
49};
50
51/*
52 * Error returns
53 */
54#define	ESS_RET_SUCCESS		0x00
55#define	ESS_RET_NO_PM		0x01
56#define	ESS_RET_UNSUP_STATE	0x02
57
58/*
59 * MSR registers for changing and reading processor power state.
60 */
61#define	IA32_PERF_STAT_MSR		0x198
62#define	IA32_PERF_CTL_MSR		0x199
63
64#define	IA32_CPUID_TSC_CONSTANT		0xF30
65#define	IA32_MISC_ENABLE_MSR		0x1A0
66#define	IA32_MISC_ENABLE_EST		(1<<16)
67#define	IA32_MISC_ENABLE_CXE		(1<<25)
68/*
69 * Debugging support
70 */
71#ifdef	DEBUG
72volatile int ess_debug = 0;
73#define	ESSDEBUG(arglist) if (ess_debug) printf arglist;
74#else
75#define	ESSDEBUG(arglist)
76#endif
77
78/*
79 * Write the ctrl register. How it is written, depends upon the _PCT
80 * APCI object value.
81 */
82static void
83write_ctrl(cpu_acpi_handle_t handle, uint32_t ctrl)
84{
85	cpu_acpi_pct_t *pct_ctrl;
86	uint64_t reg;
87
88	pct_ctrl = CPU_ACPI_PCT_CTRL(handle);
89
90	switch (pct_ctrl->cr_addrspace_id) {
91	case ACPI_ADR_SPACE_FIXED_HARDWARE:
92		/*
93		 * Read current power state because reserved bits must be
94		 * preserved, compose new value, and write it.
95		 */
96		reg = rdmsr(IA32_PERF_CTL_MSR);
97		reg &= ~((uint64_t)0xFFFF);
98		reg |= ctrl;
99		wrmsr(IA32_PERF_CTL_MSR, reg);
100		break;
101
102	case ACPI_ADR_SPACE_SYSTEM_IO:
103		(void) cpu_acpi_write_port(pct_ctrl->cr_address, ctrl,
104		    pct_ctrl->cr_width);
105		break;
106
107	default:
108		DTRACE_PROBE1(ess_ctrl_unsupported_type, uint8_t,
109		    pct_ctrl->cr_addrspace_id);
110		return;
111	}
112
113	DTRACE_PROBE1(ess_ctrl_write, uint32_t, ctrl);
114}
115
116/*
117 * Transition the current processor to the requested state.
118 */
119void
120speedstep_pstate_transition(uint32_t req_state)
121{
122	cpupm_mach_state_t *mach_state =
123	    (cpupm_mach_state_t *)CPU->cpu_m.mcpu_pm_mach_state;
124	cpu_acpi_handle_t handle = mach_state->ms_acpi_handle;
125	cpu_acpi_pstate_t *req_pstate;
126	uint32_t ctrl;
127
128	req_pstate = (cpu_acpi_pstate_t *)CPU_ACPI_PSTATES(handle);
129	req_pstate += req_state;
130
131	DTRACE_PROBE1(ess_transition, uint32_t, CPU_ACPI_FREQ(req_pstate));
132
133	/*
134	 * Initiate the processor p-state change.
135	 */
136	ctrl = CPU_ACPI_PSTATE_CTRL(req_pstate);
137	write_ctrl(handle, ctrl);
138
139	mach_state->ms_pstate.cma_state.pstate = req_state;
140	cpu_set_curr_clock(((uint64_t)CPU_ACPI_FREQ(req_pstate) * 1000000));
141}
142
143static void
144speedstep_power(cpuset_t set, uint32_t req_state)
145{
146	/*
147	 * If thread is already running on target CPU then just
148	 * make the transition request. Otherwise, we'll need to
149	 * make a cross-call.
150	 */
151	kpreempt_disable();
152	if (CPU_IN_SET(set, CPU->cpu_id)) {
153		speedstep_pstate_transition(req_state);
154		CPUSET_DEL(set, CPU->cpu_id);
155	}
156	if (!CPUSET_ISNULL(set)) {
157		xc_call((xc_arg_t)req_state, NULL, NULL, X_CALL_HIPRI, set,
158		    (xc_func_t)speedstep_pstate_transition);
159	}
160	kpreempt_enable();
161}
162
163/*
164 * Validate that this processor supports Speedstep and if so,
165 * get the P-state data from ACPI and cache it.
166 */
167static int
168speedstep_init(cpu_t *cp)
169{
170	cpupm_mach_state_t *mach_state =
171	    (cpupm_mach_state_t *)cp->cpu_m.mcpu_pm_mach_state;
172	cpu_acpi_handle_t handle = mach_state->ms_acpi_handle;
173	cpu_acpi_pct_t *pct_stat;
174
175	ESSDEBUG(("speedstep_init: processor %d\n", cp->cpu_id));
176
177	/*
178	 * Cache the P-state specific ACPI data.
179	 */
180	if (cpu_acpi_cache_pstate_data(handle) != 0) {
181		ESSDEBUG(("Failed to cache ACPI data\n"));
182		speedstep_fini(cp);
183		return (ESS_RET_NO_PM);
184	}
185
186	pct_stat = CPU_ACPI_PCT_STATUS(handle);
187	switch (pct_stat->cr_addrspace_id) {
188	case ACPI_ADR_SPACE_FIXED_HARDWARE:
189		ESSDEBUG(("Transitions will use fixed hardware\n"));
190		break;
191	case ACPI_ADR_SPACE_SYSTEM_IO:
192		ESSDEBUG(("Transitions will use system IO\n"));
193		break;
194	default:
195		cmn_err(CE_WARN, "!_PCT conifgured for unsupported "
196		    "addrspace = %d.", pct_stat->cr_addrspace_id);
197		cmn_err(CE_NOTE, "!CPU power management will not function.");
198		speedstep_fini(cp);
199		return (ESS_RET_NO_PM);
200	}
201
202	cpupm_alloc_domains(cp, CPUPM_P_STATES);
203
204	ESSDEBUG(("Processor %d succeeded.\n", cp->cpu_id))
205	return (ESS_RET_SUCCESS);
206}
207
208/*
209 * Free resources allocated by speedstep_init().
210 */
211static void
212speedstep_fini(cpu_t *cp)
213{
214	cpupm_mach_state_t *mach_state =
215	    (cpupm_mach_state_t *)(cp->cpu_m.mcpu_pm_mach_state);
216	cpu_acpi_handle_t handle = mach_state->ms_acpi_handle;
217
218	cpupm_free_domains(&cpupm_pstate_domains);
219	cpu_acpi_free_pstate_data(handle);
220}
221
222boolean_t
223speedstep_supported(uint_t family, uint_t model)
224{
225	struct cpuid_regs cpu_regs;
226
227	/* Required features */
228	if (!(x86_feature & X86_CPUID) ||
229	    !(x86_feature & X86_MSR)) {
230		return (B_FALSE);
231	}
232
233	/*
234	 * We only support family/model combinations which
235	 * are P-state TSC invariant.
236	 */
237	if (!((family == 0xf && model >= 0x3) ||
238	    (family == 0x6 && model >= 0xe))) {
239		return (B_FALSE);
240	}
241
242	/*
243	 * Enhanced SpeedStep supported?
244	 */
245	cpu_regs.cp_eax = 0x1;
246	(void) __cpuid_insn(&cpu_regs);
247	if (!(cpu_regs.cp_ecx & CPUID_INTC_ECX_EST)) {
248		return (B_FALSE);
249	}
250
251	return (B_TRUE);
252}
253