Kill the old processor driver; the ACPI CA functions it depended on
are not coming back any time soon. Implement a new 'acpi_cpu' driver with support for CPU throttling and power policies.
This commit is contained in:
parent
7d3bcec9fb
commit
fec754d4b4
@ -200,13 +200,13 @@ dev/acpica/acpi_acad.c optional acpica
|
||||
dev/acpica/acpi_battery.c optional acpica
|
||||
dev/acpica/acpi_button.c optional acpica
|
||||
dev/acpica/acpi_cmbat.c optional acpica
|
||||
dev/acpica/acpi_cpu.c optional acpica
|
||||
dev/acpica/acpi_ec.c optional acpica
|
||||
dev/acpica/acpi_isa.c optional acpica isa
|
||||
dev/acpica/acpi_lid.c optional acpica
|
||||
dev/acpica/acpi_pcib.c optional acpica pci
|
||||
dev/acpica/acpi_powerres.c optional acpica
|
||||
dev/acpica/acpi_powerprofile.c optional acpica
|
||||
#dev/acpica/acpi_processor.c optional acpica
|
||||
dev/acpica/acpi_resource.c optional acpica
|
||||
dev/acpica/acpi_thermal.c optional acpica
|
||||
dev/acpica/acpi_timer.c optional acpica
|
||||
|
388
sys/dev/acpica/acpi_cpu.c
Normal file
388
sys/dev/acpica/acpi_cpu.c
Normal file
@ -0,0 +1,388 @@
|
||||
/*-
|
||||
* Copyright (c) 2001 Michael Smith
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include "opt_acpi.h"
|
||||
#include <sys/param.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/bus.h>
|
||||
|
||||
#include <machine/bus_pio.h>
|
||||
#include <machine/bus.h>
|
||||
#include <machine/resource.h>
|
||||
#include <sys/rman.h>
|
||||
|
||||
#include "acpi.h"
|
||||
|
||||
#include <dev/acpica/acpivar.h>
|
||||
|
||||
/*
|
||||
* Support for ACPI Processor devices.
|
||||
*
|
||||
* Note that this only provides ACPI 1.0 support (with the exception of the
|
||||
* PSTATE_CNT field). 2.0 support will involve implementing _PTC, _PCT,
|
||||
* _PSS and _PPC.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Hooks for the ACPI CA debugging infrastructure
|
||||
*/
|
||||
#define _COMPONENT ACPI_PROCESSOR
|
||||
MODULE_NAME("PROCESSOR")
|
||||
|
||||
struct acpi_cpu_softc {
|
||||
device_t cpu_dev;
|
||||
ACPI_HANDLE cpu_handle;
|
||||
|
||||
u_int32_t cpu_id;
|
||||
|
||||
/* CPU throttling control register */
|
||||
struct resource *cpu_p_blk;
|
||||
#define CPU_GET_P_CNT(sc) (bus_space_read_4(rman_get_bustag((sc)->cpu_p_blk), \
|
||||
rman_get_bushandle((sc)->cpu_p_blk), \
|
||||
0))
|
||||
#define CPU_SET_P_CNT(sc, val) (bus_space_write_4(rman_get_bustag((sc)->cpu_p_blk), \
|
||||
rman_get_bushandle((sc)->cpu_p_blk), \
|
||||
0, (val)))
|
||||
#define CPU_P_CNT_THT_EN (1<<4)
|
||||
};
|
||||
|
||||
/*
|
||||
* Speeds are stored in counts, from 1 - CPU_MAX_SPEED, and
|
||||
* reported to the user in tenths of a percent.
|
||||
*/
|
||||
static u_int32_t cpu_duty_offset;
|
||||
static u_int32_t cpu_duty_width;
|
||||
#define CPU_MAX_SPEED (1 << cpu_duty_width)
|
||||
#define CPU_SPEED_PERCENT(x) ((1000 * (x)) / CPU_MAX_SPEED)
|
||||
#define CPU_SPEED_PRINTABLE(x) (CPU_SPEED_PERCENT(x) / 10),(CPU_SPEED_PERCENT(x) % 10)
|
||||
|
||||
static u_int32_t cpu_smi_cmd; /* should be a generic way to do this */
|
||||
static u_int8_t cpu_pstate_cnt;
|
||||
|
||||
static u_int32_t cpu_current_state;
|
||||
static u_int32_t cpu_performance_state;
|
||||
static u_int32_t cpu_economy_state;
|
||||
static u_int32_t cpu_max_state;
|
||||
|
||||
static device_t *cpu_devices;
|
||||
static int cpu_ndevices;
|
||||
|
||||
static struct sysctl_ctx_list acpi_cpu_sysctl_ctx;
|
||||
static struct sysctl_oid *acpi_cpu_sysctl_tree;
|
||||
|
||||
static int acpi_cpu_probe(device_t dev);
|
||||
static int acpi_cpu_attach(device_t dev);
|
||||
static void acpi_cpu_init_throttling(void *arg);
|
||||
static void acpi_cpu_set_speed(u_int32_t speed);
|
||||
static void acpi_cpu_powerprofile(void *arg);
|
||||
static int acpi_cpu_speed_sysctl(SYSCTL_HANDLER_ARGS);
|
||||
|
||||
static device_method_t acpi_cpu_methods[] = {
|
||||
/* Device interface */
|
||||
DEVMETHOD(device_probe, acpi_cpu_probe),
|
||||
DEVMETHOD(device_attach, acpi_cpu_attach),
|
||||
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
static driver_t acpi_cpu_driver = {
|
||||
"acpi_cpu",
|
||||
acpi_cpu_methods,
|
||||
sizeof(struct acpi_cpu_softc),
|
||||
};
|
||||
|
||||
devclass_t acpi_cpu_devclass;
|
||||
DRIVER_MODULE(acpi_cpu, acpi, acpi_cpu_driver, acpi_cpu_devclass, 0, 0);
|
||||
|
||||
static int
|
||||
acpi_cpu_probe(device_t dev)
|
||||
{
|
||||
if (acpi_get_type(dev) == ACPI_TYPE_PROCESSOR) {
|
||||
device_set_desc(dev, "CPU"); /* XXX get more verbose description? */
|
||||
return(0);
|
||||
}
|
||||
return(ENXIO);
|
||||
}
|
||||
|
||||
static int
|
||||
acpi_cpu_attach(device_t dev)
|
||||
{
|
||||
struct acpi_cpu_softc *sc;
|
||||
struct acpi_softc *acpi_sc;
|
||||
ACPI_OBJECT processor;
|
||||
ACPI_BUFFER buf;
|
||||
ACPI_STATUS status;
|
||||
u_int32_t p_blk;
|
||||
u_int32_t p_blk_length;
|
||||
u_int32_t duty_end;
|
||||
int rid;
|
||||
|
||||
FUNCTION_TRACE(__func__);
|
||||
|
||||
ACPI_ASSERTLOCK;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
sc->cpu_dev = dev;
|
||||
sc->cpu_handle = acpi_get_handle(dev);
|
||||
|
||||
/*
|
||||
* Get global parameters from the FADT.
|
||||
*/
|
||||
if (device_get_unit(sc->cpu_dev) == 0) {
|
||||
/* get the FADT */
|
||||
if (ACPI_FAILURE(status = acpi_GetTableIntoBuffer(ACPI_TABLE_FADT, 1, &buf))) {
|
||||
device_printf(sc->cpu_dev, "couldn't get FADT - %s\n", acpi_strerror(status));
|
||||
if (buf.Pointer != NULL)
|
||||
AcpiOsFree(buf.Pointer);
|
||||
return_VALUE(ENXIO);
|
||||
}
|
||||
cpu_duty_offset = ((FADT_DESCRIPTOR_REV2 *)buf.Pointer)->DutyOffset;
|
||||
cpu_duty_width = ((FADT_DESCRIPTOR_REV2 *)buf.Pointer)->DutyWidth;
|
||||
cpu_smi_cmd = ((FADT_DESCRIPTOR_REV1 *)buf.Pointer)->SmiCmd;
|
||||
cpu_pstate_cnt = ((FADT_DESCRIPTOR_REV2 *)buf.Pointer)->PstateCnt;
|
||||
AcpiOsFree(buf.Pointer);
|
||||
|
||||
/* validate the offset/width */
|
||||
duty_end = cpu_duty_offset + cpu_duty_width - 1;
|
||||
/* check that it fits */
|
||||
if (duty_end > 31) {
|
||||
printf("acpi_cpu: CLK_VAL field overflows P_CNT register\n");
|
||||
cpu_duty_width = 0;
|
||||
}
|
||||
/* check for overlap with the THT_EN bit */
|
||||
if ((cpu_duty_offset <= 4) && (duty_end >= 4)) {
|
||||
printf("acpi_cpu: CLK_VAL field overlaps THT_EN bit\n");
|
||||
cpu_duty_width = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Start the throttling process once the probe phase completes, if we think that
|
||||
* it's going to be useful. If the duty width value is zero, there are no significant
|
||||
* bits in the register and thus no throttled states.
|
||||
*/
|
||||
if (cpu_duty_width > 0) {
|
||||
AcpiOsQueueForExecution(OSD_PRIORITY_LO, acpi_cpu_init_throttling, NULL);
|
||||
|
||||
acpi_sc = acpi_device_get_parent_softc(dev);
|
||||
sysctl_ctx_init(&acpi_cpu_sysctl_ctx);
|
||||
acpi_cpu_sysctl_tree = SYSCTL_ADD_NODE(&acpi_cpu_sysctl_ctx,
|
||||
SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree),
|
||||
OID_AUTO, "cpu", CTLFLAG_RD, 0, "");
|
||||
|
||||
SYSCTL_ADD_INT(&acpi_cpu_sysctl_ctx, SYSCTL_CHILDREN(acpi_cpu_sysctl_tree),
|
||||
OID_AUTO, "max_speed", CTLFLAG_RD,
|
||||
&cpu_max_state, 0, "maximum CPU speed");
|
||||
SYSCTL_ADD_INT(&acpi_cpu_sysctl_ctx, SYSCTL_CHILDREN(acpi_cpu_sysctl_tree),
|
||||
OID_AUTO, "current_speed", CTLFLAG_RD,
|
||||
&cpu_current_state, 0, "current CPU speed");
|
||||
SYSCTL_ADD_PROC(&acpi_cpu_sysctl_ctx, SYSCTL_CHILDREN(acpi_cpu_sysctl_tree),
|
||||
OID_AUTO, "performance_speed", CTLTYPE_INT | CTLFLAG_RW,
|
||||
&cpu_performance_state, 0, acpi_cpu_speed_sysctl, "I", "");
|
||||
SYSCTL_ADD_PROC(&acpi_cpu_sysctl_ctx, SYSCTL_CHILDREN(acpi_cpu_sysctl_tree),
|
||||
OID_AUTO, "economy_speed", CTLTYPE_INT | CTLFLAG_RW,
|
||||
&cpu_economy_state, 0, acpi_cpu_speed_sysctl, "I", "");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the processor object.
|
||||
*/
|
||||
buf.Pointer = &processor;
|
||||
buf.Length = sizeof(processor);
|
||||
if (ACPI_FAILURE(status = AcpiEvaluateObject(sc->cpu_handle, NULL, NULL, &buf))) {
|
||||
device_printf(sc->cpu_dev, "couldn't get Processor object - %s\n", acpi_strerror(status));
|
||||
return_VALUE(ENXIO);
|
||||
}
|
||||
if (processor.Type != ACPI_TYPE_PROCESSOR) {
|
||||
device_printf(sc->cpu_dev, "Processor object has bad type %d\n", processor.Type);
|
||||
return_VALUE(ENXIO);
|
||||
}
|
||||
sc->cpu_id = processor.Processor.ProcId;
|
||||
|
||||
/*
|
||||
* If it looks like we support throttling, find this CPU's P_BLK.
|
||||
*
|
||||
* Note that some systems seem to duplicate the P_BLK pointer across
|
||||
* multiple CPUs, so not getting the resource is not fatal.
|
||||
*
|
||||
* XXX should support _PTC here as well, once we work out how to parse it.
|
||||
*
|
||||
* XXX is it valid to assume that the P_BLK must be 6 bytes long?
|
||||
*/
|
||||
if (cpu_duty_width > 0) {
|
||||
p_blk = processor.Processor.PblkAddress;
|
||||
p_blk_length = processor.Processor.PblkLength;
|
||||
|
||||
/* allocate bus space if possible */
|
||||
if ((p_blk > 0) && (p_blk_length == 6)) {
|
||||
rid = 0;
|
||||
bus_set_resource(sc->cpu_dev, SYS_RES_IOPORT, rid, p_blk, p_blk_length);
|
||||
sc->cpu_p_blk = bus_alloc_resource(sc->cpu_dev, SYS_RES_IOPORT, &rid, 0, ~0, 1,
|
||||
RF_ACTIVE);
|
||||
|
||||
DEBUG_PRINT(TRACE_IO, ("acpi_cpu%d: throttling with P_BLK at 0x%x/%d%s\n",
|
||||
device_get_unit(sc->cpu_dev), p_blk, p_blk_length,
|
||||
sc->cpu_p_blk ? "" : " (shadowed)");
|
||||
}
|
||||
}
|
||||
return_VALUE(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Call this *after* all CPUs have been attached.
|
||||
*
|
||||
* Takes the ACPI lock to avoid fighting anyone over the SMI command
|
||||
* port. Could probably lock less code.
|
||||
*/
|
||||
static void
|
||||
acpi_cpu_init_throttling(void *arg)
|
||||
{
|
||||
|
||||
ACPI_LOCK;
|
||||
|
||||
/* get set of CPU devices */
|
||||
devclass_get_devices(acpi_cpu_devclass, &cpu_devices, &cpu_ndevices);
|
||||
|
||||
/* initialise throttling states */
|
||||
cpu_max_state = CPU_MAX_SPEED;
|
||||
cpu_performance_state = cpu_max_state;
|
||||
cpu_economy_state = cpu_performance_state / 2;
|
||||
if (cpu_economy_state == 0) /* 0 is 'reserved' */
|
||||
cpu_economy_state++;
|
||||
|
||||
/* register performance profile change handler */
|
||||
EVENTHANDLER_REGISTER(powerprofile_change, acpi_cpu_powerprofile, NULL, 0);
|
||||
|
||||
/* if ACPI 2.0+, signal platform that we are taking over throttling */
|
||||
if (cpu_pstate_cnt != 0) {
|
||||
/* XXX should be a generic interface for this */
|
||||
AcpiOsOut8(cpu_smi_cmd, cpu_pstate_cnt);
|
||||
}
|
||||
|
||||
ACPI_UNLOCK;
|
||||
|
||||
/* set initial speed */
|
||||
acpi_cpu_powerprofile(NULL);
|
||||
|
||||
printf("acpi_cpu: CPU throttling enabled, %d steps from 100%% to %d.%d%%\n",
|
||||
CPU_MAX_SPEED, CPU_SPEED_PRINTABLE(1));
|
||||
}
|
||||
|
||||
/*
|
||||
* Set CPUs to the new state.
|
||||
*
|
||||
* Must be called with the ACPI lock held.
|
||||
*/
|
||||
static void
|
||||
acpi_cpu_set_speed(u_int32_t speed)
|
||||
{
|
||||
struct acpi_cpu_softc *sc;
|
||||
int i;
|
||||
u_int32_t p_cnt, clk_val;
|
||||
|
||||
ACPI_ASSERTLOCK;
|
||||
|
||||
/* iterate over processors */
|
||||
for (i = 0; i < cpu_ndevices; i++) {
|
||||
sc = device_get_softc(cpu_devices[i]);
|
||||
if (sc->cpu_p_blk == NULL)
|
||||
continue;
|
||||
|
||||
/* get the current P_CNT value and disable throttling */
|
||||
p_cnt = CPU_GET_P_CNT(sc);
|
||||
p_cnt &= ~CPU_P_CNT_THT_EN;
|
||||
CPU_SET_P_CNT(sc, p_cnt);
|
||||
|
||||
/* if we're at maximum speed, that's all */
|
||||
if (speed < CPU_MAX_SPEED) {
|
||||
|
||||
/* mask the old CLK_VAL off and or-in the new value */
|
||||
clk_val = CPU_MAX_SPEED << cpu_duty_offset;
|
||||
p_cnt &= ~clk_val;
|
||||
p_cnt |= (speed << cpu_duty_offset);
|
||||
|
||||
/* write the new P_CNT value and then enable throttling */
|
||||
CPU_SET_P_CNT(sc, p_cnt);
|
||||
p_cnt |= CPU_P_CNT_THT_EN;
|
||||
CPU_SET_P_CNT(sc, p_cnt);
|
||||
}
|
||||
device_printf(sc->cpu_dev, "set speed to %d.%d%%\n", CPU_SPEED_PRINTABLE(speed));
|
||||
}
|
||||
cpu_current_state = speed;
|
||||
}
|
||||
|
||||
/*
|
||||
* Power profile change hook.
|
||||
*
|
||||
* Uses the ACPI lock to avoid reentrancy.
|
||||
*/
|
||||
static void
|
||||
acpi_cpu_powerprofile(void *arg)
|
||||
{
|
||||
u_int32_t new;
|
||||
|
||||
ACPI_LOCK;
|
||||
|
||||
new = (powerprofile_get_state() == POWERPROFILE_PERFORMANCE) ? cpu_performance_state : cpu_economy_state;
|
||||
if (cpu_current_state != new)
|
||||
acpi_cpu_set_speed(new);
|
||||
|
||||
ACPI_UNLOCK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle changes in the performance/ecomony CPU settings.
|
||||
*
|
||||
* Does not need the ACPI lock (although setting *argp should
|
||||
* probably be atomic).
|
||||
*/
|
||||
static int
|
||||
acpi_cpu_speed_sysctl(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
u_int32_t *argp;
|
||||
u_int32_t arg;
|
||||
int error;
|
||||
|
||||
argp = (u_int32_t *)oidp->oid_arg1;
|
||||
arg = *argp;
|
||||
error = sysctl_handle_int(oidp, &arg, 0, req);
|
||||
|
||||
/* error or no new value */
|
||||
if ((error != 0) || (req->newptr == NULL))
|
||||
return(error);
|
||||
|
||||
/* range check */
|
||||
if ((arg < 1) || (arg >= cpu_max_state))
|
||||
return(EINVAL);
|
||||
|
||||
/* set new value and possibly switch */
|
||||
*argp = arg;
|
||||
acpi_cpu_powerprofile(NULL);
|
||||
|
||||
return(0);
|
||||
}
|
@ -1,669 +0,0 @@
|
||||
/*-
|
||||
* Copyright (c) 2000 Michael Smith
|
||||
* Copyright (c) 2000 BSDi
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
/******************************************************************************
|
||||
*
|
||||
* 1. Copyright Notice
|
||||
*
|
||||
* Some or all of this work - Copyright (c) 1999, Intel Corp. All rights
|
||||
* reserved.
|
||||
*
|
||||
* 2. License
|
||||
*
|
||||
* 2.1. This is your license from Intel Corp. under its intellectual property
|
||||
* rights. You may have additional license terms from the party that provided
|
||||
* you this software, covering your right to use that party's intellectual
|
||||
* property rights.
|
||||
*
|
||||
* 2.2. Intel grants, free of charge, to any person ("Licensee") obtaining a
|
||||
* copy of the source code appearing in this file ("Covered Code") an
|
||||
* irrevocable, perpetual, worldwide license under Intel's copyrights in the
|
||||
* base code distributed originally by Intel ("Original Intel Code") to copy,
|
||||
* make derivatives, distribute, use and display any portion of the Covered
|
||||
* Code in any form, with the right to sublicense such rights; and
|
||||
*
|
||||
* 2.3. Intel grants Licensee a non-exclusive and non-transferable patent
|
||||
* license (with the right to sublicense), under only those claims of Intel
|
||||
* patents that are infringed by the Original Intel Code, to make, use, sell,
|
||||
* offer to sell, and import the Covered Code and derivative works thereof
|
||||
* solely to the minimum extent necessary to exercise the above copyright
|
||||
* license, and in no event shall the patent license extend to any additions
|
||||
* to or modifications of the Original Intel Code. No other license or right
|
||||
* is granted directly or by implication, estoppel or otherwise;
|
||||
*
|
||||
* The above copyright and patent license is granted only if the following
|
||||
* conditions are met:
|
||||
*
|
||||
* 3. Conditions
|
||||
*
|
||||
* 3.1. Redistribution of Source with Rights to Further Distribute Source.
|
||||
* Redistribution of source code of any substantial portion of the Covered
|
||||
* Code or modification with rights to further distribute source must include
|
||||
* the above Copyright Notice, the above License, this list of Conditions,
|
||||
* and the following Disclaimer and Export Compliance provision. In addition,
|
||||
* Licensee must cause all Covered Code to which Licensee contributes to
|
||||
* contain a file documenting the changes Licensee made to create that Covered
|
||||
* Code and the date of any change. Licensee must include in that file the
|
||||
* documentation of any changes made by any predecessor Licensee. Licensee
|
||||
* must include a prominent statement that the modification is derived,
|
||||
* directly or indirectly, from Original Intel Code.
|
||||
*
|
||||
* 3.2. Redistribution of Source with no Rights to Further Distribute Source.
|
||||
* Redistribution of source code of any substantial portion of the Covered
|
||||
* Code or modification without rights to further distribute source must
|
||||
* include the following Disclaimer and Export Compliance provision in the
|
||||
* documentation and/or other materials provided with distribution. In
|
||||
* addition, Licensee may not authorize further sublicense of source of any
|
||||
* portion of the Covered Code, and must include terms to the effect that the
|
||||
* license from Licensee to its licensee is limited to the intellectual
|
||||
* property embodied in the software Licensee provides to its licensee, and
|
||||
* not to intellectual property embodied in modifications its licensee may
|
||||
* make.
|
||||
*
|
||||
* 3.3. Redistribution of Executable. Redistribution in executable form of any
|
||||
* substantial portion of the Covered Code or modification must reproduce the
|
||||
* above Copyright Notice, and the following Disclaimer and Export Compliance
|
||||
* provision in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* 3.4. Intel retains all right, title, and interest in and to the Original
|
||||
* Intel Code.
|
||||
*
|
||||
* 3.5. Neither the name Intel nor any other trademark owned or controlled by
|
||||
* Intel shall be used in advertising or otherwise to promote the sale, use or
|
||||
* other dealings in products derived from or relating to the Covered Code
|
||||
* without prior written authorization from Intel.
|
||||
*
|
||||
* 4. Disclaimer and Export Compliance
|
||||
*
|
||||
* 4.1. INTEL MAKES NO WARRANTY OF ANY KIND REGARDING ANY SOFTWARE PROVIDED
|
||||
* HERE. ANY SOFTWARE ORIGINATING FROM INTEL OR DERIVED FROM INTEL SOFTWARE
|
||||
* IS PROVIDED "AS IS," AND INTEL WILL NOT PROVIDE ANY SUPPORT, ASSISTANCE,
|
||||
* INSTALLATION, TRAINING OR OTHER SERVICES. INTEL WILL NOT PROVIDE ANY
|
||||
* UPDATES, ENHANCEMENTS OR EXTENSIONS. INTEL SPECIFICALLY DISCLAIMS ANY
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY, NONINFRINGEMENT AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE.
|
||||
*
|
||||
* 4.2. IN NO EVENT SHALL INTEL HAVE ANY LIABILITY TO LICENSEE, ITS LICENSEES
|
||||
* OR ANY OTHER THIRD PARTY, FOR ANY LOST PROFITS, LOST DATA, LOSS OF USE OR
|
||||
* COSTS OF PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, OR FOR ANY INDIRECT,
|
||||
* SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THIS AGREEMENT, UNDER ANY
|
||||
* CAUSE OF ACTION OR THEORY OF LIABILITY, AND IRRESPECTIVE OF WHETHER INTEL
|
||||
* HAS ADVANCE NOTICE OF THE POSSIBILITY OF SUCH DAMAGES. THESE LIMITATIONS
|
||||
* SHALL APPLY NOTWITHSTANDING THE FAILURE OF THE ESSENTIAL PURPOSE OF ANY
|
||||
* LIMITED REMEDY.
|
||||
*
|
||||
* 4.3. Licensee shall not export, either directly or indirectly, any of this
|
||||
* software or system incorporating such software without first obtaining any
|
||||
* required license or other approval from the U. S. Department of Commerce or
|
||||
* any other agency or department of the United States Government. In the
|
||||
* event Licensee exports any such software from the United States or
|
||||
* re-exports any such software from a foreign destination, Licensee shall
|
||||
* ensure that the distribution and export/re-export of the software is in
|
||||
* compliance with all laws, regulations, orders, or other restrictions of the
|
||||
* U.S. Export Administration Regulations. Licensee agrees that neither it nor
|
||||
* any of its subsidiaries will export/re-export any technical data, process,
|
||||
* software, or service, directly or indirectly, to any country for which the
|
||||
* United States government or any agency thereof requires an export license,
|
||||
* other governmental approval, or letter of assurance, without first obtaining
|
||||
* such license, approval or letter.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
* Processor driver.
|
||||
*
|
||||
* XXX Note that the power state code here is almost certainly suboptimal.
|
||||
* We should go raid the Linux code for their ideas and experience.
|
||||
*
|
||||
* Code style here is a hairy mix of BSD-like and Intel-like. Should be
|
||||
* sanitised at some point.
|
||||
*/
|
||||
|
||||
#include "opt_acpi.h"
|
||||
#include <sys/param.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/bus.h>
|
||||
|
||||
#include "acpi.h"
|
||||
|
||||
#include <dev/acpica/acpivar.h>
|
||||
|
||||
/*
|
||||
* Hooks for the ACPI CA debugging infrastructure
|
||||
*/
|
||||
#define _COMPONENT ACPI_PROCESSOR_CONTROL
|
||||
MODULE_NAME("PROCESSOR")
|
||||
|
||||
#define PR_MAX_POWER_STATES 4
|
||||
#define PR_MAX_PERFORMANCE_STATES 8
|
||||
#define PR_MAX_THROTTLING_STATES 8
|
||||
|
||||
/*
|
||||
* Processor Commands:
|
||||
* -------------------
|
||||
*/
|
||||
#define PR_COMMAND_GET_INFO ((BM_COMMAND) 0x80)
|
||||
#define PR_COMMAND_SET_CX_STATE_INFO ((BM_COMMAND) 0x81)
|
||||
#define PR_COMMAND_GET_THROTTLING_STATE ((BM_COMMAND) 0x82)
|
||||
#define PR_COMMAND_SET_THROTTLING_STATE ((BM_COMMAND) 0x83)
|
||||
#define PR_COMMAND_GET_PERF_STATE ((BM_COMMAND) 0x84)
|
||||
#define PR_COMMAND_SET_PERF_STATE ((BM_COMMAND) 0x85)
|
||||
#define PR_COMMAND_GET_CURRENT_FREQ ((BM_COMMAND) 0x86)
|
||||
|
||||
/*
|
||||
* PR_POWER_STATE:
|
||||
* ---------------
|
||||
*/
|
||||
typedef u_int32_t PR_POWER_STATE;
|
||||
|
||||
#define PR_POWER_STATE_UNKNOWN ((PR_POWER_STATE) 0xFFFFFFFF)
|
||||
|
||||
#define PR_POWER_STATE_C0 ((PR_POWER_STATE) 0x00000000)
|
||||
#define PR_POWER_STATE_C1 ((PR_POWER_STATE) 0x00000001)
|
||||
#define PR_POWER_STATE_C2 ((PR_POWER_STATE) 0x00000002)
|
||||
#define PR_POWER_STATE_C3 ((PR_POWER_STATE) 0x00000003)
|
||||
|
||||
/*
|
||||
* Processor Notifications:
|
||||
* ------------------------
|
||||
*/
|
||||
#define PR_NOTIFY_PERF_STATES_CHANGE ((BM_NOTIFY) 0x80)
|
||||
#define PR_NOTIFY_POWER_STATES_CHANGE ((BM_NOTIFY) 0x81)
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u_int32_t TimeThreshold;
|
||||
u_int32_t CountThreshold;
|
||||
u_int32_t Count;
|
||||
PR_POWER_STATE TargetState;
|
||||
} PR_POLICY_VALUES;
|
||||
|
||||
/*
|
||||
* PR_CX_STATE_INFO:
|
||||
* -----------------
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
u_int32_t Latency;
|
||||
u_int64_t Utilization;
|
||||
PR_POLICY_VALUES PromotionPolicy;
|
||||
PR_POLICY_VALUES DemotionPolicy;
|
||||
} PR_CX_STATE_INFO;
|
||||
|
||||
/*
|
||||
* PR_POWER_INFO:
|
||||
* --------------
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
u_int32_t Count;
|
||||
PR_POWER_STATE ActiveState;
|
||||
PR_CX_STATE_INFO Info[PR_MAX_POWER_STATES];
|
||||
} PR_POWER_INFO;
|
||||
|
||||
/*
|
||||
* PR_PERFORMANCE_INFO:
|
||||
* --------------------
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
u_int32_t Count;
|
||||
/* TODO... */
|
||||
} PR_PERFORMANCE_INFO;
|
||||
|
||||
/*
|
||||
* PR_THROTTLING_INFO:
|
||||
* -------------------
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
u_int32_t Count;
|
||||
u_int32_t Percentage[PR_MAX_THROTTLING_STATES];
|
||||
} PR_THROTTLING_INFO;
|
||||
|
||||
struct acpi_pr_softc {
|
||||
device_t pr_dev;
|
||||
ACPI_HANDLE pr_handle;
|
||||
PR_POWER_INFO pr_PowerStates;
|
||||
PR_PERFORMANCE_INFO pr_PerformanceStates;
|
||||
PR_THROTTLING_INFO pr_ThrottlingStates;
|
||||
eventhandler_tag pr_idleevent;
|
||||
|
||||
/* local APIC data */
|
||||
PROCESSOR_APIC pr_lapic;
|
||||
};
|
||||
|
||||
#define PR_MAGIC 0x20555043 /* "CPU " */
|
||||
|
||||
static void acpi_pr_identify(driver_t *driver, device_t bus);
|
||||
static ACPI_STATUS acpi_pr_identify_cpu(ACPI_HANDLE handle, UINT32 level, void *context, void **status);
|
||||
static int acpi_pr_probe(device_t dev);
|
||||
static int acpi_pr_attach(device_t dev);
|
||||
|
||||
static void acpi_pr_FindLapic(device_t dev, ACPI_HANDLE handle, PROCESSOR_APIC *lapic);
|
||||
static ACPI_STATUS acpi_pr_CalculatePowerStates(struct acpi_pr_softc *sc);
|
||||
static ACPI_STATUS acpi_pr_CalculatePerformanceStates(struct acpi_pr_softc *sc);
|
||||
static ACPI_STATUS acpi_pr_CalculateThrottlingStates(struct acpi_pr_softc *sc);
|
||||
static void acpi_pr_IdleHandler(void *arg, int count) __unused;
|
||||
static ACPI_STATUS acpi_pr_PolicyInitialize(struct acpi_pr_softc *sc);
|
||||
|
||||
static device_method_t acpi_pr_methods[] = {
|
||||
/* Device interface */
|
||||
DEVMETHOD(device_identify, acpi_pr_identify),
|
||||
DEVMETHOD(device_probe, acpi_pr_probe),
|
||||
DEVMETHOD(device_attach, acpi_pr_attach),
|
||||
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
static driver_t acpi_pr_driver = {
|
||||
"acpi_pr",
|
||||
acpi_pr_methods,
|
||||
sizeof(struct acpi_pr_softc),
|
||||
};
|
||||
|
||||
devclass_t acpi_pr_devclass;
|
||||
DRIVER_MODULE(acpi_pr, acpi, acpi_pr_driver, acpi_pr_devclass, 0, 0);
|
||||
|
||||
/*
|
||||
* Scan the \_PR_ scope for processor objects, and attach them accordingly.
|
||||
*
|
||||
* XXX note that we should find the local APIC address and obtain a resource
|
||||
* that we can hand to child devices for access to it...
|
||||
*/
|
||||
static void
|
||||
acpi_pr_identify(driver_t *driver, device_t bus)
|
||||
{
|
||||
ACPI_HANDLE handle;
|
||||
|
||||
FUNCTION_TRACE(__func__);
|
||||
|
||||
if (!acpi_disabled("processor") &&
|
||||
(AcpiGetHandle(ACPI_ROOT_OBJECT, "\\_PR_", &handle) == AE_OK))
|
||||
AcpiWalkNamespace(ACPI_TYPE_PROCESSOR, handle, 2, acpi_pr_identify_cpu, bus, NULL);
|
||||
|
||||
return_VOID;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a child device for CPUs
|
||||
*/
|
||||
static ACPI_STATUS
|
||||
acpi_pr_identify_cpu(ACPI_HANDLE handle, UINT32 level, void *context, void **status)
|
||||
{
|
||||
device_t bus = (device_t)context;
|
||||
device_t child;
|
||||
PROCESSOR_APIC lapic;
|
||||
|
||||
FUNCTION_TRACE(__func__);
|
||||
|
||||
acpi_pr_FindLapic(bus, handle, &lapic);
|
||||
|
||||
if (lapic.ProcessorEnabled) {
|
||||
if ((child = BUS_ADD_CHILD(bus, 0, "acpi_pr", -1)) == NULL) {
|
||||
device_printf(bus, "could not create CPU device\n");
|
||||
return_ACPI_STATUS(AE_OK);
|
||||
}
|
||||
acpi_set_handle(child, handle);
|
||||
acpi_set_magic(child, PR_MAGIC);
|
||||
device_set_desc(child, "processor device");
|
||||
}
|
||||
|
||||
return_ACPI_STATUS(AE_OK);
|
||||
}
|
||||
|
||||
static int
|
||||
acpi_pr_probe(device_t dev)
|
||||
{
|
||||
if (acpi_get_magic(dev) == PR_MAGIC)
|
||||
return(0);
|
||||
return(ENXIO);
|
||||
}
|
||||
|
||||
static int
|
||||
acpi_pr_attach(device_t dev)
|
||||
{
|
||||
struct acpi_pr_softc *sc;
|
||||
|
||||
FUNCTION_TRACE(__func__);
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
sc->pr_dev = dev;
|
||||
sc->pr_handle = acpi_get_handle(dev);
|
||||
acpi_pr_FindLapic(dev, sc->pr_handle, &sc->pr_lapic);
|
||||
|
||||
/*
|
||||
* If the APIC information is valid, print it
|
||||
*/
|
||||
if (sc->pr_lapic.LocalApicId != (UINT8)0xff)
|
||||
device_printf(dev, "local APIC ID %d\n", sc->pr_lapic.LocalApicId);
|
||||
|
||||
/*
|
||||
* Fetch operational parameters.
|
||||
*/
|
||||
if (acpi_pr_CalculatePowerStates(sc) == AE_OK) {
|
||||
acpi_pr_PolicyInitialize(sc);
|
||||
}
|
||||
acpi_pr_CalculatePerformanceStates(sc);
|
||||
acpi_pr_CalculateThrottlingStates(sc);
|
||||
|
||||
/* XXX call MD cpu-identification here? */
|
||||
|
||||
return_VALUE(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the Local Apic information for this CPU
|
||||
*/
|
||||
static void
|
||||
acpi_pr_FindLapic(device_t dev, ACPI_HANDLE handle, PROCESSOR_APIC *lapic)
|
||||
{
|
||||
#if 0 /* broken by new ACPICA code that doesn't support the APIC table */
|
||||
ACPI_BUFFER buf;
|
||||
ACPI_STATUS status;
|
||||
APIC_HEADER *hdr;
|
||||
APIC_TABLE *tbl;
|
||||
PROCESSOR_APIC *pap;
|
||||
int len, cpuno;
|
||||
#else
|
||||
ACPI_STATUS status;
|
||||
int cpuno;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Assume that we're not going to suceed in finding/parsing the APIC table.
|
||||
* In this case, CPU 0 is valid, and any other CPU is invalid.
|
||||
*/
|
||||
lapic->LocalApicId = 0xff;
|
||||
lapic->ProcessorEnabled = 0;
|
||||
if ((status = AcpiGetProcessorId(handle, &cpuno)) != AE_OK) {
|
||||
device_printf(dev, "error fetching CPU device ID - %s\n", acpi_strerror(status));
|
||||
return;
|
||||
}
|
||||
lapic->ProcessorEnabled = (cpuno == 0);
|
||||
|
||||
#if 0 /* broken by new ACPICA code that doesn't support the APIC table */
|
||||
/*
|
||||
* Perform the tedious double-get to fetch the actual APIC table, and suck it in.
|
||||
*/
|
||||
buf.Length = 0;
|
||||
buf.Pointer = NULL;
|
||||
if ((status = AcpiGetTable(ACPI_TABLE_APIC, 1, &buf)) != AE_BUFFER_OVERFLOW) {
|
||||
if (status != AE_NOT_EXIST)
|
||||
device_printf(dev, "error sizing APIC table - %s\n", acpi_strerror(status));
|
||||
return;
|
||||
}
|
||||
if ((buf.Pointer = AcpiOsAllocate(buf.Length)) == NULL)
|
||||
return;
|
||||
if ((status = AcpiGetTable(ACPI_TABLE_APIC, 1, &buf)) != AE_OK) {
|
||||
device_printf(dev, "error fetching APIC table - %s\n", acpi_strerror(status));
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Scan the tables looking for this CPU index.
|
||||
*/
|
||||
tbl = (APIC_TABLE *)buf.Pointer;
|
||||
len = tbl->header.Length - sizeof(APIC_TABLE);
|
||||
hdr = (APIC_HEADER *)((char *)buf.Pointer + sizeof(APIC_TABLE));
|
||||
while(len > 0) {
|
||||
if (hdr->Length > len) {
|
||||
device_printf(dev, "APIC header corrupt (claims %d bytes where only %d left in structure)\n",
|
||||
hdr->Length, len);
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* If we have found a processor APIC definition with
|
||||
* matching CPU index, copy it out and return.
|
||||
*/
|
||||
if (hdr->Type == APIC_PROC) {
|
||||
pap = (PROCESSOR_APIC *)hdr;
|
||||
if (pap->ProcessorApicId == cpuno) {
|
||||
bcopy(pap, lapic, sizeof(*pap));
|
||||
break;
|
||||
}
|
||||
}
|
||||
len -= hdr->Length;
|
||||
hdr = (APIC_HEADER *)((char *)hdr + hdr->Length);
|
||||
}
|
||||
AcpiOsFree(buf.Pointer);
|
||||
#endif
|
||||
}
|
||||
|
||||
static ACPI_STATUS
|
||||
acpi_pr_CalculatePowerStates(struct acpi_pr_softc *sc)
|
||||
{
|
||||
ACPI_STATUS Status = AE_OK;
|
||||
ACPI_BUFFER Buffer;
|
||||
ACPI_CX_STATE *State = NULL;
|
||||
u_int32_t StateCount = 0;
|
||||
u_int32_t i = 0;
|
||||
|
||||
FUNCTION_TRACE(__func__);
|
||||
|
||||
/*
|
||||
* Set Latency Defaults:
|
||||
* ---------------------
|
||||
* Default state latency to ACPI_UINT32_MAX -- meaning that this state
|
||||
* should not be used by policy. This value is overriden by states
|
||||
* that are present and have usable latencies (e.g. <= 1000us for C3).
|
||||
*/
|
||||
for (i = 0; i < PR_MAX_POWER_STATES; i++)
|
||||
sc->pr_PowerStates.Info[i].Latency = ACPI_UINT32_MAX;
|
||||
|
||||
/*
|
||||
* Get Power State Latencies:
|
||||
* --------------------------
|
||||
*
|
||||
* XXX Note that ACPICA will never give us back C2 if it costs more than 100us,
|
||||
* or C3 if it costs more than 1000us, so some of this code is redundant.
|
||||
*/
|
||||
Status = acpi_GetIntoBuffer(sc->pr_handle, AcpiGetProcessorCxInfo, &Buffer);
|
||||
if (Status != AE_OK) {
|
||||
device_printf(sc->pr_dev, "could not fetch ProcessorCxInfo - %s\n", acpi_strerror(Status));
|
||||
return_ACPI_STATUS(Status);
|
||||
}
|
||||
|
||||
State = (ACPI_CX_STATE*)(Buffer.Pointer);
|
||||
if (State != NULL) {
|
||||
device_printf(sc->pr_dev, "supported power states:");
|
||||
StateCount = Buffer.Length / sizeof(ACPI_CX_STATE);
|
||||
for (i = 0; i < StateCount; i++) {
|
||||
/* XXX C3 isn't supportable in MP configurations, how to best handle this? */
|
||||
if ((State[i].StateNumber < PR_MAX_POWER_STATES) && (State[i].Latency <= 1000)) {
|
||||
printf(" C%d (%dus)", i, State[i].Latency);
|
||||
sc->pr_PowerStates.Info[State[i].StateNumber].Latency = State[i].Latency;
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
sc->pr_PowerStates.Count = PR_MAX_POWER_STATES;
|
||||
sc->pr_PowerStates.ActiveState = PR_POWER_STATE_C1;
|
||||
|
||||
AcpiOsFree(Buffer.Pointer);
|
||||
return_ACPI_STATUS(Status);
|
||||
}
|
||||
|
||||
static ACPI_STATUS
|
||||
acpi_pr_CalculatePerformanceStates(struct acpi_pr_softc *sc)
|
||||
{
|
||||
ACPI_STATUS Status = AE_OK;
|
||||
|
||||
FUNCTION_TRACE(__func__);
|
||||
|
||||
/* TODO... */
|
||||
|
||||
return_ACPI_STATUS(Status);
|
||||
}
|
||||
|
||||
static ACPI_STATUS
|
||||
acpi_pr_CalculateThrottlingStates(struct acpi_pr_softc *sc)
|
||||
{
|
||||
ACPI_STATUS Status = AE_OK;
|
||||
ACPI_BUFFER Buffer;
|
||||
ACPI_CPU_THROTTLING_STATE *State = NULL;
|
||||
u_int32_t StateCount = 0;
|
||||
u_int32_t i = 0;
|
||||
|
||||
FUNCTION_TRACE(__func__);
|
||||
|
||||
/*
|
||||
* Get Throttling States:
|
||||
* ----------------------
|
||||
*/
|
||||
Status = acpi_GetIntoBuffer(sc->pr_handle, AcpiGetProcessorThrottlingInfo, &Buffer);
|
||||
if (Status != AE_OK) {
|
||||
device_printf(sc->pr_dev, "could not fetch ThrottlingInfo - %s\n", acpi_strerror(Status));
|
||||
return_ACPI_STATUS(Status);
|
||||
}
|
||||
|
||||
State = (ACPI_CPU_THROTTLING_STATE*)(Buffer.Pointer);
|
||||
if (State != NULL) {
|
||||
StateCount = Buffer.Length / sizeof(ACPI_CPU_THROTTLING_STATE);
|
||||
device_printf(sc->pr_dev, "supported throttling states:");
|
||||
for (i = 0; i < StateCount; i++) {
|
||||
if (State[i].StateNumber < PR_MAX_THROTTLING_STATES) {
|
||||
/* TODO: Verify that state is *really* supported by this chipset/processor (e.g. errata). */
|
||||
sc->pr_ThrottlingStates.Percentage[State[i].StateNumber] = State[i].PercentOfClock;
|
||||
sc->pr_ThrottlingStates.Count++;
|
||||
printf(" %d%%", State[i].PercentOfClock);
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
AcpiOsFree(Buffer.Pointer);
|
||||
return_ACPI_STATUS(Status);
|
||||
}
|
||||
|
||||
static ACPI_STATUS
|
||||
acpi_pr_PolicyInitialize(struct acpi_pr_softc *sc)
|
||||
{
|
||||
ACPI_STATUS Status;
|
||||
|
||||
FUNCTION_TRACE(__func__);
|
||||
|
||||
if ((Status = AcpiSetProcessorSleepState(sc->pr_handle, sc->pr_PowerStates.ActiveState)) != AE_OK) {
|
||||
device_printf(sc->pr_dev, "could not set Active sleep state - %s\n", acpi_strerror(Status));
|
||||
return_ACPI_STATUS(Status);
|
||||
}
|
||||
|
||||
/* XXX need to hook ourselves to be called when things go idle */
|
||||
/* sc->pr_idleevent = EVENTHANDLER_FAST_REGISTER(idle_event, acpi_pr_IdleHandler, sc, IDLE_PRI_FIRST); */
|
||||
return_ACPI_STATUS(AE_OK);
|
||||
}
|
||||
|
||||
static void
|
||||
acpi_pr_IdleHandler(void *arg, int count)
|
||||
{
|
||||
struct acpi_pr_softc *sc = (struct acpi_pr_softc *)arg;
|
||||
ACPI_STATUS Status = AE_OK;
|
||||
PR_CX_STATE_INFO *CxState = NULL;
|
||||
PR_POWER_STATE ActiveState = PR_POWER_STATE_UNKNOWN;
|
||||
PR_POWER_STATE NextState = PR_POWER_STATE_UNKNOWN;
|
||||
u_int32_t PmTimerTicks = 0;
|
||||
|
||||
ActiveState = NextState = sc->pr_PowerStates.ActiveState;
|
||||
CxState = &(sc->pr_PowerStates.Info[ActiveState]);
|
||||
CxState->Utilization++;
|
||||
|
||||
/*
|
||||
* Invoke Cx State:
|
||||
* ----------------
|
||||
*/
|
||||
if ((Status = AcpiProcessorSleep(sc->pr_handle, &PmTimerTicks)) != AE_OK) {
|
||||
device_printf(sc->pr_dev, "AcpiProcessorSleep() failed - %s\n", acpi_strerror(Status));
|
||||
/*
|
||||
* Something went wrong with the sleep attempt, so give up on trying to do this.
|
||||
*/
|
||||
/* EVENTHANDLER_FAST_DEREGISTER(idle_event, sc->pr_idleevent);*/
|
||||
device_printf(sc->pr_dev, "disabling CPU power saving\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check For State Promotion:
|
||||
* --------------------------
|
||||
* Only need to check for promotion on C1 and C2, and then only
|
||||
* when the state has a non-zero count threshold and target state.
|
||||
*/
|
||||
if (CxState->PromotionPolicy.CountThreshold && CxState->PromotionPolicy.TargetState &&
|
||||
((ActiveState == PR_POWER_STATE_C1) || (ActiveState == PR_POWER_STATE_C2))) {
|
||||
/*
|
||||
* Check the amount of time we spent in the Cx state against our
|
||||
* promotion policy. If successful (asleep longer than our threshold)
|
||||
* increment our count and see if a promotion is in order.
|
||||
*/
|
||||
if (PmTimerTicks > (CxState->PromotionPolicy.TimeThreshold)) {
|
||||
CxState->PromotionPolicy.Count++;
|
||||
CxState->DemotionPolicy.Count = 0;
|
||||
|
||||
if (CxState->PromotionPolicy.Count >= CxState->PromotionPolicy.CountThreshold)
|
||||
NextState = CxState->PromotionPolicy.TargetState;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check For State Demotion:
|
||||
* -------------------------
|
||||
* Only need to check for demotion on C2 and C3, and then only
|
||||
* when the state has a non-zero count threshold and target state.
|
||||
*/
|
||||
if (CxState->DemotionPolicy.CountThreshold && CxState->DemotionPolicy.TargetState &&
|
||||
((ActiveState == PR_POWER_STATE_C2) || (ActiveState == PR_POWER_STATE_C3))) {
|
||||
/*
|
||||
* Check the amount of time we spent in the Cx state against our
|
||||
* demotion policy. If unsuccessful (asleep shorter than our threshold)
|
||||
* increment our count and see if a demotion is in order.
|
||||
*/
|
||||
if (PmTimerTicks < (CxState->DemotionPolicy.TimeThreshold)) {
|
||||
CxState->DemotionPolicy.Count++;
|
||||
CxState->PromotionPolicy.Count = 0;
|
||||
|
||||
if (CxState->DemotionPolicy.Count >= CxState->DemotionPolicy.CountThreshold)
|
||||
NextState = CxState->DemotionPolicy.TargetState;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* New Cx State?
|
||||
* -------------
|
||||
* If so, clean up from the previous Cx state (if necessary).
|
||||
*/
|
||||
if (NextState != sc->pr_PowerStates.ActiveState) {
|
||||
if ((Status = AcpiSetProcessorSleepState(sc->pr_handle, NextState)) != AE_OK) {
|
||||
device_printf(sc->pr_dev, "AcpiSetProcessorSleepState() returned error [0x%08X]\n", Status);
|
||||
} else {
|
||||
CxState->PromotionPolicy.Count = 0;
|
||||
CxState->DemotionPolicy.Count = 0;
|
||||
sc->pr_PowerStates.ActiveState = NextState;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user