Files
system76-edk2/MdeModulePkg/Bus/Pci/XhciDxe/XhciReg.c
Sean Rhodes ec25e904c7 MdeModulePkg/Bus/Pci/XhciDxe: Check port is compatible before getting PSIV
On some platforms, including Sky Lake and Kaby Lake, the PSIV (Protocol
Speed ID Value) indices are shared between Protocol Speed ID DWORD' in
the extended capabilities registers for both USB2 (Full Speed) and USB3
(Super Speed).

An example can be found below:

    XhcCheckUsbPortSpeedUsedPsic: checking for USB2 ext caps
    XhciPsivGetPsid: found 3 PSID entries
    XhciPsivGetPsid: looking for port speed 1
    XhciPsivGetPsid: PSIV 1 PSIE 2 PLT 0 PSIM 12
    XhciPsivGetPsid: PSIV 2 PSIE 1 PLT 0 PSIM 1500
    XhciPsivGetPsid: PSIV 3 PSIE 2 PLT 0 PSIM 480
    XhcCheckUsbPortSpeedUsedPsic: checking for USB3 ext caps
    XhciPsivGetPsid: found 3 PSID entries
    XhciPsivGetPsid: looking for port speed 1
    XhciPsivGetPsid: PSIV 1 PSIE 3 PLT 0 PSIM 5
    XhciPsivGetPsid: PSIV 2 PSIE 3 PLT 0 PSIM 10
    XhciPsivGetPsid: PSIV 34 PSIE 2 PLT 0 PSIM 1248

The result is edk2 detecting USB2 devices as USB3 devices, which
consequently causes enumeration to fail.

To avoid incorrect detection, check the Compatible Port Offset to find
the starting Port of Root Hubs that support the protocol.

Signed-off-by: Sean Rhodes <sean@starlabs.systems>
Reviewed-by: Hao A Wu <hao.a.wu@intel.com>
2022-12-21 00:46:58 +00:00

947 lines
24 KiB
C

/** @file
The XHCI register operation routines.
Copyright (c) 2011 - 2017, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "Xhci.h"
/**
Read 1-byte width XHCI capability register.
@param Xhc The XHCI Instance.
@param Offset The offset of the 1-byte width capability register.
@return The register content read.
@retval If err, return 0xFF.
**/
UINT8
XhcReadCapReg8 (
IN USB_XHCI_INSTANCE *Xhc,
IN UINT32 Offset
)
{
UINT8 Data;
EFI_STATUS Status;
Status = Xhc->PciIo->Mem.Read (
Xhc->PciIo,
EfiPciIoWidthUint8,
XHC_BAR_INDEX,
(UINT64)Offset,
1,
&Data
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "XhcReadCapReg: Pci Io read error - %r at %d\n", Status, Offset));
Data = 0xFF;
}
return Data;
}
/**
Read 4-bytes width XHCI capability register.
@param Xhc The XHCI Instance.
@param Offset The offset of the 4-bytes width capability register.
@return The register content read.
@retval If err, return 0xFFFFFFFF.
**/
UINT32
XhcReadCapReg (
IN USB_XHCI_INSTANCE *Xhc,
IN UINT32 Offset
)
{
UINT32 Data;
EFI_STATUS Status;
Status = Xhc->PciIo->Mem.Read (
Xhc->PciIo,
EfiPciIoWidthUint32,
XHC_BAR_INDEX,
(UINT64)Offset,
1,
&Data
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "XhcReadCapReg: Pci Io read error - %r at %d\n", Status, Offset));
Data = 0xFFFFFFFF;
}
return Data;
}
/**
Read 4-bytes width XHCI Operational register.
@param Xhc The XHCI Instance.
@param Offset The offset of the 4-bytes width operational register.
@return The register content read.
@retval If err, return 0xFFFFFFFF.
**/
UINT32
XhcReadOpReg (
IN USB_XHCI_INSTANCE *Xhc,
IN UINT32 Offset
)
{
UINT32 Data;
EFI_STATUS Status;
ASSERT (Xhc->CapLength != 0);
Status = Xhc->PciIo->Mem.Read (
Xhc->PciIo,
EfiPciIoWidthUint32,
XHC_BAR_INDEX,
Xhc->CapLength + Offset,
1,
&Data
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "XhcReadOpReg: Pci Io Read error - %r at %d\n", Status, Offset));
Data = 0xFFFFFFFF;
}
return Data;
}
/**
Write the data to the 4-bytes width XHCI operational register.
@param Xhc The XHCI Instance.
@param Offset The offset of the 4-bytes width operational register.
@param Data The data to write.
**/
VOID
XhcWriteOpReg (
IN USB_XHCI_INSTANCE *Xhc,
IN UINT32 Offset,
IN UINT32 Data
)
{
EFI_STATUS Status;
ASSERT (Xhc->CapLength != 0);
Status = Xhc->PciIo->Mem.Write (
Xhc->PciIo,
EfiPciIoWidthUint32,
XHC_BAR_INDEX,
Xhc->CapLength + Offset,
1,
&Data
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "XhcWriteOpReg: Pci Io Write error: %r at %d\n", Status, Offset));
}
}
/**
Write the data to the XHCI door bell register.
@param Xhc The XHCI Instance.
@param Offset The offset of the door bell register.
@param Data The data to write.
**/
VOID
XhcWriteDoorBellReg (
IN USB_XHCI_INSTANCE *Xhc,
IN UINT32 Offset,
IN UINT32 Data
)
{
EFI_STATUS Status;
ASSERT (Xhc->DBOff != 0);
Status = Xhc->PciIo->Mem.Write (
Xhc->PciIo,
EfiPciIoWidthUint32,
XHC_BAR_INDEX,
Xhc->DBOff + Offset,
1,
&Data
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "XhcWriteOpReg: Pci Io Write error: %r at %d\n", Status, Offset));
}
}
/**
Read XHCI runtime register.
@param Xhc The XHCI Instance.
@param Offset The offset of the runtime register.
@return The register content read
**/
UINT32
XhcReadRuntimeReg (
IN USB_XHCI_INSTANCE *Xhc,
IN UINT32 Offset
)
{
UINT32 Data;
EFI_STATUS Status;
ASSERT (Xhc->RTSOff != 0);
Status = Xhc->PciIo->Mem.Read (
Xhc->PciIo,
EfiPciIoWidthUint32,
XHC_BAR_INDEX,
Xhc->RTSOff + Offset,
1,
&Data
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "XhcReadRuntimeReg: Pci Io Read error - %r at %d\n", Status, Offset));
Data = 0xFFFFFFFF;
}
return Data;
}
/**
Write the data to the XHCI runtime register.
@param Xhc The XHCI Instance.
@param Offset The offset of the runtime register.
@param Data The data to write.
**/
VOID
XhcWriteRuntimeReg (
IN USB_XHCI_INSTANCE *Xhc,
IN UINT32 Offset,
IN UINT32 Data
)
{
EFI_STATUS Status;
ASSERT (Xhc->RTSOff != 0);
Status = Xhc->PciIo->Mem.Write (
Xhc->PciIo,
EfiPciIoWidthUint32,
XHC_BAR_INDEX,
Xhc->RTSOff + Offset,
1,
&Data
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "XhcWriteRuntimeReg: Pci Io Write error: %r at %d\n", Status, Offset));
}
}
/**
Read XHCI extended capability register.
@param Xhc The XHCI Instance.
@param Offset The offset of the extended capability register.
@return The register content read
**/
UINT32
XhcReadExtCapReg (
IN USB_XHCI_INSTANCE *Xhc,
IN UINT32 Offset
)
{
UINT32 Data;
EFI_STATUS Status;
ASSERT (Xhc->ExtCapRegBase != 0);
Status = Xhc->PciIo->Mem.Read (
Xhc->PciIo,
EfiPciIoWidthUint32,
XHC_BAR_INDEX,
Xhc->ExtCapRegBase + Offset,
1,
&Data
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "XhcReadExtCapReg: Pci Io Read error - %r at %d\n", Status, Offset));
Data = 0xFFFFFFFF;
}
return Data;
}
/**
Write the data to the XHCI extended capability register.
@param Xhc The XHCI Instance.
@param Offset The offset of the extended capability register.
@param Data The data to write.
**/
VOID
XhcWriteExtCapReg (
IN USB_XHCI_INSTANCE *Xhc,
IN UINT32 Offset,
IN UINT32 Data
)
{
EFI_STATUS Status;
ASSERT (Xhc->ExtCapRegBase != 0);
Status = Xhc->PciIo->Mem.Write (
Xhc->PciIo,
EfiPciIoWidthUint32,
XHC_BAR_INDEX,
Xhc->ExtCapRegBase + Offset,
1,
&Data
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "XhcWriteExtCapReg: Pci Io Write error: %r at %d\n", Status, Offset));
}
}
/**
Set one bit of the runtime register while keeping other bits.
@param Xhc The XHCI Instance.
@param Offset The offset of the runtime register.
@param Bit The bit mask of the register to set.
**/
VOID
XhcSetRuntimeRegBit (
IN USB_XHCI_INSTANCE *Xhc,
IN UINT32 Offset,
IN UINT32 Bit
)
{
UINT32 Data;
Data = XhcReadRuntimeReg (Xhc, Offset);
Data |= Bit;
XhcWriteRuntimeReg (Xhc, Offset, Data);
}
/**
Clear one bit of the runtime register while keeping other bits.
@param Xhc The XHCI Instance.
@param Offset The offset of the runtime register.
@param Bit The bit mask of the register to set.
**/
VOID
XhcClearRuntimeRegBit (
IN USB_XHCI_INSTANCE *Xhc,
IN UINT32 Offset,
IN UINT32 Bit
)
{
UINT32 Data;
Data = XhcReadRuntimeReg (Xhc, Offset);
Data &= ~Bit;
XhcWriteRuntimeReg (Xhc, Offset, Data);
}
/**
Set one bit of the operational register while keeping other bits.
@param Xhc The XHCI Instance.
@param Offset The offset of the operational register.
@param Bit The bit mask of the register to set.
**/
VOID
XhcSetOpRegBit (
IN USB_XHCI_INSTANCE *Xhc,
IN UINT32 Offset,
IN UINT32 Bit
)
{
UINT32 Data;
Data = XhcReadOpReg (Xhc, Offset);
Data |= Bit;
XhcWriteOpReg (Xhc, Offset, Data);
}
/**
Clear one bit of the operational register while keeping other bits.
@param Xhc The XHCI Instance.
@param Offset The offset of the operational register.
@param Bit The bit mask of the register to clear.
**/
VOID
XhcClearOpRegBit (
IN USB_XHCI_INSTANCE *Xhc,
IN UINT32 Offset,
IN UINT32 Bit
)
{
UINT32 Data;
Data = XhcReadOpReg (Xhc, Offset);
Data &= ~Bit;
XhcWriteOpReg (Xhc, Offset, Data);
}
/**
Wait the operation register's bit as specified by Bit
to become set (or clear).
@param Xhc The XHCI Instance.
@param Offset The offset of the operation register.
@param Bit The bit of the register to wait for.
@param WaitToSet Wait the bit to set or clear.
@param Timeout The time to wait before abort (in millisecond, ms).
@retval EFI_SUCCESS The bit successfully changed by host controller.
@retval EFI_TIMEOUT The time out occurred.
@retval EFI_OUT_OF_RESOURCES Memory for the timer event could not be allocated.
**/
EFI_STATUS
XhcWaitOpRegBit (
IN USB_XHCI_INSTANCE *Xhc,
IN UINT32 Offset,
IN UINT32 Bit,
IN BOOLEAN WaitToSet,
IN UINT32 Timeout
)
{
EFI_STATUS Status;
EFI_EVENT TimeoutEvent;
TimeoutEvent = NULL;
if (Timeout == 0) {
return EFI_TIMEOUT;
}
Status = gBS->CreateEvent (
EVT_TIMER,
TPL_CALLBACK,
NULL,
NULL,
&TimeoutEvent
);
if (EFI_ERROR (Status)) {
goto DONE;
}
Status = gBS->SetTimer (
TimeoutEvent,
TimerRelative,
EFI_TIMER_PERIOD_MILLISECONDS (Timeout)
);
if (EFI_ERROR (Status)) {
goto DONE;
}
do {
if (XHC_REG_BIT_IS_SET (Xhc, Offset, Bit) == WaitToSet) {
Status = EFI_SUCCESS;
goto DONE;
}
gBS->Stall (XHC_1_MICROSECOND);
} while (EFI_ERROR (gBS->CheckEvent (TimeoutEvent)));
Status = EFI_TIMEOUT;
DONE:
if (TimeoutEvent != NULL) {
gBS->CloseEvent (TimeoutEvent);
}
return Status;
}
/**
Set Bios Ownership
@param Xhc The XHCI Instance.
**/
VOID
XhcSetBiosOwnership (
IN USB_XHCI_INSTANCE *Xhc
)
{
UINT32 Buffer;
if (Xhc->UsbLegSupOffset == 0xFFFFFFFF) {
return;
}
DEBUG ((DEBUG_INFO, "XhcSetBiosOwnership: called to set BIOS ownership\n"));
Buffer = XhcReadExtCapReg (Xhc, Xhc->UsbLegSupOffset);
Buffer = ((Buffer & (~USBLEGSP_OS_SEMAPHORE)) | USBLEGSP_BIOS_SEMAPHORE);
XhcWriteExtCapReg (Xhc, Xhc->UsbLegSupOffset, Buffer);
}
/**
Clear Bios Ownership
@param Xhc The XHCI Instance.
**/
VOID
XhcClearBiosOwnership (
IN USB_XHCI_INSTANCE *Xhc
)
{
UINT32 Buffer;
if (Xhc->UsbLegSupOffset == 0xFFFFFFFF) {
return;
}
DEBUG ((DEBUG_INFO, "XhcClearBiosOwnership: called to clear BIOS ownership\n"));
Buffer = XhcReadExtCapReg (Xhc, Xhc->UsbLegSupOffset);
Buffer = ((Buffer & (~USBLEGSP_BIOS_SEMAPHORE)) | USBLEGSP_OS_SEMAPHORE);
XhcWriteExtCapReg (Xhc, Xhc->UsbLegSupOffset, Buffer);
}
/**
Calculate the offset of the XHCI capability.
@param Xhc The XHCI Instance.
@param CapId The XHCI Capability ID.
@return The offset of XHCI legacy support capability register.
**/
UINT32
XhcGetCapabilityAddr (
IN USB_XHCI_INSTANCE *Xhc,
IN UINT8 CapId
)
{
UINT32 ExtCapOffset;
UINT8 NextExtCapReg;
UINT32 Data;
ExtCapOffset = 0;
do {
//
// Check if the extended capability register's capability id is USB Legacy Support.
//
Data = XhcReadExtCapReg (Xhc, ExtCapOffset);
if ((Data & 0xFF) == CapId) {
return ExtCapOffset;
}
//
// If not, then traverse all of the ext capability registers till finding out it.
//
NextExtCapReg = (UINT8)((Data >> 8) & 0xFF);
ExtCapOffset += (NextExtCapReg << 2);
} while (NextExtCapReg != 0);
return 0xFFFFFFFF;
}
/**
Calculate the offset of the xHCI Supported Protocol Capability.
@param Xhc The XHCI Instance.
@param MajorVersion The USB Major Version in xHCI Support Protocol Capability Field
@return The offset of xHCI Supported Protocol capability register.
**/
UINT32
XhcGetSupportedProtocolCapabilityAddr (
IN USB_XHCI_INSTANCE *Xhc,
IN UINT8 MajorVersion
)
{
UINT32 ExtCapOffset;
UINT8 NextExtCapReg;
UINT32 Data;
UINT32 NameString;
XHC_SUPPORTED_PROTOCOL_DW0 UsbSupportDw0;
if (Xhc == NULL) {
return 0;
}
ExtCapOffset = 0;
do {
//
// Check if the extended capability register's capability id is USB Legacy Support.
//
Data = XhcReadExtCapReg (Xhc, ExtCapOffset);
UsbSupportDw0.Dword = Data;
if ((Data & 0xFF) == XHC_CAP_USB_SUPPORTED_PROTOCOL) {
if (UsbSupportDw0.Data.RevMajor == MajorVersion) {
NameString = XhcReadExtCapReg (Xhc, ExtCapOffset + XHC_SUPPORTED_PROTOCOL_NAME_STRING_OFFSET);
if (NameString == XHC_SUPPORTED_PROTOCOL_NAME_STRING_VALUE) {
//
// Ensure Name String field is xHCI supported protocols in xHCI Supported Protocol Capability Offset 04h
//
return ExtCapOffset;
}
}
}
//
// If not, then traverse all of the ext capability registers till finding out it.
//
NextExtCapReg = (UINT8)((Data >> 8) & 0xFF);
ExtCapOffset += (NextExtCapReg << 2);
} while (NextExtCapReg != 0);
return 0xFFFFFFFF;
}
/**
Find PortSpeed value match Protocol Speed ID Value (PSIV).
@param Xhc The XHCI Instance.
@param ExtCapOffset The USB Major Version in xHCI Support Protocol Capability Field
@param PortSpeed The Port Speed Field in USB PortSc register
@param PortNumber The Port Number (0-indexed)
@return The Protocol Speed ID (PSI) from xHCI Supported Protocol capability register.
**/
UINT32
XhciPsivGetPsid (
IN USB_XHCI_INSTANCE *Xhc,
IN UINT32 ExtCapOffset,
IN UINT8 PortSpeed,
IN UINT8 PortNumber
)
{
XHC_SUPPORTED_PROTOCOL_DW2 PortId;
XHC_SUPPORTED_PROTOCOL_PROTOCOL_SPEED_ID Reg;
UINT32 Count;
UINT32 MinPortIndex;
UINT32 MaxPortIndex;
if ((Xhc == NULL) || (ExtCapOffset == 0xFFFFFFFF)) {
return 0;
}
//
// According to XHCI 1.1 spec November 2017,
// Section 7.2 xHCI Supported Protocol Capability
// 1. Get the PSIC(Protocol Speed ID Count) value.
// 2. The PSID register boundary should be Base address + PSIC * 0x04
//
PortId.Dword = XhcReadExtCapReg (Xhc, ExtCapOffset + XHC_SUPPORTED_PROTOCOL_DW2_OFFSET);
//
// According to XHCI 1.1 spec November 2017, valid values
// for CompPortOffset are 1 to CompPortCount - 1.
//
// PortNumber is zero-indexed, so subtract 1.
//
if ((PortId.Data.CompPortOffset == 0) || (PortId.Data.CompPortCount == 0)) {
return 0;
}
MinPortIndex = PortId.Data.CompPortOffset - 1;
MaxPortIndex = MinPortIndex + PortId.Data.CompPortCount - 1;
if ((PortNumber < MinPortIndex) || (PortNumber > MaxPortIndex)) {
return 0;
}
for (Count = 0; Count < PortId.Data.Psic; Count++) {
Reg.Dword = XhcReadExtCapReg (Xhc, ExtCapOffset + XHC_SUPPORTED_PROTOCOL_PSI_OFFSET + (Count << 2));
if (Reg.Data.Psiv == PortSpeed) {
return Reg.Dword;
}
}
return 0;
}
/**
Find PortSpeed value match case in XHCI Supported Protocol Capability
@param Xhc The XHCI Instance.
@param PortSpeed The Port Speed Field in USB PortSc register
@param PortNumber The Port Number (0-indexed)
@return The USB Port Speed.
**/
UINT16
XhcCheckUsbPortSpeedUsedPsic (
IN USB_XHCI_INSTANCE *Xhc,
IN UINT8 PortSpeed,
IN UINT8 PortNumber
)
{
XHC_SUPPORTED_PROTOCOL_PROTOCOL_SPEED_ID SpField;
UINT16 UsbSpeedIdMap;
if (Xhc == NULL) {
return 0;
}
SpField.Dword = 0;
UsbSpeedIdMap = 0;
//
// Check xHCI Supported Protocol Capability, find the PSIV field to match
// PortSpeed definition when the Major Revision is 03h.
//
if (Xhc->Usb3SupOffset != 0xFFFFFFFF) {
SpField.Dword = XhciPsivGetPsid (Xhc, Xhc->Usb3SupOffset, PortSpeed, PortNumber);
if (SpField.Dword != 0) {
//
// Found the corresponding PORTSC value in PSIV field of USB3 offset.
//
UsbSpeedIdMap = USB_PORT_STAT_SUPER_SPEED;
}
}
//
// Check xHCI Supported Protocol Capability, find the PSIV field to match
// PortSpeed definition when the Major Revision is 02h.
//
if ((UsbSpeedIdMap == 0) && (Xhc->Usb2SupOffset != 0xFFFFFFFF)) {
SpField.Dword = XhciPsivGetPsid (Xhc, Xhc->Usb2SupOffset, PortSpeed, PortNumber);
if (SpField.Dword != 0) {
//
// Found the corresponding PORTSC value in PSIV field of USB2 offset.
//
if (SpField.Data.Psie == 2) {
//
// According to XHCI 1.1 spec November 2017,
// Section 7.2.1 the Protocol Speed ID Exponent (PSIE) field definition,
// PSIE value shall be applied to Protocol Speed ID Mantissa when calculating, value 2 shall represent bit rate in Mb/s
//
if (SpField.Data.Psim == XHC_SUPPORTED_PROTOCOL_USB2_HIGH_SPEED_PSIM) {
//
// PSIM shows as default High-speed protocol, apply to High-speed mapping
//
UsbSpeedIdMap = USB_PORT_STAT_HIGH_SPEED;
}
} else if (SpField.Data.Psie == 1) {
//
// According to XHCI 1.1 spec November 2017,
// Section 7.2.1 the Protocol Speed ID Exponent (PSIE) field definition,
// PSIE value shall be applied to Protocol Speed ID Mantissa when calculating, value 1 shall represent bit rate in Kb/s
//
if (SpField.Data.Psim == XHC_SUPPORTED_PROTOCOL_USB2_LOW_SPEED_PSIM) {
//
// PSIM shows as default Low-speed protocol, apply to Low-speed mapping
//
UsbSpeedIdMap = USB_PORT_STAT_LOW_SPEED;
}
}
}
}
return UsbSpeedIdMap;
}
/**
Whether the XHCI host controller is halted.
@param Xhc The XHCI Instance.
@retval TRUE The controller is halted.
@retval FALSE It isn't halted.
**/
BOOLEAN
XhcIsHalt (
IN USB_XHCI_INSTANCE *Xhc
)
{
return XHC_REG_BIT_IS_SET (Xhc, XHC_USBSTS_OFFSET, XHC_USBSTS_HALT);
}
/**
Whether system error occurred.
@param Xhc The XHCI Instance.
@retval TRUE System error happened.
@retval FALSE No system error.
**/
BOOLEAN
XhcIsSysError (
IN USB_XHCI_INSTANCE *Xhc
)
{
return XHC_REG_BIT_IS_SET (Xhc, XHC_USBSTS_OFFSET, XHC_USBSTS_HSE);
}
/**
Set USBCMD Host System Error Enable(HSEE) Bit if PCICMD SERR# Enable Bit is set.
The USBCMD HSEE Bit will be reset to default 0 by USBCMD Host Controller Reset(HCRST).
This function is to set USBCMD HSEE Bit if PCICMD SERR# Enable Bit is set.
@param Xhc The XHCI Instance.
**/
VOID
XhcSetHsee (
IN USB_XHCI_INSTANCE *Xhc
)
{
EFI_STATUS Status;
EFI_PCI_IO_PROTOCOL *PciIo;
UINT16 XhciCmd;
PciIo = Xhc->PciIo;
Status = PciIo->Pci.Read (
PciIo,
EfiPciIoWidthUint16,
PCI_COMMAND_OFFSET,
sizeof (XhciCmd) / sizeof (UINT16),
&XhciCmd
);
if (!EFI_ERROR (Status)) {
if ((XhciCmd & EFI_PCI_COMMAND_SERR) != 0) {
XhcSetOpRegBit (Xhc, XHC_USBCMD_OFFSET, XHC_USBCMD_HSEE);
}
}
}
/**
Reset the XHCI host controller.
@param Xhc The XHCI Instance.
@param Timeout Time to wait before abort (in millisecond, ms).
@retval EFI_SUCCESS The XHCI host controller is reset.
@return Others Failed to reset the XHCI before Timeout.
**/
EFI_STATUS
XhcResetHC (
IN USB_XHCI_INSTANCE *Xhc,
IN UINT32 Timeout
)
{
EFI_STATUS Status;
Status = EFI_SUCCESS;
DEBUG ((DEBUG_INFO, "XhcResetHC!\n"));
//
// Host can only be reset when it is halt. If not so, halt it
//
if (!XHC_REG_BIT_IS_SET (Xhc, XHC_USBSTS_OFFSET, XHC_USBSTS_HALT)) {
Status = XhcHaltHC (Xhc, Timeout);
if (EFI_ERROR (Status)) {
return Status;
}
}
if ((Xhc->DebugCapSupOffset == 0xFFFFFFFF) || ((XhcReadExtCapReg (Xhc, Xhc->DebugCapSupOffset) & 0xFF) != XHC_CAP_USB_DEBUG) ||
((XhcReadExtCapReg (Xhc, Xhc->DebugCapSupOffset + XHC_DC_DCCTRL) & BIT0) == 0))
{
XhcSetOpRegBit (Xhc, XHC_USBCMD_OFFSET, XHC_USBCMD_RESET);
//
// Some XHCI host controllers require to have extra 1ms delay before accessing any MMIO register during reset.
// Otherwise there may have the timeout case happened.
// The below is a workaround to solve such problem.
//
gBS->Stall (XHC_1_MILLISECOND);
Status = XhcWaitOpRegBit (Xhc, XHC_USBCMD_OFFSET, XHC_USBCMD_RESET, FALSE, Timeout);
if (!EFI_ERROR (Status)) {
//
// The USBCMD HSEE Bit will be reset to default 0 by USBCMD HCRST.
// Set USBCMD HSEE Bit if PCICMD SERR# Enable Bit is set.
//
XhcSetHsee (Xhc);
}
}
return Status;
}
/**
Halt the XHCI host controller.
@param Xhc The XHCI Instance.
@param Timeout Time to wait before abort (in millisecond, ms).
@return EFI_SUCCESS The XHCI host controller is halt.
@return EFI_TIMEOUT Failed to halt the XHCI before Timeout.
**/
EFI_STATUS
XhcHaltHC (
IN USB_XHCI_INSTANCE *Xhc,
IN UINT32 Timeout
)
{
EFI_STATUS Status;
XhcClearOpRegBit (Xhc, XHC_USBCMD_OFFSET, XHC_USBCMD_RUN);
Status = XhcWaitOpRegBit (Xhc, XHC_USBSTS_OFFSET, XHC_USBSTS_HALT, TRUE, Timeout);
return Status;
}
/**
Set the XHCI host controller to run.
@param Xhc The XHCI Instance.
@param Timeout Time to wait before abort (in millisecond, ms).
@return EFI_SUCCESS The XHCI host controller is running.
@return EFI_TIMEOUT Failed to set the XHCI to run before Timeout.
**/
EFI_STATUS
XhcRunHC (
IN USB_XHCI_INSTANCE *Xhc,
IN UINT32 Timeout
)
{
EFI_STATUS Status;
XhcSetOpRegBit (Xhc, XHC_USBCMD_OFFSET, XHC_USBCMD_RUN);
Status = XhcWaitOpRegBit (Xhc, XHC_USBSTS_OFFSET, XHC_USBSTS_HALT, FALSE, Timeout);
return Status;
}