/*++ Copyright (c) 2000 Microsoft Corporation Module Name: amdk7.c Abstract: This module implements code specific to the AMDK7 processor Author: Todd Carpenter (1/30/01) - create file Environment: Kernel mode Notes: DeviceInst = "ACPI\AuthenticAMD_-_x86_Family_6_Model_6" ServiceName = "amdk7" Revision History: --*/ #include "..\lib\processor.h" #include "amdk7.h" #if DBG PUCHAR DebugName = "AmdK7.sys"; #endif PFDO_DATA DeviceExtensions[MAX_SUPPORTED_PROCESSORS]; UCHAR DevExtIndex; ULONG AmdK7HackFlags; extern GLOBALS Globals; #ifdef ALLOC_PRAGMA #pragma alloc_text (PAGE, InitializeDriver) #pragma alloc_text (PAGE, GetProcessorBrandString) #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: --*/ { // // Get AmdK7 Hack Flags // GetRegistryDwordValue((PWCHAR) AMDK7_PARAMETERS_KEY, L"HackFlags", &AmdK7HackFlags); 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]; // // This driver only supports FFH addresses, but SetPerfLevelGeneric // only finds the correct state, it will then call Acpi2PerfStateTransition // 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]; // // This driver supports Acpi 1.0b style of throttling, use // generic function // return SetThrottleLevelGeneric(Throttle, DeviceExtension); } NTSTATUS GetProcessorBrandString ( PUCHAR BrandString, PULONG Size ) /*++ Routine Description: Arguments: Return Value: --*/ { // // This processor supports the CpuId based brand string // return GetCPUIDProcessorBrandString(BrandString, Size); } 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((WARN, "ONLY Acpi 2.0 FFH 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. --*/ { NTSTATUS status; NTSTATUS findStatus; ULONG pssState = 0xdeadbeef; DebugEnter(); // // This function just merges available _PSS and stop clock throttle states // status = MergePerformanceStatesGeneric(DeviceExtension); // // With amdk7.sys, the order of transition execution differs if going from a // higher state to a lower state when compared to going from a lower state to // a higher state... Therefore, it is important that we set the current state // before registering new performance states with the kernel. // // NOTE: MergePerformanceStatesGeneric() will invalidate CurrentPerfState, // so it must be set here. // if (NT_SUCCESS(status)) { findStatus = FindCurrentPssPerfState(DeviceExtension->PssPackage, &pssState); if (NT_SUCCESS(findStatus)) { DebugAssert(pssState < DeviceExtension->PssPackage->NumPStates); DeviceExtension->CurrentPerfState = pssState; } } return status; } 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: State - Index into _PSS object Return Value: none --*/ { NTSTATUS status = STATUS_SUCCESS; ULONG transitionStatus; ULONG pssControl; ULONG64 fidControl; ULONG64 vidControl; ULONG64 fidVidStatus; DebugEnter(); DebugAssert(DeviceExtension->PssPackage) DebugAssert(NewState >= DeviceExtension->PpcResult); DebugAssert(NewState < DeviceExtension->PssPackage->NumPStates); DebugAssert(DeviceExtension->PssPackage->State[NewState].Control); 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; } // // Transitioning the Mobile Athlon Processor requires two steps: // // If transitioning from a higher state to a lower state, then: // 1) core frequency transition // 2) core voltage transition // // If transitioning from a lower state to a higher state, then: // 1) core voltage transition // 2) core frequency transition // // // Get Frequency and Voltage value to write to MSR // pssControl = DeviceExtension->PssPackage->State[NewState].Control; fidControl = ConvertPssControlToFidVidControl(pssControl, TRUE); vidControl = ConvertPssControlToFidVidControl(pssControl, FALSE); if (NewState > DeviceExtension->CurrentPssState) { // // High -> Low // DebugPrint((TRACE, "Perf Transition: state %u -> state %u\n", DeviceExtension->CurrentPssState, NewState)); DebugPrint((TRACE, "FidControl = 0x%I64x, VidControl = 0x%I64x\n", fidControl, vidControl)); AmdK7FidVidTransition(fidControl); AmdK7FidVidTransition(vidControl); } else { // // Low -> High // DebugPrint((TRACE, "Perf Transition: state %u -> state %u\n", DeviceExtension->CurrentPssState, NewState)); DebugPrint((TRACE, "VidControl = 0x%I64x, FidControl = 0x%I64x\n", vidControl, fidControl)); AmdK7FidVidTransition(vidControl); AmdK7FidVidTransition(fidControl); } // // check status // fidVidStatus = ReadMSR(AMDK7_FID_VID_STATUS_MSR); transitionStatus = ConvertFidVidStatusToPssStatus(fidVidStatus); if (transitionStatus == DeviceExtension->PssPackage->State[NewState].Status) { DeviceExtension->CurrentPssState = NewState; } else { DebugPrint((ERROR, "ERROR! Expected 0x%x status value, recieved 0x%x\n", DeviceExtension->PssPackage->State[NewState].Status, transitionStatus)); DebugPrint((ERROR, "_PSS[%u]->Status:\n", NewState)); DumpPssStatus(DeviceExtension->PssPackage->State[NewState].Status); DebugPrint((ERROR, "FidVidStatus MSR:\n", fidVidStatus)); DumpFidVidStatus(fidVidStatus); status = STATUS_UNSUCCESSFUL; } 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 previous state, transition to lowest non-throttled perf state // return SaveCurrentStateGoToLowVolts(DeviceExtension); } ULONG64 ConvertPssControlToFidVidControl( ULONG PssControlValue, BOOLEAN Fid ) /*++ Routine Description: Arguments: Return Value: NOTES: _PSS Control field: Bit Name ---- ---- 4:0 FID 9:5 VID 29:10 SGTC 31:30 Reserved FidVidCtl: Bit Name Function ---- ---- -------- 4:0 FID[4:0] New 5-bit FID to transition to 7:5 reserved 12:8 VID[4:0] New 5-bit VID to transition to 15:13 reserved 16 FIDC FID Control bit 17 VIDC VID Control bit 19:18 reserved 20 FidChgRatio Fid Change Ratio (always set to 1) 31:21 reserved 51:32 SGTC[19:0] Stop-Grant Timeout Count (write only) 63:52 reserved --*/ { FID_VID_CONTROL fidVidControl = {0}; PSS_CONTROL pssControl; //DebugEnter(); DebugAssert(PssControlValue); pssControl.AsDWord = PssControlValue; fidVidControl.Fid = pssControl.Fid; fidVidControl.Vid = pssControl.Vid; fidVidControl.FidChngRatio = 1; // // The timeout needed for the Frequency Transion (FID) is exactly half that // needed by the Voltage Transion (VID). We do this because of the latency // savings // if (Fid) { fidVidControl.SGTC = pssControl.SGTC / 2; fidVidControl.FidControl = 1; } else { fidVidControl.SGTC = pssControl.SGTC; fidVidControl.VidControl = 1; } return fidVidControl.AsQWord; } ULONG ConvertFidVidStatusToPssStatus( ULONG64 FidVidStatusValue ) /*++ Routine Description: Arguments: Return Value: NOTES: FidVidStatus: Bit Name Function ---- ---- -------- 4:0 CFID[4:0] Current 5-bit FID 7:5 reserved 12:8 SFID[4:0] Startup 5-bit FID 15:13 reserved 20:16 MFID[4:0] Maximum 5-bit FID 31:21 reserved 36:32 CVID[4:0] Current 5-bit VID 39:37 reserved 44:40 SVID[4:0] Startup 5-bit VID 47:45 reserved 52:48 MVID[4:0] Maximum 5-bit VID 63:53 reserved _PSS Status Field: Bit Name ---- ---- 4:0 FID current frequency 9:5 VID current voltage 31:10 reserved --*/ { PSS_STATUS pssStatus; FID_VID_STATUS fidVidStatus; //DebugEnter(); DebugAssert(FidVidStatusValue); fidVidStatus.AsQWord = FidVidStatusValue; pssStatus.Fid = fidVidStatus.CFid; pssStatus.Vid = fidVidStatus.CVid; pssStatus.Reserved = 0; return pssStatus.AsDWord; } NTSTATUS FindCurrentPssPerfState( PACPI_PSS_PACKAGE PssPackage, PULONG PssState ) /*++ Routine Description: Arguments: Return Value: --*/ { NTSTATUS status = STATUS_UNSUCCESSFUL; ULONG x; ULONG transitionStatus; ULONG64 fidVidStatus; DebugEnter(); DebugAssert(PssPackage); DebugAssert(PssState); fidVidStatus = ReadMSR(AMDK7_FID_VID_STATUS_MSR); transitionStatus = ConvertFidVidStatusToPssStatus(fidVidStatus); for (x=0; x < PssPackage->NumPStates; x++) { if (transitionStatus == PssPackage->State[x].Status) { status = STATUS_SUCCESS; *PssState = x; break; } } DebugExitStatus(status); return status; } #if DBG VOID DumpFidVidStatus( IN ULONG64 FidVidStatus ) { FID_VID_STATUS status; status.AsQWord = FidVidStatus; DebugPrint((MAXTRACE, " CFid: 0x%x\n", status.CFid)); DebugPrint((MAXTRACE, " Reserved: 0x%x\n", status.Reserved1)); DebugPrint((MAXTRACE, " SFid: 0x%x\n", status.SFid)); DebugPrint((MAXTRACE, " Reserved: 0x%x\n", status.Reserved2)); DebugPrint((MAXTRACE, " MFid: 0x%x\n", status.MFid)); DebugPrint((MAXTRACE, " Reserved: 0x%x\n", status.Reserved3)); DebugPrint((MAXTRACE, " CVid: 0x%x\n", status.CVid)); DebugPrint((MAXTRACE, " Reserved: 0x%x\n", status.Reserved4)); DebugPrint((MAXTRACE, " SVid: 0x%x\n", status.SVid)); DebugPrint((MAXTRACE, " Reserved: 0x%x\n", status.Reserved5)); DebugPrint((MAXTRACE, " MVid: 0x%x\n", status.MVid)); DebugPrint((MAXTRACE, " Reserved: 0x%x\n", status.Reserved6)); DebugPrint((MAXTRACE, "\n")); } VOID DumpFidVidControl( IN ULONG64 FidVidControl ) { FID_VID_CONTROL control; control.AsQWord = FidVidControl; DebugPrint((MAXTRACE, " Fid: 0x%x\n", control.Fid)); DebugPrint((MAXTRACE, " Reserved: 0x%x\n", control.Reserved1)); DebugPrint((MAXTRACE, " Vid: 0x%x\n", control.Vid)); DebugPrint((MAXTRACE, " Reserved: 0x%x\n", control.Reserved2)); DebugPrint((MAXTRACE, " FidControl: 0x%x\n", control.FidControl)); DebugPrint((MAXTRACE, " VidControl: 0x%x\n", control.VidControl)); DebugPrint((MAXTRACE, " Reserved: 0x%x\n", control.Reserved3)); DebugPrint((MAXTRACE, " FidChngRatio: 0x%x\n", control.FidChngRatio)); DebugPrint((MAXTRACE, " Reserved: 0x%x\n", control.Reserved4)); DebugPrint((MAXTRACE, " SGTC: 0x%x\n", control.SGTC)); DebugPrint((MAXTRACE, " Reserved: 0x%x\n", control.Reserved5)); DebugPrint((MAXTRACE, "\n")); } VOID DumpPssStatus( IN ULONG PssStatus ) { PSS_STATUS status; status.AsDWord = PssStatus; DebugPrint((MAXTRACE, " Fid: 0x%x\n", status.Fid)); DebugPrint((MAXTRACE, " Vid: 0x%x\n", status.Vid)); DebugPrint((MAXTRACE, " Reserved: 0x%x\n", status.Reserved)); DebugPrint((MAXTRACE, "\n")); } VOID DumpPssControl( IN ULONG PssControl ) { PSS_CONTROL control; control.AsDWord = PssControl; DebugPrint((MAXTRACE, " Fid: 0x%x\n", control.Fid)); DebugPrint((MAXTRACE, " Vid: 0x%x\n", control.Vid)); DebugPrint((MAXTRACE, " SGTC: 0x%x\n", control.SGTC)); DebugPrint((MAXTRACE, " Reserved: 0x%x\n", control.Reserved)); DebugPrint((MAXTRACE, "\n")); } #endif