| /** @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; | |
| Data = 0; | |
| 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; | |
| } |