/*- * 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 #include #include #include "acpi.h" #include #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); 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; if (AcpiGetHandle(ACPI_ROOT_OBJECT, "\\_PR_", &handle) == AE_OK) AcpiWalkNamespace(ACPI_TYPE_PROCESSOR, handle, 2, acpi_pr_identify_cpu, bus, NULL); } /* * 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; 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(AE_OK); } acpi_set_handle(child, handle); acpi_set_magic(child, PR_MAGIC); device_set_desc(child, "processor device"); } return(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; 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(0); } /* * Find the Local Apic information for this CPU */ static void acpi_pr_FindLapic(device_t dev, ACPI_HANDLE handle, PROCESSOR_APIC *lapic) { ACPI_BUFFER buf; ACPI_STATUS status; APIC_HEADER *hdr; APIC_TABLE *tbl; PROCESSOR_APIC *pap; int len, cpuno; /* * 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); /* * 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); } 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; /* * 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(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(Status); } static ACPI_STATUS acpi_pr_CalculatePerformanceStates(struct acpi_pr_softc *sc) { ACPI_STATUS Status = AE_OK; /* TODO... */ return(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; /* * 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(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(Status); } static ACPI_STATUS acpi_pr_PolicyInitialize(struct acpi_pr_softc *sc) { ACPI_STATUS status; 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(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(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; } } }