18906SEric.Saxe@Sun.COM /*
28906SEric.Saxe@Sun.COM * CDDL HEADER START
38906SEric.Saxe@Sun.COM *
48906SEric.Saxe@Sun.COM * The contents of this file are subject to the terms of the
58906SEric.Saxe@Sun.COM * Common Development and Distribution License (the "License").
68906SEric.Saxe@Sun.COM * You may not use this file except in compliance with the License.
78906SEric.Saxe@Sun.COM *
88906SEric.Saxe@Sun.COM * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
98906SEric.Saxe@Sun.COM * or http://www.opensolaris.org/os/licensing.
108906SEric.Saxe@Sun.COM * See the License for the specific language governing permissions
118906SEric.Saxe@Sun.COM * and limitations under the License.
128906SEric.Saxe@Sun.COM *
138906SEric.Saxe@Sun.COM * When distributing Covered Code, include this CDDL HEADER in each
148906SEric.Saxe@Sun.COM * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
158906SEric.Saxe@Sun.COM * If applicable, add the following below this CDDL HEADER, with the
168906SEric.Saxe@Sun.COM * fields enclosed by brackets "[]" replaced with your own identifying
178906SEric.Saxe@Sun.COM * information: Portions Copyright [yyyy] [name of copyright owner]
188906SEric.Saxe@Sun.COM *
198906SEric.Saxe@Sun.COM * CDDL HEADER END
208906SEric.Saxe@Sun.COM */
218906SEric.Saxe@Sun.COM /*
22*12826Skuriakose.kuruvilla@oracle.com * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
238906SEric.Saxe@Sun.COM */
2410488SMark.Haywood@Sun.COM /*
2510488SMark.Haywood@Sun.COM * Copyright (c) 2009, Intel Corporation.
2610488SMark.Haywood@Sun.COM * All Rights Reserved.
2710488SMark.Haywood@Sun.COM */
288906SEric.Saxe@Sun.COM
298906SEric.Saxe@Sun.COM #include <sys/x86_archext.h>
308906SEric.Saxe@Sun.COM #include <sys/machsystm.h>
318935Srafael.vanoni@sun.com #include <sys/archsystm.h>
328906SEric.Saxe@Sun.COM #include <sys/x_call.h>
338906SEric.Saxe@Sun.COM #include <sys/acpi/acpi.h>
348906SEric.Saxe@Sun.COM #include <sys/acpica.h>
358906SEric.Saxe@Sun.COM #include <sys/speedstep.h>
368906SEric.Saxe@Sun.COM #include <sys/cpu_acpi.h>
378906SEric.Saxe@Sun.COM #include <sys/cpupm.h>
388906SEric.Saxe@Sun.COM #include <sys/dtrace.h>
398906SEric.Saxe@Sun.COM #include <sys/sdt.h>
408906SEric.Saxe@Sun.COM
418935Srafael.vanoni@sun.com /*
428935Srafael.vanoni@sun.com * turbo related structure definitions
438935Srafael.vanoni@sun.com */
448935Srafael.vanoni@sun.com typedef struct cpupm_turbo_info {
458935Srafael.vanoni@sun.com kstat_t *turbo_ksp; /* turbo kstat */
468935Srafael.vanoni@sun.com int in_turbo; /* in turbo? */
478935Srafael.vanoni@sun.com int turbo_supported; /* turbo flag */
488935Srafael.vanoni@sun.com uint64_t t_mcnt; /* turbo mcnt */
498935Srafael.vanoni@sun.com uint64_t t_acnt; /* turbo acnt */
508935Srafael.vanoni@sun.com } cpupm_turbo_info_t;
518935Srafael.vanoni@sun.com
528935Srafael.vanoni@sun.com typedef struct turbo_kstat_s {
538935Srafael.vanoni@sun.com struct kstat_named turbo_supported; /* turbo flag */
548935Srafael.vanoni@sun.com struct kstat_named t_mcnt; /* IA32_MPERF_MSR */
558935Srafael.vanoni@sun.com struct kstat_named t_acnt; /* IA32_APERF_MSR */
568935Srafael.vanoni@sun.com } turbo_kstat_t;
578935Srafael.vanoni@sun.com
588906SEric.Saxe@Sun.COM static int speedstep_init(cpu_t *);
598906SEric.Saxe@Sun.COM static void speedstep_fini(cpu_t *);
608906SEric.Saxe@Sun.COM static void speedstep_power(cpuset_t, uint32_t);
6110488SMark.Haywood@Sun.COM static void speedstep_stop(cpu_t *);
628935Srafael.vanoni@sun.com static boolean_t turbo_supported(void);
638935Srafael.vanoni@sun.com static int turbo_kstat_update(kstat_t *, int);
648935Srafael.vanoni@sun.com static void get_turbo_info(cpupm_turbo_info_t *);
658935Srafael.vanoni@sun.com static void reset_turbo_info(void);
668935Srafael.vanoni@sun.com static void record_turbo_info(cpupm_turbo_info_t *, uint32_t, uint32_t);
678935Srafael.vanoni@sun.com static void update_turbo_info(cpupm_turbo_info_t *);
688906SEric.Saxe@Sun.COM
698906SEric.Saxe@Sun.COM /*
708906SEric.Saxe@Sun.COM * Interfaces for modules implementing Intel's Enhanced SpeedStep.
718906SEric.Saxe@Sun.COM */
728906SEric.Saxe@Sun.COM cpupm_state_ops_t speedstep_ops = {
738906SEric.Saxe@Sun.COM "Enhanced SpeedStep Technology",
748906SEric.Saxe@Sun.COM speedstep_init,
758906SEric.Saxe@Sun.COM speedstep_fini,
7610488SMark.Haywood@Sun.COM speedstep_power,
7710488SMark.Haywood@Sun.COM speedstep_stop
788906SEric.Saxe@Sun.COM };
798906SEric.Saxe@Sun.COM
808906SEric.Saxe@Sun.COM /*
818906SEric.Saxe@Sun.COM * Error returns
828906SEric.Saxe@Sun.COM */
838906SEric.Saxe@Sun.COM #define ESS_RET_SUCCESS 0x00
848906SEric.Saxe@Sun.COM #define ESS_RET_NO_PM 0x01
858906SEric.Saxe@Sun.COM #define ESS_RET_UNSUP_STATE 0x02
868906SEric.Saxe@Sun.COM
878906SEric.Saxe@Sun.COM /*
888906SEric.Saxe@Sun.COM * MSR registers for changing and reading processor power state.
898906SEric.Saxe@Sun.COM */
908906SEric.Saxe@Sun.COM #define IA32_PERF_STAT_MSR 0x198
918906SEric.Saxe@Sun.COM #define IA32_PERF_CTL_MSR 0x199
928906SEric.Saxe@Sun.COM
938906SEric.Saxe@Sun.COM #define IA32_CPUID_TSC_CONSTANT 0xF30
948906SEric.Saxe@Sun.COM #define IA32_MISC_ENABLE_MSR 0x1A0
958906SEric.Saxe@Sun.COM #define IA32_MISC_ENABLE_EST (1<<16)
968906SEric.Saxe@Sun.COM #define IA32_MISC_ENABLE_CXE (1<<25)
978935Srafael.vanoni@sun.com
988935Srafael.vanoni@sun.com #define CPUID_TURBO_SUPPORT (1 << 1)
998935Srafael.vanoni@sun.com #define CPU_ACPI_P0 0
1008935Srafael.vanoni@sun.com #define CPU_IN_TURBO 1
1018935Srafael.vanoni@sun.com
1028935Srafael.vanoni@sun.com /*
1038935Srafael.vanoni@sun.com * MSR for hardware coordination feedback mechanism
1048935Srafael.vanoni@sun.com * - IA32_MPERF: increments in proportion to a fixed frequency
1058935Srafael.vanoni@sun.com * - IA32_APERF: increments in proportion to actual performance
1068935Srafael.vanoni@sun.com */
1078935Srafael.vanoni@sun.com #define IA32_MPERF_MSR 0xE7
1088935Srafael.vanoni@sun.com #define IA32_APERF_MSR 0xE8
1098935Srafael.vanoni@sun.com
1108906SEric.Saxe@Sun.COM /*
1118906SEric.Saxe@Sun.COM * Debugging support
1128906SEric.Saxe@Sun.COM */
1138906SEric.Saxe@Sun.COM #ifdef DEBUG
1148906SEric.Saxe@Sun.COM volatile int ess_debug = 0;
1158906SEric.Saxe@Sun.COM #define ESSDEBUG(arglist) if (ess_debug) printf arglist;
1168906SEric.Saxe@Sun.COM #else
1178906SEric.Saxe@Sun.COM #define ESSDEBUG(arglist)
1188906SEric.Saxe@Sun.COM #endif
1198906SEric.Saxe@Sun.COM
1208935Srafael.vanoni@sun.com static kmutex_t turbo_mutex;
1218935Srafael.vanoni@sun.com
1228935Srafael.vanoni@sun.com turbo_kstat_t turbo_kstat = {
1238935Srafael.vanoni@sun.com { "turbo_supported", KSTAT_DATA_UINT32 },
1248935Srafael.vanoni@sun.com { "turbo_mcnt", KSTAT_DATA_UINT64 },
1258935Srafael.vanoni@sun.com { "turbo_acnt", KSTAT_DATA_UINT64 },
1268935Srafael.vanoni@sun.com };
1278935Srafael.vanoni@sun.com
1288935Srafael.vanoni@sun.com /*
1298935Srafael.vanoni@sun.com * kstat update function of the turbo mode info
1308935Srafael.vanoni@sun.com */
1318935Srafael.vanoni@sun.com static int
turbo_kstat_update(kstat_t * ksp,int flag)1328935Srafael.vanoni@sun.com turbo_kstat_update(kstat_t *ksp, int flag)
1338935Srafael.vanoni@sun.com {
1348935Srafael.vanoni@sun.com cpupm_turbo_info_t *turbo_info = ksp->ks_private;
1358935Srafael.vanoni@sun.com
1368935Srafael.vanoni@sun.com if (flag == KSTAT_WRITE) {
1378935Srafael.vanoni@sun.com return (EACCES);
1388935Srafael.vanoni@sun.com }
1398935Srafael.vanoni@sun.com
1408935Srafael.vanoni@sun.com /*
1418935Srafael.vanoni@sun.com * update the count in case CPU is in the turbo
1428935Srafael.vanoni@sun.com * mode for a long time
1438935Srafael.vanoni@sun.com */
1448935Srafael.vanoni@sun.com if (turbo_info->in_turbo == CPU_IN_TURBO)
1458935Srafael.vanoni@sun.com update_turbo_info(turbo_info);
1468935Srafael.vanoni@sun.com
1478935Srafael.vanoni@sun.com turbo_kstat.turbo_supported.value.ui32 =
1488935Srafael.vanoni@sun.com turbo_info->turbo_supported;
1498935Srafael.vanoni@sun.com turbo_kstat.t_mcnt.value.ui64 = turbo_info->t_mcnt;
1508935Srafael.vanoni@sun.com turbo_kstat.t_acnt.value.ui64 = turbo_info->t_acnt;
1518935Srafael.vanoni@sun.com
1528935Srafael.vanoni@sun.com return (0);
1538935Srafael.vanoni@sun.com }
1548935Srafael.vanoni@sun.com
1558935Srafael.vanoni@sun.com /*
1568935Srafael.vanoni@sun.com * Get count of MPERF/APERF MSR
1578935Srafael.vanoni@sun.com */
1588935Srafael.vanoni@sun.com static void
get_turbo_info(cpupm_turbo_info_t * turbo_info)1598935Srafael.vanoni@sun.com get_turbo_info(cpupm_turbo_info_t *turbo_info)
1608935Srafael.vanoni@sun.com {
1618935Srafael.vanoni@sun.com ulong_t iflag;
1628935Srafael.vanoni@sun.com uint64_t mcnt, acnt;
1638935Srafael.vanoni@sun.com
1648935Srafael.vanoni@sun.com iflag = intr_clear();
1658935Srafael.vanoni@sun.com mcnt = rdmsr(IA32_MPERF_MSR);
1668935Srafael.vanoni@sun.com acnt = rdmsr(IA32_APERF_MSR);
1678935Srafael.vanoni@sun.com turbo_info->t_mcnt += mcnt;
1688935Srafael.vanoni@sun.com turbo_info->t_acnt += acnt;
1698935Srafael.vanoni@sun.com intr_restore(iflag);
1708935Srafael.vanoni@sun.com }
1718935Srafael.vanoni@sun.com
1728935Srafael.vanoni@sun.com /*
1738935Srafael.vanoni@sun.com * Clear MPERF/APERF MSR
1748935Srafael.vanoni@sun.com */
1758935Srafael.vanoni@sun.com static void
reset_turbo_info(void)1768935Srafael.vanoni@sun.com reset_turbo_info(void)
1778935Srafael.vanoni@sun.com {
1788935Srafael.vanoni@sun.com ulong_t iflag;
1798935Srafael.vanoni@sun.com
1808935Srafael.vanoni@sun.com iflag = intr_clear();
1818935Srafael.vanoni@sun.com wrmsr(IA32_MPERF_MSR, 0);
1828935Srafael.vanoni@sun.com wrmsr(IA32_APERF_MSR, 0);
1838935Srafael.vanoni@sun.com intr_restore(iflag);
1848935Srafael.vanoni@sun.com }
1858935Srafael.vanoni@sun.com
1868935Srafael.vanoni@sun.com /*
1878935Srafael.vanoni@sun.com * sum up the count of one CPU_ACPI_P0 transition
1888935Srafael.vanoni@sun.com */
1898935Srafael.vanoni@sun.com static void
record_turbo_info(cpupm_turbo_info_t * turbo_info,uint32_t cur_state,uint32_t req_state)1908935Srafael.vanoni@sun.com record_turbo_info(cpupm_turbo_info_t *turbo_info,
1918935Srafael.vanoni@sun.com uint32_t cur_state, uint32_t req_state)
1928935Srafael.vanoni@sun.com {
1938935Srafael.vanoni@sun.com if (!turbo_info->turbo_supported)
1948935Srafael.vanoni@sun.com return;
1958935Srafael.vanoni@sun.com /*
1968935Srafael.vanoni@sun.com * enter P0 state
1978935Srafael.vanoni@sun.com */
1988935Srafael.vanoni@sun.com if (req_state == CPU_ACPI_P0) {
1998935Srafael.vanoni@sun.com reset_turbo_info();
2008935Srafael.vanoni@sun.com turbo_info->in_turbo = CPU_IN_TURBO;
2018935Srafael.vanoni@sun.com }
2028935Srafael.vanoni@sun.com /*
2038935Srafael.vanoni@sun.com * Leave P0 state
2048935Srafael.vanoni@sun.com */
2058935Srafael.vanoni@sun.com else if (cur_state == CPU_ACPI_P0) {
2068935Srafael.vanoni@sun.com turbo_info->in_turbo = 0;
2078935Srafael.vanoni@sun.com get_turbo_info(turbo_info);
2088935Srafael.vanoni@sun.com }
2098935Srafael.vanoni@sun.com }
2108935Srafael.vanoni@sun.com
2118935Srafael.vanoni@sun.com /*
2128935Srafael.vanoni@sun.com * update the sum of counts and clear MSRs
2138935Srafael.vanoni@sun.com */
2148935Srafael.vanoni@sun.com static void
update_turbo_info(cpupm_turbo_info_t * turbo_info)2158935Srafael.vanoni@sun.com update_turbo_info(cpupm_turbo_info_t *turbo_info)
2168935Srafael.vanoni@sun.com {
2178935Srafael.vanoni@sun.com ulong_t iflag;
2188935Srafael.vanoni@sun.com uint64_t mcnt, acnt;
2198935Srafael.vanoni@sun.com
2208935Srafael.vanoni@sun.com iflag = intr_clear();
2218935Srafael.vanoni@sun.com mcnt = rdmsr(IA32_MPERF_MSR);
2228935Srafael.vanoni@sun.com acnt = rdmsr(IA32_APERF_MSR);
2238935Srafael.vanoni@sun.com wrmsr(IA32_MPERF_MSR, 0);
2248935Srafael.vanoni@sun.com wrmsr(IA32_APERF_MSR, 0);
2258935Srafael.vanoni@sun.com turbo_info->t_mcnt += mcnt;
2268935Srafael.vanoni@sun.com turbo_info->t_acnt += acnt;
2278935Srafael.vanoni@sun.com intr_restore(iflag);
2288935Srafael.vanoni@sun.com }
2298935Srafael.vanoni@sun.com
2308906SEric.Saxe@Sun.COM /*
2318906SEric.Saxe@Sun.COM * Write the ctrl register. How it is written, depends upon the _PCT
2328906SEric.Saxe@Sun.COM * APCI object value.
2338906SEric.Saxe@Sun.COM */
2348906SEric.Saxe@Sun.COM static void
write_ctrl(cpu_acpi_handle_t handle,uint32_t ctrl)2358906SEric.Saxe@Sun.COM write_ctrl(cpu_acpi_handle_t handle, uint32_t ctrl)
2368906SEric.Saxe@Sun.COM {
2378906SEric.Saxe@Sun.COM cpu_acpi_pct_t *pct_ctrl;
2388906SEric.Saxe@Sun.COM uint64_t reg;
2398906SEric.Saxe@Sun.COM
2408906SEric.Saxe@Sun.COM pct_ctrl = CPU_ACPI_PCT_CTRL(handle);
2418906SEric.Saxe@Sun.COM
2428906SEric.Saxe@Sun.COM switch (pct_ctrl->cr_addrspace_id) {
2438906SEric.Saxe@Sun.COM case ACPI_ADR_SPACE_FIXED_HARDWARE:
2448906SEric.Saxe@Sun.COM /*
2458906SEric.Saxe@Sun.COM * Read current power state because reserved bits must be
2468906SEric.Saxe@Sun.COM * preserved, compose new value, and write it.
2478906SEric.Saxe@Sun.COM */
2488906SEric.Saxe@Sun.COM reg = rdmsr(IA32_PERF_CTL_MSR);
2498906SEric.Saxe@Sun.COM reg &= ~((uint64_t)0xFFFF);
2508906SEric.Saxe@Sun.COM reg |= ctrl;
2518906SEric.Saxe@Sun.COM wrmsr(IA32_PERF_CTL_MSR, reg);
2528906SEric.Saxe@Sun.COM break;
2538906SEric.Saxe@Sun.COM
2548906SEric.Saxe@Sun.COM case ACPI_ADR_SPACE_SYSTEM_IO:
2558906SEric.Saxe@Sun.COM (void) cpu_acpi_write_port(pct_ctrl->cr_address, ctrl,
2568906SEric.Saxe@Sun.COM pct_ctrl->cr_width);
2578906SEric.Saxe@Sun.COM break;
2588906SEric.Saxe@Sun.COM
2598906SEric.Saxe@Sun.COM default:
2608906SEric.Saxe@Sun.COM DTRACE_PROBE1(ess_ctrl_unsupported_type, uint8_t,
2618906SEric.Saxe@Sun.COM pct_ctrl->cr_addrspace_id);
2628906SEric.Saxe@Sun.COM return;
2638906SEric.Saxe@Sun.COM }
2648906SEric.Saxe@Sun.COM
2658906SEric.Saxe@Sun.COM DTRACE_PROBE1(ess_ctrl_write, uint32_t, ctrl);
2668906SEric.Saxe@Sun.COM }
2678906SEric.Saxe@Sun.COM
2688906SEric.Saxe@Sun.COM /*
2698906SEric.Saxe@Sun.COM * Transition the current processor to the requested state.
2708906SEric.Saxe@Sun.COM */
2718906SEric.Saxe@Sun.COM void
speedstep_pstate_transition(uint32_t req_state)2728906SEric.Saxe@Sun.COM speedstep_pstate_transition(uint32_t req_state)
2738906SEric.Saxe@Sun.COM {
2748906SEric.Saxe@Sun.COM cpupm_mach_state_t *mach_state =
2758906SEric.Saxe@Sun.COM (cpupm_mach_state_t *)CPU->cpu_m.mcpu_pm_mach_state;
2768906SEric.Saxe@Sun.COM cpu_acpi_handle_t handle = mach_state->ms_acpi_handle;
2778906SEric.Saxe@Sun.COM cpu_acpi_pstate_t *req_pstate;
2788906SEric.Saxe@Sun.COM uint32_t ctrl;
2798935Srafael.vanoni@sun.com cpupm_turbo_info_t *turbo_info =
2808935Srafael.vanoni@sun.com (cpupm_turbo_info_t *)(mach_state->ms_vendor);
2818906SEric.Saxe@Sun.COM
2828906SEric.Saxe@Sun.COM req_pstate = (cpu_acpi_pstate_t *)CPU_ACPI_PSTATES(handle);
2838906SEric.Saxe@Sun.COM req_pstate += req_state;
2848906SEric.Saxe@Sun.COM
2858906SEric.Saxe@Sun.COM DTRACE_PROBE1(ess_transition, uint32_t, CPU_ACPI_FREQ(req_pstate));
2868906SEric.Saxe@Sun.COM
2878906SEric.Saxe@Sun.COM /*
2888906SEric.Saxe@Sun.COM * Initiate the processor p-state change.
2898906SEric.Saxe@Sun.COM */
2908906SEric.Saxe@Sun.COM ctrl = CPU_ACPI_PSTATE_CTRL(req_pstate);
2918906SEric.Saxe@Sun.COM write_ctrl(handle, ctrl);
2928906SEric.Saxe@Sun.COM
2938935Srafael.vanoni@sun.com if (turbo_info)
2948935Srafael.vanoni@sun.com record_turbo_info(turbo_info,
2958935Srafael.vanoni@sun.com mach_state->ms_pstate.cma_state.pstate, req_state);
2968935Srafael.vanoni@sun.com
2978935Srafael.vanoni@sun.com
2988906SEric.Saxe@Sun.COM mach_state->ms_pstate.cma_state.pstate = req_state;
2998906SEric.Saxe@Sun.COM cpu_set_curr_clock(((uint64_t)CPU_ACPI_FREQ(req_pstate) * 1000000));
3008906SEric.Saxe@Sun.COM }
3018906SEric.Saxe@Sun.COM
3028906SEric.Saxe@Sun.COM static void
speedstep_power(cpuset_t set,uint32_t req_state)3038906SEric.Saxe@Sun.COM speedstep_power(cpuset_t set, uint32_t req_state)
3048906SEric.Saxe@Sun.COM {
3058906SEric.Saxe@Sun.COM /*
3068906SEric.Saxe@Sun.COM * If thread is already running on target CPU then just
3078906SEric.Saxe@Sun.COM * make the transition request. Otherwise, we'll need to
3088906SEric.Saxe@Sun.COM * make a cross-call.
3098906SEric.Saxe@Sun.COM */
3108906SEric.Saxe@Sun.COM kpreempt_disable();
3118906SEric.Saxe@Sun.COM if (CPU_IN_SET(set, CPU->cpu_id)) {
3128906SEric.Saxe@Sun.COM speedstep_pstate_transition(req_state);
3138906SEric.Saxe@Sun.COM CPUSET_DEL(set, CPU->cpu_id);
3148906SEric.Saxe@Sun.COM }
3158906SEric.Saxe@Sun.COM if (!CPUSET_ISNULL(set)) {
3169489SJoe.Bonasera@sun.com xc_call((xc_arg_t)req_state, NULL, NULL, CPUSET2BV(set),
3178906SEric.Saxe@Sun.COM (xc_func_t)speedstep_pstate_transition);
3188906SEric.Saxe@Sun.COM }
3198906SEric.Saxe@Sun.COM kpreempt_enable();
3208906SEric.Saxe@Sun.COM }
3218906SEric.Saxe@Sun.COM
3228906SEric.Saxe@Sun.COM /*
3238906SEric.Saxe@Sun.COM * Validate that this processor supports Speedstep and if so,
3248906SEric.Saxe@Sun.COM * get the P-state data from ACPI and cache it.
3258906SEric.Saxe@Sun.COM */
3268906SEric.Saxe@Sun.COM static int
speedstep_init(cpu_t * cp)3278906SEric.Saxe@Sun.COM speedstep_init(cpu_t *cp)
3288906SEric.Saxe@Sun.COM {
3298906SEric.Saxe@Sun.COM cpupm_mach_state_t *mach_state =
3308906SEric.Saxe@Sun.COM (cpupm_mach_state_t *)cp->cpu_m.mcpu_pm_mach_state;
3318906SEric.Saxe@Sun.COM cpu_acpi_handle_t handle = mach_state->ms_acpi_handle;
3328906SEric.Saxe@Sun.COM cpu_acpi_pct_t *pct_stat;
3338935Srafael.vanoni@sun.com cpupm_turbo_info_t *turbo_info;
3348906SEric.Saxe@Sun.COM
3358906SEric.Saxe@Sun.COM ESSDEBUG(("speedstep_init: processor %d\n", cp->cpu_id));
3368906SEric.Saxe@Sun.COM
3378906SEric.Saxe@Sun.COM /*
3388906SEric.Saxe@Sun.COM * Cache the P-state specific ACPI data.
3398906SEric.Saxe@Sun.COM */
3408906SEric.Saxe@Sun.COM if (cpu_acpi_cache_pstate_data(handle) != 0) {
34110075SMark.Haywood@Sun.COM cmn_err(CE_NOTE, "!SpeedStep support is being "
34210075SMark.Haywood@Sun.COM "disabled due to errors parsing ACPI P-state objects "
34310075SMark.Haywood@Sun.COM "exported by BIOS.");
3448906SEric.Saxe@Sun.COM speedstep_fini(cp);
3458906SEric.Saxe@Sun.COM return (ESS_RET_NO_PM);
3468906SEric.Saxe@Sun.COM }
3478906SEric.Saxe@Sun.COM
3488906SEric.Saxe@Sun.COM pct_stat = CPU_ACPI_PCT_STATUS(handle);
3498906SEric.Saxe@Sun.COM switch (pct_stat->cr_addrspace_id) {
3508906SEric.Saxe@Sun.COM case ACPI_ADR_SPACE_FIXED_HARDWARE:
3518906SEric.Saxe@Sun.COM ESSDEBUG(("Transitions will use fixed hardware\n"));
3528906SEric.Saxe@Sun.COM break;
3538906SEric.Saxe@Sun.COM case ACPI_ADR_SPACE_SYSTEM_IO:
3548906SEric.Saxe@Sun.COM ESSDEBUG(("Transitions will use system IO\n"));
3558906SEric.Saxe@Sun.COM break;
3568906SEric.Saxe@Sun.COM default:
3578906SEric.Saxe@Sun.COM cmn_err(CE_WARN, "!_PCT conifgured for unsupported "
3588906SEric.Saxe@Sun.COM "addrspace = %d.", pct_stat->cr_addrspace_id);
3598906SEric.Saxe@Sun.COM cmn_err(CE_NOTE, "!CPU power management will not function.");
3608906SEric.Saxe@Sun.COM speedstep_fini(cp);
3618906SEric.Saxe@Sun.COM return (ESS_RET_NO_PM);
3628906SEric.Saxe@Sun.COM }
3638906SEric.Saxe@Sun.COM
3648906SEric.Saxe@Sun.COM cpupm_alloc_domains(cp, CPUPM_P_STATES);
3658906SEric.Saxe@Sun.COM
3668935Srafael.vanoni@sun.com if (!turbo_supported()) {
3678935Srafael.vanoni@sun.com mach_state->ms_vendor = NULL;
3688935Srafael.vanoni@sun.com goto ess_ret_success;
3698935Srafael.vanoni@sun.com }
3708935Srafael.vanoni@sun.com /*
3718935Srafael.vanoni@sun.com * turbo mode supported
3728935Srafael.vanoni@sun.com */
3738935Srafael.vanoni@sun.com turbo_info = mach_state->ms_vendor =
3748935Srafael.vanoni@sun.com kmem_zalloc(sizeof (cpupm_turbo_info_t), KM_SLEEP);
3758935Srafael.vanoni@sun.com turbo_info->turbo_supported = 1;
3768935Srafael.vanoni@sun.com turbo_info->turbo_ksp = kstat_create("turbo", cp->cpu_id,
3778935Srafael.vanoni@sun.com "turbo", "misc", KSTAT_TYPE_NAMED,
3788935Srafael.vanoni@sun.com sizeof (turbo_kstat) / sizeof (kstat_named_t),
3798935Srafael.vanoni@sun.com KSTAT_FLAG_VIRTUAL);
3808935Srafael.vanoni@sun.com
3818935Srafael.vanoni@sun.com if (turbo_info->turbo_ksp == NULL) {
3828935Srafael.vanoni@sun.com cmn_err(CE_NOTE, "kstat_create(turbo) fail");
3838935Srafael.vanoni@sun.com } else {
3848935Srafael.vanoni@sun.com turbo_info->turbo_ksp->ks_data = &turbo_kstat;
3858935Srafael.vanoni@sun.com turbo_info->turbo_ksp->ks_lock = &turbo_mutex;
3868935Srafael.vanoni@sun.com turbo_info->turbo_ksp->ks_update = turbo_kstat_update;
3878935Srafael.vanoni@sun.com turbo_info->turbo_ksp->ks_data_size += MAXNAMELEN;
3888935Srafael.vanoni@sun.com turbo_info->turbo_ksp->ks_private = turbo_info;
3898935Srafael.vanoni@sun.com
3908935Srafael.vanoni@sun.com kstat_install(turbo_info->turbo_ksp);
3918935Srafael.vanoni@sun.com }
3928935Srafael.vanoni@sun.com
3938935Srafael.vanoni@sun.com ess_ret_success:
3948935Srafael.vanoni@sun.com
3958906SEric.Saxe@Sun.COM ESSDEBUG(("Processor %d succeeded.\n", cp->cpu_id))
3968906SEric.Saxe@Sun.COM return (ESS_RET_SUCCESS);
3978906SEric.Saxe@Sun.COM }
3988906SEric.Saxe@Sun.COM
3998906SEric.Saxe@Sun.COM /*
4008906SEric.Saxe@Sun.COM * Free resources allocated by speedstep_init().
4018906SEric.Saxe@Sun.COM */
4028906SEric.Saxe@Sun.COM static void
speedstep_fini(cpu_t * cp)4038906SEric.Saxe@Sun.COM speedstep_fini(cpu_t *cp)
4048906SEric.Saxe@Sun.COM {
4058906SEric.Saxe@Sun.COM cpupm_mach_state_t *mach_state =
4068906SEric.Saxe@Sun.COM (cpupm_mach_state_t *)(cp->cpu_m.mcpu_pm_mach_state);
4078906SEric.Saxe@Sun.COM cpu_acpi_handle_t handle = mach_state->ms_acpi_handle;
4088935Srafael.vanoni@sun.com cpupm_turbo_info_t *turbo_info =
4098935Srafael.vanoni@sun.com (cpupm_turbo_info_t *)(mach_state->ms_vendor);
4108906SEric.Saxe@Sun.COM
4118906SEric.Saxe@Sun.COM cpupm_free_domains(&cpupm_pstate_domains);
4128906SEric.Saxe@Sun.COM cpu_acpi_free_pstate_data(handle);
4138935Srafael.vanoni@sun.com
4148935Srafael.vanoni@sun.com if (turbo_info) {
4158935Srafael.vanoni@sun.com if (turbo_info->turbo_ksp != NULL)
4168935Srafael.vanoni@sun.com kstat_delete(turbo_info->turbo_ksp);
4178935Srafael.vanoni@sun.com kmem_free(turbo_info, sizeof (cpupm_turbo_info_t));
4188935Srafael.vanoni@sun.com }
4198906SEric.Saxe@Sun.COM }
4208906SEric.Saxe@Sun.COM
42110488SMark.Haywood@Sun.COM static void
speedstep_stop(cpu_t * cp)42210488SMark.Haywood@Sun.COM speedstep_stop(cpu_t *cp)
42310488SMark.Haywood@Sun.COM {
42410488SMark.Haywood@Sun.COM cpupm_mach_state_t *mach_state =
42510488SMark.Haywood@Sun.COM (cpupm_mach_state_t *)(cp->cpu_m.mcpu_pm_mach_state);
42610488SMark.Haywood@Sun.COM cpu_acpi_handle_t handle = mach_state->ms_acpi_handle;
42710488SMark.Haywood@Sun.COM cpupm_turbo_info_t *turbo_info =
42810488SMark.Haywood@Sun.COM (cpupm_turbo_info_t *)(mach_state->ms_vendor);
42910488SMark.Haywood@Sun.COM
43010488SMark.Haywood@Sun.COM cpupm_remove_domains(cp, CPUPM_P_STATES, &cpupm_pstate_domains);
43110488SMark.Haywood@Sun.COM cpu_acpi_free_pstate_data(handle);
43210488SMark.Haywood@Sun.COM
43310488SMark.Haywood@Sun.COM if (turbo_info) {
43410488SMark.Haywood@Sun.COM if (turbo_info->turbo_ksp != NULL)
43510488SMark.Haywood@Sun.COM kstat_delete(turbo_info->turbo_ksp);
43610488SMark.Haywood@Sun.COM kmem_free(turbo_info, sizeof (cpupm_turbo_info_t));
43710488SMark.Haywood@Sun.COM }
43810488SMark.Haywood@Sun.COM }
43910488SMark.Haywood@Sun.COM
4408906SEric.Saxe@Sun.COM boolean_t
speedstep_supported(uint_t family,uint_t model)4418906SEric.Saxe@Sun.COM speedstep_supported(uint_t family, uint_t model)
4428906SEric.Saxe@Sun.COM {
4438906SEric.Saxe@Sun.COM struct cpuid_regs cpu_regs;
4448906SEric.Saxe@Sun.COM
4458906SEric.Saxe@Sun.COM /* Required features */
446*12826Skuriakose.kuruvilla@oracle.com if (!is_x86_feature(x86_featureset, X86FSET_CPUID) ||
447*12826Skuriakose.kuruvilla@oracle.com !is_x86_feature(x86_featureset, X86FSET_MSR)) {
4488906SEric.Saxe@Sun.COM return (B_FALSE);
4498906SEric.Saxe@Sun.COM }
4508906SEric.Saxe@Sun.COM
4518906SEric.Saxe@Sun.COM /*
4528906SEric.Saxe@Sun.COM * We only support family/model combinations which
4538906SEric.Saxe@Sun.COM * are P-state TSC invariant.
4548906SEric.Saxe@Sun.COM */
4558906SEric.Saxe@Sun.COM if (!((family == 0xf && model >= 0x3) ||
4568906SEric.Saxe@Sun.COM (family == 0x6 && model >= 0xe))) {
4578906SEric.Saxe@Sun.COM return (B_FALSE);
4588906SEric.Saxe@Sun.COM }
4598906SEric.Saxe@Sun.COM
4608906SEric.Saxe@Sun.COM /*
4618906SEric.Saxe@Sun.COM * Enhanced SpeedStep supported?
4628906SEric.Saxe@Sun.COM */
4638906SEric.Saxe@Sun.COM cpu_regs.cp_eax = 0x1;
4648906SEric.Saxe@Sun.COM (void) __cpuid_insn(&cpu_regs);
4658906SEric.Saxe@Sun.COM if (!(cpu_regs.cp_ecx & CPUID_INTC_ECX_EST)) {
4668906SEric.Saxe@Sun.COM return (B_FALSE);
4678906SEric.Saxe@Sun.COM }
4688906SEric.Saxe@Sun.COM
4698906SEric.Saxe@Sun.COM return (B_TRUE);
4708906SEric.Saxe@Sun.COM }
4718935Srafael.vanoni@sun.com
4728935Srafael.vanoni@sun.com boolean_t
turbo_supported(void)4738935Srafael.vanoni@sun.com turbo_supported(void)
4748935Srafael.vanoni@sun.com {
4758935Srafael.vanoni@sun.com struct cpuid_regs cpu_regs;
4768935Srafael.vanoni@sun.com
4778935Srafael.vanoni@sun.com /* Required features */
478*12826Skuriakose.kuruvilla@oracle.com if (!is_x86_feature(x86_featureset, X86FSET_CPUID) ||
479*12826Skuriakose.kuruvilla@oracle.com !is_x86_feature(x86_featureset, X86FSET_MSR)) {
4808935Srafael.vanoni@sun.com return (B_FALSE);
4818935Srafael.vanoni@sun.com }
4828935Srafael.vanoni@sun.com
4838935Srafael.vanoni@sun.com /*
4848935Srafael.vanoni@sun.com * turbo mode supported?
4858935Srafael.vanoni@sun.com */
4868935Srafael.vanoni@sun.com cpu_regs.cp_eax = 0x6;
4878935Srafael.vanoni@sun.com (void) __cpuid_insn(&cpu_regs);
4888935Srafael.vanoni@sun.com if (!(cpu_regs.cp_eax & CPUID_TURBO_SUPPORT)) {
4898935Srafael.vanoni@sun.com return (B_FALSE);
4908935Srafael.vanoni@sun.com }
4918935Srafael.vanoni@sun.com
4928935Srafael.vanoni@sun.com return (B_TRUE);
4938935Srafael.vanoni@sun.com }
494