Home | History | Annotate | Download | only in FdtPciPcdProducerLib
      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