/** @file
FDT client library for consumers of PCI related dynamic PCDs
Copyright (c) 2016, Linaro Ltd. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include
#include
#include
#include
#include
#include
//
// We expect the "ranges" property of "pci-host-ecam-generic" to consist of
// records like this.
//
#pragma pack (1)
typedef struct {
UINT32 Type;
UINT64 ChildBase;
UINT64 CpuBase;
UINT64 Size;
} DTB_PCI_HOST_RANGE_RECORD;
#pragma pack ()
#define DTB_PCI_HOST_RANGE_RELOCATABLE BIT31
#define DTB_PCI_HOST_RANGE_PREFETCHABLE BIT30
#define DTB_PCI_HOST_RANGE_ALIASED BIT29
#define DTB_PCI_HOST_RANGE_MMIO32 BIT25
#define DTB_PCI_HOST_RANGE_MMIO64 (BIT25 | BIT24)
#define DTB_PCI_HOST_RANGE_IO BIT24
#define DTB_PCI_HOST_RANGE_TYPEMASK (BIT31 | BIT30 | BIT29 | BIT25 | BIT24)
STATIC
RETURN_STATUS
GetPciIoTranslation (
IN FDT_CLIENT_PROTOCOL *FdtClient,
IN INT32 Node,
OUT UINT64 *IoTranslation
)
{
UINT32 RecordIdx;
CONST VOID *Prop;
UINT32 Len;
EFI_STATUS Status;
UINT64 IoBase;
//
// Iterate over "ranges".
//
Status = FdtClient->GetNodeProperty (FdtClient, Node, "ranges", &Prop, &Len);
if (EFI_ERROR (Status) || (Len == 0) ||
(Len % sizeof (DTB_PCI_HOST_RANGE_RECORD) != 0))
{
DEBUG ((DEBUG_ERROR, "%a: 'ranges' not found or invalid\n", __func__));
return RETURN_PROTOCOL_ERROR;
}
for (RecordIdx = 0; RecordIdx < Len / sizeof (DTB_PCI_HOST_RANGE_RECORD);
++RecordIdx)
{
CONST DTB_PCI_HOST_RANGE_RECORD *Record;
UINT32 Type;
Record = (CONST DTB_PCI_HOST_RANGE_RECORD *)Prop + RecordIdx;
Type = SwapBytes32 (Record->Type) & DTB_PCI_HOST_RANGE_TYPEMASK;
if (Type == DTB_PCI_HOST_RANGE_IO) {
IoBase = SwapBytes64 (Record->ChildBase);
*IoTranslation = SwapBytes64 (Record->CpuBase) - IoBase;
return RETURN_SUCCESS;
}
}
return RETURN_NOT_FOUND;
}
RETURN_STATUS
EFIAPI
FdtPciPcdProducerLibConstructor (
VOID
)
{
UINT64 PciExpressBaseAddress;
FDT_CLIENT_PROTOCOL *FdtClient;
CONST UINT64 *Reg;
UINT32 RegSize;
EFI_STATUS Status;
INT32 Node;
RETURN_STATUS RetStatus;
UINT64 IoTranslation;
RETURN_STATUS PcdStatus;
PciExpressBaseAddress = PcdGet64 (PcdPciExpressBaseAddress);
if (PciExpressBaseAddress != MAX_UINT64) {
//
// Assume that the fact that PciExpressBaseAddress has been changed from
// its default value of MAX_UINT64 implies that this code has been
// executed already, in the context of another module. That means we can
// assume that PcdPciIoTranslation has been discovered from the DT node
// as well.
//
return EFI_SUCCESS;
}
Status = gBS->LocateProtocol (
&gFdtClientProtocolGuid,
NULL,
(VOID **)&FdtClient
);
ASSERT_EFI_ERROR (Status);
PciExpressBaseAddress = 0;
Status = FdtClient->FindCompatibleNode (
FdtClient,
"pci-host-ecam-generic",
&Node
);
if (!EFI_ERROR (Status)) {
Status = FdtClient->GetNodeProperty (
FdtClient,
Node,
"reg",
(CONST VOID **)&Reg,
&RegSize
);
if (!EFI_ERROR (Status) && (RegSize == 2 * sizeof (UINT64))) {
PciExpressBaseAddress = SwapBytes64 (*Reg);
PcdStatus = PcdSetBoolS (PcdPciDisableBusEnumeration, FALSE);
ASSERT_RETURN_ERROR (PcdStatus);
IoTranslation = 0;
RetStatus = GetPciIoTranslation (FdtClient, Node, &IoTranslation);
if (!RETURN_ERROR (RetStatus)) {
PcdStatus = PcdSet64S (PcdPciIoTranslation, IoTranslation);
ASSERT_RETURN_ERROR (PcdStatus);
} else {
//
// Support for I/O BARs is not mandatory, and so it does not make sense
// to abort in the general case. So leave it up to the actual driver to
// complain about this if it wants to, and just issue a warning here.
//
DEBUG ((
DEBUG_WARN,
"%a: 'pci-host-ecam-generic' device encountered with no I/O range\n",
__func__
));
}
}
}
PcdStatus = PcdSet64S (PcdPciExpressBaseAddress, PciExpressBaseAddress);
ASSERT_RETURN_ERROR (PcdStatus);
return RETURN_SUCCESS;
}