windows-nt/Source/XPSP1/NT/base/hals/processor/viac3/legacy.c

1223 lines
37 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 2001 Microsoft Corporation
Module Name:
legacy.c
Abstract:
This module implements code that works on Cyrix processors with LongHaul power management support.
Author:
Tom Brown (t-tbrown) 11-Jun-2001
Environment:
Kernel mode
Revision History:
--*/
#include <ntddk.h>
#include "..\lib\processor.h"
#include "legacy.h"
#include "viac3.h"
extern PFDO_DATA DeviceExtensions[MAX_SUPPORTED_PROCESSORS];
extern UCHAR DevExtIndex;
extern GLOBALS Globals;
// set in IdentifyCPUVersion according to data in
// "Samuel 2, Ezra, and C5M LongHaul Programmer's Guide (Version 3.0.0 Gamma)"
ULONG LongHaulFlags; // Encoding of the LongHaul revision set by IdentifyCPUVersion. Use it with the following macros
ULONG FsbFreq; // (MHz) Front Side Bus frequency
#define POWER_V 1200 // Power is measured in mW, but this is a hack value used for states that have an unknown mV
#define ABSOLUTE_MIN_FREQ 300
#define DEFAULT_LATENCY_US 500000 // Set default latency to 500ms in microseconds, because transitions suck (take a long time, occupy whole proc)
ULONG NextTransitionThrottle; // (%) Next state (as percentage of total speed) that a worker thread will transition to, or INVALID_THROTTLE if no worker thread is queued
#define RESV (-1) // A reserved value in the tables
#define BR_MULT 2 // Used to avoid putting floats in the BR tables
// divide by 2 is optimised to a shift by the compiler
#define LH_SOFTBR_MIN 1 // index of lowest value in LH_SOFTBR
const ULONG LH_SOFTBR [32] = { // Indexed by bits written to softBR(14,19:16) to the BR that the bits represent
20, 6, 8, 18, // Actual bus ratio = LH_SOFTBR[i] / BR_MULT
19, 7, 9, 11,
12, 14, 16, 10,
13, 15, 17, 24,
RESV, 22, 24, RESV,
21, 23, 25, 27,
28, 30, 32, 26,
29, 31, RESV, RESV
};
const ULONG LH_SOFTBR_SORT [32] = { // Indexes of LH_SOFTBR, sorted decreasing by value
26, 29, 25, 28, // BRs are put into the pss in decreasing order
24, 23, 27, 22,
18, 21, 17, 20, // LH_SOFTBR[15] is never used because there are two entries for 12.0X(24)
0, 4, 3, 14,
10, 13, 9, 12,
8, 7, 11, 6,
2, 5, 1, RESV,
RESV, RESV, RESV, RESV
};
#define LH_SOFTBR_SIZE (sizeof(LH_SOFTBR) / sizeof(ULONG))
ULONG const* LhSoftVid; // Set to LH_SOFTVID_VRM85 or LH_SOFTVID_MOBILE
ULONG const* LhSoftVidSort; // Set to LH_SOFTVID_VRM85_SORT or LH_SOFTVID_MOBILE_SORT
ULONG LhSoftVidSize; // Set to LH_SOFTVID_VRM85_SIZE or LH_SOFTVID_MOBILE_SIZE
const ULONG LH_SOFTVID_VRM85 [32] = { // mVolts with a VRM 8.5
// VRM 8.5, bit15=0
1250, 1200, 1150, 1100,
1050, 1800, 1750, 1700,
1650, 1600, 1550, 1500,
1450, 1400, 1350, 1300,
1275, 1225, 1175, 1125,
1075, 1825, 1775, 1725,
1675, 1625, 1575, 1525,
1475, 1425, 1375, 1325
};
const ULONG LH_SOFTVID_VRM85_SORT [32] = { // Indexes of LH_SOFTVID_VRM85, sorted decreasing by value
21, 5, 22, 6, // voltage levels are used in decreasing order in the pss
23, 7, 24, 8,
25, 9, 26, 10,
27, 11, 28, 12,
29, 13, 30, 14,
31, 15, 16, 0,
17, 1, 18, 2,
19, 3, 20, 4
};
#define LH_SOFTVID_VRM85_SIZE (sizeof(LH_SOFTVID_VRM85) / sizeof(ULONG))
const ULONG LH_SOFTVID_MOBILE [32] = { // mVolts with a Mobile VRM
// Mobile VRM, bit15=1
2000, 1950, 1900, 1850,
1800, 1750, 1700, 1650,
1600, 1550, 1500, 1450,
1400, 1350, 1300, RESV,
1275, 1250, 1225, 1200,
1175, 1150, 1125, 1100,
1075, 1050, 1025, 1000,
975, 950, 925, RESV
};
const ULONG LH_SOFTVID_MOBILE_SORT [32] = { // Indexes of LH_SOFTVID_MOBILE, sorted decreasing by value
0, 1, 2, 3, // voltage levels are used in decreasing order in the pss
4, 5, 6, 7,
8, 9, 10, 11,
12, 13, 14, 16,
17, 18, 19, 20,
21, 22, 23, 24,
25, 26, 27, 28,
29, 30, RESV, RESV
};
#define LH_SOFTVID_MOBILE_SIZE (sizeof(LH_SOFTVID_MOBILE) / sizeof(ULONG))
// Indexed by bits MSR[43,35:32] to the BR*BR_MULT they represent
const ULONG LH_MAX_BR [32] = {
// bit 43=0
10, 6, 8, 20, // DIVIDE these values by BR_MULT to get actual BR!
11, 7, 9, 19,
18, 14, 16, 12,
24, 15, 17, 13,
// bit 43=1
RESV, 22, 24, RESV,
27, 23, 25, 21,
26, 30, 32, 28,
RESV, 31, RESV, 29
};
#define LH_MAX_BR_SIZE (sizeof(LH_MAX_BR) / sizeof(ULONG))
// Indexed by bits MSR[59,51:48] to the BR*BR_MULT they represent
const ULONG LH_MIN_BR [32] = {
// bit 59=0
10, 6, 8, 20, // DIVIDE these values by BR_MULT to get actual BR!
11, 7, 9, 19,
18, 14, 16, 12,
24, 15, 17, 13,
// bit 59=1
RESV, RESV, RESV, RESV,
RESV, RESV, RESV, RESV,
RESV, RESV, RESV, RESV,
RESV, RESV, RESV, RESV
};
#define LH_MIN_BR_SIZE (sizeof(LH_MIN_BR) / sizeof(ULONG))
const ULONG LH_FSB [4] = { // (MHz) Frequency of FSB
133,
100,
RESV,
66
};
VOID
InitializeCPU()
/*++
Routine Description:
Optionally initializes the softVID value in the MSR to MaximumVID.
Arguments:
None
Return Value:
None
--*/
{
LONGHAUL_MSR longhaul_msr;
// Need to initilize the MSR since it boots with an undetermined value.
// The transition routine needs the value to be correct since it may need
// to avoid taking voltage steps larger than MAX_V_STEP mV.
if( SUPPORTS_SOFTVID && SET_MAXV_AT_STARTUP ) {
longhaul_msr.AsQWord = ReadMSR(MSR_LONGHAUL_ADDR);
ExSetTimerResolution (10032,TRUE);
TransitionNow( RESV, FALSE, longhaul_msr.MaximumVID, TRUE );
ExSetTimerResolution (0, FALSE);
}
}
VOID
IdentifyLongHaulParameters()
/*++
Routine Description:
Read LondHaulMSR RevisionID. Make sure this software supports the revision
and set the flags.
Sets LhSoftVid, LhSoftVidSort, LhSoftVidSize to the correct constant.
Arguments:
None
Return Value:
None
--*/
{
LONGHAUL_MSR longhaul_msr;
ULONG freq;
ACPI_PSS_DESCRIPTOR pss;
PSS_CONTROL control;
longhaul_msr.AsQWord = ReadMSR(MSR_LONGHAUL_ADDR);
if( longhaul_msr.RevisionID == 0 ) {
LongHaulFlags |= SUPPORTS_MSR_FLAG | SUPPORTS_SOFTBR_FLAG;
} else if( longhaul_msr.RevisionID == 1 ) {
LongHaulFlags |= SUPPORTS_MSR_FLAG | SUPPORTS_SOFTBR_FLAG | SUPPORTS_SOFTVID_FLAG | NEEDS_VOLTAGE_STEPPING_FLAG | SET_MAXV_AT_STARTUP_FLAG;
} else if( longhaul_msr.RevisionID == 2 ) {
LongHaulFlags |= SUPPORTS_MSR_FLAG | SUPPORTS_SOFTBR_FLAG | SUPPORTS_SOFTVID_FLAG | NEEDS_VOLTAGE_STEPPING_FLAG | SET_MAXV_AT_STARTUP_FLAG;
} else {
DebugPrint((WARN, "Unknown LongHaul revision %u.\n", longhaul_msr.RevisionID));
return;
}
if( SUPPORTS_SOFTVID ) {
if( longhaul_msr.VRMRev == VRM85 ) {
LhSoftVid = LH_SOFTVID_VRM85;
LhSoftVidSort = LH_SOFTVID_VRM85_SORT;
LhSoftVidSize = LH_SOFTVID_VRM85_SIZE;
} else {
// Mobile VRM
LhSoftVid = LH_SOFTVID_MOBILE;
LhSoftVidSort = LH_SOFTVID_MOBILE_SORT;
LhSoftVidSize = LH_SOFTVID_MOBILE_SIZE;
}
}
}
VOID
IdentifyCPUVersion()
/*++
Routine Description:
Set the LongHaul flags according to what is known about the hardware. Expects
flags to be cleared when called.
Arguments:
None
Return Value:
None
--*/
{
ULONG eax, ebx, ecx, edx;
CPUID_FUNC1 cpuid_result;
// Check CPUID Function 0 returns vendor string EBX:EDX:ECX = "CentaurHauls"
CPUID(0x0, &eax, &ebx, &ecx, &edx);
if ( ebx != CPUID_FUNC0_EBX || edx != CPUID_FUNC0_EDX || ecx != CPUID_FUNC0_ECX ) {
DebugPrint((WARN, "Invalid result from CPUID Function 0.\n"));
return;
}
// Get Family/Model/Stepping from CPUID Function 1
CPUID(0x1, &cpuid_result.AsDWord, &ebx, &ecx, &edx);
if (cpuid_result.Family == 6 ) {
if ( cpuid_result.Model == 6 ) {
DebugPrint((TRACE, "Found VIA C3 Samuel 1 (C5A).\n" ));
return;
}
if ( cpuid_result.Model == 7 ) {
if( cpuid_result.Stepping==0) {
DebugPrint((TRACE, "Found VIA C3 Samuel 2 (C5B), stepping 0.\n" ));
return;
} else if ( cpuid_result.Stepping <= 7 ) {
DebugPrint((TRACE, "Found VIA C3 Samuel 2 (C5B), stepping 1-7.\n" ));
IdentifyLongHaulParameters();
return;
} else {
DebugPrint((TRACE, "Found VIA C3 Ezra (C5C).\n" ));
IdentifyLongHaulParameters();
return;
}
}
if ( cpuid_result.Model == 8 ) {
DebugPrint((TRACE, "Found VIA C3 Ezra-T (C5M).\n" ));
IdentifyLongHaulParameters();
return;
}
}
DebugPrint((WARN, "Unknown CPU family/model/stepping %u/%u/%u is not supported.\n",
cpuid_result.Family,
cpuid_result.Model,
cpuid_result.Stepping
));
return;
}
#ifdef DBG
VOID
DebugShowPossibleLongHaulMSR()
/*++
Routine Description:
Debug spew the contents of the LongHaul MSR.
Arguments:
None
Return Value:
None
--*/
{
LONGHAUL_MSR longhaul_msr;
DebugAssert( SUPPORTS_MSR );
longhaul_msr.AsQWord = ReadMSR(MSR_LONGHAUL_ADDR);
DebugPrint((TRACE," RevisionID: %i\n",longhaul_msr.RevisionID));
DebugPrint((TRACE," RevisionKey: %i\n", longhaul_msr.RevisionKey));
DebugPrint((TRACE," EnableSoftBusRatio: %i\n", longhaul_msr.EnableSoftBusRatio));
DebugPrint((TRACE," EnableSoftVID: %i\n", longhaul_msr.EnableSoftVID));
DebugPrint((TRACE," EnableSoftBSEL: %i\n", longhaul_msr.EnableSoftBSEL));
DebugPrint((TRACE," Reserved1: %i\n", longhaul_msr.Reserved1));
DebugPrint((TRACE," Reserved2: %i\n", longhaul_msr.Reserved2));
DebugPrint((TRACE," Reserved3: %i\n", longhaul_msr.Reserved3));
DebugPrint((TRACE," VRMRev: %i\n", longhaul_msr.VRMRev));
DebugPrint((TRACE," SoftBusRatio: %i\n", longhaul_msr.SoftBusRatio4<<4 | longhaul_msr.SoftBusRatio0));
DebugPrint((TRACE," SoftVID: %i\n", longhaul_msr.SoftVID));
DebugPrint((TRACE," Reserved4: %i\n", longhaul_msr.Reserved4));
DebugPrint((TRACE," SoftBSEL: %i\n", longhaul_msr.SoftBSEL));
DebugPrint((TRACE," Reserved5: %i\n", longhaul_msr.Reserved5));
DebugPrint((TRACE," MaxMHzBR: %i\n", longhaul_msr.MaxMHzBR4<<4 | longhaul_msr.MaxMHzBR0));
DebugPrint((TRACE," MaximumVID: %i\n", longhaul_msr.MaximumVID));
DebugPrint((TRACE," MaxMHzFSB: %i\n", longhaul_msr.MaxMHzFSB));
DebugPrint((TRACE," Reserved6: %i\n", longhaul_msr.Reserved6));
DebugPrint((TRACE," MinMHzBR: %i\n", longhaul_msr.MinMHzBR4<<4 | longhaul_msr.MinMHzBR0));
DebugPrint((TRACE," MinimumVID: %i\n", longhaul_msr.MinimumVID));
DebugPrint((TRACE," MinMHzFSB: %i\n", longhaul_msr.MinMHzFSB));
DebugPrint((TRACE," Reserved7: %i\n", longhaul_msr.Reserved7));
}
VOID
DebugShowCurrent()
/*++
Routine Description:
Show some information about the current state of the CPU.
Arguments:
None
Return Value:
None
--*/
{
ULONG freq;
DebugPrint((TRACE,"Reading current CPU power state\n"));
CalculateCpuFrequency( &freq );
DebugPrint((TRACE," running at %iMhz\n", freq));
if( SUPPORTS_MSR ) {
DebugShowPossibleLongHaulMSR();
} else {
DebugPrint((ERROR," Unknown CPU.\n"));
}
}
#endif // DBG
VOID
TransitionRoutine (
IN PDEVICE_OBJECT DeviceObject,
IN PVOID Context
)
/*++
Routine Description:
Run by a system worker thread as a work item.
Arguments:
DeviceObject - Device object
Context - Pointer to ACPI_PSS_DESCRIPTOR for the state to transition to
Return Value:
None
--*/
{
ULONG Throttle;
ULONG OldThrottle;
ULONG newState;
PFDO_DATA DeviceExtension;
DebugEnter();
DeviceExtension = (PFDO_DATA) DeviceObject->DeviceExtension;
do {
// Get the next state
Throttle = NextTransitionThrottle;
DebugAssert( INVALID_PERF_STATE > 100 ); // Check
// Run through the performance states looking for one
// that matches this throttling level.
for (newState = 0; newState < DeviceExtension->PerfStates->Count; newState++) {
if (DeviceExtension->PerfStates->State[newState].PercentFrequency <= Throttle) {
DebugPrint((TRACE, "TransitionRoutine found match. PerfState = %u, Freq %u%%\n",
newState,
DeviceExtension->PerfStates->State[newState].PercentFrequency));
break;
}
}
if( newState >= DeviceExtension->PerfStates->Count ) {
DebugAssert( !"Invalid throttle or perf state not found" );
return;
}
// Convert index in PerfStates to index in PssPackage
newState = newState + DeviceExtension->PpcResult;
TransitionToState( &(DeviceExtension->PssPackage->State[newState]) );
// If NextTransitionThrottle is still Throttle then set
// NextTransitionThrottle to INVALID_PERF_STATE and exit.
// If NextTransitionThrottle is a different value from the top
// of this do...while then loop to perform transition to that throttle.
OldThrottle = InterlockedCompareExchange(
&NextTransitionThrottle, // pointer to data that will be read, compared, and optionly changed
INVALID_PERF_STATE, // data that will be written if NextTransitionThrottle=Throttle
Throttle ); // Throttle we just transitioned to. Check if NextTransitionThrottle still has this value
DebugAssert( OldThrottle!=INVALID_PERF_STATE ); // OldThrottle could only be INVALID_PERF_STATE here if another thread in TransitionRoutine had cleared it, QueueTransition is designied to prevent that happening.
} while( OldThrottle!=Throttle );
IoFreeWorkItem((PIO_WORKITEM)Context);
DebugExit();
}
NTSTATUS
QueueTransition(
IN PFDO_DATA DeviceExtension,
IN ULONG NewState
)
/*++
Routine Description:
Setup a transition to be executed by a system worker thread and return.
Needed because the transition code must be run at IRQL < DISPATCH_LEVEL.
Creates a new queued work item if NextTransitionThrottle was INVALID_PERF_STATE.
Arguments:
DeviceExtension - Device object
NewState - Number of the state to transition to in the PerfStates table
Return Value:
NTSTATUS - success if transition was successfully completed
--*/
{
PIO_WORKITEM pWorkItem;
ULONG old_next_transition;
DebugAssert( NewState < DeviceExtension->PerfStates->Count );
old_next_transition = InterlockedExchange(
&NextTransitionThrottle,
DeviceExtension->PerfStates->State[NewState].PercentFrequency );
if ( old_next_transition == INVALID_THROTTLE ) {
DebugPrint((TRACE,"No worker thread item was in the queue. Making new work item to transition to %i%%.\n",
DeviceExtension->PerfStates->State[NewState].PercentFrequency));
pWorkItem = IoAllocateWorkItem( DeviceExtension->Self );
IoQueueWorkItem(
pWorkItem,
TransitionRoutine,
DelayedWorkQueue,
pWorkItem
);
} else {
DebugPrint((TRACE,"Worker thread item was going to transition to %i%%. Now going to %i%%.\n",
old_next_transition, DeviceExtension->PerfStates->State[NewState].PercentFrequency));
}
return STATUS_SUCCESS;
}
ULONG
CalcNextVoltageStep(
IN ULONG VidFinal,
IN ULONG VidCurrent
)
/*++
Routine Description:
Calculate the next safe vid. Part of a hack that only changes voltage in
MAX_V_STEP (mV) steps.
Arguments:
vidFinal - Final goal vid(index into LhSoftVid)
vidCurrent - Current vid
Return Value:
Next vid which the CPU can change to.
--*/
{
ULONG pos; // position in the LhSoftVidSort table
if( (!NEEDS_VOLTAGE_STEPPING) || VidFinal==VidCurrent || LhSoftVid[VidFinal]==LhSoftVid[VidCurrent]) {
return VidFinal;
}
// Search for vidCurrent in LhSoftVidSort
pos = 0;
while( pos<LhSoftVidSize && LhSoftVidSort[pos]!=VidCurrent ) {
++pos;
}
DebugAssert( LhSoftVidSort[pos]==VidCurrent ); // Must find the vidCurrent in the LhSoftVidSort
if( LhSoftVid[VidFinal] > LhSoftVid[VidCurrent] ) {
// need to move pos backwards in LhSoftVidSort
DebugAssert( pos != 0 ); // can't be first in table unless it is highest
do {
--pos;
if( pos == 0 ) {
break;
}
} while( LhSoftVidSort[pos] != VidFinal
&& LhSoftVid[LhSoftVidSort[pos]]-LhSoftVid[VidCurrent] < MAX_V_STEP );
DebugAssert( LhSoftVid[LhSoftVidSort[pos]]-LhSoftVid[VidCurrent] <= MAX_V_STEP );
} else{
// Need to move pos forwards in LhSoftVidSort
DebugAssert( LhSoftVid[VidFinal] < LhSoftVid[VidCurrent] );
DebugAssert( pos+1 < LhSoftVidSize );
do {
++pos;
} while( pos+1<LhSoftVidSize && LhSoftVidSort[pos] != VidFinal
&& LhSoftVid[VidCurrent]-LhSoftVid[LhSoftVidSort[pos]] < MAX_V_STEP );
DebugAssert( LhSoftVid[VidCurrent]-LhSoftVid[LhSoftVidSort[pos]] <= MAX_V_STEP );
}
return LhSoftVidSort[pos];
}
NTSTATUS
TransitionNow(
IN ULONG Fid,
IN BOOLEAN EnableFid,
IN ULONG Vid,
IN BOOLEAN EnableVid
)
/*++
Routine Description:
Set the clock to 1ms BEFORE calling TransitionNow.
Perform the state transition now and return success or not.
Arguments:
fid - fid(BR) to transition to
enableFid - should enable fid(softBR) transition
vid - vid to transition to
enableVid - should enable vid(softVID) transition
Return Value:
NTSTATUS - success if transition was successfully completed
--*/
{
LONGHAUL_MSR msr;
KIRQL irql;
DebugAssert( SUPPORTS_MSR );
msr.AsQWord = ReadMSR(MSR_LONGHAUL_ADDR);
msr.RevisionKey = msr.RevisionID; // Need to copy to RevisionKey for each write
if( EnableFid ) {
msr.EnableSoftBusRatio = 1;
msr.SoftBusRatio0 = Fid & 0x0F; // Write the 4 LS bits of the BR
msr.SoftBusRatio4 = Fid >> 4; // Write the MS bit of the BR
DebugPrint((TRACE,"changing to fid=%i(%i/%i)\n",
Fid, LH_SOFTBR[Fid], BR_MULT ));
}
if( EnableVid ) {
DebugAssert( SUPPORTS_SOFTVID );
msr.EnableSoftVID = 1;
msr.SoftVID = Vid;
DebugPrint((TRACE,"changing to vid=%i(%imV)\n",
Vid, LhSoftVid[Vid] ));
}
// Raise the IRQL to mask off all interrupts except the timer
KeRaiseIrql(CLOCK1_LEVEL-1, &irql);
DebugPrint((TRACE,"Raised IRQL from %i to %i\n", irql, KeGetCurrentIrql()));
WriteMSR(MSR_LONGHAUL_ADDR, msr.AsQWord);
// Halt twice to guarantee that second halt lasts a full 1 ms before the timer interrupt
// Possible optimization!! use a timer to see if the first halt last long enough to meet
// the hardware specs
DebugPrint((TRACE,"Calling halt\n"));
_asm {
hlt
hlt
}
// Clean up as needed
msr.AsQWord = ReadMSR(MSR_LONGHAUL_ADDR);
msr.RevisionKey = msr.RevisionID; // Need to copy to RevisionKey for each write
msr.EnableSoftBusRatio = 0;
msr.EnableSoftVID = 0;
WriteMSR(MSR_LONGHAUL_ADDR, msr.AsQWord);
// Bring the IRQL back down to its previous value
KeLowerIrql(irql);
DebugPrint((TRACE,"Restored IRQL. Now %i\n", KeGetCurrentIrql()));
}
NTSTATUS
TransitionToState(
IN PACPI_PSS_DESCRIPTOR Pss
)
/*++
Routine Description:
Perform the state transition now and return success or not.
Arguments:
Pss - pointer to the ACPI_PSS_DESCRIPTOR of the state to transition to
Return Value:
NTSTATUS - success if transition was successfully completed
--*/
{
ULONG set_timer_result;
PSS_CONTROL pssControl;
ULONG vidNext;
LONGHAUL_MSR msr_longhaul;
DebugEnter();
// Copy state control bits
pssControl.AsDWord = Pss->Control;
DebugAssert( KeGetCurrentIrql() < DISPATCH_LEVEL ); // Callers of ExSetTimerResolution must be running at IRQL < DISPATCH_LEVEL
do {
// Change the timer resolution to the smallest safe amount, 1ms
// Since we are at a low irql another thread may have changed it so
// set it each time we call TransitionNow
set_timer_result = ExSetTimerResolution (10032,TRUE); // Set timer to 1ms in 100ns units, fudged to what ExSetTimerResolution normally returns
DebugPrint((TRACE,"Set the timer, returned value %lu\n", set_timer_result));
// Read the current state of the msr
msr_longhaul.AsQWord = ReadMSR(MSR_LONGHAUL_ADDR);
DebugPrint((TRACE,"current vid=%i(%imV) fid=%i(%i)\n",
msr_longhaul.SoftVID, LhSoftVid[msr_longhaul.SoftVID],
(msr_longhaul.SoftBusRatio4<<4) + msr_longhaul.SoftBusRatio0, LH_SOFTBR[msr_longhaul.SoftBusRatio4<<4 | msr_longhaul.SoftBusRatio0] ));
if( pssControl.EnableVid ) {
vidNext = CalcNextVoltageStep(pssControl.Vid,msr_longhaul.SoftVID);
if( LhSoftVid[vidNext] >= LhSoftVid[pssControl.Vid]
&& pssControl.EnableFid ) {
// Can set the freq and voltage
DebugPrint((TRACE," vidNext=%i(%imV) pssControl.Vid=%i(%imV)\n",
vidNext, LhSoftVid[vidNext],
pssControl.Vid, LhSoftVid[pssControl.Vid] ));
TransitionNow( pssControl.Fid, TRUE, vidNext, TRUE );
} else {
// only set the voltage
TransitionNow( RESV, FALSE, vidNext, TRUE );
}
} else {
DebugAssert( pssControl.EnableFid ); // every state should have EnableVid or EnableFid
// No voltage transition
TransitionNow( pssControl.Fid, TRUE, RESV, FALSE );
}
}while( pssControl.EnableVid && pssControl.Vid != vidNext );
// Reset the timer to its default value
set_timer_result = ExSetTimerResolution (0, FALSE);
DebugPrint((TRACE,"Reset the timer, returned value %lu\n", set_timer_result));
DebugExit();
return STATUS_SUCCESS;
}
VOID
MeasureFSBFreq()
/*++
Routine Description:
Measure the frequency the front side bus is running by setting the bus
ratio to a known safe value and timing the core frequency.
Value is put in global FsbFreq.
Arguments:
None
Return Value:
None
--*/
{
PSS_CONTROL control;
ACPI_PSS_DESCRIPTOR pss;
ULONG freq;
// Set the BR to a known, safe, constant
control.EnableFid = 1;
control.Fid = LH_SOFTBR_MIN;
control.EnableVid = 0;
control.Vid = 0;
pss.Control = control.AsDWord;
TransitionToState(&pss);
// Time the fsb
CalculateCpuFrequency( &freq );
FsbFreq = freq * BR_MULT / LH_SOFTBR[LH_SOFTBR_MIN];
DebugPrint((TRACE, "Set BR to %i(%i.%i), measured CPU at %iMHz, meaning FSB at %iMHz\n",
LH_SOFTBR_MIN,
LH_SOFTBR[LH_SOFTBR_MIN]/BR_MULT, (100/BR_MULT) * (LH_SOFTBR[LH_SOFTBR_MIN]%BR_MULT),
freq,
FsbFreq));
}
ULONG
CalcMaxFreq(
IN ULONG V,
IN ULONG Vmin,
IN ULONG Fmin,
IN ULONG Vmax,
IN ULONG Fmax
)
/*++
Routine Description:
Calculate the maximum operating frequency allowed at a particular voltage
given parameters from the LongHaul MSR.
Picks the max frequency that is below a line formed between two points on
a voltage vs. frequency graph.
The two points are (Vmin,Fmin) and (Vmax,Fmax)
Arguments:
V - The voltage at which you want to know the max freq
Vmin - Min allowed voltage
Fmin - Max allowed frequency
Vmax - Max allowed voltage
Fmax - Min allowed frequency
Return Value:
The maximum operating frequency at a specific voltage.
--*/
{
return ((Fmax-Fmin)*(V-Vmin) + Fmin*(Vmax-Vmin)) / (Vmax-Vmin);
}
NTSTATUS
FindAcpiPerformanceStatesLongHaul(
OUT PACPI_PSS_PACKAGE* Pss
)
/*++
Routine Description:
Build a new pss containing all the available states on this CPU.
Only call if supports_longhaul_msr and supports_softBR.
Function will either succeed and return STATUS_SUCCESS or fail and not
touch pPss.
Arguments:
Pss - Reference to a pointer to a pss. The pointer must point to NULL when this function is called.
Return Value:
NT status code
--*/
{
LONGHAUL_MSR longhaul_msr;
PACPI_PSS_PACKAGE tmpPss;
ULONG iPssSize;
ULONG vidStart; // index in LhSoftVidSort of the greatest possible vid
ULONG vidEnd; // index+1 in LhSoftVidSort of the smallest possible vid
ULONG fidStart; // index in LH_SOFTBR_SORT of the greatest possible fid
ULONG fidEnd; // index+1 in LH_SOFTBR_SORT of the smallest possible fid
ULONG vid;
ULONG fid;
ULONG freq; // temp var used for frequencies
ULONG numPStates;
ULONG pssState;
DebugAssert( Pss );
DebugAssert( *Pss == NULL );
DebugAssert( SUPPORTS_MSR && SUPPORTS_SOFTBR ); // Must support the LongHaul MSR and softBR
MeasureFSBFreq();
longhaul_msr.AsQWord= ReadMSR(MSR_LONGHAUL_ADDR);
if( SUPPORTS_SOFTVID ) {
// Search for the greatest vid
vidStart = 0;
while( vidStart < LhSoftVidSize
&& LhSoftVidSort[vidStart] != longhaul_msr.MaximumVID ) {
++vidStart;
}
DebugAssert( vidStart < LhSoftVidSize );
// search for the smallest vid, which should come after vidStart
// set vidEnd to one more than that
vidEnd = vidStart;
while( vidEnd < LhSoftVidSize
&& LhSoftVidSort[vidEnd] != longhaul_msr.MinimumVID ) {
++vidEnd;
}
DebugAssert( vidEnd < LhSoftVidSize );
++vidEnd;
// Find the max freq at min volts in LH_SOFTBR_SORT
freq = LH_MIN_BR[longhaul_msr.MinMHzBR4<<4 | longhaul_msr.MinMHzBR0] * LH_FSB[longhaul_msr.MinMHzFSB]; // freq is MHz*BR_MULT
fidStart = 0;
while( fidStart < LH_SOFTBR_SIZE
&& LH_SOFTBR_SORT[fidStart] != RESV
&& LH_SOFTBR[LH_SOFTBR_SORT[fidStart]] > (freq / FsbFreq) ) {
++fidStart;
}
if( fidStart == LH_SOFTBR_SIZE || LH_SOFTBR_SORT[fidStart] == RESV) {
DebugAssert( fidStart > 0 );
--fidStart;
}
DebugPrint((TRACE, " vidStart: %u(%u,%u) vidEnd-1: %u(%u,%u)\n",
vidStart, LhSoftVidSort[vidStart], LhSoftVid[LhSoftVidSort[vidStart]],
vidEnd-1, LhSoftVidSort[vidEnd-1], LhSoftVid[LhSoftVidSort[vidEnd-1]] ));
} else {
// no vids
vidStart = vidEnd = 0;
// Find top fid, the max freq
freq = LH_MAX_BR[longhaul_msr.MaxMHzBR4<<4 | longhaul_msr.MaxMHzBR0] * LH_FSB[longhaul_msr.MaxMHzFSB];
fidStart = 0;
while( fidStart < LH_SOFTBR_SIZE
&& LH_SOFTBR_SORT[fidStart] != RESV
&& LH_SOFTBR[LH_SOFTBR_SORT[fidStart]] > freq/FsbFreq ) {
++fidStart;
}
if( fidStart == LH_SOFTBR_SIZE || LH_SOFTBR_SORT[fidStart] == RESV) {
DebugAssert( fidStart > 0 );
--fidStart;
}
}
fidEnd = fidStart;
// Set fidEnd to index+1 in the LH_SOFTBR_SORT table of the lowest possible
// BR at the lowest possible voltage
while( fidEnd < LH_SOFTBR_SIZE
&& LH_SOFTBR_SORT[fidEnd] != RESV
&& LH_SOFTBR[LH_SOFTBR_SORT[fidEnd]] >= ABSOLUTE_MIN_FREQ/FsbFreq*BR_MULT) {
++fidEnd;
}
DebugAssert( fidStart <= fidEnd ); // Last fid to go in PSS must be slower than first fid, or they are the same for no freq only states
DebugAssert( LH_SOFTBR_SORT[fidEnd-1]!=RESV || fidStart==fidEnd);
DebugAssert( LH_SOFTBR[LH_SOFTBR_SORT[fidEnd-1]]>=(ABSOLUTE_MIN_FREQ/FsbFreq*BR_MULT) || fidStart==fidEnd);
DebugPrint((TRACE, " fidStart: %u(%u,%u) fidEnd-1: %u(%u,%u)\n",
fidStart, LH_SOFTBR_SORT[fidStart], LH_SOFTBR[LH_SOFTBR_SORT[fidStart]],
fidEnd-1, LH_SOFTBR_SORT[fidEnd-1], LH_SOFTBR[LH_SOFTBR_SORT[fidEnd-1]] ));
numPStates = fidEnd - fidStart;
if( vidStart != vidEnd ) {
// Have some vid states, add them without double counting the state that is both a vid and fid
numPStates += vidEnd - vidStart - 1;
}
iPssSize = (sizeof(ACPI_PSS_DESCRIPTOR) * (numPStates - 1) ) +
sizeof(ACPI_PSS_PACKAGE);
tmpPss = ExAllocatePoolWithTag(NonPagedPool,
iPssSize,
PROCESSOR_POOL_TAG);
if( ! tmpPss ) {
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlZeroMemory(tmpPss, iPssSize);
tmpPss->NumPStates = (UCHAR) numPStates;
pssState = 0;
if( SUPPORTS_SOFTVID ) {
// Add the voltage states
DebugAssert( vidStart != vidEnd ); // must have some states
for( vid = vidStart; vid+1 < vidEnd; ++vid ) { // Last vid state is top fid state
PSS_CONTROL pssControl = {0};
PSS_STATUS pssStatus = {0};
freq = CalcMaxFreq(
LhSoftVid[LhSoftVidSort[vid]],
LhSoftVid[longhaul_msr.MinimumVID],
LH_MIN_BR[longhaul_msr.MinMHzBR4<<4 | longhaul_msr.MinMHzBR0] * LH_FSB[longhaul_msr.MinMHzFSB],
LhSoftVid[longhaul_msr.MaximumVID],
LH_MAX_BR[longhaul_msr.MaxMHzBR4<<4 | longhaul_msr.MaxMHzBR0] * LH_FSB[longhaul_msr.MaxMHzFSB]
); // freq is in MHz * BR_MULT
DebugPrint((TRACE, "Max freq at %i volts: %i\n",
LhSoftVid[LhSoftVidSort[vid]],
freq/BR_MULT));
fid = 0;
while( fid < LH_SOFTBR_SIZE
&& LH_SOFTBR_SORT[fid] != RESV
&& LH_SOFTBR[LH_SOFTBR_SORT[fid]] > freq/FsbFreq ) {
++fid;
}
DebugAssert( fid < LH_SOFTBR_SIZE );
DebugAssert( LH_SOFTBR[LH_SOFTBR_SORT[fid]] != RESV );
pssStatus.Fid = pssControl.Fid = LH_SOFTBR_SORT[fid];
pssControl.EnableFid = 1;
pssStatus.Vid = pssControl.Vid = LhSoftVidSort[vid];
pssControl.EnableVid = 1;
tmpPss->State[pssState].Control = pssControl.AsDWord;
tmpPss->State[pssState].Status = pssStatus.AsDWord;
tmpPss->State[pssState].CoreFrequency =
FsbFreq * LH_SOFTBR[LH_SOFTBR_SORT[fid]] / BR_MULT;
tmpPss->State[pssState].Power = LhSoftVid[LhSoftVidSort[vid]];
tmpPss->State[pssState].Latency = DEFAULT_LATENCY_US;
DebugPrint((TRACE, "Adding state[%i]: Control:%x Status:%x CoreFreq:%i Power:%i V:%i F:%i/%i\n",
pssState, tmpPss->State[pssState].Control, tmpPss->State[pssState].Status,
tmpPss->State[pssState].CoreFrequency, tmpPss->State[pssState].Power,
LhSoftVid[LhSoftVidSort[vid]], LH_SOFTBR[LH_SOFTBR_SORT[fid]], BR_MULT));
++pssState;
}
DebugAssert(vid == vidEnd-1); // all fid states are at low volts
for( fid = fidStart; fid < fidEnd; ++fid ) {
PSS_CONTROL pssControl = {0};
PSS_STATUS pssStatus = {0};
pssStatus.Fid = pssControl.Fid = LH_SOFTBR_SORT[fid];
pssControl.EnableFid = 1;
pssStatus.Vid = pssControl.Vid = LhSoftVidSort[vidEnd-1];
pssControl.EnableVid = 1;
tmpPss->State[pssState].Control = pssControl.AsDWord;
tmpPss->State[pssState].Status = pssStatus.AsDWord;
tmpPss->State[pssState].CoreFrequency =
FsbFreq * LH_SOFTBR[LH_SOFTBR_SORT[fid]] / BR_MULT;
tmpPss->State[pssState].Power = LhSoftVid[LhSoftVidSort[vid]];
tmpPss->State[pssState].Latency = DEFAULT_LATENCY_US;
DebugPrint((TRACE, "Adding state[%i]: Control:%x Status:%x CoreFreq:%i Power:%i V:%i F:%i/%i\n",
pssState, tmpPss->State[pssState].Control, tmpPss->State[pssState].Status,
tmpPss->State[pssState].CoreFrequency, tmpPss->State[pssState].Power,
LhSoftVid[LhSoftVidSort[vid]], LH_SOFTBR[LH_SOFTBR_SORT[fid]], BR_MULT));
++pssState;
}
} else {
for( fid = fidStart; fid < fidEnd; ++fid ) {
PSS_CONTROL pssControl = {0};
PSS_STATUS pssStatus = {0};
pssStatus.Fid = pssControl.Fid = LH_SOFTBR_SORT[fid];
pssControl.EnableFid = 1;
pssControl.EnableVid = 0;
tmpPss->State[pssState].Control = pssControl.AsDWord;
tmpPss->State[pssState].Status = pssStatus.AsDWord;
tmpPss->State[pssState].CoreFrequency =
FsbFreq * LH_SOFTBR[LH_SOFTBR_SORT[fid]] / BR_MULT;
tmpPss->State[pssState].Power = POWER_V;
tmpPss->State[pssState].Latency = DEFAULT_LATENCY_US;
DebugPrint((TRACE, "Adding state[%i]: Control:%x Status:%x CoreFreq:%i Power:%i F:%i/%i\n",
pssState, tmpPss->State[pssState].Control, tmpPss->State[pssState].Status,
tmpPss->State[pssState].CoreFrequency, tmpPss->State[pssState].Power,
LH_SOFTBR[LH_SOFTBR_SORT[fid]], BR_MULT));
++pssState;
}
}
*Pss = tmpPss;
return STATUS_SUCCESS;
}
NTSTATUS
InitializeNonAcpiPerformanceStates(
IN PFDO_DATA DeviceExtension
)
/*++
Routine Description:
Create a PSS table for the legacy CPU states.
Arguments:
DeviceExtension - pointer to the device extension
Return Value:
NTSTATUS - NT status code
--*/
{
NTSTATUS status = STATUS_SUCCESS;
DebugEnter();
PAGED_CODE();
if ( !(SUPPORTS_MSR && SUPPORTS_SOFTBR) ) {
DebugPrint((ERROR,"CPU not supported.\n"));
status = STATUS_NOT_SUPPORTED;
goto InitializeNonAcpiPerformanceStatesExit;
}
DeviceExtension->CurrentPerfState = INVALID_PERF_STATE;
DeviceExtension->LegacyInterface = TRUE;
// Set up _PCT
DeviceExtension->PctPackage.Control.AddressSpaceID = AcpiGenericSpaceFixedFunction;
DeviceExtension->PctPackage.Status.AddressSpaceID = AcpiGenericSpaceFixedFunction;
status = FindAcpiPerformanceStatesLongHaul(&(DeviceExtension->PssPackage));
if (!NT_SUCCESS(status)) {
goto InitializeNonAcpiPerformanceStatesExit;
}
// Need to merge this new data with our perfstates
status = MergePerformanceStates(DeviceExtension);
InitializeNonAcpiPerformanceStatesExit:
if (!NT_SUCCESS(status)) {
if (DeviceExtension->PssPackage) {
// Need to undo what has been done
ExFreePool(DeviceExtension->PssPackage);
DeviceExtension->PssPackage = NULL;
}
DeviceExtension->LegacyInterface = FALSE;
}
DebugExitStatus(status);
return status;
}
NTSTATUS
AcpiLegacyPerfStateTransition(
IN PFDO_DATA DeviceExtension,
IN ULONG State
)
/*++
Routine Description:
Perform a transition using the FFH on LongHaul viac3 chips
Arguments:
DeviceExtension - pointer to the device extension
State - Target State
Return Value:
NT Status
--*/
{
return Acpi2PerfStateTransition(DeviceExtension, State + DeviceExtension->PpcResult);
}
NTSTATUS
GetLegacyMaxProcFrequency(
OUT PULONG CpuSpeed
)
/*++
Routine Description:
Don't use
Arguments:
CpuSpeed - pointer to a ULONG
Return Value:
NTSTATUS - Always returns STATUS_NOT_FOUND
--*/
{
TRAP();
return STATUS_NOT_FOUND;
}