/** @file | |
The XHCI register operation routines. | |
(C) Copyright 2023 Hewlett Packard Enterprise Development LP<BR> | |
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. | |
**/ | |
EFI_STATUS | |
XhcWaitOpRegBit ( | |
IN USB_XHCI_INSTANCE *Xhc, | |
IN UINT32 Offset, | |
IN UINT32 Bit, | |
IN BOOLEAN WaitToSet, | |
IN UINT32 Timeout | |
) | |
{ | |
UINT64 TimeoutTicks; | |
UINT64 ElapsedTicks; | |
UINT64 TicksDelta; | |
UINT64 CurrentTick; | |
if (Timeout == 0) { | |
return EFI_TIMEOUT; | |
} | |
TimeoutTicks = XhcConvertTimeToTicks (XHC_MICROSECOND_TO_NANOSECOND (Timeout * XHC_1_MILLISECOND)); | |
ElapsedTicks = 0; | |
CurrentTick = GetPerformanceCounter (); | |
do { | |
if (XHC_REG_BIT_IS_SET (Xhc, Offset, Bit) == WaitToSet) { | |
return EFI_SUCCESS; | |
} | |
gBS->Stall (XHC_1_MICROSECOND); | |
TicksDelta = XhcGetElapsedTicks (&CurrentTick); | |
// Ensure that ElapsedTicks is always incremented to avoid indefinite hangs | |
if (TicksDelta == 0) { | |
TicksDelta = XhcConvertTimeToTicks (XHC_MICROSECOND_TO_NANOSECOND (XHC_1_MICROSECOND)); | |
} | |
ElapsedTicks += TicksDelta; | |
} while (ElapsedTicks < TimeoutTicks); | |
return EFI_TIMEOUT; | |
} | |
/** | |
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 (PcdGet16 (PcdDelayXhciHCReset)); | |
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; | |
} |