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