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>
		
	
		
			
				
	
	
		
			947 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			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;
 | |
| }
 |