/** @file
  Usb3 Debug Port initialization

  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/PcdLib.h>
#include <Library/TimerLib.h>
#include <Library/HobLib.h>
#include <Library/Usb3DebugPortParamLib.h>
#include "Usb3DebugPortLibInternal.h"

UINT16   mString0Desc[] = {
  //  String Descriptor Type + Length
  ( USB_DESC_TYPE_STRING << 8 ) + STRING0_DESC_LEN,
  0x0409
};

UINT16   mManufacturerStrDesc[] = {
  //  String Descriptor Type + Length
  ( USB_DESC_TYPE_STRING << 8 ) + MANU_DESC_LEN,
  'I', 'n', 't', 'e', 'l'
};

//USB 3.0 Debug Cable
UINT16   mProductStrDesc[] = {
  //  String Descriptor Type + Length
  ( USB_DESC_TYPE_STRING << 8 ) +  PRODUCT_DESC_LEN,
  'U', 'S', 'B', ' ', '3', '.', '0', ' ', 'D', 'e', 'b', 'u', 'g', ' ', 'C', 'a', 'b', 'l', 'e'
};

UINT16   mSerialNumberStrDesc[] = {
  //  String Descriptor Type + Length
  ( USB_DESC_TYPE_STRING << 8 ) +  SERIAL_DESC_LEN,
  '1'
};

XHC_DC_CONTEXT   DebugCapabilityContextTemplate = {
  {
    0,
    0,
    0,
    0,
    STRING0_DESC_LEN,
    MANU_DESC_LEN,
    PRODUCT_DESC_LEN,
    SERIAL_DESC_LEN,
    0,
    0,
    0,
    0
  },
  {
    0,  // EPState
    0,  // RsvdZ1
    0,  // Mult
    0,  // MaxPStreams
    0,  // LSA
    0,  // Interval
    0,  // RsvdZ2
    0,  // RsvdZ3
    3,  // CERR
    ED_BULK_OUT,  // EPType
    0,  // RsvdZ4
    0,  // HID
    0,  // MaxBurstSize
    1024, // MaxPacketSize
    0,    // PtrLo
    0,    // PtrHi
    0x1000, // AverageTRBLength
    0,     // MaxESITPayload
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0
  },
  {
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    3,    //CERR
    ED_BULK_IN,
    0,
    0,
    0,
    1024, //MaxPacketSize
    0,
    0,
    0x1000,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0
  }
};

/**
  Return command register value in XHCI controller.

**/
UINT16
GetXhciPciCommand (
  VOID
  )
{
  UINT8                                  Bus;
  UINT8                                  Device;
  UINT8                                  Function;
  UINT16                                 Command;
  USB3_DEBUG_PORT_CONTROLLER             UsbDebugPort;

  UsbDebugPort.Controller = GetUsb3DebugPortController();

  Bus = UsbDebugPort.PciAddress.Bus;
  Device = UsbDebugPort.PciAddress.Device;
  Function = UsbDebugPort.PciAddress.Function;
  Command = PciRead16(PCI_LIB_ADDRESS(Bus, Device, Function, PCI_COMMAND_OFFSET));
  return Command;
}

/**
  Discover the USB3 debug device.

  @param[in] Instance           Pointer to USB3 debug port object instance.

  @retval RETURN_SUCCESS        The USB3 debug device was found.
  @retval RETURN_DEVICE_ERROR   The USB3 debug device was not found.

**/
RETURN_STATUS
DiscoverUsb3DebugPort(
  IN USB3_DEBUG_PORT_INSTANCE   *Instance
  )
{
  UINT8                           Bus;
  UINT8                           Device;
  UINT8                           Function;
  UINT16                          Command;
  EFI_PHYSICAL_ADDRESS            UsbBase;
  USB3_DEBUG_PORT_CONTROLLER      UsbDebugPort;
  EFI_PHYSICAL_ADDRESS            CapabilityPointer;
  UINT32                          Capability;
  BOOLEAN                         Flag;
  UINT8                           CapLength;

  UsbDebugPort.Controller = GetUsb3DebugPortController();
  Bus = UsbDebugPort.PciAddress.Bus;
  Device = UsbDebugPort.PciAddress.Device;
  Function = UsbDebugPort.PciAddress.Function;
  if ((PciRead8(PCI_LIB_ADDRESS(Bus, Device, Function, PCI_CLASSCODE_OFFSET + 2)) != PCI_CLASS_SERIAL) ||
      (PciRead8(PCI_LIB_ADDRESS(Bus, Device, Function, PCI_CLASSCODE_OFFSET + 1)) != PCI_CLASS_SERIAL_USB) ||
      (PciRead8(PCI_LIB_ADDRESS(Bus, Device, Function, PCI_CLASSCODE_OFFSET)) != 0x30)) {
    //
    // The device is not XHCI controller
    //
    return RETURN_NOT_FOUND;
  }

  //
  // USBBASE is at 10-13h, i.e. the first BAR, clear the low bits which is not part of base address
  //
  UsbBase = GetXhciBaseAddress ();

  //
  // Set XHCI MMIO base address if necessary
  //
  if ((UsbBase == 0) || (UsbBase == XHCI_BASE_ADDRESS_64_BIT_MASK)) {
    UsbBase  = PcdGet32 (PcdXhciDefaultBaseAddress);
    PciWrite32 (PCI_LIB_ADDRESS(Bus, Device, Function, PCI_BASE_ADDRESSREG_OFFSET), (UINT32)UsbBase);
    PciWrite32 (PCI_LIB_ADDRESS(Bus, Device, Function, PCI_BASE_ADDRESSREG_OFFSET + 4), 0x0);
    UsbBase = PciRead32 (PCI_LIB_ADDRESS(Bus, Device, Function, PCI_BASE_ADDRESSREG_OFFSET)) & XHCI_BASE_ADDRESS_32_BIT_MASK;
    if (UsbBase == 0 || UsbBase == XHCI_BASE_ADDRESS_32_BIT_MASK) {
      return RETURN_DEVICE_ERROR;
    }
  }

  //
  //  Set MSE and BME bit - enable the address space
  //
  Command = PciRead16 (PCI_LIB_ADDRESS(Bus, Device, Function, PCI_COMMAND_OFFSET));
  if ((Command & EFI_PCI_COMMAND_MEMORY_SPACE) == 0) {
    PciWrite16(PCI_LIB_ADDRESS(Bus, Device, Function, PCI_COMMAND_OFFSET), Command | EFI_PCI_COMMAND_MEMORY_SPACE);
    PciRead16(PCI_LIB_ADDRESS(Bus, Device, Function, PCI_COMMAND_OFFSET));
  }

  CapLength = MmioRead8 ((UINTN) UsbBase);

  //
  // Get capability pointer from HCCPARAMS at offset 0x10
  //
  CapabilityPointer = UsbBase + (MmioRead32 ((UINTN)(UsbBase + XHC_HCCPARAMS_OFFSET)) >> 16) * 4;

  //
  // Search XHCI debug capability
  //
  Flag = FALSE;
  Capability = MmioRead32 ((UINTN)CapabilityPointer);
  while (TRUE) {
    if ((Capability & XHC_CAPABILITY_ID_MASK) == PCI_CAPABILITY_ID_DEBUG_PORT) {
      Flag = TRUE;
      break;
    }
    if ((((Capability & XHC_NEXT_CAPABILITY_MASK) >> 8) & XHC_CAPABILITY_ID_MASK) == 0) {
      //
      // Reach the end of capability list, quit
      //
      break;
    }
    CapabilityPointer += ((Capability & XHC_NEXT_CAPABILITY_MASK) >> 8) * 4;
    Capability = MmioRead32 ((UINTN)CapabilityPointer);
  }

  Instance->Signature             = USB3_DEBUG_PORT_INSTANCE_SIGNATURE;

  if (Flag) {
    Instance->DebugSupport          = TRUE;
    Instance->DebugCapabilityBase   = CapabilityPointer;
    Instance->DebugCapabilityOffset = CapabilityPointer - UsbBase;

    Instance->XhciOpRegister        = UsbBase + CapLength;
    Instance->XhcMmioBase           = UsbBase;

    Instance->PciBusNumber          = Bus;
    Instance->PciDeviceNumber       = Device;
    Instance->PciFunctionNumber     = Function;
  }

  //
  // Restore Command Register
  //
  PciWrite16(PCI_LIB_ADDRESS (Bus, Device, Function, PCI_COMMAND_OFFSET), Command);

  return RETURN_SUCCESS;
}

/**
  Create XHCI event ring.

  @param  Xhc                 The XHCI Instance.
  @param  EventRing           The created event ring.

**/
EFI_STATUS
CreateEventRing (
  IN  USB3_DEBUG_PORT_INSTANCE   *Xhc,
  OUT EVENT_RING                 *EventRing
  )
{
  VOID                        *Buf;
  EVENT_RING_SEG_TABLE_ENTRY  *ERSTBase;

  ASSERT (EventRing != NULL);

  //
  // Allocate Event Ring
  //
  Buf = AllocateAlignBuffer (sizeof (TRB_TEMPLATE) * EVENT_RING_TRB_NUMBER);
  ASSERT (Buf != NULL);
  ASSERT (((UINTN) Buf & 0x3F) == 0);
  ZeroMem (Buf, sizeof (TRB_TEMPLATE) * EVENT_RING_TRB_NUMBER);

  EventRing->EventRingSeg0    = (EFI_PHYSICAL_ADDRESS)(UINTN) Buf;
  EventRing->TrbNumber        = EVENT_RING_TRB_NUMBER;
  EventRing->EventRingDequeue = (EFI_PHYSICAL_ADDRESS)(UINTN) EventRing->EventRingSeg0;
  EventRing->EventRingEnqueue = (EFI_PHYSICAL_ADDRESS)(UINTN) EventRing->EventRingSeg0;

  //
  // Software maintains an Event Ring Consumer Cycle State (CCS) bit, initializing it to '1'
  // and toggling it every time the Event Ring Dequeue Pointer wraps back to the beginning of the Event Ring.
  //
  EventRing->EventRingCCS = 1;

  //
  // Allocate Event Ring Segment Table Entry 0 in Event Ring Segment Table
  //
  Buf = AllocateAlignBuffer (sizeof (EVENT_RING_SEG_TABLE_ENTRY) * ERST_NUMBER);
  ASSERT (Buf != NULL);
  ASSERT (((UINTN) Buf & 0x3F) == 0);
  ZeroMem (Buf, sizeof (EVENT_RING_SEG_TABLE_ENTRY) * ERST_NUMBER);

  ERSTBase              = (EVENT_RING_SEG_TABLE_ENTRY *) Buf;
  EventRing->ERSTBase   = (EFI_PHYSICAL_ADDRESS)(UINTN) ERSTBase;

  //
  // Fill Event Segment address
  //
  ERSTBase->PtrLo       = XHC_LOW_32BIT (EventRing->EventRingSeg0);
  ERSTBase->PtrHi       = XHC_HIGH_32BIT (EventRing->EventRingSeg0);
  ERSTBase->RingTrbSize = EVENT_RING_TRB_NUMBER;

  //
  // Program the Interrupter Event Ring Dequeue Pointer (DCERDP) register (7.6.4.1)
  //
  XhcWriteDebugReg (
    Xhc,
    XHC_DC_DCERDP,
    XHC_LOW_32BIT((UINT64)(UINTN)EventRing->EventRingDequeue)
    );

  XhcWriteDebugReg (
    Xhc,
    XHC_DC_DCERDP + 4,
    XHC_HIGH_32BIT((UINT64)(UINTN)EventRing->EventRingDequeue)
    );

  //
  // Program the Debug Capability Event Ring Segment Table Base Address (DCERSTBA) register(7.6.4.1)
  //
  XhcWriteDebugReg (
    Xhc,
    XHC_DC_DCERSTBA,
    XHC_LOW_32BIT((UINT64)(UINTN)ERSTBase)
    );

  XhcWriteDebugReg (
    Xhc,
    XHC_DC_DCERSTBA + 4,
    XHC_HIGH_32BIT((UINT64)(UINTN)ERSTBase)
    );

  //
  // Program the Debug Capability Event Ring Segment Table Size (DCERSTSZ) register(7.6.4.1)
  //
  XhcWriteDebugReg (
    Xhc,
    XHC_DC_DCERSTSZ,
    ERST_NUMBER
    );
  return EFI_SUCCESS;
}

/**
  Create XHCI transfer ring.

  @param  Xhc               The XHCI Instance.
  @param  TrbNum            The number of TRB in the ring.
  @param  TransferRing      The created transfer ring.

**/
VOID
CreateTransferRing (
  IN  USB3_DEBUG_PORT_INSTANCE    *Xhc,
  IN  UINT32                      TrbNum,
  OUT TRANSFER_RING               *TransferRing
  )
{
  VOID                  *Buf;
  LINK_TRB              *EndTrb;

  Buf = AllocateAlignBuffer (sizeof (TRB_TEMPLATE) * TrbNum);
  ASSERT (Buf != NULL);
  ASSERT (((UINTN) Buf & 0xF) == 0);
  ZeroMem (Buf, sizeof (TRB_TEMPLATE) * TrbNum);

  TransferRing->RingSeg0     = (EFI_PHYSICAL_ADDRESS)(UINTN) Buf;
  TransferRing->TrbNumber    = TrbNum;
  TransferRing->RingEnqueue  = TransferRing->RingSeg0;
  TransferRing->RingDequeue  = TransferRing->RingSeg0;
  TransferRing->RingPCS      = 1;
  //
  // 4.9.2 Transfer Ring Management
  // To form a ring (or circular queue) a Link TRB may be inserted at the end of a ring to
  // point to the first TRB in the ring.
  //
  EndTrb        = (LINK_TRB *) ((UINTN)Buf + sizeof (TRB_TEMPLATE) * (TrbNum - 1));
  EndTrb->Type  = TRB_TYPE_LINK;
  EndTrb->PtrLo = XHC_LOW_32BIT (Buf);
  EndTrb->PtrHi = XHC_HIGH_32BIT (Buf);
  //
  // Toggle Cycle (TC). When set to '1', the xHC shall toggle its interpretation of the Cycle bit.
  //
  EndTrb->TC    = 1;
  //
  // Set Cycle bit as other TRB PCS init value
  //
  EndTrb->CycleBit = 0;
}

/**
  Create debug capability context for XHC debug device.

  @param  Xhc          The XHCI Instance.

  @retval EFI_SUCCESS  The bit successfully changed by host controller.
  @retval EFI_TIMEOUT  The time out occurred.

**/
EFI_STATUS
CreateDebugCapabilityContext (
  IN  USB3_DEBUG_PORT_INSTANCE   *Xhc
  )
{
  VOID                        *Buf;
  XHC_DC_CONTEXT              *DebugCapabilityContext;
  UINT8                       *String0Desc;
  UINT8                       *ManufacturerStrDesc;
  UINT8                       *ProductStrDesc;
  UINT8                       *SerialNumberStrDesc;

  //
  // Allocate debug device context
  //
  Buf = AllocateAlignBuffer (sizeof (XHC_DC_CONTEXT));
  ASSERT (Buf != NULL);
  ASSERT (((UINTN) Buf & 0xF) == 0);
  ZeroMem (Buf, sizeof (XHC_DC_CONTEXT));

  DebugCapabilityContext = (XHC_DC_CONTEXT *)(UINTN) Buf;
  Xhc->DebugCapabilityContext = (EFI_PHYSICAL_ADDRESS)(UINTN) DebugCapabilityContext;

  CopyMem (DebugCapabilityContext, &DebugCapabilityContextTemplate, sizeof (XHC_DC_CONTEXT));

  //
  // Update string descriptor address
  //
  String0Desc = (UINT8 *) AllocateAlignBuffer (STRING0_DESC_LEN + MANU_DESC_LEN + PRODUCT_DESC_LEN + SERIAL_DESC_LEN);
  ASSERT (String0Desc != NULL);
  ZeroMem (String0Desc, STRING0_DESC_LEN + MANU_DESC_LEN + PRODUCT_DESC_LEN + SERIAL_DESC_LEN);
  CopyMem (String0Desc, mString0Desc, STRING0_DESC_LEN);
  DebugCapabilityContext->DbcInfoContext.String0DescAddress = (UINT64)(UINTN)String0Desc;

  ManufacturerStrDesc = String0Desc + STRING0_DESC_LEN;
  CopyMem (ManufacturerStrDesc, mManufacturerStrDesc, MANU_DESC_LEN);
  DebugCapabilityContext->DbcInfoContext.ManufacturerStrDescAddress = (UINT64)(UINTN)ManufacturerStrDesc;

  ProductStrDesc = ManufacturerStrDesc + MANU_DESC_LEN;
  CopyMem (ProductStrDesc, mProductStrDesc, PRODUCT_DESC_LEN);
  DebugCapabilityContext->DbcInfoContext.ProductStrDescAddress = (UINT64)(UINTN)ProductStrDesc;

  SerialNumberStrDesc = ProductStrDesc + PRODUCT_DESC_LEN;
  CopyMem (SerialNumberStrDesc, mSerialNumberStrDesc, SERIAL_DESC_LEN);
  DebugCapabilityContext->DbcInfoContext.SerialNumberStrDescAddress = (UINT64)(UINTN)SerialNumberStrDesc;

  //
  // Allocate and initialize the Transfer Ring for the Input Endpoint Context.
  //
  ZeroMem (&Xhc->TransferRingIn, sizeof (TRANSFER_RING));
  CreateTransferRing (Xhc, TR_RING_TRB_NUMBER, &Xhc->TransferRingIn);

  //
  // Can not set BIT0, otherwise there is no transfer ring detected.
  //
  DebugCapabilityContext->EpInContext.PtrLo = XHC_LOW_32BIT (Xhc->TransferRingIn.RingSeg0) | BIT0;
  DebugCapabilityContext->EpInContext.PtrHi = XHC_HIGH_32BIT (Xhc->TransferRingIn.RingSeg0);

  //
  // Allocate and initialize the Transfer Ring for the Output Endpoint Context.
  //
  ZeroMem (&Xhc->TransferRingOut, sizeof (TRANSFER_RING));
  CreateTransferRing (Xhc, TR_RING_TRB_NUMBER, &Xhc->TransferRingOut);

  //
  // Can not set BIT0, otherwise there is no transfer ring detected.
  //
  DebugCapabilityContext->EpOutContext.PtrLo = XHC_LOW_32BIT (Xhc->TransferRingOut.RingSeg0) | BIT0;
  DebugCapabilityContext->EpOutContext.PtrHi = XHC_HIGH_32BIT (Xhc->TransferRingOut.RingSeg0);

  //
  // Program the Debug Capability Context Pointer (DCCP) register(7.6.8.7)
  //
  XhcWriteDebugReg (
    Xhc,
    XHC_DC_DCCP,
    XHC_LOW_32BIT((UINT64)(UINTN)DebugCapabilityContext)
    );
  XhcWriteDebugReg (
    Xhc,
    XHC_DC_DCCP + 4,
    XHC_HIGH_32BIT((UINT64)(UINTN)DebugCapabilityContext)
    );
  return EFI_SUCCESS;
}

/**
  Initialize the USB3 debug Device hardware.

  @param[in] Instance       Pointer to USB3 debug port object instance.

  @retval RETURN_SUCCESS    The USB3 debug device was initialized successfully.
  @retval !RETURN_SUCCESS   Error.

**/
RETURN_STATUS
InitializeUsb3DebugPort (
  IN USB3_DEBUG_PORT_INSTANCE   *Instance
  )
{
  RETURN_STATUS                   Status;
  UINT64                          XhciOpRegister;
  volatile UINT32                 Dcctrl;
  UINT8                           Bus;
  UINT8                           Device;
  UINT8                           Function;
  UINT16                          Command;
  EFI_BOOT_MODE                   BootMode;
  UINT64                          TimeOut;
  CHAR8                           *TestString;
  UINTN                           Length;
  UINT32                          TransferResult;

  Bus      = Instance->PciBusNumber;
  Device   = Instance->PciDeviceNumber;
  Function = Instance->PciFunctionNumber;

  Command = GetXhciPciCommand ();

  //
  // Save and set Command Register
  //
  if (((Command & EFI_PCI_COMMAND_MEMORY_SPACE) == 0) || ((Command & EFI_PCI_COMMAND_BUS_MASTER) == 0)) {
    PciWrite16(PCI_LIB_ADDRESS(Bus, Device, Function, PCI_COMMAND_OFFSET), Command | EFI_PCI_COMMAND_MEMORY_SPACE | EFI_PCI_COMMAND_BUS_MASTER);
    PciRead16(PCI_LIB_ADDRESS(Bus, Device, Function, PCI_COMMAND_OFFSET));
  }

  //
  // Clear DCE bit and LSE bit in DCCTRL
  //
  if ((XhcReadDebugReg (Instance, XHC_DC_DCCTRL) & (BIT1|BIT31)) == (BIT1|BIT31)) {
    XhcClearDebugRegBit (Instance, XHC_DC_DCCTRL, BIT1|BIT31);
  }

  XhciOpRegister = Instance->XhciOpRegister;

  //
  // Get current Boot Mode
  //
  BootMode = GetBootModeHob ();

  if (BootMode != BOOT_ON_S3_RESUME) {
    if (!XhcIsBitSet((UINTN)(XhciOpRegister + XHC_USBSTS_OFFSET), XHC_USBSTS_HALT)) {
      XhcClrR32Bit((UINTN) XhciOpRegister + XHC_USBCMD_OFFSET, XHC_USBCMD_RUN);
      while (!XhcIsBitSet((UINTN)(XhciOpRegister + XHC_USBSTS_OFFSET), XHC_USBSTS_HALT)) {
        MicroSecondDelay (10);
      }
    }

    //
    // Reset host controller
    //
    XhcSetR32Bit((UINTN)XhciOpRegister + XHC_USBCMD_OFFSET, XHC_USBCMD_RESET);

    //
    // Ensure that the host controller is reset (RESET bit must be cleared after reset)
    //
    while (XhcIsBitSet((UINTN)XhciOpRegister + XHC_USBCMD_OFFSET, XHC_USBCMD_RESET)) {
      MicroSecondDelay (10);
    }
  }

  //
  // Initialize event ring
  //
  ZeroMem (&Instance->EventRing, sizeof (EVENT_RING));
  Status = CreateEventRing (Instance, &Instance->EventRing);
  ASSERT_EFI_ERROR (Status);

  //
  // Init IN and OUT endpoint context
  //
  Status = CreateDebugCapabilityContext (Instance);
  ASSERT_EFI_ERROR (Status);

  //
  // Init data buffer used to transfer
  //
  Instance->Urb.Data = (EFI_PHYSICAL_ADDRESS) (UINTN) AllocateAlignBuffer (XHC_DEBUG_PORT_DATA_LENGTH);

  //
  // Init DCDDI1 and DCDDI2
  //
  XhcWriteDebugReg (
   Instance,
   XHC_DC_DCDDI1,
   (UINT32)((XHCI_DEBUG_DEVICE_VENDOR_ID << 16) | XHCI_DEBUG_DEVICE_PROTOCOL)
   );

  XhcWriteDebugReg (
   Instance,
   XHC_DC_DCDDI2,
   (UINT32)((XHCI_DEBUG_DEVICE_REVISION << 16) | XHCI_DEBUG_DEVICE_PRODUCT_ID)
   );

  //
  // Set DCE bit and LSE bit to "1" in DCCTRL
  //
  XhcSetDebugRegBit (Instance, XHC_DC_DCCTRL, BIT1|BIT31);

  TimeOut = DivU64x32 (PcdGet64 (PcdXhciHostWaitTimeout), XHC_POLL_DELAY) + 1;
  while (TimeOut != 0) {
    //
    // Check if debug device is in configured state
    //
    Dcctrl = XhcReadDebugReg (Instance, XHC_DC_DCCTRL);
    if ((Dcctrl & BIT0) != 0) {
      //
      // Set the flag to indicate debug device is ready
      //
      Instance->Ready = TRUE;
      break;
    }
    MicroSecondDelay (XHC_POLL_DELAY);
    TimeOut--;
  }

  if (!Instance->Ready) {
    XhcClearDebugRegBit (Instance, XHC_DC_DCCTRL, BIT1|BIT31);
  } else {
    TestString = "Usb 3.0 Debug Message Start\n";
    Length = AsciiStrLen (TestString);
    XhcDataTransfer (
      Instance,
      EfiUsbDataOut,
      TestString,
      &Length,
      0,
      &TransferResult
      );
  }

  //
  // Restore Command Register
  //
  PciWrite16 (PCI_LIB_ADDRESS (Bus, Device, Function, PCI_COMMAND_OFFSET), Command);

  return EFI_SUCCESS;
}

/**
  Update XHC hardware address when MMIO base is changed.

  @param  Instance          The XHCI Instance.
  @param  XhcMmioBase       XHCI MMIO base address.

**/
VOID
FixUsb3InstanceResource (
  IN OUT USB3_DEBUG_PORT_INSTANCE           *Instance,
  IN EFI_PHYSICAL_ADDRESS                   XhcMmioBase
  )
{
  if ((Instance == NULL) || (Instance->XhcMmioBase == XhcMmioBase)) {
    return;
  }

  //
  // Need fix Instance data according to PCI resource
  //
  Instance->XhcMmioBase         = XhcMmioBase;
  Instance->DebugCapabilityBase = XhcMmioBase + Instance->DebugCapabilityOffset;
  Instance->XhciOpRegister      = XhcMmioBase + MmioRead8 ((UINTN)XhcMmioBase);
}

/**
  Save USB3 instance address.

  @param[in] Instance   The XHCI Instance.

**/
VOID
SaveUsb3InstanceAddress (
  IN USB3_DEBUG_PORT_INSTANCE   *Instance
  )
{
  UINT16                        Command;
  USB3_DEBUG_PORT_CONTROLLER    UsbDebugPort;
  UINT8                         Bus;
  UINT8                         Device;
  UINT8                         Function;

  Command = GetXhciPciCommand ();
  UsbDebugPort.Controller = GetUsb3DebugPortController();
  Bus      = UsbDebugPort.PciAddress.Bus;
  Device   = UsbDebugPort.PciAddress.Device;
  Function = UsbDebugPort.PciAddress.Function;

  //
  // Set Command Register
  //
  if ((Command & EFI_PCI_COMMAND_MEMORY_SPACE) == 0) {
    PciWrite16(PCI_LIB_ADDRESS(Bus, Device, Function, PCI_COMMAND_OFFSET), Command | EFI_PCI_COMMAND_MEMORY_SPACE);
    PciRead16(PCI_LIB_ADDRESS(Bus, Device, Function, PCI_COMMAND_OFFSET));
  }

  //
  // After debug device is finished to enumerate, use DCDDI2 register to store instance address
  //
  XhcWriteDebugReg (
   Instance,
   XHC_DC_DCDDI2,
   (UINT32)(UINTN)Instance
   );

  //
  // Restore Command Register
  //
  PciWrite16 (PCI_LIB_ADDRESS (Bus, Device, Function, PCI_COMMAND_OFFSET), Command);
}

