//
// This file contains a 'Sample Driver' and is licensed as such
// under the terms of your license agreement with Intel or your
// vendor.  This file may be modified by the user, subject to
// the additional terms of the license agreement
//
/** @file
  Usb Debug Port library instance

  Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.<BR>
  This software and associated documentation (if any) is furnished
  under a license and may only be used or copied in accordance
  with the terms of the license. Except as permitted by such
  license, no part of this software or documentation may be
  reproduced, stored in a retrieval system, or transmitted in any
  form or by any means without the express written consent of
  Intel Corporation.

**/

#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/Usb3DebugPortParameterLib.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;
}

