xref: /titanic_50/usr/src/uts/i86pc/os/cpupm/speedstep.c (revision 585995d5d19489bf178112c08c8c61ffc049ff6e)
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 
37 static int speedstep_init(cpu_t *);
38 static void speedstep_fini(cpu_t *);
39 static void speedstep_power(cpuset_t, uint32_t);
40 
41 /*
42  * Interfaces for modules implementing Intel's Enhanced SpeedStep.
43  */
44 cpupm_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
72 volatile 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  */
82 static void
83 write_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  */
119 void
120 speedstep_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 
143 static void
144 speedstep_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  */
167 static int
168 speedstep_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  */
211 static void
212 speedstep_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 
222 boolean_t
223 speedstep_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