/* $NoKeywords:$ */ /** * @file * * Service procedure to initialize Integrated Info Table * * * * @xrefitem bom "File Content Label" "Release Content" * @e project: AGESA * @e sub-project: GNB * @e \$Revision: 63425 $ @e \$Date: 2011-12-22 11:24:10 -0600 (Thu, 22 Dec 2011) $ * */ /* ***************************************************************************** * * Copyright (c) 2008 - 2012, Advanced Micro Devices, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Advanced Micro Devices, Inc. nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *************************************************************************** * */ /*---------------------------------------------------------------------------------------- * M O D U L E S U S E D *---------------------------------------------------------------------------------------- */ #include "AGESA.h" #include "Ids.h" #include "amdlib.h" #include "heapManager.h" #include "Gnb.h" #include "GnbFuseTable.h" #include "GnbPcie.h" #include "GnbGfx.h" #include "GnbFuseTable.h" #include "GnbGfxFamServices.h" #include "GnbCommonLib.h" #include "GfxPowerPlayTable.h" #include "Filecode.h" #define FILECODE PROC_GNB_MODULES_GNBGFXINITLIBV1_GFXPOWERPLAYTABLE_FILECODE /*---------------------------------------------------------------------------------------- * D E F I N I T I O N S A N D M A C R O S *---------------------------------------------------------------------------------------- */ /*---------------------------------------------------------------------------------------- * T Y P E D E F S A N D S T R U C T U R E S *---------------------------------------------------------------------------------------- */ /// Software state typedef struct { BOOLEAN Valid; ///< State valid UINT16 Classification; ///< State classification UINT32 CapsAndSettings; ///< State capability and settings UINT16 Classification2; ///< State classification2 UINT32 Vclk; ///< UVD VCLK UINT32 Dclk; ///< UVD DCLK UINT8 NumberOfDpmStates; ///< Number of DPM states UINT8 DpmSatesArray[MAX_NUM_OF_DPM_STATES]; ///< DPM state index array } SW_STATE; /// DPM state typedef struct { BOOLEAN Valid; ///< State valid UINT32 Sclk; ///< Sclk in kHz UINT8 Vid; ///< VID index UINT16 Tdp; ///< Tdp limit } DPM_STATE; typedef struct { GFX_PLATFORM_CONFIG *Gfx; ATOM_PPLIB_POWERPLAYTABLE3 *PpTable; PP_FUSE_ARRAY *PpFuses; SW_STATE SwStateArray [MAX_NUM_OF_SW_STATES]; ///< SW state array DPM_STATE DpmStateArray[MAX_NUM_OF_DPM_STATES]; ///< Sclk DPM state array UINT8 NumOfClockVoltageLimitEnties; /// ATOM_PPLIB_VCE_CLOCK_VOLTAGE_LIMIT_RECORD VceClockVoltageLimitArray[MAX_NUM_OF_VCE_CLK_STATES]; UINT8 NumOfVceClockEnties; VCECLOCKINFO VceClockInfoArray[MAX_NUM_OF_VCE_CLK_STATES]; UINT8 NumOfVceStateEntries; ATOM_PPLIB_VCE_STATE_RECORD VceStateArray[MAX_NUM_OF_VCE_STATES]; ///< VCE state array } PP_WORKSPACE; /*---------------------------------------------------------------------------------------- * P R O T O T Y P E S O F L O C A L F U N C T I O N S *---------------------------------------------------------------------------------------- */ VOID GfxIntegratedDebugDumpPpTable ( IN ATOM_PPLIB_POWERPLAYTABLE3 *PpTable, IN GFX_PLATFORM_CONFIG *Gfx ); /*----------------------------------------------------------------------------------------*/ /** * Locate existing tdp * * * @param[in ] PpFuses Pointer to PP_FUSE_ARRAY * @param[in] Sclk Sclk in 10kHz * @param[in] StdHeader Standard configuration header * @retval Tdp limit in DPM state array */ STATIC UINT16 GfxPowerPlayLocateTdp ( IN PP_FUSE_ARRAY *PpFuses, IN UINT32 Sclk, IN AMD_CONFIG_PARAMS *StdHeader ) { UINT8 Index; UINT32 DpmIndex; UINT32 DpmSclk; UINT32 DeltaSclk; UINT32 MinDeltaSclk; DpmIndex = 0; MinDeltaSclk = 0xFFFFFFFF; for (Index = 0; Index < MAX_NUM_OF_FUSED_DPM_STATES; Index++) { if (PpFuses->SclkDpmDid[Index] != 0) { DpmSclk = GfxFmCalculateClock (PpFuses->SclkDpmDid[Index], StdHeader); DeltaSclk = (DpmSclk > Sclk) ? (DpmSclk - Sclk) : (Sclk - DpmSclk); if (DeltaSclk < MinDeltaSclk) { MinDeltaSclk = DeltaSclk; DpmIndex = Index; } } } return PpFuses->SclkDpmTdpLimit[DpmIndex]; } /*----------------------------------------------------------------------------------------*/ /** * Create new software state * * * @param[in, out] PpWorkspace PP workspace * @retval Pointer to state entry in SW state array */ STATIC SW_STATE* GfxPowerPlayCreateSwState ( IN OUT PP_WORKSPACE *PpWorkspace ) { UINTN Index; for (Index = 0; Index < MAX_NUM_OF_SW_STATES; Index++) { if (PpWorkspace->SwStateArray[Index].Valid == FALSE) { PpWorkspace->SwStateArray[Index].Valid = TRUE; return &(PpWorkspace->SwStateArray[Index]); } } return NULL; } /*----------------------------------------------------------------------------------------*/ /** * Create new DPM state * * * @param[in, out] PpWorkspace PP workspace * @param[in] Sclk SCLK in kHz * @param[in] Vid Vid index * @param[in] Tdp Tdp limit * @retval Index of state entry in DPM state array */ STATIC UINT8 GfxPowerPlayCreateDpmState ( IN OUT PP_WORKSPACE *PpWorkspace, IN UINT32 Sclk, IN UINT8 Vid, IN UINT16 Tdp ) { UINT8 Index; for (Index = 0; Index < MAX_NUM_OF_DPM_STATES; Index++) { if (PpWorkspace->DpmStateArray[Index].Valid == FALSE) { PpWorkspace->DpmStateArray[Index].Sclk = Sclk; PpWorkspace->DpmStateArray[Index].Vid = Vid; PpWorkspace->DpmStateArray[Index].Valid = TRUE; PpWorkspace->DpmStateArray[Index].Tdp = Tdp; return Index; } } return 0; } /*----------------------------------------------------------------------------------------*/ /** * Locate existing or Create new DPM state * * * @param[in, out] PpWorkspace PP workspace * @param[in] Sclk SCLK in kHz * @param[in] Vid Vid index * @param[in] Tdp Tdp limit * @retval Index of state entry in DPM state array */ STATIC UINT8 GfxPowerPlayAddDpmState ( IN OUT PP_WORKSPACE *PpWorkspace, IN UINT32 Sclk, IN UINT8 Vid, IN UINT16 Tdp ) { UINT8 Index; for (Index = 0; Index < MAX_NUM_OF_DPM_STATES; Index++) { if (PpWorkspace->DpmStateArray[Index].Valid && Sclk == PpWorkspace->DpmStateArray[Index].Sclk && Vid == PpWorkspace->DpmStateArray[Index].Vid) { return Index; } } return GfxPowerPlayCreateDpmState (PpWorkspace, Sclk, Vid, Tdp); } /*----------------------------------------------------------------------------------------*/ /** * Add reference to DPM state for SW state * * * @param[in, out] SwStateArray Pointer to SW state array * @param[in] DpmStateIndex DPM state index */ STATIC VOID GfxPowerPlayAddDpmStateToSwState ( IN OUT SW_STATE *SwStateArray, IN UINT8 DpmStateIndex ) { SwStateArray->DpmSatesArray[SwStateArray->NumberOfDpmStates++] = DpmStateIndex; } /*----------------------------------------------------------------------------------------*/ /** * Copy SW state info to PPTable * * * @param[in, out] PpWorkspace PP workspace */ STATIC VOID * GfxPowerPlayAttachStateInfoBlock ( IN OUT PP_WORKSPACE *PpWorkspace ) { UINT8 Index; UINT8 SwStateIndex; STATE_ARRAY *StateArray; ATOM_PPLIB_STATE_V2 *States; StateArray = (STATE_ARRAY *) ((UINT8 *) PpWorkspace->PpTable + PpWorkspace->PpTable->sHeader.usStructureSize); States = &StateArray->States[0]; SwStateIndex = 0; for (Index = 0; Index < MAX_NUM_OF_SW_STATES; Index++) { if (PpWorkspace->SwStateArray[Index].Valid && PpWorkspace->SwStateArray[Index].NumberOfDpmStates != 0) { States->nonClockInfoIndex = SwStateIndex; States->ucNumDPMLevels = PpWorkspace->SwStateArray[Index].NumberOfDpmStates; LibAmdMemCopy ( &States->ClockInfoIndex[0], PpWorkspace->SwStateArray[Index].DpmSatesArray, PpWorkspace->SwStateArray[Index].NumberOfDpmStates, GnbLibGetHeader (PpWorkspace->Gfx) ); States = (ATOM_PPLIB_STATE_V2*) ((UINT8*) States + sizeof (ATOM_PPLIB_STATE_V2) + sizeof (UINT8) * (States->ucNumDPMLevels - 1)); SwStateIndex++; } } StateArray->ucNumEntries = SwStateIndex; PpWorkspace->PpTable->sHeader.usStructureSize = PpWorkspace->PpTable->sHeader.usStructureSize + (USHORT) ((UINT8 *) States - (UINT8 *) StateArray); return StateArray; } /*----------------------------------------------------------------------------------------*/ /** * Copy clock info to PPTable * * * @param[in, out] PpWorkspace PP workspace */ STATIC VOID * GfxPowerPlayAttachClockInfoBlock ( IN OUT PP_WORKSPACE *PpWorkspace ) { CLOCK_INFO_ARRAY *ClockInfoArray; UINT8 Index; UINT8 ClkStateIndex; ClkStateIndex = 0; ClockInfoArray = (CLOCK_INFO_ARRAY *) ((UINT8 *) PpWorkspace->PpTable + PpWorkspace->PpTable->sHeader.usStructureSize); for (Index = 0; Index < MAX_NUM_OF_DPM_STATES; Index++) { if (PpWorkspace->DpmStateArray[Index].Valid == TRUE) { ClockInfoArray->ClockInfo[ClkStateIndex].ucEngineClockHigh = (UINT8) (PpWorkspace->DpmStateArray[Index].Sclk >> 16); ClockInfoArray->ClockInfo[ClkStateIndex].usEngineClockLow = (UINT16) (PpWorkspace->DpmStateArray[Index].Sclk); ClockInfoArray->ClockInfo[ClkStateIndex].vddcIndex = PpWorkspace->DpmStateArray[Index].Vid; ClockInfoArray->ClockInfo[ClkStateIndex].tdpLimit = PpWorkspace->DpmStateArray[Index].Tdp; ClkStateIndex++; } } ClockInfoArray->ucNumEntries = ClkStateIndex; ClockInfoArray->ucEntrySize = sizeof (ATOM_PPLIB_SUMO_CLOCK_INFO); PpWorkspace->PpTable->sHeader.usStructureSize += sizeof (CLOCK_INFO_ARRAY) + sizeof (ATOM_PPLIB_SUMO_CLOCK_INFO) * ClkStateIndex - sizeof (ATOM_PPLIB_SUMO_CLOCK_INFO); return ClockInfoArray; } /*----------------------------------------------------------------------------------------*/ /** * Copy non clock info to PPTable * * * @param[in, out] PpWorkspace PP workspace */ STATIC VOID * GfxPowerPlayAttachNonClockInfoBlock ( IN OUT PP_WORKSPACE *PpWorkspace ) { NON_CLOCK_INFO_ARRAY *NonClockInfoArray; UINT8 Index; UINT8 NonClkStateIndex; NonClockInfoArray = (NON_CLOCK_INFO_ARRAY *) ((UINT8 *) PpWorkspace->PpTable + PpWorkspace->PpTable->sHeader.usStructureSize); NonClkStateIndex = 0; for (Index = 0; Index < MAX_NUM_OF_SW_STATES; Index++) { if (PpWorkspace->SwStateArray[Index].Valid && PpWorkspace->SwStateArray[Index].NumberOfDpmStates != 0) { NonClockInfoArray->NonClockInfo[NonClkStateIndex].usClassification = PpWorkspace->SwStateArray[Index].Classification; NonClockInfoArray->NonClockInfo[NonClkStateIndex].ulCapsAndSettings = PpWorkspace->SwStateArray[Index].CapsAndSettings; NonClockInfoArray->NonClockInfo[NonClkStateIndex].usClassification2 = PpWorkspace->SwStateArray[Index].Classification2; NonClockInfoArray->NonClockInfo[NonClkStateIndex].ulDCLK = PpWorkspace->SwStateArray[Index].Dclk; NonClockInfoArray->NonClockInfo[NonClkStateIndex].ulVCLK = PpWorkspace->SwStateArray[Index].Vclk; NonClkStateIndex++; } } NonClockInfoArray->ucNumEntries = NonClkStateIndex; NonClockInfoArray->ucEntrySize = sizeof (ATOM_PPLIB_NONCLOCK_INFO); PpWorkspace->PpTable->sHeader.usStructureSize += sizeof (NON_CLOCK_INFO_ARRAY) + sizeof (ATOM_PPLIB_NONCLOCK_INFO) * NonClkStateIndex - sizeof (ATOM_PPLIB_NONCLOCK_INFO); return NonClockInfoArray; } /*----------------------------------------------------------------------------------------*/ /** * Check if fused state valid * * * @param[out] Index State index * @param[in] PpFuses Pointer to fuse table * @param[in] Gfx Gfx configuration info * @retval TRUE State is valid */ STATIC BOOLEAN GfxPowerPlayIsFusedStateValid ( IN UINT8 Index, IN PP_FUSE_ARRAY *PpFuses, IN GFX_PLATFORM_CONFIG *Gfx ) { BOOLEAN Result; Result = FALSE; if (PpFuses->SclkDpmValid[Index] != 0) { Result = TRUE; if (PpFuses->PolicyLabel[Index] == POLICY_LABEL_BATTERY && (Gfx->AmdPlatformType & AMD_PLATFORM_MOBILE) == 0) { Result = FALSE; } } return Result; } /*----------------------------------------------------------------------------------------*/ /** * Get SW state calssification from fuses * * * @param[out] Index State index * @param[in] PpFuses Pointer to fuse table * @param[in] Gfx Gfx configuration info * @retval State classification */ STATIC UINT16 GfxPowerPlayGetClassificationFromFuses ( IN UINT8 Index, IN PP_FUSE_ARRAY *PpFuses, IN GFX_PLATFORM_CONFIG *Gfx ) { UINT16 Classification; Classification = 0; switch (PpFuses->PolicyFlags[Index]) { case 0x1: Classification |= ATOM_PPLIB_CLASSIFICATION_NONUVDSTATE; break; case 0x2: Classification |= ATOM_PPLIB_CLASSIFICATION_UVDSTATE; break; case 0x4: //Possible SD + HD state break; case 0x8: Classification |= ATOM_PPLIB_CLASSIFICATION_HDSTATE; break; case 0x10: Classification |= ATOM_PPLIB_CLASSIFICATION_SDSTATE; break; default: break; } switch (PpFuses->PolicyLabel[Index]) { case POLICY_LABEL_BATTERY: Classification |= ATOM_PPLIB_CLASSIFICATION_UI_BATTERY; break; case POLICY_LABEL_PERFORMANCE: Classification |= ATOM_PPLIB_CLASSIFICATION_UI_PERFORMANCE; break; default: break; } return Classification; } /*----------------------------------------------------------------------------------------*/ /** * Get SW state calssification2 from fuses * * * @param[out] Index State index * @param[in] PpFuses Pointer to fuse table * @param[in] Gfx Gfx configuration info * @retval State classification2 */ STATIC UINT16 GfxPowerPlayGetClassification2FromFuses ( IN UINT8 Index, IN PP_FUSE_ARRAY *PpFuses, IN GFX_PLATFORM_CONFIG *Gfx ) { UINT16 Classification2; Classification2 = 0; switch (PpFuses->PolicyFlags[Index]) { case 0x4: Classification2 |= ATOM_PPLIB_CLASSIFICATION2_MVC; break; default: break; } return Classification2; } /*----------------------------------------------------------------------------------------*/ /** * Build SCLK state info * * * @param[in, out] PpWorkspace PP workspace */ STATIC VOID GfxPowerPlayBuildSclkStateTable ( IN OUT PP_WORKSPACE *PpWorkspace ) { UINT8 ClkStateIndex; UINT8 DpmFuseIndex; UINT8 Index; UINT32 Sclk; SW_STATE *State; PP_FUSE_ARRAY *PpFuses; PpFuses = PpWorkspace->PpFuses; // Create States from Fuses for (Index = 0; Index < MAX_NUM_OF_FUSED_SW_STATES; Index++) { if (GfxPowerPlayIsFusedStateValid (Index, PpFuses, PpWorkspace->Gfx)) { //Create new SW State; State = GfxPowerPlayCreateSwState (PpWorkspace); State->Classification = GfxPowerPlayGetClassificationFromFuses (Index, PpFuses, PpWorkspace->Gfx); State->Classification2 = GfxPowerPlayGetClassification2FromFuses (Index, PpFuses, PpWorkspace->Gfx); if ((State->Classification & (ATOM_PPLIB_CLASSIFICATION_HDSTATE | ATOM_PPLIB_CLASSIFICATION_UVDSTATE | ATOM_PPLIB_CLASSIFICATION_SDSTATE)) != 0 || (State->Classification2 & ATOM_PPLIB_CLASSIFICATION2_MVC) != 0) { State->Vclk = (PpFuses->VclkDid[PpFuses->VclkDclkSel[Index]] != 0) ? GfxFmCalculateClock (PpFuses->VclkDid[PpFuses->VclkDclkSel[Index]], GnbLibGetHeader (PpWorkspace->Gfx)) : 0; State->Dclk = (PpFuses->DclkDid[PpFuses->VclkDclkSel[Index]] != 0) ? GfxFmCalculateClock (PpFuses->DclkDid[PpFuses->VclkDclkSel[Index]], GnbLibGetHeader (PpWorkspace->Gfx)) : 0; } if (((State->Classification & 0x7) == ATOM_PPLIB_CLASSIFICATION_UI_BATTERY) || ((State->Classification & (ATOM_PPLIB_CLASSIFICATION_HDSTATE | ATOM_PPLIB_CLASSIFICATION_SDSTATE)) != 0)) { if (PpWorkspace->Gfx->AbmSupport != 0) { State->CapsAndSettings |= ATOM_PPLIB_ENABLE_VARIBRIGHT; } if (PpWorkspace->Gfx->DynamicRefreshRate != 0) { State->CapsAndSettings |= ATOM_PPLIB_ENABLE_DRR; } } for (DpmFuseIndex = 0; DpmFuseIndex < MAX_NUM_OF_FUSED_DPM_STATES; DpmFuseIndex++) { if ((PpFuses->SclkDpmValid[Index] & (1 << DpmFuseIndex)) != 0 ) { Sclk = (PpFuses->SclkDpmDid[DpmFuseIndex] != 0) ? GfxFmCalculateClock (PpFuses->SclkDpmDid[DpmFuseIndex], GnbLibGetHeader (PpWorkspace->Gfx)) : 0; if (Sclk != 0) { ClkStateIndex = GfxPowerPlayAddDpmState (PpWorkspace, Sclk, PpFuses->SclkDpmVid[DpmFuseIndex], PpFuses->SclkDpmTdpLimit[DpmFuseIndex]); GfxPowerPlayAddDpmStateToSwState (State, ClkStateIndex); } } } } } // Create Boot State State = GfxPowerPlayCreateSwState (PpWorkspace); State->Classification = ATOM_PPLIB_CLASSIFICATION_BOOT; Sclk = 200 * 100; ClkStateIndex = GfxPowerPlayAddDpmState (PpWorkspace, Sclk, 0, GfxPowerPlayLocateTdp (PpFuses, Sclk, GnbLibGetHeader (PpWorkspace->Gfx))); GfxPowerPlayAddDpmStateToSwState (State, ClkStateIndex); // Create Thermal State State = GfxPowerPlayCreateSwState (PpWorkspace); State->Classification = ATOM_PPLIB_CLASSIFICATION_THERMAL; Sclk = GfxFmCalculateClock (PpFuses->SclkThermDid, GnbLibGetHeader (PpWorkspace->Gfx)); ClkStateIndex = GfxPowerPlayAddDpmState (PpWorkspace, Sclk, 0, GfxPowerPlayLocateTdp (PpFuses, Sclk, GnbLibGetHeader (PpWorkspace->Gfx))); GfxPowerPlayAddDpmStateToSwState (State, ClkStateIndex); } /*----------------------------------------------------------------------------------------*/ /** * Add ECLK state * * * @param[in, out] PpWorkspace PP workspace * @param[in] Eclk SCLK in kHz * @retval Index of state entry in ECLK clock array */ STATIC UINT8 GfxPowerPlayAddEclkState ( IN OUT PP_WORKSPACE *PpWorkspace, IN UINT32 Eclk ) { UINT8 Index; USHORT EclkLow; UCHAR EclkHigh; EclkLow = (USHORT) (Eclk & 0xffff); EclkHigh = (UCHAR) (Eclk >> 16); for (Index = 0; Index < PpWorkspace->NumOfVceClockEnties; Index++) { if (PpWorkspace->VceClockInfoArray[Index].ucECClkHigh == EclkHigh && PpWorkspace->VceClockInfoArray[Index].usECClkLow == EclkLow) { return Index; } } PpWorkspace->VceClockInfoArray[PpWorkspace->NumOfVceClockEnties].ucECClkHigh = EclkHigh; PpWorkspace->VceClockInfoArray[PpWorkspace->NumOfVceClockEnties].usECClkLow = EclkLow; PpWorkspace->VceClockInfoArray[PpWorkspace->NumOfVceClockEnties].ucEVClkHigh = EclkHigh; PpWorkspace->VceClockInfoArray[PpWorkspace->NumOfVceClockEnties].usEVClkLow = EclkLow; return PpWorkspace->NumOfVceClockEnties++; } /*----------------------------------------------------------------------------------------*/ /** * Add ECLK state * * * @param[in, out] PpWorkspace PP workspace * @param[in] EclkIndex ECLK index * @param[in] Vid Vid index * @retval Index of state entry in Eclk Voltage record array */ STATIC UINT8 GfxPowerPlayAddEclkVoltageRecord ( IN OUT PP_WORKSPACE *PpWorkspace, IN UINT8 EclkIndex, IN UINT8 Vid ) { UINT8 Index; for (Index = 0; Index < PpWorkspace->NumOfClockVoltageLimitEnties; Index++) { if (PpWorkspace->VceClockVoltageLimitArray[Index].ucVCEClockInfoIndex == EclkIndex) { return Index; } } PpWorkspace->VceClockVoltageLimitArray[PpWorkspace->NumOfClockVoltageLimitEnties].ucVCEClockInfoIndex = EclkIndex; PpWorkspace->VceClockVoltageLimitArray[PpWorkspace->NumOfClockVoltageLimitEnties].usVoltage = Vid; return PpWorkspace->NumOfClockVoltageLimitEnties++; } /*----------------------------------------------------------------------------------------*/ /** * Attach extended header * * * @param[in, out] PpWorkspace PP workspace */ STATIC VOID * GfxPowerPlayAttachVceTableRevBlock ( IN OUT PP_WORKSPACE *PpWorkspace ) { ATOM_PPLIB_VCE_TABLE *VceTable; VceTable = (ATOM_PPLIB_VCE_TABLE *) ((UINT8 *) PpWorkspace->PpTable + PpWorkspace->PpTable->sHeader.usStructureSize); VceTable->revid = 0; PpWorkspace->PpTable->sHeader.usStructureSize += sizeof (ATOM_PPLIB_VCE_TABLE); return VceTable; } /*----------------------------------------------------------------------------------------*/ /** * Attach extended header * * * @param[in, out] PpWorkspace PP workspace */ STATIC VOID * GfxPowerPlayAttachExtendedHeaderBlock ( IN OUT PP_WORKSPACE *PpWorkspace ) { ATOM_PPLIB_EXTENDEDHEADER *ExtendedHeader; ExtendedHeader = (ATOM_PPLIB_EXTENDEDHEADER *) ((UINT8 *) PpWorkspace->PpTable + PpWorkspace->PpTable->sHeader.usStructureSize); ExtendedHeader->usSize = sizeof (ATOM_PPLIB_EXTENDEDHEADER); PpWorkspace->PpTable->sHeader.usStructureSize += sizeof (ATOM_PPLIB_EXTENDEDHEADER); return ExtendedHeader; } /*----------------------------------------------------------------------------------------*/ /** * Attach VCE clock info block * * * @param[in, out] PpWorkspace PP workspace */ STATIC VOID * GfxPowerPlayAttachVceClockInfoBlock ( IN OUT PP_WORKSPACE *PpWorkspace ) { VCECLOCKINFOARRAY *VceClockInfoArray; VceClockInfoArray = (VCECLOCKINFOARRAY *) ((UINT8 *) PpWorkspace->PpTable + PpWorkspace->PpTable->sHeader.usStructureSize); VceClockInfoArray->ucNumEntries = PpWorkspace->NumOfVceClockEnties; LibAmdMemCopy ( &VceClockInfoArray->entries[0], &PpWorkspace->VceClockInfoArray[0], VceClockInfoArray->ucNumEntries * sizeof (VCECLOCKINFO), GnbLibGetHeader (PpWorkspace->Gfx) ); PpWorkspace->PpTable->sHeader.usStructureSize = PpWorkspace->PpTable->sHeader.usStructureSize + sizeof (VCECLOCKINFOARRAY) + VceClockInfoArray->ucNumEntries * sizeof (VCECLOCKINFO) - sizeof (VCECLOCKINFO); return VceClockInfoArray; } /*----------------------------------------------------------------------------------------*/ /** * Attach VCE voltage limit block * * * @param[in, out] PpWorkspace PP workspace */ STATIC VOID * GfxPowerPlayAttachVceVoltageLimitBlock ( IN OUT PP_WORKSPACE *PpWorkspace ) { ATOM_PPLIB_VCE_CLOCK_VOLTAGE_LIMIT_TABLE *VceClockVoltageLimitTable; VceClockVoltageLimitTable = (ATOM_PPLIB_VCE_CLOCK_VOLTAGE_LIMIT_TABLE *) ((UINT8 *) PpWorkspace->PpTable + PpWorkspace->PpTable->sHeader.usStructureSize); VceClockVoltageLimitTable->numEntries = PpWorkspace->NumOfClockVoltageLimitEnties; LibAmdMemCopy ( &VceClockVoltageLimitTable->entries[0], &PpWorkspace->VceClockVoltageLimitArray[0], VceClockVoltageLimitTable->numEntries * sizeof (ATOM_PPLIB_VCE_CLOCK_VOLTAGE_LIMIT_RECORD), GnbLibGetHeader (PpWorkspace->Gfx) ); PpWorkspace->PpTable->sHeader.usStructureSize = PpWorkspace->PpTable->sHeader.usStructureSize + sizeof (ATOM_PPLIB_VCE_CLOCK_VOLTAGE_LIMIT_TABLE) + VceClockVoltageLimitTable->numEntries * sizeof (ATOM_PPLIB_VCE_CLOCK_VOLTAGE_LIMIT_RECORD) - sizeof (ATOM_PPLIB_VCE_CLOCK_VOLTAGE_LIMIT_RECORD); return VceClockVoltageLimitTable; } /*----------------------------------------------------------------------------------------*/ /** * Attach VCE state block * * * @param[in, out] PpWorkspace PP workspace */ STATIC VOID * GfxPowerPlayAttachVceStateTaleBlock ( IN OUT PP_WORKSPACE *PpWorkspace ) { ATOM_PPLIB_VCE_STATE_TABLE *VceStateTable; VceStateTable = (ATOM_PPLIB_VCE_STATE_TABLE *) ((UINT8 *) PpWorkspace->PpTable + PpWorkspace->PpTable->sHeader.usStructureSize); VceStateTable->numEntries = PpWorkspace->NumOfVceStateEntries; LibAmdMemCopy ( &VceStateTable->entries[0], &PpWorkspace->VceStateArray[0], VceStateTable->numEntries * sizeof (ATOM_PPLIB_VCE_STATE_RECORD), GnbLibGetHeader (PpWorkspace->Gfx) ); PpWorkspace->PpTable->sHeader.usStructureSize = PpWorkspace->PpTable->sHeader.usStructureSize + sizeof (ATOM_PPLIB_VCE_STATE_TABLE) + VceStateTable->numEntries * sizeof (ATOM_PPLIB_VCE_STATE_RECORD) - sizeof (ATOM_PPLIB_VCE_STATE_RECORD); return VceStateTable; } /*----------------------------------------------------------------------------------------*/ /** * Build VCE state info * * * @param[in, out] PpWorkspace PP workspace */ STATIC VOID GfxPowerPlayBuildVceStateTable ( IN OUT PP_WORKSPACE *PpWorkspace ) { UINT8 Index; UINT8 VceStateIndex; UINT8 Vid; UINT32 Eclk; UINT32 Sclk; UINT8 UsedStateBitmap; UsedStateBitmap = 0; // build used state for (Index = 0; Index < (sizeof (PpWorkspace->PpFuses->VceFlags) / sizeof (PpWorkspace->PpFuses->VceFlags[0])) ; Index++) { UsedStateBitmap |= PpWorkspace->PpFuses->VceFlags[Index]; for (VceStateIndex = 0; VceStateIndex < (sizeof (PpWorkspace->VceStateArray) / sizeof (PpWorkspace->VceStateArray[0])); VceStateIndex++) { if ((PpWorkspace->PpFuses->VceFlags[Index] & (1 << VceStateIndex)) != 0) { Sclk = GfxFmCalculateClock (PpWorkspace->PpFuses->SclkDpmDid[PpWorkspace->PpFuses->VceReqSclkSel[Index]], GnbLibGetHeader (PpWorkspace->Gfx)); Vid = PpWorkspace->PpFuses->SclkDpmVid[PpWorkspace->PpFuses->VceReqSclkSel[Index]]; PpWorkspace->VceStateArray[VceStateIndex].ucClockInfoIndex = GfxPowerPlayAddDpmState (PpWorkspace, Sclk, Vid, GfxPowerPlayLocateTdp (PpWorkspace->PpFuses, Sclk, GnbLibGetHeader (PpWorkspace->Gfx))); if (PpWorkspace->PpFuses->VceMclk[Index] == 1) { PpWorkspace->VceStateArray[VceStateIndex].ucClockInfoIndex |= (PpWorkspace->PpFuses->VceMclk[Index] << 6); } Eclk = GfxFmCalculateClock (PpWorkspace->PpFuses->EclkDid[Index], GnbLibGetHeader (PpWorkspace->Gfx)); PpWorkspace->VceStateArray[VceStateIndex].ucVCEClockInfoIndex = GfxPowerPlayAddEclkState (PpWorkspace, Eclk); GfxPowerPlayAddEclkVoltageRecord (PpWorkspace, PpWorkspace->VceStateArray[VceStateIndex].ucVCEClockInfoIndex, Vid); PpWorkspace->NumOfVceStateEntries++; } } } //build unused states for (VceStateIndex = 0; VceStateIndex < (sizeof (PpWorkspace->VceStateArray) / sizeof (PpWorkspace->VceStateArray[0])); VceStateIndex++) { if ((UsedStateBitmap & (1 << VceStateIndex)) == 0) { PpWorkspace->VceStateArray[VceStateIndex].ucClockInfoIndex = 0; PpWorkspace->VceStateArray[VceStateIndex].ucVCEClockInfoIndex = GfxPowerPlayAddEclkState (PpWorkspace, 0); PpWorkspace->NumOfVceStateEntries++; } } } /*----------------------------------------------------------------------------------------*/ /** * Build PP table * * * @param[out] Buffer Buffer to create PP table * @param[in] Gfx Gfx configuration info * @retval AGESA_SUCCESS * @retval AGESA_ERROR */ AGESA_STATUS GfxPowerPlayBuildTable ( OUT VOID *Buffer, IN GFX_PLATFORM_CONFIG *Gfx ) { PP_WORKSPACE PpWorkspace; VOID *BlockPtr; LibAmdMemFill (&PpWorkspace, 0x00, sizeof (PP_WORKSPACE), GnbLibGetHeader (Gfx)); PpWorkspace.PpFuses = GnbLocateHeapBuffer (AMD_PP_FUSE_TABLE_HANDLE, GnbLibGetHeader (Gfx)); ASSERT (PpWorkspace.PpFuses != NULL); if (PpWorkspace.PpFuses == NULL) { return AGESA_ERROR; } PpWorkspace.PpTable = (ATOM_PPLIB_POWERPLAYTABLE3 *) Buffer; PpWorkspace.Gfx = Gfx; //Fill static info PpWorkspace.PpTable->sHeader.ucTableFormatRevision = 6; PpWorkspace.PpTable->sHeader.ucTableContentRevision = 1; PpWorkspace.PpTable->ucDataRevision = PpWorkspace.PpFuses->PPlayTableRev; PpWorkspace.PpTable->sThermalController.ucType = ATOM_PP_THERMALCONTROLLER_SUMO; PpWorkspace.PpTable->sThermalController.ucFanParameters = ATOM_PP_FANPARAMETERS_NOFAN; PpWorkspace.PpTable->sHeader.usStructureSize = sizeof (ATOM_PPLIB_POWERPLAYTABLE3); PpWorkspace.PpTable->usTableSize = sizeof (ATOM_PPLIB_POWERPLAYTABLE3); PpWorkspace.PpTable->usFormatID = 7; if ((Gfx->AmdPlatformType & AMD_PLATFORM_MOBILE) != 0) { PpWorkspace.PpTable->ulPlatformCaps |= ATOM_PP_PLATFORM_CAP_POWERPLAY; } // Fill Slck SW/DPM state info GfxPowerPlayBuildSclkStateTable (&PpWorkspace); // Fill Eclk state info if (PpWorkspace.PpFuses->VceSateTableSupport) { GfxPowerPlayBuildVceStateTable (&PpWorkspace); } //Copy state info to actual PP table BlockPtr = GfxPowerPlayAttachStateInfoBlock (&PpWorkspace); PpWorkspace.PpTable->usStateArrayOffset = (USHORT) ((UINT8 *) BlockPtr - (UINT8 *) (PpWorkspace.PpTable)); BlockPtr = GfxPowerPlayAttachClockInfoBlock (&PpWorkspace); PpWorkspace.PpTable->usClockInfoArrayOffset = (USHORT) ((UINT8 *) BlockPtr - (UINT8 *) (PpWorkspace.PpTable)); BlockPtr = GfxPowerPlayAttachNonClockInfoBlock (&PpWorkspace); PpWorkspace.PpTable->usNonClockInfoArrayOffset = (USHORT) ((UINT8 *) BlockPtr - (UINT8 *) (PpWorkspace.PpTable)); if (PpWorkspace.PpFuses->VceSateTableSupport) { ATOM_PPLIB_EXTENDEDHEADER *ExtendedHeader; ExtendedHeader = (ATOM_PPLIB_EXTENDEDHEADER *) GfxPowerPlayAttachExtendedHeaderBlock (&PpWorkspace); PpWorkspace.PpTable->usExtendendedHeaderOffset = (USHORT) ((UINT8 *) ExtendedHeader - (UINT8 *) (PpWorkspace.PpTable)); BlockPtr = GfxPowerPlayAttachVceTableRevBlock (&PpWorkspace); ExtendedHeader->usVCETableOffset = (USHORT) ((UINT8 *) BlockPtr - (UINT8 *) (PpWorkspace.PpTable)); GfxPowerPlayAttachVceClockInfoBlock (&PpWorkspace); GfxPowerPlayAttachVceVoltageLimitBlock (&PpWorkspace); GfxPowerPlayAttachVceStateTaleBlock (&PpWorkspace); } GNB_DEBUG_CODE ( GfxIntegratedDebugDumpPpTable (PpWorkspace.PpTable, Gfx); ); return AGESA_SUCCESS; } /*----------------------------------------------------------------------------------------*/ /** * Dump PP table * * * * @param[in] PpTable Power Play table * @param[in] Gfx Gfx configuration info */ VOID GfxIntegratedDebugDumpPpTable ( IN ATOM_PPLIB_POWERPLAYTABLE3 *PpTable, IN GFX_PLATFORM_CONFIG *Gfx ) { UINTN Index; UINTN DpmIndex; STATE_ARRAY *StateArray; ATOM_PPLIB_STATE_V2 *StatesPtr; NON_CLOCK_INFO_ARRAY *NonClockInfoArrayPtr; CLOCK_INFO_ARRAY *ClockInfoArrayPtr; ATOM_PPLIB_EXTENDEDHEADER *ExtendedHeader; ATOM_PPLIB_VCE_STATE_TABLE *VceStateTable; ATOM_PPLIB_VCE_CLOCK_VOLTAGE_LIMIT_TABLE *VceClockVoltageLimitTable; VCECLOCKINFOARRAY *VceClockInfoArray; UINT8 SclkIndex; UINT8 EclkIndex; IDS_HDT_CONSOLE (GFX_MISC, " < --- Power Play Table ------ > \n"); IDS_HDT_CONSOLE (GFX_MISC, " Table Revision = %d\n", PpTable->ucDataRevision); StateArray = (STATE_ARRAY *) ((UINT8 *) PpTable + PpTable->usStateArrayOffset); StatesPtr = StateArray->States; NonClockInfoArrayPtr = (NON_CLOCK_INFO_ARRAY *) ((UINT8 *) PpTable + PpTable->usNonClockInfoArrayOffset); ClockInfoArrayPtr = (CLOCK_INFO_ARRAY *) ((UINT8 *) PpTable + PpTable->usClockInfoArrayOffset); IDS_HDT_CONSOLE (GFX_MISC, " < --- SW State Table ---------> \n"); for (Index = 0; Index < StateArray->ucNumEntries; Index++) { IDS_HDT_CONSOLE (GFX_MISC, " State #%d\n", Index + 1 ); IDS_HDT_CONSOLE (GFX_MISC, " Classification 0x%x\n", NonClockInfoArrayPtr->NonClockInfo[StatesPtr->nonClockInfoIndex].usClassification ); IDS_HDT_CONSOLE (GFX_MISC, " Classification2 0x%x\n", NonClockInfoArrayPtr->NonClockInfo[StatesPtr->nonClockInfoIndex].usClassification2 ); IDS_HDT_CONSOLE (GFX_MISC, " VCLK = %dkHz\n", NonClockInfoArrayPtr->NonClockInfo[StatesPtr->nonClockInfoIndex].ulVCLK ); IDS_HDT_CONSOLE (GFX_MISC, " DCLK = %dkHz\n", NonClockInfoArrayPtr->NonClockInfo[StatesPtr->nonClockInfoIndex].ulDCLK ); IDS_HDT_CONSOLE (GFX_MISC, " DPM State Index: "); for (DpmIndex = 0; DpmIndex < StatesPtr->ucNumDPMLevels; DpmIndex++) { IDS_HDT_CONSOLE (GFX_MISC, "%d ", StatesPtr->ClockInfoIndex [DpmIndex] ); } IDS_HDT_CONSOLE (GFX_MISC, "\n"); StatesPtr = (ATOM_PPLIB_STATE_V2 *) ((UINT8 *) StatesPtr + sizeof (ATOM_PPLIB_STATE_V2) + StatesPtr->ucNumDPMLevels - 1); } IDS_HDT_CONSOLE (GFX_MISC, " < --- SCLK DPM State Table ---> \n"); for (Index = 0; Index < ClockInfoArrayPtr->ucNumEntries; Index++) { UINT32 Sclk; Sclk = ClockInfoArrayPtr->ClockInfo[Index].usEngineClockLow | (ClockInfoArrayPtr->ClockInfo[Index].ucEngineClockHigh << 16); IDS_HDT_CONSOLE (GFX_MISC, " DPM State #%d\n", Index ); IDS_HDT_CONSOLE (GFX_MISC, " SCLK = %d\n", ClockInfoArrayPtr->ClockInfo[Index].usEngineClockLow | (ClockInfoArrayPtr->ClockInfo[Index].ucEngineClockHigh << 16) ); IDS_HDT_CONSOLE (GFX_MISC, " VID index = %d\n", ClockInfoArrayPtr->ClockInfo[Index].vddcIndex ); IDS_HDT_CONSOLE (GFX_MISC, " tdpLimit = %d\n", ClockInfoArrayPtr->ClockInfo[Index].tdpLimit ); } if (PpTable->usExtendendedHeaderOffset != 0) { ExtendedHeader = (ATOM_PPLIB_EXTENDEDHEADER *) ((UINT8 *) PpTable + PpTable->usExtendendedHeaderOffset); VceClockInfoArray = (VCECLOCKINFOARRAY *) ((UINT8 *) ExtendedHeader + sizeof (ATOM_PPLIB_EXTENDEDHEADER) + sizeof (ATOM_PPLIB_VCE_TABLE)); VceClockVoltageLimitTable = (ATOM_PPLIB_VCE_CLOCK_VOLTAGE_LIMIT_TABLE *) ((UINT8 *) VceClockInfoArray + sizeof (VCECLOCKINFOARRAY) + VceClockInfoArray->ucNumEntries * sizeof (VCECLOCKINFO) - sizeof (VCECLOCKINFO)); VceStateTable = (ATOM_PPLIB_VCE_STATE_TABLE *) ((UINT8 *) VceClockVoltageLimitTable + sizeof (ATOM_PPLIB_VCE_CLOCK_VOLTAGE_LIMIT_TABLE) + VceClockVoltageLimitTable->numEntries * sizeof (ATOM_PPLIB_VCE_CLOCK_VOLTAGE_LIMIT_RECORD) - sizeof (ATOM_PPLIB_VCE_CLOCK_VOLTAGE_LIMIT_RECORD)); IDS_HDT_CONSOLE (GFX_MISC, " < --- VCE State Table [%d]--> \n", VceStateTable->numEntries); for (Index = 0; Index < VceStateTable->numEntries; Index++) { SclkIndex = VceStateTable->entries[Index].ucClockInfoIndex & 0x3F; EclkIndex = VceStateTable->entries[Index].ucVCEClockInfoIndex; IDS_HDT_CONSOLE (GFX_MISC, " VCE State #%d\n", Index ); if ((VceClockInfoArray->entries[EclkIndex].usECClkLow | (VceClockInfoArray->entries[EclkIndex].ucECClkHigh << 16)) == 0) { IDS_HDT_CONSOLE (GFX_MISC, " Disable\n"); } else { IDS_HDT_CONSOLE (GFX_MISC, " SCLK = %d\n", ClockInfoArrayPtr->ClockInfo[SclkIndex].usEngineClockLow | (ClockInfoArrayPtr->ClockInfo[SclkIndex].ucEngineClockHigh << 16) ); IDS_HDT_CONSOLE (GFX_MISC, " ECCLK = %d\n", VceClockInfoArray->entries[EclkIndex].usECClkLow | (VceClockInfoArray->entries[EclkIndex].ucECClkHigh << 16) ); IDS_HDT_CONSOLE (GFX_MISC, " EVCLK = %d\n", VceClockInfoArray->entries[EclkIndex].usEVClkLow | (VceClockInfoArray->entries[EclkIndex].ucEVClkHigh << 16) ); IDS_HDT_CONSOLE (GFX_MISC, " MCLK = %d\n", (VceStateTable->entries[Index].ucClockInfoIndex >> 6 ) & 0x3 ); } } IDS_HDT_CONSOLE (GFX_MISC, " < --- VCE Voltage Record Table ---> \n"); for (Index = 0; Index < VceClockVoltageLimitTable->numEntries; Index++) { EclkIndex = VceClockVoltageLimitTable->entries[Index].ucVCEClockInfoIndex; IDS_HDT_CONSOLE (GFX_MISC, " VCE Voltage Record #%d\n", Index ); IDS_HDT_CONSOLE (GFX_MISC, " ECLK = %d\n", VceClockInfoArray->entries[EclkIndex].usECClkLow | (VceClockInfoArray->entries[EclkIndex].ucECClkHigh << 16) ); IDS_HDT_CONSOLE (GFX_MISC, " VID index = %d\n", VceClockVoltageLimitTable->entries[Index].usVoltage ); } } }