1 /** @file 2 FDT client library for consumers of PCI related dynamic PCDs 3 4 Copyright (c) 2016, Linaro Ltd. All rights reserved.<BR> 5 6 This program and the accompanying materials 7 are licensed and made available under the terms and conditions of the BSD License 8 which accompanies this distribution. The full text of the license may be found at 9 http://opensource.org/licenses/bsd-license.php 10 11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 13 14 **/ 15 16 #include <Uefi.h> 17 18 #include <Library/BaseLib.h> 19 #include <Library/DebugLib.h> 20 #include <Library/PcdLib.h> 21 #include <Library/UefiBootServicesTableLib.h> 22 23 #include <Protocol/FdtClient.h> 24 25 // 26 // We expect the "ranges" property of "pci-host-ecam-generic" to consist of 27 // records like this. 28 // 29 #pragma pack (1) 30 typedef struct { 31 UINT32 Type; 32 UINT64 ChildBase; 33 UINT64 CpuBase; 34 UINT64 Size; 35 } DTB_PCI_HOST_RANGE_RECORD; 36 #pragma pack () 37 38 #define DTB_PCI_HOST_RANGE_RELOCATABLE BIT31 39 #define DTB_PCI_HOST_RANGE_PREFETCHABLE BIT30 40 #define DTB_PCI_HOST_RANGE_ALIASED BIT29 41 #define DTB_PCI_HOST_RANGE_MMIO32 BIT25 42 #define DTB_PCI_HOST_RANGE_MMIO64 (BIT25 | BIT24) 43 #define DTB_PCI_HOST_RANGE_IO BIT24 44 #define DTB_PCI_HOST_RANGE_TYPEMASK (BIT31 | BIT30 | BIT29 | BIT25 | BIT24) 45 46 STATIC 47 RETURN_STATUS 48 GetPciIoTranslation ( 49 IN FDT_CLIENT_PROTOCOL *FdtClient, 50 IN INT32 Node, 51 OUT UINT64 *IoTranslation 52 ) 53 { 54 UINT32 RecordIdx; 55 CONST VOID *Prop; 56 UINT32 Len; 57 EFI_STATUS Status; 58 UINT64 IoBase; 59 60 // 61 // Iterate over "ranges". 62 // 63 Status = FdtClient->GetNodeProperty (FdtClient, Node, "ranges", &Prop, &Len); 64 if (EFI_ERROR (Status) || Len == 0 || 65 Len % sizeof (DTB_PCI_HOST_RANGE_RECORD) != 0) { 66 DEBUG ((EFI_D_ERROR, "%a: 'ranges' not found or invalid\n", __FUNCTION__)); 67 return RETURN_PROTOCOL_ERROR; 68 } 69 70 for (RecordIdx = 0; RecordIdx < Len / sizeof (DTB_PCI_HOST_RANGE_RECORD); 71 ++RecordIdx) { 72 CONST DTB_PCI_HOST_RANGE_RECORD *Record; 73 UINT32 Type; 74 75 Record = (CONST DTB_PCI_HOST_RANGE_RECORD *)Prop + RecordIdx; 76 Type = SwapBytes32 (Record->Type) & DTB_PCI_HOST_RANGE_TYPEMASK; 77 if (Type == DTB_PCI_HOST_RANGE_IO) { 78 IoBase = SwapBytes64 (Record->ChildBase); 79 *IoTranslation = SwapBytes64 (Record->CpuBase) - IoBase; 80 81 return RETURN_SUCCESS; 82 } 83 } 84 return RETURN_NOT_FOUND; 85 } 86 87 RETURN_STATUS 88 EFIAPI 89 FdtPciPcdProducerLibConstructor ( 90 VOID 91 ) 92 { 93 UINT64 PciExpressBaseAddress; 94 FDT_CLIENT_PROTOCOL *FdtClient; 95 CONST UINT64 *Reg; 96 UINT32 RegSize; 97 EFI_STATUS Status; 98 INT32 Node; 99 RETURN_STATUS RetStatus; 100 UINT64 IoTranslation; 101 RETURN_STATUS PcdStatus; 102 103 PciExpressBaseAddress = PcdGet64 (PcdPciExpressBaseAddress); 104 if (PciExpressBaseAddress != MAX_UINT64) { 105 // 106 // Assume that the fact that PciExpressBaseAddress has been changed from 107 // its default value of MAX_UINT64 implies that this code has been 108 // executed already, in the context of another module. That means we can 109 // assume that PcdPciIoTranslation has been discovered from the DT node 110 // as well. 111 // 112 return EFI_SUCCESS; 113 } 114 115 Status = gBS->LocateProtocol (&gFdtClientProtocolGuid, NULL, 116 (VOID **)&FdtClient); 117 ASSERT_EFI_ERROR (Status); 118 119 PciExpressBaseAddress = 0; 120 Status = FdtClient->FindCompatibleNode (FdtClient, "pci-host-ecam-generic", 121 &Node); 122 123 if (!EFI_ERROR (Status)) { 124 Status = FdtClient->GetNodeProperty (FdtClient, Node, "reg", 125 (CONST VOID **)&Reg, &RegSize); 126 127 if (!EFI_ERROR (Status) && RegSize == 2 * sizeof (UINT64)) { 128 PciExpressBaseAddress = SwapBytes64 (*Reg); 129 130 PcdStatus = PcdSetBoolS (PcdPciDisableBusEnumeration, FALSE); 131 ASSERT_RETURN_ERROR (PcdStatus); 132 133 IoTranslation = 0; 134 RetStatus = GetPciIoTranslation (FdtClient, Node, &IoTranslation); 135 if (!RETURN_ERROR (RetStatus)) { 136 PcdStatus = PcdSet64S (PcdPciIoTranslation, IoTranslation); 137 ASSERT_RETURN_ERROR (PcdStatus); 138 } else { 139 // 140 // Support for I/O BARs is not mandatory, and so it does not make sense 141 // to abort in the general case. So leave it up to the actual driver to 142 // complain about this if it wants to, and just issue a warning here. 143 // 144 DEBUG ((EFI_D_WARN, 145 "%a: 'pci-host-ecam-generic' device encountered with no I/O range\n", 146 __FUNCTION__)); 147 } 148 } 149 } 150 151 PcdStatus = PcdSet64S (PcdPciExpressBaseAddress, PciExpressBaseAddress); 152 ASSERT_RETURN_ERROR (PcdStatus); 153 154 return RETURN_SUCCESS; 155 } 156