/*++

Copyright (c) 2000  Microsoft Corporation

Module Name:

    amdk6.c

Abstract:

    This module implements code specific to the AMDK6-2 processor


Author:

    Todd Carpenter (7/20/00) - create file

Environment:

    Kernel mode

Notes:

  DeviceInst  = "ACPI\AuthenticAMD_-_x86_Family_5_Model_13"
  ServiceName = "amdk6"

Revision History:


--*/
#include "amdk6.h"
#include "..\lib\processor.h"

#if DBG
PUCHAR DebugName = "AmdK6.sys";
#endif

PFDO_DATA DeviceExtensions[MAX_SUPPORTED_PROCESSORS];
UCHAR DevExtIndex;

extern GLOBALS Globals;
extern LEGACY_GEMINI_SMI LegacyInterface;


#ifdef ALLOC_PRAGMA
#pragma alloc_text (PAGE, InitializeNonAcpiPerformanceStates)
#pragma alloc_text (PAGE, InitializeAcpi2PStates)
#pragma alloc_text (PAGE, InitializeAcpi2Cstates)
#pragma alloc_text (PAGE, MergePerformanceStates)
#endif


NTSTATUS
InitializeDriver(
  PUNICODE_STRING ServiceKeyRegPath
  )
/*++

  Routine Description:


  Arguments:


  Return Value:


--*/
{
  return STATUS_SUCCESS;
}

NTSTATUS
 InitializeNonAcpiPerformanceStates(
  IN PFDO_DATA DeviceExtension
  )
/*++

Routine Description:

    This function tries to gather as much information as it can about what
    states are available.  It puts a table in the device extension that
    describes the available states.  Unfortunately, we might not have enough
    information to fill in the state table, as the BIOS doesn't tell us much.
    We try to run through all the states, figuring out what we can about them
    as we pass through them.  But some states may not be available at the
    moment, particularly if we are running on DC power at the moment.  So some
    states may have zeros in the table.

Arguments:

   FdoData - pointer to the device extension

Return Value:

    NT status code


--*/
{
  LARGE_INTEGER   perfCounterOld, perfCounterNew;
  LARGE_INTEGER   perfFreq, latency;
  ULONG           newState, maxFreq, currentFreq;
  ULONG           currentlatency, maxTransitionLatency = 0;
  ULONG           perfStatesSize;
  UCHAR           i;
  NTSTATUS        status;

  PLEGACY_GEMINI_SMI info = &LegacyInterface;

  DebugEnter();
  PAGED_CODE();


  //
  // We automatically fail to use the Legacy Interface
  //
  
  if (Globals.HackFlags & DISABLE_LEGACY_INTERFACE_FLAG) {
    DebugPrint((ERROR, " Legacy Interface Disabled\n"));
    return STATUS_NOT_FOUND;
  }
  

  //
  // Find and gather legacy interface info
  //

  status = InitializeLegacyInterface();

  if (!NT_SUCCESS(status)) {
    DebugExitStatus(status);
    return status;
  }

  //
  // Found Legacy Interface, and is available to use.
  //

  DeviceExtension->LegacyInterface = TRUE;

  if (info->CurrentlyAvailableStates == 0) {
    status = STATUS_NOT_FOUND;
    DebugPrint((ERROR, "  Found Legacy Interface, but no perfstates.\n"));
    DebugExitStatus(status);
    return status;
  }

  //
  // if there were already PerfStates, they were probably acpi 1.0 type
  // throttling, so we will blow them away, because they will be recreated
  // in MergePerformanceStates()
  //

  if (DeviceExtension->PerfStates) {
    ExFreePool(DeviceExtension->PerfStates);
  }

  perfStatesSize = sizeof(PROCESSOR_PERFORMANCE_STATES) +
                     (sizeof(PROCESSOR_PERFORMANCE_STATE) *
                     (info->CurrentlyAvailableStates - 1));


  DeviceExtension->PerfStates = ExAllocatePoolWithTag(PagedPool,
                                                      perfStatesSize,
                                                      PROCESSOR_POOL_TAG);


  if (!DeviceExtension->PerfStates) {

    status = STATUS_INSUFFICIENT_RESOURCES;
    goto InitalizeNonAcpiPerformanceStatesExit;

  }

  RtlZeroMemory(DeviceExtension->PerfStates, perfStatesSize);


  //
  // Jump through each state so that we can figure out how to fill in
  // the table.  Start by jumping to the low state.
  //

  DebugPrint((INFO, "Perf States available: 0x%x  Current State: 0x%x\n",
              info->CurrentlyAvailableStates,
              info->CurrentState));


  if (info->CurrentlyAvailableStates >= 1) {

    if (info->CurrentState != 0) {

      TRAP();

      //
      // Go to the highest state available
      //

      status = AcpiPerfStateTransition(DeviceExtension, 0);
                      
      if (!NT_SUCCESS(status)) {
        goto InitalizeNonAcpiPerformanceStatesExit;
      }
    }

    maxFreq = GetMaxProcFrequency(DeviceExtension);
    DebugAssert(maxFreq != 0);

    //
    // Get Perf Counter Frequency
    //
    KeQueryPerformanceCounter(&perfFreq);

    for (i = 0; i < info->CurrentlyAvailableStates; i++) {

      newState = info->CurrentlyAvailableStates - i - 1;

      perfCounterOld = KeQueryPerformanceCounter(NULL);
      status = AcpiPerfStateTransition(DeviceExtension, newState); 
      perfCounterNew = KeQueryPerformanceCounter(NULL);


      if (!NT_SUCCESS(status)) {
        goto InitalizeNonAcpiPerformanceStatesExit;
      }


      //
      // Calculate transition latency.
      //
      currentlatency = (ULONG)((perfCounterNew.QuadPart - perfCounterOld.QuadPart) *
                       1000000 / perfFreq.QuadPart);

      //
      // Save highest transition latency
      //
      maxTransitionLatency = MAX(maxTransitionLatency, currentlatency);

      DebugPrint((INFO, "Jumped to state 0x%x in %d microseconds\n",
                  newState,
                  currentlatency));


      //
      // Get current speed.
      //

      GetCpuFrequency(newState, &currentFreq);
      DebugAssert(currentFreq != 0);
      
      DeviceExtension->PerfStates->State[newState].Frequency = currentFreq;
      DeviceExtension->PerfStates->State[newState].PercentFrequency = (UCHAR)
        PERCENT_TO_PERF_LEVEL((currentFreq * 100) / maxFreq);
        

      //
      // Mark this as a Perf State
      //
      
      DeviceExtension->PerfStates->State[newState].Flags = PROCESSOR_STATE_TYPE_PERFORMANCE;

    }
  }

  DeviceExtension->PerfStates->TransitionLatency  = maxTransitionLatency;
  DeviceExtension->PerfStates->TransitionFunction = SetPerfLevel;
  DeviceExtension->PerfStates->Count = (UCHAR) info->CurrentlyAvailableStates;
  DeviceExtension->LowestPerfState = info->CurrentlyAvailableStates - 1;


  
  //
  // Return to orginal perf state.
  //
  
  status = AcpiPerfStateTransition(DeviceExtension, info->CurrentState); 
 
  if (!NT_SUCCESS(status)) {
    goto InitalizeNonAcpiPerformanceStatesExit;
  }

  //
  // Check to ensure that state 0 is higher than lowest perf state.
  //

  DebugAssert(DeviceExtension->PerfStates->State[0].Frequency >
              DeviceExtension->PerfStates->State[DeviceExtension->LowestPerfState].Frequency);


  //
  // Merge Perf states with other states we may have.
  //

  DumpProcessorPerfStates(DeviceExtension->PerfStates);
  status = MergePerformanceStates(DeviceExtension);


  InitalizeNonAcpiPerformanceStatesExit:

  if (!NT_SUCCESS(status)) {

    if (DeviceExtension->PerfStates) {
      ExFreePool(DeviceExtension->PerfStates);
    }

    DeviceExtension->PerfStates = NULL;
    DeviceExtension->LegacyInterface = FALSE;
  }
  
  DebugExitStatus(status);
  return status;
}


NTSTATUS
AcpiLegacyPerfStateTransition(
  IN PFDO_DATA    DeviceExtension,
  IN ULONG        State
  )
/*++

  Routine Description:


  Arguments:


  Return Value:


--*/
{
  //return SetCurrentStateSmm(State);
  return SetCurrentStateMSR(State);
}


NTSTATUS
FASTCALL
SetPerfLevel(
  IN UCHAR Throttle
  )
/*++

  Routine Description:


  Arguments:


  Return Value:


--*/
{
  PFDO_DATA   DeviceExtension;
  ULONG       apicId;
  ULONG       index = 0;

  DebugEnter();


  //
  // Get APIC Id and retrieve Device Extension index
  //

  if (!Globals.SingleProcessorProfile) {
    apicId = GetApicId();
    index = Globals.ProcInfo.ApicIdToDevExtIndex[apicId];
  }
  
  //
  // Get the DeviceExtension.
  //
  
  DeviceExtension = DeviceExtensions[index];

  //
  // Since this driver does not support Functional Fixed Hardware, we
  // use generic method.
  //

  return SetPerfLevelGeneric(Throttle, DeviceExtension);

}

NTSTATUS
FASTCALL
SetThrottleLevel(
  IN UCHAR Throttle
  )
/*++

  Routine Description:


  Arguments:


  Return Value:


--*/
{
  PFDO_DATA   DeviceExtension;
  ULONG       apicId;
  ULONG       index = 0;

  DebugEnter();

  //
  // Get APIC Id and retrieve Device Extension index
  //

  if (!Globals.SingleProcessorProfile) {
    apicId = GetApicId();
    index = Globals.ProcInfo.ApicIdToDevExtIndex[apicId];
  }

  
  //
  // Get the DeviceExtension.
  //
  
  DeviceExtension = DeviceExtensions[index];
  

  //
  // Since this driver does not support Functional Fixed Hardware, we
  // use generic method.
  //

  return SetThrottleLevelGeneric(Throttle, DeviceExtension);

}


NTSTATUS
InitializeAcpi2PStates(
  IN PFDO_DATA DevExt
  )
/*++

  Routine Description:
 

  Arguments:
  

  Return Value:


--*/
{
  NTSTATUS status;

  status = InitializeAcpi2PStatesGeneric(DevExt);

  //
  // Make sure we didn't find any NON I/O or MEM addresses
  //
  
  if (NT_SUCCESS(status)) {
  
    //
    // 4chan: server2003 change, fix if statement below so it's not always true (|| to &&)
    //
    if (((DevExt->PctPackage.Control.AddressSpaceID != AcpiGenericSpaceIO) &&
         (DevExt->PctPackage.Control.AddressSpaceID != AcpiGenericSpaceMemory)) ||
        ((DevExt->PctPackage.Status.AddressSpaceID != AcpiGenericSpaceIO) &&
         (DevExt->PctPackage.Status.AddressSpaceID != AcpiGenericSpaceMemory))) {

      DebugPrint((WARN, "ONLY Memory & I/O _PCT addresses are supported\n"));
      DebugPrint((WARN, "NOT using Acpi 2.0 Performance States\n"));

      //
      // Undo what InitializeAcpi2PStatesGeneric() did
      //

      if (DevExt->PssPackage) {
        ExFreePool(DevExt->PssPackage);
        DevExt->PssPackage = NULL;
      }
      
      return STATUS_NOT_SUPPORTED;
    }

    //
    // Walk through _PSS states to calculate latency values
    //
    
    ValidatePssLatencyValues(DevExt);

    //
    // Need to merge this new data with our perfstates
    //
  
    MergePerformanceStates(DevExt);

  }

  return status;
}

NTSTATUS
InitializeAcpi2Cstates(
    PFDO_DATA DevExt
    )
/*++

Routine Description:

    This function looks to see if there is an ACPI 2.0 _CST object in the
    namespace, and, if there is, it replaces the functions found by
    InitializeAcpi1Cstates.

    Further note:  This function leaves the filling in of throttling functions
    to the InitializePerformanceStates functions.

Arguments:

    DeviceExtension

Return Value:

    A NTSTATUS code to indicate the result of the initialization.

--*/
{
  ULONG  apicId;
  ULONG  index = 0;

  DebugEnter();

  
  //
  // Record the address of this processor's DeviceExtension, as the
  // throttling API doesn't give it to us.
  //

  if (!Globals.SingleProcessorProfile) {

    //
    // save the index into the DeviceExtension[] for later retrieval based
    // on APIC Id.
    //
    
    apicId = Globals.ProcInfo.ProcIdToApicId[DevExt->ProcObjInfo.PhysicalID];  
    Globals.ProcInfo.ApicIdToDevExtIndex[apicId] = DevExtIndex;

    index = DevExtIndex++;
    
  }

  
  //
  // save Device Extension pointer
  //
  
  DeviceExtensions[index] = DevExt;
  


  //
  //  This processor driver only supports I/O Space based Cstates.
  //  InitializeAcpi2IoSpaceCstates() will fail if it finds Cstates with
  //  non AcpiGenericSpaceIO type addresses.
  //

  return InitializeAcpi2IoSpaceCstates(DevExt);

}

NTSTATUS
MergePerformanceStates (
  IN  PFDO_DATA DeviceExtension
  )
/*++

  Routine Description:

      This routine looks at the performance states stored in the device extension.

  Arguments:

      DeviceExtension


  Return Value:

      A NTSTATUS code to indicate the result of the initialization.

  NOTE:

      - The caller must hold PerfStateLock.

      - This is called during START_DEVICE, and after recieving a Notify(0x80)
        on the processor.

--*/
{

  DebugEnter();

  //
  // Since this driver does not support Functional Fixed Hardware, we
  // use generic method.
  //

  return MergePerformanceStatesGeneric(DeviceExtension);

}


NTSTATUS
Acpi2PerfStateTransition(
  IN PFDO_DATA DeviceExtension,
  IN ULONG     State
  )
/*++

Routine Description:

    This routine changes the performance state of the processor
    based on ACPI 2.0 performance state objects.

Arguments:

    State - Index into _PSS object

Return Value:

    none

--*/
{

  //
  // Since this driver does not support Functional Fixed Hardware, we
  // use generic method.
  //

  return Acpi2PerfStateTransitionGeneric(DeviceExtension, State);

}


NTSTATUS
ProcessResumeFromSleepState(
  SYSTEM_POWER_STATE PreviousState,
  PFDO_DATA          DeviceExtension
  )
/*++

  Routine Description:


  Arguments:


  Return Value:


--*/
{
  DebugEnter();

  //
  // if we are resuming from Hibernate, and this is an Acpi 2.0 system,
  // we must re-claim perf state and cstate control from the bios.
  //

  if (PreviousState == PowerSystemHibernate) {

    if (DeviceExtension->PssPackage) {
    
      AssumeProcessorPerformanceControl();
    }
    
    if (DeviceExtension->CstPresent) {
      AssumeCStateControl();
    }
    
  }

  //
  // Reinit the legacy interface
  //

  if (DeviceExtension->LegacyInterface) {
    EnableGeminiTransitionsMSR(&LegacyInterface.EpmIoAddress);
  }

  //
  // restore previous state
  //

  return RestoreToSavedPerformanceState(DeviceExtension);

}


NTSTATUS
ProcessSuspendToSleepState(
  SYSTEM_POWER_STATE TargetState,
  PFDO_DATA          DeviceExtension
  )
/*++

  Routine Description:


  Arguments:


  Return Value:


--*/
{ 
  DebugEnter();

  //
  // save current state, transition to lowest non-throttled perf state
  //

  return SaveCurrentStateGoToLowVolts(DeviceExtension);
}


NTSTATUS
GetProcessorBrandString (
  PUCHAR BrandString,
  PULONG Size
  )
/*++

  Routine Description:
  
  Arguments:
  
  Return Value:


--*/
{
  //
  // This processor family supports CPUID Brand String
  //
  
  return GetCPUIDProcessorBrandString(BrandString, Size);
}
