14b3651bmh/* 24b3651bmh * CDDL HEADER START 34b3651bmh * 44b3651bmh * The contents of this file are subject to the terms of the 54b3651bmh * Common Development and Distribution License (the "License"). 64b3651bmh * You may not use this file except in compliance with the License. 74b3651bmh * 84b3651bmh * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 94b3651bmh * or http://www.opensolaris.org/os/licensing. 104b3651bmh * See the License for the specific language governing permissions 114b3651bmh * and limitations under the License. 124b3651bmh * 134b3651bmh * When distributing Covered Code, include this CDDL HEADER in each 144b3651bmh * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 154b3651bmh * If applicable, add the following below this CDDL HEADER, with the 164b3651bmh * fields enclosed by brackets "[]" replaced with your own identifying 174b3651bmh * information: Portions Copyright [yyyy] [name of copyright owner] 184b3651bmh * 194b3651bmh * CDDL HEADER END 204b3651bmh */ 214b3651bmh/* 227417cfdKuriakose Kuruvilla * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. 234b3651bmh */ 244b3651bmh 254b3651bmh#include <sys/x86_archext.h> 264b3651bmh#include <sys/machsystm.h> 274b3651bmh#include <sys/x_call.h> 284b3651bmh#include <sys/acpi/acpi.h> 294b3651bmh#include <sys/acpica.h> 304b3651bmh#include <sys/pwrnow.h> 314b3651bmh#include <sys/cpu_acpi.h> 324b3651bmh#include <sys/cpupm.h> 334b3651bmh#include <sys/dtrace.h> 344b3651bmh#include <sys/sdt.h> 354b3651bmh 360e75152Eric Saxestatic int pwrnow_init(cpu_t *); 370e75152Eric Saxestatic void pwrnow_fini(cpu_t *); 380e75152Eric Saxestatic void pwrnow_power(cpuset_t, uint32_t); 39444f66eMark Haywoodstatic void pwrnow_stop(cpu_t *); 407f606acMark Haywood 415951cedHans Rosenfeldstatic boolean_t pwrnow_cpb_supported(void); 425951cedHans Rosenfeld 437f606acMark Haywood/* 447f606acMark Haywood * Interfaces for modules implementing AMD's PowerNow!. 457f606acMark Haywood */ 460e75152Eric Saxecpupm_state_ops_t pwrnow_ops = { 477f606acMark Haywood "PowerNow! Technology", 487f606acMark Haywood pwrnow_init, 497f606acMark Haywood pwrnow_fini, 50444f66eMark Haywood pwrnow_power, 51444f66eMark Haywood pwrnow_stop 527f606acMark Haywood}; 537f606acMark Haywood 544b3651bmh/* 554b3651bmh * Error returns 564b3651bmh */ 574b3651bmh#define PWRNOW_RET_SUCCESS 0x00 584b3651bmh#define PWRNOW_RET_NO_PM 0x01 594b3651bmh#define PWRNOW_RET_UNSUP_STATE 0x02 604b3651bmh#define PWRNOW_RET_TRANS_INCOMPLETE 0x03 614b3651bmh 624b3651bmh#define PWRNOW_LATENCY_WAIT 10 634b3651bmh 644b3651bmh/* 654b3651bmh * MSR registers for changing and reading processor power state. 664b3651bmh */ 674b3651bmh#define PWRNOW_PERF_CTL_MSR 0xC0010062 684b3651bmh#define PWRNOW_PERF_STATUS_MSR 0xC0010063 694b3651bmh 704b3651bmh#define AMD_CPUID_PSTATE_HARDWARE (1<<7) 714b3651bmh#define AMD_CPUID_TSC_CONSTANT (1<<8) 725951cedHans Rosenfeld#define AMD_CPUID_CPB (1<<9) 734b3651bmh 744b3651bmh/* 754b3651bmh * Debugging support 764b3651bmh */ 774b3651bmh#ifdef DEBUG 784b3651bmhvolatile int pwrnow_debug = 0; 794b3651bmh#define PWRNOW_DEBUG(arglist) if (pwrnow_debug) printf arglist; 804b3651bmh#else 814b3651bmh#define PWRNOW_DEBUG(arglist) 824b3651bmh#endif 834b3651bmh 844b3651bmh/* 854b3651bmh * Write the ctrl register. 864b3651bmh */ 870e75152Eric Saxestatic void 884b3651bmhwrite_ctrl(cpu_acpi_handle_t handle, uint32_t ctrl) 894b3651bmh{ 904b3651bmh cpu_acpi_pct_t *pct_ctrl; 914b3651bmh uint64_t reg; 924b3651bmh 934b3651bmh pct_ctrl = CPU_ACPI_PCT_CTRL(handle); 944b3651bmh 957f606acMark Haywood switch (pct_ctrl->cr_addrspace_id) { 964b3651bmh case ACPI_ADR_SPACE_FIXED_HARDWARE: 974b3651bmh reg = ctrl; 984b3651bmh wrmsr(PWRNOW_PERF_CTL_MSR, reg); 994b3651bmh break; 1004b3651bmh 1014b3651bmh default: 1024b3651bmh DTRACE_PROBE1(pwrnow_ctrl_unsupported_type, uint8_t, 1037f606acMark Haywood pct_ctrl->cr_addrspace_id); 1040e75152Eric Saxe return; 1054b3651bmh } 1064b3651bmh 1074b3651bmh DTRACE_PROBE1(pwrnow_ctrl_write, uint32_t, ctrl); 1084b3651bmh} 1094b3651bmh 1104b3651bmh/* 1114b3651bmh * Transition the current processor to the requested state. 1124b3651bmh */ 113027bcc9Toomas Soomestatic int 114027bcc9Toomas Soomepwrnow_pstate_transition(xc_arg_t arg1, xc_arg_t arg2 __unused, 115027bcc9Toomas Soome xc_arg_t arg3 __unused) 1164b3651bmh{ 117027bcc9Toomas Soome uint32_t req_state = (uint32_t)arg1; 1180e75152Eric Saxe cpupm_mach_state_t *mach_state = 1190e75152Eric Saxe (cpupm_mach_state_t *)CPU->cpu_m.mcpu_pm_mach_state; 1200e75152Eric Saxe cpu_acpi_handle_t handle = mach_state->ms_acpi_handle; 1214b3651bmh cpu_acpi_pstate_t *req_pstate; 1224b3651bmh uint32_t ctrl; 1234b3651bmh 1247f606acMark Haywood req_pstate = (cpu_acpi_pstate_t *)CPU_ACPI_PSTATES(handle); 1257f606acMark Haywood req_pstate += req_state; 1260e75152Eric Saxe 1274b3651bmh DTRACE_PROBE1(pwrnow_transition_freq, uint32_t, 1284b3651bmh CPU_ACPI_FREQ(req_pstate)); 1294b3651bmh 1304b3651bmh /* 1314b3651bmh * Initiate the processor p-state change. 1324b3651bmh */ 1337f606acMark Haywood ctrl = CPU_ACPI_PSTATE_CTRL(req_pstate); 1340e75152Eric Saxe write_ctrl(handle, ctrl); 1354b3651bmh 1365951cedHans Rosenfeld if (mach_state->ms_turbo != NULL) 1375951cedHans Rosenfeld cpupm_record_turbo_info(mach_state->ms_turbo, 1385951cedHans Rosenfeld mach_state->ms_pstate.cma_state.pstate, req_state); 1395951cedHans Rosenfeld 1400e75152Eric Saxe mach_state->ms_pstate.cma_state.pstate = req_state; 1410e75152Eric Saxe cpu_set_curr_clock((uint64_t)CPU_ACPI_FREQ(req_pstate) * 1000000); 142027bcc9Toomas Soome return (0); 1434b3651bmh} 1444b3651bmh 1450e75152Eric Saxestatic void 1460e75152Eric Saxepwrnow_power(cpuset_t set, uint32_t req_state) 1474b3651bmh{ 148375e050Mark Haywood /* 149375e050Mark Haywood * If thread is already running on target CPU then just 150375e050Mark Haywood * make the transition request. Otherwise, we'll need to 151375e050Mark Haywood * make a cross-call. 152375e050Mark Haywood */ 1534b3651bmh kpreempt_disable(); 1540e75152Eric Saxe if (CPU_IN_SET(set, CPU->cpu_id)) { 155027bcc9Toomas Soome (void) pwrnow_pstate_transition(req_state, 0, 0); 1560e75152Eric Saxe CPUSET_DEL(set, CPU->cpu_id); 1570e75152Eric Saxe } 1580e75152Eric Saxe if (!CPUSET_ISNULL(set)) { 1594da9975Toomas Soome xc_call((xc_arg_t)req_state, 0, 0, 160027bcc9Toomas Soome CPUSET2BV(set), pwrnow_pstate_transition); 161375e050Mark Haywood } 1624b3651bmh kpreempt_enable(); 1634b3651bmh} 1644b3651bmh 1654b3651bmh/* 1664b3651bmh * Validate that this processor supports PowerNow! and if so, 1674b3651bmh * get the P-state data from ACPI and cache it. 1684b3651bmh */ 1697f606acMark Haywoodstatic int 1700e75152Eric Saxepwrnow_init(cpu_t *cp) 1714b3651bmh{ 1720e75152Eric Saxe cpupm_mach_state_t *mach_state = 1730e75152Eric Saxe (cpupm_mach_state_t *)cp->cpu_m.mcpu_pm_mach_state; 1740e75152Eric Saxe cpu_acpi_handle_t handle = mach_state->ms_acpi_handle; 1754b3651bmh cpu_acpi_pct_t *pct_stat; 176511588bYuri Pankov static int logged = 0; 1774b3651bmh 1780e75152Eric Saxe PWRNOW_DEBUG(("pwrnow_init: processor %d\n", cp->cpu_id)); 1794b3651bmh 1804b3651bmh /* 1817f606acMark Haywood * Cache the P-state specific ACPI data. 1824b3651bmh */ 1837f606acMark Haywood if (cpu_acpi_cache_pstate_data(handle) != 0) { 184511588bYuri Pankov if (!logged) { 185511588bYuri Pankov cmn_err(CE_NOTE, "!PowerNow! support is being " 186511588bYuri Pankov "disabled due to errors parsing ACPI P-state " 187511588bYuri Pankov "objects exported by BIOS."); 188511588bYuri Pankov logged = 1; 189511588bYuri Pankov } 1900e75152Eric Saxe pwrnow_fini(cp); 1914b3651bmh return (PWRNOW_RET_NO_PM); 1924b3651bmh } 1934b3651bmh 1944b3651bmh pct_stat = CPU_ACPI_PCT_STATUS(handle); 1957f606acMark Haywood switch (pct_stat->cr_addrspace_id) { 1964b3651bmh case ACPI_ADR_SPACE_FIXED_HARDWARE: 1974b3651bmh PWRNOW_DEBUG(("Transitions will use fixed hardware\n")); 1984b3651bmh break; 1994b3651bmh default: 2004b3651bmh cmn_err(CE_WARN, "!_PCT configured for unsupported " 2017f606acMark Haywood "addrspace = %d.", pct_stat->cr_addrspace_id); 2024b3651bmh cmn_err(CE_NOTE, "!CPU power management will not function."); 2030e75152Eric Saxe pwrnow_fini(cp); 2044b3651bmh return (PWRNOW_RET_NO_PM); 2054b3651bmh } 2064b3651bmh 2070e75152Eric Saxe cpupm_alloc_domains(cp, CPUPM_P_STATES); 2084b3651bmh 2095951cedHans Rosenfeld /* 2105951cedHans Rosenfeld * Check for Core Performance Boost support 2115951cedHans Rosenfeld */ 2125951cedHans Rosenfeld if (pwrnow_cpb_supported()) 2135951cedHans Rosenfeld mach_state->ms_turbo = cpupm_turbo_init(cp); 2145951cedHans Rosenfeld 2150e75152Eric Saxe PWRNOW_DEBUG(("Processor %d succeeded.\n", cp->cpu_id)) 2164b3651bmh return (PWRNOW_RET_SUCCESS); 2174b3651bmh} 2184b3651bmh 2194b3651bmh/* 2204b3651bmh * Free resources allocated by pwrnow_init(). 2214b3651bmh */ 2227f606acMark Haywoodstatic void 2230e75152Eric Saxepwrnow_fini(cpu_t *cp) 2244b3651bmh{ 2250e75152Eric Saxe cpupm_mach_state_t *mach_state = 2260e75152Eric Saxe (cpupm_mach_state_t *)(cp->cpu_m.mcpu_pm_mach_state); 2270e75152Eric Saxe cpu_acpi_handle_t handle = mach_state->ms_acpi_handle; 2287f606acMark Haywood 2290e75152Eric Saxe cpupm_free_domains(&cpupm_pstate_domains); 2307f606acMark Haywood cpu_acpi_free_pstate_data(handle); 2315951cedHans Rosenfeld 2325951cedHans Rosenfeld if (mach_state->ms_turbo != NULL) 2335951cedHans Rosenfeld cpupm_turbo_fini(mach_state->ms_turbo); 2345951cedHans Rosenfeld mach_state->ms_turbo = NULL; 2357f606acMark Haywood} 2367f606acMark Haywood 2377f606acMark Haywoodboolean_t 2387f606acMark Haywoodpwrnow_supported() 2397f606acMark Haywood{ 2407f606acMark Haywood struct cpuid_regs cpu_regs; 2417f606acMark Haywood 2427f606acMark Haywood /* Required features */ 2437417cfdKuriakose Kuruvilla if (!is_x86_feature(x86_featureset, X86FSET_CPUID) || 2447417cfdKuriakose Kuruvilla !is_x86_feature(x86_featureset, X86FSET_MSR)) { 2457f606acMark Haywood PWRNOW_DEBUG(("No CPUID or MSR support.")); 2467f606acMark Haywood return (B_FALSE); 2477f606acMark Haywood } 2487f606acMark Haywood 2497f606acMark Haywood /* 2507f606acMark Haywood * Get the Advanced Power Management Information. 2517f606acMark Haywood */ 2527f606acMark Haywood cpu_regs.cp_eax = 0x80000007; 2537f606acMark Haywood (void) __cpuid_insn(&cpu_regs); 2547f606acMark Haywood 2557f606acMark Haywood /* 2567f606acMark Haywood * We currently only support CPU power management of 2577f606acMark Haywood * processors that are P-state TSC invariant 2587f606acMark Haywood */ 2597f606acMark Haywood if (!(cpu_regs.cp_edx & AMD_CPUID_TSC_CONSTANT)) { 2607f606acMark Haywood PWRNOW_DEBUG(("No support for CPUs that are not P-state " 2617f606acMark Haywood "TSC invariant.\n")); 2627f606acMark Haywood return (B_FALSE); 2637f606acMark Haywood } 2647f606acMark Haywood 2657f606acMark Haywood /* 2667f606acMark Haywood * We only support the "Fire and Forget" style of PowerNow! (i.e., 2677f606acMark Haywood * single MSR write to change speed). 2687f606acMark Haywood */ 2697f606acMark Haywood if (!(cpu_regs.cp_edx & AMD_CPUID_PSTATE_HARDWARE)) { 2707f606acMark Haywood PWRNOW_DEBUG(("Hardware P-State control is not supported.\n")); 2717f606acMark Haywood return (B_FALSE); 2727f606acMark Haywood } 2737f606acMark Haywood return (B_TRUE); 2744b3651bmh} 275444f66eMark Haywood 2765951cedHans Rosenfeldstatic boolean_t 2775951cedHans Rosenfeldpwrnow_cpb_supported(void) 2785951cedHans Rosenfeld{ 2795951cedHans Rosenfeld struct cpuid_regs cpu_regs; 2805951cedHans Rosenfeld 2815951cedHans Rosenfeld /* Required features */ 2825951cedHans Rosenfeld if (!is_x86_feature(x86_featureset, X86FSET_CPUID) || 2835951cedHans Rosenfeld !is_x86_feature(x86_featureset, X86FSET_MSR)) { 2845951cedHans Rosenfeld PWRNOW_DEBUG(("No CPUID or MSR support.")); 2855951cedHans Rosenfeld return (B_FALSE); 2865951cedHans Rosenfeld } 2875951cedHans Rosenfeld 2885951cedHans Rosenfeld /* 2895951cedHans Rosenfeld * Get the Advanced Power Management Information. 2905951cedHans Rosenfeld */ 2915951cedHans Rosenfeld cpu_regs.cp_eax = 0x80000007; 2925951cedHans Rosenfeld (void) __cpuid_insn(&cpu_regs); 2935951cedHans Rosenfeld 2945951cedHans Rosenfeld if (!(cpu_regs.cp_edx & AMD_CPUID_CPB)) 2955951cedHans Rosenfeld return (B_FALSE); 2965951cedHans Rosenfeld 2975951cedHans Rosenfeld return (B_TRUE); 2985951cedHans Rosenfeld} 2995951cedHans Rosenfeld 300444f66eMark Haywoodstatic void 301444f66eMark Haywoodpwrnow_stop(cpu_t *cp) 302444f66eMark Haywood{ 303444f66eMark Haywood cpupm_mach_state_t *mach_state = 304444f66eMark Haywood (cpupm_mach_state_t *)(cp->cpu_m.mcpu_pm_mach_state); 305444f66eMark Haywood cpu_acpi_handle_t handle = mach_state->ms_acpi_handle; 306444f66eMark Haywood 307444f66eMark Haywood cpupm_remove_domains(cp, CPUPM_P_STATES, &cpupm_pstate_domains); 308444f66eMark Haywood cpu_acpi_free_pstate_data(handle); 3095951cedHans Rosenfeld 3105951cedHans Rosenfeld if (mach_state->ms_turbo != NULL) 3115951cedHans Rosenfeld cpupm_turbo_fini(mach_state->ms_turbo); 3125951cedHans Rosenfeld mach_state->ms_turbo = NULL; 313444f66eMark Haywood} 314