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
GetPciIoTranslation(IN FDT_CLIENT_PROTOCOL * FdtClient,IN INT32 Node,OUT UINT64 * IoTranslation)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     DEBUG ((EFI_D_ERROR, "%a: 'ranges' not found or invalid\n", __FUNCTION__));
61     return RETURN_PROTOCOL_ERROR;
62   }
63 
64   for (RecordIdx = 0; RecordIdx < Len / sizeof (DTB_PCI_HOST_RANGE_RECORD);
65        ++RecordIdx) {
66     CONST DTB_PCI_HOST_RANGE_RECORD *Record;
67     UINT32                          Type;
68 
69     Record = (CONST DTB_PCI_HOST_RANGE_RECORD *)Prop + RecordIdx;
70     Type = SwapBytes32 (Record->Type) & DTB_PCI_HOST_RANGE_TYPEMASK;
71     if (Type == DTB_PCI_HOST_RANGE_IO) {
72       IoBase = SwapBytes64 (Record->ChildBase);
73       *IoTranslation = SwapBytes64 (Record->CpuBase) - IoBase;
74 
75       return RETURN_SUCCESS;
76     }
77   }
78   return RETURN_NOT_FOUND;
79 }
80 
81 RETURN_STATUS
82 EFIAPI
FdtPciPcdProducerLibConstructor(VOID)83 FdtPciPcdProducerLibConstructor (
84   VOID
85   )
86 {
87   UINT64              PciExpressBaseAddress;
88   FDT_CLIENT_PROTOCOL *FdtClient;
89   CONST UINT64        *Reg;
90   UINT32              RegSize;
91   EFI_STATUS          Status;
92   INT32               Node;
93   RETURN_STATUS       RetStatus;
94   UINT64              IoTranslation;
95   RETURN_STATUS       PcdStatus;
96 
97   PciExpressBaseAddress = PcdGet64 (PcdPciExpressBaseAddress);
98   if (PciExpressBaseAddress != MAX_UINT64) {
99     //
100     // Assume that the fact that PciExpressBaseAddress has been changed from
101     // its default value of MAX_UINT64 implies that this code has been
102     // executed already, in the context of another module. That means we can
103     // assume that PcdPciIoTranslation has been discovered from the DT node
104     // as well.
105     //
106     return EFI_SUCCESS;
107   }
108 
109   Status = gBS->LocateProtocol (&gFdtClientProtocolGuid, NULL,
110                   (VOID **)&FdtClient);
111   ASSERT_EFI_ERROR (Status);
112 
113   PciExpressBaseAddress = 0;
114   Status = FdtClient->FindCompatibleNode (FdtClient, "pci-host-ecam-generic",
115                         &Node);
116 
117   if (!EFI_ERROR (Status)) {
118     Status = FdtClient->GetNodeProperty (FdtClient, Node, "reg",
119                           (CONST VOID **)&Reg, &RegSize);
120 
121     if (!EFI_ERROR (Status) && RegSize == 2 * sizeof (UINT64)) {
122       PciExpressBaseAddress = SwapBytes64 (*Reg);
123 
124       PcdStatus = PcdSetBoolS (PcdPciDisableBusEnumeration, FALSE);
125       ASSERT_RETURN_ERROR (PcdStatus);
126 
127       IoTranslation = 0;
128       RetStatus = GetPciIoTranslation (FdtClient, Node, &IoTranslation);
129       if (!RETURN_ERROR (RetStatus)) {
130           PcdStatus = PcdSet64S (PcdPciIoTranslation, IoTranslation);
131           ASSERT_RETURN_ERROR (PcdStatus);
132       } else {
133         //
134         // Support for I/O BARs is not mandatory, and so it does not make sense
135         // to abort in the general case. So leave it up to the actual driver to
136         // complain about this if it wants to, and just issue a warning here.
137         //
138         DEBUG ((EFI_D_WARN,
139           "%a: 'pci-host-ecam-generic' device encountered with no I/O range\n",
140           __FUNCTION__));
141       }
142     }
143   }
144 
145   PcdStatus = PcdSet64S (PcdPciExpressBaseAddress, PciExpressBaseAddress);
146   ASSERT_RETURN_ERROR (PcdStatus);
147 
148   return RETURN_SUCCESS;
149 }
150