/** @file
This file contains PSF routines for RC usage
Copyright (c) 2019 Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "PchPsfPrivateLibInternal.h"
/**
Disable device at PSF level
Method not for bridges (e.g. PCIe Root Port)
@param[in] PsfPort PSF PORT data structure
**/
VOID
PsfDisableDevice (
IN PSF_PORT PsfPort
)
{
if (PSF_IS_PORT_NULL (PsfPort)) {
ASSERT (FALSE);
return;
}
//
// Read back is needed to enforce the sideband and primary ordering.
//
PchPcrAndThenOr32WithReadback (
PsfPort.PsfPid,
PsfPort.RegBase + R_PCH_PSFX_PCR_T0_SHDW_PCIEN,
~0u,
B_PCH_PSFX_PCR_T0_SHDW_PCIEN_FUNDIS
);
}
/**
Hide PciCfgSpace of device at PSF level
Method not for bridges (e.g. PCIe Root Port)
@param[in] PsfPort PSF PORT data structure
**/
VOID
PsfHideDevice (
IN PSF_PORT PsfPort
)
{
if (PSF_IS_PORT_NULL (PsfPort)) {
ASSERT (FALSE);
return;
}
//
// Read back is needed to enforce the sideband and primary ordering.
// If there is PCI access right after the PSF hide device, the device might
// still be accessible since the PSF cycle is not completed yet, and causes
// the race condition between sideband and primary cycles.
//
PchPcrAndThenOr32WithReadback (
PsfPort.PsfPid,
PsfPort.RegBase + R_PCH_PSFX_PCR_T0_SHDW_CFG_DIS,
~0u,
B_PCH_PSFX_PCR_T0_SHDW_CFG_DIS_CFGDIS
);
}
/**
Unhide PciCfgSpace of device at PSF level
Method not for bridges (e.g. PCIe Root Port)
@param[in] PsfPort PSF PORT data structure
**/
VOID
PsfUnhideDevice (
IN PSF_PORT PsfPort
)
{
if (PSF_IS_PORT_NULL (PsfPort)) {
ASSERT (FALSE);
return;
}
//
// Read back is needed to enforce the sideband and primary ordering.
//
PchPcrAndThenOr32WithReadback (
PsfPort.PsfPid,
PsfPort.RegBase + R_PCH_PSFX_PCR_T0_SHDW_CFG_DIS,
(UINT32) ~(B_PCH_PSFX_PCR_T0_SHDW_CFG_DIS_CFGDIS),
0
);
}
/**
Disable device BARs at PSF level
Method not for bridges (e.g. PCIe Root Port)
@param[in] PsfPort PSF PORT data structure
@param[in] BarDisMask BIT0-BAR0, BIT1-BAR1,...
Mask corresponds to 32bit wide BARs
**/
VOID
PsfDisableDeviceBar (
IN PSF_PORT PsfPort,
IN UINT32 BarDisMask
)
{
if (PSF_IS_PORT_NULL (PsfPort)) {
ASSERT (FALSE);
return;
}
//
// BAR0-5 supported
//
ASSERT (BarDisMask < BIT6);
//
// Read back is needed to enforce the sideband and primary ordering.
//
PchPcrAndThenOr32WithReadback (
PsfPort.PsfPid,
PsfPort.RegBase + R_PCH_PSFX_PCR_T0_SHDW_PCIEN,
~0u,
BarDisMask << N_PCH_PSFX_PCR_T0_SHDW_PCIEN_BARXDIS
);
}
/**
Enable device BARs at PSF level
Method not for bridges (e.g. PCIe Root Port)
@param[in] PsfPort PSF PORT data structure
@param[in] BarEnMask BIT0-BAR0, BIT1-BAR1,...
Mask corresponds to 32bit wide BARs
**/
VOID
PsfEnableDeviceBar (
IN PSF_PORT PsfPort,
IN UINT32 BarEnMask
)
{
if (PSF_IS_PORT_NULL (PsfPort)) {
ASSERT (FALSE);
return;
}
//
// BAR0-5 supported
//
ASSERT (BarEnMask < BIT6);
//
// Read back is needed to enforce the sideband and primary ordering.
//
PchPcrAndThenOr32WithReadback (
PsfPort.PsfPid,
PsfPort.RegBase + R_PCH_PSFX_PCR_T0_SHDW_PCIEN,
(UINT32)~(BarEnMask << N_PCH_PSFX_PCR_T0_SHDW_PCIEN_BARXDIS),
0
);
}
/**
Disable device IOSpace at PSF level
Method not for bridges (e.g. PCIe Root Port)
@param[in] PsfPort PSF PORT data structure
**/
VOID
PsfDisableDeviceIoSpace (
IN PSF_PORT PsfPort
)
{
if (PSF_IS_PORT_NULL (PsfPort)) {
ASSERT (FALSE);
return;
}
//
// Read back is needed to enforce the sideband and primary ordering.
//
PchPcrAndThenOr32WithReadback (
PsfPort.PsfPid,
PsfPort.RegBase + R_PCH_PSFX_PCR_T0_SHDW_PCIEN,
~(UINT32)(B_PCH_PSFX_PCR_T0_SHDW_PCIEN_IOEN),
0
);
}
/**
Enable device IOSpace at PSF level
Method not for bridges (e.g. PCIe Root Port)
@param[in] PsfPort PSF PORT data structure
**/
VOID
PsfEnableDeviceIoSpace (
IN PSF_PORT PsfPort
)
{
if (PSF_IS_PORT_NULL (PsfPort)) {
ASSERT (FALSE);
return;
}
//
// Read back is needed to enforce the sideband and primary ordering.
//
PchPcrAndThenOr32WithReadback (
PsfPort.PsfPid,
PsfPort.RegBase + R_PCH_PSFX_PCR_T0_SHDW_PCIEN,
~0u,
B_PCH_PSFX_PCR_T0_SHDW_PCIEN_IOEN
);
}
/**
Set device BARx address at PSF level
Method not for bridges (e.g. PCIe Root Port)
@param[in] PsfPort PSF PORT data structure
@param[in] BarNum BAR Number (0:BAR0, 1:BAR1, ...)
@param[in] BarValue 32bit BAR value
**/
VOID
PsfSetDeviceBarValue (
IN PSF_PORT PsfPort,
IN UINT8 BarNum,
IN UINT32 BarValue
)
{
ASSERT (BarNum < 6);
if (PSF_IS_PORT_NULL (PsfPort)) {
ASSERT (FALSE);
return;
}
//
// Read back is needed to enforce the sideband and primary ordering.
//
PchPcrAndThenOr32WithReadback (
PsfPort.PsfPid,
PsfPort.RegBase + R_PCH_PSFX_PCR_T0_SHDW_BAR0 + BarNum * 0x4,
0,
BarValue
);
}
/**
Hide PMC device at PSF level
**/
VOID
PsfHidePmcDevice (
VOID
)
{
PsfHideDevice (PsfPmcPort ());
}
/**
Set PMC ABASE value in PSF
@param[in] Address Address for ACPI base address.
**/
VOID
PsfSetPmcAbase (
IN UINT16 Address
)
{
PSF_PORT PsfPort;
PsfPort = PsfPmcPort ();
ASSERT (PchPcrRead32 (PsfPort.PsfPid, PsfPort.RegBase + R_PCH_PSFX_PCR_T0_SHDW_BAR4) != 0xFFFFFFFF);
//
// Disable IOSpace before changing the address
//
PsfDisableDeviceIoSpace (PsfPort);
//
// Program ABASE in PSF PMC space BAR4
//
PsfSetDeviceBarValue (PsfPort, 4, Address);
//
// Enable IOSpace
//
PsfEnableDeviceIoSpace (PsfPort);
}
/**
Get PMC ABASE value from PSF
@retval Address Address for ACPI base.
**/
UINT16
PsfGetPmcAbase (
VOID
)
{
UINT16 Address;
PSF_PORT PsfPort;
PsfPort = PsfPmcPort ();
//
// Read ABASE from PSF PMC space BAR4
//
Address = PchPcrRead16 (
PsfPort.PsfPid,
PsfPort.RegBase + R_PCH_PSFX_PCR_T0_SHDW_BAR4
);
ASSERT (Address != 0xFFFF);
return Address;
}
/**
Get PMC PWRMBASE value from PSF
@retval Address Address for PWRM base.
**/
UINT32
PsfGetPmcPwrmBase (
VOID
)
{
UINT32 Address;
PSF_PORT PsfPort;
PsfPort = PsfPmcPort ();
//
// Read PWRMBASE from PSF PMC space BAR0
//
Address = PchPcrRead32 (
PsfPort.PsfPid,
PsfPort.RegBase + R_PCH_PSFX_PCR_T0_SHDW_BAR0
);
ASSERT (Address != 0xFFFFFFFF);
return Address;
}
/**
Get PSF SideBand Port ID from PSF ID (1 - PSF1, 2 - PSF2, ...)
@param[in] PsfId PSF ID (1 - PSF1, 2 - PSF2, ...)
@retval PSF SideBand Port ID
**/
PCH_SBI_PID
PsfSbPortId (
UINT32 PsfId
)
{
UINT32 PsfTableIndex;
PSF_SEGMENT *PsfTable;
UINT32 PsfTableSize;
PsfSegments (&PsfTable, &PsfTableSize);
for (PsfTableIndex = 0; PsfTableIndex < PsfTableSize; PsfTableIndex++) {
if (PsfTable[PsfTableIndex].Id == PsfId) {
return PsfTable[PsfTableIndex].SbPid;
}
}
ASSERT (FALSE);
return 0;
}
/**
Get PCH Root PSF ID. This is the PSF segment to which OPDMI/DMI is connected.
@retval PsfId Root PSF ID
**/
UINT32
PsfRootId (
VOID
)
{
PSF_SEGMENT *PsfTable;
UINT32 PsfTableSize;
PsfSegments (&PsfTable, &PsfTableSize);
return PsfTable[0].Id;
}
/**
Add EOI Target in a given PSF
@param[in] PsfId PSF ID (1 - PSF1, 2 - PSF2, ...)
@param[in] TargetId EOI Target ID
**/
STATIC
VOID
PsfAddEoiTarget (
UINT32 PsfId,
PSF_PORT_DEST_ID TargetId
)
{
UINT16 EoiTargetBase;
UINT16 EoiControlBase;
UINT8 NumOfEnabledTargets;
UINT8 MaximalNumberOfTargets;
PCH_SBI_PID PsfSbiPortId;
UINT32 Data32;
UINT8 TargetIndex;
MaximalNumberOfTargets = PsfEoiRegData (PsfId, &EoiTargetBase, &EoiControlBase);
PsfSbiPortId = PsfSbPortId (PsfId);
//
// Get number of enabled agents from PSF_x_PSF_MC_CONTROL_MCAST0_RS0_EOI register
//
Data32 = PchPcrRead32 (PsfSbiPortId, EoiControlBase);
NumOfEnabledTargets = (UINT8) (Data32 >> N_PCH_PSFX_PCR_MC_CONTROL_MCASTX_NUMMC);
//
// Check if target was not already enabled
// Targets from a different PSF segment are aggregated into single destination on
// current PSF segment.
//
for (TargetIndex = 0; TargetIndex < NumOfEnabledTargets; TargetIndex++) {
Data32 = PchPcrRead32 (PsfSbiPortId, EoiTargetBase + TargetIndex * 4);
//
// If target already added don't add it again
//
if (Data32 == TargetId.RegVal) {
ASSERT (FALSE);
return;
}
//
// If target is from different PSF segment than currently being analyzed
// it is enough that its PsfID is matching
//
if ((Data32 & B_PCH_PSFX_PCR_TARGET_PSFID) >> N_PCH_PSFX_PCR_TARGET_PSFID == TargetId.Fields.PsfId) {
return;
}
}
//
// Check if next one can be added
//
if (NumOfEnabledTargets >= MaximalNumberOfTargets) {
ASSERT (FALSE);
return;
}
//
// Add next target
// Configure Multicast Destination ID register with target device on PSF.
// Configuration must be done in next available PSF_MC_AGENT_MCAST0_RS0_TGT_EOI register
// so that other targets are not overridden. is known from the number of multicast agents
// in Multicast Control Register. Value programmed is based on
// PsfID, PortGroupID, PortID and ChannelID of the target
//
PchPcrWrite32 (PsfSbiPortId, EoiTargetBase + NumOfEnabledTargets * 4, TargetId.RegVal);
//
// Enable new target
// Configure PSF_x_PSF_MC_CONTROL_MCAST0_RS0_EOI, increase NumMc and set MultCEn
//
NumOfEnabledTargets++;
Data32 = (NumOfEnabledTargets << N_PCH_PSFX_PCR_MC_CONTROL_MCASTX_NUMMC) | B_PCH_PSFX_PCR_MC_CONTROL_MCASTX_MULTCEN;
PchPcrWrite32 (PsfSbiPortId, EoiControlBase, Data32);
}
/**
Enable EOI Target
@param[in] TargetId Target ID
**/
STATIC
VOID
PsfEnableEoiTarget (
PSF_PORT_DEST_ID TargetId
)
{
UINT32 RootLevelPsf;
RootLevelPsf = PsfRootId ();
//
// Enable EOI target in root PSF
//
PsfAddEoiTarget (RootLevelPsf, TargetId);
//
// Enable EOI target on other PSF segment if target
// is not located on root PSF
//
if (TargetId.Fields.PsfId != RootLevelPsf) {
PsfAddEoiTarget (TargetId.Fields.PsfId, TargetId);
}
}
/**
This function enables EOI message forwarding in PSF for PCIe ports
for cases where IOAPIC is present behind this root port.
@param[in] RpIndex Root port index (0 based)
@retval Status
**/
EFI_STATUS
PsfConfigurEoiForPciePort (
IN UINT32 RpIndex
)
{
ASSERT (RpIndex < GetPchMaxPciePortNum ());
//
// If there is an IOAPIC discovered behind root port program PSF Multicast registers
// accordingly to PCH BWG PSF EOI Multicast Configuration
// Since there is a device behind RootPort to which EOI needs to be forwarded
// enable multicast (MULTCEN) and increase the number of multicast agents (NUMMC)
// in Multicast Control Register.
//
PsfEnableEoiTarget (PsfPcieDestinationId (RpIndex));
return EFI_SUCCESS;
}