xref: /illumos-gate/usr/src/uts/i86pc/os/cpupm/speedstep.c (revision 62c4c2f31ab3d2e1eae90df658ee8da30e851673)
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 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <sys/x86_archext.h>
29 #include <sys/machsystm.h>
30 #include <sys/x_call.h>
31 #include <sys/acpi/acpi.h>
32 #include <sys/acpica.h>
33 #include <sys/speedstep.h>
34 #include <sys/cpu_acpi.h>
35 #include <sys/cpupm.h>
36 #include <sys/dtrace.h>
37 #include <sys/sdt.h>
38 
39 /*
40  * Error returns
41  */
42 #define	ESS_RET_SUCCESS		0x00
43 #define	ESS_RET_NO_PM		0x01
44 #define	ESS_RET_UNSUP_STATE	0x02
45 
46 /*
47  * Intel docs indicate that maximum latency of P-state changes should
48  * be on the order of 10mS. When waiting, wait in 100uS increments.
49  */
50 #define	ESS_MAX_LATENCY_MICROSECS	10000
51 #define	ESS_LATENCY_WAIT		100
52 
53 /*
54  * The SpeedStep related Processor Driver Capabilities (_PDC).
55  * See Intel Processor Vendor-Specific ACPI Interface Specification
56  * for details.
57  */
58 #define	ESS_PDC_REVISION		0x1
59 #define	ESS_PDC_PS_MSR			(1<<0)
60 #define	ESS_PDC_MP			(1<<3)
61 #define	ESS_PDC_PSD			(1<<5)
62 
63 /*
64  * MSR registers for changing and reading processor power state.
65  */
66 #define	IA32_PERF_STAT_MSR		0x198
67 #define	IA32_PERF_CTL_MSR		0x199
68 
69 #define	IA32_CPUID_TSC_CONSTANT		0xF30
70 #define	IA32_MISC_ENABLE_MSR		0x1A0
71 #define	IA32_MISC_ENABLE_EST		(1<<16)
72 #define	IA32_MISC_ENABLE_CXE		(1<<25)
73 /*
74  * Debugging support
75  */
76 #ifdef	DEBUG
77 volatile int ess_debug = 0;
78 #define	ESSDEBUG(arglist) if (ess_debug) printf arglist;
79 #else
80 #define	ESSDEBUG(arglist)
81 #endif
82 
83 typedef struct speedstep_state {
84 	uint32_t ss_state;
85 } speedstep_state_t;
86 
87 uint32_t ess_pdccap = ESS_PDC_PS_MSR | ESS_PDC_MP | ESS_PDC_PSD;
88 
89 /*
90  * Read the status register. How it is read, depends upon the _PCT
91  * APCI object value.
92  */
93 static int
94 read_status(cpu_acpi_handle_t handle, uint32_t *stat)
95 {
96 	cpu_acpi_pct_t *pct_stat;
97 	uint64_t reg;
98 	int ret = 0;
99 
100 	pct_stat = CPU_ACPI_PCT_STATUS(handle);
101 
102 	switch (pct_stat->pc_addrspace_id) {
103 	case ACPI_ADR_SPACE_FIXED_HARDWARE:
104 		reg = rdmsr(IA32_PERF_STAT_MSR);
105 		*stat = reg & 0xFFFF;
106 		ret = 0;
107 		break;
108 
109 	case ACPI_ADR_SPACE_SYSTEM_IO:
110 		ret = cpu_acpi_read_port(pct_stat->pc_address, stat,
111 		    pct_stat->pc_width);
112 		break;
113 
114 	default:
115 		DTRACE_PROBE1(ess_status_unsupported_type, uint8_t,
116 		    pct_stat->pc_addrspace_id);
117 		return (-1);
118 	}
119 
120 	DTRACE_PROBE1(ess_status_read, uint32_t, *stat);
121 	DTRACE_PROBE1(ess_status_read_err, int, ret);
122 
123 	return (ret);
124 }
125 
126 /*
127  * Write the ctrl register. How it is written, depends upon the _PCT
128  * APCI object value.
129  */
130 static int
131 write_ctrl(cpu_acpi_handle_t handle, uint32_t ctrl)
132 {
133 	cpu_acpi_pct_t *pct_ctrl;
134 	uint64_t reg;
135 	int ret = 0;
136 
137 	pct_ctrl = CPU_ACPI_PCT_CTRL(handle);
138 
139 	switch (pct_ctrl->pc_addrspace_id) {
140 	case ACPI_ADR_SPACE_FIXED_HARDWARE:
141 		/*
142 		 * Read current power state because reserved bits must be
143 		 * preserved, compose new value, and write it.
144 		 */
145 		reg = rdmsr(IA32_PERF_CTL_MSR);
146 		reg &= ~((uint64_t)0xFFFF);
147 		reg |= ctrl;
148 		wrmsr(IA32_PERF_CTL_MSR, reg);
149 		ret = 0;
150 		break;
151 
152 	case ACPI_ADR_SPACE_SYSTEM_IO:
153 		ret = cpu_acpi_write_port(pct_ctrl->pc_address, ctrl,
154 		    pct_ctrl->pc_width);
155 		break;
156 
157 	default:
158 		DTRACE_PROBE1(ess_ctrl_unsupported_type, uint8_t,
159 		    pct_ctrl->pc_addrspace_id);
160 		return (-1);
161 	}
162 
163 	DTRACE_PROBE1(ess_ctrl_write, uint32_t, ctrl);
164 	DTRACE_PROBE1(ess_ctrl_write_err, int, ret);
165 
166 	return (ret);
167 }
168 
169 /*
170  * Transition the current processor to the requested state.
171  */
172 void
173 speedstep_pstate_transition(int *ret, cpudrv_devstate_t *cpudsp,
174     uint32_t req_state)
175 {
176 	speedstep_state_t *speedstep_state = cpudsp->module_state;
177 	cpu_acpi_handle_t handle = cpudsp->acpi_handle;
178 	cpu_acpi_pstate_t *req_pstate;
179 	uint32_t ctrl;
180 	uint32_t stat;
181 	int i;
182 
183 	req_pstate = CPU_ACPI_PSTATE(handle, req_state);
184 	DTRACE_PROBE1(ess_transition, uint32_t, CPU_ACPI_FREQ(req_pstate));
185 
186 	/*
187 	 * Initiate the processor p-state change.
188 	 */
189 	ctrl = CPU_ACPI_CTRL(req_pstate);
190 	if (write_ctrl(handle, ctrl) != 0) {
191 		*ret = ESS_RET_UNSUP_STATE;
192 		return;
193 	}
194 
195 	/* Wait until switch is complete, but bound the loop just in case. */
196 	for (i = 0; i < ESS_MAX_LATENCY_MICROSECS; i += ESS_LATENCY_WAIT) {
197 		if (read_status(handle, &stat) == 0 &&
198 		    CPU_ACPI_STAT(req_pstate) == stat)
199 			break;
200 		drv_usecwait(ESS_LATENCY_WAIT);
201 	}
202 	if (i >= ESS_MAX_LATENCY_MICROSECS) {
203 		DTRACE_PROBE(ess_transition_incomplete);
204 	}
205 
206 	speedstep_state->ss_state = req_state;
207 	CPU->cpu_curr_clock =
208 	    (((uint64_t)CPU_ACPI_FREQ(req_pstate) * 1000000));
209 	*ret = ESS_RET_SUCCESS;
210 }
211 
212 int
213 speedstep_power(cpudrv_devstate_t *cpudsp, uint32_t req_state)
214 {
215 	cpuset_t cpus;
216 	int ret;
217 
218 	CPUSET_ONLY(cpus, cpudsp->cpu_id);
219 
220 	kpreempt_disable();
221 	xc_call((xc_arg_t)&ret, (xc_arg_t)cpudsp, (xc_arg_t)req_state,
222 	    X_CALL_HIPRI, cpus, (xc_func_t)speedstep_pstate_transition);
223 	kpreempt_enable();
224 
225 	return (ret);
226 }
227 
228 /*
229  * Validate that this processor supports Speedstep and if so,
230  * get the P-state data from ACPI and cache it.
231  */
232 int
233 speedstep_init(cpudrv_devstate_t *cpudsp)
234 {
235 	speedstep_state_t *speedstep_state;
236 	cpu_acpi_handle_t handle;
237 	cpu_acpi_pct_t *pct_stat;
238 	uint64_t reg;
239 	uint_t family;
240 	uint_t model;
241 	struct cpuid_regs cpu_regs;
242 	cpu_t *cp;
243 	int dependency;
244 
245 	ESSDEBUG(("speedstep_init: instance %d\n",
246 	    ddi_get_instance(cpudsp->dip)));
247 
248 	/* Intel w/ CPUID support and rdmsr/wrmsr? */
249 	if (x86_vendor != X86_VENDOR_Intel ||
250 	    !(x86_feature & X86_CPUID) ||
251 	    !(x86_feature & X86_MSR)) {
252 		ESSDEBUG(("Either not Intel or feature not supported.\n"));
253 		return (ESS_RET_NO_PM);
254 	}
255 
256 	/*
257 	 * Enhanced Speedstep supported?
258 	 */
259 	cpu_regs.cp_eax = 0x1;
260 	(void) __cpuid_insn(&cpu_regs);
261 	if (!(cpu_regs.cp_ecx & CPUID_INTC_ECX_EST)) {
262 		ESSDEBUG(("Enhanced Speedstep not supported.\n"));
263 		return (ESS_RET_NO_PM);
264 	}
265 
266 	family = cpuid_getfamily(CPU);
267 	model = cpuid_getmodel(CPU);
268 	if (!((family == 0xf && model >= 0x3) ||
269 	    (family == 0x6 && model >= 0xe))) {
270 		ESSDEBUG(("Variant TSC not supported.\n"));
271 		return (ESS_RET_NO_PM);
272 	}
273 
274 	/*
275 	 * If Enhanced Speedstep has not been enabled on the system,
276 	 * then we probably should not override the BIOS setting.
277 	 */
278 	reg = rdmsr(IA32_MISC_ENABLE_MSR);
279 	if (! (reg & IA32_MISC_ENABLE_EST)) {
280 		cmn_err(CE_NOTE, "!Enhanced Intel SpeedStep not enabled.");
281 		cmn_err(CE_NOTE, "!CPU power management will not function.");
282 		return (ESS_RET_NO_PM);
283 	}
284 
285 	/*
286 	 * Enhanced Speedstep requires ACPI support. Get a handle
287 	 * to the correct processor object for this dip.
288 	 */
289 	handle = cpudsp->acpi_handle = cpu_acpi_init(cpudsp->dip);
290 	if (handle == NULL) {
291 		cmn_err(CE_WARN, "!speedstep_init: instance %d: "
292 		    "unable to get ACPI handle",
293 		    ddi_get_instance(cpudsp->dip));
294 
295 		cmn_err(CE_NOTE, "!CPU power management will not function.");
296 		return (ESS_RET_NO_PM);
297 	}
298 
299 	/*
300 	 * _PDC support is optional and the driver should
301 	 * function even if the _PDC write fails.
302 	 */
303 	if (cpu_acpi_write_pdc(handle, ESS_PDC_REVISION, 1,
304 	    &ess_pdccap) != 0)
305 		ESSDEBUG(("Failed to write PDC\n"));
306 
307 	if (cpu_acpi_cache_data(handle) != 0) {
308 		ESSDEBUG(("Failed to cache ACPI data\n"));
309 		cpu_acpi_fini(handle);
310 		return (ESS_RET_NO_PM);
311 	}
312 
313 	pct_stat = CPU_ACPI_PCT_STATUS(handle);
314 	switch (pct_stat->pc_addrspace_id) {
315 	case ACPI_ADR_SPACE_FIXED_HARDWARE:
316 		ESSDEBUG(("Transitions will use fixed hardware\n"));
317 		break;
318 	case ACPI_ADR_SPACE_SYSTEM_IO:
319 		ESSDEBUG(("Transitions will use system IO\n"));
320 		break;
321 	default:
322 		cmn_err(CE_WARN, "!_PCT conifgured for unsupported "
323 		    "addrspace = %d.", pct_stat->pc_addrspace_id);
324 		cmn_err(CE_NOTE, "!CPU power management will not function.");
325 		cpu_acpi_fini(handle);
326 		return (ESS_RET_NO_PM);
327 	}
328 
329 	if (CPU_ACPI_IS_OBJ_CACHED(handle, CPU_ACPI_PSD_CACHED))
330 		dependency = CPU_ACPI_PSD(handle).pd_domain;
331 	else {
332 		mutex_enter(&cpu_lock);
333 		cp = cpu[CPU->cpu_id];
334 		dependency = cpuid_get_chipid(cp);
335 		mutex_exit(&cpu_lock);
336 	}
337 	cpupm_add_cpu2dependency(cpudsp->dip, dependency);
338 
339 	speedstep_state = kmem_zalloc(sizeof (speedstep_state_t), KM_SLEEP);
340 	speedstep_state->ss_state = NULL;
341 	cpudsp->module_state = speedstep_state;
342 
343 	ESSDEBUG(("Instance %d succeeded.\n", ddi_get_instance(cpudsp->dip)));
344 	return (ESS_RET_SUCCESS);
345 }
346 
347 /*
348  * Free resources allocated by speedstep_init().
349  */
350 void
351 speedstep_fini(cpudrv_devstate_t *cpudsp)
352 {
353 	cpu_acpi_fini(cpudsp->acpi_handle);
354 	kmem_free(cpudsp->module_state, sizeof (speedstep_state_t));
355 }
356