/* $NoKeywords:$ */ /** * @file * * SMU access routine * * * * @xrefitem bom "File Content Label" "Release Content" * @e project: AGESA * @e sub-project: GNB * @e \$Revision: 39275 $ @e \$Date: 2010-10-09 08:22:05 +0800 (Sat, 09 Oct 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. * * *************************************************************************** * */ /*---------------------------------------------------------------------------------------- * M O D U L E S U S E D *---------------------------------------------------------------------------------------- */ #include "AGESA.h" #include "amdlib.h" #include "Ids.h" #include "Gnb.h" #include GNB_MODULE_DEFINITIONS (GnbCommonLib) #include "OptionGnb.h" #include "NbSmuLib.h" #include "GnbRegistersON.h" #include "S3SaveState.h" #include "Filecode.h" #define FILECODE PROC_GNB_NB_NBSMULIB_FILECODE /*---------------------------------------------------------------------------------------- * D E F I N I T I O N S A N D M A C R O S *---------------------------------------------------------------------------------------- */ /// Efuse write packet typedef struct { SMUx0B_x8600_STRUCT SMUx0B_x8600; ///< Reg SMUx0B_x8600 SMUx0B_x8604_STRUCT SMUx0B_x8604; ///< Reg SMUx0B_x8604 SMUx0B_x8608_STRUCT SMUx0B_x8608; ///< Reg SMUx0B_x8605 } MBUS; /*---------------------------------------------------------------------------------------- * T Y P E D E F S A N D S T R U C T U R E S *---------------------------------------------------------------------------------------- */ /*---------------------------------------------------------------------------------------- * 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 *---------------------------------------------------------------------------------------- */ /*----------------------------------------------------------------------------------------*/ /** * SMU indirect register read * * * * @param[in] Address Register Address * @param[in] Width Data width for read * @param[out] Value Pointer read value * @param[in] StdHeader Pointer to standard configuration */ VOID NbSmuIndirectRead ( IN UINT8 Address, IN ACCESS_WIDTH Width, OUT VOID *Value, IN AMD_CONFIG_PARAMS *StdHeader ) { D0F0x64_x4D_STRUCT D0F0x64_x4D; UINT32 Data; GnbLibPciIndirectRead ( MAKE_SBDFO (0, 0, 0, 0, D0F0x60_ADDRESS), D0F0x64_x4D_ADDRESS | IOC_WRITE_ENABLE, AccessWidth32, &D0F0x64_x4D.Value, StdHeader ); D0F0x64_x4D.Field.ReqType = 0; D0F0x64_x4D.Field.SmuAddr = Address; if (Width == AccessS3SaveWidth32 || Width == AccessWidth32) { D0F0x64_x4D.Field.SmuAddr += 1; } D0F0x64_x4D.Field.ReqToggle = !D0F0x64_x4D.Field.ReqToggle; GnbLibPciIndirectWrite ( MAKE_SBDFO (0, 0, 0, 0, D0F0x60_ADDRESS), D0F0x64_x4D_ADDRESS | IOC_WRITE_ENABLE, (Width >= AccessS3SaveWidth8) ? AccessS3SaveWidth32 : AccessWidth32, &D0F0x64_x4D.Value, StdHeader ); GnbLibPciIndirectRead ( MAKE_SBDFO (0, 0, 0, 0, D0F0x60_ADDRESS), D0F0x64_x4E_ADDRESS | IOC_WRITE_ENABLE, (Width >= AccessS3SaveWidth8) ? AccessS3SaveWidth32 : AccessWidth32, &Data, StdHeader ); switch (Width) { case AccessWidth16: //no break; intended to fall through case AccessS3SaveWidth16: *(UINT16 *) Value = (UINT16) Data; break; case AccessWidth32: //no break; intended to fall through case AccessS3SaveWidth32: *(UINT32 *) Value = Data; break; default: ASSERT (FALSE); } } /*----------------------------------------------------------------------------------------*/ /** * SMU indirect register read * * * * @param[in] Address Register Address * @param[in] Width Access width * @param[in] Mask Data mask for compare * @param[in] CompateData Compare data * @param[in] StdHeader Pointer to standard configuration */ VOID NbSmuIndirectPoll ( IN UINT8 Address, IN ACCESS_WIDTH Width, IN UINT32 Mask, IN UINT32 CompateData, IN AMD_CONFIG_PARAMS *StdHeader ) { UINT32 Value; do { NbSmuIndirectRead ( Address, Width, &Value, StdHeader ); } while ((Value & Mask) != CompateData); } /*----------------------------------------------------------------------------------------*/ /** * SMU indirect register write * * * * @param[in] Address Register Address * @param[in] Width Data width for write * @param[in] Value Pointer to write value * @param[in] StdHeader Pointer to standard configuration */ VOID NbSmuIndirectWriteEx ( IN UINT8 Address, IN ACCESS_WIDTH Width, IN VOID *Value, IN AMD_CONFIG_PARAMS *StdHeader ) { D0F0x64_x4D_STRUCT D0F0x64_x4D; ASSERT (Width != AccessWidth8); ASSERT (Width != AccessS3SaveWidth8); GnbLibPciIndirectRead ( MAKE_SBDFO (0, 0, 0, 0, D0F0x60_ADDRESS), D0F0x64_x4D_ADDRESS | IOC_WRITE_ENABLE, AccessWidth32, &D0F0x64_x4D.Value, StdHeader ); D0F0x64_x4D.Field.ReqType = 0x1; D0F0x64_x4D.Field.SmuAddr = Address; D0F0x64_x4D.Field.ReqToggle = (!D0F0x64_x4D.Field.ReqToggle); D0F0x64_x4D.Field.WriteData = ((UINT16 *) Value) [0]; GnbLibPciIndirectWrite ( MAKE_SBDFO (0, 0, 0, 0, D0F0x60_ADDRESS), D0F0x64_x4D_ADDRESS | IOC_WRITE_ENABLE, (Width >= AccessS3SaveWidth8) ? AccessS3SaveWidth32 : AccessWidth32, &D0F0x64_x4D.Value, StdHeader ); if (LibAmdAccessWidth (Width) <= 2) { return; } D0F0x64_x4D.Field.ReqType = 0x1; D0F0x64_x4D.Field.SmuAddr = Address + 1; D0F0x64_x4D.Field.ReqToggle = (!D0F0x64_x4D.Field.ReqToggle); D0F0x64_x4D.Field.WriteData = ((UINT16 *) Value)[1]; GnbLibPciIndirectWrite ( MAKE_SBDFO (0, 0, 0, 0, D0F0x60_ADDRESS), D0F0x64_x4D_ADDRESS | IOC_WRITE_ENABLE, (Width >= AccessS3SaveWidth8) ? AccessS3SaveWidth32 : AccessWidth32, &D0F0x64_x4D.Value, StdHeader ); } /*----------------------------------------------------------------------------------------*/ /** * SMU indirect register write * * * * @param[in] Address Register Address * @param[in] Width Data width for write * @param[in] Value Pointer to write value * @param[in] StdHeader Pointer to standard configuration */ VOID NbSmuIndirectWrite ( IN UINT8 Address, IN ACCESS_WIDTH Width, IN VOID *Value, IN AMD_CONFIG_PARAMS *StdHeader ) { if (Width >= AccessS3SaveWidth8) { SMU_INDIRECT_WRITE_DATA Data; Data.Address = Address; Data.Width = Width; Data.Value = *((UINT32*) Value); S3_SAVE_DISPATCH (StdHeader, S3DispatchGnbSmuIndirectWrite, sizeof (SMU_INDIRECT_WRITE_DATA), &Data); Width = Width - (AccessS3SaveWidth8 - AccessWidth8); } NbSmuIndirectWriteEx (Address, Width, Value, StdHeader); } /*----------------------------------------------------------------------------------------*/ /** * SMU Service request for S3 script * * * @param[in] StdHeader Standard configuration header * @param[in] ContextLength Not used * @param[in] Context Pointer to service request ID */ VOID NbSmuIndirectWriteS3Script ( IN AMD_CONFIG_PARAMS *StdHeader, IN UINT16 ContextLength, IN VOID* Context ) { SMU_INDIRECT_WRITE_DATA *Data; Data = (SMU_INDIRECT_WRITE_DATA*) Context; NbSmuIndirectWriteEx (Data->Address, Data->Width, &Data->Value, StdHeader); } /*----------------------------------------------------------------------------------------*/ /** * SMU RAM mapped register write * * * * @param[in] Address Register Address * @param[in] Value Data pointer for write * @param[in] Count Number of registers to write * @param[in] S3Save Save for S3 (True/False) * @param[in] StdHeader Standard configuration header */ VOID NbSmuRcuRegisterWrite ( IN UINT16 Address, IN UINT32 *Value, IN UINT32 Count, IN BOOLEAN S3Save, IN AMD_CONFIG_PARAMS *StdHeader ) { UINT32 CurrentAddress; CurrentAddress = Address; NbSmuIndirectWrite ( SMUx0B_ADDRESS, S3Save ? AccessS3SaveWidth16 : AccessWidth16, &Address, StdHeader ); while (Count-- > 0) { IDS_HDT_CONSOLE (NB_SMUREG_TRACE, " *WR SMUx0B:0x%x = 0x%x\n", CurrentAddress, *Value); NbSmuIndirectWrite ( SMUx05_ADDRESS, S3Save ? AccessS3SaveWidth32 : AccessWidth32, Value++, StdHeader ); CurrentAddress += 4; } } /*----------------------------------------------------------------------------------------*/ /** * SMU RAM mapped register read * * * * @param[in] Address Register Address * @param[out] Value Pointer read value * @param[in] Count Number of registers to read * @param[in] StdHeader Pointer to standard configuration */ VOID NbSmuRcuRegisterRead ( IN UINT16 Address, OUT UINT32 *Value, IN UINT32 Count, IN AMD_CONFIG_PARAMS *StdHeader ) { NbSmuIndirectWrite (SMUx0B_ADDRESS, AccessWidth16, &Address, StdHeader); while (Count-- > 0) { NbSmuIndirectRead (SMUx05_ADDRESS, AccessWidth32, Value++, StdHeader); } } /*----------------------------------------------------------------------------------------*/ /** * SMU Service request Ext * * * @param[in] RequestId request ID * @param[in] Flags Flags * @param[in] StdHeader Standard configuration header */ VOID NbSmuServiceRequestEx ( IN UINT8 RequestId, IN UINT8 Flags, IN AMD_CONFIG_PARAMS *StdHeader ) { SMUx03_STRUCT SMUx03; SMUx03.Value = 0; SMUx03.Field.IntReq = 1; SMUx03.Field.ServiceIndex = RequestId; NbSmuIndirectWrite (SMUx03_ADDRESS, AccessWidth32, &SMUx03.Value, StdHeader); if ((Flags & SMU_EXT_SERVICE_FLAGS_POLL_ACK) != 0) { NbSmuIndirectPoll (SMUx03_ADDRESS, AccessWidth32, BIT1, BIT1, StdHeader); // Wait till IntAck } if ((Flags & SMU_EXT_SERVICE_FLAGS_POLL_DONE) != 0) { NbSmuIndirectPoll (SMUx03_ADDRESS, AccessWidth32, BIT2, BIT2, StdHeader); // Wait till IntDone } SMUx03.Value = 0; // Clear IRQ register NbSmuIndirectWrite (SMUx03_ADDRESS, AccessWidth32, &SMUx03.Value, StdHeader); } /*----------------------------------------------------------------------------------------*/ /** * SMU Service request * * * @param[in] RequestId request ID * @param[in] S3Save Save for S3 (True/False) * @param[in] StdHeader Standard configuration header */ VOID NbSmuServiceRequest ( IN UINT8 RequestId, IN BOOLEAN S3Save, IN AMD_CONFIG_PARAMS *StdHeader ) { IDS_HDT_CONSOLE (GNB_TRACE, "NbSmuServiceRequest Enter [0x%02x]\n", RequestId); if (S3Save) { S3_SAVE_DISPATCH (StdHeader, S3DispatchGnbSmuServiceRequest, sizeof (RequestId), &RequestId); } NbSmuServiceRequestEx ( RequestId, SMU_EXT_SERVICE_FLAGS_POLL_ACK | SMU_EXT_SERVICE_FLAGS_POLL_DONE, StdHeader ); IDS_HDT_CONSOLE (GNB_TRACE, "NbSmuServiceRequest Exit\n"); } /*----------------------------------------------------------------------------------------*/ /** * SMU Service request for S3 script * * * @param[in] StdHeader Standard configuration header * @param[in] ContextLength Not used * @param[in] Context Pointer to service request ID */ VOID NbSmuServiceRequestS3Script ( IN AMD_CONFIG_PARAMS *StdHeader, IN UINT16 ContextLength, IN VOID* Context ) { NbSmuServiceRequest (*((UINT8*) Context), FALSE, StdHeader); } /*----------------------------------------------------------------------------------------*/ /** * SMU Read FCR register * * * @param[in] Address FCR Address * @param[in] StdHeader Standard configuration header */ UINT32 NbSmuReadEfuse ( IN UINT32 Address, IN AMD_CONFIG_PARAMS *StdHeader ) { UINT32 Value; NbSmuSrbmRegisterRead (Address, &Value, StdHeader); Value = (Value >> 24) | (Value << 24) | ((Value >> 8) & 0xFF00) | ((Value << 8) & 0xFF0000); return Value; } /*----------------------------------------------------------------------------------------*/ /** * SMU Read arbitrary fuse field * * * @param[in] Chain Address * @param[in] Offset Offcet * @param[in] Length Length * @param[in] StdHeader Standard configuration header */ UINT32 NbSmuReadEfuseField ( IN UINT8 Chain, IN UINT16 Offset, IN UINT8 Length, IN AMD_CONFIG_PARAMS *StdHeader ) { UINT32 Value; UINT32 Result; UINT32 Address; UINT16 Shift; ASSERT (Length <= 32); ASSERT (Chain <= 0xff); Shift = (Offset - (Offset & ~0x7)); Address = 0xFE000000 | (Chain << 12) | (Offset >> 3); Value = NbSmuReadEfuse (Address, StdHeader); Result = Value >> Shift; if ((Shift + Length) > 32) { Value = NbSmuReadEfuse (Address + 1, StdHeader); Result |= (Value << (32 - Shift)); } Result &= ((1 << Length) - 1); return Value; } /*----------------------------------------------------------------------------------------*/ /** * SMU SRBM (GMM) register read * * * * @param[in] Address Register Address * @param[out] Value Pointer read value * @param[in] StdHeader Pointer to standard configuration */ VOID NbSmuSrbmRegisterRead ( IN UINT32 Address, OUT UINT32 *Value, IN AMD_CONFIG_PARAMS *StdHeader ) { MBUS Mbus; Mbus.SMUx0B_x8600.Value = (0x8650 << SMUx0B_x8600_MemAddr_7_0__OFFSET) | (1 << SMUx0B_x8600_TransactionCount_OFFSET); Mbus.SMUx0B_x8604.Value = (4 << SMUx0B_x8604_Txn1TransferLength_7_0__OFFSET); Mbus.SMUx0B_x8608.Value = (UINT32) (3 << SMUx0B_x8608_Txn1Tsize_OFFSET); Mbus.SMUx0B_x8600.Field.Txn1MBusAddr_7_0_ = Address & 0xff; Mbus.SMUx0B_x8604.Field.Txn1MBusAddr_15_8_ = (Address >> 8) & 0xff; Mbus.SMUx0B_x8604.Field.Txn1MBusAddr_23_16_ = (Address >> 16) & 0xff; Mbus.SMUx0B_x8604.Field.Txn1MBusAddr_31_24_ = (Address >> 24) & 0xff; NbSmuRcuRegisterWrite (SMUx0B_x8600_ADDRESS, (UINT32*) &Mbus, 3, FALSE, StdHeader); NbSmuServiceRequest (0x0B, FALSE, StdHeader); NbSmuRcuRegisterRead (SMUx0B_x8650_ADDRESS, Value, 1, StdHeader); } /*----------------------------------------------------------------------------------------*/ /** * SMU SRBM (GMM) register write * * * * @param[in] Address Register Address * @param[in] Value Data pointer for write * @param[in] S3Save Save for S3 (True/False) * @param[in] StdHeader Standard configuration header */ VOID NbSmuSrbmRegisterWrite ( IN UINT32 Address, IN UINT32 *Value, IN BOOLEAN S3Save, IN AMD_CONFIG_PARAMS *StdHeader ) { MBUS Mbus; IDS_HDT_CONSOLE (NB_SMUREG_TRACE, " *WR SRBM (GMM):0x%x = 0x%x\n", Address, *Value); Mbus.SMUx0B_x8600.Value = (0x8650 << SMUx0B_x8600_MemAddr_7_0__OFFSET) | (1 << SMUx0B_x8600_TransactionCount_OFFSET); Mbus.SMUx0B_x8604.Value = (4 << SMUx0B_x8604_Txn1TransferLength_7_0__OFFSET); Mbus.SMUx0B_x8608.Value = (UINT32) (3 << SMUx0B_x8608_Txn1Tsize_OFFSET); Mbus.SMUx0B_x8608.Field.Txn1Mode = 0x1; Mbus.SMUx0B_x8600.Field.Txn1MBusAddr_7_0_ = Address & 0xff; Mbus.SMUx0B_x8604.Field.Txn1MBusAddr_15_8_ = (Address >> 8) & 0xff; Mbus.SMUx0B_x8604.Field.Txn1MBusAddr_23_16_ = (Address >> 16) & 0xff; Mbus.SMUx0B_x8604.Field.Txn1MBusAddr_31_24_ = (Address >> 24) & 0xff; NbSmuRcuRegisterWrite (SMUx0B_x8600_ADDRESS, (UINT32*) &Mbus, 3, S3Save, StdHeader); NbSmuRcuRegisterWrite (SMUx0B_x8650_ADDRESS, Value, 1, S3Save, StdHeader); NbSmuServiceRequest (0x0B, S3Save, StdHeader); } /*----------------------------------------------------------------------------------------*/ /** * SMU firmware download * * * * @param[in] StdHeader Pointer to Standard configuration * @param[in] Firmware Pointer to SMU firmware header * @retval AGESA_STATUS */ VOID NbSmuFirmwareDownload ( IN SMU_FIRMWARE_HEADER *Firmware, IN AMD_CONFIG_PARAMS *StdHeader ) { UINTN Index; SMUx01_STRUCT SMUx01; NbSmuServiceRequestEx (0x10, SMU_EXT_SERVICE_FLAGS_POLL_ACK , StdHeader); SMUx01.Value = (1 << SMUx01_RamSwitch_OFFSET) | (1 << SMUx01_VectorOverride_OFFSET); NbSmuIndirectWrite (SMUx01_ADDRESS, AccessWidth32, &SMUx01.Value, StdHeader); for (Index = 0; Index < Firmware->NumberOfBlock; Index++) { NbSmuRcuRegisterWrite ( (Firmware->BlockArray)[Index].Address, (Firmware->BlockArray)[Index].Data, (Firmware->BlockArray)[Index].Length, FALSE, StdHeader ); } SMUx01.Value = (1 << SMUx01_Reset_OFFSET) | (1 << SMUx01_VectorOverride_OFFSET); NbSmuIndirectWrite (SMUx01_ADDRESS, AccessWidth32, &SMUx01.Value, StdHeader); } /*----------------------------------------------------------------------------------------*/ /** * SMU firmware revision * * * * @param[in] StdHeader Pointer to Standard configuration * @retval Firmware revision info */ SMU_FIRMWARE_REV NbSmuFirmwareRevision ( IN AMD_CONFIG_PARAMS *StdHeader ) { SMU_FIRMWARE_REV Revision; UINT32 FmRev; NbSmuRcuRegisterRead ( 0x830C, &FmRev, 1, StdHeader ); Revision.MajorRev = ((UINT16*)&FmRev) [1]; Revision.MinorRev = ((UINT16*)&FmRev) [0]; return Revision; }