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