/** @file
  The OHCI register operation routines.

Copyright(c) 2013 Intel Corporation. All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name of Intel Corporation nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

**/


#include "Ohci.h"

/**

  Get OHCI operational reg value

  @param  PciIo                 PciIo protocol instance
  @param  Offset                Offset of the operational reg

  @retval                       Value of the register

**/
UINT32
OhciGetOperationalReg (
  IN EFI_PCI_IO_PROTOCOL  *PciIo,
  IN UINT32               Offset
  )
{
  UINT32                  Value = 0;
//  EFI_STATUS              Status;

/*  Status = */PciIo->Mem.Read(PciIo, EfiPciIoWidthUint32, OHC_BAR_INDEX, Offset, 1, &Value);

  return Value;
}
/**

  Set OHCI operational reg value

  @param  PciIo                  PCI Bus Io protocol instance
  @param  Offset                 Offset of the operational reg
  @param  Value                  Value to set

  @retval EFI_SUCCESS            Value set to the reg

**/


EFI_STATUS
OhciSetOperationalReg (
  IN EFI_PCI_IO_PROTOCOL  *PciIo,
  IN UINT32               Offset,
  IN VOID                 *Value
  )
{
  EFI_STATUS Status;

  Status = PciIo->Mem.Write(PciIo, EfiPciIoWidthUint32, OHC_BAR_INDEX, Offset, 1, Value);

  return Status;
}
/**

  Get HcRevision reg value

  @param  PciIo                 PCI Bus Io protocol instance

  @retval                       Value of the register

**/


UINT32
OhciGetHcRevision (
  IN EFI_PCI_IO_PROTOCOL  *PciIo
  )
{
  return OhciGetOperationalReg (PciIo, HC_REVISION);
}
/**

  Set HcReset reg value

  @param  Ohc                   UHC private data
  @param  Field                 Field to set
  @param  Value                 Value to set

  @retval EFI_SUCCESS           Value set

**/

EFI_STATUS
OhciSetHcReset (
  IN USB_OHCI_HC_DEV            *Ohc,
  IN UINT32                     Field,
  IN UINT32                     Value
  )
{
//  EFI_STATUS                    Status;
  HcRESET                       Reset;

//  Status = EFI_SUCCESS;
  *(UINT32 *) &Reset = OhciGetOperationalReg (Ohc->PciIo, USBHOST_OFFSET_UHCHR);

  if (Field & RESET_SYSTEM_BUS) {
    Reset.FSBIR = Value;
  }

  if (Field & RESET_HOST_CONTROLLER) {
    Reset.FHR = Value;
  }

  if (Field & RESET_CLOCK_GENERATION) {
    Reset.CGR = Value;
  }

  if (Field & RESET_SSE_GLOBAL) {
    Reset.SSE = Value;
  }

  if (Field & RESET_PSPL) {
    Reset.PSPL = Value;
  }

  if (Field & RESET_PCPL) {
    Reset.PCPL = Value;
  }

  if (Field & RESET_SSEP1) {
    Reset.SSEP1 = Value;
  }

  if (Field & RESET_SSEP2) {
    Reset.SSEP2 = Value;
  }

  if (Field & RESET_SSEP3) {
    Reset.SSEP3 = Value;
  }

  OhciSetOperationalReg (Ohc->PciIo, USBHOST_OFFSET_UHCHR, &Reset);

  return EFI_SUCCESS;
}

/**

  Get specific field of HcReset reg value

  @param  Ohc                   UHC private data
  @param  Field                 Field to get

  @retval                       Value of the field

**/

UINT32
OhciGetHcReset (
  IN USB_OHCI_HC_DEV      *Ohc,
  IN UINT32               Field
  )
{
  HcRESET                 Reset;
  UINT32                  Value;


  *(UINT32 *) &Reset = OhciGetOperationalReg (Ohc->PciIo, USBHOST_OFFSET_UHCHR);
  Value = 0;

  switch (Field) {
  case RESET_SYSTEM_BUS:
    Value = Reset.FSBIR;
    break;

  case RESET_HOST_CONTROLLER:
    Value = Reset.FHR;
    break;

  case RESET_CLOCK_GENERATION:
    Value = Reset.CGR;
    break;

  case RESET_SSE_GLOBAL:
    Value = Reset.SSE;
    break;

  case RESET_PSPL:
    Value = Reset.PSPL;
    break;

  case RESET_PCPL:
    Value = Reset.PCPL;
    break;

  case RESET_SSEP1:
    Value = Reset.SSEP1;
    break;

  case RESET_SSEP2:
    Value = Reset.SSEP2;
    break;

  case RESET_SSEP3:
    Value = Reset.SSEP3;
    break;

  default:
    ASSERT (FALSE);
  }


  return Value;
}

/**

  Set HcControl reg value

  @param  Ohc                   UHC private data
  @param  Field                 Field to set
  @param  Value                 Value to set

  @retval  EFI_SUCCESS          Value set

**/

EFI_STATUS
OhciSetHcControl (
  IN USB_OHCI_HC_DEV      *Ohc,
  IN UINTN                Field,
  IN UINT32               Value
  )
{
  EFI_STATUS              Status;
  HcCONTROL               Control;



  *(UINT32 *) &Control = OhciGetOperationalReg (Ohc->PciIo, HC_CONTROL);

  if (Field & CONTROL_BULK_RATIO) {
    Control.ControlBulkRatio = Value;
  }

  if (Field & HC_FUNCTIONAL_STATE) {
    Control.FunctionalState = Value;
  }

  if (Field & PERIODIC_ENABLE) {
    Control.PeriodicEnable = Value;
  }

  if (Field & CONTROL_ENABLE) {
    Control.ControlEnable = Value;
  }

  if (Field & ISOCHRONOUS_ENABLE) {
    Control.IsochronousEnable = Value;
  }

  if (Field & BULK_ENABLE) {
    Control.BulkEnable = Value;
  }

  if (Field & INTERRUPT_ROUTING) {
    Control.InterruptRouting = Value;
  }

  Status = OhciSetOperationalReg (Ohc->PciIo, HC_CONTROL, &Control);

  return Status;
}


/**

  Get specific field of HcControl reg value

  @param  Ohc                   UHC private data
  @param  Field                 Field to get

  @retval                       Value of the field

**/


UINT32
OhciGetHcControl (
  IN USB_OHCI_HC_DEV   *Ohc,
  IN UINTN             Field
  )
{
  HcCONTROL     Control;

  *(UINT32 *) &Control = OhciGetOperationalReg (Ohc->PciIo, HC_CONTROL);

  switch (Field) {
  case CONTROL_BULK_RATIO:
    return Control.ControlBulkRatio;
    break;
  case PERIODIC_ENABLE:
    return Control.PeriodicEnable;
    break;
  case CONTROL_ENABLE:
    return Control.ControlEnable;
    break;
  case BULK_ENABLE:
    return Control.BulkEnable;
    break;
  case ISOCHRONOUS_ENABLE:
    return Control.IsochronousEnable;
    break;
  case HC_FUNCTIONAL_STATE:
    return Control.FunctionalState;
    break;
  case INTERRUPT_ROUTING:
    return Control.InterruptRouting;
    break;
  default:
    ASSERT (FALSE);
  }

  return 0;
}

/**

  Set HcCommand reg value

  @param  Ohc                   UHC private data
  @param  Field                 Field to set
  @param  Value                 Value to set

  @retval EFI_SUCCESS           Value set

**/

EFI_STATUS
OhciSetHcCommandStatus (
  IN USB_OHCI_HC_DEV      *Ohc,
  IN UINTN                Field,
  IN UINT32               Value
  )
{
  EFI_STATUS              Status;
  HcCOMMAND_STATUS        CommandStatus;

  ZeroMem (&CommandStatus, sizeof (HcCOMMAND_STATUS));

  if(Field & HC_RESET){
    CommandStatus.HcReset = Value;
  }

  if(Field & CONTROL_LIST_FILLED){
    CommandStatus.ControlListFilled = Value;
  }

  if(Field & BULK_LIST_FILLED){
    CommandStatus.BulkListFilled = Value;
  }

  if(Field & CHANGE_OWNER_REQUEST){
    CommandStatus.ChangeOwnerRequest = Value;
  }

  if(Field & SCHEDULE_OVERRUN_COUNT){
    CommandStatus.ScheduleOverrunCount = Value;
  }

  Status = OhciSetOperationalReg (Ohc->PciIo, HC_COMMAND_STATUS, &CommandStatus);

  return Status;
}

/**

  Get specific field of HcCommand reg value

  @param  Ohc                   UHC private data
  @param  Field                 Field to get

  @retval                       Value of the field

**/

UINT32
OhciGetHcCommandStatus (
  IN USB_OHCI_HC_DEV      *Ohc,
  IN UINTN                Field
  )
{
  HcCOMMAND_STATUS        CommandStatus;

  *(UINT32 *) &CommandStatus = OhciGetOperationalReg (Ohc->PciIo, HC_COMMAND_STATUS);

  switch (Field){
  case HC_RESET:
    return CommandStatus.HcReset;
    break;
  case CONTROL_LIST_FILLED:
    return CommandStatus.ControlListFilled;
    break;
  case BULK_LIST_FILLED:
    return CommandStatus.BulkListFilled;
    break;
  case CHANGE_OWNER_REQUEST:
    return CommandStatus.ChangeOwnerRequest;
    break;
  case SCHEDULE_OVERRUN_COUNT:
    return CommandStatus.ScheduleOverrunCount;
    break;
  default:
    ASSERT (FALSE);
  }

  return 0;
}

/**

  Clear specific fields of Interrupt Status

  @param  Ohc                   UHC private data
  @param  Field                 Field to clear

  @retval EFI_SUCCESS           Fields cleared

**/

EFI_STATUS
OhciClearInterruptStatus (
  IN USB_OHCI_HC_DEV      *Ohc,
  IN UINTN                Field
  )
{
  EFI_STATUS              Status;
  HcINTERRUPT_STATUS      InterruptStatus;

  ZeroMem (&InterruptStatus, sizeof (HcINTERRUPT_STATUS));

  if(Field & SCHEDULE_OVERRUN){
    InterruptStatus.SchedulingOverrun = 1;
  }

  if(Field & WRITEBACK_DONE_HEAD){
    InterruptStatus.WriteBackDone = 1;
  }
  if(Field & START_OF_FRAME){
    InterruptStatus.Sof = 1;
  }

  if(Field & RESUME_DETECT){
    InterruptStatus.ResumeDetected = 1;
  }

  if(Field & UNRECOVERABLE_ERROR){
    InterruptStatus.UnrecoverableError = 1;
  }

  if(Field & FRAME_NUMBER_OVERFLOW){
    InterruptStatus.FrameNumOverflow = 1;
  }

  if(Field & ROOTHUB_STATUS_CHANGE){
    InterruptStatus.RHStatusChange = 1;
  }

  if(Field & OWNERSHIP_CHANGE){
    InterruptStatus.OwnerChange = 1;
  }

  Status = OhciSetOperationalReg (Ohc->PciIo, HC_INTERRUPT_STATUS, &InterruptStatus);

  return Status;
}

/**

  Get fields of HcInterrupt reg value

  @param  Ohc                   UHC private data
  @param  Field                 Field to get

  @retval                       Value of the field

**/

UINT32
OhciGetHcInterruptStatus (
  IN USB_OHCI_HC_DEV      *Ohc,
  IN UINTN                Field
  )
{
  HcINTERRUPT_STATUS      InterruptStatus;

  *(UINT32 *) &InterruptStatus = OhciGetOperationalReg (Ohc->PciIo, HC_INTERRUPT_STATUS);

  switch (Field){
  case SCHEDULE_OVERRUN:
    return InterruptStatus.SchedulingOverrun;
    break;

  case  WRITEBACK_DONE_HEAD:
    return InterruptStatus.WriteBackDone;
    break;

  case START_OF_FRAME:
    return InterruptStatus.Sof;
    break;

  case RESUME_DETECT:
    return InterruptStatus.ResumeDetected;
    break;

  case UNRECOVERABLE_ERROR:
    return InterruptStatus.UnrecoverableError;
    break;

  case FRAME_NUMBER_OVERFLOW:
    return InterruptStatus.FrameNumOverflow;
    break;

  case ROOTHUB_STATUS_CHANGE:
    return InterruptStatus.RHStatusChange;
    break;

  case OWNERSHIP_CHANGE:
    return InterruptStatus.OwnerChange;
    break;

  default:
    ASSERT (FALSE);
  }

  return 0;
}

/**

  Set Interrupt Control reg value

  @param  Ohc                   UHC private data
  @param  StatEnable            Enable or Disable
  @param  Field                 Field to set
  @param  Value                 Value to set

  @retval EFI_SUCCESS           Value set

**/

EFI_STATUS
OhciSetInterruptControl (
  IN USB_OHCI_HC_DEV      *Ohc,
  IN BOOLEAN              StatEnable,
  IN UINTN                Field,
  IN UINT32               Value
  )
{
  EFI_STATUS              Status;
  HcINTERRUPT_CONTROL     InterruptState;


  ZeroMem (&InterruptState, sizeof (HcINTERRUPT_CONTROL));

  if(Field & SCHEDULE_OVERRUN) {
    InterruptState.SchedulingOverrunInt = Value;
  }

  if(Field & WRITEBACK_DONE_HEAD) {
    InterruptState.WriteBackDoneInt = Value;
  }
  if(Field & START_OF_FRAME) {
    InterruptState.SofInt = Value;
  }

  if(Field & RESUME_DETECT) {
    InterruptState.ResumeDetectedInt = Value;
  }

  if(Field & UNRECOVERABLE_ERROR) {
    InterruptState.UnrecoverableErrorInt = Value;
  }

  if(Field & FRAME_NUMBER_OVERFLOW) {
    InterruptState.FrameNumOverflowInt = Value;
  }

  if(Field & ROOTHUB_STATUS_CHANGE) {
    InterruptState.RHStatusChangeInt = Value;
  }

  if(Field & OWNERSHIP_CHANGE) {
    InterruptState.OwnerChangedInt = Value;
  }

  if(Field & MASTER_INTERRUPT) {
    InterruptState.MasterInterruptEnable = Value;
  }

  if (StatEnable) {
    Status = OhciSetOperationalReg (Ohc->PciIo, HC_INTERRUPT_ENABLE, &InterruptState);
  } else {
    Status = OhciSetOperationalReg (Ohc->PciIo, HC_INTERRUPT_DISABLE, &InterruptState);
  }

  return Status;
}

/**

  Get field of HcInterruptControl reg value

  @param  Ohc                   UHC private data
  @param  Field                 Field to get

  @retval                       Value of the field

**/

UINT32
OhciGetHcInterruptControl (
  IN USB_OHCI_HC_DEV      *Ohc,
  IN UINTN                Field
  )
{
  HcINTERRUPT_CONTROL     InterruptState;

  *(UINT32 *) &InterruptState = OhciGetOperationalReg (Ohc->PciIo, HC_INTERRUPT_ENABLE);

  switch (Field){
    case SCHEDULE_OVERRUN:
      return InterruptState.SchedulingOverrunInt;
      break;

    case WRITEBACK_DONE_HEAD:
      return InterruptState.WriteBackDoneInt;
      break;

    case START_OF_FRAME:
      return InterruptState.SofInt;
      break;

    case RESUME_DETECT:
      return InterruptState.ResumeDetectedInt;
      break;

    case UNRECOVERABLE_ERROR:
      return InterruptState.UnrecoverableErrorInt;
      break;

    case FRAME_NUMBER_OVERFLOW:
      return InterruptState.FrameNumOverflowInt;
      break;

    case ROOTHUB_STATUS_CHANGE:
      return InterruptState.RHStatusChangeInt;
      break;

    case OWNERSHIP_CHANGE:
      return InterruptState.OwnerChangedInt;
      break;

    case MASTER_INTERRUPT:
      return InterruptState.MasterInterruptEnable;
      break;

    default:
      ASSERT (FALSE);
  }

  return 0;
}

/**

  Set memory pointer of specific type

  @param  Ohc                   UHC private data
  @param  PointerType           Type of the pointer to set
  @param  Value                 Value to set

  @retval EFI_SUCCESS           Memory pointer set

**/

EFI_STATUS
OhciSetMemoryPointer(
  IN USB_OHCI_HC_DEV      *Ohc,
  IN UINTN                PointerType,
  IN VOID                 *Value
  )
{
  EFI_STATUS              Status;
  UINT32                  Verify;

  Status = OhciSetOperationalReg (Ohc->PciIo, (UINT32)PointerType, &Value);

  if (EFI_ERROR (Status)) {
    return Status;
  }

  Verify = OhciGetOperationalReg (Ohc->PciIo, (UINT32)PointerType);

  while (Verify != (UINT32)(UINTN)Value) {
    gBS->Stall(1000);
    Verify = OhciGetOperationalReg (Ohc->PciIo, (UINT32)PointerType);
  };


  return Status;
}

/**

  Get memory pointer of specific type

  @param  Ohc                   UHC private data
  @param  PointerType           Type of pointer

  @retval                       Memory pointer of the specific type

**/

VOID *
OhciGetMemoryPointer (
  IN USB_OHCI_HC_DEV      *Ohc,
  IN UINTN                PointerType
  )
{
  
  return (VOID *)(UINTN)OhciGetOperationalReg (Ohc->PciIo, (UINT32)PointerType);
}


/**

  Set Frame Interval value

  @param  Ohc                   UHC private data
  @param  Field                 Field to set
  @param  Value                 Value to set

  @retval  EFI_SUCCESS          Value set

**/

EFI_STATUS
OhciSetFrameInterval (
  IN USB_OHCI_HC_DEV      *Ohc,
  IN UINTN                Field,
  IN UINT32               Value
  )
{
  EFI_STATUS              Status;
  HcFRM_INTERVAL          FrameInterval;


  *(UINT32 *) &FrameInterval = OhciGetOperationalReg(Ohc->PciIo, HC_FRM_INTERVAL);

  if (Field & FRAME_INTERVAL) {
    FrameInterval.FrmIntervalToggle = !FrameInterval.FrmIntervalToggle;
    FrameInterval.FrameInterval = Value;
  }

  if (Field & FS_LARGEST_DATA_PACKET) {
    FrameInterval.FSMaxDataPacket = Value;
  }

  if (Field & FRMINT_TOGGLE) {
    FrameInterval.FrmIntervalToggle = Value;
  }

  Status = OhciSetOperationalReg (
             Ohc->PciIo,
             HC_FRM_INTERVAL,
             &FrameInterval
             );

  return Status;
}


/**

  Get field of frame interval reg value

  @param  Ohc                   UHC private data
  @param  Field                 Field to get

  @retval                       Value of the field

**/

UINT32
OhciGetFrameInterval (
  IN USB_OHCI_HC_DEV      *Ohc,
  IN UINTN                Field
  )
{
  HcFRM_INTERVAL          FrameInterval;

  *(UINT32 *) &FrameInterval = OhciGetOperationalReg (Ohc->PciIo, HC_FRM_INTERVAL);

  switch (Field){
    case FRAME_INTERVAL:
      return FrameInterval.FrameInterval;
      break;

    case FS_LARGEST_DATA_PACKET:
      return FrameInterval.FSMaxDataPacket;
      break;

    case FRMINT_TOGGLE:
      return FrameInterval.FrmIntervalToggle;
      break;

    default:
      ASSERT (FALSE);
  }

  return 0;
}

/**

  Set Frame Remaining reg value

  @param  Ohc                   UHC private data
  @param  Value                 Value to set

  @retval  EFI_SUCCESS          Value set

**/

EFI_STATUS
OhciSetFrameRemaining (
  IN USB_OHCI_HC_DEV      *Ohc,
  IN UINT32               Value
  )
{
  EFI_STATUS              Status;
  HcFRAME_REMAINING       FrameRemaining;


  *(UINT32 *) &FrameRemaining = OhciGetOperationalReg (Ohc->PciIo, HC_FRM_REMAINING);

  FrameRemaining.FrameRemaining = Value;
  FrameRemaining.FrameRemainingToggle = !FrameRemaining.FrameRemainingToggle;

  Status = OhciSetOperationalReg (Ohc->PciIo, HC_FRM_REMAINING, &FrameRemaining);

  return Status;
}
/**

  Get value of frame remaining reg

  @param  Ohc                   UHC private data
  @param  Field                 Field to get

  @retval                       Value of frame remaining reg

**/
UINT32
OhciGetFrameRemaining (
  IN USB_OHCI_HC_DEV      *Ohc,
  IN UINTN                Field
  )

{
  HcFRAME_REMAINING       FrameRemaining;


  *(UINT32 *) &FrameRemaining = OhciGetOperationalReg (Ohc->PciIo, HC_FRM_REMAINING);

  switch (Field){
    case FRAME_REMAINING:
      return FrameRemaining.FrameRemaining;
      break;

    case FRAME_REMAIN_TOGGLE:
      return FrameRemaining.FrameRemainingToggle;
      break;

    default:
      ASSERT (FALSE);
  }

  return 0;
}

/**

  Set frame number reg value

  @param  Ohc                   UHC private data
  @param  Value                 Value to set

  @retval  EFI_SUCCESS          Value set

**/

EFI_STATUS
OhciSetFrameNumber(
  IN USB_OHCI_HC_DEV      *Ohc,
  IN UINT32               Value
  )
{
  EFI_STATUS              Status;

  Status = OhciSetOperationalReg (Ohc->PciIo, HC_FRM_NUMBER, &Value);

  return Status;
}

/**

  Get frame number reg value

  @param  Ohc                   UHC private data

  @retval                       Value of frame number reg

**/

UINT32
OhciGetFrameNumber (
  IN USB_OHCI_HC_DEV      *Ohc
  )
{
  return OhciGetOperationalReg(Ohc->PciIo, HC_FRM_NUMBER);
}

/**

  Set period start reg value
  
  @param  Ohc                   UHC private data
  @param  Value                 Value to set

  @retval EFI_SUCCESS           Value set

**/

EFI_STATUS
OhciSetPeriodicStart (
  IN USB_OHCI_HC_DEV      *Ohc,
  IN UINT32               Value
  )
{
  EFI_STATUS              Status;


  Status = OhciSetOperationalReg (Ohc->PciIo, HC_PERIODIC_START, &Value);

  return Status;
}


/**

  Get periodic start reg value

  @param  Ohc                   UHC private data

  @param                        Value of periodic start reg

**/

UINT32
OhciGetPeriodicStart (
  IN USB_OHCI_HC_DEV      *Ohc
  )
{
  return OhciGetOperationalReg(Ohc->PciIo, HC_PERIODIC_START);
}


/**

  Set Ls Threshold reg value

  @param  Ohc                   UHC private data
  @param  Value                 Value to set

  @retval  EFI_SUCCESS          Value set

**/

EFI_STATUS
OhciSetLsThreshold (
  IN USB_OHCI_HC_DEV      *Ohc,
  IN UINT32               Value
  )
{
  EFI_STATUS              Status;


  Status = OhciSetOperationalReg (Ohc->PciIo, HC_PERIODIC_START, &Value);

  return Status;
}


/**

  Get Ls Threshold reg value

  @param  Ohc                   UHC private data

  @retval                       Value of Ls Threshold reg

**/

UINT32
OhciGetLsThreshold (
  IN USB_OHCI_HC_DEV      *Ohc
  )
{
  return OhciGetOperationalReg(Ohc->PciIo, HC_PERIODIC_START);
}

/**

  Set Root Hub Descriptor reg value

  @param  Ohc                   UHC private data
  @param  Field                 Field to set
  @param  Value                 Value to set

  @retval  EFI_SUCCESS          Value set

**/
EFI_STATUS
OhciSetRootHubDescriptor (
  IN USB_OHCI_HC_DEV      *Ohc,
  IN UINTN                Field,
  IN UINT32               Value
  )
{
  EFI_STATUS              Status;
  HcRH_DESC_A             DescriptorA;
  HcRH_DESC_B             DescriptorB;


  if (Field & (RH_DEV_REMOVABLE | RH_PORT_PWR_CTRL_MASK)) {
    *(UINT32 *) &DescriptorB = OhciGetOperationalReg (Ohc->PciIo, HC_RH_DESC_B);

    if(Field & RH_DEV_REMOVABLE) {
      DescriptorB.DeviceRemovable = Value;
    }
    if(Field & RH_PORT_PWR_CTRL_MASK) {
      DescriptorB.PortPowerControlMask = Value;
    }

    Status = OhciSetOperationalReg (Ohc->PciIo, HC_RH_DESC_B, &DescriptorB);

    return Status;
  }

  *(UINT32 *)&DescriptorA = OhciGetOperationalReg (Ohc->PciIo, HC_RH_DESC_A);

  if(Field & RH_NUM_DS_PORTS) {
    DescriptorA.NumDownStrmPorts = Value;
  }
  if(Field & RH_NO_PSWITCH) {
    DescriptorA.NoPowerSwitch = Value;
  }
  if(Field & RH_PSWITCH_MODE) {
    DescriptorA.PowerSwitchMode = Value;
  }
  if(Field & RH_DEVICE_TYPE) {
    DescriptorA.DeviceType = Value;
  }
  if(Field & RH_OC_PROT_MODE) {
    DescriptorA.OverCurrentProtMode = Value;
  }
  if(Field & RH_NOC_PROT) {
    DescriptorA.NoOverCurrentProtMode = Value;
  }
  if(Field & RH_NO_POTPGT) {
    DescriptorA.PowerOnToPowerGoodTime = Value;
  }

  Status = OhciSetOperationalReg (Ohc->PciIo, HC_RH_DESC_A, &DescriptorA);

  return Status;
}


/**

  Get Root Hub Descriptor reg value

  @param  Ohc                   UHC private data
  @param  Field                 Field to get

  @retval                       Value of the field

**/

UINT32
OhciGetRootHubDescriptor (
  IN USB_OHCI_HC_DEV     *Ohc,
  IN UINTN               Field
  )
{
  HcRH_DESC_A             DescriptorA;
  HcRH_DESC_B             DescriptorB;


  *(UINT32 *) &DescriptorA = OhciGetOperationalReg (Ohc->PciIo, HC_RH_DESC_A);
  *(UINT32 *) &DescriptorB = OhciGetOperationalReg (Ohc->PciIo, HC_RH_DESC_B);

  switch (Field){
    case RH_DEV_REMOVABLE:
      return DescriptorB.DeviceRemovable;
      break;

    case RH_PORT_PWR_CTRL_MASK:
      return DescriptorB.PortPowerControlMask;
      break;

    case RH_NUM_DS_PORTS:
      return DescriptorA.NumDownStrmPorts;
      break;

    case RH_NO_PSWITCH:
      return DescriptorA.NoPowerSwitch;
      break;

    case RH_PSWITCH_MODE:
      return DescriptorA.PowerSwitchMode;
      break;

    case RH_DEVICE_TYPE:
      return DescriptorA.DeviceType;
      break;

    case RH_OC_PROT_MODE:
      return DescriptorA.OverCurrentProtMode;
      break;

    case RH_NOC_PROT:
      return DescriptorA.NoOverCurrentProtMode;
      break;

    case RH_NO_POTPGT:
      return DescriptorA.PowerOnToPowerGoodTime;
      break;

    default:
      ASSERT (FALSE);
  }

  return 0;
}


/**

  Set Root Hub Status reg value

  @param  Ohc                   UHC private data
  @param  Field                 Field to set

  @retval  EFI_SUCCESS          Value set

**/

EFI_STATUS
OhciSetRootHubStatus (
  IN USB_OHCI_HC_DEV      *Ohc,
  IN UINTN                Field
  )
{
  EFI_STATUS              Status;
  HcRH_STATUS             RootHubStatus;


  ZeroMem (&RootHubStatus, sizeof(HcRH_STATUS));

  if(Field & RH_LOCAL_PSTAT){
    RootHubStatus.LocalPowerStat = 1;
  }
  if(Field & RH_OC_ID){
    RootHubStatus.OverCurrentIndicator = 1;
  }
  if(Field & RH_REMOTE_WK_ENABLE){
    RootHubStatus.DevRemoteWakeupEnable = 1;
  }
  if(Field & RH_LOCAL_PSTAT_CHANGE){
    RootHubStatus.LocalPowerStatChange = 1;
  }
  if(Field & RH_OC_ID_CHANGE){
    RootHubStatus.OverCurrentIndicatorChange = 1;
  }
  if(Field & RH_CLR_RMT_WK_ENABLE){
    RootHubStatus.ClearRemoteWakeupEnable = 1;
  }

  Status = OhciSetOperationalReg (Ohc->PciIo, HC_RH_STATUS, &RootHubStatus);

  return Status;
}


/**

  Get Root Hub Status reg value

  @param  Ohc                   UHC private data
  @param  Field                 Field to get

  @retval                       Value of the field

**/

UINT32
OhciGetRootHubStatus (
  IN USB_OHCI_HC_DEV      *Ohc,
  IN UINTN                Field
  )
{
  HcRH_STATUS             RootHubStatus;


  *(UINT32 *) &RootHubStatus = OhciGetOperationalReg (Ohc->PciIo, HC_RH_STATUS);

  switch (Field) {
    case RH_LOCAL_PSTAT:
      return RootHubStatus.LocalPowerStat;
      break;
    case RH_OC_ID:
      return RootHubStatus.OverCurrentIndicator;
      break;
    case RH_REMOTE_WK_ENABLE:
      return RootHubStatus.DevRemoteWakeupEnable;
      break;
    case RH_LOCAL_PSTAT_CHANGE:
      return RootHubStatus.LocalPowerStatChange;
      break;
    case RH_OC_ID_CHANGE:
      return RootHubStatus.OverCurrentIndicatorChange;
      break;
    case RH_CLR_RMT_WK_ENABLE:
      return RootHubStatus.ClearRemoteWakeupEnable;
      break;
    default:
      ASSERT (FALSE);
  }

  return 0;
}


/**

  Set Root Hub Port Status reg value

  @param  Ohc                   UHC private data
  @param  Index                 Index of the port
  @param  Field                 Field to set

  @retval  EFI_SUCCESS          Value set

**/

EFI_STATUS
OhciSetRootHubPortStatus (
  IN USB_OHCI_HC_DEV      *Ohc,
  IN UINT32               Index,
  IN UINTN                Field
  )
{
  EFI_STATUS              Status;
  HcRHPORT_STATUS         PortStatus;


  ZeroMem (&PortStatus, sizeof(HcRHPORT_STATUS));

  if (Field & RH_CLEAR_PORT_ENABLE) {
    PortStatus.CurrentConnectStat = 1;
  }
  if (Field & RH_SET_PORT_ENABLE) {
    PortStatus.EnableStat = 1;
  }
  if (Field & RH_SET_PORT_SUSPEND) {
    PortStatus.SuspendStat = 1;
  }
  if (Field & RH_CLEAR_SUSPEND_STATUS) {
    PortStatus.OCIndicator = 1;
  }
  if (Field & RH_SET_PORT_RESET) {
    PortStatus.ResetStat = 1;
  }
  if (Field & RH_SET_PORT_POWER) {
    PortStatus.PowerStat = 1;
  }
  if (Field & RH_CLEAR_PORT_POWER) {
    PortStatus.LsDeviceAttached = 1;
  }
  if (Field & RH_CONNECT_STATUS_CHANGE) {
    PortStatus.ConnectStatChange = 1;
  }
  if (Field & RH_PORT_ENABLE_STAT_CHANGE) {
    PortStatus.EnableStatChange = 1;
  }
  if (Field & RH_PORT_SUSPEND_STAT_CHANGE) {
    PortStatus.SuspendStatChange = 1;
  }
  if (Field & RH_OC_INDICATOR_CHANGE) {
    PortStatus.OCIndicatorChange = 1;
  }
  if (Field & RH_PORT_RESET_STAT_CHANGE ) {
    PortStatus.ResetStatChange = 1;
  }

  Status = OhciSetOperationalReg (Ohc->PciIo, HC_RH_PORT_STATUS + (Index * 4), &PortStatus);

  return Status;
}


/**

  Get Root Hub Port Status reg value

  @param  Ohc                   UHC private data
  @param  Index                 Index of the port
  @param  Field                 Field to get

  @retval                       Value of the field and index

**/

UINT32
OhciReadRootHubPortStatus (
  IN USB_OHCI_HC_DEV      *Ohc,
  IN UINT32               Index,
  IN UINTN                Field
  )
{
  HcRHPORT_STATUS         PortStatus;

  *(UINT32 *) &PortStatus = OhciGetOperationalReg (
                              Ohc->PciIo,
                              HC_RH_PORT_STATUS + (Index * 4)
                              );

  switch (Field){
  case RH_CURR_CONNECT_STAT:
    return PortStatus.CurrentConnectStat;
    break;
  case RH_PORT_ENABLE_STAT:
    return PortStatus.EnableStat;
    break;
  case RH_PORT_SUSPEND_STAT:
    return PortStatus.SuspendStat;
    break;
  case RH_PORT_OC_INDICATOR:
    return PortStatus.OCIndicator;
    break;
  case RH_PORT_RESET_STAT:
    return PortStatus.ResetStat;
    break;
  case RH_PORT_POWER_STAT:
    return PortStatus.PowerStat;
    break;
  case RH_LSDEVICE_ATTACHED:
    return PortStatus.LsDeviceAttached;
    break;
  case RH_CONNECT_STATUS_CHANGE:
    return PortStatus.ConnectStatChange;
    break;
  case RH_PORT_ENABLE_STAT_CHANGE:
    return PortStatus.EnableStatChange;
    break;
  case RH_PORT_SUSPEND_STAT_CHANGE:
    return PortStatus.SuspendStatChange;
    break;
  case RH_OC_INDICATOR_CHANGE:
    return PortStatus.OCIndicatorChange;
    break;
  case RH_PORT_RESET_STAT_CHANGE:
    return PortStatus.ResetStatChange;
    break;
  default:
    ASSERT (FALSE);
  }

  return 0;
}
