blob: 705b1a82fdff24da511381795605c036f79ec5e7 [file] [log] [blame]
/**
* @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);
}