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