summaryrefslogtreecommitdiffstats
path: root/sys/dev
diff options
context:
space:
mode:
authormsmith <msmith@FreeBSD.org>2001-07-07 10:27:17 +0000
committermsmith <msmith@FreeBSD.org>2001-07-07 10:27:17 +0000
commit69e8b198c501f15406c99c41c59ceb575aa9a374 (patch)
treec605e11361d7f8bc987c0698ec7d97dde1e859a1 /sys/dev
parenta9dfae497adb601087b346f63456c2b1f750b3a4 (diff)
downloadFreeBSD-src-69e8b198c501f15406c99c41c59ceb575aa9a374.zip
FreeBSD-src-69e8b198c501f15406c99c41c59ceb575aa9a374.tar.gz
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.
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/acpica/acpi_cpu.c388
-rw-r--r--sys/dev/acpica/acpi_processor.c669
2 files changed, 388 insertions, 669 deletions
diff --git a/sys/dev/acpica/acpi_cpu.c b/sys/dev/acpica/acpi_cpu.c
new file mode 100644
index 0000000..0a6c6f3
--- /dev/null
+++ b/sys/dev/acpica/acpi_cpu.c
@@ -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);
+}
diff --git a/sys/dev/acpica/acpi_processor.c b/sys/dev/acpica/acpi_processor.c
deleted file mode 100644
index 1898602..0000000
--- a/sys/dev/acpica/acpi_processor.c
+++ /dev/null
@@ -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;
- }
- }
-}
OpenPOWER on IntegriCloud