aboutsummaryrefslogtreecommitdiff
path: root/src/vendorcode/amd/cimx/rd890/nbIommu.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/vendorcode/amd/cimx/rd890/nbIommu.c')
-rw-r--r--src/vendorcode/amd/cimx/rd890/nbIommu.c1737
1 files changed, 1737 insertions, 0 deletions
diff --git a/src/vendorcode/amd/cimx/rd890/nbIommu.c b/src/vendorcode/amd/cimx/rd890/nbIommu.c
new file mode 100644
index 0000000000..705b1a82fd
--- /dev/null
+++ b/src/vendorcode/amd/cimx/rd890/nbIommu.c
@@ -0,0 +1,1737 @@
+/**
+ * @file
+ *
+ * Routines for IOMMU.
+ *
+ * Implement the IOMMU init and ACPI feature.
+ *
+ * @xrefitem bom "File Content Label" "Release Content"
+ * @e project: CIMx-NB
+ * @e sub-project:
+ * @e \$Revision:$ @e \$Date:$
+ *
+ */
+/*****************************************************************************
+ *
+ * Copyright (C) 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 "NbPlatform.h"
+#include "amdDebugOutLib.h"
+#include "amdSbLib.h"
+
+#define Int32FromChar(a,b,c,d) ((a) << 0 | (b) << 8 | (c) << 16 | (d) << 24)
+
+/*----------------------------------------------------------------------------------------
+ * R D 8 9 0 / S D A T A
+ *----------------------------------------------------------------------------------------
+ */
+
+// IOMMU Initialization
+
+INDIRECT_REG_ENTRY
+CONST
+STATIC
+IommuL1Table[] = {
+ // 01. 0x0C [30:28]=7 L1VirtOrderQueues Increase maximum number virtual queues
+ // for all devices
+ { L1CFG_SEL_WR_EN | L1CFG_SEL_GPP1 | L1REG_0C, 0x8FFFFFFF, 0x70000000 },
+ { L1CFG_SEL_WR_EN | L1CFG_SEL_GPP2 | L1REG_0C, 0x8FFFFFFF, 0x70000000 },
+ { L1CFG_SEL_WR_EN | L1CFG_SEL_SB | L1REG_0C, 0x8FFFFFFF, 0x70000000 },
+ { L1CFG_SEL_WR_EN | L1CFG_SEL_GPP3A | L1REG_0C, 0x8FFFFFFF, 0x70000000 },
+ { L1CFG_SEL_WR_EN | L1CFG_SEL_GPP3B | L1REG_0C, 0x8FFFFFFF, 0x70000000 },
+ { L1CFG_SEL_WR_EN | L1CFG_SEL_VC1 | L1REG_0C, 0x8FFFFFFF, 0x70000000 },
+ // 02. 0x07 [11] L1DEBUG_1 Multiple error logs possible
+ { L1CFG_SEL_WR_EN | L1CFG_SEL_GPP1 | L1REG_07, (UINT32)~(BIT8 + BIT9 + BIT10), BIT11 + BIT5 },
+ { L1CFG_SEL_WR_EN | L1CFG_SEL_GPP2 | L1REG_07, (UINT32)~(BIT8 + BIT9 + BIT10), BIT11 + BIT5},
+ { L1CFG_SEL_WR_EN | L1CFG_SEL_SB | L1REG_07, (UINT32)~(BIT8 + BIT9 + BIT10), BIT11 + BIT5},
+ { L1CFG_SEL_WR_EN | L1CFG_SEL_GPP3A | L1REG_07, (UINT32)~(BIT8 + BIT9 + BIT10), BIT11 + BIT5},
+ { L1CFG_SEL_WR_EN | L1CFG_SEL_GPP3B | L1REG_07, (UINT32)~(BIT8 + BIT9 + BIT10), BIT11 + BIT5},
+ { L1CFG_SEL_WR_EN | L1CFG_SEL_VC1 | L1REG_07, (UINT32)~(BIT8 + BIT9 + BIT10), BIT11 + BIT5},
+ // 02. 0x06 [0] L1DEBUG_0 Phantom function disable
+ { L1CFG_SEL_WR_EN | L1CFG_SEL_GPP1 | L1REG_06, (UINT32)~BIT0, 0 },
+ { L1CFG_SEL_WR_EN | L1CFG_SEL_GPP2 | L1REG_06, (UINT32)~BIT0, 0 },
+ { L1CFG_SEL_WR_EN | L1CFG_SEL_SB | L1REG_06, (UINT32)~BIT0, 0 },
+ { L1CFG_SEL_WR_EN | L1CFG_SEL_GPP3A | L1REG_06, (UINT32)~BIT0, 0 },
+ { L1CFG_SEL_WR_EN | L1CFG_SEL_GPP3B | L1REG_06, (UINT32)~BIT0, 0 },
+ { L1CFG_SEL_WR_EN | L1CFG_SEL_VC1 | L1REG_06, (UINT32)~BIT0, 0 }
+};
+
+INDIRECT_REG_ENTRY
+CONST
+STATIC
+IommuL2Table[] = {
+ // 01. 0x0C [29]=1 IFifoClientPriority Set attribute to VC1 L1 client high priority
+ { L2CFG_SEL_WR_EN | L2REG_0C, 0xD0000000, 0x20000000 },
+ // 02. 0x10 [9:8]=2 DTCInvalidationSel DTC cache invalidation sequential precise
+ { L2CFG_SEL_WR_EN | L2REG_10, 0xFFFFFC00, 0x00000200 },
+ // 03. 0x14 [9:8]=2 ITCInvalidationSel ... cache invalidation sequential precise
+ { L2CFG_SEL_WR_EN | L2REG_14, 0xFFFFFC00, 0x00000200 },
+ // 04. 0x18 [9:8]=2 IPTCAInvalidationSel ... cache invalidation sequential precise
+ { L2CFG_SEL_WR_EN | L2REG_18, 0xFFFFFC00, 0x00000200 },
+ // 05. 0x1C [9:8]=2 IPTCBInvalidationSel ... cache invalidation sequential precise
+ { L2CFG_SEL_WR_EN | L2REG_1C, 0xFFFFFC00, 0x00000200 },
+ // 06. 0x50 [9:8]=2 PDCInvalidationSel ... cache invalidation sequential precise
+ { L2CFG_SEL_WR_EN | L2REG_50, 0xFFFFFC00, 0x00000200 },
+ // 07. 0x10 [4]=1 DTCParityEn DTC cache parity protection
+ { L2CFG_SEL_WR_EN | L2REG_10, (UINT32)~BIT4, BIT4 },
+ // 08. 0x14 [4]=1 ITCParityEn ... cache parity protection
+ { L2CFG_SEL_WR_EN | L2REG_14, (UINT32)~BIT4, BIT4 },
+ // 09. 0x18 [4]=1 PTCAParityEn ... cache parity protection
+ { L2CFG_SEL_WR_EN | L2REG_18, (UINT32)~BIT4, BIT4 },
+ // 10. 0x1C [4]=1 PTCBParityEn ... cache parity protection
+ { L2CFG_SEL_WR_EN | L2REG_1C, (UINT32)~BIT4, BIT4 },
+ // 11. 0x50 [4]=1 PDCParityEn ... cache parity protection
+ { L2CFG_SEL_WR_EN | L2REG_50, (UINT32)~BIT4, BIT4 },
+ // 12. 0x80 [0]=1 ERRRuleLock0 Lock fault detection rule sets
+ // 0x30 [0]=1 ERRRuleLock1
+ { L2CFG_SEL_WR_EN | L2REG_80, (UINT32)~BIT0, BIT0 },
+ { L2CFG_SEL_WR_EN | L2REG_30, (UINT32)~BIT0, BIT0 },
+ // 13. 0x56 [2]=0 L2_CP_CONTROL Disable CP flush on invalidation
+ // 0x56 [1]=1 L2_CP_CONTROL Enable CP flush on wait
+ { L2CFG_SEL_WR_EN | L2REG_56, 0xFFFFFFF9, BIT1 },
+ // A21
+ { L2CFG_SEL_WR_EN | L2REG_06, 0xFFFFFFFF, BIT6 + BIT7 + BIT5 + BIT8 },
+ { L2CFG_SEL_WR_EN | L2REG_47, 0xFFFFFFFF, BIT1 + BIT3 + BIT0 + BIT4 + BIT5 },
+ { L2CFG_SEL_WR_EN | L2REG_07, 0xFFFFFFFF, BIT1 + BIT2 + BIT3 + BIT4 + BIT5 + BIT8 + BIT6},
+
+};
+
+// IOMMU ACPI Initialization
+
+IOMMU_IVRS_HEADER
+STATIC
+RD890S_DfltHeader = {
+// 'SRVI',
+ Int32FromChar ('S', 'R', 'V', 'I'),
+ 48,
+ 1,
+ 0,
+ {'A', 'M', 'D', ' ', ' ', 0},
+ {'R', 'D', '8', '9', '0', 'S', 0, 0},
+ {'1', ' ', ' ', 0},
+ {'A','M','D',' '},
+ {'1', ' ', ' ', 0},
+ 0,
+ 0
+};
+
+IOMMU_EXCLUSIONTABLE
+STATIC
+RD890S_DfltExclusion = {
+ sizeof (UINTN) + sizeof (IOMMU_EXCLUSIONRANGE) * 0,
+ {{0, 0}}
+};
+
+IOMMU_DEVICELIST
+STATIC
+RD890S_DfltDevices = {
+ (sizeof (UINT16) + sizeof (UINT16) * 12),
+ {
+ DEVICEID_NB, // Type 2 entry, Device 0, Func 0 <-- NB all functions
+ DEVICEID_GPP1_0, // Type 2 entry, Device 2, Func 0 <-- GPP1 port 0
+ DEVICEID_GPP1_1, // Type 2 entry, Device 3, Func 0 <-- GPP1 port 1
+ DEVICEID_GPP3A_0, // Type 2 entry, Device 4, Func 0 <-- GPP3a port 0
+ DEVICEID_GPP3A_1, // Type 2 entry, Device 5, Func 0 <-- GPP3a port 1
+ DEVICEID_GPP3A_2, // Type 2 entry, Device 6, Func 0 <-- GPP3a port 2
+ DEVICEID_GPP3A_3, // Type 2 entry, Device 7, Func 0 <-- GPP3a port 3
+ DEVICEID_GPP3A_4, // Type 2 entry, Device 9, Func 0 <-- GPP3a port 4
+ DEVICEID_GPP3A_5, // Type 2 entry, Device A, Func 0 <-- GPP3a port 5
+ DEVICEID_GPP2_0, // Type 2 entry, Device B, Func 0 <-- GPP2 port 0
+ DEVICEID_GPP2_1, // Type 2 entry, Device C, Func 0 <-- GPP2 port 1
+ DEVICEID_GPP3B_0, // Type 2 entry, Device D, Func 0 <-- GPP3b port 0
+ }
+};
+
+/*----------------------------------------------------------------------------------------
+ * 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
+ *----------------------------------------------------------------------------------------
+*/
+
+// IOMMU Library
+
+BOOLEAN
+NbIommuEnabled (
+ IN OUT AMD_NB_CONFIG_BLOCK *ConfigPtr
+ );
+
+BOOLEAN
+IommuCheckEnable (
+ IN PCI_ADDR IommuPciAddress,
+ IN AMD_NB_CONFIG *pConfig
+ );
+
+BOOLEAN
+IommuCheckHp (
+ IN UINT16 DeviceId,
+ IN AMD_NB_CONFIG *pConfig
+ );
+
+BOOLEAN
+IommuCheckPhantom (
+ IN UINT16 DeviceId,
+ IN AMD_NB_CONFIG *pConfig
+ );
+
+UINT32
+IommuGetL1 (
+ IN UINT16 DeviceId
+ );
+
+UINT8
+IommuGetLog2 (
+ IN UINT32 Value
+ );
+
+VOID
+IommuRecordBusDevFuncInfo (
+ IN OUT IOMMU_PCI_TOPOLOGY *PciPtr,
+ IN UINT16 DeviceId,
+ IN AMD_NB_CONFIG *pConfig
+ );
+
+
+AGESA_STATUS
+IommuInit (
+ IN OUT AMD_NB_CONFIG *pConfig
+ );
+
+VOID
+IommuInitL2CacheControl (
+ IN IOMMU_PCI_TOPOLOGY *PciPtr,
+ IN OUT AMD_NB_CONFIG *pConfig
+);
+
+VOID
+IommuPlaceHeader (
+ IN OUT VOID *BufferPtr,
+ IN OUT AMD_NB_CONFIG *pConfig
+ );
+
+VOID
+IommuPlaceIvhdAndScanDevices (
+ IN OUT VOID *BufferPtr,
+ IN OUT AMD_NB_CONFIG *pConfig
+ );
+
+VOID
+IommuPlaceIvmdAndExclusions (
+ IN OUT VOID *BufferPtr,
+ IN OUT AMD_NB_CONFIG *pConfig
+ );
+
+VOID
+IommuIvhdNorthbridgeDevices (
+ IN OUT IOMMU_PCI_TOPOLOGY *PciPtr,
+ IN OUT IOMMU_IVHD_ENTRY *IvhdPtr,
+ IN OUT AMD_NB_CONFIG *pConfig
+ );
+
+VOID
+IommuIvhdSouthbridgeDevices (
+ IN OUT IOMMU_PCI_TOPOLOGY *PciPtr,
+ IN OUT IOMMU_IVHD_ENTRY *IvhdPtr,
+ IN OUT AMD_NB_CONFIG *pConfig
+ );
+
+VOID
+IommuIvhdApicsAndHpets (
+ IN OUT IOMMU_IVHD_ENTRY *IvhdPtr,
+ IN OUT AMD_NB_CONFIG *pConfig
+ );
+
+VOID
+IommuCreateDeviceEntry (
+ IN OUT IOMMU_PCI_TOPOLOGY *PciPtr,
+ IN UINT16 DeviceId,
+ IN OUT IOMMU_IVHD_ENTRY *IvhdPtr,
+ IN AMD_NB_CONFIG *pConfig
+ );
+
+VOID
+IommuCreate4ByteEntry (
+ IN UINT8 Type,
+ IN UINT8 Data,
+ IN UINT16 Word1,
+ IN OUT IOMMU_IVHD_ENTRY *IvhdPtr
+ );
+
+VOID
+IommuCreate8ByteEntry (
+ IN UINT8 Type,
+ IN UINT8 Data,
+ IN UINT16 Word1,
+ IN UINT8 Byte4,
+ IN UINT16 Word5,
+ IN UINT8 Byte7,
+ IN OUT IOMMU_IVHD_ENTRY *IvhdPtr
+ );
+
+VOID
+IommuFinalizeIvrs (
+ IN OUT VOID *BufferPtr,
+ IN AMD_NB_CONFIG *pConfig
+ );
+
+// IOMMU ACPI Final
+
+UINT64
+IommuGetApicBaseAddress (
+ IN VOID *DevicePtr,
+ IN AMD_NB_CONFIG *pConfig
+ );
+
+UINT8
+IommuGetApicId (
+ IN UINT64 BaseAddress,
+ IN VOID *MadtPtr,
+ IN AMD_NB_CONFIG *pConfig
+ );
+
+AGESA_STATUS
+NbIommuHwInit (
+ IN OUT AMD_NB_CONFIG_BLOCK *ConfigPtr
+ );
+
+AGESA_STATUS
+NbIommuHwTopologyInit (
+ IN OUT AMD_NB_CONFIG_BLOCK *ConfigPtr
+ );
+
+AGESA_STATUS
+IommuTopologyInit (
+ IN OUT AMD_NB_CONFIG *pConfig
+ );
+
+/*----------------------------------------------------------------------------------------
+ * E X P O R T E D
+ *----------------------------------------------------------------------------------------
+ */
+
+
+/*----------------------------------------------------------------------------------------*/
+/**
+ * Check if IOMMU enable on platform
+ *
+ * @param[in] ConfigPtr Northbridges configuration block pointer.
+ * @retval AGESA_SUCCESS IOMMU initialized and table created
+ * @retval AGESA_UNSUPPORTED IOMMU not enabled or not found
+ * @retval AGESA_ERROR IOMMU initialization failed.
+ *
+ */
+BOOLEAN
+NbIommuEnabled (
+ IN OUT AMD_NB_CONFIG_BLOCK *ConfigPtr
+ )
+{
+ UINT8 NorthbridgeId;
+ BOOLEAN Result;
+ Result = FALSE;
+ for (NorthbridgeId = 0; NorthbridgeId <= ConfigPtr->NumberOfNorthbridges; NorthbridgeId++) {
+ if (ConfigPtr->Northbridges[NorthbridgeId].pNbConfig->IommuBaseAddress != 0) {
+ Result = TRUE;
+ break;
+ }
+ }
+ return Result;
+}
+
+/*----------------------------------------------------------------------------------------*/
+/**
+ * Northbridge Iommu Initialization for all NB in system.
+ *
+ * @param[in] ConfigPtr Northbridges configuration block pointer.
+ * @retval AGESA_SUCCESS IOMMU initialized and table created
+ * @retval AGESA_UNSUPPORTED IOMMU not enabled or not found
+ * @retval AGESA_ERROR IOMMU initialization failed.
+ *
+ */
+AGESA_STATUS
+NbIommuInit (
+ IN OUT AMD_NB_CONFIG_BLOCK *ConfigPtr
+ )
+{
+ AGESA_STATUS Status;
+
+ Status = AGESA_SUCCESS;
+
+ CIMX_TRACE ((TRACE_DATA (ConfigPtr, CIMX_NB_TRACE), "[NBIOMMU]NbIommuInit Enter\n"));
+
+ if (NbIommuEnabled (ConfigPtr)) {
+ NbIommuHwInit (ConfigPtr);
+ NbIommuAcpiInit (ConfigPtr);
+ NbIommuHwTopologyInit (ConfigPtr);
+ } else {
+ Status = AGESA_UNSUPPORTED;
+ }
+
+ CIMX_TRACE ((TRACE_DATA (ConfigPtr, CIMX_NB_TRACE), "[NBIOMMU]NbIommuInit Exit [Status = 0x%x]\n", Status));
+ return Status;
+}
+
+
+
+
+/*----------------------------------------------------------------------------------------*/
+/**
+ * Northbridge Iommu Initialization for all NB in system.
+ *
+ * @param[in] ConfigPtr Northbridges configuration block pointer.
+ * @retval AGESA_SUCCESS IOMMU initialized and table created
+ * @retval AGESA_UNSUPPORTED IOMMU not enabled or not found
+ * @retval AGESA_ERROR IOMMU initialization failed.
+ *
+ */
+AGESA_STATUS
+NbIommuInitS3 (
+ IN OUT AMD_NB_CONFIG_BLOCK *ConfigPtr
+ )
+{
+ AGESA_STATUS Status;
+
+ Status = AGESA_SUCCESS;
+ CIMX_TRACE ((TRACE_DATA (ConfigPtr, CIMX_NB_TRACE), "[NBIOMMU]NbIommuInitS3 Enter\n"));
+
+ if (NbIommuEnabled (ConfigPtr)) {
+ NbIommuHwInit (ConfigPtr);
+ NbIommuHwTopologyInit (ConfigPtr);
+ } else {
+ Status = AGESA_UNSUPPORTED;
+ }
+ CIMX_TRACE ((TRACE_DATA (ConfigPtr, CIMX_NB_TRACE), "[NBIOMMU]NbIommuInitS3 Exit [Status = 0x%x]\n", Status));
+ return Status;
+}
+
+
+
+
+/*----------------------------------------------------------------------------------------*/
+/**
+ * Northbridge Iommu HW Initialization for all NB in system.
+ *
+ * @param[in] ConfigPtr Northbridges configuration block pointer.
+ * @retval AGESA_SUCCESS IOMMU initialized and table created
+ * @retval AGESA_ERROR IOMMU initialization failed.
+ *
+ */
+AGESA_STATUS
+NbIommuHwInit (
+ IN OUT AMD_NB_CONFIG_BLOCK *ConfigPtr
+ )
+{
+ UINT8 NorthbridgeId;
+ AMD_NB_CONFIG *pConfig;
+
+ for (NorthbridgeId = 0; NorthbridgeId <= ConfigPtr->NumberOfNorthbridges; NorthbridgeId++) {
+ pConfig = &ConfigPtr->Northbridges[NorthbridgeId];
+ ConfigPtr->CurrentNorthbridge = NorthbridgeId;
+ IommuInit (pConfig);
+ }
+ return AGESA_SUCCESS;
+}
+
+/*----------------------------------------------------------------------------------------*/
+/**
+ * Northbridge Iommu HW Initialization for all NB in system.
+ *
+ * @param[in] ConfigPtr Northbridges configuration block pointer.
+ * @retval AGESA_SUCCESS IOMMU initialized and table created
+ * @retval AGESA_ERROR IOMMU initialization failed.
+ *
+ */
+AGESA_STATUS
+NbIommuHwTopologyInit (
+ IN OUT AMD_NB_CONFIG_BLOCK *ConfigPtr
+ )
+{
+ UINT8 NorthbridgeId;
+ AMD_NB_CONFIG *pConfig;
+
+ for (NorthbridgeId = 0; NorthbridgeId <= ConfigPtr->NumberOfNorthbridges; NorthbridgeId++) {
+ pConfig = &ConfigPtr->Northbridges[NorthbridgeId];
+ ConfigPtr->CurrentNorthbridge = NorthbridgeId;
+ IommuTopologyInit (pConfig);
+ }
+ return AGESA_SUCCESS;
+}
+
+/*----------------------------------------------------------------------------------------*/
+/**
+ * Northbridge Iommu Initialization for all NB in system.
+ *
+ * @param[in] ConfigPtr Northbridges configuration block pointer.
+ * @retval AGESA_SUCCESS IOMMU initialized and table created
+ * @retval AGESA_UNSUPPORTED IOMMU not enabled or not found
+ * @retval AGESA_ERROR IOMMU initialization failed.
+ *
+ */
+AGESA_STATUS
+NbIommuAcpiInit (
+ IN OUT AMD_NB_CONFIG_BLOCK *ConfigPtr
+ )
+{
+ UINT8 NorthbridgeId;
+ AGESA_STATUS Status;
+ AMD_NB_CONFIG *pConfig;
+ NB_BUFFER_PARAMS Ivrs;
+ UINTN IvrsHandle;
+
+ Status = AGESA_SUCCESS;
+ Ivrs.BufferLength = 0;
+ Ivrs.BufferHandle = IVRS_HANDLE;
+ Ivrs.BufferPtr = NULL;
+ pConfig = &ConfigPtr->Northbridges[0];
+ ConfigPtr->CurrentNorthbridge = 0;
+ IvrsHandle = 0;
+
+ CIMX_TRACE ((TRACE_DATA (ConfigPtr, CIMX_NB_TRACE), "[NBIOMMU]NbIommuAcpiInit Enter\n"));
+
+ // Get a buffer for IVRS
+ Ivrs.BufferLength = IVRS_BUFFER_SIZE;
+ Status = LibNbCallBack (PHCB_AmdAllocateBuffer, (UINTN)&Ivrs, &ConfigPtr->Northbridges[0]);
+ if (Status != AGESA_SUCCESS || Ivrs.BufferPtr == NULL) {
+ // Table creation failed
+ return AGESA_ERROR;
+ }
+
+ // Clear buffer before using
+ LibAmdMemFill (Ivrs.BufferPtr, 0, Ivrs.BufferLength, (AMD_CONFIG_PARAMS *)&(pConfig->sHeader));
+
+ // PLACE OUR ACPI IVRS TABLE
+ // 1. Create IVRS header
+ // 2. For each northbridge place IVHD
+ // 3. For northbridge 0 only, place IVMD exclusion entries
+ for (NorthbridgeId = 0; NorthbridgeId <= ConfigPtr->NumberOfNorthbridges; NorthbridgeId++) {
+ pConfig = &ConfigPtr->Northbridges[NorthbridgeId];
+ ConfigPtr->CurrentNorthbridge = NorthbridgeId;
+ if (NorthbridgeId == 0) {
+ IommuPlaceHeader (Ivrs.BufferPtr, pConfig);
+ }
+ IommuPlaceIvhdAndScanDevices (Ivrs.BufferPtr, pConfig);
+ IommuPlaceIvmdAndExclusions (Ivrs.BufferPtr, pConfig);
+ }
+ IommuFinalizeIvrs (Ivrs.BufferPtr, pConfig);
+
+ LibAmdSetAcpiTable (Ivrs.BufferPtr, TRUE, &IvrsHandle);
+
+ CIMX_TRACE ((TRACE_DATA (ConfigPtr, CIMX_NB_TRACE), "[NBIOMMU]NbIommuAcpiInit [IVRS TableAddress = 0x%x]\n", (UINT32)(Ivrs.BufferPtr)));
+ CIMX_TRACE ((TRACE_DATA (ConfigPtr, CIMX_NB_TRACE), "[NBIOMMU]NbIommuAcpiInit Exit [Status = 0x%x]\n", Status));
+
+ return AGESA_SUCCESS;
+}
+
+/*----------------------------------------------------------------------------------------*/
+/**
+ * Northbridge Iommu IVRS fixup for APICS
+ *
+ * @param[in] ConfigPtr Northbridges configuration block pointer.
+ * @retval AGESA_SUCCESS IOMMU initialized and table patched, or no patching required
+ * @retval AGESA_ERROR IOMMU enabled but no previously generated IVRC table found.
+ *
+ */
+AGESA_STATUS
+NbIommuAcpiFixup (
+ IN OUT AMD_NB_CONFIG_BLOCK *ConfigPtr
+ )
+{
+ AGESA_STATUS Status;
+ UINT8 ApicId;
+ UINT64 ApicBaseAddress;
+ UINT8 NorthbridgeId;
+ BOOLEAN IommuFound;
+ VOID *DevicePtr;
+ VOID *IvhdPtr;
+ VOID *IvrsPtr;
+ VOID *MadtPtr;
+ AMD_NB_CONFIG *pConfig;
+ PCI_ADDR IommuPciAddress;
+ UINTN IvrsHandle;
+
+
+ pConfig = &ConfigPtr->Northbridges[0];
+ IommuFound = FALSE;
+ ApicId = 0xFF;
+ ApicBaseAddress = 0;
+
+ CIMX_TRACE ((TRACE_DATA (ConfigPtr, CIMX_NB_TRACE), "[NBIOMMU]NbIommuAcpiFixup Enter\n"));
+
+ for (NorthbridgeId = 0; NorthbridgeId <= ConfigPtr->NumberOfNorthbridges; NorthbridgeId++) {
+ pConfig = &ConfigPtr->Northbridges[NorthbridgeId];
+ ConfigPtr->CurrentNorthbridge = NorthbridgeId;
+ IommuPciAddress = pConfig->NbPciAddress;
+ IommuPciAddress.Address.Function = NB_IOMMU;
+ if (IommuCheckEnable (IommuPciAddress, pConfig)) {
+ IommuFound = TRUE;
+ }
+ }
+
+ // Any Iommus enabled? If no, we don't need to patch anything
+ if (!IommuFound) {
+ return AGESA_SUCCESS;
+ }
+
+ // Check for an IVRS
+ // Check IVRS for a type 10 block (IVHD)
+ // Check for an MADT
+ // If these conditions fail, abort
+
+// Status = LibAmdGetAcpiTable ('SRVI', &IvrsPtr, &IvrsHandle);
+ Status = LibAmdGetAcpiTable (Int32FromChar ('S', 'R', 'V', 'I'), &IvrsPtr, &IvrsHandle);
+ if (Status != AGESA_SUCCESS) {
+// REPORT_EVENT (AGESA_ERROR, GENERAL_ERROR_LOCATE_ACPI_TABLE, 'SRVI', 0, 0, 0, pConfig);
+ REPORT_EVENT (AGESA_ERROR, GENERAL_ERROR_LOCATE_ACPI_TABLE, Int32FromChar ('S', 'R', 'V', 'I'), 0, 0, 0, pConfig);
+ return AGESA_ERROR;
+ }
+
+// Status = LibAmdGetAcpiTable ('CIPA', &MadtPtr, NULL);
+ Status = LibAmdGetAcpiTable (Int32FromChar ('C', 'I', 'P', 'A'), &MadtPtr, NULL);
+ if (Status != AGESA_SUCCESS) {
+// REPORT_EVENT (AGESA_ERROR, GENERAL_ERROR_LOCATE_ACPI_TABLE, 'CIPA', 0, 0, 0, pConfig);
+ REPORT_EVENT (AGESA_ERROR, GENERAL_ERROR_LOCATE_ACPI_TABLE, Int32FromChar ('C', 'I', 'P', 'A'), 0, 0, 0, pConfig);
+ return AGESA_ERROR;
+ }
+
+ IvhdPtr = LibAmdGetFirstIvrsBlockEntry (TYPE_IVHD, IvrsPtr);
+ if (IvhdPtr == NULL) {
+ return AGESA_ERROR;
+ }
+
+ // An IVRS can contain one or more IVHD entries (one per IOMMU)
+ // Each IVHD entry can contain one or more APIC entries
+
+ while (IvhdPtr != NULL) {
+ DevicePtr = LibAmdGetFirstDeviceEntry (DE_SPECIAL, IvhdPtr);
+ do {
+ // Be sure to only fix APIC entries
+ if (*(UINT8*) ((UINT8*)DevicePtr + DE_SPECIAL_VARIETY) == VARIETY_IOAPIC) {
+ ApicBaseAddress = IommuGetApicBaseAddress (DevicePtr, pConfig);
+ ApicId = IommuGetApicId (ApicBaseAddress, MadtPtr, pConfig);
+ *(UINT8*)((UINT8*)DevicePtr + DE_SPECIAL_ID) = ApicId;
+ }
+ DevicePtr = LibAmdGetNextDeviceEntry (DE_SPECIAL, DevicePtr, IvhdPtr);
+ } while (DevicePtr != NULL);
+
+ IvhdPtr = LibAmdGetNextIvrsBlockEntry (TYPE_IVHD, IvhdPtr, IvrsPtr);
+ }
+
+ LibAmdSetAcpiTable (IvrsPtr, TRUE, &IvrsHandle);
+
+ CIMX_TRACE ((TRACE_DATA (ConfigPtr, CIMX_NB_TRACE), "[NBIOMMU]NbIommuAcpiFixup [IVRS TableAddress = 0x%x]\n", (UINT32)IvrsPtr));
+ CIMX_TRACE ((TRACE_DATA (ConfigPtr, CIMX_NB_TRACE), "[NBIOMMU]NbIommuAcpiFixup [APIC TableAddress = 0x%x]\n", (UINT32)MadtPtr));
+ CIMX_TRACE ((TRACE_DATA (ConfigPtr, CIMX_NB_TRACE), "[NBIOMMU]NbIommuAcpiFixup Exit\n"));
+
+ return AGESA_SUCCESS;
+}
+
+/*----------------------------------------------------------------------------------------
+ * P R I V A T E
+ *----------------------------------------------------------------------------------------
+ */
+UINT32 IommuMmioInitTable[] = {
+ 0x8, 0x0,
+ 0xC, 0x08000000,
+ 0x10, 0x0,
+ 0x14, 0x08000000,
+ 0x2000, 0x0,
+ 0x2008, 0x0
+};
+
+/*----------------------------------------------------------------------------------------*/
+/**
+ * Nb Iommu Initialization.
+ *
+ * @param[in] pConfig Northbridge configuration pointer
+ * @retval AGESA_SUCCESS IOMMU enable and initialized succesfully.
+ * @retval AGESA_UNSUPPORTED IOMMU not initialized.
+ */
+AGESA_STATUS
+IommuInit (
+ IN OUT AMD_NB_CONFIG *pConfig
+ )
+{
+ UINT8 CapBase;
+ PCI_ADDR IommuPciAddress;
+ UINTN i;
+
+ IommuPciAddress = pConfig->NbPciAddress;
+ IommuPciAddress.Address.Function = NB_IOMMU;
+
+ // If the base address = 0, don't enable IOMMU
+ if (pConfig->pNbConfig->IommuBaseAddress == 0) {
+ return AGESA_UNSUPPORTED;
+ }
+
+ // NBMISCIND:0x75 IOC_FEATURE_CNTL_10_0[10]=1
+ // 0=disable
+ // 1=enable
+
+ LibNbPciIndexRMW (pConfig->NbPciAddress.AddressValue | NB_MISC_INDEX, NB_MISC_REG75, AccessS3SaveWidth32, (UINT32)~(BIT28), BIT0, pConfig);
+
+ // Get Capabilities pointer 32h (points to 40h) - capability ID 0x0F. Not found, we have no IOMMU.
+ CapBase = LibNbFindPciCapability (IommuPciAddress.AddressValue, IOMMU_CAPID, pConfig);
+ if (CapBase == 0) {
+ return AGESA_UNSUPPORTED;
+ }
+
+ // IOMMU_ADAPTER_ID_W - RW - 32 bits - nbconfigfunc2:0x68
+ // SUBSYSTEM_VENDOR_ID_W 15:0 0x0 Sets the subsystem vendor ID register header
+ // SUBSYSTEM_ID_W 31:16 0x0 Sets the subsystem ID register in the configuration header
+ CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (pConfig), CIMX_NB_TRACE), "[NBIOMMU]Iommu Device Found [PCI Address = 0x%x]\n", IommuPciAddress.AddressValue));
+ if (pConfig->pNbConfig->SSID == PCI_INVALID) {
+ LibNbPciRead (IommuPciAddress.AddressValue, AccessWidth32, &pConfig->pNbConfig->SSID, pConfig);
+ }
+ if (pConfig->pNbConfig->SSID) {
+ LibNbPciWrite (IommuPciAddress.AddressValue | 0x68, AccessS3SaveWidth32, &pConfig->pNbConfig->SSID, pConfig);
+ }
+
+ // Get Capabilities pointer 32h (points to 40h) - capability ID 0x0F
+ // Set Cap_Offset+04h [31:14] Base Address Low [31:14]
+ // Set Cap_Offset+08h [31:0] Base Address High [64:32]
+ // Set Cap_Offset+04h [0] Enable
+ LibNbPciRMW ((IommuPciAddress.AddressValue | (CapBase + 8)), AccessS3SaveWidth32, 0x0, ((UINT32*)&pConfig->pNbConfig->IommuBaseAddress)[1], pConfig);
+ LibNbPciRMW ((IommuPciAddress.AddressValue | (CapBase + 4)), AccessS3SaveWidth32, 0x0, ((UINT32*)&pConfig->pNbConfig->IommuBaseAddress)[0], pConfig);
+
+ // Enable zeroing of address for zero-byte reads when IOMMU enabled
+ LibNbPciIndexRMW (pConfig->NbPciAddress.AddressValue | NB_MISC_INDEX, NB_MISC_REG01, AccessS3SaveWidth32, (UINT32)~(BIT8 | BIT9), BIT8, pConfig);
+
+ // 8.3.1 L1 Initialization
+ LibNbIndirectTableInit (IommuPciAddress.AddressValue | L1CFG_INDEX,
+ 0,
+ (INDIRECT_REG_ENTRY*)FIX_PTR_ADDR(&IommuL1Table[0],NULL),
+ (sizeof (IommuL1Table) / sizeof (INDIRECT_REG_ENTRY)),
+ pConfig
+ );
+
+ // 8.3.3.1 L2 Common Initialization
+ LibNbIndirectTableInit (IommuPciAddress.AddressValue | L2CFG_INDEX,
+ 0,
+ (INDIRECT_REG_ENTRY*)FIX_PTR_ADDR(&IommuL2Table[0], NULL),
+ (sizeof (IommuL2Table) / sizeof (INDIRECT_REG_ENTRY)),
+ pConfig
+ );
+ //Configure PDC cache to 12-way set associative cache for A21
+ if (LibNbGetRevisionInfo (pConfig).Revision > NB_REV_A11) {
+ LibNbPciIndexRMW (IommuPciAddress.AddressValue | L2CFG_INDEX, L2CFG_SEL_WR_EN | L2REG_52, AccessS3SaveWidth32, 0x0, 0xF0000002 , pConfig);
+ }
+ // Start and lock the Iommu settings
+ LibNbPciRMW ((IommuPciAddress.AddressValue | (CapBase + 4)), AccessS3SaveWidth32, 0xFFFFFFFF, (UINT32)BIT0, pConfig);
+
+ //Reset IOMMU MMIO registers on system reset
+ for (i = 0; i < (sizeof (IommuMmioInitTable) / sizeof (UINT32)); i = i + 2) {
+ LibNbMemRMW (pConfig->pNbConfig->IommuBaseAddress + IommuMmioInitTable[i], AccessS3SaveWidth32, 0x0, IommuMmioInitTable[i + 1], pConfig);
+ }
+ return AGESA_SUCCESS;
+}
+
+
+/*----------------------------------------------------------------------------------------*/
+/**
+ * Iommu Initialization of topology specific data.
+ *
+ * @param[in] pConfig Northbridge configuration pointer
+ * @retval AGESA_SUCCESS IOMMU enable and initialized succesfully.
+ * @retval AGESA_UNSUPPORTED IOMMU not initialized.
+ */
+AGESA_STATUS
+IommuTopologyInit (
+ IN OUT AMD_NB_CONFIG *pConfig
+ )
+{
+ // Set L2 Caches Hash Control based on maximum bus, device, function
+ IommuInitL2CacheControl ((IOMMU_PCI_TOPOLOGY*) &pConfig->pNbConfig->IommuTpologyInfo, pConfig);
+ return AGESA_SUCCESS;
+}
+
+L2_HASH_CONTROL HashControls[] = {
+ {
+ L2_DTC_CONTROL
+ },
+ {
+ L2_ITC_CONTROL
+ },
+ {
+ L2_PTC_A_CONTROL
+ },
+ {
+ L2_PTC_B_CONTROL
+ },
+ {
+ L2_PDC_CONTROL
+ }
+};
+
+/*----------------------------------------------------------------------------------------*/
+/**
+ * Set L2 Cache Hash Control based on maximum Bus, Dev, Function found
+ *
+ * @param[in] PciPtr Array of bus, device, function
+ * @param[in] pConfig Northbridge configuration structure pointer.
+ */
+VOID
+IommuInitL2CacheControl (
+ IN IOMMU_PCI_TOPOLOGY *PciPtr,
+ IN OUT AMD_NB_CONFIG *pConfig
+ )
+{
+ UINT32 Value;
+ PCI_ADDR IommuPciAddress;
+ UINTN i;
+ UINT8 FuncBitsUsed;
+ UINT8 DevBitsUsed;
+ UINT8 BusBitsUsed;
+
+ IommuPciAddress = pConfig->NbPciAddress;
+ IommuPciAddress.Address.Function = NB_IOMMU;
+
+ CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (pConfig), CIMX_NB_TRACE), " L2Cache Init Max Bus = 0x%x Max Device = 0x%x Mux Func = 0x%x\n", PciPtr->MaxBus, PciPtr->MaxDevice, PciPtr->MaxFunction));
+
+ FuncBitsUsed = CIMX_MAX (IommuGetLog2 (PciPtr->MaxFunction + 1), 3);
+ DevBitsUsed = IommuGetLog2 (PciPtr->MaxDevice + 1);
+ BusBitsUsed = IommuGetLog2 (PciPtr->MaxBus + 1);
+
+ for (i = 0; i < (sizeof (HashControls) / sizeof (L2_HASH_CONTROL)); i++) {
+ UINT8 NBits;
+ UINT8 NFuncBits;
+ UINT8 NDevBits;
+ UINT8 NBusBits;
+ LibNbPciIndexRead (IommuPciAddress.AddressValue | L2CFG_INDEX, L2CFG_SEL_WR_EN | HashControls[i].HashControl, AccessWidth32, &Value, pConfig);
+ NBits = (UINT8) (Value >> 28) - IommuGetLog2 ((Value >> 16) & 0xff);
+ NFuncBits = CIMX_MIN (NBits, 0x3);
+ NBits = NBits - NFuncBits;
+ NDevBits = CIMX_MIN ( NBits, DevBitsUsed + FuncBitsUsed - NFuncBits);
+ NBusBits = NBits - NDevBits;
+ CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (pConfig), CIMX_NB_TRACE), " NBusBits = %d, NDevBits = %d, NFuncBits = %d \n", NBusBits, NDevBits, NFuncBits));
+ LibNbPciIndexRMW (
+ IommuPciAddress.AddressValue | L2CFG_INDEX,
+ L2CFG_SEL_WR_EN | (HashControls[i].HashControl + 1),
+ AccessS3SaveWidth32,
+ 0xFFFFFE00,
+ (NFuncBits | (NDevBits << 2) | (NBusBits << 5)) & 0x1FF,
+ pConfig
+ );
+ }
+}
+
+/*----------------------------------------------------------------------------------------*/
+/**
+ * Check to see if current PCI Address is an IOMMU
+ *
+ * @param[in] IommuPciAddress PCI Address to check
+ * @param[in] pConfig Northbridge configuration structure pointer.
+ * @retval TRUE if Iommu is enabled and found
+ */
+BOOLEAN
+IommuCheckEnable (
+ IN PCI_ADDR IommuPciAddress,
+ IN AMD_NB_CONFIG *pConfig
+ )
+{
+ UINT8 CapBase;
+
+ if (pConfig->pNbConfig->IommuBaseAddress == 0x0) {
+ return FALSE;
+ }
+ CapBase = LibNbFindPciCapability (IommuPciAddress.AddressValue, IOMMU_CAPID, pConfig);
+ if (CapBase == 0) {
+ return FALSE;
+ } else {
+ return TRUE;
+ }
+}
+
+/*----------------------------------------------------------------------------------------*/
+/**
+ * Check an RD890 PCIE bridge to see if hot plug is enabled
+ *
+ * @param[in] DeviceId Device Id to check
+ * @param[in] pConfig Northbridge configuration structure pointer.
+ * @retval TRUE if current device supports hotplug
+ */
+BOOLEAN
+IommuCheckHp (
+ IN UINT16 DeviceId,
+ IN AMD_NB_CONFIG *pConfig
+ )
+{
+ UINT32 PciData;
+ PCI_ADDR PciAddress;
+
+ PciAddress.AddressValue = MAKE_SBDFO (0, (DeviceId >> 8) & 0xFF, (DeviceId >> 3) & 0x1F, DeviceId & 0x7, 0);
+
+ LibNbPciRead (PciAddress.AddressValue | NB_PCIP_REG6C, AccessWidth32, &PciData, pConfig);
+
+ // Check for hot plug by reading PCIE_SLOT_CAP pcieConfigDev[13:2]:0x6C [6] HOTPLUG_CAPABLE
+ PciData &= BIT6;
+ if (PciData != 0) {
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+/*----------------------------------------------------------------------------------------*/
+/**
+ * Check a PCIE device to see if it supports phantom functions
+ *
+ * @param[in] DeviceId Device Id to check
+ * @param[in] pConfig Northbridge configuration structure pointer.
+ * @retval TRUE if current device supports phantom functions
+ */
+BOOLEAN
+IommuCheckPhantom (
+ IN UINT16 DeviceId,
+ IN AMD_NB_CONFIG *pConfig
+ )
+{
+ UINT32 PciData;
+ UINT8 PcieCapBase;
+ PCI_ADDR PciAddress;
+
+ PciAddress.AddressValue = MAKE_SBDFO (0, (DeviceId >> 8) & 0xFF, (DeviceId >> 3) & 0x1F, DeviceId & 0x7, 0);
+
+ // Check for phantom functions by reading PCIE Device Capabilities register (base + 4) [4:3] 0 = not supported
+ PcieCapBase = LibNbFindPciCapability (PciAddress.AddressValue, PCIE_CAPID, pConfig);
+ if (PcieCapBase != 0) {
+ LibNbPciRead (((PciAddress.AddressValue) | (PcieCapBase + 4)), AccessWidth32, &PciData, pConfig);
+ PciData &= PCIE_PHANTOMMASK;
+ if (PciData != 0) {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/*----------------------------------------------------------------------------------------*/
+/**
+ * Check to see if current PCI Address is a multi-port PCIE core
+ *
+ * @param[in] DeviceId 16-bit device id to check
+ * @retval L1 configuration select
+ */
+UINT32
+IommuGetL1 (
+ IN UINT16 DeviceId
+ )
+{
+ // This function translates an RD890 multi-port pci core to the offset of the L1 entry
+ // corresponding to it. An unknown device returns as invalid
+ switch (DeviceId) {
+ case DEVICEID_GPP1_0 :
+ return L1CFG_SEL_GPP1;
+ case DEVICEID_GPP1_1 :
+ return L1CFG_SEL_GPP1;
+ case DEVICEID_GPP2_0 :
+ return L1CFG_SEL_GPP2;
+ case DEVICEID_GPP2_1 :
+ return L1CFG_SEL_GPP2;
+ case DEVICEID_GPP3A_0 :
+ return L1CFG_SEL_GPP3A;
+ case DEVICEID_GPP3A_1 :
+ return L1CFG_SEL_GPP3A;
+ case DEVICEID_GPP3A_2 :
+ return L1CFG_SEL_GPP3A;
+ case DEVICEID_GPP3A_3 :
+ return L1CFG_SEL_GPP3A;
+ case DEVICEID_GPP3A_4 :
+ return L1CFG_SEL_GPP3A;
+ case DEVICEID_GPP3A_5 :
+ return L1CFG_SEL_GPP3A;
+ case DEVICEID_GPP3B_0 :
+ return L1CFG_SEL_GPP3B;
+ default:
+ return PCI_INVALID;
+ }
+}
+
+/*----------------------------------------------------------------------------------------*/
+/**
+ * Place IVHD Device Entries
+ *
+ * @param[in] Value Value to find the logarithm of
+ * @retval Logarithm of input Value
+ */
+UINT8
+IommuGetLog2 (
+ IN UINT32 Value
+ )
+{
+ UINT8 Result;
+
+ Result = 0;
+
+ // This code will round a 32bit value to the next highest power of 2
+ Value--;
+ Value |= Value >> 1;
+ Value |= Value >> 2;
+ Value |= Value >> 4;
+ Value |= Value >> 8;
+ Value |= Value >> 16;
+ Value++;
+
+ // Calculate the logarithm
+ while (Value >>= 1) {
+ Result++;
+ }
+
+ return Result;
+}
+
+/*----------------------------------------------------------------------------------------*/
+/**
+ * Place IVRS Header for IOMMU ACPI table
+ *
+ * @param[in, out] BufferPtr Pointer to buffer to return IVRS.
+ * @param[in] pConfig Northbridge configuration structure pointer.
+ */
+VOID
+IommuPlaceHeader (
+ IN OUT VOID *BufferPtr,
+ IN OUT AMD_NB_CONFIG *pConfig
+ )
+{
+ IOMMU_IVRS_HEADER *HeaderPtr;
+ HeaderPtr = (IOMMU_IVRS_HEADER *)BufferPtr;
+ LibAmdMemCopy (HeaderPtr, &RD890S_DfltHeader, sizeof (IOMMU_IVRS_HEADER), (AMD_CONFIG_PARAMS *)&(pConfig->sHeader));
+}
+
+/*----------------------------------------------------------------------------------------*/
+/**
+ * Place IVMD (memory device) Create all IVMD entries for a single exclusion table
+ *
+ * @param[in, out] BufferPtr Pointer to text buffer to return IVRS
+ * @param[in] pConfig Northbridge configuration pointer
+ */
+VOID
+IommuPlaceIvmdAndExclusions (
+ IN OUT VOID *BufferPtr,
+ IN OUT AMD_NB_CONFIG *pConfig
+ )
+{
+ UINT8 EntryCount;
+ UINT8 CurrentExclusion;
+ AGESA_STATUS Status;
+ IOMMU_EXCLUSIONTABLE *pExclusion;
+ IOMMU_IVRS_HEADER *HeaderPtr;
+ IOMMU_IVMD_ENTRY *IvmdPtr;
+
+ pExclusion = &RD890S_DfltExclusion;
+ HeaderPtr = (IOMMU_IVRS_HEADER *)BufferPtr;
+ IvmdPtr = (IOMMU_IVMD_ENTRY *)BufferPtr;
+
+ Status = LibNbCallBack (PHCB_AmdGetExclusionTable, (UINTN)&pExclusion, pConfig);
+ if (Status == AGESA_SUCCESS) {
+ EntryCount = (UINT8) ((pExclusion->TableLength - sizeof (UINTN)) / sizeof (IOMMU_EXCLUSIONRANGE));
+ for (CurrentExclusion = 0; CurrentExclusion < EntryCount; CurrentExclusion++) {
+ IvmdPtr = (IOMMU_IVMD_ENTRY*) ((UINT8*)HeaderPtr + HeaderPtr->Length);
+ IvmdPtr->Type = TYPE_IVMD_ALL; // 20h = All peripherals
+ IvmdPtr->Flags = 0x7; // Exclusion range
+ IvmdPtr->Length = 32; // 32 byte structure
+ IvmdPtr->DeviceId = 0; // Reserved for type 20h
+ IvmdPtr->AuxData = 0; // Reserved for type 20h
+ IvmdPtr->Reserved = 0;
+ IvmdPtr->BlockStartAddress = pExclusion->ExclusionRange[CurrentExclusion].Start;
+ IvmdPtr->BlockLength = pExclusion->ExclusionRange[CurrentExclusion].Length;
+ HeaderPtr->Length += 32; // Update size of IVRS
+ }
+ }
+}
+
+/*----------------------------------------------------------------------------------------*/
+/**
+ * Place IVHD (hardware device) Entry for IOMMU ACPI table
+ *
+ * @param[in, out] BufferPtr Pointer to text buffer to return IVRS
+ * @param[in] pConfig Northbridge configuration pointer
+ */
+VOID
+IommuPlaceIvhdAndScanDevices (
+ IN OUT VOID *BufferPtr,
+ IN OUT AMD_NB_CONFIG *pConfig
+ )
+{
+ UINT32 Value;
+ IOMMU_PCI_TOPOLOGY PciFlags;
+ IOMMU_IVRS_HEADER *HeaderPtr;
+ IOMMU_IVHD_ENTRY *IvhdPtr;
+ PCI_ADDR IommuPciAddress;
+
+ HeaderPtr = (IOMMU_IVRS_HEADER *)BufferPtr;
+ IvhdPtr = (IOMMU_IVHD_ENTRY *)BufferPtr;
+ //PciFlags.PhantomFunction = FALSE;
+ PciFlags.MaxBus = 0;
+ PciFlags.MaxDevice = 0;
+ PciFlags.MaxFunction = 0;
+ IommuPciAddress = pConfig->NbPciAddress;
+ IommuPciAddress.Address.Function = NB_IOMMU;
+
+ IvhdPtr = (IOMMU_IVHD_ENTRY*) ((UINT8*)HeaderPtr + HeaderPtr->Length);
+ IvhdPtr->Type = TYPE_IVHD; // Hardware block
+ IvhdPtr->Flags = FLAGS_COHERENT | FLAGS_IOTLBSUP | FLAGS_ISOC | FLAGS_RESPASSPW | FLAGS_PASSPW;
+ IvhdPtr->Length = 24; // Length = 24 with no devices
+ IvhdPtr->DeviceId = (UINT16)((IommuPciAddress.AddressValue >> 12) & 0xFFFF); // Change 32 bit ID into 16 bit
+ IvhdPtr->CapabilityOffset = (UINT16) (LibNbFindPciCapability (IommuPciAddress.AddressValue, IOMMU_CAPID, pConfig));
+ IvhdPtr->BaseAddress = pConfig->pNbConfig->IommuBaseAddress;
+ IvhdPtr->PciSegment = 0;
+ LibNbPciRead (IommuPciAddress.AddressValue | (IvhdPtr->CapabilityOffset + 0x10), AccessWidth32, &Value, pConfig);
+ IvhdPtr->IommuInfo = (UINT16)(Value & 0x1f); //Set MSInum.
+ IvhdPtr->IommuInfo |= ((0x13) << 8); //set UnitID
+ IvhdPtr->Reserved = 0;
+
+ IommuIvhdNorthbridgeDevices (&PciFlags, IvhdPtr, pConfig);
+ if (IommuPciAddress.Address.Bus == 0) {
+ IommuIvhdSouthbridgeDevices (&PciFlags, IvhdPtr, pConfig);
+ }
+ IommuIvhdApicsAndHpets (IvhdPtr, pConfig);
+ pConfig->pNbConfig->IommuTpologyInfo = *((UINT32*) &PciFlags);
+ HeaderPtr->Length += IvhdPtr->Length;
+}
+
+/*----------------------------------------------------------------------------------------*/
+/**
+ * Place IVHD Device Entries
+ *
+ * @param[in, out] PciPtr Pci topology flags
+ * @param[in, out] IvhdPtr Pointer to IVHD where entry is appended
+ * @param[in] pConfig NB config block
+ */
+VOID
+IommuIvhdNorthbridgeDevices (
+ IN OUT IOMMU_PCI_TOPOLOGY *PciPtr,
+ IN OUT IOMMU_IVHD_ENTRY *IvhdPtr,
+ IN OUT AMD_NB_CONFIG *pConfig
+ )
+{
+ UINT16 CurrentDevice;
+ UINT16 DeviceId;
+ UINT8 EntryCount;
+ IOMMU_DEVICELIST *pDevices;
+ PCI_ADDR NbPciAddress;
+ PCI_ADDR IommuPciAddress;
+
+ pDevices = &RD890S_DfltDevices;
+ NbPciAddress = pConfig->NbPciAddress;
+ IommuPciAddress = pConfig->NbPciAddress;
+
+ IommuPciAddress.Address.Function = NB_IOMMU;
+ EntryCount = (UINT8) ((pDevices->TableLength - sizeof (UINT16)) / sizeof (UINT16));
+
+ // Run RD890S device table, fixed for current bus
+ for (CurrentDevice = 0; CurrentDevice < EntryCount; CurrentDevice++) {
+ DeviceId = (UINT16) (NbPciAddress.Address.Bus << 8) | pDevices->Device[CurrentDevice];
+ IommuCreateDeviceEntry (PciPtr, DeviceId, IvhdPtr, pConfig);
+
+ // CHECK HOTPLUG OR PHANTOM FUNCTION SUPPORT
+ // For each device, reset PhantomEnable, but set it as a one-shot. If any device under the northbridge PCIE bridge
+ // device has phantom function support enabled, set the L1. Additionally, check the bridge for hotplug, and set the
+ // L1 if so.
+
+ //PciPtr->PhantomFunction = FALSE;
+ //if (PciPtr->PhantomFunction || IommuCheckHp (DeviceId, pConfig)) {
+ // if (IommuGetL1 (DeviceId) != PCI_INVALID && LibNbGetRevisionInfo (pConfig).Revision != NB_REV_A11) {
+ // // Determine from deviceID which L1
+ // LibNbPciIndexRMW (IommuPciAddress.AddressValue | L1CFG_INDEX, L1CFG_SEL_WR_EN | IommuGetL1 (DeviceId), AccessS3SaveWidth32, (UINT32)~BIT0, BIT0, pConfig);
+ // }
+ //}
+ //PciPtr->PhantomFunction = FALSE;
+ }
+}
+
+/*----------------------------------------------------------------------------------------*/
+/**
+ * Place IVHD Device Entries
+ *
+ * @param[in, out] PciPtr PCI topology flags
+ * @param[in, out] IvhdPtr Pointer to IVHD where entry is appended
+ * @param[in] pConfig NB config structute
+ */
+VOID
+IommuIvhdSouthbridgeDevices (
+ IN OUT IOMMU_PCI_TOPOLOGY *PciPtr,
+ IN OUT IOMMU_IVHD_ENTRY *IvhdPtr,
+ IN OUT AMD_NB_CONFIG *pConfig
+ )
+{
+ UINT16 DeviceId;
+ PCI_ADDR IommuPciAddress;
+ IommuPciAddress = pConfig->NbPciAddress;
+ IommuPciAddress.Address.Function = NB_IOMMU;
+
+ // Assume Device 0x10 Function 0 - Device 0x17 Function 7 belong to the SB
+ //PciPtr->PhantomFunction = FALSE;
+ for (DeviceId = (0x10 << 3); (DeviceId < (0x18 << 3)); DeviceId++) {
+ IommuCreateDeviceEntry (PciPtr, DeviceId, IvhdPtr, pConfig);
+ //if (PciPtr->PhantomFunction) {
+ // // Enable SB phantom enable
+ // LibNbPciIndexRMW (IommuPciAddress.AddressValue | L1CFG_INDEX, L1CFG_SEL_WR_EN | L1CFG_SEL_SB, AccessS3SaveWidth32, (UINT32)~BIT0, BIT0, pConfig);
+ //}
+ //PciPtr->PhantomFunction = FALSE;
+ }
+
+#if defined (IVHD_APIC_SUPPORT) || defined (IVHD_HPET_SUPPORT)
+ DeviceId = (SB_DEV << 3); // Bus 0 Dev 14 Func 0
+#endif
+
+#ifdef IVHD_APIC_SUPPORT
+ // Southbridge IOAPIC
+ IommuCreate8ByteEntry (DE_SPECIAL, DATA_ALLINTS, 0, 0xFF, DeviceId, VARIETY_IOAPIC, IvhdPtr);
+#endif
+
+#ifdef IVHD_HPET_SUPPORT
+ // Southbridge HPET
+ IommuCreate8ByteEntry (DE_SPECIAL, DATA_ALLINTS, 0, 0, DeviceId, VARIETY_HPET, IvhdPtr);
+#endif
+
+}
+
+/*----------------------------------------------------------------------------------------*/
+/**
+ * Place IVHD Device Entries
+ *
+ * @param[in, out] IvhdPtr Pointer to buffer to return IVRS.
+ * @param[in] pConfig NB config structute
+ */
+VOID
+IommuIvhdApicsAndHpets (
+ IN OUT IOMMU_IVHD_ENTRY *IvhdPtr,
+ IN OUT AMD_NB_CONFIG *pConfig
+ )
+{
+#ifdef IVHD_APIC_SUPPORT
+ PCI_ADDR PciAddress;
+ UINT16 DeviceId;
+ UINT32 PciData;
+
+ PciAddress = pConfig->NbPciAddress;
+
+ // Northbridge IOAPIC
+ DeviceId = (UINT16)((PciAddress.Address.Bus << 8)) | 1; // Bus X Dev 0 Func 0
+ LibNbPciRead (PciAddress.AddressValue | 0x4C, AccessWidth32, &PciData, pConfig);
+ if (PciData & (UINT32)BIT1) {
+ IommuCreate8ByteEntry (DE_SPECIAL, DATA_NOINTS, 0, 0xFF, DeviceId, VARIETY_IOAPIC, IvhdPtr);
+ }
+#endif
+}
+
+/*----------------------------------------------------------------------------------------*/
+/**
+ * Place IVHD device entry (type 2 for single function or 3/4 for multifunction) at end of IVHD entry
+ *
+ * @param[in, out] PciPtr Pci topology flags
+ * @param[in] DeviceId DeviceID of entry to potentially create
+ * @param[in, out] IvhdPtr Pointer to IVHD
+ * @param[in] pConfig NB config structute
+ */
+VOID
+IommuCreateDeviceEntry (
+ IN OUT IOMMU_PCI_TOPOLOGY *PciPtr,
+ IN UINT16 DeviceId,
+ IN OUT IOMMU_IVHD_ENTRY *IvhdPtr,
+ IN AMD_NB_CONFIG *pConfig
+ )
+{
+ // 8 BYTE DEVICE ENTRY:
+ // [0] Type (0x2 = Device, 0x3 = Device Range Start, 0x4 = Device Range End
+ // [1] DeviceID LSB (Device/Function)
+ // [2] DeviceID MSB (Bus)
+ // [3] Data (0 = No legacy interrupts)
+
+ // DEVICEID
+ // A 16 bit bus/device/function DeviceId consists of:
+ // [15:8] Bus
+ // [7:3] Device
+ // [2:0] Function
+
+ PCI_ADDR PciAddress;
+ UINT32 PciData;
+ UINT8 DeviceCount;
+ UINT8 FunctionCount;
+ UINT8 PcieCapBase;
+ UINT8 PcixCapBase;
+
+ BOOLEAN LegacyBridge;
+ BOOLEAN MultiFunction;
+ BOOLEAN SubFunction;
+ BOOLEAN DiscreteEntry;
+ UINT8 HighFunction;
+ UINT32 ClassCode;
+ UINT16 ExtendedCapabilityPtr;
+ SB_INFO SbInfo;
+ UINT8 SataEnableRegValue;
+ BOOLEAN SrIovDevice;
+
+ PcieCapBase = 0;
+ PcixCapBase = 0;
+ LegacyBridge = FALSE;
+ MultiFunction = FALSE;
+ SubFunction = FALSE;
+ DiscreteEntry = FALSE;
+ HighFunction = 0;
+ SataEnableRegValue = 0;
+ SrIovDevice = FALSE;
+
+ //For SB700, get combined mode status
+ SbInfo = LibAmdSbGetRevisionInfo ((pConfig == NULL)?NULL:GET_BLOCK_CONFIG_PTR (pConfig));
+ if (SbInfo.Type == SB_SB700) {
+ LibNbPciRead (MAKE_SBDFO (0, 0, 0x14, 0, SATA_ENABLE_REG), AccessWidth8, &SataEnableRegValue , pConfig);
+ }
+
+
+ // If the device to check does not exist, exit
+ PciAddress.AddressValue = MAKE_SBDFO (0, (DeviceId >> 8) & 0xFF, (DeviceId >> 3) & 0x1F, DeviceId & 0x7, 0);
+ if (!LibNbIsDevicePresent (PciAddress, pConfig)) {
+ return;
+ };
+ LibNbPciRead (PciAddress.AddressValue | PCI_CLASS, AccessWidth32, &ClassCode, pConfig);
+ ClassCode = (ClassCode >> 16) & 0xFFFF; // Keep class code and sub-class only
+
+ // THREE STAGES TO THIS FUNCTION
+ // 1. Check for multifunction or special devices
+ // 2. Place device entry for the current device ID
+ // 3. If a bridge, decide if we need to traverse further
+
+ // STEP 1 - CHECK FUNCTIONS ON THIS DEVICE
+ // To make decisions, we will need several pieces of information about this device not found with current SBDFO
+ // 1. Multifunction device - To determine if a device entry, or device range entry is needed - check function 0 only
+ // 2. DisableRange - We will create single entries a device containing a PCI or PCIE bridge
+ // 3. How many functions on this device?
+
+ PciAddress.AddressValue = MAKE_SBDFO (0, (DeviceId >> 8) & 0xFF, (DeviceId >> 3) & 0x1F, 0, 0);
+ LibNbPciRead (PciAddress.AddressValue | PCI_HEADER, AccessWidth32, &PciData, pConfig);
+ if ((PciData & PCI_MULTIFUNCTION) != 0) {
+ MultiFunction = TRUE;
+ } else {
+ DiscreteEntry = TRUE;
+ }
+ if ((DeviceId & 0x7) != 0 && MultiFunction) {
+ SubFunction = TRUE;
+ }
+
+ if (MultiFunction) {
+ for (FunctionCount = 0; FunctionCount < 8; FunctionCount++) {
+ PciAddress.AddressValue = MAKE_SBDFO (0, (DeviceId >> 8) & 0xFF, (DeviceId >> 3) & 0x1F, FunctionCount, 0);
+ LibNbPciRead (PciAddress.AddressValue | PCI_HEADER, AccessWidth32, &PciData, pConfig);
+ if (PciData != PCI_INVALID) {
+ HighFunction = FunctionCount;
+ }
+ LibNbPciRead (PciAddress.AddressValue | PCI_CLASS, AccessWidth32, &PciData, pConfig);
+ if (((PciData >> 16) & 0xFFFF) == PCI_BRIDGE_CLASS) {
+ DiscreteEntry = TRUE;
+ }
+ }
+ }
+
+ // For SR IOV devices set for all functions to be available
+ if (MultiFunction && (!DiscreteEntry) && (!SubFunction)) {
+ PciAddress.AddressValue = MAKE_SBDFO (0, (DeviceId >> 8) & 0xFF, (DeviceId >> 3) & 0x1F, 0, 0);
+ ExtendedCapabilityPtr = LibNbFindPcieExtendedCapability (PciAddress.AddressValue, 0x10, pConfig);
+ if (ExtendedCapabilityPtr != 0) {
+ SrIovDevice = TRUE;
+ }
+ }
+ // STEP 2 - PLACE DEVICE ENTRY
+ // We have already decided whether we should use discrete type2 entries, or ranged type3/4 entries
+ // Place each device entry at the end of the current IVHD
+ // In each case, increment the maximum bus/device/function for L2 cache control done after the IVHD is created
+
+ CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (pConfig), CIMX_NB_TRACE), "[NBIOMMU]Placing Entry for Device [0x%x]\n", DeviceId));
+
+ //if (IommuCheckPhantom (DeviceId, pConfig)) {
+ // PciPtr->PhantomFunction = TRUE;
+ //}
+
+ if (!MultiFunction || DiscreteEntry) {
+ //For Device 0x14, function 0, Set DATA_ALLINTS
+ if (DeviceId == (SB_DEV << 3)) {
+ IommuCreate4ByteEntry (DE_SELECT, DATA_ALLINTS, DeviceId, IvhdPtr);
+ } else if (DeviceId == DEVICEID_IDE) {
+ // For IDE device 0x14, function 1, first check if in combined mode
+ if (SataEnableRegValue & SATA_COMBINED_MODE) {
+ // Create Alias entry in combined mode
+ IommuCreate8ByteEntry (DE_ALIASSELECT, DATA_NOINTS, DEVICEID_IDE, 0, DEVICEID_SATA, 00, IvhdPtr);
+ } else {
+ //Create select entry if not in the combined mode
+ IommuCreate4ByteEntry (DE_SELECT, 0, DeviceId, IvhdPtr);
+ }
+ } else {
+ // For all other single function devices other than device 0x14, functions 0 or 1, create select entry
+ IommuCreate4ByteEntry (DE_SELECT, 0, DeviceId, IvhdPtr);
+ }
+
+ // Record the largest bus, device, function which will be used as a mask by the Iommu L2 cache
+ // Record if phantom device present for current device. Only set it if present, do not clear.
+// if (IommuCheckPhantom (DeviceId, pConfig)) {
+// PciPtr->PhantomFunction = TRUE;
+// }
+ IommuRecordBusDevFuncInfo (PciPtr, DeviceId, pConfig);
+ }
+
+ if (MultiFunction && (!DiscreteEntry) && (!SubFunction)) {
+
+ // This is a multifunction device without a bridge, so create a type 3 and 4 device entry
+ IommuCreate4ByteEntry (DE_START, 0, DeviceId, IvhdPtr);
+ if (SrIovDevice) {
+ IommuCreate4ByteEntry (DE_END, 0, (DeviceId | 0x00FF), IvhdPtr);
+ } else {
+ IommuCreate4ByteEntry (DE_END, 0, DeviceId + HighFunction, IvhdPtr);
+ }
+
+ // Record the largest bus, device, function which will be used as a mask by the Iommu L2 cache
+ // Record if phantom device present for current device. Only set it if present, do not clear.
+// if (IommuCheckPhantom (DeviceId, pConfig)) {
+// PciPtr->PhantomFunction = TRUE;
+// }
+ IommuRecordBusDevFuncInfo (PciPtr, DeviceId + HighFunction, pConfig);
+ }
+
+ if (ClassCode == PCI_BRIDGE_CLASS) {
+ UINTN Type;
+ UINT32 BusData;
+ // STEP 3 - BRIDGE DEVICE
+ // These are treated a little differently. We already created the entry for the bridge itself...
+ // For a PCIe bridge, continue down the bridge device creating more entries until we find an endpoint
+ // For a PCI bridge, define the entire sub-bus range as belonging to this source id
+ // For a PCIX bridge, figure out which mode it is operating in
+ PciAddress.AddressValue = MAKE_SBDFO (0, (DeviceId >> 8) & 0xFF, (DeviceId >> 3) & 0x1F, DeviceId & 0x7, 0);
+ LibNbPciRead (PciAddress.AddressValue | PCI_BUS, AccessWidth32, &BusData, pConfig);
+ PcieCapBase = LibNbFindPciCapability (PciAddress.AddressValue, PCIE_CAPID, pConfig);
+ PcixCapBase = LibNbFindPciCapability (PciAddress.AddressValue, PCIX_CAPID, pConfig);
+
+ Type = 0;
+ if (PcieCapBase != 0) {
+ Type = 1;
+ LibNbPciRead (PciAddress.AddressValue | PcieCapBase, AccessWidth32, &PciData, pConfig);
+ PciData = (PciData >> 16) & PCIE_PORTMASK; // [7:4] are Device/Port type, 01
+ if (PciData == PCIE_PCIE2PCIX) {
+ Type = 2;
+ }
+ }
+ if (PcixCapBase != 0) {
+ Type = 2;
+ }
+
+ //For Hot plug capable devices, create 'Start of range' and 'End of range' IVRS'.
+ // This will override Type 1 and Type 2.
+ if (IommuCheckHp (DeviceId, pConfig)) {
+ Type = 3;
+ }
+
+ switch (Type) {
+ case 0:
+ //PCI
+ IommuRecordBusDevFuncInfo (PciPtr, DeviceId, pConfig);
+ IommuCreate8ByteEntry (DE_ALIASSTART, DATA_NOINTS, (UINT16) (BusData & 0xFF00), 0, DeviceId, 0, IvhdPtr);
+ IommuCreate4ByteEntry (DE_END, 0, (UINT16) (((BusData & 0xFF0000) >> 8) + 0xFF), IvhdPtr);
+ break;
+ case 1:
+ //Pcie (non hot plug)
+ for (DeviceCount = 0; DeviceCount <= 0x1f; DeviceCount++) {
+ for (FunctionCount = 0; FunctionCount <= 0x7; FunctionCount++) {
+ IommuCreateDeviceEntry (PciPtr, ((UINT16) (BusData & 0xFF00)) | (DeviceCount << 3) | FunctionCount, IvhdPtr, pConfig);
+ }
+ }
+ break;
+ case 2:
+ //PCIx
+ IommuRecordBusDevFuncInfo (PciPtr, (UINT16) (BusData & 0xFF00), pConfig);
+ IommuCreate8ByteEntry (DE_ALIASSTART, DATA_NOINTS, (UINT16) ((BusData & 0xFF00) | ( 1 << 3)), 0, (UINT16) (BusData & 0xFF00), 0, IvhdPtr);
+ IommuCreate4ByteEntry (DE_END, 0, (UINT16) (((BusData & 0xFF0000) >> 8) + 0xFF), IvhdPtr);
+ break;
+ case 3:
+ //For Hot plug ports, set all devices and functions behind the secondary bus.
+ IommuCreate4ByteEntry (DE_START, 0, (UINT16) (BusData & 0xFF00), IvhdPtr); // Secondary bus, Device 0, Function 0
+ IommuCreate4ByteEntry (DE_END, 0, (UINT16) ((BusData & 0xFF00) | (0x1F << 3) | 7), IvhdPtr); // Secondary bus, Device 1f, Function 7
+ break;
+ default:
+ CIMX_ASSERT (FALSE);
+ }
+ }
+}
+
+/*----------------------------------------------------------------------------------------*/
+/**
+ * Place IVHD device entry (type 2 for single function or 3/4 for multifunction) at end of IVHD entry
+ *
+ * @param[in, out] PciPtr Pci topology flags
+ * @param[in] DeviceId DeviceID
+ * @param[in] pConfig NB config structute
+ */
+VOID
+IommuRecordBusDevFuncInfo (
+ IN OUT IOMMU_PCI_TOPOLOGY *PciPtr,
+ IN UINT16 DeviceId,
+ IN AMD_NB_CONFIG *pConfig
+ )
+{
+ UINT16 ExtendedCapabilityPtr;
+ PCI_ADDR Device;
+ Device.AddressValue = MAKE_SBDFO (0, DeviceId >> 8, (DeviceId >> 3) & 0x1f, DeviceId & 0x7, 0);
+#ifdef EXCLUDE_SB_DEVICE_FROM_L2_HASH
+ if ((UINT8)Device.Address.Bus == 0) {
+ AMD_NB_CONFIG_BLOCK *ConfigPtr = GET_BLOCK_CONFIG_PTR (pConfig);
+ if (ConfigPtr->PlatformType == ServerPlatform) {
+ return;
+ }
+ }
+#endif
+ Device.AddressValue = MAKE_SBDFO (0, DeviceId >> 8, (DeviceId >> 3) & 0x1f, DeviceId & 0x7, 0);
+ CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (pConfig), CIMX_NB_TRACE), " Device Data For L2 Hash Bus = 0x%x Device = 0x%x Func = 0x%x\n", Device.Address.Bus, Device.Address.Device, Device.Address.Function));
+ ExtendedCapabilityPtr = LibNbFindPcieExtendedCapability (Device.AddressValue, 0x10, pConfig);
+ if (ExtendedCapabilityPtr != 0) {
+ UINT16 TotalVF;
+ LibNbPciRead (Device.AddressValue | (ExtendedCapabilityPtr + 0xE), AccessWidth16, &TotalVF, pConfig);
+ PciPtr->MaxFunction = CIMX_MAX (PciPtr->MaxFunction, TotalVF);
+ }
+ PciPtr->MaxBus = CIMX_MAX (PciPtr->MaxBus, (UINT8)Device.Address.Bus);
+ PciPtr->MaxDevice = CIMX_MAX (PciPtr->MaxDevice, (UINT8)Device.Address.Device);
+ PciPtr->MaxFunction = CIMX_MAX (PciPtr->MaxFunction, (UINT16) (UINT8)Device.Address.Function);
+}
+
+
+/*----------------------------------------------------------------------------------------*/
+/**
+ * Append data entry to IVRS
+ *
+ * @param[in] Type IVRC entry type
+ * @param[in] Data IVRC entry data
+ * @param[in] Word1 IVRC entry data
+ * @param[in, out] IvhdPtr Current IVHD pointer
+ *
+ */
+VOID
+IommuCreate4ByteEntry (
+ IN UINT8 Type,
+ IN UINT8 Data,
+ IN UINT16 Word1,
+ IN OUT IOMMU_IVHD_ENTRY *IvhdPtr
+ )
+{
+ UINT32 Buffer;
+ UINT16 AlignedDeviceEntryIndex;
+ UINT16 DeviceEntryIndex;
+
+ Buffer = Type + (Word1 << 8) + (Data << 24);
+ DeviceEntryIndex = (IvhdPtr->Length - 24) / sizeof (UINT32);
+ AlignedDeviceEntryIndex = DeviceEntryIndex;
+
+#ifdef IVHD_MIN_8BYTE_ALIGNMENT
+ AlignedDeviceEntryIndex = (DeviceEntryIndex + 1) & 0xfffe;
+#endif
+
+ IvhdPtr->DeviceEntry[AlignedDeviceEntryIndex] = Buffer;
+ IvhdPtr->Length += (4 + (AlignedDeviceEntryIndex - DeviceEntryIndex) * 4);
+ CIMX_TRACE ((TRACE_DATA (NULL, CIMX_NB_TRACE), "[NBIOMMU]Added entry - [0x%x]\n", Buffer));
+}
+
+/*----------------------------------------------------------------------------------------*/
+/**
+ * Append data entry to IVRS
+ *
+ * @param[in] Type IVRC entry type
+ * @param[in] Data IVRC entry data
+ * @param[in] Word1 IVRC entry data
+ * @param[in] Byte4 IVRC entry data
+ * @param[in] Word5 IVRC entry data
+ * @param[in] Byte7 IVRC entry data
+ * @param[in, out] IvhdPtr Current IVHD pointer
+ *
+ */
+VOID
+IommuCreate8ByteEntry (
+ IN UINT8 Type,
+ IN UINT8 Data,
+ IN UINT16 Word1,
+ IN UINT8 Byte4,
+ IN UINT16 Word5,
+ IN UINT8 Byte7,
+ IN OUT IOMMU_IVHD_ENTRY *IvhdPtr
+ )
+{
+ UINT16 AlignedDeviceEntryIndex;
+ UINT16 DeviceEntryIndex;
+ UINT64 Buffer;
+
+ Buffer = Type + (Word1 << 8) + ((UINT32)Data << 24);
+ ((UINT32*)&Buffer)[1] = Byte4 + (Word5 << 8) + (Byte7 << 24);
+ DeviceEntryIndex = (IvhdPtr->Length - 24) / sizeof (UINT32);
+ AlignedDeviceEntryIndex = DeviceEntryIndex;
+
+#if defined (IVHD_MIN_8BYTE_ALIGNMENT) || defined (IVHD_SIZE_ALIGNMENT)
+ AlignedDeviceEntryIndex = (DeviceEntryIndex + 1) & 0xfffe;
+#endif
+ IvhdPtr->DeviceEntry[AlignedDeviceEntryIndex] = ((UINT32*)&Buffer)[0];
+ IvhdPtr->DeviceEntry[AlignedDeviceEntryIndex + 1] = ((UINT32*)&Buffer)[1];
+ IvhdPtr->Length += (8 + (AlignedDeviceEntryIndex - DeviceEntryIndex) * 4);
+ CIMX_TRACE ((TRACE_DATA (NULL, CIMX_NB_TRACE), "[NBIOMMU]Added entry - [0x%llx]\n", Buffer));
+}
+
+/*----------------------------------------------------------------------------------------*/
+/**
+ * Set checksum, IvInfo, finish IVRS table
+ *
+ * @param[in, out] BufferPtr Pointer to text buffer to return IVRS.
+ * @param[in, out] pConfig Northbridge configuration structure pointer.
+ *
+ */
+VOID
+IommuFinalizeIvrs (
+ IN OUT VOID *BufferPtr,
+ IN AMD_NB_CONFIG *pConfig
+ )
+{
+ IOMMU_IVRS_HEADER *HeaderPtr;
+ PCI_ADDR IommuPciAddress;
+ UINT32 PciData;
+
+ HeaderPtr = (IOMMU_IVRS_HEADER *)BufferPtr;
+ IommuPciAddress = pConfig->NbPciAddress;
+ IommuPciAddress.Address.Function = NB_IOMMU;
+
+ // Find common IvInfo (largest shared) 0x50
+ // [22] = ATS Translation Reserved
+ // [21:15] = VA Size
+ // [14:8] = PA Size
+
+ LibNbPciRead (IommuPciAddress.AddressValue | RD890S_CAP_MISC, AccessWidth32, &PciData, pConfig);
+ PciData &= (IVINFO_ATSMASK | IVINFO_VAMASK | IVINFO_PAMASK);
+ HeaderPtr->IvInfo = PciData;
+
+ //LibAmdUpdateAcpiTableChecksum (HeaderPtr);
+}
+
+/*----------------------------------------------------------------------------------------*/
+/**
+ * Nb Iommu Fixup of IVRS APIC entries
+ *
+ * @param[in] DevicePtr Pointer to current device entry
+ * @param[in] pConfig Northbridge configuration structure pointer.
+ *
+ */
+UINT64
+IommuGetApicBaseAddress (
+ IN VOID *DevicePtr,
+ IN AMD_NB_CONFIG *pConfig
+ )
+{
+ PCI_ADDR PciAddress;
+ UINT16 DeviceId;
+ UINT32 Data;
+
+ // If no pointer provided, return no base address
+ if (DevicePtr == NULL) {
+ return 0;
+ }
+
+ // Special entry can be IOAPIC or other(HPET). We only care about the IOAPIC.
+ if (*(UINT8*) ((UINT8*)DevicePtr + DE_SPECIAL_VARIETY) != VARIETY_IOAPIC) {
+ return 0;
+ }
+
+ DeviceId = *(UINT16*) ((UINT8*)DevicePtr + DE_DEVICEID);
+ PciAddress.AddressValue = MAKE_SBDFO (0, (DeviceId >> 8) & 0xFF, (DeviceId >> 3) & 0x1F, 0, 0);
+
+ // An APIC entry will only be created for AMD northbridge or southbridges, so
+ // we can assume PCI dev/func = 0, 0 will be a northbridge IOAPIC device
+ // and any other will be a southbridge IOAPIC device. If the device was not
+ // already enabled and known to be an AMD device, no entry would have been created.
+
+ if ((PciAddress.Address.Device == NB_PCI_DEV) && (PciAddress.Address.Function == NB_HOST)) {
+
+ // We have an AMD NB, check function 0
+ Data = 1;
+ PciAddress.Address.Function = 0;
+ LibNbPciWrite (PciAddress.AddressValue | 0xF8, AccessS3SaveWidth32, &Data, pConfig);
+ LibNbPciRead (PciAddress.AddressValue | 0xFC, AccessWidth32, &Data, pConfig);
+ } else {
+ SB_INFO SbInfo;
+ SbInfo = LibAmdSbGetRevisionInfo ((pConfig == NULL)?NULL:GET_BLOCK_CONFIG_PTR (pConfig));
+ if (SbInfo.Type == SB_SB700) {
+ PciAddress.Address.Function = 0;
+ LibNbPciRead (PciAddress.AddressValue | 0x74, AccessWidth32, &Data, pConfig);
+ } else {
+ LibAmdSbPmioRead ( 0x34, AccessWidth32, &Data, pConfig);
+ }
+ }
+ return ((UINT64) (Data & 0xFFFFFF00));
+}
+
+/*----------------------------------------------------------------------------------------*/
+/**
+ * Nb Iommu Fixup of IVRS APIC entries
+ *
+ * @param[in] BaseAddress Base address to match
+ * @param[in] MadtPtr Pointer to current device entry
+ * @param[in] pConfig Northbridge configuration structure pointer.
+ *
+ */
+UINT8
+IommuGetApicId (
+ IN UINT64 BaseAddress,
+ IN VOID *MadtPtr,
+ IN AMD_NB_CONFIG *pConfig
+ )
+{
+ VOID *EntryPtr;
+
+ EntryPtr = LibAmdGetFirstMadtStructure (MADT_APIC_TYPE, MadtPtr);
+
+ do {
+ // If our base address for a known device matches this MADT, get the APIC ID
+ if (*(UINT32*) ((UINT8*)EntryPtr + MADT_APIC_BASE) == (UINT32)BaseAddress) {
+ return *(UINT8*) ((UINT8*)EntryPtr + MADT_APIC_ID);
+ }
+ EntryPtr = LibAmdGetNextMadtStructure (MADT_APIC_TYPE, EntryPtr, MadtPtr);
+ } while (EntryPtr != NULL);
+ return 0xFF;
+}
+
+/*----------------------------------------------------------------------------------------*/
+/**
+ * Disconnect unused PCIe core from IOMMU block.
+ *
+ * @param[in] CoreId Pcie Core Id
+ * @param[in] pConfig Northbridge configuration structure pointer.
+ *
+ */
+VOID
+NbIommuDisconnectPcieCore (
+ IN CORE CoreId,
+ IN AMD_NB_CONFIG *pConfig
+ )
+{
+ PCI_ADDR IommuPciAddress;
+ UINT32 Value;
+ IommuPciAddress = pConfig->NbPciAddress;
+ IommuPciAddress.Address.Function = NB_IOMMU;
+ Value = 1 << ((0x4310 >> (CoreId * 4)) & 0xf);
+ LibNbPciIndexRMW (IommuPciAddress.AddressValue | L2CFG_INDEX, L2CFG_SEL_WR_EN | L2REG_46, AccessS3SaveWidth32, 0xFFFFFFFF, Value , pConfig);
+}