• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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