cpupm_mach.c revision 37d22dc0ee639b47fcbc0f16ce223299317f60d5
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/x86_archext.h>
#include <sys/machsystm.h>
#include <sys/cpu_idle.h>
#include <sys/cpu_acpi.h>
#include <sys/cpupm_throttle.h>
/*
* This callback is used to build the PPM CPU domains once
* all the CPU devices have been started. The callback is
* initialized by the PPM driver to point to a routine that
* will build the domains.
*/
void (*cpupm_rebuild_cpu_domains)(void);
/*
* This callback is used to reset the topspeed for all the
* CPU devices. The callback is initialized by the PPM driver to
* point to a routine that will reinitialize all the CPU devices
* once all the CPU devices have been started and the CPU domains
* built.
*/
void (*cpupm_init_topspeed)(void);
/*
* This callback is used to redefine the topspeed for a CPU device.
* Since all CPUs in a domain should have identical properties, this
* callback is initialized by the PPM driver to point to a routine
* that will redefine the topspeed for all devices in a CPU domain.
* This callback is exercised whenever an ACPI _PPC change notification
* is received by the CPU driver.
*/
void (*cpupm_redefine_topspeed)(void *);
/*
* This callback is used by the PPM driver to call into the CPU driver
* to find a CPU's current topspeed (i.e., it's current ACPI _PPC value).
*/
void (*cpupm_set_topspeed_callb)(void *, int);
/*
* This callback is used by the PPM driver to call into the CPU driver
* to set a new topspeed for a CPU.
*/
int (*cpupm_get_topspeed_callb)(void *);
static void cpupm_free_notify_handlers(cpu_t *);
/*
* Until proven otherwise, all power states are manageable.
*/
/*
* Until all CPUs have started, we do not allow
* power management.
*/
/*
* c-state tunables
*
* cpupm_cs_idle_cost_tunable is the ratio of time CPU spends executing + idle
* divided by time spent in the idle state transitions.
* A value of 10 means the CPU will not spend more than 1/10 of its time
* in idle latency. The worst case performance will be 90% of non Deep C-state
* kernel.
*
* cpupm_cs_idle_save_tunable is how long we must stay in a deeper C-state
* before it is worth going there. Expressed as a multiple of latency.
*/
#ifndef __xpv
typedef struct cpupm_vendor {
/*
* Table of supported vendors.
*/
static cpupm_vendor_t cpupm_vendors[] = {
};
#endif
/*
* Initialize the machine.
* See if a module exists for managing power for this CPU.
*/
/*ARGSUSED*/
void
{
#ifndef __xpv
int *speeds;
int ret;
cpupm_free(cp);
return;
}
/*
* Loop through the CPU management module table and see if
* any of the modules implement CPU power management
* for this CPU.
*/
break;
}
/*
* Nope, we can't power manage this CPU.
*/
cpupm_free(cp);
return;
}
/*
* If P-state support exists for this system, then initialize it.
*/
if (ret != 0) {
" unable to initialize P-state support",
} else {
if (nspeeds == 0) {
} else {
}
}
}
if (ret != 0) {
char err_msg[128];
int p_res;
"!cpupm_init: processor %d: unable to initialize "
if (p_res >= 0)
} else {
}
}
/*
* If C-states support exists for this system, then initialize it.
*/
if (ret != 0) {
" unable to initialize C-state support",
} else if (cpu_deep_cstates_supported()) {
} else {
}
} else {
}
}
cpupm_free(cp);
return;
}
#endif
}
/*
* Free any resources allocated by cpupm_init().
*/
/*ARGSUSED*/
void
{
#ifndef __xpv
if (mach_state == NULL)
return;
}
}
}
}
#endif
}
/*
* If all CPUs have started and at least one power state is manageable,
* then the CPUs are ready for power management.
*/
{
#ifndef __xpv
if (cpupm_enabled == CPUPM_NO_STATES)
return (B_FALSE);
return (cpupm_ready);
#else
return (B_FALSE);
#endif
}
{
}
/*
* By default, all states are enabled.
*/
void
{
if (state & CPUPM_P_STATES) {
}
if (state & CPUPM_T_STATES) {
}
if (state & CPUPM_C_STATES) {
}
cpupm_enabled &= ~state;
}
/*
* Once all CPUs have been started, the PPM driver should build CPU
* domains and initialize the topspeed for all CPU devices.
*/
void
{
#ifndef __xpv
/*
* The CPU domain built by the PPM during CPUs attaching
* should be rebuilt with the information retrieved from
* ACPI.
*/
if (cpupm_rebuild_cpu_domains != NULL)
/*
* Only initialize the topspeed if P-states are enabled.
*/
(*cpupm_init_topspeed)();
#endif
}
/*
* Allocate power domains for C,P and T States
*/
void
{
switch (state) {
case CPUPM_P_STATES:
} else {
}
break;
case CPUPM_T_STATES:
} else {
}
break;
case CPUPM_C_STATES:
} else {
}
break;
default:
return;
}
break;
}
/* new domain is created and linked at the head */
(void *)ipltospl(DISP_LEVEL));
}
}
/*
* Free C, P or T state power domains
*/
void
{
this_domain = *dom_ptr;
while (this_domain != NULL) {
kmem_free((void *)this_domain,
sizeof (cpupm_state_domains_t));
}
}
void
{
KM_SLEEP);
}
void
{
}
}
void
{
if (mach_state == NULL) {
return;
}
switch (state) {
case CPUPM_P_STATES:
break;
case CPUPM_T_STATES:
break;
default:
break;
}
switch (state_domain->pm_type) {
case CPU_ACPI_SW_ANY:
/*
* A request on any CPU in the domain transitions the domain
*/
break;
case CPU_ACPI_SW_ALL:
/*
* All CPUs in the domain must request the transition
*/
case CPU_ACPI_HW_ALL:
/*
* For now, request the transition on all CPUs in the domain,
* but looking ahead we can probably be smarter about this.
*/
break;
default:
}
}
/*
* CPU PM interfaces exposed to the CPU power manager
*/
/*ARGSUSED*/
{
return (CPUPM_NO_DOMAIN);
}
if (type == CPUPM_DTYPE_ACTIVE) {
/*
* Return P-State domain for the specified CPU
*/
}
} else if (type == CPUPM_DTYPE_IDLE) {
/*
* Return C-State domain for the specified CPU
*/
}
}
return (CPUPM_NO_DOMAIN);
}
/*ARGSUSED*/
{
int *speeds;
/*
* Idle domain support unimplemented
*/
if (type != CPUPM_DTYPE_ACTIVE) {
return (0);
}
/*
* If the caller passes NULL for states, just return the
* number of states.
*/
for (i = 0; i < nspeeds; i++) {
}
}
return (nspeeds);
}
/*ARGSUSED*/
int
{
if (!cpupm_is_ready())
return (-1);
return (0);
}
/*ARGSUSED*/
/*
* Note: It is the responsibility of the users of
* cpupm_get_speeds() to free the memory allocated
* for speeds using cpupm_free_speeds()
*/
{
#ifndef __xpv
#else
return (0);
#endif
}
/*ARGSUSED*/
void
{
#ifndef __xpv
#endif
}
/*
* All CPU instances have been initialized successfully.
*/
cpupm_power_ready(void)
{
}
/*
* All CPU instances have been initialized successfully.
*/
cpupm_throttle_ready(void)
{
}
/*
* All CPU instances have been initialized successfully.
*/
cpupm_cstate_ready(void)
{
}
void
{
}
}
/*ARGSUSED*/
void
{
#ifndef __xpv
} else {
}
#endif
}
/*ARGSUSED*/
static void
{
#ifndef __xpv
return;
}
}
}
#endif
}
/*
* Get the current max speed from the ACPI _PPC object
*/
/*ARGSUSED*/
int
{
#ifndef __xpv
int plat_level;
int max_level;
plat_level = 0;
}
return (plat_level);
#else
return (0);
#endif
}
/*
* This notification handler is called whenever the ACPI _PPC
* object changes. The _PPC is a sort of governor on power levels.
* It sets an upper threshold on which, _PSS defined, power levels
* are usuable. The _PPC value is dynamic and may change as properties
* (i.e., thermal or AC source) of the system change.
*/
static void
{
int top_speed;
}
/* ARGSUSED */
static void
{
#ifndef __xpv
/*
* Currently, we handle _TPC,_CST and _PPC change notifications.
*/
if (val == CPUPM_TPC_CHANGE_NOTIFICATION) {
} else if (val == CPUPM_CST_CHANGE_NOTIFICATION) {
} else if (val == CPUPM_PPC_CHANGE_NOTIFICATION) {
}
#endif
}
/*
* Update cpupm cstate data each time CPU exits idle.
*/
void
{
}
/*
* Determine next cstate based on cpupm data.
* Update cpupm cstate data each time CPU goes idle.
* Do as much as possible in the idle state bookkeeping function because the
* performance impact while idle is minimal compared to in the wakeup function
* when there is real work to do.
*/
{
uint32_t i;
/*
* Strand level C-state policy
* The cpu_acpi_cstate_t *cstates array is not required to
* have an entry for both CPU_ACPI_C2 and CPU_ACPI_C3.
* There are cs_count entries in the cstates array.
* cs_data->cs_next_cstate contains the index of the next
* C-state this CPU should enter.
*/
/*
* Will CPU be idle long enough to save power?
*/
cpupm_cs_sample_tunable) / 1000;
for (i = 1; i < cs_count; ++i) {
cs_count = i;
CPU, int, i);
}
}
/*
* Wakeup often (even when non-idle time is very short)?
*/
/ 1000;
for (i = 1; i < cs_count; ++i) {
cs_count = i;
CPU, int, (CPU_MAX_CSTATES + i));
}
}
/*
* Idle percent
*/
for (i = 1; i < cs_count; ++i) {
case CPU_ACPI_C2:
if (cs_data->cs_smpl_idle_pct <
cs_count = i;
((2 * CPU_MAX_CSTATES) + i));
}
break;
case CPU_ACPI_C3:
if (cs_data->cs_smpl_idle_pct <
cs_count = i;
((2 * CPU_MAX_CSTATES) + i));
}
break;
}
}
}
return (cs_data->cs_next_cstate);
}