/*++

Copyright (c) 2001  Microsoft Corporation

Module Name:

    viac3.c

Abstract:

    This module implements code that works on Cyrix processors with LongHaul power management support.

Author:

    Tom Brown (t-tbrown) 07-Jun-2001

Environment:

    Kernel mode

Notes:

    DeviceInst  = "ACPI\CentaurHauls_-_x86_Family_6_Model_7"
    DeviceInst  = "ACPI\CentaurHauls_-_x86_Family_6_Model_8"
    ServiceName = "viac3"

Revision History:

--*/
#include "..\lib\processor.h"
#include "legacy.h"
#include "viac3.h"


//
// Must define for debug output
//

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

PFDO_DATA DeviceExtensions[MAX_SUPPORTED_PROCESSORS];
UCHAR DevExtIndex;

extern GLOBALS Globals;


#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(
    IN PUNICODE_STRING ServiceKeyRegPath
    )

/*++

Routine Description:

    Initializes the LongHaul flags be getting information from the CPU and reading
    the HackFlagsOn and HackFlagsOff keys.
    
Arguments:

    ServiceKeyRegPath - This proc drivers key in the registry
    
Return Value:

    NTSTATUS - STATUS_SUCCESS or an error
    
--*/

{
    ULONG flags;
 
    LongHaulFlags = 0x0;

    // This driver is not MP safe, and most likely never will
    if ( Globals.SingleProcessorProfile == FALSE ) {
        return STATUS_DRIVER_UNABLE_TO_LOAD;    // Lib ignores this return value
    }

    // Just loaded, there are no queued work items
    NextTransitionThrottle = INVALID_THROTTLE;

    IdentifyCPUVersion();

    // Add and remove flags according to registry
    // HackFlagsOn will be set, then
    // HackFlagsOff will be cleared
    GetRegistryDwordValue((PWCHAR) VIAC3_PARAMETERS_KEY, 
                        L"HackFlags", 
                        &flags);

    if( flags & DISABLE_ALL_HACK_FLAG ) {
        DebugPrint((WARN, "All legacy functionality disabled by DISABLE_ALL_HACK_FLAG.\n"));
        LongHaulFlags = 0x0;
    } else if( flags & NO_SOFTVID_HACK_FLAG ) {
        DebugPrint((WARN, "softVID disabled by NO_SOFTVID_HACK_FLAG.\n"));
        LongHaulFlags &= ~SUPPORTS_SOFTVID_FLAG;        
    } else if( flags & NO_VOLTAGE_STEPPING_HACK_FLAG ) {
        DebugPrint((WARN, "Will not transition voltage in steps. Can cause crashes. Set by NO_VOLTAGE_STEPPING_HACK_FLAG.\n"));
        LongHaulFlags &= ~NEEDS_VOLTAGE_STEPPING_FLAG;
    }
       
    InitializeCPU();

    #ifdef DBG
    DebugShowCurrent();
    #endif

    return STATUS_SUCCESS;
}

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

  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
GetProcessorBrandString (
    PUCHAR BrandString,
    PULONG Size
    )
/*++

Routine Description:
  
Arguments:
  
Return Value:

--*/
{
  //
  // With the generic driver we only try to find the Processor Brand String 
  // via the CPUID
  //
  
  return GetCPUIDProcessorBrandString(BrandString, Size);
}


NTSTATUS
InitializeAcpi2PStates(
    IN PFDO_DATA DevExt
    )
/*++

Routine Description:

Arguments:

Return Value:

--*/
{
  NTSTATUS status;

  status = InitializeAcpi2PStatesGeneric(DevExt);
  
  //
  // Check that we only access FFH  
  //
  
  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"));
      status = STATUS_NOT_FOUND;
      goto InitializeAcpi2PStatesExit;
    }

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

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

  }

InitializeAcpi2PStatesExit:
  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();

  return MergePerformanceStatesGeneric(DeviceExtension);

}

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

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

Arguments:

    DeviceExtension - The device extension for this object
    
    NewState - Index into _PSS object

Return Value:

    None

--*/

{
    NTSTATUS status = STATUS_SUCCESS;
    
    DebugEnter();
    DebugAssert(DeviceExtension->PssPackage)
    DebugAssert(NewState >= DeviceExtension->PpcResult);
    DebugAssert(NewState < DeviceExtension->PssPackage->NumPStates);
    DebugAssert(DeviceExtension->PssPackage->State[NewState].CoreFrequency);

    if (DeviceExtension->PctPackage.Control.AddressSpaceID != AcpiGenericSpaceFixedFunction) {
        DebugAssert(!"Acpi2PerfStateTransition ONLY understands FFH addresses");
        status = STATUS_UNSUCCESSFUL;
        goto Acpi2PerfStateTransitionExit;
    }

    if (NewState == DeviceExtension->CurrentPssState) {
        DebugPrint((WARN, "Acpi2PerfStateTransition() CurrentState == TargetState, exiting...\n"));
        goto Acpi2PerfStateTransitionExit;
    }

    // Queue the transition so it always run at PASSIVE irql
    status = QueueTransition( DeviceExtension, NewState);

    if( NT_SUCCESS(status) ) {
        DeviceExtension->CurrentPssState = NewState;
    }

    Acpi2PerfStateTransitionExit:

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


