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