439 lines
12 KiB
C
439 lines
12 KiB
C
/*++
|
|
|
|
Copyright (c) 2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
cstatec.c
|
|
|
|
Abstract:
|
|
|
|
This module implements code to find and intialize
|
|
ACPI C-states.
|
|
|
|
Author:
|
|
|
|
Jake Oshins (3/27/00) - create file
|
|
|
|
Environment:
|
|
|
|
Kernel mode
|
|
|
|
Notes:
|
|
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "processor.h"
|
|
#include "ntacpi.h"
|
|
|
|
//
|
|
// Acpi defines
|
|
//
|
|
|
|
#define P_LVL2 4
|
|
#define P_LVL3 5
|
|
|
|
|
|
UCHAR HalpPiix4;
|
|
BOOLEAN HalpBroken440BX;
|
|
PULONG HalpOutstandingScatterGatherCount;
|
|
ULONG HalpThrottleScale;
|
|
ULONG HalpPiix4SlotNumber;
|
|
ULONG HalpPiix4DevActB;
|
|
BOOLEAN AcpiC3Win2kCompatable;
|
|
ULONG Piix4ThrottleFix;
|
|
|
|
GEN_ADDR PBlkAddress;
|
|
extern FADT HalpFixedAcpiDescTable;
|
|
extern GEN_ADDR PCntAddress;
|
|
extern GEN_ADDR C2Address;
|
|
extern GEN_ADDR C3Address;
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text (PAGE, InitializeAcpi1Cstates)
|
|
#pragma alloc_text (PAGE, GetNumThrottleSettings)
|
|
#endif
|
|
|
|
NTSTATUS
|
|
InitializeAcpi1Cstates(
|
|
PFDO_DATA DeviceExtension
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine discovers any available ACPI 1.0 C-states
|
|
and fills in the CState structure in the device
|
|
extension.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension
|
|
|
|
Return Value:
|
|
|
|
NT status code
|
|
|
|
|
|
--*/
|
|
#define NUM_HACKS 5
|
|
{
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
UNICODE_STRING UnicodeString;
|
|
HANDLE BaseHandle = NULL;
|
|
ULONG disposition, length, i;
|
|
NTSTATUS status;
|
|
FADT *fadt;
|
|
BOOLEAN C2present = FALSE;
|
|
BOOLEAN C3present = FALSE;
|
|
|
|
struct {
|
|
KEY_VALUE_PARTIAL_INFORMATION Inf;
|
|
ULONG Data;
|
|
} PartialInformation;
|
|
|
|
struct {
|
|
PVOID HackTarget;
|
|
ULONG DataSize;
|
|
PWCHAR RegString;
|
|
} HacksFromHal[NUM_HACKS] =
|
|
{
|
|
|
|
{&HalpPiix4,
|
|
sizeof(HalpPiix4),
|
|
L"Piix4"},
|
|
{&HalpBroken440BX, // unused
|
|
sizeof(HalpBroken440BX),
|
|
L"440BX"},
|
|
{&HalpOutstandingScatterGatherCount, // unused
|
|
sizeof(HalpOutstandingScatterGatherCount),
|
|
L"SGCount"},
|
|
{&HalpPiix4SlotNumber,
|
|
sizeof(HalpPiix4SlotNumber),
|
|
L"Piix4Slot"},
|
|
{&HalpPiix4DevActB,
|
|
sizeof(HalpPiix4DevActB),
|
|
L"Piix4DevActB"}
|
|
|
|
};
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Get CState hacks from Registy
|
|
//
|
|
|
|
GetRegistryDwordValue(PROCESSOR_PARAMETERS_REG_PATH,
|
|
CSTATE_FLAGS_REG_KEY,
|
|
&Globals.CStateFlags);
|
|
|
|
|
|
//
|
|
// Determine whether C2 and C3 are usable. We don't try to use
|
|
// C2 and C3 on any ACPI MP machine. Even if they say that they
|
|
// support it, they really don't.
|
|
//
|
|
|
|
if ((DeviceExtension->ProcObjInfo.PBlkLength &&
|
|
DeviceExtension->ProcObjInfo.PBlkAddress) &&
|
|
Globals.SingleProcessorProfile) {
|
|
|
|
//
|
|
// We have a PBLK, which means that we might
|
|
// be able to do C2 and/or C3.
|
|
//
|
|
|
|
if (HalpFixedAcpiDescTable.lvl2_latency <= 100) {
|
|
C2present = TRUE;
|
|
}
|
|
|
|
//
|
|
// HACKHACK
|
|
//
|
|
// Win98 does not handle the 440BX workaround. So a prudent
|
|
// BIOS vendor cannot specify a useful latency for C3 without
|
|
// causing their machine to hang on Win98. So we are letting
|
|
// them specify the real value plus 0xa000 so that they can
|
|
// trick Win98 into not using C3 and let it work for us.
|
|
//
|
|
|
|
if (HalpFixedAcpiDescTable.lvl3_latency >= 0xa000) {
|
|
HalpFixedAcpiDescTable.lvl3_latency -= 0xa000;
|
|
}
|
|
|
|
if ((HalpFixedAcpiDescTable.lvl3_latency <= 1000) &&
|
|
HalpFixedAcpiDescTable.pm2_ctrl_blk_io_port) {
|
|
|
|
C3present = TRUE;
|
|
}
|
|
|
|
|
|
PCntAddress.AddressSpaceID = AcpiGenericSpaceIO;
|
|
PCntAddress.BitWidth = 32;
|
|
PCntAddress.BitOffset = 0;
|
|
PCntAddress.Reserved = 0;
|
|
PCntAddress.Address.HighPart = 0;
|
|
PCntAddress.Address.LowPart = DeviceExtension->ProcObjInfo.PBlkAddress;
|
|
|
|
//
|
|
// Compatability with Acpi 1.0 Cstate handlers
|
|
//
|
|
|
|
PBlkAddress = PCntAddress;
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Check for C state overrides
|
|
//
|
|
|
|
if (Globals.CStateFlags & CSTATE_FLAG_DISABLE_C2) {
|
|
C2present = FALSE;
|
|
}
|
|
|
|
|
|
if (Globals.CStateFlags & CSTATE_FLAG_DISABLE_C3) {
|
|
C3present = FALSE;
|
|
}
|
|
|
|
|
|
if (Globals.CStateFlags & CSTATE_FLAG_WIN2K_COMPAT) {
|
|
AcpiC3Win2kCompatable = TRUE;
|
|
}
|
|
|
|
//
|
|
// Fill in the DeviceExtension with C-state handlers.
|
|
//
|
|
|
|
ASSERT(DeviceExtension->CStates == NULL);
|
|
|
|
DeviceExtension->CStates =
|
|
ExAllocatePoolWithTag(NonPagedPool,
|
|
sizeof(PROCESSOR_IDLE_STATES) +
|
|
(C2present ? sizeof(PROCESSOR_IDLE_STATE) : 0) +
|
|
(C3present ? sizeof(PROCESSOR_IDLE_STATE) : 0),
|
|
PROCESSOR_POOL_TAG);
|
|
|
|
if (!DeviceExtension->CStates) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// C1 is always possible.
|
|
//
|
|
|
|
DeviceExtension->CStates->Count = 1;
|
|
RtlZeroMemory(&(DeviceExtension->CStates->State[0].Register), sizeof(GEN_ADDR));
|
|
DeviceExtension->CStates->State[0].StateType = 1;
|
|
DeviceExtension->CStates->State[0].Latency = 0;
|
|
DeviceExtension->CStates->State[0].IdleHandler = AcpiC1Idle;
|
|
|
|
i = 1;
|
|
|
|
if (C2present) {
|
|
|
|
DeviceExtension->CStates->Count++;
|
|
DeviceExtension->CStates->State[i].Register.AddressSpaceID = AcpiGenericSpaceIO;
|
|
DeviceExtension->CStates->State[i].Register.BitWidth = 8;
|
|
DeviceExtension->CStates->State[i].Register.BitOffset = 0;
|
|
DeviceExtension->CStates->State[i].Register.Reserved = 0;
|
|
DeviceExtension->CStates->State[i].Register.Address.HighPart = 0;
|
|
DeviceExtension->CStates->State[i].Register.Address.LowPart =
|
|
DeviceExtension->ProcObjInfo.PBlkAddress + P_LVL2;
|
|
DeviceExtension->CStates->State[i].StateType = 2;
|
|
DeviceExtension->CStates->State[i].Latency =
|
|
HalpFixedAcpiDescTable.lvl2_latency;
|
|
DeviceExtension->CStates->State[i].IdleHandler = AcpiC2Idle;
|
|
|
|
C2Address = DeviceExtension->CStates->State[i].Register;
|
|
|
|
i++;
|
|
}
|
|
|
|
if (C3present) {
|
|
|
|
DeviceExtension->CStates->Count++;
|
|
DeviceExtension->CStates->State[i].Register.AddressSpaceID = AcpiGenericSpaceIO;
|
|
DeviceExtension->CStates->State[i].Register.BitWidth = 8;
|
|
DeviceExtension->CStates->State[i].Register.BitOffset = 0;
|
|
DeviceExtension->CStates->State[i].Register.Reserved = 0;
|
|
DeviceExtension->CStates->State[i].Register.Address.HighPart = 0;
|
|
DeviceExtension->CStates->State[i].Register.Address.LowPart =
|
|
DeviceExtension->ProcObjInfo.PBlkAddress + P_LVL3;
|
|
DeviceExtension->CStates->State[i].StateType = 3;
|
|
DeviceExtension->CStates->State[i].Latency =
|
|
HalpFixedAcpiDescTable.lvl3_latency;
|
|
DeviceExtension->CStates->State[i].IdleHandler = AcpiC3ArbdisIdle;
|
|
|
|
C3Address = DeviceExtension->CStates->State[i].Register;
|
|
}
|
|
|
|
//
|
|
// Read the registry to figure out what special HAL hacks to duplicate here.
|
|
//
|
|
|
|
RtlInitUnicodeString(&UnicodeString,
|
|
L"\\REGISTRY\\MACHINE\\SYSTEM\\CURRENTCONTROLSET\\Control\\HAL\\CStateHacks");
|
|
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&UnicodeString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
status = ZwOpenKey (&BaseHandle,
|
|
KEY_READ,
|
|
&ObjectAttributes);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto InitializeAcpi1CstatesExit;
|
|
}
|
|
|
|
//
|
|
// Read values for each of the hacks.
|
|
//
|
|
|
|
for (i = 0; i < NUM_HACKS; i++) {
|
|
|
|
RtlInitUnicodeString(&UnicodeString,
|
|
HacksFromHal[i].RegString);
|
|
|
|
status = ZwQueryValueKey(BaseHandle,
|
|
&UnicodeString,
|
|
KeyValuePartialInformation,
|
|
&PartialInformation,
|
|
sizeof(PartialInformation),
|
|
&length);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto InitializeAcpi1CstatesExit;
|
|
}
|
|
|
|
ASSERT(PartialInformation.Inf.DataLength == sizeof(ULONG));
|
|
|
|
RtlCopyMemory(HacksFromHal[i].HackTarget,
|
|
(PUCHAR)(PartialInformation.Inf.Data),
|
|
HacksFromHal[i].DataSize);
|
|
}
|
|
|
|
|
|
InitializeAcpi1CstatesExit:
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
ExFreePool(DeviceExtension->CStates);
|
|
DeviceExtension->CStates = NULL;
|
|
}
|
|
|
|
ZwClose(BaseHandle);
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
InitializeAcpi1TStates(
|
|
PFDO_DATA DeviceExtension
|
|
)
|
|
{
|
|
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
ULONG freq;
|
|
ULONG stepFreq;
|
|
ULONG maxFreq;
|
|
ULONG i;
|
|
|
|
|
|
//
|
|
// We may be called to re-init the Acpi 1.0 Throttling states,
|
|
// remove any previous states.
|
|
//
|
|
|
|
if (DeviceExtension->PerfStates) {
|
|
ExFreePool(DeviceExtension->PerfStates);
|
|
DeviceExtension->PerfStates = NULL;
|
|
DeviceExtension->CurrentPerfState = INVALID_PERF_STATE;
|
|
}
|
|
|
|
|
|
HalpThrottleScale = (ULONG) GetNumThrottleSettings(DeviceExtension);
|
|
|
|
if (HalpThrottleScale) {
|
|
|
|
DeviceExtension->PerfStates = ExAllocatePoolWithTag(NonPagedPool,
|
|
sizeof(PROCESSOR_PERFORMANCE_STATES) +
|
|
(sizeof(PROCESSOR_PERFORMANCE_STATE) *
|
|
(HalpThrottleScale - 1)),
|
|
PROCESSOR_POOL_TAG);
|
|
|
|
if (!DeviceExtension->PerfStates) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto InitializeAcpi1TStatesExit;
|
|
}
|
|
|
|
//
|
|
// Initialize Throttle States to "Throttle off"
|
|
//
|
|
|
|
ProcessorThrottle((UCHAR)HalpThrottleScale);
|
|
|
|
|
|
DeviceExtension->PerfStates->Count = (UCHAR) HalpThrottleScale;
|
|
DeviceExtension->PerfStates->TransitionLatency = 0;
|
|
DeviceExtension->PerfStates->TransitionFunction = SetThrottleLevel;
|
|
DeviceExtension->CurrentPerfState = 0;
|
|
DeviceExtension->LowestPerfState = 0;
|
|
|
|
freq = maxFreq = GetMaxProcFrequency(DeviceExtension);
|
|
stepFreq = (maxFreq / DeviceExtension->PerfStates->Count);
|
|
|
|
for (i = 0; i < DeviceExtension->PerfStates->Count; i++) {
|
|
|
|
//
|
|
// Create a perfstate for each throttle setting.
|
|
//
|
|
|
|
DeviceExtension->PerfStates->State[i].Frequency = freq;
|
|
DeviceExtension->PerfStates->State[i].PercentFrequency = (UCHAR)
|
|
((freq * POWER_PERF_SCALE) / maxFreq);
|
|
|
|
DeviceExtension->PerfStates->State[i].Flags = PROCESSOR_STATE_TYPE_THROTTLE;
|
|
|
|
freq -= stepFreq;
|
|
}
|
|
}
|
|
|
|
InitializeAcpi1TStatesExit:
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
UCHAR
|
|
GetNumThrottleSettings(
|
|
IN PFDO_DATA DeviceExtension
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
if (HalpFixedAcpiDescTable.duty_width &&
|
|
DeviceExtension->ProcObjInfo.PBlkLength &&
|
|
DeviceExtension->ProcObjInfo.PBlkAddress &&
|
|
Globals.SingleProcessorProfile) {
|
|
|
|
//
|
|
// Only do throttling if the machine claims to support it.
|
|
// Currently we only support throttling on UP machines.
|
|
//
|
|
|
|
return 1 << HalpFixedAcpiDescTable.duty_width;
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|