/*++

Copyright (c) 2000  Microsoft Corporation

Module Name:

    crusoe.c

Abstract:

    This module implements code specific to the Crusoe processor
    

Author:

    Todd Carpenter (10/31/00) - create file

Environment:

    Kernel mode

Notes:

  DeviceInst  = "ACPI\GenuineTMx86_-_x86_Family_5_Model_4"
  ServiceName = "crusoe"
   
Revision History:


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

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

PFDO_DATA DeviceExtensions[MAX_SUPPORTED_PROCESSORS];
UCHAR DevExtIndex;

extern GLOBALS Globals;
extern LONGRUN_STATES LongRunStates;

#ifdef ALLOC_PRAGMA
#pragma alloc_text (PAGE, InitializeDriver)
#pragma alloc_text (PAGE, InitializeNonAcpiPerformanceStates)
#pragma alloc_text (PAGE, InitializeAcpi2PStates)
#pragma alloc_text (PAGE, InitializeAcpi2Cstates)
#pragma alloc_text (PAGE, MergePerformanceStates)
#pragma alloc_text (PAGE, GetLegacyMaxProcFrequency)
#pragma alloc_text (PAGE, AdjustLegacyProcessorPerformanceStates)
#pragma alloc_text (PAGE, GetProcessorBrandString)
#endif


NTSTATUS
InitializeDriver(
  PUNICODE_STRING ServiceKeyRegPath
  )
/*++

  Routine Description:


  Arguments:


  Return Value:


--*/
{
  PAGED_CODE();
  return STATUS_SUCCESS;
}


NTSTATUS
InitializeNonAcpiPerformanceStates(
  IN  PFDO_DATA   DeviceExtension
  )
/*++

  Routine Description:

  Arguments:

  Return Value:


--*/
{
  NTSTATUS  status;
  
  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(DeviceExtension);
  
  if (!NT_SUCCESS(status)) {
    DebugExitStatus(status);
    return status;
  }
  
  //
  // Found Legacy Interface, and is available to use.
  //
  
  DeviceExtension->LegacyInterface = TRUE;

              
  //
  // Merge Perf states with other states we may have.
  //
  
  status = MergePerformanceStates(DeviceExtension);
  DumpProcessorPerfStates(DeviceExtension->PerfStates);
  
  
  if (!NT_SUCCESS(status)) {
      
    if (DeviceExtension->PerfStates) {
      ExFreePool(DeviceExtension->PerfStates);
    }

    if (DeviceExtension->PssPackage) {
      ExFreePool(DeviceExtension->PssPackage);
    }
    
    DeviceExtension->PerfStates = NULL;
    DeviceExtension->LegacyInterface = FALSE;
  }

  
  DebugExitStatus(status);
  return status;
  
}


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

  Routine Description:
  

  Arguments:
  

  Return Value:


--*/
{  
  return Acpi2PerfStateTransition(DevExt, 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);

  if (NT_SUCCESS(status)) {
  
    if ((DevExt->PctPackage.Control.AddressSpaceID != AcpiGenericSpaceFixedFunction) ||
        (DevExt->PctPackage.Status.AddressSpaceID != AcpiGenericSpaceFixedFunction)) {

      DebugPrint((ERROR, "This driver ONLY supports FFH addresses for Acpi 2.0\n"));
	  
      //
      // 4chan: server2003 update, free PssPackage & return from here properly
      //
      
      //
      // 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();
  PAGED_CODE();

  
  //
  // 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();
  PAGED_CODE();

  //
  // Use the generic method to merge our perf states with stop clock states
  //
  
  return MergePerformanceStatesGeneric(DeviceExtension);

}


NTSTATUS
Acpi2PerfStateTransition(
  IN PFDO_DATA DevExt,
  IN ULONG     NewState
  )
/*++

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

--*/
{
  ULONG lowerBound;
  ULONG uppperBound;
  NTSTATUS   status = STATUS_SUCCESS;
  STATE_INFO stateInfo;
  CRUSOE_PSS_VALUE  pssControl;
  CRUSOE_PSS_VALUE  pssStatus;
  ULARGE_INTEGER longRunRange;

  DebugEnter();
  
  //
  // The only type of Acpi 2.0 implementation this driver supports is FFH
  //
  
  DebugAssert(DevExt);
  DebugAssert(DevExt->PctPackage.Control.AddressSpaceID == AcpiGenericSpaceFixedFunction);
  DebugAssert(DevExt->PctPackage.Status.AddressSpaceID == AcpiGenericSpaceFixedFunction);

  pssControl.AsDWord = DevExt->PssPackage->State[NewState].Control;
  pssStatus.AsDWord = DevExt->PssPackage->State[NewState].Status;
  
  //
  // Sanity Check illeagal conditions
  //

  if (pssControl.Min > MAX_LONGRUN_VALUE) {
    DebugPrint((ERROR, "ERROR: illegal LongRun value: Min=0x%x\n", pssControl.Min));
    pssControl.Min = MAX_LONGRUN_VALUE;
  }

  if (pssControl.Max > MAX_LONGRUN_VALUE) {
    DebugPrint((ERROR, "ERROR: illegal LongRun value: Max=0x%x\n", pssControl.Max));
    pssControl.Max = MAX_LONGRUN_VALUE;
  }

  if (pssControl.Min > pssControl.Max) {
    DebugPrint((ERROR, "ERROR: illegal LongRun Range: Min: 0x%x, Max: 0x%x\n",
                pssControl.Min,
                pssControl.Max));

    pssControl.Min = pssControl.Max;  
  }

  
  //
  // Transition to new performance state
  //

  DebugPrint((ERROR, "Setting Long Run Range: l=0x%x, u=0x%x\n", pssControl.Min, pssControl.Max));
  SetCurrentPerformanceRange(pssControl.Min, pssControl.Max);

  
  //
  // Check to see if the transition was successful
  // NOTE: we have to check the LongRun Range rather than a specific state as
  //       there is no way to know which state within a given range the cpu 
  //       will be in.
  //
  
  longRunRange.QuadPart = GetCurrentPerformanceRange();

    
  if ((longRunRange.HighPart == pssStatus.Max) &&
      (longRunRange.LowPart == pssStatus.Min)) {

    DevExt->CurrentPssState = NewState;

  } else {

    DebugPrint((ERROR, "ERROR! Expected: l=0x%x, h=0x%x Recieved: l=0x%x, h=0x%x\n",
                pssStatus.Min, 
                pssStatus.Max,
                longRunRange.LowPart,
                longRunRange.HighPart));
                

    status = STATUS_UNSUCCESSFUL;
  }

  return status;
}


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

  //
  // 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
GetLegacyMaxProcFrequency(
  OUT PULONG CpuSpeed
  )
/*++

  Description:
    

  Arguments:
    
    
  Return Value:

    NTSTATUS


--*/ 
{
  PAGED_CODE();
  return GetMaxCpuSpeed(CpuSpeed);
}


NTSTATUS
AdjustLegacyProcessorPerformanceStates(
  IN OUT PPROCESSOR_PERFORMANCE_STATES PerfStates
  )
/*++

  Routine Description:
  

  Arguments:
  

  Return Value:


--*/
{
  PAGED_CODE();
  return STATUS_SUCCESS;
}


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

  Routine Description:
  
  Arguments:
  
  Return Value:


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