diff options
Diffstat (limited to 'Silicon/Intel/PurleyRcPkg/Library/UsraAccessLib/PcieAccess.c')
-rw-r--r-- | Silicon/Intel/PurleyRcPkg/Library/UsraAccessLib/PcieAccess.c | 360 |
1 files changed, 360 insertions, 0 deletions
diff --git a/Silicon/Intel/PurleyRcPkg/Library/UsraAccessLib/PcieAccess.c b/Silicon/Intel/PurleyRcPkg/Library/UsraAccessLib/PcieAccess.c new file mode 100644 index 0000000000..5af2c5953a --- /dev/null +++ b/Silicon/Intel/PurleyRcPkg/Library/UsraAccessLib/PcieAccess.c @@ -0,0 +1,360 @@ +/** @file + +Copyright (c) 2018, Intel Corporation. All rights reserved.<BR> +This program and the accompanying materials are licensed and made available under +the terms and conditions of the BSD License that accompanies this distribution. +The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "UsraAccessLib.h" + +#define MAX_IO_PORT_ADDRESS 0xFFFF + +// +// Lookup table for increment values based on transfer widths +// +UINT8 mInStride[] = { + 1, // UsraWidth8 + 2, // UsraWidth16 + 4, // UsraWidth32 + 8, // UsraWidth64 + 0, // UsraWidthFifo8 + 0, // UsraWidthFifo16 + 0, // UsraWidthFifo32 + 0, // UsraWidthFifo64 + 1, // UsraWidthFill8 + 2, // UsraWidthFill16 + 4, // UsraWidthFill32 + 8 // UsraWidthFill64 +}; + +// +// Lookup table for increment values based on transfer widths +// +UINT8 mOutStride[] = { + 1, // UsraWidth8 + 2, // UsraWidth16 + 4, // UsraWidth32 + 8, // UsraWidth64 + 1, // UsraWidthFifo8 + 2, // UsraWidthFifo16 + 4, // UsraWidthFifo32 + 8, // UsraWidthFifo64 + 0, // UsraWidthFill8 + 0, // UsraWidthFill16 + 0, // UsraWidthFill32 + 0 // UsraWidthFill64 +}; + + +/** + This API gets the Pcie address from the given USRA Address. + + @param[in] Global Global pointer + @param[in] Virtual Virtual address + @param[in] Address A pointer of the address of the USRA Address Structure + @param[out] AlignedAddress A pointer of aligned address converted from USRA address + + @retval NONE +**/ +VOID +GetPcieAccessAddress ( + IN VOID *Global, + IN BOOLEAN Virtual, + IN USRA_ADDRESS *Address, + OUT UINTN *AlignedAddress + ) +{ + INTN MmCfgBase; + + MmCfgBase = GetPcieSegMmcfgBaseAddress(Address); + // TODO: add Error Check for NULL later + *AlignedAddress = MmCfgBase + (UINTN)(Address->Attribute.RawData32[0] & 0x0fffffff); +} + +/** + This API performs 8-bit, 16-bit, 32-bit or 64-bit Pcie silicon register read operations. + It transfers data from a register into a naturally aligned data buffer. + + @param[in] Address A pointer of the address of the USRA Address Structure to be read out + @param[in] Buffer A pointer of buffer for the value read from the register + + @retval RETURN_SUCCESS The function completed successfully. +**/ +RETURN_STATUS +PcieRegisterRead ( + IN USRA_ADDRESS *Address, + IN VOID *Buffer + ) +{ + UINTN AlignedAddress; + + GetPcieAccessAddress (NULL, 0, Address, &AlignedAddress); + UsraRegAlignedRead((UINT32)Address->Attribute.AccessWidth, AlignedAddress, Buffer); + + return RETURN_SUCCESS; +} + +/** + Check parameters to PcieBlkRegisterRead() function request. + + The I/O operations are carried out exactly as requested. The caller is responsible + for satisfying any alignment and I/O width restrictions that a PI System on a + platform might require. For example on some platforms, width requests of + UsraWidth64 do not work. Misaligned buffers, on the other hand, will + be handled by the driver. + + @param[in] MmioOperation TRUE for an MMIO operation, FALSE for I/O Port operation. + @param[in] Width Signifies the width of the I/O or Memory operation. + @param[in] Address The base address of the I/O operation. + @param[in] Count The number of I/O operations to perform. The number of + bytes moved is Width size * Count, starting at Address. + @param[in] Buffer For read operations, the destination buffer to store the results. + For write operations, the source buffer from which to write data. + + @retval EFI_SUCCESS The parameters for this request pass the checks. + @retval EFI_INVALID_PARAMETER Buffer is NULL. + @retval EFI_INVALID_PARAMETER Width is invalid for this PI system. + @retval EFI_UNSUPPORTED The Buffer is not aligned for the given Width. + @retval EFI_UNSUPPORTED The address range specified by Address, Width, + and Count is not valid for this PI system. + +**/ +STATIC +RETURN_STATUS +CpuIoCheckParameter ( + IN BOOLEAN MmioOperation, + IN USRA_ACCESS_WIDTH Width, + IN UINT64 Address, + IN UINTN Count, + IN VOID *Buffer + ) +{ + UINT64 MaxCount; + UINT64 Limit; + + // + // Check to see if Buffer is NULL + // + if (Buffer == NULL) { + return RETURN_INVALID_PARAMETER; + } + + // + // Check to see if Width is in the valid range + // + if ((UINT32)Width >= UsraWidthMaximum) { + return RETURN_INVALID_PARAMETER; + } + + // + // For FIFO type, the target address won't increase during the access, + // so treat Count as 1 + // + if (Width >= UsraWidthFifo8 && Width <= UsraWidthFifo64) { + Count = 1; + } + + // + // Check to see if Width is in the valid range for I/O Port operations + // + Width = (USRA_ACCESS_WIDTH) (Width & 0x03); + if (!MmioOperation && (Width == UsraWidth64)) { + return RETURN_INVALID_PARAMETER; + } + + // + // Check to see if Address is aligned + // + if ((Address & (UINT64)(mInStride[Width] - 1)) != 0) { + return RETURN_UNSUPPORTED; + } + + // + // Check to see if any address associated with this transfer exceeds the maximum + // allowed address. The maximum address implied by the parameters passed in is + // Address + Size * Count. If the following condition is met, then the transfer + // is not supported. + // + // Address + Size * Count > (MmioOperation ? MAX_ADDRESS : MAX_IO_PORT_ADDRESS) + 1 + // + // Since MAX_ADDRESS can be the maximum integer value supported by the CPU and Count + // can also be the maximum integer value supported by the CPU, this range + // check must be adjusted to avoid all oveflow conditions. + // + // The following form of the range check is equivalent but assumes that + // MAX_ADDRESS and MAX_IO_PORT_ADDRESS are of the form (2^n - 1). + // + Limit = (MmioOperation ? MAX_ADDRESS : MAX_IO_PORT_ADDRESS); + if (Count == 0) { + if (Address > Limit) { + return RETURN_UNSUPPORTED; + } + } else { + MaxCount = RShiftU64 (Limit, Width); + if (MaxCount < (Count - 1)) { + return RETURN_UNSUPPORTED; + } + if (Address > LShiftU64 (MaxCount - Count + 1, Width)) { + return RETURN_UNSUPPORTED; + } + } + + // + // Check to see if Buffer is aligned + // (IA-32 allows UINT64 and INT64 data types to be 32-bit aligned.) + // + if (((UINTN)Buffer & ((MIN (sizeof (UINTN), mInStride[Width]) - 1))) != 0) { + return RETURN_UNSUPPORTED; + } + + return RETURN_SUCCESS; +} + +/** + This API performs 8-bit, 16-bit, 32-bit or 64-bit Pcie block silicon register read operations. + It transfers data from a register into a naturally aligned data buffer. + + @param[in] Address A pointer of the address of the USRA Address Structure to be read out + @param[in] Buffer A pointer of buffer for the value read from the register + + @retval RETURN_SUCCESS The function completed successfully. + @retval Others Some error occurs when executing CpuIoCheckParameter function. +**/ +RETURN_STATUS +PcieBlkRegisterRead ( + IN USRA_ADDRESS *Address, + IN VOID *Buffer + ) +{ + UINT8 InStride; + UINT8 OutStride; + RETURN_STATUS Status; + UINTN AlignedAddress; + UINT32 ReadCount = Address->PcieBlk.Count; + UINT8 *UINT8Buffer; + + GetPcieAccessAddress (NULL, 0, Address, &AlignedAddress); + Status = CpuIoCheckParameter (TRUE, Address->Attribute.AccessWidth, AlignedAddress, ReadCount, Buffer); + if (RETURN_ERROR (Status)) { + return Status; + } + + InStride = mInStride[Address->Attribute.AccessWidth]; + OutStride = mOutStride[Address->Attribute.AccessWidth]; + for (UINT8Buffer = Buffer; ReadCount > 0; AlignedAddress += InStride, UINT8Buffer += OutStride, ReadCount--) { + UsraRegAlignedRead((USRA_ACCESS_WIDTH) (Address->Attribute.AccessWidth & 0x03), AlignedAddress, (VOID *)UINT8Buffer); + } + + return RETURN_SUCCESS; +} + +/** + This API performs 8-bit, 16-bit, 32-bit or 64-bit Pcie silicon register write operations. + It transfers data from a naturally aligned data buffer into a register. + + @param[in] Address A pointer of the address of the USRA Address Structure to be written + @param[in] Buffer A pointer of buffer for the value write to the register + + @retval RETURN_SUCCESS The function completed successfully. +**/ +RETURN_STATUS +PcieRegisterWrite ( + IN USRA_ADDRESS *Address, + OUT VOID *Buffer + ) +{ + UINTN AlignedAddress; + + GetPcieAccessAddress(NULL, 0, Address, &AlignedAddress); + UsraRegAlignedWrite((UINT32)Address->Attribute.AccessWidth, AlignedAddress, Buffer); + + if (FeaturePcdGet (PcdUsraSupportS3)) + { + if(Address->Attribute.S3Enable) + { + S3BootScriptSaveMemWrite ((S3_BOOT_SCRIPT_LIB_WIDTH)Address->Attribute.AccessWidth, (UINT64)AlignedAddress, 1, Buffer); + } + } + + return RETURN_SUCCESS; +} + +/** + This API performs 8-bit, 16-bit, 32-bit or 64-bit Pcie block silicon register write operations. + It transfers data from a naturally aligned data buffer into a register. + + @param[in] Address A pointer of the address of the USRA Address Structure to be written + @param[in] Buffer A pointer of buffer for the value write to the register + + @retval RETURN_SUCCESS The function completed successfully. + @retval Others Some error occurs when executing CpuIoCheckParameter function. +**/ +RETURN_STATUS +PcieBlkRegisterWrite ( + IN USRA_ADDRESS *Address, + OUT VOID *Buffer + ) +{ + UINT8 InStride; + UINT8 OutStride; + RETURN_STATUS Status; + UINTN AlignedAddress; + UINT32 WriteCount = Address->PcieBlk.Count; + UINT8 *UINT8Buffer; + + GetPcieAccessAddress (NULL, 0, Address, &AlignedAddress); + Status = CpuIoCheckParameter (TRUE, Address->Attribute.AccessWidth, AlignedAddress, WriteCount, Buffer); + if (RETURN_ERROR (Status)) { + return Status; + } + + InStride = mInStride[Address->Attribute.AccessWidth]; + OutStride = mOutStride[Address->Attribute.AccessWidth]; + for (UINT8Buffer = Buffer; WriteCount > 0; AlignedAddress += InStride, UINT8Buffer += OutStride, WriteCount--) { + UsraRegAlignedWrite((USRA_ACCESS_WIDTH) (Address->Attribute.AccessWidth & 0x03), AlignedAddress, (VOID *)UINT8Buffer); + + if (FeaturePcdGet (PcdUsraSupportS3)) { + if(Address->Attribute.S3Enable) { + S3BootScriptSaveMemWrite ((S3_BOOT_SCRIPT_LIB_WIDTH)(Address->Attribute.AccessWidth & 0x03), (UINT64)AlignedAddress, 1, (VOID *)UINT8Buffer); + } + } + } + + return RETURN_SUCCESS; +} + +/** + This API performs 8-bit, 16-bit, 32-bit or 64-bit Pcie silicon register AND then OR operations. It read data from a + register, And it with the AndBuffer, then Or it with the OrBuffer, and write the result back to the register + + @param[in] Address A pointer of the address of the silicon register to be modified + @param[in] AndBuffer A pointer of buffer for the value used for AND operation + A NULL pointer means no AND operation. RegisterModify() equivalents to RegisterOr() + @param[in] OrBuffer A pointer of buffer for the value used for OR operation + A NULL pointer means no OR operation. RegisterModify() equivalents to RegisterAnd() + + @retval RETURN_SUCCESS The function completed successfully. +**/ +RETURN_STATUS +PcieRegisterModify ( + IN USRA_ADDRESS *Address, + IN VOID *AndBuffer, + IN VOID *OrBuffer + ) +{ + UINT64 Data; + UINT8 WidthTable[] = {1,2,4,8}; + + PcieRegisterRead(Address, &Data); + DataAndOr (&Data, AndBuffer, OrBuffer, WidthTable[(UINT8)Address->Attribute.AccessWidth]); + PcieRegisterWrite(Address, &Data); + + return RETURN_SUCCESS; +} + |