xref: /illumos-gate/usr/src/uts/i86pc/os/cpupm/speedstep.c (revision 027bcc9f64a0a5915089267b0dc54c9ee05782b0)
15cff7825Smh /*
25cff7825Smh  * CDDL HEADER START
35cff7825Smh  *
45cff7825Smh  * The contents of this file are subject to the terms of the
55cff7825Smh  * Common Development and Distribution License (the "License").
65cff7825Smh  * You may not use this file except in compliance with the License.
75cff7825Smh  *
85cff7825Smh  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95cff7825Smh  * or http://www.opensolaris.org/os/licensing.
105cff7825Smh  * See the License for the specific language governing permissions
115cff7825Smh  * and limitations under the License.
125cff7825Smh  *
135cff7825Smh  * When distributing Covered Code, include this CDDL HEADER in each
145cff7825Smh  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155cff7825Smh  * If applicable, add the following below this CDDL HEADER, with the
165cff7825Smh  * fields enclosed by brackets "[]" replaced with your own identifying
175cff7825Smh  * information: Portions Copyright [yyyy] [name of copyright owner]
185cff7825Smh  *
195cff7825Smh  * CDDL HEADER END
205cff7825Smh  */
215cff7825Smh /*
227417cfdeSKuriakose Kuruvilla  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
235cff7825Smh  */
24444f66e7SMark Haywood /*
25444f66e7SMark Haywood  * Copyright (c) 2009,  Intel Corporation.
26444f66e7SMark Haywood  * All Rights Reserved.
27444f66e7SMark Haywood  */
285cff7825Smh 
295cff7825Smh #include <sys/x86_archext.h>
305cff7825Smh #include <sys/machsystm.h>
31e5bbdc06SRafael Vanoni #include <sys/archsystm.h>
325cff7825Smh #include <sys/x_call.h>
335cff7825Smh #include <sys/acpi/acpi.h>
345cff7825Smh #include <sys/acpica.h>
355cff7825Smh #include <sys/speedstep.h>
365cff7825Smh #include <sys/cpu_acpi.h>
375cff7825Smh #include <sys/cpupm.h>
385cff7825Smh #include <sys/dtrace.h>
395cff7825Smh #include <sys/sdt.h>
405cff7825Smh 
410e751525SEric Saxe static int speedstep_init(cpu_t *);
420e751525SEric Saxe static void speedstep_fini(cpu_t *);
430e751525SEric Saxe static void speedstep_power(cpuset_t, uint32_t);
44444f66e7SMark Haywood static void speedstep_stop(cpu_t *);
455951ced0SHans Rosenfeld static boolean_t speedstep_turbo_supported(void);
467f606aceSMark Haywood 
477f606aceSMark Haywood /*
487f606aceSMark Haywood  * Interfaces for modules implementing Intel's Enhanced SpeedStep.
497f606aceSMark Haywood  */
500e751525SEric Saxe cpupm_state_ops_t speedstep_ops = {
517f606aceSMark Haywood 	"Enhanced SpeedStep Technology",
527f606aceSMark Haywood 	speedstep_init,
537f606aceSMark Haywood 	speedstep_fini,
54444f66e7SMark Haywood 	speedstep_power,
55444f66e7SMark Haywood 	speedstep_stop
567f606aceSMark Haywood };
577f606aceSMark Haywood 
585cff7825Smh /*
595cff7825Smh  * Error returns
605cff7825Smh  */
615cff7825Smh #define	ESS_RET_SUCCESS		0x00
625cff7825Smh #define	ESS_RET_NO_PM		0x01
635cff7825Smh #define	ESS_RET_UNSUP_STATE	0x02
645cff7825Smh 
655cff7825Smh /*
665cff7825Smh  * MSR registers for changing and reading processor power state.
675cff7825Smh  */
6862c4c2f3Smh #define	IA32_PERF_STAT_MSR		0x198
695cff7825Smh #define	IA32_PERF_CTL_MSR		0x199
705cff7825Smh 
715cff7825Smh #define	IA32_CPUID_TSC_CONSTANT		0xF30
725cff7825Smh #define	IA32_MISC_ENABLE_MSR		0x1A0
735cff7825Smh #define	IA32_MISC_ENABLE_EST		(1<<16)
745cff7825Smh #define	IA32_MISC_ENABLE_CXE		(1<<25)
75e5bbdc06SRafael Vanoni 
76e5bbdc06SRafael Vanoni #define	CPUID_TURBO_SUPPORT		(1 << 1)
77e5bbdc06SRafael Vanoni 
785cff7825Smh /*
795cff7825Smh  * Debugging support
805cff7825Smh  */
815cff7825Smh #ifdef	DEBUG
825cff7825Smh volatile int ess_debug = 0;
835cff7825Smh #define	ESSDEBUG(arglist) if (ess_debug) printf arglist;
845cff7825Smh #else
855cff7825Smh #define	ESSDEBUG(arglist)
865cff7825Smh #endif
875cff7825Smh 
885cff7825Smh /*
895cff7825Smh  * Write the ctrl register. How it is written, depends upon the _PCT
905cff7825Smh  * APCI object value.
915cff7825Smh  */
920e751525SEric Saxe static void
935cff7825Smh write_ctrl(cpu_acpi_handle_t handle, uint32_t ctrl)
945cff7825Smh {
955cff7825Smh 	cpu_acpi_pct_t *pct_ctrl;
965cff7825Smh 	uint64_t reg;
975cff7825Smh 
985cff7825Smh 	pct_ctrl = CPU_ACPI_PCT_CTRL(handle);
995cff7825Smh 
1007f606aceSMark Haywood 	switch (pct_ctrl->cr_addrspace_id) {
1015cff7825Smh 	case ACPI_ADR_SPACE_FIXED_HARDWARE:
1025cff7825Smh 		/*
1035cff7825Smh 		 * Read current power state because reserved bits must be
1045cff7825Smh 		 * preserved, compose new value, and write it.
1055cff7825Smh 		 */
1065cff7825Smh 		reg = rdmsr(IA32_PERF_CTL_MSR);
1075cff7825Smh 		reg &= ~((uint64_t)0xFFFF);
1085cff7825Smh 		reg |= ctrl;
1095cff7825Smh 		wrmsr(IA32_PERF_CTL_MSR, reg);
1105cff7825Smh 		break;
1115cff7825Smh 
1125cff7825Smh 	case ACPI_ADR_SPACE_SYSTEM_IO:
1130e751525SEric Saxe 		(void) cpu_acpi_write_port(pct_ctrl->cr_address, ctrl,
1147f606aceSMark Haywood 		    pct_ctrl->cr_width);
1155cff7825Smh 		break;
1165cff7825Smh 
1175cff7825Smh 	default:
1185cff7825Smh 		DTRACE_PROBE1(ess_ctrl_unsupported_type, uint8_t,
1197f606aceSMark Haywood 		    pct_ctrl->cr_addrspace_id);
1200e751525SEric Saxe 		return;
1215cff7825Smh 	}
1225cff7825Smh 
1235cff7825Smh 	DTRACE_PROBE1(ess_ctrl_write, uint32_t, ctrl);
1245cff7825Smh }
1255cff7825Smh 
1265cff7825Smh /*
1275cff7825Smh  * Transition the current processor to the requested state.
1285cff7825Smh  */
129*027bcc9fSToomas Soome int
130*027bcc9fSToomas Soome speedstep_pstate_transition(xc_arg_t arg1, xc_arg_t arg2 __unused,
131*027bcc9fSToomas Soome     xc_arg_t arg3 __unused)
1325cff7825Smh {
133*027bcc9fSToomas Soome 	uint32_t req_state = (uint32_t)arg1;
1340e751525SEric Saxe 	cpupm_mach_state_t *mach_state =
1350e751525SEric Saxe 	    (cpupm_mach_state_t *)CPU->cpu_m.mcpu_pm_mach_state;
1360e751525SEric Saxe 	cpu_acpi_handle_t handle = mach_state->ms_acpi_handle;
1375cff7825Smh 	cpu_acpi_pstate_t *req_pstate;
1385cff7825Smh 	uint32_t ctrl;
1395cff7825Smh 
1407f606aceSMark Haywood 	req_pstate = (cpu_acpi_pstate_t *)CPU_ACPI_PSTATES(handle);
1417f606aceSMark Haywood 	req_pstate += req_state;
1420e751525SEric Saxe 
1435cff7825Smh 	DTRACE_PROBE1(ess_transition, uint32_t, CPU_ACPI_FREQ(req_pstate));
1445cff7825Smh 
1455cff7825Smh 	/*
1465cff7825Smh 	 * Initiate the processor p-state change.
1475cff7825Smh 	 */
1487f606aceSMark Haywood 	ctrl = CPU_ACPI_PSTATE_CTRL(req_pstate);
1490e751525SEric Saxe 	write_ctrl(handle, ctrl);
1505cff7825Smh 
1515951ced0SHans Rosenfeld 	if (mach_state->ms_turbo != NULL)
1525951ced0SHans Rosenfeld 		cpupm_record_turbo_info(mach_state->ms_turbo,
153e5bbdc06SRafael Vanoni 		    mach_state->ms_pstate.cma_state.pstate, req_state);
154e5bbdc06SRafael Vanoni 
1550e751525SEric Saxe 	mach_state->ms_pstate.cma_state.pstate = req_state;
1560e751525SEric Saxe 	cpu_set_curr_clock(((uint64_t)CPU_ACPI_FREQ(req_pstate) * 1000000));
157*027bcc9fSToomas Soome 	return (0);
1585cff7825Smh }
1595cff7825Smh 
1600e751525SEric Saxe static void
1610e751525SEric Saxe speedstep_power(cpuset_t set, uint32_t req_state)
1625cff7825Smh {
163375e0503SMark Haywood 	/*
164375e0503SMark Haywood 	 * If thread is already running on target CPU then just
165375e0503SMark Haywood 	 * make the transition request. Otherwise, we'll need to
166375e0503SMark Haywood 	 * make a cross-call.
167375e0503SMark Haywood 	 */
1685cff7825Smh 	kpreempt_disable();
1690e751525SEric Saxe 	if (CPU_IN_SET(set, CPU->cpu_id)) {
170*027bcc9fSToomas Soome 		(void) speedstep_pstate_transition(req_state, 0, 0);
1710e751525SEric Saxe 		CPUSET_DEL(set, CPU->cpu_id);
1720e751525SEric Saxe 	}
1730e751525SEric Saxe 	if (!CPUSET_ISNULL(set)) {
1744da99751SToomas Soome 		xc_call((xc_arg_t)req_state, 0, 0, CPUSET2BV(set),
175*027bcc9fSToomas Soome 		    speedstep_pstate_transition);
176375e0503SMark Haywood 	}
1775cff7825Smh 	kpreempt_enable();
1785cff7825Smh }
1795cff7825Smh 
1805cff7825Smh /*
1815cff7825Smh  * Validate that this processor supports Speedstep and if so,
1825cff7825Smh  * get the P-state data from ACPI and cache it.
1835cff7825Smh  */
1847f606aceSMark Haywood static int
1850e751525SEric Saxe speedstep_init(cpu_t *cp)
1865cff7825Smh {
1870e751525SEric Saxe 	cpupm_mach_state_t *mach_state =
1880e751525SEric Saxe 	    (cpupm_mach_state_t *)cp->cpu_m.mcpu_pm_mach_state;
1890e751525SEric Saxe 	cpu_acpi_handle_t handle = mach_state->ms_acpi_handle;
1905cff7825Smh 	cpu_acpi_pct_t *pct_stat;
191511588bbSYuri Pankov 	static int logged = 0;
1925cff7825Smh 
1930e751525SEric Saxe 	ESSDEBUG(("speedstep_init: processor %d\n", cp->cpu_id));
1945cff7825Smh 
1955cff7825Smh 	/*
1967f606aceSMark Haywood 	 * Cache the P-state specific ACPI data.
1975cff7825Smh 	 */
1987f606aceSMark Haywood 	if (cpu_acpi_cache_pstate_data(handle) != 0) {
199511588bbSYuri Pankov 		if (!logged) {
200511588bbSYuri Pankov 			cmn_err(CE_NOTE, "!SpeedStep support is being "
201511588bbSYuri Pankov 			    "disabled due to errors parsing ACPI P-state "
202511588bbSYuri Pankov 			    "objects exported by BIOS.");
203511588bbSYuri Pankov 			logged = 1;
204511588bbSYuri Pankov 		}
2050e751525SEric Saxe 		speedstep_fini(cp);
2065cff7825Smh 		return (ESS_RET_NO_PM);
2075cff7825Smh 	}
2085cff7825Smh 
2095cff7825Smh 	pct_stat = CPU_ACPI_PCT_STATUS(handle);
2107f606aceSMark Haywood 	switch (pct_stat->cr_addrspace_id) {
2115cff7825Smh 	case ACPI_ADR_SPACE_FIXED_HARDWARE:
2125cff7825Smh 		ESSDEBUG(("Transitions will use fixed hardware\n"));
2135cff7825Smh 		break;
2145cff7825Smh 	case ACPI_ADR_SPACE_SYSTEM_IO:
2155cff7825Smh 		ESSDEBUG(("Transitions will use system IO\n"));
2165cff7825Smh 		break;
2175cff7825Smh 	default:
2185cff7825Smh 		cmn_err(CE_WARN, "!_PCT conifgured for unsupported "
2197f606aceSMark Haywood 		    "addrspace = %d.", pct_stat->cr_addrspace_id);
2205cff7825Smh 		cmn_err(CE_NOTE, "!CPU power management will not function.");
2210e751525SEric Saxe 		speedstep_fini(cp);
2225cff7825Smh 		return (ESS_RET_NO_PM);
2235cff7825Smh 	}
2245cff7825Smh 
2250e751525SEric Saxe 	cpupm_alloc_domains(cp, CPUPM_P_STATES);
2265cff7825Smh 
2275951ced0SHans Rosenfeld 	if (speedstep_turbo_supported())
2285951ced0SHans Rosenfeld 		mach_state->ms_turbo = cpupm_turbo_init(cp);
229e5bbdc06SRafael Vanoni 
2300e751525SEric Saxe 	ESSDEBUG(("Processor %d succeeded.\n", cp->cpu_id))
2315cff7825Smh 	return (ESS_RET_SUCCESS);
2325cff7825Smh }
2335cff7825Smh 
2345cff7825Smh /*
2355cff7825Smh  * Free resources allocated by speedstep_init().
2365cff7825Smh  */
2377f606aceSMark Haywood static void
2380e751525SEric Saxe speedstep_fini(cpu_t *cp)
2395cff7825Smh {
2400e751525SEric Saxe 	cpupm_mach_state_t *mach_state =
2410e751525SEric Saxe 	    (cpupm_mach_state_t *)(cp->cpu_m.mcpu_pm_mach_state);
2420e751525SEric Saxe 	cpu_acpi_handle_t handle = mach_state->ms_acpi_handle;
2437f606aceSMark Haywood 
2440e751525SEric Saxe 	cpupm_free_domains(&cpupm_pstate_domains);
2457f606aceSMark Haywood 	cpu_acpi_free_pstate_data(handle);
246e5bbdc06SRafael Vanoni 
2475951ced0SHans Rosenfeld 	if (mach_state->ms_turbo != NULL)
2485951ced0SHans Rosenfeld 		cpupm_turbo_fini(mach_state->ms_turbo);
2495951ced0SHans Rosenfeld 	mach_state->ms_turbo = NULL;
250444f66e7SMark Haywood }
251444f66e7SMark Haywood 
252444f66e7SMark Haywood static void
253444f66e7SMark Haywood speedstep_stop(cpu_t *cp)
254444f66e7SMark Haywood {
255444f66e7SMark Haywood 	cpupm_mach_state_t *mach_state =
256444f66e7SMark Haywood 	    (cpupm_mach_state_t *)(cp->cpu_m.mcpu_pm_mach_state);
257444f66e7SMark Haywood 	cpu_acpi_handle_t handle = mach_state->ms_acpi_handle;
258444f66e7SMark Haywood 
259444f66e7SMark Haywood 	cpupm_remove_domains(cp, CPUPM_P_STATES, &cpupm_pstate_domains);
260444f66e7SMark Haywood 	cpu_acpi_free_pstate_data(handle);
261444f66e7SMark Haywood 
2625951ced0SHans Rosenfeld 	if (mach_state->ms_turbo != NULL)
2635951ced0SHans Rosenfeld 		cpupm_turbo_fini(mach_state->ms_turbo);
2645951ced0SHans Rosenfeld 	mach_state->ms_turbo = NULL;
2657f606aceSMark Haywood }
2667f606aceSMark Haywood 
2677f606aceSMark Haywood boolean_t
2687f606aceSMark Haywood speedstep_supported(uint_t family, uint_t model)
2697f606aceSMark Haywood {
2707f606aceSMark Haywood 	struct cpuid_regs cpu_regs;
2717f606aceSMark Haywood 
2727f606aceSMark Haywood 	/* Required features */
2737417cfdeSKuriakose Kuruvilla 	if (!is_x86_feature(x86_featureset, X86FSET_CPUID) ||
2747417cfdeSKuriakose Kuruvilla 	    !is_x86_feature(x86_featureset, X86FSET_MSR)) {
2757f606aceSMark Haywood 		return (B_FALSE);
2767f606aceSMark Haywood 	}
2777f606aceSMark Haywood 
2787f606aceSMark Haywood 	/*
2797f606aceSMark Haywood 	 * We only support family/model combinations which
2807f606aceSMark Haywood 	 * are P-state TSC invariant.
2817f606aceSMark Haywood 	 */
2827f606aceSMark Haywood 	if (!((family == 0xf && model >= 0x3) ||
2837f606aceSMark Haywood 	    (family == 0x6 && model >= 0xe))) {
2847f606aceSMark Haywood 		return (B_FALSE);
2857f606aceSMark Haywood 	}
2867f606aceSMark Haywood 
2877f606aceSMark Haywood 	/*
2887f606aceSMark Haywood 	 * Enhanced SpeedStep supported?
2897f606aceSMark Haywood 	 */
2907f606aceSMark Haywood 	cpu_regs.cp_eax = 0x1;
2917f606aceSMark Haywood 	(void) __cpuid_insn(&cpu_regs);
2927f606aceSMark Haywood 	if (!(cpu_regs.cp_ecx & CPUID_INTC_ECX_EST)) {
2937f606aceSMark Haywood 		return (B_FALSE);
2947f606aceSMark Haywood 	}
2957f606aceSMark Haywood 
2967f606aceSMark Haywood 	return (B_TRUE);
2975cff7825Smh }
298e5bbdc06SRafael Vanoni 
299e5bbdc06SRafael Vanoni boolean_t
3005951ced0SHans Rosenfeld speedstep_turbo_supported(void)
301e5bbdc06SRafael Vanoni {
302e5bbdc06SRafael Vanoni 	struct cpuid_regs cpu_regs;
303e5bbdc06SRafael Vanoni 
304e5bbdc06SRafael Vanoni 	/* Required features */
3057417cfdeSKuriakose Kuruvilla 	if (!is_x86_feature(x86_featureset, X86FSET_CPUID) ||
3067417cfdeSKuriakose Kuruvilla 	    !is_x86_feature(x86_featureset, X86FSET_MSR)) {
307e5bbdc06SRafael Vanoni 		return (B_FALSE);
308e5bbdc06SRafael Vanoni 	}
309e5bbdc06SRafael Vanoni 
310e5bbdc06SRafael Vanoni 	/*
311e5bbdc06SRafael Vanoni 	 * turbo mode supported?
312e5bbdc06SRafael Vanoni 	 */
313e5bbdc06SRafael Vanoni 	cpu_regs.cp_eax = 0x6;
314e5bbdc06SRafael Vanoni 	(void) __cpuid_insn(&cpu_regs);
315e5bbdc06SRafael Vanoni 	if (!(cpu_regs.cp_eax & CPUID_TURBO_SUPPORT)) {
316e5bbdc06SRafael Vanoni 		return (B_FALSE);
317e5bbdc06SRafael Vanoni 	}
318e5bbdc06SRafael Vanoni 
319e5bbdc06SRafael Vanoni 	return (B_TRUE);
320e5bbdc06SRafael Vanoni }
321