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.
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.
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]
22 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
25 #include <sys/x86_archext.h>
26 #include <sys/machsystm.h>
27 #include <sys/x_call.h>
28 #include <sys/acpi/acpi.h>
29 #include <sys/acpica.h>
30 #include <sys/pwrnow.h>
31 #include <sys/cpu_acpi.h>
32 #include <sys/cpupm.h>
33 #include <sys/dtrace.h>
36 static int pwrnow_init(cpu_t
*);
37 static void pwrnow_fini(cpu_t
*);
38 static void pwrnow_power(cpuset_t
, uint32_t);
39 static void pwrnow_stop(cpu_t
*);
41 static boolean_t
pwrnow_cpb_supported(void);
44 * Interfaces for modules implementing AMD's PowerNow!.
46 cpupm_state_ops_t pwrnow_ops
= {
47 "PowerNow! Technology",
57 #define PWRNOW_RET_SUCCESS 0x00
58 #define PWRNOW_RET_NO_PM 0x01
59 #define PWRNOW_RET_UNSUP_STATE 0x02
60 #define PWRNOW_RET_TRANS_INCOMPLETE 0x03
62 #define PWRNOW_LATENCY_WAIT 10
65 * MSR registers for changing and reading processor power state.
67 #define PWRNOW_PERF_CTL_MSR 0xC0010062
68 #define PWRNOW_PERF_STATUS_MSR 0xC0010063
70 #define AMD_CPUID_PSTATE_HARDWARE (1<<7)
71 #define AMD_CPUID_TSC_CONSTANT (1<<8)
72 #define AMD_CPUID_CPB (1<<9)
78 volatile int pwrnow_debug
= 0;
79 #define PWRNOW_DEBUG(arglist) if (pwrnow_debug) printf arglist;
81 #define PWRNOW_DEBUG(arglist)
85 * Write the ctrl register.
88 write_ctrl(cpu_acpi_handle_t handle
, uint32_t ctrl
)
90 cpu_acpi_pct_t
*pct_ctrl
;
93 pct_ctrl
= CPU_ACPI_PCT_CTRL(handle
);
95 switch (pct_ctrl
->cr_addrspace_id
) {
96 case ACPI_ADR_SPACE_FIXED_HARDWARE
:
98 wrmsr(PWRNOW_PERF_CTL_MSR
, reg
);
102 DTRACE_PROBE1(pwrnow_ctrl_unsupported_type
, uint8_t,
103 pct_ctrl
->cr_addrspace_id
);
107 DTRACE_PROBE1(pwrnow_ctrl_write
, uint32_t, ctrl
);
111 * Transition the current processor to the requested state.
114 pwrnow_pstate_transition(uint32_t req_state
)
116 cpupm_mach_state_t
*mach_state
=
117 (cpupm_mach_state_t
*)CPU
->cpu_m
.mcpu_pm_mach_state
;
118 cpu_acpi_handle_t handle
= mach_state
->ms_acpi_handle
;
119 cpu_acpi_pstate_t
*req_pstate
;
122 req_pstate
= (cpu_acpi_pstate_t
*)CPU_ACPI_PSTATES(handle
);
123 req_pstate
+= req_state
;
125 DTRACE_PROBE1(pwrnow_transition_freq
, uint32_t,
126 CPU_ACPI_FREQ(req_pstate
));
129 * Initiate the processor p-state change.
131 ctrl
= CPU_ACPI_PSTATE_CTRL(req_pstate
);
132 write_ctrl(handle
, ctrl
);
134 if (mach_state
->ms_turbo
!= NULL
)
135 cpupm_record_turbo_info(mach_state
->ms_turbo
,
136 mach_state
->ms_pstate
.cma_state
.pstate
, req_state
);
138 mach_state
->ms_pstate
.cma_state
.pstate
= req_state
;
139 cpu_set_curr_clock((uint64_t)CPU_ACPI_FREQ(req_pstate
) * 1000000);
143 pwrnow_power(cpuset_t set
, uint32_t req_state
)
146 * If thread is already running on target CPU then just
147 * make the transition request. Otherwise, we'll need to
151 if (CPU_IN_SET(set
, CPU
->cpu_id
)) {
152 pwrnow_pstate_transition(req_state
);
153 CPUSET_DEL(set
, CPU
->cpu_id
);
155 if (!CPUSET_ISNULL(set
)) {
156 xc_call((xc_arg_t
)req_state
, NULL
, NULL
,
157 CPUSET2BV(set
), (xc_func_t
)pwrnow_pstate_transition
);
163 * Validate that this processor supports PowerNow! and if so,
164 * get the P-state data from ACPI and cache it.
167 pwrnow_init(cpu_t
*cp
)
169 cpupm_mach_state_t
*mach_state
=
170 (cpupm_mach_state_t
*)cp
->cpu_m
.mcpu_pm_mach_state
;
171 cpu_acpi_handle_t handle
= mach_state
->ms_acpi_handle
;
172 cpu_acpi_pct_t
*pct_stat
;
173 static int logged
= 0;
175 PWRNOW_DEBUG(("pwrnow_init: processor %d\n", cp
->cpu_id
));
178 * Cache the P-state specific ACPI data.
180 if (cpu_acpi_cache_pstate_data(handle
) != 0) {
182 cmn_err(CE_NOTE
, "!PowerNow! support is being "
183 "disabled due to errors parsing ACPI P-state "
184 "objects exported by BIOS.");
188 return (PWRNOW_RET_NO_PM
);
191 pct_stat
= CPU_ACPI_PCT_STATUS(handle
);
192 switch (pct_stat
->cr_addrspace_id
) {
193 case ACPI_ADR_SPACE_FIXED_HARDWARE
:
194 PWRNOW_DEBUG(("Transitions will use fixed hardware\n"));
197 cmn_err(CE_WARN
, "!_PCT configured for unsupported "
198 "addrspace = %d.", pct_stat
->cr_addrspace_id
);
199 cmn_err(CE_NOTE
, "!CPU power management will not function.");
201 return (PWRNOW_RET_NO_PM
);
204 cpupm_alloc_domains(cp
, CPUPM_P_STATES
);
207 * Check for Core Performance Boost support
209 if (pwrnow_cpb_supported())
210 mach_state
->ms_turbo
= cpupm_turbo_init(cp
);
212 PWRNOW_DEBUG(("Processor %d succeeded.\n", cp
->cpu_id
))
213 return (PWRNOW_RET_SUCCESS
);
217 * Free resources allocated by pwrnow_init().
220 pwrnow_fini(cpu_t
*cp
)
222 cpupm_mach_state_t
*mach_state
=
223 (cpupm_mach_state_t
*)(cp
->cpu_m
.mcpu_pm_mach_state
);
224 cpu_acpi_handle_t handle
= mach_state
->ms_acpi_handle
;
226 cpupm_free_domains(&cpupm_pstate_domains
);
227 cpu_acpi_free_pstate_data(handle
);
229 if (mach_state
->ms_turbo
!= NULL
)
230 cpupm_turbo_fini(mach_state
->ms_turbo
);
231 mach_state
->ms_turbo
= NULL
;
237 struct cpuid_regs cpu_regs
;
239 /* Required features */
240 if (!is_x86_feature(x86_featureset
, X86FSET_CPUID
) ||
241 !is_x86_feature(x86_featureset
, X86FSET_MSR
)) {
242 PWRNOW_DEBUG(("No CPUID or MSR support."));
247 * Get the Advanced Power Management Information.
249 cpu_regs
.cp_eax
= 0x80000007;
250 (void) __cpuid_insn(&cpu_regs
);
253 * We currently only support CPU power management of
254 * processors that are P-state TSC invariant
256 if (!(cpu_regs
.cp_edx
& AMD_CPUID_TSC_CONSTANT
)) {
257 PWRNOW_DEBUG(("No support for CPUs that are not P-state "
258 "TSC invariant.\n"));
263 * We only support the "Fire and Forget" style of PowerNow! (i.e.,
264 * single MSR write to change speed).
266 if (!(cpu_regs
.cp_edx
& AMD_CPUID_PSTATE_HARDWARE
)) {
267 PWRNOW_DEBUG(("Hardware P-State control is not supported.\n"));
274 pwrnow_cpb_supported(void)
276 struct cpuid_regs cpu_regs
;
278 /* Required features */
279 if (!is_x86_feature(x86_featureset
, X86FSET_CPUID
) ||
280 !is_x86_feature(x86_featureset
, X86FSET_MSR
)) {
281 PWRNOW_DEBUG(("No CPUID or MSR support."));
286 * Get the Advanced Power Management Information.
288 cpu_regs
.cp_eax
= 0x80000007;
289 (void) __cpuid_insn(&cpu_regs
);
291 if (!(cpu_regs
.cp_edx
& AMD_CPUID_CPB
))
298 pwrnow_stop(cpu_t
*cp
)
300 cpupm_mach_state_t
*mach_state
=
301 (cpupm_mach_state_t
*)(cp
->cpu_m
.mcpu_pm_mach_state
);
302 cpu_acpi_handle_t handle
= mach_state
->ms_acpi_handle
;
304 cpupm_remove_domains(cp
, CPUPM_P_STATES
, &cpupm_pstate_domains
);
305 cpu_acpi_free_pstate_data(handle
);
307 if (mach_state
->ms_turbo
!= NULL
)
308 cpupm_turbo_fini(mach_state
->ms_turbo
);
309 mach_state
->ms_turbo
= NULL
;