14b3651bdSmh /* 24b3651bdSmh * CDDL HEADER START 34b3651bdSmh * 44b3651bdSmh * The contents of this file are subject to the terms of the 54b3651bdSmh * Common Development and Distribution License (the "License"). 64b3651bdSmh * You may not use this file except in compliance with the License. 74b3651bdSmh * 84b3651bdSmh * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 94b3651bdSmh * or http://www.opensolaris.org/os/licensing. 104b3651bdSmh * See the License for the specific language governing permissions 114b3651bdSmh * and limitations under the License. 124b3651bdSmh * 134b3651bdSmh * When distributing Covered Code, include this CDDL HEADER in each 144b3651bdSmh * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 154b3651bdSmh * If applicable, add the following below this CDDL HEADER, with the 164b3651bdSmh * fields enclosed by brackets "[]" replaced with your own identifying 174b3651bdSmh * information: Portions Copyright [yyyy] [name of copyright owner] 184b3651bdSmh * 194b3651bdSmh * CDDL HEADER END 204b3651bdSmh */ 214b3651bdSmh /* 22*7417cfdeSKuriakose Kuruvilla * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. 234b3651bdSmh */ 244b3651bdSmh 254b3651bdSmh #include <sys/x86_archext.h> 264b3651bdSmh #include <sys/machsystm.h> 274b3651bdSmh #include <sys/x_call.h> 284b3651bdSmh #include <sys/acpi/acpi.h> 294b3651bdSmh #include <sys/acpica.h> 304b3651bdSmh #include <sys/pwrnow.h> 314b3651bdSmh #include <sys/cpu_acpi.h> 324b3651bdSmh #include <sys/cpupm.h> 334b3651bdSmh #include <sys/dtrace.h> 344b3651bdSmh #include <sys/sdt.h> 354b3651bdSmh 360e751525SEric Saxe static int pwrnow_init(cpu_t *); 370e751525SEric Saxe static void pwrnow_fini(cpu_t *); 380e751525SEric Saxe static void pwrnow_power(cpuset_t, uint32_t); 39444f66e7SMark Haywood static void pwrnow_stop(cpu_t *); 407f606aceSMark Haywood 417f606aceSMark Haywood /* 427f606aceSMark Haywood * Interfaces for modules implementing AMD's PowerNow!. 437f606aceSMark Haywood */ 440e751525SEric Saxe cpupm_state_ops_t pwrnow_ops = { 457f606aceSMark Haywood "PowerNow! Technology", 467f606aceSMark Haywood pwrnow_init, 477f606aceSMark Haywood pwrnow_fini, 48444f66e7SMark Haywood pwrnow_power, 49444f66e7SMark Haywood pwrnow_stop 507f606aceSMark Haywood }; 517f606aceSMark Haywood 524b3651bdSmh /* 534b3651bdSmh * Error returns 544b3651bdSmh */ 554b3651bdSmh #define PWRNOW_RET_SUCCESS 0x00 564b3651bdSmh #define PWRNOW_RET_NO_PM 0x01 574b3651bdSmh #define PWRNOW_RET_UNSUP_STATE 0x02 584b3651bdSmh #define PWRNOW_RET_TRANS_INCOMPLETE 0x03 594b3651bdSmh 604b3651bdSmh #define PWRNOW_LATENCY_WAIT 10 614b3651bdSmh 624b3651bdSmh /* 634b3651bdSmh * MSR registers for changing and reading processor power state. 644b3651bdSmh */ 654b3651bdSmh #define PWRNOW_PERF_CTL_MSR 0xC0010062 664b3651bdSmh #define PWRNOW_PERF_STATUS_MSR 0xC0010063 674b3651bdSmh 684b3651bdSmh #define AMD_CPUID_PSTATE_HARDWARE (1<<7) 694b3651bdSmh #define AMD_CPUID_TSC_CONSTANT (1<<8) 704b3651bdSmh 714b3651bdSmh /* 724b3651bdSmh * Debugging support 734b3651bdSmh */ 744b3651bdSmh #ifdef DEBUG 754b3651bdSmh volatile int pwrnow_debug = 0; 764b3651bdSmh #define PWRNOW_DEBUG(arglist) if (pwrnow_debug) printf arglist; 774b3651bdSmh #else 784b3651bdSmh #define PWRNOW_DEBUG(arglist) 794b3651bdSmh #endif 804b3651bdSmh 814b3651bdSmh /* 824b3651bdSmh * Write the ctrl register. 834b3651bdSmh */ 840e751525SEric Saxe static void 854b3651bdSmh write_ctrl(cpu_acpi_handle_t handle, uint32_t ctrl) 864b3651bdSmh { 874b3651bdSmh cpu_acpi_pct_t *pct_ctrl; 884b3651bdSmh uint64_t reg; 894b3651bdSmh 904b3651bdSmh pct_ctrl = CPU_ACPI_PCT_CTRL(handle); 914b3651bdSmh 927f606aceSMark Haywood switch (pct_ctrl->cr_addrspace_id) { 934b3651bdSmh case ACPI_ADR_SPACE_FIXED_HARDWARE: 944b3651bdSmh reg = ctrl; 954b3651bdSmh wrmsr(PWRNOW_PERF_CTL_MSR, reg); 964b3651bdSmh break; 974b3651bdSmh 984b3651bdSmh default: 994b3651bdSmh DTRACE_PROBE1(pwrnow_ctrl_unsupported_type, uint8_t, 1007f606aceSMark Haywood pct_ctrl->cr_addrspace_id); 1010e751525SEric Saxe return; 1024b3651bdSmh } 1034b3651bdSmh 1044b3651bdSmh DTRACE_PROBE1(pwrnow_ctrl_write, uint32_t, ctrl); 1054b3651bdSmh } 1064b3651bdSmh 1074b3651bdSmh /* 1084b3651bdSmh * Transition the current processor to the requested state. 1094b3651bdSmh */ 1107f606aceSMark Haywood static void 1110e751525SEric Saxe pwrnow_pstate_transition(uint32_t req_state) 1124b3651bdSmh { 1130e751525SEric Saxe cpupm_mach_state_t *mach_state = 1140e751525SEric Saxe (cpupm_mach_state_t *)CPU->cpu_m.mcpu_pm_mach_state; 1150e751525SEric Saxe cpu_acpi_handle_t handle = mach_state->ms_acpi_handle; 1164b3651bdSmh cpu_acpi_pstate_t *req_pstate; 1174b3651bdSmh uint32_t ctrl; 1184b3651bdSmh 1197f606aceSMark Haywood req_pstate = (cpu_acpi_pstate_t *)CPU_ACPI_PSTATES(handle); 1207f606aceSMark Haywood req_pstate += req_state; 1210e751525SEric Saxe 1224b3651bdSmh DTRACE_PROBE1(pwrnow_transition_freq, uint32_t, 1234b3651bdSmh CPU_ACPI_FREQ(req_pstate)); 1244b3651bdSmh 1254b3651bdSmh /* 1264b3651bdSmh * Initiate the processor p-state change. 1274b3651bdSmh */ 1287f606aceSMark Haywood ctrl = CPU_ACPI_PSTATE_CTRL(req_pstate); 1290e751525SEric Saxe write_ctrl(handle, ctrl); 1304b3651bdSmh 1310e751525SEric Saxe mach_state->ms_pstate.cma_state.pstate = req_state; 1320e751525SEric Saxe cpu_set_curr_clock((uint64_t)CPU_ACPI_FREQ(req_pstate) * 1000000); 1334b3651bdSmh } 1344b3651bdSmh 1350e751525SEric Saxe static void 1360e751525SEric Saxe pwrnow_power(cpuset_t set, uint32_t req_state) 1374b3651bdSmh { 138375e0503SMark Haywood /* 139375e0503SMark Haywood * If thread is already running on target CPU then just 140375e0503SMark Haywood * make the transition request. Otherwise, we'll need to 141375e0503SMark Haywood * make a cross-call. 142375e0503SMark Haywood */ 1434b3651bdSmh kpreempt_disable(); 1440e751525SEric Saxe if (CPU_IN_SET(set, CPU->cpu_id)) { 1450e751525SEric Saxe pwrnow_pstate_transition(req_state); 1460e751525SEric Saxe CPUSET_DEL(set, CPU->cpu_id); 1470e751525SEric Saxe } 1480e751525SEric Saxe if (!CPUSET_ISNULL(set)) { 149f34a7178SJoe Bonasera xc_call((xc_arg_t)req_state, NULL, NULL, 150f34a7178SJoe Bonasera CPUSET2BV(set), (xc_func_t)pwrnow_pstate_transition); 151375e0503SMark Haywood } 1524b3651bdSmh kpreempt_enable(); 1534b3651bdSmh } 1544b3651bdSmh 1554b3651bdSmh /* 1564b3651bdSmh * Validate that this processor supports PowerNow! and if so, 1574b3651bdSmh * get the P-state data from ACPI and cache it. 1584b3651bdSmh */ 1597f606aceSMark Haywood static int 1600e751525SEric Saxe pwrnow_init(cpu_t *cp) 1614b3651bdSmh { 1620e751525SEric Saxe cpupm_mach_state_t *mach_state = 1630e751525SEric Saxe (cpupm_mach_state_t *)cp->cpu_m.mcpu_pm_mach_state; 1640e751525SEric Saxe cpu_acpi_handle_t handle = mach_state->ms_acpi_handle; 1654b3651bdSmh cpu_acpi_pct_t *pct_stat; 1664b3651bdSmh 1670e751525SEric Saxe PWRNOW_DEBUG(("pwrnow_init: processor %d\n", cp->cpu_id)); 1684b3651bdSmh 1694b3651bdSmh /* 1707f606aceSMark Haywood * Cache the P-state specific ACPI data. 1714b3651bdSmh */ 1727f606aceSMark Haywood if (cpu_acpi_cache_pstate_data(handle) != 0) { 17300f97612SMark Haywood cmn_err(CE_NOTE, "!PowerNow! support is being " 17400f97612SMark Haywood "disabled due to errors parsing ACPI P-state objects " 17500f97612SMark Haywood "exported by BIOS."); 1760e751525SEric Saxe pwrnow_fini(cp); 1774b3651bdSmh return (PWRNOW_RET_NO_PM); 1784b3651bdSmh } 1794b3651bdSmh 1804b3651bdSmh pct_stat = CPU_ACPI_PCT_STATUS(handle); 1817f606aceSMark Haywood switch (pct_stat->cr_addrspace_id) { 1824b3651bdSmh case ACPI_ADR_SPACE_FIXED_HARDWARE: 1834b3651bdSmh PWRNOW_DEBUG(("Transitions will use fixed hardware\n")); 1844b3651bdSmh break; 1854b3651bdSmh default: 1864b3651bdSmh cmn_err(CE_WARN, "!_PCT configured for unsupported " 1877f606aceSMark Haywood "addrspace = %d.", pct_stat->cr_addrspace_id); 1884b3651bdSmh cmn_err(CE_NOTE, "!CPU power management will not function."); 1890e751525SEric Saxe pwrnow_fini(cp); 1904b3651bdSmh return (PWRNOW_RET_NO_PM); 1914b3651bdSmh } 1924b3651bdSmh 1930e751525SEric Saxe cpupm_alloc_domains(cp, CPUPM_P_STATES); 1944b3651bdSmh 1950e751525SEric Saxe PWRNOW_DEBUG(("Processor %d succeeded.\n", cp->cpu_id)) 1964b3651bdSmh return (PWRNOW_RET_SUCCESS); 1974b3651bdSmh } 1984b3651bdSmh 1994b3651bdSmh /* 2004b3651bdSmh * Free resources allocated by pwrnow_init(). 2014b3651bdSmh */ 2027f606aceSMark Haywood static void 2030e751525SEric Saxe pwrnow_fini(cpu_t *cp) 2044b3651bdSmh { 2050e751525SEric Saxe cpupm_mach_state_t *mach_state = 2060e751525SEric Saxe (cpupm_mach_state_t *)(cp->cpu_m.mcpu_pm_mach_state); 2070e751525SEric Saxe cpu_acpi_handle_t handle = mach_state->ms_acpi_handle; 2087f606aceSMark Haywood 2090e751525SEric Saxe cpupm_free_domains(&cpupm_pstate_domains); 2107f606aceSMark Haywood cpu_acpi_free_pstate_data(handle); 2117f606aceSMark Haywood } 2127f606aceSMark Haywood 2137f606aceSMark Haywood boolean_t 2147f606aceSMark Haywood pwrnow_supported() 2157f606aceSMark Haywood { 2167f606aceSMark Haywood struct cpuid_regs cpu_regs; 2177f606aceSMark Haywood 2187f606aceSMark Haywood /* Required features */ 219*7417cfdeSKuriakose Kuruvilla if (!is_x86_feature(x86_featureset, X86FSET_CPUID) || 220*7417cfdeSKuriakose Kuruvilla !is_x86_feature(x86_featureset, X86FSET_MSR)) { 2217f606aceSMark Haywood PWRNOW_DEBUG(("No CPUID or MSR support.")); 2227f606aceSMark Haywood return (B_FALSE); 2237f606aceSMark Haywood } 2247f606aceSMark Haywood 2257f606aceSMark Haywood /* 2267f606aceSMark Haywood * Get the Advanced Power Management Information. 2277f606aceSMark Haywood */ 2287f606aceSMark Haywood cpu_regs.cp_eax = 0x80000007; 2297f606aceSMark Haywood (void) __cpuid_insn(&cpu_regs); 2307f606aceSMark Haywood 2317f606aceSMark Haywood /* 2327f606aceSMark Haywood * We currently only support CPU power management of 2337f606aceSMark Haywood * processors that are P-state TSC invariant 2347f606aceSMark Haywood */ 2357f606aceSMark Haywood if (!(cpu_regs.cp_edx & AMD_CPUID_TSC_CONSTANT)) { 2367f606aceSMark Haywood PWRNOW_DEBUG(("No support for CPUs that are not P-state " 2377f606aceSMark Haywood "TSC invariant.\n")); 2387f606aceSMark Haywood return (B_FALSE); 2397f606aceSMark Haywood } 2407f606aceSMark Haywood 2417f606aceSMark Haywood /* 2427f606aceSMark Haywood * We only support the "Fire and Forget" style of PowerNow! (i.e., 2437f606aceSMark Haywood * single MSR write to change speed). 2447f606aceSMark Haywood */ 2457f606aceSMark Haywood if (!(cpu_regs.cp_edx & AMD_CPUID_PSTATE_HARDWARE)) { 2467f606aceSMark Haywood PWRNOW_DEBUG(("Hardware P-State control is not supported.\n")); 2477f606aceSMark Haywood return (B_FALSE); 2487f606aceSMark Haywood } 2497f606aceSMark Haywood return (B_TRUE); 2504b3651bdSmh } 251444f66e7SMark Haywood 252444f66e7SMark Haywood static void 253444f66e7SMark Haywood pwrnow_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 } 262