| /* $NoKeywords:$ */ |
| /** |
| * @file |
| * |
| * AMD CPU Core Leveling Function. |
| * |
| * Contains code to Level the number of core in a multi-socket system |
| * |
| * @xrefitem bom "File Content Label" "Release Content" |
| * @e project: AGESA |
| * @e sub-project: CPU |
| * @e \$Revision: 35136 $ @e \$Date: 2010-07-16 11:29:48 +0800 (Fri, 16 Jul 2010) $ |
| * |
| */ |
| /* |
| ***************************************************************************** |
| * |
| * Copyright (c) 2011, 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. |
| * |
| * *************************************************************************** |
| * |
| */ |
| |
| |
| /* |
| *---------------------------------------------------------------------------- |
| * MODULES USED |
| * |
| *---------------------------------------------------------------------------- |
| */ |
| #include "AGESA.h" |
| #include "AMD.h" |
| #include "amdlib.h" |
| #include "Topology.h" |
| #include "cpuRegisters.h" |
| #include "GeneralServices.h" |
| #include "cpuServices.h" |
| #include "cpuFamilyTranslation.h" |
| #include "cpuFeatures.h" |
| #include "cpuEarlyInit.h" |
| #include "heapManager.h" |
| #include "Filecode.h" |
| CODE_GROUP (G1_PEICC) |
| RDATA_GROUP (G1_PEICC) |
| #define FILECODE PROC_CPU_FEATURE_CPUCORELEVELING_FILECODE |
| /*---------------------------------------------------------------------------- |
| * DEFINITIONS AND MACROS |
| * |
| *---------------------------------------------------------------------------- |
| */ |
| |
| |
| /*---------------------------------------------------------------------------- |
| * TYPEDEFS AND STRUCTURES |
| * |
| *---------------------------------------------------------------------------- |
| */ |
| |
| /*---------------------------------------------------------------------------------------- |
| * E X P O R T E D F U N C T I O N S |
| *---------------------------------------------------------------------------------------- |
| */ |
| extern CPU_FAMILY_SUPPORT_TABLE CoreLevelingFamilyServiceTable; |
| |
| /*---------------------------------------------------------------------------- |
| * PROTOTYPES OF LOCAL FUNCTIONS |
| * |
| *---------------------------------------------------------------------------- |
| */ |
| |
| /*---------------------------------------------------------------------------------------- |
| * P U B L I C F U N C T I O N S |
| *---------------------------------------------------------------------------------------- |
| */ |
| |
| /*---------------------------------------------------------------------------------------*/ |
| /** |
| * Should core leveling be enabled |
| * |
| * @param[in] PlatformConfig Contains the runtime modifiable feature input data. |
| * @param[in] StdHeader Config Handle for library, services. |
| * |
| * @retval TRUE core leveling is supported. |
| * @retval FALSE core leveling cannot be enabled. |
| * |
| */ |
| BOOLEAN |
| STATIC |
| IsCoreLevelingEnabled ( |
| IN PLATFORM_CONFIGURATION *PlatformConfig, |
| IN AMD_CONFIG_PARAMS *StdHeader |
| ) |
| { |
| CORE_LEVELING_TYPE CoreLevelMode; |
| |
| CoreLevelMode = (CORE_LEVELING_TYPE) PlatformConfig->CoreLevelingMode; |
| if (CoreLevelMode != CORE_LEVEL_NONE) { |
| return (TRUE); |
| } else { |
| return (FALSE); |
| } |
| } |
| |
| /*---------------------------------------------------------------------------------------*/ |
| /** |
| * Performs core leveling for the system. |
| * |
| * This function implements the AMD_CPU_EARLY_PARAMS.CoreLevelingMode parameter. |
| * The possible modes are: |
| * -0 CORE_LEVEL_LOWEST Level to lowest common denominator |
| * -1 CORE_LEVEL_TWO Level to 2 cores |
| * -2 CORE_LEVEL_POWER_OF_TWO Level to 1,2,4 or 8 |
| * -3 CORE_LEVEL_NONE Do no leveling |
| * -4 CORE_LEVEL_COMPUTE_UNIT Level cores to one core per compute unit |
| * |
| * @param[in] EntryPoint Timepoint designator. |
| * @param[in] PlatformConfig Contains the leveling mode parameter |
| * @param[in] StdHeader Config handle for library and services |
| * |
| * @return The most severe status of any family specific service. |
| * |
| */ |
| AGESA_STATUS |
| CoreLevelingAtEarly ( |
| IN UINT64 EntryPoint, |
| IN PLATFORM_CONFIGURATION *PlatformConfig, |
| IN AMD_CONFIG_PARAMS *StdHeader |
| ) |
| { |
| UINT32 CoreNumPerComputeUnit; |
| UINT32 MinNumOfComputeUnit; |
| UINT32 EnabledComputeUnit; |
| UINT32 Socket; |
| UINT32 Module; |
| UINT32 NumberOfSockets; |
| UINT32 NumberOfModules; |
| UINT32 MinCoreCountOnNode; |
| UINT32 MaxCoreCountOnNode; |
| UINT32 LowCore; |
| UINT32 HighCore; |
| UINT32 LeveledCores; |
| UINT32 RequestedCores; |
| UINT32 TotalEnabledCoresOnNode; |
| BOOLEAN RegUpdated; |
| AP_MAIL_INFO ApMailboxInfo; |
| CORE_LEVELING_TYPE CoreLevelMode; |
| CPU_CORE_LEVELING_FAMILY_SERVICES *FamilySpecificServices; |
| WARM_RESET_REQUEST Request; |
| |
| MaxCoreCountOnNode = 0; |
| MinCoreCountOnNode = 0xFFFFFFFF; |
| LeveledCores = 0; |
| CoreNumPerComputeUnit = 1; |
| MinNumOfComputeUnit = 0xFF; |
| |
| ASSERT (PlatformConfig->CoreLevelingMode < CoreLevelModeMax); |
| |
| // Get OEM IO core level mode |
| CoreLevelMode = (CORE_LEVELING_TYPE) PlatformConfig->CoreLevelingMode; |
| |
| // Get socket count |
| NumberOfSockets = GetPlatformNumberOfSockets (); |
| GetApMailbox (&ApMailboxInfo.Info, StdHeader); |
| NumberOfModules = ApMailboxInfo.Fields.ModuleType + 1; |
| |
| // Collect cpu core info |
| for (Socket = 0; Socket < NumberOfSockets; Socket++) { |
| if (IsProcessorPresent (Socket, StdHeader)) { |
| for (Module = 0; Module < NumberOfModules; Module++) { |
| if (GetGivenModuleCoreRange (Socket, Module, &LowCore, &HighCore, StdHeader)) { |
| // Get the highest and lowest core count in all nodes |
| TotalEnabledCoresOnNode = HighCore - LowCore + 1; |
| if (TotalEnabledCoresOnNode < MinCoreCountOnNode) { |
| MinCoreCountOnNode = TotalEnabledCoresOnNode; |
| } |
| if (TotalEnabledCoresOnNode > MaxCoreCountOnNode) { |
| MaxCoreCountOnNode = TotalEnabledCoresOnNode; |
| } |
| EnabledComputeUnit = TotalEnabledCoresOnNode; |
| switch (GetComputeUnitMapping (StdHeader)) { |
| case AllCoresMapping: |
| // All cores are in their own compute unit. |
| break; |
| case EvenCoresMapping: |
| // Cores are paired in compute units. |
| CoreNumPerComputeUnit = 2; |
| EnabledComputeUnit = (TotalEnabledCoresOnNode / 2); |
| break; |
| default: |
| ASSERT (FALSE); |
| } |
| // Get minimum of compute unit. This will either be the minimum number of cores (AllCoresMapping), |
| // or less (EvenCoresMapping). |
| if (EnabledComputeUnit < MinNumOfComputeUnit) { |
| MinNumOfComputeUnit = EnabledComputeUnit; |
| } |
| } |
| } |
| } |
| } |
| |
| // Get LeveledCores |
| switch (CoreLevelMode) { |
| case CORE_LEVEL_LOWEST: |
| if (MinCoreCountOnNode == MaxCoreCountOnNode) { |
| return (AGESA_SUCCESS); |
| } |
| LeveledCores = (MinCoreCountOnNode / CoreNumPerComputeUnit) * CoreNumPerComputeUnit; |
| break; |
| case CORE_LEVEL_TWO: |
| LeveledCores = 2 / NumberOfModules; |
| if (LeveledCores != 0) { |
| LeveledCores = (LeveledCores <= MinCoreCountOnNode) ? LeveledCores : MinCoreCountOnNode; |
| } else { |
| return (AGESA_WARNING); |
| } |
| if ((LeveledCores * NumberOfModules) != 2) { |
| PutEventLog ( |
| AGESA_WARNING, |
| CPU_WARNING_ADJUSTED_LEVELING_MODE, |
| 2, (LeveledCores * NumberOfModules), 0, 0, StdHeader |
| ); |
| } |
| break; |
| case CORE_LEVEL_POWER_OF_TWO: |
| // Level to power of 2 (1, 2, 4, 8...) |
| LeveledCores = 1; |
| while (MinCoreCountOnNode >= (LeveledCores * 2)) { |
| LeveledCores = LeveledCores * 2; |
| } |
| break; |
| case CORE_LEVEL_COMPUTE_UNIT: |
| // Level cores to one core per compute unit, with additional reduction to level |
| // all processors to match the processor with the minimum number of cores. |
| if (CoreNumPerComputeUnit == 1) { |
| // If there is one core per compute unit, this is the same as CORE_LEVEL_LOWEST. |
| if (MinCoreCountOnNode == MaxCoreCountOnNode) { |
| return (AGESA_SUCCESS); |
| } |
| LeveledCores = MinCoreCountOnNode; |
| } else { |
| // If there are more than one core per compute unit, level to the number of compute units. |
| LeveledCores = MinNumOfComputeUnit; |
| } |
| break; |
| case CORE_LEVEL_ONE: |
| LeveledCores = 1; |
| if (NumberOfModules > 1) { |
| PutEventLog ( |
| AGESA_WARNING, |
| CPU_WARNING_ADJUSTED_LEVELING_MODE, |
| 1, NumberOfModules, 0, 0, StdHeader |
| ); |
| } |
| break; |
| case CORE_LEVEL_THREE: |
| case CORE_LEVEL_FOUR: |
| case CORE_LEVEL_FIVE: |
| case CORE_LEVEL_SIX: |
| case CORE_LEVEL_SEVEN: |
| case CORE_LEVEL_EIGHT: |
| case CORE_LEVEL_NINE: |
| case CORE_LEVEL_TEN: |
| case CORE_LEVEL_ELEVEN: |
| case CORE_LEVEL_TWELVE: |
| case CORE_LEVEL_THIRTEEN: |
| case CORE_LEVEL_FOURTEEN: |
| case CORE_LEVEL_FIFTEEN: |
| // MCM processors can not have an odd number of cores. For an odd CORE_LEVEL_N, MCM processors will be |
| // leveled as though CORE_LEVEL_N+1 was chosen. |
| // Processors with compute units disable all cores in an entire compute unit at a time, or on an MCM processor, |
| // two compute units at a time. For example, on an SCM processor with two cores per compute unit, the effective |
| // explicit levels are CORE_LEVEL_ONE, CORE_LEVEL_TWO, CORE_LEVEL_FOUR, CORE_LEVEL_SIX, and |
| // CORE_LEVEL_EIGHT. The same example for an MCM processor with two cores per compute unit has effective |
| // explicit levels of CORE_LEVEL_TWO, CORE_LEVEL_FOUR, CORE_LEVEL_EIGHT, and CORE_LEVEL_TWELVE. |
| RequestedCores = CoreLevelMode - CORE_LEVEL_THREE + 3; |
| LeveledCores = (RequestedCores + NumberOfModules - 1) / NumberOfModules; |
| LeveledCores = (LeveledCores / CoreNumPerComputeUnit) * CoreNumPerComputeUnit; |
| LeveledCores = (LeveledCores <= MinCoreCountOnNode) ? LeveledCores : MinCoreCountOnNode; |
| if (LeveledCores != 1) { |
| LeveledCores = (LeveledCores / CoreNumPerComputeUnit) * CoreNumPerComputeUnit; |
| } |
| if ((LeveledCores * NumberOfModules * CoreNumPerComputeUnit) != RequestedCores) { |
| PutEventLog ( |
| AGESA_WARNING, |
| CPU_WARNING_ADJUSTED_LEVELING_MODE, |
| RequestedCores, (LeveledCores * NumberOfModules * CoreNumPerComputeUnit), 0, 0, StdHeader |
| ); |
| } |
| break; |
| default: |
| ASSERT (FALSE); |
| } |
| |
| // Set down core register |
| for (Socket = 0; Socket < NumberOfSockets; Socket++) { |
| if (IsProcessorPresent (Socket, StdHeader)) { |
| GetFeatureServicesOfSocket (&CoreLevelingFamilyServiceTable, Socket, (const VOID **)&FamilySpecificServices, StdHeader); |
| if (FamilySpecificServices != NULL) { |
| for (Module = 0; Module < NumberOfModules; Module++) { |
| RegUpdated = FamilySpecificServices->SetDownCoreRegister (FamilySpecificServices, &Socket, &Module, &LeveledCores, CoreLevelMode, StdHeader); |
| // If the down core register is updated, trigger a warm reset. |
| if (RegUpdated) { |
| GetWarmResetFlag (StdHeader, &Request); |
| Request.RequestBit = TRUE; |
| Request.StateBits = Request.PostStage - 1; |
| SetWarmResetFlag (StdHeader, &Request); |
| } |
| } |
| } |
| } |
| } |
| |
| return (AGESA_SUCCESS); |
| } |
| |
| |
| CONST CPU_FEATURE_DESCRIPTOR ROMDATA CpuFeatureCoreLeveling = |
| { |
| CoreLeveling, |
| (CPU_FEAT_AFTER_PM_INIT), |
| IsCoreLevelingEnabled, |
| CoreLevelingAtEarly |
| }; |
| |
| /*---------------------------------------------------------------------------------------- |
| * L O C A L F U N C T I O N S |
| *---------------------------------------------------------------------------------------- |
| */ |
| |