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:
Mike Smith 2001-07-07 10:27:17 +00:00
parent 7d3bcec9fb
commit fec754d4b4
3 changed files with 389 additions and 670 deletions

View File

@ -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
View 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);
}

View File

@ -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;
}
}
}