/*++

Copyright (c) 2006, Intel Corporation
All rights reserved. This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution.  The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php

THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

Module Name:

    Ehchlp.c

Abstract:


Revision History
--*/

#include "Ehci.h"


VOID
HostReset (
  IN USB2_HC_DEV    *HcDev
  )
{
  UINT32  Value;
  UINT32  TimeOut;

  ReadEhcOperationalReg (
    HcDev,
    USBCMD,
    &Value
    );

  Value = Value & (~USBCMD_RS);
  WriteEhcOperationalReg (
    HcDev,
    USBCMD,
    Value
    );

  TimeOut = 40;
  while (TimeOut --) {
    gBS->Stall (500);
    ReadEhcOperationalReg (
      HcDev,
      USBSTS,
      &Value
      );
    if ((Value & USBSTS_HCH) != 0) {
      break;
    }
  }

  if (TimeOut == 0) {
    DEBUG((gEHCErrorLevel, "TimeOut for clearing Run/Stop bit\n"));
  }

  ReadEhcOperationalReg (
    HcDev,
    USBCMD,
    &Value
    );
  Value = Value | USBCMD_HCRESET;
  WriteEhcOperationalReg (
    HcDev,
    USBCMD,
    Value
    );

  TimeOut = 40;
  while (TimeOut --) {
    gBS->Stall (500);
    ReadEhcOperationalReg (
      HcDev,
      USBCMD,
      &Value
      );
    if ((Value & USBCMD_HCRESET) == 0) {
      break;
    }
  }

  if (TimeOut == 0) {
    DEBUG((gEHCErrorLevel, "TimeOut for Host Reset\n"));
  }

}

EFI_STATUS
ReadEhcCapabiltiyReg (
  IN USB2_HC_DEV             *HcDev,
  IN UINT32                  CapabiltiyRegAddr,
  IN OUT UINT32              *Data
  )
/*++

Routine Description:

  Read  Ehc Capabitlity register

Arguments:

  HcDev              - USB2_HC_DEV
  CapabiltiyRegAddr  - Ehc Capability register address
  Data               - A pointer to data read from register

Returns:

  EFI_SUCCESS        Success
  EFI_DEVICE_ERROR   Fail

--*/
{
  return HcDev->PciIo->Mem.Read (
                             HcDev->PciIo,
                             EfiPciIoWidthUint32,
                             USB_BAR_INDEX,
                             (UINT64) CapabiltiyRegAddr,
                             1,
                             Data
                             );
}

EFI_STATUS
ReadEhcOperationalReg (
  IN USB2_HC_DEV             *HcDev,
  IN UINT32                  OperationalRegAddr,
  IN OUT UINT32              *Data
  )
/*++

Routine Description:

  Read  Ehc Operation register

Arguments:

  HcDev                - USB2_HC_DEV
  OperationalRegAddr   - Ehc Operation register address
  Data                 - A pointer to data read from register

Returns:

  EFI_SUCCESS        Success
  EFI_DEVICE_ERROR   Fail

--*/
{
  ASSERT (HcDev->UsbCapabilityLen);
  return HcDev->PciIo->Mem.Read (
                             HcDev->PciIo,
                             EfiPciIoWidthUint32,
                             USB_BAR_INDEX,
                             (UINT64) (OperationalRegAddr + HcDev->UsbCapabilityLen),
                             1,
                             Data
                             );
}

EFI_STATUS
WriteEhcOperationalReg (
  IN USB2_HC_DEV             *HcDev,
  IN UINT32                  OperationalRegAddr,
  IN UINT32                  Data
  )
/*++

Routine Description:

  Write  Ehc Operation register

Arguments:

  HcDev                - USB2_HC_DEV
  OperationalRegAddr   - Ehc Operation register address
  Data                 - 32bit write to register

Returns:

  EFI_SUCCESS        Success
  EFI_DEVICE_ERROR   Fail

--*/
{
  ASSERT (HcDev->UsbCapabilityLen);
  return HcDev->PciIo->Mem.Write (
                             HcDev->PciIo,
                             EfiPciIoWidthUint32,
                             USB_BAR_INDEX,
                             (UINT64) (OperationalRegAddr + HcDev->UsbCapabilityLen),
                             1,
                             &Data
                             );
}



VOID
ClearLegacySupport (
  IN USB2_HC_DEV     *HcDev
  )
/*++

Routine Description:

  Stop the legacy USB SMI

Arguments:

  HcDev - USB2_HC_DEV

Returns:

  EFI_SUCCESS       Success
  EFI_DEVICE_ERROR  Fail

--*/
{
  UINT32  EECP;
  UINT32  Value;
  UINT32  TimeOut;

  ReadEhcCapabiltiyReg (
    HcDev,
    HCCPARAMS,
    &EECP
    );

  EECP = (EECP >> 8) & 0xFF;

  DEBUG ((gEHCDebugLevel, "EHCI: EECPBase = 0x%x\n", EECP));


  HcDev->PciIo->Pci.Read (
                     HcDev->PciIo,
                     EfiPciIoWidthUint32,
                     EECP,
                     1,
                     &Value
                     );

  DEBUG((gEHCDebugLevel, "EECP[0] = 0x%x\n", Value));

  HcDev->PciIo->Pci.Read (
                     HcDev->PciIo,
                     EfiPciIoWidthUint32,
                     EECP + 0x4,
                     1,
                     &Value
                     );

  DEBUG((gEHCDebugLevel, "EECP[4] = 0x%x\n", Value));

  HcDev->PciIo->Pci.Read (
                     HcDev->PciIo,
                     EfiPciIoWidthUint32,
                     EECP,
                     1,
                     &Value
                     );

  Value = Value | (0x1 << 24);
  DEBUG((gEHCErrorLevel, "Value Written = 0x%x\n", Value));

  HcDev->PciIo->Pci.Write (
                     HcDev->PciIo,
                     EfiPciIoWidthUint32,
                     EECP,
                     1,
                     &Value
                     );

 TimeOut = 40;
 while (TimeOut --) {
   gBS->Stall (500);

   HcDev->PciIo->Pci.Read (
                      HcDev->PciIo,
                      EfiPciIoWidthUint32,
                      EECP,
                      1,
                      &Value
                      );
  if ((Value & 0x01010000) == 0x01000000) {
    break;
  }
 }

  if (TimeOut == 0) {
    DEBUG((gEHCErrorLevel, "Timeout for getting HC OS Owned Semaphore\n" ));
  }

  DEBUG((gEHCErrorLevel, "After Release Value\n" ));

  HcDev->PciIo->Pci.Read (
                     HcDev->PciIo,
                     EfiPciIoWidthUint32,
                     EECP,
                     1,
                     &Value
                     );

  DEBUG((gEHCDebugLevel, "EECP[0] = 0x%x\n", Value));

  HcDev->PciIo->Pci.Read (
                     HcDev->PciIo,
                     EfiPciIoWidthUint32,
                     EECP + 0x4,
                     1,
                     &Value
                     );

  DEBUG((gEHCDebugLevel, "EECP[4] = 0x%x\n", Value));


}

EFI_STATUS
GetCapabilityLen (
  IN USB2_HC_DEV     *HcDev
  )
/*++

Routine Description:

  Get the length of capability register

Arguments:

  HcDev - USB2_HC_DEV

Returns:

  EFI_SUCCESS       Success
  EFI_DEVICE_ERROR  Fail

--*/
{
  EFI_STATUS  Status;
  UINT32      CapabilityLenAddr;

  CapabilityLenAddr = CAPLENGTH;

  Status = ReadEhcCapabiltiyReg (
             HcDev,
             CapabilityLenAddr,
             &(HcDev->UsbCapabilityLen)
             );
  HcDev->UsbCapabilityLen = (UINT8) HcDev->UsbCapabilityLen;

  return Status;
}

EFI_STATUS
SetFrameListLen (
  IN USB2_HC_DEV     *HcDev,
  IN UINTN           Length
  )
/*++

Routine Description:

  Set the length of Frame List

Arguments:

  HcDev    - USB2_HC_DEV
  Length   - the required length of frame list

Returns:

  EFI_SUCCESS            Success
  EFI_INVALID_PARAMETER  Invalid parameter
  EFI_DEVICE_ERROR       Fail

--*/
{
  EFI_STATUS  Status;
  UINT32      UsbCommandAddr;
  UINT32      UsbCommandReg;

  UsbCommandAddr = USBCMD;

  if (256 != Length && 512 != Length) {
    Status = EFI_INVALID_PARAMETER;
    goto exit;
  }

  Status = ReadEhcOperationalReg (
             HcDev,
             UsbCommandAddr,
             &UsbCommandReg
             );
  if (EFI_ERROR (Status)) {
    Status = EFI_DEVICE_ERROR;
    goto exit;
  }

  if (256 == Length) {
    UsbCommandReg |= USBCMD_FLS_256;
  } else {
    UsbCommandReg |= USBCMD_FLS_512;
  }

  Status = WriteEhcOperationalReg (
             HcDev,
             UsbCommandAddr,
             UsbCommandReg
             );
  if (EFI_ERROR (Status)) {
    Status = EFI_DEVICE_ERROR;
  }

exit:
  return Status;
}

EFI_STATUS
SetFrameListBaseAddr (
  IN USB2_HC_DEV     *HcDev,
  IN UINT32          FrameBuffer
  )
/*++

Routine Description:

  Set base address of frame list first entry

Arguments:

  HcDev        - USB2_HC_DEV
  FrameBuffer  - base address of first entry of frame list

Returns:

--*/
{
  EFI_STATUS  Status;
  UINT32      PeriodicListBaseAddr;
  UINT32      PeriodicListBaseReg;

  Status                = EFI_SUCCESS;
  PeriodicListBaseAddr  = PERIODICLISTBASE;
  PeriodicListBaseReg   = FrameBuffer & 0xfffff000;

  if (IsEhcHalted (HcDev)) {

    Status = WriteEhcOperationalReg (
               HcDev,
               PeriodicListBaseAddr,
               PeriodicListBaseReg
               );
    if (EFI_ERROR (Status)) {
      Status = EFI_DEVICE_ERROR;
      goto exit;
    }

  }

exit:
  return Status;
}

EFI_STATUS
SetAsyncListAddr (
  IN USB2_HC_DEV        *HcDev,
  IN EHCI_QH_ENTITY     *QhPtr
  )
/*++

Routine Description:

  Set address of first Async schedule Qh

Arguments:

  HcDev   - USB2_HC_DEV
  QhPtr   - A pointer to first Qh in the Async schedule

Returns:

  EFI_SUCCESS       Success
  EFI_DEVICE_ERROR  Fail

--*/
{
  EFI_STATUS  Status;
  UINT32      AsyncListAddr;
  UINT32      AsyncListReg;

  AsyncListAddr = ASYNCLISTADDR;
  AsyncListReg  = (UINT32) GET_0B_TO_31B (&(QhPtr->Qh));

  Status = WriteEhcOperationalReg (
             HcDev,
             AsyncListAddr,
             AsyncListReg
             );

  return Status;
}

EFI_STATUS
SetCtrlDataStructSeg (
  IN USB2_HC_DEV     *HcDev
  )
/*++

Routine Description:

  Set register of control and data structure segment

Arguments:

  HcDev  - USB2_HC_DEV

Returns:

  EFI_SUCCESS       Success
  EFI_DEVICE_ERROR  Fail


--*/
{
  EFI_STATUS  Status;
  UINT32      CtrlDsSegmentAddr;
  UINT32      CtrlDsSegmentReg;

  CtrlDsSegmentAddr = CTRLDSSGMENT;
  CtrlDsSegmentReg  = HcDev->High32BitAddr;

  Status = WriteEhcOperationalReg (
             HcDev,
             CtrlDsSegmentAddr,
             CtrlDsSegmentReg
             );

  return Status;
}

EFI_STATUS
SetPortRoutingEhc (
  IN USB2_HC_DEV     *HcDev
  )
/*++

Routine Description:

  Set Ehc port routing bit

Arguments:

  HcDev - USB2_HC_DEV

Returns:

  EFI_SUCCESS       Success
  EFI_DEVICE_ERROR  Fail

--*/
{
  EFI_STATUS  Status;
  UINT32      ConfigFlagAddr;
  UINT32      ConfigFlagReg;

  ConfigFlagAddr = CONFIGFLAG;

  Status = ReadEhcOperationalReg (
             HcDev,
             ConfigFlagAddr,
             &ConfigFlagReg
             );
  if (EFI_ERROR (Status)) {
    Status = EFI_DEVICE_ERROR;
    goto exit;
  }

  ConfigFlagReg |= CONFIGFLAG_CF;
  Status = WriteEhcOperationalReg (
             HcDev,
             ConfigFlagAddr,
             ConfigFlagReg
             );
  if (EFI_ERROR (Status)) {
    Status = EFI_DEVICE_ERROR;
  }

exit:
  return Status;
}

EFI_STATUS
SetEhcDoorbell (
  IN  USB2_HC_DEV     *HcDev
  )
/*++

Routine Description:

  Set Ehc door bell bit

Arguments:

  HcDev - USB2_HC_DEV

Returns:

  EFI_SUCCESS       Success
  EFI_DEVICE_ERROR  Fail

--*/
{
  EFI_STATUS  Status;
  UINT32      UsbCommandAddr;
  UINT32      UsbCommandReg;

  UsbCommandAddr = USBCMD;

  Status = ReadEhcOperationalReg (
             HcDev,
             UsbCommandAddr,
             &UsbCommandReg
             );
  if (EFI_ERROR (Status)) {
    Status = EFI_DEVICE_ERROR;
    goto exit;
  }

  UsbCommandReg |= USBCMD_IAAD;
  Status = WriteEhcOperationalReg (
             HcDev,
             UsbCommandAddr,
             UsbCommandReg
             );
  if (EFI_ERROR (Status)) {
    Status = EFI_DEVICE_ERROR;
  }

exit:
  return Status;
}

EFI_STATUS
ClearEhcAllStatus (
  IN  USB2_HC_DEV     *HcDev
  )
/*++

Routine Description:

  Clear Ehc all status bits

Arguments:

  HcDev - USB2_HC_DEV

Returns:

  EFI_SUCCESS       Success
  EFI_DEVICE_ERROR  Fail

--*/
{
  UINT32  UsbStatusAddr;

  UsbStatusAddr = USBSTS;

  return WriteEhcOperationalReg (
           HcDev,
           UsbStatusAddr,
           0x003F
           );
}

EFI_STATUS
EnablePeriodicSchedule (
  IN  USB2_HC_DEV     *HcDev
  )
/*++

Routine Description:

  Enable periodic schedule

Arguments:

  HcDev - USB2_HC_DEV

Returns:

  EFI_SUCCESS       Success
  EFI_DEVICE_ERROR  Fail

--*/
{
  EFI_STATUS  Status;
  UINT32      UsbCommandAddr;
  UINT32      UsbCommandReg;

  UsbCommandAddr = USBCMD;

  Status = ReadEhcOperationalReg (
             HcDev,
             UsbCommandAddr,
             &UsbCommandReg
             );
  if (EFI_ERROR (Status)) {
    Status = EFI_DEVICE_ERROR;
    goto exit;
  }

  UsbCommandReg |= USBCMD_PSE;
  Status = WriteEhcOperationalReg (
             HcDev,
             UsbCommandAddr,
             UsbCommandReg
             );
  if (EFI_ERROR (Status)) {
    Status = EFI_DEVICE_ERROR;
  }

exit:
  return Status;
}

EFI_STATUS
DisablePeriodicSchedule (
  IN  USB2_HC_DEV     *HcDev
  )
/*++

Routine Description:

  Disable periodic schedule

Arguments:

  HcDev - USB2_HC_DEV

Returns:

  EFI_SUCCESS       Success
  EFI_DEVICE_ERROR  Fail

--*/
{
  EFI_STATUS  Status;
  UINT32      UsbCommandAddr;
  UINT32      UsbCommandReg;

  UsbCommandAddr = USBCMD;

  Status = ReadEhcOperationalReg (
             HcDev,
             UsbCommandAddr,
             &UsbCommandReg
             );
  if (EFI_ERROR (Status)) {
    return EFI_DEVICE_ERROR;
  }

  UsbCommandReg &= ~USBCMD_PSE;
  Status = WriteEhcOperationalReg (
             HcDev,
             UsbCommandAddr,
             UsbCommandReg
             );
  if (EFI_ERROR (Status)) {
    return EFI_DEVICE_ERROR;
  }

  return Status;
}

EFI_STATUS
EnableAsynchronousSchedule (
  IN  USB2_HC_DEV     *HcDev
  )
/*++

Routine Description:

  Enable asynchrounous schedule

Arguments:

  HcDev - USB2_HC_DEV

Returns:

  EFI_SUCCESS       Success
  EFI_DEVICE_ERROR  Fail

--*/
{
  EFI_STATUS  Status;
  UINT32      UsbCommandAddr;
  UINT32      UsbCommandReg;

  UsbCommandAddr = USBCMD;

  Status = ReadEhcOperationalReg (
             HcDev,
             UsbCommandAddr,
             &UsbCommandReg
             );
  if (EFI_ERROR (Status)) {
    Status = EFI_DEVICE_ERROR;
    goto exit;
  }

  UsbCommandReg |= USBCMD_ASE;
  Status = WriteEhcOperationalReg (
             HcDev,
             UsbCommandAddr,
             UsbCommandReg
             );
  if (EFI_ERROR (Status)) {
    Status = EFI_DEVICE_ERROR;
  }

exit:
  return Status;
}

EFI_STATUS
DisableAsynchronousSchedule (
  IN  USB2_HC_DEV     *HcDev
  )
/*++

Routine Description:

  Disable asynchrounous schedule

Arguments:

  HcDev - USB2_HC_DEV

Returns:

  EFI_SUCCESS       Success
  EFI_DEVICE_ERROR  Fail

--*/
{
  EFI_STATUS  Status;
  UINT32      UsbCommandAddr;
  UINT32      UsbCommandReg;

  UsbCommandAddr = USBCMD;

  Status = ReadEhcOperationalReg (
             HcDev,
             UsbCommandAddr,
             &UsbCommandReg
             );
  if (EFI_ERROR (Status)) {
    return EFI_DEVICE_ERROR;
  }

  UsbCommandReg &= ~USBCMD_ASE;
  Status = WriteEhcOperationalReg (
             HcDev,
             UsbCommandAddr,
             UsbCommandReg
             );
  if (EFI_ERROR (Status)) {
    return EFI_DEVICE_ERROR;
  }

  return Status;
}

EFI_STATUS
ResetEhc (
  IN  USB2_HC_DEV     *HcDev
  )
/*++

Routine Description:

  Reset Ehc

Arguments:

  HcDev - USB2_HC_DEV

Returns:

  EFI_SUCCESS       Success
  EFI_DEVICE_ERROR  Fail

--*/
{
  EFI_STATUS  Status;
  UINT32      UsbCommandAddr;
  UINT32      UsbCommandReg;

  UsbCommandAddr = USBCMD;

  Status = ReadEhcOperationalReg (
             HcDev,
             UsbCommandAddr,
             &UsbCommandReg
             );
  if (EFI_ERROR (Status)) {
    Status = EFI_DEVICE_ERROR;
    goto exit;
  }

  UsbCommandReg |= USBCMD_HCRESET;
  Status = WriteEhcOperationalReg (
             HcDev,
             UsbCommandAddr,
             UsbCommandReg
             );
  if (EFI_ERROR (Status)) {
    Status = EFI_DEVICE_ERROR;
  }

exit:
  return Status;
}

EFI_STATUS
StartScheduleExecution (
  IN  USB2_HC_DEV     *HcDev
  )
/*++

Routine Description:

  Start Ehc schedule execution

Arguments:

  HcDev - USB2_HC_DEV

Returns:

  EFI_SUCCESS       Success
  EFI_DEVICE_ERROR  Fail

--*/
{
  EFI_STATUS  Status;
  UINT32      UsbCommandAddr;
  UINT32      UsbCommandReg;

  UsbCommandAddr = USBCMD;

  Status = ReadEhcOperationalReg (
             HcDev,
             UsbCommandAddr,
             &UsbCommandReg
             );
  if (EFI_ERROR (Status)) {
    Status = EFI_DEVICE_ERROR;
    goto exit;
  }

  UsbCommandReg |= USBCMD_RS;
  Status = WriteEhcOperationalReg (
             HcDev,
             UsbCommandAddr,
             UsbCommandReg
             );
  if (EFI_ERROR (Status)) {
    Status = EFI_DEVICE_ERROR;
  }

exit:
  return Status;
}

BOOLEAN
IsFrameListProgrammable (
  IN  USB2_HC_DEV     *HcDev
  )
/*++

Routine Description:

  Whether frame list is programmable

Arguments:

  HcDev - USB2_HC_DEV

Returns:

  TRUE   Programmable
  FALSE  Unprogrammable

--*/
{
  BOOLEAN Value;
  UINT32  HcCapParamsAddr;
  UINT32  HcCapParamsReg;

  HcCapParamsAddr = HCCPARAMS;

  ReadEhcCapabiltiyReg(
    HcDev,
    HcCapParamsAddr,
    &HcCapParamsReg
    );

  if (HcCapParamsReg & HCCP_PFLF) {
    Value = TRUE;
  } else {
    Value = FALSE;
  }

  return Value;
}

BOOLEAN
IsPeriodicScheduleEnabled (
  IN  USB2_HC_DEV     *HcDev
  )
/*++

Routine Description:

  Whether periodic schedule is enabled

Arguments:

  HcDev - USB2_HC_DEV

Returns:

  TRUE    Enabled
  FALSE   Disabled

--*/
{
  BOOLEAN Value;
  UINT32  UsbStatusAddr;
  UINT32  UsbStatusReg;

  UsbStatusAddr = USBSTS;

  ReadEhcOperationalReg (
    HcDev,
    UsbStatusAddr,
    &UsbStatusReg
    );

  if (UsbStatusReg & USBSTS_PSS) {
    Value = TRUE;
  } else {
    Value = FALSE;
  }

  return Value;
}

BOOLEAN
IsAsyncScheduleEnabled (
  IN  USB2_HC_DEV     *HcDev
  )
/*++

Routine Description:

  Whether asynchronous schedule is enabled

Arguments:

  HcDev - USB2_HC_DEV

Returns:

  TRUE   Enabled
  FALSE  Disabled

--*/
{
  BOOLEAN Value;
  UINT32  UsbStatusAddr;
  UINT32  UsbStatusReg;

  UsbStatusAddr = USBSTS;

  ReadEhcOperationalReg (
    HcDev,
    UsbStatusAddr,
    &UsbStatusReg
    );

  if (UsbStatusReg & USBSTS_ASS) {
    Value = TRUE;
  } else {
    Value = FALSE;
  }

  return Value;
}

BOOLEAN
IsEhcPortEnabled (
  IN  USB2_HC_DEV     *HcDev,
  IN  UINT8           PortNum
  )
/*++

Routine Description:

  Whether port is enabled

Arguments:

  HcDev - USB2_HC_DEV

Returns:

  TRUE   Enabled
  FALSE  Disabled

--*/
{
  UINT32  PortStatusControlAddr;
  UINT32  PortStatusControlReg;

  PortStatusControlAddr = (UINT32) (PORTSC + (4 * PortNum));

  ReadEhcOperationalReg (
    HcDev,
    PortStatusControlAddr,
    &PortStatusControlReg
    );

  return ((BOOLEAN) ((PortStatusControlReg & PORTSC_PED) ? TRUE : FALSE));
}

BOOLEAN
IsEhcReseted (
  IN  USB2_HC_DEV     *HcDev
  )
/*++

Routine Description:

  Whether Ehc is reseted

Arguments:

  HcDev - USB2_HC_DEV

Returns:

  TRUE   Reseted
  FALSE  Unreseted

--*/
{
  BOOLEAN Value;
  UINT32  UsbCommandAddr;
  UINT32  UsbCommandReg;

  UsbCommandAddr = USBCMD;

  ReadEhcOperationalReg (
    HcDev,
    UsbCommandAddr,
    &UsbCommandReg
    );

  if (UsbCommandReg & USBCMD_HCRESET) {
    Value = FALSE;
  } else {
    Value = TRUE;
  }

  return Value;
}

BOOLEAN
IsEhcHalted (
  IN  USB2_HC_DEV     *HcDev
  )
/*++

Routine Description:

  Whether Ehc is halted

Arguments:

  HcDev - USB2_HC_DEV

Returns:

  TRUE   Halted
  FALSE  Not halted

--*/
{
  BOOLEAN Value;
  UINT32  UsbStatusAddr;
  UINT32  UsbStatusReg;

  UsbStatusAddr = USBSTS;

  ReadEhcOperationalReg (
    HcDev,
    UsbStatusAddr,
    &UsbStatusReg
    );

  if (UsbStatusReg & USBSTS_HCH) {
    Value = TRUE;
  } else {
    Value = FALSE;
  }

  return Value;
}

BOOLEAN
IsEhcSysError (
  IN  USB2_HC_DEV     *HcDev
  )
/*++

Routine Description:

  Whether Ehc is system error

Arguments:

  HcDev - USB2_HC_DEV

Returns:

  TRUE   System error
  FALSE  No system error

--*/
{
  BOOLEAN Value;
  UINT32  UsbStatusAddr;
  UINT32  UsbStatusReg;

  UsbStatusAddr = USBSTS;

  ReadEhcOperationalReg (
    HcDev,
    UsbStatusAddr,
    &UsbStatusReg
    );

  if (UsbStatusReg & USBSTS_HSE) {
    Value = TRUE;
  } else {
    Value = FALSE;
  }

  return Value;
}

BOOLEAN
IsHighSpeedDevice (
  IN EFI_USB2_HC_PROTOCOL *This,
  IN UINT8                PortNum
  )
/*++

Routine Description:

  Whether high speed device attached

Arguments:

  HcDev - USB2_HC_DEV

Returns:

  TRUE   High speed
  FALSE  Full speed

--*/
{
  USB2_HC_DEV          *HcDev;
  UINT32               PortStatusControlAddr;
  UINT32               PortStatusControlReg;

  HcDev = USB2_HC_DEV_FROM_THIS (This);
  PortStatusControlAddr = (UINT32) (PORTSC + (4 * PortNum));

  //
  // Set port reset bit
  //
  ReadEhcOperationalReg (
    HcDev,
    PortStatusControlAddr,
    &PortStatusControlReg
    );
  //
  // Make sure Host Controller not halt before reset it
  //
  if (IsEhcHalted (HcDev)) {
    StartScheduleExecution (HcDev);
    WaitForEhcNotHalt (HcDev, EHCI_GENERIC_TIMEOUT);
  }
  PortStatusControlReg &= 0xffffffd5;
  PortStatusControlReg |= PORTSC_PR;
  //
  // Set one to PortReset bit must also set zero to PortEnable bit
  //
  PortStatusControlReg &= ~PORTSC_PED;
  WriteEhcOperationalReg (
    HcDev,
    PortStatusControlAddr,
    PortStatusControlReg
    );

  //
  // Set Port reset recovery time
  //
  gBS->Stall (EHCI_SET_PORT_RESET_RECOVERY_TIME);

  //
  // Clear port reset bit
  //
  ReadEhcOperationalReg (
    HcDev,
    PortStatusControlAddr,
    &PortStatusControlReg
    );
  PortStatusControlReg &= 0xffffffd5;
  PortStatusControlReg &= ~PORTSC_PR;
  WriteEhcOperationalReg (
    HcDev,
    PortStatusControlAddr,
    PortStatusControlReg
    );

  //
  // Clear port reset recovery time
  //
  gBS->Stall (EHCI_CLEAR_PORT_RESET_RECOVERY_TIME);

  return ((BOOLEAN) (IsEhcPortEnabled (HcDev, PortNum) ? TRUE : FALSE));
}

EFI_STATUS
WaitForEhcReset (
  IN USB2_HC_DEV             *HcDev,
  IN UINTN                   Timeout
  )
/*++

Routine Description:

  wait for Ehc reset or timeout

Arguments:

  HcDev   - USB2_HC_DEV
  Timeout - timeout threshold

Returns:

  EFI_SUCCESS    Success
  EFI_TIMEOUT    Timeout

--*/
{
  EFI_STATUS  Status;
  UINTN       Delay;

  //
  // Timeout is in US unit
  //
  Delay = (Timeout / 50) + 1;
  do {

    if (IsEhcReseted (HcDev)) {
      Status = EFI_SUCCESS;
      goto exit;
    }
    gBS->Stall (EHCI_GENERIC_RECOVERY_TIME);

  } while (Delay--);

  Status = EFI_TIMEOUT;

exit:
  return Status;
}

EFI_STATUS
WaitForEhcHalt (
  IN USB2_HC_DEV             *HcDev,
  IN UINTN                   Timeout
  )
/*++

Routine Description:

  wait for Ehc halt or timeout

Arguments:

  HcDev   - USB2_HC_DEV
  Timeout - timeout threshold

Returns:

  EFI_SUCCESS    Success
  EFI_TIMEOUT    Timeout

--*/
{
  EFI_STATUS  Status;
  UINTN       Delay;

  //
  // Timeout is in US unit
  //
  Delay = (Timeout / 50) + 1;
  do {

    if (IsEhcHalted (HcDev)) {
      Status = EFI_SUCCESS;
      goto exit;
    }
    gBS->Stall (EHCI_GENERIC_RECOVERY_TIME);

  } while (Delay--);

  Status = EFI_TIMEOUT;

exit:
  return Status;
}

EFI_STATUS
WaitForEhcNotHalt (
  IN USB2_HC_DEV             *HcDev,
  IN UINTN                   Timeout
  )
/*++

Routine Description:

  wait for Ehc not halt or timeout

Arguments:

  HcDev   - USB2_HC_DEV
  Timeout - timeout threshold

Returns:

  EFI_SUCCESS    Success
  EFI_TIMEOUT    Timeout

--*/
{
  EFI_STATUS  Status;
  UINTN       Delay;

  //
  // Timeout is in US unit
  //
  Delay = (Timeout / 50) + 1;
  do {

    if (!IsEhcHalted (HcDev)) {
      Status = EFI_SUCCESS;
      goto exit;
    }
    gBS->Stall (EHCI_GENERIC_RECOVERY_TIME);

  } while (Delay--);

  Status = EFI_TIMEOUT;

exit:
  return Status;
}

EFI_STATUS
WaitForAsyncScheduleEnable (
  IN  USB2_HC_DEV            *HcDev,
  IN UINTN                   Timeout
  )
/*++

Routine Description:

  Wait for Ehc asynchronous schedule enable or timeout

Arguments:

  HcDev   - USB2_HC_DEV
  Timeout - timeout threshold

Returns:

  EFI_SUCCESS    Success
  EFI_TIMEOUT    Timeout

--*/
{
  EFI_STATUS  Status;
  UINTN       Delay;

  //
  // Timeout is in US unit
  //
  Delay = (Timeout / 50) + 1;
  do {

    if (IsAsyncScheduleEnabled (HcDev)) {
      Status = EFI_SUCCESS;
      goto exit;
    }
    gBS->Stall (EHCI_GENERIC_RECOVERY_TIME);

  } while (Delay--);

  Status = EFI_TIMEOUT;

exit:
  return Status;
}

EFI_STATUS
WaitForAsyncScheduleDisable (
  IN USB2_HC_DEV             *HcDev,
  IN UINTN                   Timeout
  )
/*++

Routine Description:

  Wait for Ehc asynchronous schedule disable or timeout

Arguments:

  HcDev   - USB2_HC_DEV
  Timeout - timeout threshold

Returns:

  EFI_SUCCESS    Success
  EFI_TIMEOUT    Timeout

--*/
{
  EFI_STATUS  Status;
  UINTN       Delay;

  //
  // Timeout is in US unit
  //
  Delay = (Timeout / 50) + 1;
  do {

    if (!IsAsyncScheduleEnabled (HcDev)) {
      Status = EFI_SUCCESS;
      goto exit;
    }
    gBS->Stall (EHCI_GENERIC_RECOVERY_TIME);

  } while (Delay--);

  Status = EFI_TIMEOUT;

exit:
  return Status;
}

EFI_STATUS
WaitForPeriodicScheduleEnable (
  IN USB2_HC_DEV             *HcDev,
  IN UINTN                   Timeout
  )
/*++

Routine Description:

  Wait for Ehc periodic schedule enable or timeout

Arguments:

  HcDev   - USB2_HC_DEV
  Timeout - timeout threshold

Returns:

  EFI_SUCCESS    Success
  EFI_TIMEOUT    Timeout

--*/
{
  EFI_STATUS  Status;
  UINTN       Delay;

  //
  // Timeout is in US unit
  //
  Delay = (Timeout / 50) + 1;
  do {

    if (IsPeriodicScheduleEnabled (HcDev)) {
      Status = EFI_SUCCESS;
      goto exit;
    }
    gBS->Stall (EHCI_GENERIC_RECOVERY_TIME);

  } while (Delay--);

  Status = EFI_TIMEOUT;

exit:
  return Status;
}

EFI_STATUS
WaitForPeriodicScheduleDisable (
  IN USB2_HC_DEV             *HcDev,
  IN UINTN                   Timeout
  )
/*++

Routine Description:

  Wait for periodic schedule disable or timeout

Arguments:

  HcDev   - USB2_HC_DEV
  Timeout - timeout threshold

Returns:

  EFI_SUCCESS    Success
  EFI_TIMEOUT    Timeout

--*/
{
  EFI_STATUS  Status;
  UINTN       Delay;

  //
  // Timeout is in US unit
  //
  Delay = (Timeout / 50) + 1;
  do {

    if (!IsPeriodicScheduleEnabled (HcDev)) {
      Status = EFI_SUCCESS;
      goto exit;
    }
    gBS->Stall (EHCI_GENERIC_RECOVERY_TIME);

  } while (Delay--);

  Status = EFI_TIMEOUT;

exit:
  return Status;
}

EFI_STATUS
WaitForEhcDoorbell (
  IN USB2_HC_DEV             *HcDev,
  IN UINTN                   Timeout
  )
/*++

Routine Description:

  Wait for periodic schedule disable or timeout

Arguments:

  HcDev   - USB2_HC_DEV
  Timeout - timeout threshold

Returns:

  EFI_SUCCESS    Success
  EFI_TIMEOUT    Timeout

--*/
{
  EFI_STATUS  Status;
  UINT32      UsbCommandAddr;
  UINT32      UsbCommandReg;
  UINTN       Delay;

  UsbCommandAddr  = USBCMD;
  Delay           = (Timeout / 50) + 1;

  do {
    Status = ReadEhcOperationalReg (
               HcDev,
               UsbCommandAddr,
               &UsbCommandReg
               );
    if (EFI_ERROR (Status)) {
      Status = EFI_DEVICE_ERROR;
      goto exit;
    }
    if (!(UsbCommandReg & USBCMD_IAAD)) {
      break;
    }
	
  } while (--Delay);

  if (0 == Delay) {
    Status = EFI_TIMEOUT;
  }

exit:
  return Status;
}
