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 | SPDX-License-Identifier: BSD-2-Clause-Patent
|
---|
7 |
|
---|
8 | **/
|
---|
9 |
|
---|
10 | #include <Uefi.h>
|
---|
11 |
|
---|
12 | #include <Library/BaseLib.h>
|
---|
13 | #include <Library/DebugLib.h>
|
---|
14 | #include <Library/PcdLib.h>
|
---|
15 | #include <Library/UefiBootServicesTableLib.h>
|
---|
16 |
|
---|
17 | #include <Protocol/FdtClient.h>
|
---|
18 |
|
---|
19 | //
|
---|
20 | // We expect the "ranges" property of "pci-host-ecam-generic" to consist of
|
---|
21 | // records like this.
|
---|
22 | //
|
---|
23 | #pragma pack (1)
|
---|
24 | typedef struct {
|
---|
25 | UINT32 Type;
|
---|
26 | UINT64 ChildBase;
|
---|
27 | UINT64 CpuBase;
|
---|
28 | UINT64 Size;
|
---|
29 | } DTB_PCI_HOST_RANGE_RECORD;
|
---|
30 | #pragma pack ()
|
---|
31 |
|
---|
32 | #define DTB_PCI_HOST_RANGE_RELOCATABLE BIT31
|
---|
33 | #define DTB_PCI_HOST_RANGE_PREFETCHABLE BIT30
|
---|
34 | #define DTB_PCI_HOST_RANGE_ALIASED BIT29
|
---|
35 | #define DTB_PCI_HOST_RANGE_MMIO32 BIT25
|
---|
36 | #define DTB_PCI_HOST_RANGE_MMIO64 (BIT25 | BIT24)
|
---|
37 | #define DTB_PCI_HOST_RANGE_IO BIT24
|
---|
38 | #define DTB_PCI_HOST_RANGE_TYPEMASK (BIT31 | BIT30 | BIT29 | BIT25 | BIT24)
|
---|
39 |
|
---|
40 | STATIC
|
---|
41 | RETURN_STATUS
|
---|
42 | GetPciIoTranslation (
|
---|
43 | IN FDT_CLIENT_PROTOCOL *FdtClient,
|
---|
44 | IN INT32 Node,
|
---|
45 | OUT UINT64 *IoTranslation
|
---|
46 | )
|
---|
47 | {
|
---|
48 | UINT32 RecordIdx;
|
---|
49 | CONST VOID *Prop;
|
---|
50 | UINT32 Len;
|
---|
51 | EFI_STATUS Status;
|
---|
52 | UINT64 IoBase;
|
---|
53 |
|
---|
54 | //
|
---|
55 | // Iterate over "ranges".
|
---|
56 | //
|
---|
57 | Status = FdtClient->GetNodeProperty (FdtClient, Node, "ranges", &Prop, &Len);
|
---|
58 | if (EFI_ERROR (Status) || (Len == 0) ||
|
---|
59 | (Len % sizeof (DTB_PCI_HOST_RANGE_RECORD) != 0))
|
---|
60 | {
|
---|
61 | DEBUG ((DEBUG_ERROR, "%a: 'ranges' not found or invalid\n", __func__));
|
---|
62 | return RETURN_PROTOCOL_ERROR;
|
---|
63 | }
|
---|
64 |
|
---|
65 | for (RecordIdx = 0; RecordIdx < Len / sizeof (DTB_PCI_HOST_RANGE_RECORD);
|
---|
66 | ++RecordIdx)
|
---|
67 | {
|
---|
68 | CONST DTB_PCI_HOST_RANGE_RECORD *Record;
|
---|
69 | UINT32 Type;
|
---|
70 |
|
---|
71 | Record = (CONST DTB_PCI_HOST_RANGE_RECORD *)Prop + RecordIdx;
|
---|
72 | Type = SwapBytes32 (Record->Type) & DTB_PCI_HOST_RANGE_TYPEMASK;
|
---|
73 | if (Type == DTB_PCI_HOST_RANGE_IO) {
|
---|
74 | IoBase = SwapBytes64 (Record->ChildBase);
|
---|
75 | *IoTranslation = SwapBytes64 (Record->CpuBase) - IoBase;
|
---|
76 |
|
---|
77 | return RETURN_SUCCESS;
|
---|
78 | }
|
---|
79 | }
|
---|
80 |
|
---|
81 | return RETURN_NOT_FOUND;
|
---|
82 | }
|
---|
83 |
|
---|
84 | RETURN_STATUS
|
---|
85 | EFIAPI
|
---|
86 | FdtPciPcdProducerLibConstructor (
|
---|
87 | VOID
|
---|
88 | )
|
---|
89 | {
|
---|
90 | UINT64 PciExpressBaseAddress;
|
---|
91 | FDT_CLIENT_PROTOCOL *FdtClient;
|
---|
92 | CONST UINT64 *Reg;
|
---|
93 | UINT32 RegSize;
|
---|
94 | EFI_STATUS Status;
|
---|
95 | INT32 Node;
|
---|
96 | RETURN_STATUS RetStatus;
|
---|
97 | UINT64 IoTranslation;
|
---|
98 | RETURN_STATUS PcdStatus;
|
---|
99 |
|
---|
100 | PciExpressBaseAddress = PcdGet64 (PcdPciExpressBaseAddress);
|
---|
101 | if (PciExpressBaseAddress != MAX_UINT64) {
|
---|
102 | //
|
---|
103 | // Assume that the fact that PciExpressBaseAddress has been changed from
|
---|
104 | // its default value of MAX_UINT64 implies that this code has been
|
---|
105 | // executed already, in the context of another module. That means we can
|
---|
106 | // assume that PcdPciIoTranslation has been discovered from the DT node
|
---|
107 | // as well.
|
---|
108 | //
|
---|
109 | return EFI_SUCCESS;
|
---|
110 | }
|
---|
111 |
|
---|
112 | Status = gBS->LocateProtocol (
|
---|
113 | &gFdtClientProtocolGuid,
|
---|
114 | NULL,
|
---|
115 | (VOID **)&FdtClient
|
---|
116 | );
|
---|
117 | ASSERT_EFI_ERROR (Status);
|
---|
118 |
|
---|
119 | PciExpressBaseAddress = 0;
|
---|
120 | Status = FdtClient->FindCompatibleNode (
|
---|
121 | FdtClient,
|
---|
122 | "pci-host-ecam-generic",
|
---|
123 | &Node
|
---|
124 | );
|
---|
125 |
|
---|
126 | if (!EFI_ERROR (Status)) {
|
---|
127 | Status = FdtClient->GetNodeProperty (
|
---|
128 | FdtClient,
|
---|
129 | Node,
|
---|
130 | "reg",
|
---|
131 | (CONST VOID **)&Reg,
|
---|
132 | &RegSize
|
---|
133 | );
|
---|
134 |
|
---|
135 | if (!EFI_ERROR (Status) && (RegSize == 2 * sizeof (UINT64))) {
|
---|
136 | PciExpressBaseAddress = SwapBytes64 (*Reg);
|
---|
137 |
|
---|
138 | PcdStatus = PcdSetBoolS (PcdPciDisableBusEnumeration, FALSE);
|
---|
139 | ASSERT_RETURN_ERROR (PcdStatus);
|
---|
140 |
|
---|
141 | IoTranslation = 0;
|
---|
142 | RetStatus = GetPciIoTranslation (FdtClient, Node, &IoTranslation);
|
---|
143 | if (!RETURN_ERROR (RetStatus)) {
|
---|
144 | PcdStatus = PcdSet64S (PcdPciIoTranslation, IoTranslation);
|
---|
145 | ASSERT_RETURN_ERROR (PcdStatus);
|
---|
146 | } else {
|
---|
147 | //
|
---|
148 | // Support for I/O BARs is not mandatory, and so it does not make sense
|
---|
149 | // to abort in the general case. So leave it up to the actual driver to
|
---|
150 | // complain about this if it wants to, and just issue a warning here.
|
---|
151 | //
|
---|
152 | DEBUG ((
|
---|
153 | DEBUG_WARN,
|
---|
154 | "%a: 'pci-host-ecam-generic' device encountered with no I/O range\n",
|
---|
155 | __func__
|
---|
156 | ));
|
---|
157 | }
|
---|
158 | }
|
---|
159 | }
|
---|
160 |
|
---|
161 | PcdStatus = PcdSet64S (PcdPciExpressBaseAddress, PciExpressBaseAddress);
|
---|
162 | ASSERT_RETURN_ERROR (PcdStatus);
|
---|
163 |
|
---|
164 | return RETURN_SUCCESS;
|
---|
165 | }
|
---|