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
GetPciIoTranslation(IN FDT_CLIENT_PROTOCOL * FdtClient,IN INT32 Node,OUT UINT64 * IoTranslation)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
FdtPciPcdProducerLibConstructor(VOID)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