/*++ 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, ¤tFreq); 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)) { 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); }