/** @file
  Usb Debug Port library instance

  Copyright (c) 2013 - 2019, Intel Corporation. All rights reserved.<BR>
  SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include <Base.h>

#include <PiPei.h>
#include <Library/SerialPortLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/PciLib.h>
#include <Library/HobLib.h>
#include <Library/PcdLib.h>
#include <Library/TimerLib.h>
#include <Library/Usb3DebugPortParamLib.h>
#include <Library/PeiServicesTablePointerLib.h>
#include <Ppi/MemoryDiscovered.h>
#include "Usb3DebugPortLibInternal.h"

GUID                      gUsb3DbgGuid =  USB3_DBG_GUID;

/**
  Return XHCI MMIO base address.

**/
EFI_PHYSICAL_ADDRESS
GetXhciBaseAddress (
  VOID
  )
{
  UINT8                       Bus;
  UINT8                       Device;
  UINT8                       Function;
  USB3_DEBUG_PORT_CONTROLLER   UsbDebugPort;
  EFI_PHYSICAL_ADDRESS        Address;
  UINT32                      Low;
  UINT32                      High;

  UsbDebugPort.Controller = GetUsb3DebugPortController();
  Bus = UsbDebugPort.PciAddress.Bus;
  Device = UsbDebugPort.PciAddress.Device;
  Function = UsbDebugPort.PciAddress.Function;
  Low = PciRead32 (PCI_LIB_ADDRESS(Bus, Device, Function, PCI_BASE_ADDRESSREG_OFFSET));
  High = PciRead32 (PCI_LIB_ADDRESS(Bus, Device, Function, PCI_BASE_ADDRESSREG_OFFSET + 4));
  Address = (EFI_PHYSICAL_ADDRESS) (LShiftU64 ((UINT64) High, 32) | Low);

  //
  // Mask other parts which are not part of base address
  //
  Address &= XHCI_BASE_ADDRESS_64_BIT_MASK;
  return Address;
}

/**
  Return XHCI debug instance address.

**/
USB3_DEBUG_PORT_INSTANCE *
GetUsb3DebugPortInstance (
  VOID
  )
{
  USB3_DEBUG_PORT_INSTANCE               *Instance;
  EFI_PEI_HOB_POINTERS                   Hob;
  EFI_PHYSICAL_ADDRESS                   XhcMmioBase;

  Hob.Raw = GetFirstGuidHob (&gUsb3DbgGuid);
  if (Hob.Raw == NULL) {
    return NULL;
  }
  Instance = GET_GUID_HOB_DATA (Hob.Guid);

  //
  // Update XHCI MMIO base address
  //
  XhcMmioBase = GetXhciBaseAddress ();

  FixUsb3InstanceResource (Instance, XhcMmioBase);

  return Instance;
}

/**
  Initialize USB3 debug port.

  This method invokes various internal functions to facilitate
  detection and initialization of USB3 debug port.

  @retval RETURN_SUCCESS        The serial device was initialized.
**/
RETURN_STATUS
EFIAPI
USB3Initialize (
  VOID
  )
{
  return EFI_SUCCESS;
}

/**
  Initialize USB3 debug port.

  This method invokes various internal functions to facilitate
  detection and initialization of USB3 debug port.

  @retval RETURN_SUCCESS        The serial device was initialized.
**/
RETURN_STATUS
EFIAPI
USB3InitializeReal (
  VOID
  )
{
  USB3_DEBUG_PORT_INSTANCE  UsbDbg;
  VOID                      *DataPtr;
  EFI_PEI_HOB_POINTERS      Hob;

  //
  // USB Initilization has to be done only once. So this function should just return when
  // it is called more than once.
  //
  Hob.Raw = GetFirstGuidHob (&gUsb3DbgGuid);
  if (Hob.Raw != NULL) {
    return RETURN_SUCCESS;
  }

  //
  // Initialize USB debug for PEI at the first time
  //
  SetMem (&UsbDbg, sizeof(UsbDbg), 0);
  UsbDbg.FromHob = TRUE;
  DiscoverUsb3DebugPort (&UsbDbg);
  if (UsbDbg.DebugSupport) {
    InitializeUsb3DebugPort (&UsbDbg);
  }

  //
  // Save Instance into HOB
  //
  DataPtr = BuildGuidDataHob (
              &gUsb3DbgGuid,
              (VOID*) &UsbDbg,
              sizeof (UsbDbg)
              );

  if (UsbDbg.DebugSupport) {
    SaveUsb3InstanceAddress ((USB3_DEBUG_PORT_INSTANCE *) DataPtr);
  }

  return RETURN_SUCCESS;
}

/**
  Allocate aligned memory for XHC's usage.

  @param BufferSize     The size, in bytes, of the Buffer.

  @return A pointer to the allocated buffer or NULL if allocation fails.

**/
VOID*
AllocateAlignBuffer (
  IN UINTN                    BufferSize
  )
{
  VOID                     *Buf;
  EFI_PHYSICAL_ADDRESS     Address;
  CONST EFI_PEI_SERVICES   **PeiServices;
  EFI_STATUS               Status;
  VOID                     *MemoryDiscoveredPpi;

  Buf = NULL;
  PeiServices = GetPeiServicesTablePointer ();

  //
  // Make sure the allocated memory is physical memory.
  //
  Status = (*PeiServices)->LocatePpi (
                             PeiServices,
                             &gEfiPeiMemoryDiscoveredPpiGuid,
                             0,
                             NULL,
                             (VOID **) &MemoryDiscoveredPpi
                             );
  if (!EFI_ERROR (Status)) {
    Status = (*PeiServices)->AllocatePages (
                               PeiServices,
                               EfiACPIMemoryNVS,
                               EFI_SIZE_TO_PAGES (BufferSize),
                               &Address
                               );
    if (!EFI_ERROR (Status)) {
      Buf = (VOID *)(UINTN) Address;
    }
  }
  return Buf;
}

/**
  Check whether AllocatePages in permanent memory is ready.

  @retval TRUE  AllocatePages in permanent memory is ready.
  @retval FALSE AllocatePages in permanent memory is not ready.

**/
BOOLEAN
IsAllocatePagesReady (
  VOID
  )
{
  CONST EFI_PEI_SERVICES   **PeiServices;
  EFI_STATUS               Status;
  VOID                     *MemoryDiscoveredPpi;

  PeiServices = GetPeiServicesTablePointer ();

  //
  // Make sure the allocated memory is physical memory.
  //
  Status = (*PeiServices)->LocatePpi (
                             PeiServices,
                             &gEfiPeiMemoryDiscoveredPpiGuid,
                             0,
                             NULL,
                             (VOID **) &MemoryDiscoveredPpi
                             );
  if (!EFI_ERROR (Status)) {
    return TRUE;
  }

  return FALSE;
}

