From d987459f8e0b78831c95188b5b0d712ed6a54c88 Mon Sep 17 00:00:00 2001 From: Star Zeng Date: Wed, 2 Jul 2014 03:20:49 +0000 Subject: [PATCH] MdeModulePkg XhciPei/UsbBusPei: Add XHCI recovery support. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Star Zeng Reviewed-by: Feng Tian git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@15611 6f19259b-4bc3-4df7-8a09-765794883524 --- MdeModulePkg/Bus/Pci/XhciPei/UsbHcMem.c | 662 +++++ MdeModulePkg/Bus/Pci/XhciPei/UsbHcMem.h | 142 + MdeModulePkg/Bus/Pci/XhciPei/XhcPeim.c | 1513 +++++++++++ MdeModulePkg/Bus/Pci/XhciPei/XhcPeim.h | 240 ++ MdeModulePkg/Bus/Pci/XhciPei/XhciPei.inf | 59 + MdeModulePkg/Bus/Pci/XhciPei/XhciReg.h | 471 ++++ MdeModulePkg/Bus/Pci/XhciPei/XhciSched.c | 2761 ++++++++++++++++++++ MdeModulePkg/Bus/Pci/XhciPei/XhciSched.h | 1228 +++++++++ MdeModulePkg/Bus/Usb/UsbBusPei/HubPeim.c | 323 ++- MdeModulePkg/Bus/Usb/UsbBusPei/HubPeim.h | 4 +- MdeModulePkg/Bus/Usb/UsbBusPei/PeiUsbLib.c | 16 +- MdeModulePkg/Bus/Usb/UsbBusPei/PeiUsbLib.h | 12 +- MdeModulePkg/Bus/Usb/UsbBusPei/UsbIoPeim.c | 4 +- MdeModulePkg/Bus/Usb/UsbBusPei/UsbPeim.c | 308 ++- MdeModulePkg/Bus/Usb/UsbBusPei/UsbPeim.h | 48 +- MdeModulePkg/Include/Ppi/UsbController.h | 8 +- MdeModulePkg/MdeModulePkg.dsc | 1 + 17 files changed, 7575 insertions(+), 225 deletions(-) create mode 100644 MdeModulePkg/Bus/Pci/XhciPei/UsbHcMem.c create mode 100644 MdeModulePkg/Bus/Pci/XhciPei/UsbHcMem.h create mode 100644 MdeModulePkg/Bus/Pci/XhciPei/XhcPeim.c create mode 100644 MdeModulePkg/Bus/Pci/XhciPei/XhcPeim.h create mode 100644 MdeModulePkg/Bus/Pci/XhciPei/XhciPei.inf create mode 100644 MdeModulePkg/Bus/Pci/XhciPei/XhciReg.h create mode 100644 MdeModulePkg/Bus/Pci/XhciPei/XhciSched.c create mode 100644 MdeModulePkg/Bus/Pci/XhciPei/XhciSched.h diff --git a/MdeModulePkg/Bus/Pci/XhciPei/UsbHcMem.c b/MdeModulePkg/Bus/Pci/XhciPei/UsbHcMem.c new file mode 100644 index 0000000000..b9ea51b38c --- /dev/null +++ b/MdeModulePkg/Bus/Pci/XhciPei/UsbHcMem.c @@ -0,0 +1,662 @@ +/** @file +PEIM to produce gPeiUsb2HostControllerPpiGuid based on gPeiUsbControllerPpiGuid +which is used to enable recovery function from USB Drivers. + +Copyright (c) 2014, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions +of the BSD License which accompanies this distribution. The +full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "XhcPeim.h" + +/** + Allocate a block of memory to be used by the buffer pool. + + @param Pages How many pages to allocate. + + @return Pointer to the allocated memory block or NULL if failed. + +**/ +USBHC_MEM_BLOCK * +UsbHcAllocMemBlock ( + IN UINTN Pages + ) +{ + USBHC_MEM_BLOCK *Block; + EFI_STATUS Status; + UINTN PageNumber; + EFI_PHYSICAL_ADDRESS TempPtr; + + PageNumber = EFI_SIZE_TO_PAGES (sizeof (USBHC_MEM_BLOCK)); + Status = PeiServicesAllocatePages ( + EfiBootServicesData, + PageNumber, + &TempPtr + ); + + if (EFI_ERROR (Status)) { + return NULL; + } + ZeroMem ((VOID *) (UINTN) TempPtr, EFI_PAGES_TO_SIZE (PageNumber)); + + // + // each bit in the bit array represents USBHC_MEM_UNIT + // bytes of memory in the memory block. + // + ASSERT (USBHC_MEM_UNIT * 8 <= EFI_PAGE_SIZE); + + Block = (USBHC_MEM_BLOCK *) (UINTN) TempPtr; + Block->BufLen = EFI_PAGES_TO_SIZE (Pages); + Block->BitsLen = Block->BufLen / (USBHC_MEM_UNIT * 8); + + PageNumber = EFI_SIZE_TO_PAGES (Block->BitsLen); + Status = PeiServicesAllocatePages ( + EfiBootServicesData, + PageNumber, + &TempPtr + ); + + if (EFI_ERROR (Status)) { + return NULL; + } + ZeroMem ((VOID *) (UINTN) TempPtr, EFI_PAGES_TO_SIZE (PageNumber)); + + Block->Bits = (UINT8 *) (UINTN) TempPtr; + + Status = PeiServicesAllocatePages ( + EfiBootServicesData, + Pages, + &TempPtr + ); + if (EFI_ERROR (Status)) { + return NULL; + } + ZeroMem ((VOID *) (UINTN) TempPtr, EFI_PAGES_TO_SIZE (Pages)); + + Block->BufHost = (UINT8 *) (UINTN) TempPtr;; + Block->Buf = (UINT8 *) (UINTN) TempPtr; + Block->Next = NULL; + + return Block; +} + +/** + Free the memory block from the memory pool. + + @param Pool The memory pool to free the block from. + @param Block The memory block to free. + +**/ +VOID +UsbHcFreeMemBlock ( + IN USBHC_MEM_POOL *Pool, + IN USBHC_MEM_BLOCK *Block + ) +{ + ASSERT ((Pool != NULL) && (Block != NULL)); + // + // No free memory in PEI. + // +} + +/** + Alloc some memory from the block. + + @param Block The memory block to allocate memory from. + @param Units Number of memory units to allocate. + + @return The pointer to the allocated memory. + If couldn't allocate the needed memory, the return value is NULL. + +**/ +VOID * +UsbHcAllocMemFromBlock ( + IN USBHC_MEM_BLOCK *Block, + IN UINTN Units + ) +{ + UINTN Byte; + UINT8 Bit; + UINTN StartByte; + UINT8 StartBit; + UINTN Available; + UINTN Count; + + ASSERT ((Block != 0) && (Units != 0)); + + StartByte = 0; + StartBit = 0; + Available = 0; + + for (Byte = 0, Bit = 0; Byte < Block->BitsLen;) { + // + // If current bit is zero, the corresponding memory unit is + // available, otherwise we need to restart our searching. + // Available counts the consective number of zero bit. + // + if (!USB_HC_BIT_IS_SET (Block->Bits[Byte], Bit)) { + Available++; + + if (Available >= Units) { + break; + } + + NEXT_BIT (Byte, Bit); + } else { + NEXT_BIT (Byte, Bit); + + Available = 0; + StartByte = Byte; + StartBit = Bit; + } + } + + if (Available < Units) { + return NULL; + } + + // + // Mark the memory as allocated + // + Byte = StartByte; + Bit = StartBit; + + for (Count = 0; Count < Units; Count++) { + ASSERT (!USB_HC_BIT_IS_SET (Block->Bits[Byte], Bit)); + + Block->Bits[Byte] = (UINT8) (Block->Bits[Byte] | (UINT8) USB_HC_BIT (Bit)); + NEXT_BIT (Byte, Bit); + } + + return Block->BufHost + (StartByte * 8 + StartBit) * USBHC_MEM_UNIT; +} + +/** + Calculate the corresponding pci bus address according to the Mem parameter. + + @param Pool The memory pool of the host controller. + @param Mem The pointer to host memory. + @param Size The size of the memory region. + + @return The pci memory address + +**/ +EFI_PHYSICAL_ADDRESS +UsbHcGetPciAddrForHostAddr ( + IN USBHC_MEM_POOL *Pool, + IN VOID *Mem, + IN UINTN Size + ) +{ + USBHC_MEM_BLOCK *Head; + USBHC_MEM_BLOCK *Block; + UINTN AllocSize; + EFI_PHYSICAL_ADDRESS PhyAddr; + UINTN Offset; + + Head = Pool->Head; + AllocSize = USBHC_MEM_ROUND (Size); + + if (Mem == NULL) { + return 0; + } + + for (Block = Head; Block != NULL; Block = Block->Next) { + // + // scan the memory block list for the memory block that + // completely contains the allocated memory. + // + if ((Block->BufHost <= (UINT8 *) Mem) && (((UINT8 *) Mem + AllocSize) <= (Block->BufHost + Block->BufLen))) { + break; + } + } + + ASSERT ((Block != NULL)); + // + // calculate the pci memory address for host memory address. + // + Offset = (UINT8 *) Mem - Block->BufHost; + PhyAddr = (EFI_PHYSICAL_ADDRESS) (UINTN) (Block->Buf + Offset); + return PhyAddr; +} + +/** + Calculate the corresponding host address according to the pci address. + + @param Pool The memory pool of the host controller. + @param Mem The pointer to pci memory. + @param Size The size of the memory region. + + @return The host memory address + +**/ +EFI_PHYSICAL_ADDRESS +UsbHcGetHostAddrForPciAddr ( + IN USBHC_MEM_POOL *Pool, + IN VOID *Mem, + IN UINTN Size + ) +{ + USBHC_MEM_BLOCK *Head; + USBHC_MEM_BLOCK *Block; + UINTN AllocSize; + EFI_PHYSICAL_ADDRESS HostAddr; + UINTN Offset; + + Head = Pool->Head; + AllocSize = USBHC_MEM_ROUND (Size); + + if (Mem == NULL) { + return 0; + } + + for (Block = Head; Block != NULL; Block = Block->Next) { + // + // scan the memory block list for the memory block that + // completely contains the allocated memory. + // + if ((Block->Buf <= (UINT8 *) Mem) && (((UINT8 *) Mem + AllocSize) <= (Block->Buf + Block->BufLen))) { + break; + } + } + + ASSERT ((Block != NULL)); + // + // calculate the host memory address for pci memory address. + // + Offset = (UINT8 *) Mem - Block->Buf; + HostAddr = (EFI_PHYSICAL_ADDRESS) (UINTN) (Block->BufHost + Offset); + return HostAddr; +} + +/** + Insert the memory block to the pool's list of the blocks. + + @param Head The head of the memory pool's block list. + @param Block The memory block to insert. + +**/ +VOID +UsbHcInsertMemBlockToPool ( + IN USBHC_MEM_BLOCK *Head, + IN USBHC_MEM_BLOCK *Block + ) +{ + ASSERT ((Head != NULL) && (Block != NULL)); + Block->Next = Head->Next; + Head->Next = Block; +} + +/** + Is the memory block empty? + + @param Block The memory block to check. + + @retval TRUE The memory block is empty. + @retval FALSE The memory block isn't empty. + +**/ +BOOLEAN +UsbHcIsMemBlockEmpty ( + IN USBHC_MEM_BLOCK *Block + ) +{ + UINTN Index; + + for (Index = 0; Index < Block->BitsLen; Index++) { + if (Block->Bits[Index] != 0) { + return FALSE; + } + } + + return TRUE; +} + +/** + Unlink the memory block from the pool's list. + + @param Head The block list head of the memory's pool. + @param BlockToUnlink The memory block to unlink. + +**/ +VOID +UsbHcUnlinkMemBlock ( + IN USBHC_MEM_BLOCK *Head, + IN USBHC_MEM_BLOCK *BlockToUnlink + ) +{ + USBHC_MEM_BLOCK *Block; + + ASSERT ((Head != NULL) && (BlockToUnlink != NULL)); + + for (Block = Head; Block != NULL; Block = Block->Next) { + if (Block->Next == BlockToUnlink) { + Block->Next = BlockToUnlink->Next; + BlockToUnlink->Next = NULL; + break; + } + } +} + +/** + Initialize the memory management pool for the host controller. + + @return Pointer to the allocated memory pool or NULL if failed. + +**/ +USBHC_MEM_POOL * +UsbHcInitMemPool ( + VOID + ) +{ + USBHC_MEM_POOL *Pool; + UINTN PageNumber; + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS TempPtr; + + PageNumber = EFI_SIZE_TO_PAGES (sizeof (USBHC_MEM_POOL)); + Status = PeiServicesAllocatePages ( + EfiBootServicesData, + PageNumber, + &TempPtr + ); + if (EFI_ERROR (Status)) { + return NULL; + } + ZeroMem ((VOID *) (UINTN) TempPtr, EFI_PAGES_TO_SIZE (PageNumber)); + + Pool = (USBHC_MEM_POOL *) ((UINTN) TempPtr); + Pool->Head = UsbHcAllocMemBlock (USBHC_MEM_DEFAULT_PAGES); + + if (Pool->Head == NULL) { + // + // No free memory in PEI. + // + Pool = NULL; + } + + return Pool; +} + +/** + Release the memory management pool. + + @param Pool The USB memory pool to free. + +**/ +VOID +UsbHcFreeMemPool ( + IN USBHC_MEM_POOL *Pool + ) +{ + USBHC_MEM_BLOCK *Block; + + ASSERT (Pool->Head != NULL); + + // + // Unlink all the memory blocks from the pool, then free them. + // UsbHcUnlinkMemBlock can't be used to unlink and free the + // first block. + // + for (Block = Pool->Head->Next; Block != NULL; Block = Pool->Head->Next) { + //UsbHcUnlinkMemBlock (Pool->Head, Block); + UsbHcFreeMemBlock (Pool, Block); + } + + UsbHcFreeMemBlock (Pool, Pool->Head); +} + +/** + Allocate some memory from the host controller's memory pool + which can be used to communicate with host controller. + + @param Pool The host controller's memory pool. + @param Size Size of the memory to allocate. + + @return The allocated memory or NULL. + +**/ +VOID * +UsbHcAllocateMem ( + IN USBHC_MEM_POOL *Pool, + IN UINTN Size + ) +{ + USBHC_MEM_BLOCK *Head; + USBHC_MEM_BLOCK *Block; + USBHC_MEM_BLOCK *NewBlock; + VOID *Mem; + UINTN AllocSize; + UINTN Pages; + + Mem = NULL; + AllocSize = USBHC_MEM_ROUND (Size); + Head = Pool->Head; + ASSERT (Head != NULL); + + // + // First check whether current memory blocks can satisfy the allocation. + // + for (Block = Head; Block != NULL; Block = Block->Next) { + Mem = UsbHcAllocMemFromBlock (Block, AllocSize / USBHC_MEM_UNIT); + + if (Mem != NULL) { + ZeroMem (Mem, Size); + break; + } + } + + if (Mem != NULL) { + return Mem; + } + + // + // Create a new memory block if there is not enough memory + // in the pool. If the allocation size is larger than the + // default page number, just allocate a large enough memory + // block. Otherwise allocate default pages. + // + if (AllocSize > EFI_PAGES_TO_SIZE (USBHC_MEM_DEFAULT_PAGES)) { + Pages = EFI_SIZE_TO_PAGES (AllocSize); + } else { + Pages = USBHC_MEM_DEFAULT_PAGES; + } + NewBlock = UsbHcAllocMemBlock (Pages); + + if (NewBlock == NULL) { + return NULL; + } + + // + // Add the new memory block to the pool, then allocate memory from it + // + UsbHcInsertMemBlockToPool (Head, NewBlock); + Mem = UsbHcAllocMemFromBlock (NewBlock, AllocSize / USBHC_MEM_UNIT); + + if (Mem != NULL) { + ZeroMem (Mem, Size); + } + + return Mem; +} + +/** + Free the allocated memory back to the memory pool. + + @param Pool The memory pool of the host controller. + @param Mem The memory to free. + @param Size The size of the memory to free. + +**/ +VOID +UsbHcFreeMem ( + IN USBHC_MEM_POOL *Pool, + IN VOID *Mem, + IN UINTN Size + ) +{ + USBHC_MEM_BLOCK *Head; + USBHC_MEM_BLOCK *Block; + UINT8 *ToFree; + UINTN AllocSize; + UINTN Byte; + UINTN Bit; + UINTN Count; + + Head = Pool->Head; + AllocSize = USBHC_MEM_ROUND (Size); + ToFree = (UINT8 *) Mem; + + for (Block = Head; Block != NULL; Block = Block->Next) { + // + // scan the memory block list for the memory block that + // completely contains the memory to free. + // + if ((Block->BufHost <= ToFree) && ((ToFree + AllocSize) <= (Block->BufHost + Block->BufLen))) { + // + // compute the start byte and bit in the bit array + // + Byte = ((ToFree - Block->BufHost) / USBHC_MEM_UNIT) / 8; + Bit = ((ToFree - Block->BufHost) / USBHC_MEM_UNIT) % 8; + + // + // reset associated bits in bit arry + // + for (Count = 0; Count < (AllocSize / USBHC_MEM_UNIT); Count++) { + ASSERT (USB_HC_BIT_IS_SET (Block->Bits[Byte], Bit)); + + Block->Bits[Byte] = (UINT8) (Block->Bits[Byte] ^ USB_HC_BIT (Bit)); + NEXT_BIT (Byte, Bit); + } + + break; + } + } + + // + // If Block == NULL, it means that the current memory isn't + // in the host controller's pool. This is critical because + // the caller has passed in a wrong memory pointer + // + ASSERT (Block != NULL); + + // + // Release the current memory block if it is empty and not the head + // + if ((Block != Head) && UsbHcIsMemBlockEmpty (Block)) { + //UsbHcUnlinkMemBlock (Head, Block); + UsbHcFreeMemBlock (Pool, Block); + } +} + +/** + Allocates pages at a specified alignment. + + If Alignment is not a power of two and Alignment is not zero, then ASSERT(). + + @param Pages The number of pages to allocate. + @param Alignment The requested alignment of the allocation. Must be a power of two. + @param HostAddress The system memory address to map to the PCI controller. + @param DeviceAddress The resulting map address for the bus master PCI controller to + use to access the hosts HostAddress. + + @retval EFI_SUCCESS Success to allocate aligned pages. + @retval EFI_INVALID_PARAMETER Pages or Alignment is not valid. + @retval EFI_OUT_OF_RESOURCES Do not have enough resources to allocate memory. + +**/ +EFI_STATUS +UsbHcAllocateAlignedPages ( + IN UINTN Pages, + IN UINTN Alignment, + OUT VOID **HostAddress, + OUT EFI_PHYSICAL_ADDRESS *DeviceAddress + ) +{ + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS Memory; + UINTN AlignedMemory; + UINTN AlignmentMask; + UINTN RealPages; + + // + // Alignment must be a power of two or zero. + // + ASSERT ((Alignment & (Alignment - 1)) == 0); + + if ((Alignment & (Alignment - 1)) != 0) { + return EFI_INVALID_PARAMETER; + } + + if (Pages == 0) { + return EFI_INVALID_PARAMETER; + } + + if (Alignment > EFI_PAGE_SIZE) { + // + // Caculate the total number of pages since alignment is larger than page size. + // + AlignmentMask = Alignment - 1; + RealPages = Pages + EFI_SIZE_TO_PAGES (Alignment); + // + // Make sure that Pages plus EFI_SIZE_TO_PAGES (Alignment) does not overflow. + // + ASSERT (RealPages > Pages); + + Status = PeiServicesAllocatePages ( + EfiBootServicesData, + Pages, + &Memory + ); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + AlignedMemory = ((UINTN) Memory + AlignmentMask) & ~AlignmentMask; + } else { + // + // Do not over-allocate pages in this case. + // + Status = PeiServicesAllocatePages ( + EfiBootServicesData, + Pages, + &Memory + ); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + AlignedMemory = (UINTN) Memory; + } + + *HostAddress = (VOID *) AlignedMemory; + *DeviceAddress = (EFI_PHYSICAL_ADDRESS) AlignedMemory; + + return EFI_SUCCESS; +} + +/** + Frees memory that was allocated with UsbHcAllocateAlignedPages(). + + @param HostAddress The system memory address to map to the PCI controller. + @param Pages The number of pages to free. + +**/ +VOID +UsbHcFreeAlignedPages ( + IN VOID *HostAddress, + IN UINTN Pages + ) +{ + ASSERT (Pages != 0); + // + // No free memory in PEI. + // +} + diff --git a/MdeModulePkg/Bus/Pci/XhciPei/UsbHcMem.h b/MdeModulePkg/Bus/Pci/XhciPei/UsbHcMem.h new file mode 100644 index 0000000000..c314e92004 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/XhciPei/UsbHcMem.h @@ -0,0 +1,142 @@ +/** @file +Private Header file for Usb Host Controller PEIM + +Copyright (c) 2014, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions +of the BSD License which accompanies this distribution. The +full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _EFI_PEI_XHCI_MEM_H_ +#define _EFI_PEI_XHCI_MEM_H_ + +#include + +#define USBHC_MEM_DEFAULT_PAGES 16 + +typedef struct _USBHC_MEM_BLOCK USBHC_MEM_BLOCK; + +struct _USBHC_MEM_BLOCK { + UINT8 *Bits; // Bit array to record which unit is allocated + UINTN BitsLen; + UINT8 *Buf; + UINT8 *BufHost; + UINTN BufLen; // Memory size in bytes + USBHC_MEM_BLOCK *Next; +}; + +// +// Memory allocation unit, must be 2^n, n>4 +// +#define USBHC_MEM_UNIT 64 + +#define USBHC_MEM_UNIT_MASK (USBHC_MEM_UNIT - 1) +#define USBHC_MEM_ROUND(Len) (((Len) + USBHC_MEM_UNIT_MASK) & (~USBHC_MEM_UNIT_MASK)) + +#define USB_HC_BIT(a) ((UINTN)(1 << (a))) + +#define USB_HC_BIT_IS_SET(Data, Bit) \ + ((BOOLEAN)(((Data) & USB_HC_BIT(Bit)) == USB_HC_BIT(Bit))) + +// +// Advance the byte and bit to the next bit, adjust byte accordingly. +// +#define NEXT_BIT(Byte, Bit) \ + do { \ + (Bit)++; \ + if ((Bit) > 7) { \ + (Byte)++; \ + (Bit) = 0; \ + } \ + } while (0) + +// +// USBHC_MEM_POOL is used to manage the memory used by USB +// host controller. XHCI requires the control memory and transfer +// data to be on the same 4G memory. +// +typedef struct _USBHC_MEM_POOL { + BOOLEAN Check4G; + UINT32 Which4G; + USBHC_MEM_BLOCK *Head; +} USBHC_MEM_POOL; + +/** + Calculate the corresponding pci bus address according to the Mem parameter. + + @param Pool The memory pool of the host controller. + @param Mem The pointer to host memory. + @param Size The size of the memory region. + + @return The pci memory address + +**/ +EFI_PHYSICAL_ADDRESS +UsbHcGetPciAddrForHostAddr ( + IN USBHC_MEM_POOL *Pool, + IN VOID *Mem, + IN UINTN Size + ); + +/** + Calculate the corresponding host address according to the pci address. + + @param Pool The memory pool of the host controller. + @param Mem The pointer to pci memory. + @param Size The size of the memory region. + + @return The host memory address + +**/ +EFI_PHYSICAL_ADDRESS +UsbHcGetHostAddrForPciAddr ( + IN USBHC_MEM_POOL *Pool, + IN VOID *Mem, + IN UINTN Size + ); + +/** + Allocates pages at a specified alignment. + + If Alignment is not a power of two and Alignment is not zero, then ASSERT(). + + @param Pages The number of pages to allocate. + @param Alignment The requested alignment of the allocation. Must be a power of two. + @param HostAddress The system memory address to map to the PCI controller. + @param DeviceAddress The resulting map address for the bus master PCI controller to + use to access the hosts HostAddress. + + @retval EFI_SUCCESS Success to allocate aligned pages. + @retval EFI_INVALID_PARAMETER Pages or Alignment is not valid. + @retval EFI_OUT_OF_RESOURCES Do not have enough resources to allocate memory. + +**/ +EFI_STATUS +UsbHcAllocateAlignedPages ( + IN UINTN Pages, + IN UINTN Alignment, + OUT VOID **HostAddress, + OUT EFI_PHYSICAL_ADDRESS *DeviceAddress + ); + +/** + Frees memory that was allocated with UsbHcAllocateAlignedPages(). + + @param HostAddress The system memory address to map to the PCI controller. + @param Pages The number of pages to free. + +**/ +VOID +UsbHcFreeAlignedPages ( + IN VOID *HostAddress, + IN UINTN Pages + ); + +#endif diff --git a/MdeModulePkg/Bus/Pci/XhciPei/XhcPeim.c b/MdeModulePkg/Bus/Pci/XhciPei/XhcPeim.c new file mode 100644 index 0000000000..9d9f53ebe3 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/XhciPei/XhcPeim.c @@ -0,0 +1,1513 @@ +/** @file +PEIM to produce gPeiUsb2HostControllerPpiGuid based on gPeiUsbControllerPpiGuid +which is used to enable recovery function from USB Drivers. + +Copyright (c) 2014, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions +of the BSD License which accompanies this distribution. The +full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "XhcPeim.h" + +// +// Two arrays used to translate the XHCI port state (change) +// to the UEFI protocol's port state (change). +// +USB_PORT_STATE_MAP mUsbPortStateMap[] = { + {XHC_PORTSC_CCS, USB_PORT_STAT_CONNECTION}, + {XHC_PORTSC_PED, USB_PORT_STAT_ENABLE}, + {XHC_PORTSC_OCA, USB_PORT_STAT_OVERCURRENT}, + {XHC_PORTSC_PP, USB_PORT_STAT_POWER}, + {XHC_PORTSC_RESET, USB_PORT_STAT_RESET} +}; + +USB_PORT_STATE_MAP mUsbPortChangeMap[] = { + {XHC_PORTSC_CSC, USB_PORT_STAT_C_CONNECTION}, + {XHC_PORTSC_PEC, USB_PORT_STAT_C_ENABLE}, + {XHC_PORTSC_OCC, USB_PORT_STAT_C_OVERCURRENT}, + {XHC_PORTSC_PRC, USB_PORT_STAT_C_RESET} +}; + +USB_CLEAR_PORT_MAP mUsbClearPortChangeMap[] = { + {XHC_PORTSC_CSC, EfiUsbPortConnectChange}, + {XHC_PORTSC_PEC, EfiUsbPortEnableChange}, + {XHC_PORTSC_OCC, EfiUsbPortOverCurrentChange}, + {XHC_PORTSC_PRC, EfiUsbPortResetChange} +}; + +USB_PORT_STATE_MAP mUsbHubPortStateMap[] = { + {XHC_HUB_PORTSC_CCS, USB_PORT_STAT_CONNECTION}, + {XHC_HUB_PORTSC_PED, USB_PORT_STAT_ENABLE}, + {XHC_HUB_PORTSC_OCA, USB_PORT_STAT_OVERCURRENT}, + {XHC_HUB_PORTSC_PP, USB_PORT_STAT_POWER}, + {XHC_HUB_PORTSC_RESET, USB_PORT_STAT_RESET} +}; + +USB_PORT_STATE_MAP mUsbHubPortChangeMap[] = { + {XHC_HUB_PORTSC_CSC, USB_PORT_STAT_C_CONNECTION}, + {XHC_HUB_PORTSC_PEC, USB_PORT_STAT_C_ENABLE}, + {XHC_HUB_PORTSC_OCC, USB_PORT_STAT_C_OVERCURRENT}, + {XHC_HUB_PORTSC_PRC, USB_PORT_STAT_C_RESET} +}; + +USB_CLEAR_PORT_MAP mUsbHubClearPortChangeMap[] = { + {XHC_HUB_PORTSC_CSC, EfiUsbPortConnectChange}, + {XHC_HUB_PORTSC_PEC, EfiUsbPortEnableChange}, + {XHC_HUB_PORTSC_OCC, EfiUsbPortOverCurrentChange}, + {XHC_HUB_PORTSC_PRC, EfiUsbPortResetChange}, + {XHC_HUB_PORTSC_BHRC, Usb3PortBHPortResetChange} +}; + +/** + Read XHCI Operation register. + + @param Xhc The XHCI device. + @param Offset The operation register offset. + + @retval the register content read. + +**/ +UINT32 +XhcPeiReadOpReg ( + IN PEI_XHC_DEV *Xhc, + IN UINT32 Offset + ) +{ + UINT32 Data; + + ASSERT (Xhc->CapLength != 0); + + Data = MmioRead32 (Xhc->UsbHostControllerBaseAddress + Xhc->CapLength + Offset); + return Data; +} + +/** + Write the data to the XHCI operation register. + + @param Xhc The XHCI device. + @param Offset The operation register offset. + @param Data The data to write. + +**/ +VOID +XhcPeiWriteOpReg ( + IN PEI_XHC_DEV *Xhc, + IN UINT32 Offset, + IN UINT32 Data + ) +{ + ASSERT (Xhc->CapLength != 0); + + MmioWrite32 (Xhc->UsbHostControllerBaseAddress + Xhc->CapLength + Offset, Data); +} + +/** + Set one bit of the operational register while keeping other bits. + + @param Xhc The XHCI device. + @param Offset The offset of the operational register. + @param Bit The bit mask of the register to set. + +**/ +VOID +XhcPeiSetOpRegBit ( + IN PEI_XHC_DEV *Xhc, + IN UINT32 Offset, + IN UINT32 Bit + ) +{ + UINT32 Data; + + Data = XhcPeiReadOpReg (Xhc, Offset); + Data |= Bit; + XhcPeiWriteOpReg (Xhc, Offset, Data); +} + +/** + Clear one bit of the operational register while keeping other bits. + + @param Xhc The XHCI device. + @param Offset The offset of the operational register. + @param Bit The bit mask of the register to clear. + +**/ +VOID +XhcPeiClearOpRegBit ( + IN PEI_XHC_DEV *Xhc, + IN UINT32 Offset, + IN UINT32 Bit + ) +{ + UINT32 Data; + + Data = XhcPeiReadOpReg (Xhc, Offset); + Data &= ~Bit; + XhcPeiWriteOpReg (Xhc, Offset, Data); +} + +/** + Wait the operation register's bit as specified by Bit + to become set (or clear). + + @param Xhc The XHCI device. + @param Offset The offset of the operational register. + @param Bit The bit mask of the register to wait for. + @param WaitToSet Wait the bit to set or clear. + @param Timeout The time to wait before abort (in microsecond, us). + + @retval EFI_SUCCESS The bit successfully changed by host controller. + @retval EFI_TIMEOUT The time out occurred. + +**/ +EFI_STATUS +XhcPeiWaitOpRegBit ( + IN PEI_XHC_DEV *Xhc, + IN UINT32 Offset, + IN UINT32 Bit, + IN BOOLEAN WaitToSet, + IN UINT32 Timeout + ) +{ + UINT32 Index; + + for (Index = 0; Index < Timeout / XHC_POLL_DELAY + 1; Index++) { + if (XHC_REG_BIT_IS_SET (Xhc, Offset, Bit) == WaitToSet) { + return EFI_SUCCESS; + } + + MicroSecondDelay (XHC_POLL_DELAY); + } + + return EFI_TIMEOUT; +} + +/** + Read XHCI capability register. + + @param Xhc The XHCI device. + @param Offset Capability register address. + + @retval the register content read. + +**/ +UINT32 +XhcPeiReadCapRegister ( + IN PEI_XHC_DEV *Xhc, + IN UINT32 Offset + ) +{ + UINT32 Data; + + Data = MmioRead32 (Xhc->UsbHostControllerBaseAddress + Offset); + + return Data; +} + +/** + Read XHCI door bell register. + + @param Xhc The XHCI device. + @param Offset The offset of the door bell register. + + @return The register content read + +**/ +UINT32 +XhcPeiReadDoorBellReg ( + IN PEI_XHC_DEV *Xhc, + IN UINT32 Offset + ) +{ + UINT32 Data; + + ASSERT (Xhc->DBOff != 0); + + Data = MmioRead32 (Xhc->UsbHostControllerBaseAddress + Xhc->DBOff + Offset); + + return Data; +} + +/** + Write the data to the XHCI door bell register. + + @param Xhc The XHCI device. + @param Offset The offset of the door bell register. + @param Data The data to write. + +**/ +VOID +XhcPeiWriteDoorBellReg ( + IN PEI_XHC_DEV *Xhc, + IN UINT32 Offset, + IN UINT32 Data + ) +{ + ASSERT (Xhc->DBOff != 0); + + MmioWrite32 (Xhc->UsbHostControllerBaseAddress + Xhc->DBOff + Offset, Data); +} + +/** + Read XHCI runtime register. + + @param Xhc The XHCI device. + @param Offset The offset of the runtime register. + + @return The register content read + +**/ +UINT32 +XhcPeiReadRuntimeReg ( + IN PEI_XHC_DEV *Xhc, + IN UINT32 Offset + ) +{ + UINT32 Data; + + ASSERT (Xhc->RTSOff != 0); + + Data = MmioRead32 (Xhc->UsbHostControllerBaseAddress + Xhc->RTSOff + Offset); + + return Data; +} + +/** + Write the data to the XHCI runtime register. + + @param Xhc The XHCI device. + @param Offset The offset of the runtime register. + @param Data The data to write. + +**/ +VOID +XhcPeiWriteRuntimeReg ( + IN PEI_XHC_DEV *Xhc, + IN UINT32 Offset, + IN UINT32 Data + ) +{ + ASSERT (Xhc->RTSOff != 0); + + MmioWrite32 (Xhc->UsbHostControllerBaseAddress + Xhc->RTSOff + Offset, Data); +} + +/** + Set one bit of the runtime register while keeping other bits. + + @param Xhc The XHCI device. + @param Offset The offset of the runtime register. + @param Bit The bit mask of the register to set. + +**/ +VOID +XhcPeiSetRuntimeRegBit ( + IN PEI_XHC_DEV *Xhc, + IN UINT32 Offset, + IN UINT32 Bit + ) +{ + UINT32 Data; + + Data = XhcPeiReadRuntimeReg (Xhc, Offset); + Data |= Bit; + XhcPeiWriteRuntimeReg (Xhc, Offset, Data); +} + +/** + Clear one bit of the runtime register while keeping other bits. + + @param Xhc The XHCI device. + @param Offset The offset of the runtime register. + @param Bit The bit mask of the register to set. + +**/ +VOID +XhcPeiClearRuntimeRegBit ( + IN PEI_XHC_DEV *Xhc, + IN UINT32 Offset, + IN UINT32 Bit + ) +{ + UINT32 Data; + + Data = XhcPeiReadRuntimeReg (Xhc, Offset); + Data &= ~Bit; + XhcPeiWriteRuntimeReg (Xhc, Offset, Data); +} + +/** + Check whether Xhc is halted. + + @param Xhc The XHCI device. + + @retval TRUE The controller is halted. + @retval FALSE The controller isn't halted. + +**/ +BOOLEAN +XhcPeiIsHalt ( + IN PEI_XHC_DEV *Xhc + ) +{ + return XHC_REG_BIT_IS_SET (Xhc, XHC_USBSTS_OFFSET, XHC_USBSTS_HALT); +} + +/** + Check whether system error occurred. + + @param Xhc The XHCI device. + + @retval TRUE System error happened. + @retval FALSE No system error. + +**/ +BOOLEAN +XhcPeiIsSysError ( + IN PEI_XHC_DEV *Xhc + ) +{ + return XHC_REG_BIT_IS_SET (Xhc, XHC_USBSTS_OFFSET, XHC_USBSTS_HSE); +} + +/** + Reset the host controller. + + @param Xhc The XHCI device. + @param Timeout Time to wait before abort (in microsecond, us). + + @retval EFI_TIMEOUT The transfer failed due to time out. + @retval Others Failed to reset the host. + +**/ +EFI_STATUS +XhcPeiResetHC ( + IN PEI_XHC_DEV *Xhc, + IN UINT32 Timeout + ) +{ + EFI_STATUS Status; + + // + // Host can only be reset when it is halt. If not so, halt it + // + if (!XhcPeiIsHalt (Xhc)) { + Status = XhcPeiHaltHC (Xhc, Timeout); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + } + + XhcPeiSetOpRegBit (Xhc, XHC_USBCMD_OFFSET, XHC_USBCMD_RESET); + Status = XhcPeiWaitOpRegBit (Xhc, XHC_USBCMD_OFFSET, XHC_USBCMD_RESET, FALSE, Timeout); +ON_EXIT: + DEBUG ((EFI_D_INFO, "XhcPeiResetHC: %r\n", Status)); + return Status; +} + +/** + Halt the host controller. + + @param Xhc The XHCI device. + @param Timeout Time to wait before abort. + + @retval EFI_TIMEOUT Failed to halt the controller before Timeout. + @retval EFI_SUCCESS The XHCI is halt. + +**/ +EFI_STATUS +XhcPeiHaltHC ( + IN PEI_XHC_DEV *Xhc, + IN UINT32 Timeout + ) +{ + EFI_STATUS Status; + + XhcPeiClearOpRegBit (Xhc, XHC_USBCMD_OFFSET, XHC_USBCMD_RUN); + Status = XhcPeiWaitOpRegBit (Xhc, XHC_USBSTS_OFFSET, XHC_USBSTS_HALT, TRUE, Timeout); + DEBUG ((EFI_D_INFO, "XhcPeiHaltHC: %r\n", Status)); + return Status; +} + +/** + Set the XHCI to run. + + @param Xhc The XHCI device. + @param Timeout Time to wait before abort. + + @retval EFI_SUCCESS The XHCI is running. + @retval Others Failed to set the XHCI to run. + +**/ +EFI_STATUS +XhcPeiRunHC ( + IN PEI_XHC_DEV *Xhc, + IN UINT32 Timeout + ) +{ + EFI_STATUS Status; + + XhcPeiSetOpRegBit (Xhc, XHC_USBCMD_OFFSET, XHC_USBCMD_RUN); + Status = XhcPeiWaitOpRegBit (Xhc, XHC_USBSTS_OFFSET, XHC_USBSTS_HALT, FALSE, Timeout); + DEBUG ((EFI_D_INFO, "XhcPeiRunHC: %r\n", Status)); + return Status; +} + +/** + Submits control transfer to a target USB device. + + @param PeiServices The pointer of EFI_PEI_SERVICES. + @param This The pointer of PEI_USB2_HOST_CONTROLLER_PPI. + @param DeviceAddress The target device address. + @param DeviceSpeed Target device speed. + @param MaximumPacketLength Maximum packet size the default control transfer + endpoint is capable of sending or receiving. + @param Request USB device request to send. + @param TransferDirection Specifies the data direction for the data stage. + @param Data Data buffer to be transmitted or received from USB device. + @param DataLength The size (in bytes) of the data buffer. + @param TimeOut Indicates the maximum timeout, in millisecond. + If Timeout is 0, then the caller must wait for the function + to be completed until EFI_SUCCESS or EFI_DEVICE_ERROR is returned. + @param Translator Transaction translator to be used by this device. + @param TransferResult Return the result of this control transfer. + + @retval EFI_SUCCESS Transfer was completed successfully. + @retval EFI_OUT_OF_RESOURCES The transfer failed due to lack of resources. + @retval EFI_INVALID_PARAMETER Some parameters are invalid. + @retval EFI_TIMEOUT Transfer failed due to timeout. + @retval EFI_DEVICE_ERROR Transfer failed due to host controller or device error. + +**/ +EFI_STATUS +EFIAPI +XhcPeiControlTransfer ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB2_HOST_CONTROLLER_PPI *This, + IN UINT8 DeviceAddress, + IN UINT8 DeviceSpeed, + IN UINTN MaximumPacketLength, + IN EFI_USB_DEVICE_REQUEST *Request, + IN EFI_USB_DATA_DIRECTION TransferDirection, + IN OUT VOID *Data, + IN OUT UINTN *DataLength, + IN UINTN TimeOut, + IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator, + OUT UINT32 *TransferResult + ) +{ + PEI_XHC_DEV *Xhc; + URB *Urb; + UINT8 Endpoint; + UINT8 Index; + UINT8 DescriptorType; + UINT8 SlotId; + UINT8 TTT; + UINT8 MTT; + UINT32 MaxPacket0; + EFI_USB_HUB_DESCRIPTOR *HubDesc; + EFI_STATUS Status; + EFI_STATUS RecoveryStatus; + UINTN MapSize; + EFI_USB_PORT_STATUS PortStatus; + UINT32 State; + EFI_USB_DEVICE_REQUEST ClearPortRequest; + UINTN Len; + + // + // Validate parameters + // + if ((Request == NULL) || (TransferResult == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if ((TransferDirection != EfiUsbDataIn) && + (TransferDirection != EfiUsbDataOut) && + (TransferDirection != EfiUsbNoData)) { + return EFI_INVALID_PARAMETER; + } + + if ((TransferDirection == EfiUsbNoData) && + ((Data != NULL) || (*DataLength != 0))) { + return EFI_INVALID_PARAMETER; + } + + if ((TransferDirection != EfiUsbNoData) && + ((Data == NULL) || (*DataLength == 0))) { + return EFI_INVALID_PARAMETER; + } + + if ((MaximumPacketLength != 8) && (MaximumPacketLength != 16) && + (MaximumPacketLength != 32) && (MaximumPacketLength != 64) && + (MaximumPacketLength != 512) + ) { + return EFI_INVALID_PARAMETER; + } + + if ((DeviceSpeed == EFI_USB_SPEED_LOW) && (MaximumPacketLength != 8)) { + return EFI_INVALID_PARAMETER; + } + + if ((DeviceSpeed == EFI_USB_SPEED_SUPER) && (MaximumPacketLength != 512)) { + return EFI_INVALID_PARAMETER; + } + + Xhc = PEI_RECOVERY_USB_XHC_DEV_FROM_THIS (This); + + Status = EFI_DEVICE_ERROR; + *TransferResult = EFI_USB_ERR_SYSTEM; + Len = 0; + + if (XhcPeiIsHalt (Xhc) || XhcPeiIsSysError (Xhc)) { + DEBUG ((EFI_D_ERROR, "XhcPeiControlTransfer: HC is halted or has system error\n")); + goto ON_EXIT; + } + + // + // Check if the device is still enabled before every transaction. + // + SlotId = XhcPeiBusDevAddrToSlotId (Xhc, DeviceAddress); + if (SlotId == 0) { + goto ON_EXIT; + } + + // + // Hook the Set_Address request from UsbBus. + // According to XHCI 1.0 spec, the Set_Address request is replaced by XHCI's Address_Device cmd. + // + if ((Request->Request == USB_REQ_SET_ADDRESS) && + (Request->RequestType == USB_REQUEST_TYPE (EfiUsbNoData, USB_REQ_TYPE_STANDARD, USB_TARGET_DEVICE))) { + // + // Reset the BusDevAddr field of all disabled entries in UsbDevContext array firstly. + // This way is used to clean the history to avoid using wrong device address afterwards. + // + for (Index = 0; Index < 255; Index++) { + if (!Xhc->UsbDevContext[Index + 1].Enabled && + (Xhc->UsbDevContext[Index + 1].SlotId == 0) && + (Xhc->UsbDevContext[Index + 1].BusDevAddr == (UINT8) Request->Value)) { + Xhc->UsbDevContext[Index + 1].BusDevAddr = 0; + } + } + + if (Xhc->UsbDevContext[SlotId].XhciDevAddr == 0) { + goto ON_EXIT; + } + // + // The actual device address has been assigned by XHCI during initializing the device slot. + // So we just need establish the mapping relationship between the device address requested from UsbBus + // and the actual device address assigned by XHCI. The following invocations through EFI_USB2_HC_PROTOCOL interface + // can find out the actual device address by it. + // + Xhc->UsbDevContext[SlotId].BusDevAddr = (UINT8) Request->Value; + Status = EFI_SUCCESS; + goto ON_EXIT; + } + + // + // Create a new URB, insert it into the asynchronous + // schedule list, then poll the execution status. + // Note that we encode the direction in address although default control + // endpoint is bidirectional. XhcPeiCreateUrb expects this + // combination of Ep addr and its direction. + // + Endpoint = (UINT8) (0 | ((TransferDirection == EfiUsbDataIn) ? 0x80 : 0)); + Urb = XhcPeiCreateUrb ( + Xhc, + DeviceAddress, + Endpoint, + DeviceSpeed, + MaximumPacketLength, + XHC_CTRL_TRANSFER, + Request, + Data, + *DataLength, + NULL, + NULL + ); + + if (Urb == NULL) { + DEBUG ((EFI_D_ERROR, "XhcPeiControlTransfer: failed to create URB")); + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + Status = XhcPeiExecTransfer (Xhc, FALSE, Urb, TimeOut); + + // + // Get the status from URB. The result is updated in XhcPeiCheckUrbResult + // which is called by XhcPeiExecTransfer + // + *TransferResult = Urb->Result; + *DataLength = Urb->Completed; + + if (*TransferResult == EFI_USB_NOERROR) { + Status = EFI_SUCCESS; + } else if (*TransferResult == EFI_USB_ERR_STALL) { + RecoveryStatus = XhcPeiRecoverHaltedEndpoint(Xhc, Urb); + if (EFI_ERROR (RecoveryStatus)) { + DEBUG ((EFI_D_ERROR, "XhcPeiControlTransfer: XhcPeiRecoverHaltedEndpoint failed\n")); + } + Status = EFI_DEVICE_ERROR; + goto FREE_URB; + } else { + goto FREE_URB; + } + + // + // Hook Get_Descriptor request from UsbBus as we need evaluate context and configure endpoint. + // Hook Get_Status request form UsbBus as we need trace device attach/detach event happened at hub. + // Hook Set_Config request from UsbBus as we need configure device endpoint. + // + if ((Request->Request == USB_REQ_GET_DESCRIPTOR) && + ((Request->RequestType == USB_REQUEST_TYPE (EfiUsbDataIn, USB_REQ_TYPE_STANDARD, USB_TARGET_DEVICE)) || + ((Request->RequestType == USB_REQUEST_TYPE (EfiUsbDataIn, USB_REQ_TYPE_CLASS, USB_TARGET_DEVICE))))) { + DescriptorType = (UINT8) (Request->Value >> 8); + if ((DescriptorType == USB_DESC_TYPE_DEVICE) && ((*DataLength == sizeof (EFI_USB_DEVICE_DESCRIPTOR)) || ((DeviceSpeed == EFI_USB_SPEED_FULL) && (*DataLength == 8)))) { + ASSERT (Data != NULL); + // + // Store a copy of device scriptor as hub device need this info to configure endpoint. + // + CopyMem (&Xhc->UsbDevContext[SlotId].DevDesc, Data, *DataLength); + if (Xhc->UsbDevContext[SlotId].DevDesc.BcdUSB == 0x0300) { + // + // If it's a usb3.0 device, then its max packet size is a 2^n. + // + MaxPacket0 = 1 << Xhc->UsbDevContext[SlotId].DevDesc.MaxPacketSize0; + } else { + MaxPacket0 = Xhc->UsbDevContext[SlotId].DevDesc.MaxPacketSize0; + } + Xhc->UsbDevContext[SlotId].ConfDesc = AllocateZeroPool (Xhc->UsbDevContext[SlotId].DevDesc.NumConfigurations * sizeof (EFI_USB_CONFIG_DESCRIPTOR *)); + if (Xhc->UsbDevContext[SlotId].ConfDesc == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto FREE_URB; + } + if (Xhc->HcCParams.Data.Csz == 0) { + Status = XhcPeiEvaluateContext (Xhc, SlotId, MaxPacket0); + } else { + Status = XhcPeiEvaluateContext64 (Xhc, SlotId, MaxPacket0); + } + } else if (DescriptorType == USB_DESC_TYPE_CONFIG) { + ASSERT (Data != NULL); + if (*DataLength == ((UINT16 *) Data)[1]) { + // + // Get configuration value from request, store the configuration descriptor for Configure_Endpoint cmd. + // + Index = (UINT8) Request->Value; + ASSERT (Index < Xhc->UsbDevContext[SlotId].DevDesc.NumConfigurations); + Xhc->UsbDevContext[SlotId].ConfDesc[Index] = AllocateZeroPool (*DataLength); + if (Xhc->UsbDevContext[SlotId].ConfDesc[Index] == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto FREE_URB; + } + CopyMem (Xhc->UsbDevContext[SlotId].ConfDesc[Index], Data, *DataLength); + } + } else if (((DescriptorType == USB_DESC_TYPE_HUB) || + (DescriptorType == USB_DESC_TYPE_HUB_SUPER_SPEED)) && (*DataLength > 2)) { + ASSERT (Data != NULL); + HubDesc = (EFI_USB_HUB_DESCRIPTOR *) Data; + ASSERT (HubDesc->NumPorts <= 15); + // + // The bit 5,6 of HubCharacter field of Hub Descriptor is TTT. + // + TTT = (UINT8) ((HubDesc->HubCharacter & (BIT5 | BIT6)) >> 5); + if (Xhc->UsbDevContext[SlotId].DevDesc.DeviceProtocol == 2) { + // + // Don't support multi-TT feature for super speed hub now. + // + MTT = 0; + DEBUG ((EFI_D_ERROR, "XHCI: Don't support multi-TT feature for Hub now. (force to disable MTT)\n")); + } else { + MTT = 0; + } + + if (Xhc->HcCParams.Data.Csz == 0) { + Status = XhcPeiConfigHubContext (Xhc, SlotId, HubDesc->NumPorts, TTT, MTT); + } else { + Status = XhcPeiConfigHubContext64 (Xhc, SlotId, HubDesc->NumPorts, TTT, MTT); + } + } + } else if ((Request->Request == USB_REQ_SET_CONFIG) && + (Request->RequestType == USB_REQUEST_TYPE (EfiUsbNoData, USB_REQ_TYPE_STANDARD, USB_TARGET_DEVICE))) { + // + // Hook Set_Config request from UsbBus as we need configure device endpoint. + // + for (Index = 0; Index < Xhc->UsbDevContext[SlotId].DevDesc.NumConfigurations; Index++) { + if (Xhc->UsbDevContext[SlotId].ConfDesc[Index]->ConfigurationValue == (UINT8)Request->Value) { + if (Xhc->HcCParams.Data.Csz == 0) { + Status = XhcPeiSetConfigCmd (Xhc, SlotId, DeviceSpeed, Xhc->UsbDevContext[SlotId].ConfDesc[Index]); + } else { + Status = XhcPeiSetConfigCmd64 (Xhc, SlotId, DeviceSpeed, Xhc->UsbDevContext[SlotId].ConfDesc[Index]); + } + break; + } + } + } else if ((Request->Request == USB_REQ_GET_STATUS) && + (Request->RequestType == USB_REQUEST_TYPE (EfiUsbDataIn, USB_REQ_TYPE_CLASS, USB_TARGET_OTHER))) { + ASSERT (Data != NULL); + // + // Hook Get_Status request from UsbBus to keep track of the port status change. + // + State = *(UINT32 *) Data; + PortStatus.PortStatus = 0; + PortStatus.PortChangeStatus = 0; + + if (DeviceSpeed == EFI_USB_SPEED_SUPER) { + // + // For super speed hub, its bit10~12 presents the attached device speed. + // + if ((State & XHC_PORTSC_PS) >> 10 == 0) { + PortStatus.PortStatus |= USB_PORT_STAT_SUPER_SPEED; + } + } else { + // + // For high or full/low speed hub, its bit9~10 presents the attached device speed. + // + if (XHC_BIT_IS_SET (State, BIT9)) { + PortStatus.PortStatus |= USB_PORT_STAT_LOW_SPEED; + } else if (XHC_BIT_IS_SET (State, BIT10)) { + PortStatus.PortStatus |= USB_PORT_STAT_HIGH_SPEED; + } + } + + // + // Convert the XHCI port/port change state to UEFI status + // + MapSize = sizeof (mUsbHubPortStateMap) / sizeof (USB_PORT_STATE_MAP); + for (Index = 0; Index < MapSize; Index++) { + if (XHC_BIT_IS_SET (State, mUsbHubPortStateMap[Index].HwState)) { + PortStatus.PortStatus = (UINT16) (PortStatus.PortStatus | mUsbHubPortStateMap[Index].UefiState); + } + } + + MapSize = sizeof (mUsbHubPortChangeMap) / sizeof (USB_PORT_STATE_MAP); + for (Index = 0; Index < MapSize; Index++) { + if (XHC_BIT_IS_SET (State, mUsbHubPortChangeMap[Index].HwState)) { + PortStatus.PortChangeStatus = (UINT16) (PortStatus.PortChangeStatus | mUsbHubPortChangeMap[Index].UefiState); + } + } + + MapSize = sizeof (mUsbHubClearPortChangeMap) / sizeof (USB_CLEAR_PORT_MAP); + + for (Index = 0; Index < MapSize; Index++) { + if (XHC_BIT_IS_SET (State, mUsbHubClearPortChangeMap[Index].HwState)) { + ZeroMem (&ClearPortRequest, sizeof (EFI_USB_DEVICE_REQUEST)); + ClearPortRequest.RequestType = USB_REQUEST_TYPE (EfiUsbNoData, USB_REQ_TYPE_CLASS, USB_TARGET_OTHER); + ClearPortRequest.Request = (UINT8) USB_REQ_CLEAR_FEATURE; + ClearPortRequest.Value = mUsbHubClearPortChangeMap[Index].Selector; + ClearPortRequest.Index = Request->Index; + ClearPortRequest.Length = 0; + + XhcPeiControlTransfer ( + PeiServices, + This, + DeviceAddress, + DeviceSpeed, + MaximumPacketLength, + &ClearPortRequest, + EfiUsbNoData, + NULL, + &Len, + TimeOut, + Translator, + TransferResult + ); + } + } + + XhcPeiPollPortStatusChange (Xhc, Xhc->UsbDevContext[SlotId].RouteString, (UINT8)Request->Index, &PortStatus); + + *(UINT32 *) Data = *(UINT32 *) &PortStatus; + } + +FREE_URB: + XhcPeiFreeUrb (Xhc, Urb); + +ON_EXIT: + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "XhcPeiControlTransfer: error - %r, transfer - %x\n", Status, *TransferResult)); + } + + return Status; +} + +/** + Submits bulk transfer to a bulk endpoint of a USB device. + + @param PeiServices The pointer of EFI_PEI_SERVICES. + @param This The pointer of PEI_USB2_HOST_CONTROLLER_PPI. + @param DeviceAddress Target device address. + @param EndPointAddress Endpoint number and its direction in bit 7. + @param DeviceSpeed Device speed, Low speed device doesn't support + bulk transfer. + @param MaximumPacketLength Maximum packet size the endpoint is capable of + sending or receiving. + @param Data Array of pointers to the buffers of data to transmit + from or receive into. + @param DataLength The lenght of the data buffer. + @param DataToggle On input, the initial data toggle for the transfer; + On output, it is updated to to next data toggle to use of + the subsequent bulk transfer. + @param TimeOut Indicates the maximum time, in millisecond, which the + transfer is allowed to complete. + If Timeout is 0, then the caller must wait for the function + to be completed until EFI_SUCCESS or EFI_DEVICE_ERROR is returned. + @param Translator A pointr to the transaction translator data. + @param TransferResult A pointer to the detailed result information of the + bulk transfer. + + @retval EFI_SUCCESS The transfer was completed successfully. + @retval EFI_OUT_OF_RESOURCES The transfer failed due to lack of resource. + @retval EFI_INVALID_PARAMETER Parameters are invalid. + @retval EFI_TIMEOUT The transfer failed due to timeout. + @retval EFI_DEVICE_ERROR The transfer failed due to host controller error. + +**/ +EFI_STATUS +EFIAPI +XhcPeiBulkTransfer ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB2_HOST_CONTROLLER_PPI *This, + IN UINT8 DeviceAddress, + IN UINT8 EndPointAddress, + IN UINT8 DeviceSpeed, + IN UINTN MaximumPacketLength, + IN OUT VOID *Data[EFI_USB_MAX_BULK_BUFFER_NUM], + IN OUT UINTN *DataLength, + IN OUT UINT8 *DataToggle, + IN UINTN TimeOut, + IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator, + OUT UINT32 *TransferResult + ) +{ + PEI_XHC_DEV *Xhc; + URB *Urb; + UINT8 SlotId; + EFI_STATUS Status; + EFI_STATUS RecoveryStatus; + + // + // Validate the parameters + // + if ((DataLength == NULL) || (*DataLength == 0) || + (Data == NULL) || (Data[0] == NULL) || (TransferResult == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if ((*DataToggle != 0) && (*DataToggle != 1)) { + return EFI_INVALID_PARAMETER; + } + + if ((DeviceSpeed == EFI_USB_SPEED_LOW) || + ((DeviceSpeed == EFI_USB_SPEED_FULL) && (MaximumPacketLength > 64)) || + ((DeviceSpeed == EFI_USB_SPEED_HIGH) && (MaximumPacketLength > 512)) || + ((DeviceSpeed == EFI_USB_SPEED_SUPER) && (MaximumPacketLength > 1024))) { + return EFI_INVALID_PARAMETER; + } + + Xhc = PEI_RECOVERY_USB_XHC_DEV_FROM_THIS (This); + + *TransferResult = EFI_USB_ERR_SYSTEM; + Status = EFI_DEVICE_ERROR; + + if (XhcPeiIsHalt (Xhc) || XhcPeiIsSysError (Xhc)) { + DEBUG ((EFI_D_ERROR, "XhcPeiBulkTransfer: HC is halted or has system error\n")); + goto ON_EXIT; + } + + // + // Check if the device is still enabled before every transaction. + // + SlotId = XhcPeiBusDevAddrToSlotId (Xhc, DeviceAddress); + if (SlotId == 0) { + goto ON_EXIT; + } + + // + // Create a new URB, insert it into the asynchronous + // schedule list, then poll the execution status. + // + Urb = XhcPeiCreateUrb ( + Xhc, + DeviceAddress, + EndPointAddress, + DeviceSpeed, + MaximumPacketLength, + XHC_BULK_TRANSFER, + NULL, + Data[0], + *DataLength, + NULL, + NULL + ); + + if (Urb == NULL) { + DEBUG ((EFI_D_ERROR, "XhcPeiBulkTransfer: failed to create URB\n")); + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + Status = XhcPeiExecTransfer (Xhc, FALSE, Urb, TimeOut); + + *TransferResult = Urb->Result; + *DataLength = Urb->Completed; + + if (*TransferResult == EFI_USB_NOERROR) { + Status = EFI_SUCCESS; + } else if (*TransferResult == EFI_USB_ERR_STALL) { + RecoveryStatus = XhcPeiRecoverHaltedEndpoint(Xhc, Urb); + if (EFI_ERROR (RecoveryStatus)) { + DEBUG ((EFI_D_ERROR, "XhcPeiBulkTransfer: XhcPeiRecoverHaltedEndpoint failed\n")); + } + Status = EFI_DEVICE_ERROR; + } + + XhcPeiFreeUrb (Xhc, Urb); + +ON_EXIT: + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "XhcPeiBulkTransfer: error - %r, transfer - %x\n", Status, *TransferResult)); + } + + return Status; +} + +/** + Retrieves the number of root hub ports. + + @param[in] PeiServices The pointer to the PEI Services Table. + @param[in] This The pointer to this instance of the + PEI_USB2_HOST_CONTROLLER_PPI. + @param[out] PortNumber The pointer to the number of the root hub ports. + + @retval EFI_SUCCESS The port number was retrieved successfully. + @retval EFI_INVALID_PARAMETER PortNumber is NULL. + +**/ +EFI_STATUS +EFIAPI +XhcPeiGetRootHubPortNumber ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB2_HOST_CONTROLLER_PPI *This, + OUT UINT8 *PortNumber + ) +{ + PEI_XHC_DEV *XhcDev; + XhcDev = PEI_RECOVERY_USB_XHC_DEV_FROM_THIS (This); + + if (PortNumber == NULL) { + return EFI_INVALID_PARAMETER; + } + + *PortNumber = XhcDev->HcSParams1.Data.MaxPorts; + DEBUG ((EFI_D_INFO, "XhcPeiGetRootHubPortNumber: PortNumber = %x\n", *PortNumber)); + return EFI_SUCCESS; +} + +/** + Clears a feature for the specified root hub port. + + @param PeiServices The pointer of EFI_PEI_SERVICES. + @param This The pointer of PEI_USB2_HOST_CONTROLLER_PPI. + @param PortNumber Specifies the root hub port whose feature + is requested to be cleared. + @param PortFeature Indicates the feature selector associated with the + feature clear request. + + @retval EFI_SUCCESS The feature specified by PortFeature was cleared + for the USB root hub port specified by PortNumber. + @retval EFI_INVALID_PARAMETER PortNumber is invalid or PortFeature is invalid. + +**/ +EFI_STATUS +EFIAPI +XhcPeiClearRootHubPortFeature ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB2_HOST_CONTROLLER_PPI *This, + IN UINT8 PortNumber, + IN EFI_USB_PORT_FEATURE PortFeature + ) +{ + PEI_XHC_DEV *Xhc; + UINT32 Offset; + UINT32 State; + EFI_STATUS Status; + + Xhc = PEI_RECOVERY_USB_XHC_DEV_FROM_THIS (This); + Status = EFI_SUCCESS; + + if (PortNumber >= Xhc->HcSParams1.Data.MaxPorts) { + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + + Offset = (UINT32) (XHC_PORTSC_OFFSET + (0x10 * PortNumber)); + State = XhcPeiReadOpReg (Xhc, Offset); + DEBUG ((EFI_D_INFO, "XhcPeiClearRootHubPortFeature: Port: %x State: %x\n", PortNumber, State)); + + // + // Mask off the port status change bits, these bits are + // write clean bits + // + State &= ~ (BIT1 | BIT17 | BIT18 | BIT19 | BIT20 | BIT21 | BIT22 | BIT23); + + switch (PortFeature) { + case EfiUsbPortEnable: + // + // Ports may only be enabled by the xHC. Software cannot enable a port by writing a '1' to this flag. + // A port may be disabled by software writing a '1' to this flag. + // + State |= XHC_PORTSC_PED; + State &= ~XHC_PORTSC_RESET; + XhcPeiWriteOpReg (Xhc, Offset, State); + break; + + case EfiUsbPortSuspend: + State |= XHC_PORTSC_LWS; + XhcPeiWriteOpReg (Xhc, Offset, State); + State &= ~XHC_PORTSC_PLS; + XhcPeiWriteOpReg (Xhc, Offset, State); + break; + + case EfiUsbPortReset: + // + // PORTSC_RESET BIT(4) bit is RW1S attribute, which means Write-1-to-set status: + // Register bits indicate status when read, a clear bit may be set by + // writing a '1'. Writing a '0' to RW1S bits has no effect. + // + break; + + case EfiUsbPortPower: + if (Xhc->HcCParams.Data.Ppc) { + // + // Port Power Control supported + // + State &= ~XHC_PORTSC_PP; + XhcPeiWriteOpReg (Xhc, Offset, State); + } + break; + + case EfiUsbPortOwner: + // + // XHCI root hub port don't has the owner bit, ignore the operation + // + break; + + case EfiUsbPortConnectChange: + // + // Clear connect status change + // + State |= XHC_PORTSC_CSC; + XhcPeiWriteOpReg (Xhc, Offset, State); + break; + + case EfiUsbPortEnableChange: + // + // Clear enable status change + // + State |= XHC_PORTSC_PEC; + XhcPeiWriteOpReg (Xhc, Offset, State); + break; + + case EfiUsbPortOverCurrentChange: + // + // Clear PortOverCurrent change + // + State |= XHC_PORTSC_OCC; + XhcPeiWriteOpReg (Xhc, Offset, State); + break; + + case EfiUsbPortResetChange: + // + // Clear Port Reset change + // + State |= XHC_PORTSC_PRC; + XhcPeiWriteOpReg (Xhc, Offset, State); + break; + + case EfiUsbPortSuspendChange: + // + // Not supported or not related operation + // + break; + + default: + Status = EFI_INVALID_PARAMETER; + break; + } + +ON_EXIT: + DEBUG ((EFI_D_INFO, "XhcPeiClearRootHubPortFeature: PortFeature: %x Status = %r\n", PortFeature, Status)); + return Status; +} + +/** + Sets a feature for the specified root hub port. + + @param PeiServices The pointer of EFI_PEI_SERVICES + @param This The pointer of PEI_USB2_HOST_CONTROLLER_PPI + @param PortNumber Root hub port to set. + @param PortFeature Feature to set. + + @retval EFI_SUCCESS The feature specified by PortFeature was set. + @retval EFI_INVALID_PARAMETER PortNumber is invalid or PortFeature is invalid. + @retval EFI_TIMEOUT The time out occurred. + +**/ +EFI_STATUS +EFIAPI +XhcPeiSetRootHubPortFeature ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB2_HOST_CONTROLLER_PPI *This, + IN UINT8 PortNumber, + IN EFI_USB_PORT_FEATURE PortFeature + ) +{ + PEI_XHC_DEV *Xhc; + UINT32 Offset; + UINT32 State; + EFI_STATUS Status; + + Xhc = PEI_RECOVERY_USB_XHC_DEV_FROM_THIS (This); + Status = EFI_SUCCESS; + + if (PortNumber >= Xhc->HcSParams1.Data.MaxPorts) { + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + + Offset = (UINT32) (XHC_PORTSC_OFFSET + (0x10 * PortNumber)); + State = XhcPeiReadOpReg (Xhc, Offset); + DEBUG ((EFI_D_INFO, "XhcPeiSetRootHubPortFeature: Port: %x State: %x\n", PortNumber, State)); + + // + // Mask off the port status change bits, these bits are + // write clean bits + // + State &= ~ (BIT1 | BIT17 | BIT18 | BIT19 | BIT20 | BIT21 | BIT22 | BIT23); + + switch (PortFeature) { + case EfiUsbPortEnable: + // + // Ports may only be enabled by the xHC. Software cannot enable a port by writing a '1' to this flag. + // A port may be disabled by software writing a '1' to this flag. + // + break; + + case EfiUsbPortSuspend: + State |= XHC_PORTSC_LWS; + XhcPeiWriteOpReg (Xhc, Offset, State); + State &= ~XHC_PORTSC_PLS; + State |= (3 << 5) ; + XhcPeiWriteOpReg (Xhc, Offset, State); + break; + + case EfiUsbPortReset: + // + // Make sure Host Controller not halt before reset it + // + if (XhcPeiIsHalt (Xhc)) { + Status = XhcPeiRunHC (Xhc, XHC_GENERIC_TIMEOUT); + if (EFI_ERROR (Status)) { + break; + } + } + + // + // 4.3.1 Resetting a Root Hub Port + // 1) Write the PORTSC register with the Port Reset (PR) bit set to '1'. + // 2) Wait for a successful Port Status Change Event for the port, where the Port Reset Change (PRC) + // bit in the PORTSC field is set to '1'. + // + State |= XHC_PORTSC_RESET; + XhcPeiWriteOpReg (Xhc, Offset, State); + XhcPeiWaitOpRegBit(Xhc, Offset, XHC_PORTSC_PRC, TRUE, XHC_GENERIC_TIMEOUT); + break; + + case EfiUsbPortPower: + if (Xhc->HcCParams.Data.Ppc) { + // + // Port Power Control supported + // + State |= XHC_PORTSC_PP; + XhcPeiWriteOpReg (Xhc, Offset, State); + } + break; + + case EfiUsbPortOwner: + // + // XHCI root hub port don't has the owner bit, ignore the operation + // + break; + + default: + Status = EFI_INVALID_PARAMETER; + } + +ON_EXIT: + DEBUG ((EFI_D_INFO, "XhcPeiSetRootHubPortFeature: PortFeature: %x Status = %r\n", PortFeature, Status)); + return Status; +} + +/** + Retrieves the current status of a USB root hub port. + + @param PeiServices The pointer of EFI_PEI_SERVICES. + @param This The pointer of PEI_USB2_HOST_CONTROLLER_PPI. + @param PortNumber The root hub port to retrieve the state from. + @param PortStatus Variable to receive the port state. + + @retval EFI_SUCCESS The status of the USB root hub port specified. + by PortNumber was returned in PortStatus. + @retval EFI_INVALID_PARAMETER PortNumber is invalid. + +**/ +EFI_STATUS +EFIAPI +XhcPeiGetRootHubPortStatus ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB2_HOST_CONTROLLER_PPI *This, + IN UINT8 PortNumber, + OUT EFI_USB_PORT_STATUS *PortStatus + ) +{ + PEI_XHC_DEV *Xhc; + UINT32 Offset; + UINT32 State; + UINTN Index; + UINTN MapSize; + USB_DEV_ROUTE ParentRouteChart; + + if (PortStatus == NULL) { + return EFI_INVALID_PARAMETER; + } + + Xhc = PEI_RECOVERY_USB_XHC_DEV_FROM_THIS (This); + + if (PortNumber >= Xhc->HcSParams1.Data.MaxPorts) { + return EFI_INVALID_PARAMETER; + } + + // + // Clear port status. + // + PortStatus->PortStatus = 0; + PortStatus->PortChangeStatus = 0; + + Offset = (UINT32) (XHC_PORTSC_OFFSET + (0x10 * PortNumber)); + State = XhcPeiReadOpReg (Xhc, Offset); + DEBUG ((EFI_D_INFO, "XhcPeiGetRootHubPortStatus: Port: %x State: %x\n", PortNumber, State)); + + // + // According to XHCI 1.0 spec, bit 10~13 of the root port status register identifies the speed of the attached device. + // + switch ((State & XHC_PORTSC_PS) >> 10) { + case 2: + PortStatus->PortStatus |= USB_PORT_STAT_LOW_SPEED; + break; + + case 3: + PortStatus->PortStatus |= USB_PORT_STAT_HIGH_SPEED; + break; + + case 4: + PortStatus->PortStatus |= USB_PORT_STAT_SUPER_SPEED; + break; + + default: + break; + } + + // + // Convert the XHCI port/port change state to UEFI status + // + MapSize = sizeof (mUsbPortStateMap) / sizeof (USB_PORT_STATE_MAP); + + for (Index = 0; Index < MapSize; Index++) { + if (XHC_BIT_IS_SET (State, mUsbPortStateMap[Index].HwState)) { + PortStatus->PortStatus = (UINT16) (PortStatus->PortStatus | mUsbPortStateMap[Index].UefiState); + } + } + // + // Bit5~8 reflects its current link state. + // + if ((State & XHC_PORTSC_PLS) >> 5 == 3) { + PortStatus->PortStatus |= USB_PORT_STAT_SUSPEND; + } + + MapSize = sizeof (mUsbPortChangeMap) / sizeof (USB_PORT_STATE_MAP); + + for (Index = 0; Index < MapSize; Index++) { + if (XHC_BIT_IS_SET (State, mUsbPortChangeMap[Index].HwState)) { + PortStatus->PortChangeStatus = (UINT16) (PortStatus->PortChangeStatus | mUsbPortChangeMap[Index].UefiState); + } + } + + MapSize = sizeof (mUsbClearPortChangeMap) / sizeof (USB_CLEAR_PORT_MAP); + + for (Index = 0; Index < MapSize; Index++) { + if (XHC_BIT_IS_SET (State, mUsbClearPortChangeMap[Index].HwState)) { + XhcPeiClearRootHubPortFeature (PeiServices, This, PortNumber, (EFI_USB_PORT_FEATURE)mUsbClearPortChangeMap[Index].Selector); + } + } + + // + // Poll the root port status register to enable/disable corresponding device slot if there is a device attached/detached. + // For those devices behind hub, we get its attach/detach event by hooking Get_Port_Status request at control transfer for those hub. + // + ParentRouteChart.Dword = 0; + XhcPeiPollPortStatusChange (Xhc, ParentRouteChart, PortNumber, PortStatus); + + DEBUG ((EFI_D_INFO, "XhcPeiGetRootHubPortStatus: PortChangeStatus: %x PortStatus: %x\n", PortStatus->PortChangeStatus, PortStatus->PortStatus)); + return EFI_SUCCESS; +} + +/** + @param FileHandle Handle of the file being invoked. + @param PeiServices Describes the list of possible PEI Services. + + @retval EFI_SUCCESS PPI successfully installed. + +**/ +EFI_STATUS +EFIAPI +XhcPeimEntry ( + IN EFI_PEI_FILE_HANDLE FileHandle, + IN CONST EFI_PEI_SERVICES **PeiServices + ) +{ + PEI_USB_CONTROLLER_PPI *UsbControllerPpi; + EFI_STATUS Status; + UINT8 Index; + UINTN ControllerType; + UINTN BaseAddress; + UINTN MemPages; + PEI_XHC_DEV *XhcDev; + EFI_PHYSICAL_ADDRESS TempPtr; + UINT32 PageSize; + + // + // Shadow this PEIM to run from memory. + // + if (!EFI_ERROR (PeiServicesRegisterForShadow (FileHandle))) { + return EFI_SUCCESS; + } + + Status = PeiServicesLocatePpi ( + &gPeiUsbControllerPpiGuid, + 0, + NULL, + (VOID **) &UsbControllerPpi + ); + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + Index = 0; + while (TRUE) { + Status = UsbControllerPpi->GetUsbController ( + (EFI_PEI_SERVICES **) PeiServices, + UsbControllerPpi, + Index, + &ControllerType, + &BaseAddress + ); + // + // When status is error, it means no controller is found. + // + if (EFI_ERROR (Status)) { + break; + } + + // + // This PEIM is for XHC type controller. + // + if (ControllerType != PEI_XHCI_CONTROLLER) { + Index++; + continue; + } + + MemPages = EFI_SIZE_TO_PAGES (sizeof (PEI_XHC_DEV)); + Status = PeiServicesAllocatePages ( + EfiBootServicesData, + MemPages, + &TempPtr + ); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + ZeroMem ((VOID *) (UINTN) TempPtr, EFI_PAGES_TO_SIZE (MemPages)); + XhcDev = (PEI_XHC_DEV *) ((UINTN) TempPtr); + + XhcDev->Signature = USB_XHC_DEV_SIGNATURE; + XhcDev->UsbHostControllerBaseAddress = (UINT32) BaseAddress; + XhcDev->CapLength = XhcPeiReadCapRegister (XhcDev, XHC_CAPLENGTH_OFFSET) & 0x0FF; + XhcDev->HcSParams1.Dword = XhcPeiReadCapRegister (XhcDev, XHC_HCSPARAMS1_OFFSET); + XhcDev->HcSParams2.Dword = XhcPeiReadCapRegister (XhcDev, XHC_HCSPARAMS2_OFFSET); + XhcDev->HcCParams.Dword = XhcPeiReadCapRegister (XhcDev, XHC_HCCPARAMS_OFFSET); + XhcDev->DBOff = XhcPeiReadCapRegister (XhcDev, XHC_DBOFF_OFFSET); + XhcDev->RTSOff = XhcPeiReadCapRegister (XhcDev, XHC_RTSOFF_OFFSET); + + // + // This PageSize field defines the page size supported by the xHC implementation. + // This xHC supports a page size of 2^(n+12) if bit n is Set. For example, + // if bit 0 is Set, the xHC supports 4k byte page sizes. + // + PageSize = XhcPeiReadOpReg (XhcDev, XHC_PAGESIZE_OFFSET) & XHC_PAGESIZE_MASK; + XhcDev->PageSize = 1 << (HighBitSet32 (PageSize) + 12); + + DEBUG ((EFI_D_INFO, "XhciPei: UsbHostControllerBaseAddress: %x\n", XhcDev->UsbHostControllerBaseAddress)); + DEBUG ((EFI_D_INFO, "XhciPei: CapLength: %x\n", XhcDev->CapLength)); + DEBUG ((EFI_D_INFO, "XhciPei: HcSParams1: %x\n", XhcDev->HcSParams1.Dword)); + DEBUG ((EFI_D_INFO, "XhciPei: HcSParams2: %x\n", XhcDev->HcSParams2.Dword)); + DEBUG ((EFI_D_INFO, "XhciPei: HcCParams: %x\n", XhcDev->HcCParams.Dword)); + DEBUG ((EFI_D_INFO, "XhciPei: DBOff: %x\n", XhcDev->DBOff)); + DEBUG ((EFI_D_INFO, "XhciPei: RTSOff: %x\n", XhcDev->RTSOff)); + DEBUG ((EFI_D_INFO, "XhciPei: PageSize: %x\n", XhcDev->PageSize)); + + XhcPeiResetHC (XhcDev, XHC_RESET_TIMEOUT); + ASSERT (XhcPeiIsHalt (XhcDev)); + + // + // Initialize the schedule + // + XhcPeiInitSched (XhcDev); + + // + // Start the Host Controller + // + XhcPeiRunHC (XhcDev, XHC_GENERIC_TIMEOUT); + + // + // Wait for root port state stable + // + MicroSecondDelay (XHC_ROOT_PORT_STATE_STABLE); + + XhcDev->Usb2HostControllerPpi.ControlTransfer = XhcPeiControlTransfer; + XhcDev->Usb2HostControllerPpi.BulkTransfer = XhcPeiBulkTransfer; + XhcDev->Usb2HostControllerPpi.GetRootHubPortNumber = XhcPeiGetRootHubPortNumber; + XhcDev->Usb2HostControllerPpi.GetRootHubPortStatus = XhcPeiGetRootHubPortStatus; + XhcDev->Usb2HostControllerPpi.SetRootHubPortFeature = XhcPeiSetRootHubPortFeature; + XhcDev->Usb2HostControllerPpi.ClearRootHubPortFeature = XhcPeiClearRootHubPortFeature; + + XhcDev->PpiDescriptor.Flags = (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST); + XhcDev->PpiDescriptor.Guid = &gPeiUsb2HostControllerPpiGuid; + XhcDev->PpiDescriptor.Ppi = &XhcDev->Usb2HostControllerPpi; + + PeiServicesInstallPpi (&XhcDev->PpiDescriptor); + + Index++; + } + + return EFI_SUCCESS; +} + diff --git a/MdeModulePkg/Bus/Pci/XhciPei/XhcPeim.h b/MdeModulePkg/Bus/Pci/XhciPei/XhcPeim.h new file mode 100644 index 0000000000..3b77f2aba7 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/XhciPei/XhcPeim.h @@ -0,0 +1,240 @@ +/** @file +Private Header file for Usb Host Controller PEIM + +Copyright (c) 2014, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions +of the BSD License which accompanies this distribution. The +full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _RECOVERY_XHC_H_ +#define _RECOVERY_XHC_H_ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +typedef struct _PEI_XHC_DEV PEI_XHC_DEV; +typedef struct _USB_DEV_CONTEXT USB_DEV_CONTEXT; + +#include "UsbHcMem.h" +#include "XhciReg.h" +#include "XhciSched.h" + +#define CMD_RING_TRB_NUMBER 0x100 +#define TR_RING_TRB_NUMBER 0x100 +#define ERST_NUMBER 0x01 +#define EVENT_RING_TRB_NUMBER 0x200 + +#define XHC_1_MICROSECOND 1 +#define XHC_1_MILLISECOND (1000 * XHC_1_MICROSECOND) +#define XHC_1_SECOND (1000 * XHC_1_MILLISECOND) + +// +// XHC reset timeout experience values. +// The unit is microsecond, setting it as 1s. +// +#define XHC_RESET_TIMEOUT (1 * XHC_1_SECOND) +// +// XHC delay experience value for polling operation. +// The unit is microsecond, set it as 1ms. +// +#define XHC_POLL_DELAY (1 * XHC_1_MILLISECOND) + +// +// Wait for root port state stable. +// +#define XHC_ROOT_PORT_STATE_STABLE (200 * XHC_1_MILLISECOND) + +#define XHC_GENERIC_TIMEOUT (10 * XHC_1_MILLISECOND) + +#define XHC_LOW_32BIT(Addr64) ((UINT32)(((UINTN)(Addr64)) & 0XFFFFFFFF)) +#define XHC_HIGH_32BIT(Addr64) ((UINT32)(RShiftU64((UINTN)(Addr64), 32) & 0XFFFFFFFF)) +#define XHC_BIT_IS_SET(Data, Bit) ((BOOLEAN)(((Data) & (Bit)) == (Bit))) + +#define XHC_REG_BIT_IS_SET(XHC, Offset, Bit) \ + (XHC_BIT_IS_SET(XhcPeiReadOpReg ((XHC), (Offset)), (Bit))) + +#define USB_DESC_TYPE_HUB 0x29 +#define USB_DESC_TYPE_HUB_SUPER_SPEED 0x2a + +// +// The RequestType in EFI_USB_DEVICE_REQUEST is composed of +// three fields: One bit direction, 2 bit type, and 5 bit +// target. +// +#define USB_REQUEST_TYPE(Dir, Type, Target) \ + ((UINT8)((((Dir) == EfiUsbDataIn ? 0x01 : 0) << 7) | (Type) | (Target))) + +struct _USB_DEV_CONTEXT { + // + // Whether this entry in UsbDevContext array is used or not. + // + BOOLEAN Enabled; + // + // The slot id assigned to the new device through XHCI's Enable_Slot cmd. + // + UINT8 SlotId; + // + // The route string presented an attached usb device. + // + USB_DEV_ROUTE RouteString; + // + // The route string of parent device if it exists. Otherwise it's zero. + // + USB_DEV_ROUTE ParentRouteString; + // + // The actual device address assigned by XHCI through Address_Device command. + // + UINT8 XhciDevAddr; + // + // The requested device address from UsbBus driver through Set_Address standard usb request. + // As XHCI spec replaces this request with Address_Device command, we have to record the + // requested device address and establish a mapping relationship with the actual device address. + // Then UsbBus driver just need to be aware of the requested device address to access usb device + // through EFI_USB2_HC_PROTOCOL. Xhci driver would be responsible for translating it to actual + // device address and access the actual device. + // + UINT8 BusDevAddr; + // + // The pointer to the input device context. + // + VOID *InputContext; + // + // The pointer to the output device context. + // + VOID *OutputContext; + // + // The transfer queue for every endpoint. + // + VOID *EndpointTransferRing[31]; + // + // The device descriptor which is stored to support XHCI's Evaluate_Context cmd. + // + EFI_USB_DEVICE_DESCRIPTOR DevDesc; + // + // As a usb device may include multiple configuration descriptors, we dynamically allocate an array + // to store them. + // Note that every configuration descriptor stored here includes those lower level descriptors, + // such as Interface descriptor, Endpoint descriptor, and so on. + // These information is used to support XHCI's Config_Endpoint cmd. + // + EFI_USB_CONFIG_DESCRIPTOR **ConfDesc; +}; + +#define USB_XHC_DEV_SIGNATURE SIGNATURE_32 ('x', 'h', 'c', 'i') + +struct _PEI_XHC_DEV { + UINTN Signature; + PEI_USB2_HOST_CONTROLLER_PPI Usb2HostControllerPpi; + EFI_PEI_PPI_DESCRIPTOR PpiDescriptor; + UINT32 UsbHostControllerBaseAddress; + USBHC_MEM_POOL *MemPool; + + // + // XHCI configuration data + // + UINT8 CapLength; ///< Capability Register Length + XHC_HCSPARAMS1 HcSParams1; ///< Structural Parameters 1 + XHC_HCSPARAMS2 HcSParams2; ///< Structural Parameters 2 + XHC_HCCPARAMS HcCParams; ///< Capability Parameters + UINT32 DBOff; ///< Doorbell Offset + UINT32 RTSOff; ///< Runtime Register Space Offset + UINT32 PageSize; + UINT32 MaxScratchpadBufs; + UINT64 *ScratchBuf; + UINT64 *ScratchEntry; + UINT64 *DCBAA; + UINT32 MaxSlotsEn; + // + // Cmd Transfer Ring + // + TRANSFER_RING CmdRing; + // + // EventRing + // + EVENT_RING EventRing; + + // + // Store device contexts managed by XHCI device + // The array supports up to 255 devices, entry 0 is reserved and should not be used. + // + USB_DEV_CONTEXT UsbDevContext[256]; +}; + +#define PEI_RECOVERY_USB_XHC_DEV_FROM_THIS(a) CR (a, PEI_XHC_DEV, Usb2HostControllerPpi, USB_XHC_DEV_SIGNATURE) + +/** + Initialize the memory management pool for the host controller. + + @return Pointer to the allocated memory pool or NULL if failed. + +**/ +USBHC_MEM_POOL * +UsbHcInitMemPool ( + VOID + ) +; + +/** + Release the memory management pool. + + @param Pool The USB memory pool to free. + +**/ +VOID +UsbHcFreeMemPool ( + IN USBHC_MEM_POOL *Pool + ) +; + +/** + Allocate some memory from the host controller's memory pool + which can be used to communicate with host controller. + + @param Pool The host controller's memory pool. + @param Size Size of the memory to allocate. + + @return The allocated memory or NULL. + +**/ +VOID * +UsbHcAllocateMem ( + IN USBHC_MEM_POOL *Pool, + IN UINTN Size + ) +; + +/** + Free the allocated memory back to the memory pool. + + @param Pool The memory pool of the host controller. + @param Mem The memory to free. + @param Size The size of the memory to free. + +**/ +VOID +UsbHcFreeMem ( + IN USBHC_MEM_POOL *Pool, + IN VOID *Mem, + IN UINTN Size + ) +; + +#endif diff --git a/MdeModulePkg/Bus/Pci/XhciPei/XhciPei.inf b/MdeModulePkg/Bus/Pci/XhciPei/XhciPei.inf new file mode 100644 index 0000000000..9883f0bc59 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/XhciPei/XhciPei.inf @@ -0,0 +1,59 @@ +## @file +# Component description file for XhcPeim PEIM to produce gPeiUsb2HostControllerPpiGuid +# based on gPeiUsbControllerPpiGuid which is used to enable recovery function from USB Drivers. +# +# Copyright (c) 2014, Intel Corporation. All rights reserved.
+# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions +# of the BSD License which accompanies this distribution. The +# full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = XhciPei + FILE_GUID = 65E5746E-9C14-467d-B5B3-932A66D59F79 + MODULE_TYPE = PEIM + VERSION_STRING = 1.0 + ENTRY_POINT = XhcPeimEntry + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources] + XhcPeim.c + XhcPeim.h + XhciSched.c + UsbHcMem.c + XhciReg.h + XhciSched.h + UsbHcMem.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + IoLib + TimerLib + BaseMemoryLib + PeimEntryPoint + PeiServicesLib + MemoryAllocationLib + +[Ppis] + gPeiUsb2HostControllerPpiGuid ## PRODUCES + gPeiUsbControllerPpiGuid ## CONSUMES + +[Depex] + gEfiPeiMemoryDiscoveredPpiGuid AND gPeiUsbControllerPpiGuid AND gEfiPeiBootInRecoveryModePpiGuid + diff --git a/MdeModulePkg/Bus/Pci/XhciPei/XhciReg.h b/MdeModulePkg/Bus/Pci/XhciPei/XhciReg.h new file mode 100644 index 0000000000..1a62560665 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/XhciPei/XhciReg.h @@ -0,0 +1,471 @@ +/** @file +Private Header file for Usb Host Controller PEIM + +Copyright (c) 2014, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions +of the BSD License which accompanies this distribution. The +full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _EFI_PEI_XHCI_REG_H_ +#define _EFI_PEI_XHCI_REG_H_ + +// +// Capability registers offset +// +#define XHC_CAPLENGTH_OFFSET 0x00 // Capability register length offset +#define XHC_HCIVERSION_OFFSET 0x02 // Interface Version Number 02-03h +#define XHC_HCSPARAMS1_OFFSET 0x04 // Structural Parameters 1 +#define XHC_HCSPARAMS2_OFFSET 0x08 // Structural Parameters 2 +#define XHC_HCSPARAMS3_OFFSET 0x0c // Structural Parameters 3 +#define XHC_HCCPARAMS_OFFSET 0x10 // Capability Parameters +#define XHC_DBOFF_OFFSET 0x14 // Doorbell Offset +#define XHC_RTSOFF_OFFSET 0x18 // Runtime Register Space Offset + +// +// Operational registers offset +// +#define XHC_USBCMD_OFFSET 0x0000 // USB Command Register Offset +#define XHC_USBSTS_OFFSET 0x0004 // USB Status Register Offset +#define XHC_PAGESIZE_OFFSET 0x0008 // USB Page Size Register Offset +#define XHC_DNCTRL_OFFSET 0x0014 // Device Notification Control Register Offset +#define XHC_CRCR_OFFSET 0x0018 // Command Ring Control Register Offset +#define XHC_DCBAAP_OFFSET 0x0030 // Device Context Base Address Array Pointer Register Offset +#define XHC_CONFIG_OFFSET 0x0038 // Configure Register Offset +#define XHC_PORTSC_OFFSET 0x0400 // Port Status and Control Register Offset + +// +// Runtime registers offset +// +#define XHC_MFINDEX_OFFSET 0x00 // Microframe Index Register Offset +#define XHC_IMAN_OFFSET 0x20 // Interrupter X Management Register Offset +#define XHC_IMOD_OFFSET 0x24 // Interrupter X Moderation Register Offset +#define XHC_ERSTSZ_OFFSET 0x28 // Event Ring Segment Table Size Register Offset +#define XHC_ERSTBA_OFFSET 0x30 // Event Ring Segment Table Base Address Register Offset +#define XHC_ERDP_OFFSET 0x38 // Event Ring Dequeue Pointer Register Offset + +// +// Register Bit Definition +// +#define XHC_USBCMD_RUN BIT0 // Run/Stop +#define XHC_USBCMD_RESET BIT1 // Host Controller Reset +#define XHC_USBCMD_INTE BIT2 // Interrupter Enable +#define XHC_USBCMD_HSEE BIT3 // Host System Error Enable + +#define XHC_USBSTS_HALT BIT0 // Host Controller Halted +#define XHC_USBSTS_HSE BIT2 // Host System Error +#define XHC_USBSTS_EINT BIT3 // Event Interrupt +#define XHC_USBSTS_PCD BIT4 // Port Change Detect +#define XHC_USBSTS_SSS BIT8 // Save State Status +#define XHC_USBSTS_RSS BIT9 // Restore State Status +#define XHC_USBSTS_SRE BIT10 // Save/Restore Error +#define XHC_USBSTS_CNR BIT11 // Host Controller Not Ready +#define XHC_USBSTS_HCE BIT12 // Host Controller Error + +#define XHC_PAGESIZE_MASK 0xFFFF // Page Size + +#define XHC_CRCR_RCS BIT0 // Ring Cycle State +#define XHC_CRCR_CS BIT1 // Command Stop +#define XHC_CRCR_CA BIT2 // Command Abort +#define XHC_CRCR_CRR BIT3 // Command Ring Running + +#define XHC_CONFIG_MASK 0xFF // Max Device Slots Enabled + +#define XHC_PORTSC_CCS BIT0 // Current Connect Status +#define XHC_PORTSC_PED BIT1 // Port Enabled/Disabled +#define XHC_PORTSC_OCA BIT3 // Over-current Active +#define XHC_PORTSC_RESET BIT4 // Port Reset +#define XHC_PORTSC_PLS (BIT5|BIT6|BIT7|BIT8) // Port Link State +#define XHC_PORTSC_PP BIT9 // Port Power +#define XHC_PORTSC_PS (BIT10|BIT11|BIT12) // Port Speed +#define XHC_PORTSC_LWS BIT16 // Port Link State Write Strobe +#define XHC_PORTSC_CSC BIT17 // Connect Status Change +#define XHC_PORTSC_PEC BIT18 // Port Enabled/Disabled Change +#define XHC_PORTSC_WRC BIT19 // Warm Port Reset Change +#define XHC_PORTSC_OCC BIT20 // Over-Current Change +#define XHC_PORTSC_PRC BIT21 // Port Reset Change +#define XHC_PORTSC_PLC BIT22 // Port Link State Change +#define XHC_PORTSC_CEC BIT23 // Port Config Error Change +#define XHC_PORTSC_CAS BIT24 // Cold Attach Status + +#define XHC_HUB_PORTSC_CCS BIT0 // Hub's Current Connect Status +#define XHC_HUB_PORTSC_PED BIT1 // Hub's Port Enabled/Disabled +#define XHC_HUB_PORTSC_OCA BIT3 // Hub's Over-current Active +#define XHC_HUB_PORTSC_RESET BIT4 // Hub's Port Reset +#define XHC_HUB_PORTSC_PP BIT9 // Hub's Port Power +#define XHC_HUB_PORTSC_CSC BIT16 // Hub's Connect Status Change +#define XHC_HUB_PORTSC_PEC BIT17 // Hub's Port Enabled/Disabled Change +#define XHC_HUB_PORTSC_OCC BIT19 // Hub's Over-Current Change +#define XHC_HUB_PORTSC_PRC BIT20 // Hub's Port Reset Change +#define XHC_HUB_PORTSC_BHRC BIT21 // Hub's Port Warm Reset Change + +#define XHC_IMAN_IP BIT0 // Interrupt Pending +#define XHC_IMAN_IE BIT1 // Interrupt Enable + +#define XHC_IMODI_MASK 0x0000FFFF // Interrupt Moderation Interval +#define XHC_IMODC_MASK 0xFFFF0000 // Interrupt Moderation Counter + + +#pragma pack (1) +typedef struct { + UINT8 MaxSlots; // Number of Device Slots + UINT16 MaxIntrs:11; // Number of Interrupters + UINT16 Rsvd:5; + UINT8 MaxPorts; // Number of Ports +} HCSPARAMS1; + +// +// Structural Parameters 1 Register Bitmap Definition +// +typedef union { + UINT32 Dword; + HCSPARAMS1 Data; +} XHC_HCSPARAMS1; + +typedef struct { + UINT32 Ist:4; // Isochronous Scheduling Threshold + UINT32 Erst:4; // Event Ring Segment Table Max + UINT32 Rsvd:13; + UINT32 ScratchBufHi:5; // Max Scratchpad Buffers Hi + UINT32 Spr:1; // Scratchpad Restore + UINT32 ScratchBufLo:5; // Max Scratchpad Buffers Lo +} HCSPARAMS2; + +// +// Structural Parameters 2 Register Bitmap Definition +// +typedef union { + UINT32 Dword; + HCSPARAMS2 Data; +} XHC_HCSPARAMS2; + +typedef struct { + UINT16 Ac64:1; // 64-bit Addressing Capability + UINT16 Bnc:1; // BW Negotiation Capability + UINT16 Csz:1; // Context Size + UINT16 Ppc:1; // Port Power Control + UINT16 Pind:1; // Port Indicators + UINT16 Lhrc:1; // Light HC Reset Capability + UINT16 Ltc:1; // Latency Tolerance Messaging Capability + UINT16 Nss:1; // No Secondary SID Support + UINT16 Pae:1; // Parse All Event Data + UINT16 Rsvd:3; + UINT16 MaxPsaSize:4; // Maximum Primary Stream Array Size + UINT16 ExtCapReg; // xHCI Extended Capabilities Pointer +} HCCPARAMS; + +// +// Capability Parameters Register Bitmap Definition +// +typedef union { + UINT32 Dword; + HCCPARAMS Data; +} XHC_HCCPARAMS; + +#pragma pack () + +// +// XHCi Data and Ctrl Structures +// +#pragma pack(1) +typedef struct { + UINT8 Pi; + UINT8 SubClassCode; + UINT8 BaseCode; +} USB_CLASSC; + +typedef struct { + UINT8 Length; + UINT8 DescType; + UINT8 NumPorts; + UINT16 HubCharacter; + UINT8 PwrOn2PwrGood; + UINT8 HubContrCurrent; + UINT8 Filler[16]; +} EFI_USB_HUB_DESCRIPTOR; +#pragma pack() + +// +// Hub Class Feature Selector for Clear Port Feature Request +// It's the extension of hub class feature selector of USB 2.0 in USB 3.0 Spec. +// For more details, Please refer to USB 3.0 Spec Table 10-7. +// +typedef enum { + Usb3PortBHPortReset = 28, + Usb3PortBHPortResetChange = 29 +} XHC_PORT_FEATURE; + +// +// Structure to map the hardware port states to the +// UEFI's port states. +// +typedef struct { + UINT32 HwState; + UINT16 UefiState; +} USB_PORT_STATE_MAP; + +// +// Structure to map the hardware port states to feature selector for clear port feature request. +// +typedef struct { + UINT32 HwState; + UINT16 Selector; +} USB_CLEAR_PORT_MAP; + +/** + Read XHCI Operation register. + + @param Xhc The XHCI device. + @param Offset The operation register offset. + + @retval the register content read. + +**/ +UINT32 +XhcPeiReadOpReg ( + IN PEI_XHC_DEV *Xhc, + IN UINT32 Offset + ); + +/** + Write the data to the XHCI operation register. + + @param Xhc The XHCI device. + @param Offset The operation register offset. + @param Data The data to write. + +**/ +VOID +XhcPeiWriteOpReg ( + IN PEI_XHC_DEV *Xhc, + IN UINT32 Offset, + IN UINT32 Data + ); + +/** + Set one bit of the operational register while keeping other bits. + + @param Xhc The XHCI device. + @param Offset The offset of the operational register. + @param Bit The bit mask of the register to set. + +**/ +VOID +XhcPeiSetOpRegBit ( + IN PEI_XHC_DEV *Xhc, + IN UINT32 Offset, + IN UINT32 Bit + ); + +/** + Clear one bit of the operational register while keeping other bits. + + @param Xhc The XHCI device. + @param Offset The offset of the operational register. + @param Bit The bit mask of the register to clear. + +**/ +VOID +XhcPeiClearOpRegBit ( + IN PEI_XHC_DEV *Xhc, + IN UINT32 Offset, + IN UINT32 Bit + ); + +/** + Wait the operation register's bit as specified by Bit + to be set (or clear). + + @param Xhc The XHCI device. + @param Offset The offset of the operational 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 microsecond, us). + + @retval EFI_SUCCESS The bit successfully changed by host controller. + @retval EFI_TIMEOUT The time out occurred. + +**/ +EFI_STATUS +XhcPeiWaitOpRegBit ( + IN PEI_XHC_DEV *Xhc, + IN UINT32 Offset, + IN UINT32 Bit, + IN BOOLEAN WaitToSet, + IN UINT32 Timeout + ); + +/** + Read XHCI door bell register. + + @param Xhc The XHCI device. + @param Offset The offset of the door bell register. + + @return The register content read + +**/ +UINT32 +XhcPeiReadDoorBellReg ( + IN PEI_XHC_DEV *Xhc, + IN UINT32 Offset + ); + +/** + Write the data to the XHCI door bell register. + + @param Xhc The XHCI device. + @param Offset The offset of the door bell register. + @param Data The data to write. + +**/ +VOID +XhcPeiWriteDoorBellReg ( + IN PEI_XHC_DEV *Xhc, + IN UINT32 Offset, + IN UINT32 Data + ); + +/** + Read XHCI runtime register. + + @param Xhc The XHCI device. + @param Offset The offset of the runtime register. + + @return The register content read + +**/ +UINT32 +XhcPeiReadRuntimeReg ( + IN PEI_XHC_DEV *Xhc, + IN UINT32 Offset + ); + +/** + Write the data to the XHCI runtime register. + + @param Xhc The XHCI device. + @param Offset The offset of the runtime register. + @param Data The data to write. + +**/ +VOID +XhcPeiWriteRuntimeReg ( + IN PEI_XHC_DEV *Xhc, + IN UINT32 Offset, + IN UINT32 Data + ); + +/** + Set one bit of the runtime register while keeping other bits. + + @param Xhc The XHCI device. + @param Offset The offset of the runtime register. + @param Bit The bit mask of the register to set. + +**/ +VOID +XhcPeiSetRuntimeRegBit ( + IN PEI_XHC_DEV *Xhc, + IN UINT32 Offset, + IN UINT32 Bit + ); + +/** + Clear one bit of the runtime register while keeping other bits. + + @param Xhc The XHCI device. + @param Offset The offset of the runtime register. + @param Bit The bit mask of the register to set. + +**/ +VOID +XhcPeiClearRuntimeRegBit ( + IN PEI_XHC_DEV *Xhc, + IN UINT32 Offset, + IN UINT32 Bit + ); + +/** + Check whether Xhc is halted. + + @param Xhc The XHCI device. + + @retval TRUE The controller is halted. + @retval FALSE The controller isn't halted. + +**/ +BOOLEAN +XhcPeiIsHalt ( + IN PEI_XHC_DEV *Xhc + ); + +/** + Check whether system error occurred. + + @param Xhc The XHCI device. + + @retval TRUE System error happened. + @retval FALSE No system error. + +**/ +BOOLEAN +XhcPeiIsSysError ( + IN PEI_XHC_DEV *Xhc + ); + +/** + Reset the host controller. + + @param Xhc The XHCI device. + @param Timeout Time to wait before abort (in millisecond, ms). + + @retval EFI_TIMEOUT The transfer failed due to time out. + @retval Others Failed to reset the host. + +**/ +EFI_STATUS +XhcPeiResetHC ( + IN PEI_XHC_DEV *Xhc, + IN UINT32 Timeout + ); + +/** + Halt the host controller. + + @param Xhc The XHCI device. + @param Timeout Time to wait before abort. + + @retval EFI_TIMEOUT Failed to halt the controller before Timeout. + @retval EFI_SUCCESS The XHCI is halt. + +**/ +EFI_STATUS +XhcPeiHaltHC ( + IN PEI_XHC_DEV *Xhc, + IN UINT32 Timeout + ); + +/** + Set the XHCI to run. + + @param Xhc The XHCI device. + @param Timeout Time to wait before abort. + + @retval EFI_SUCCESS The XHCI is running. + @retval Others Failed to set the XHCI to run. + +**/ +EFI_STATUS +XhcPeiRunHC ( + IN PEI_XHC_DEV *Xhc, + IN UINT32 Timeout + ); + +#endif diff --git a/MdeModulePkg/Bus/Pci/XhciPei/XhciSched.c b/MdeModulePkg/Bus/Pci/XhciPei/XhciSched.c new file mode 100644 index 0000000000..3ae1511222 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/XhciPei/XhciSched.c @@ -0,0 +1,2761 @@ +/** @file +PEIM to produce gPeiUsb2HostControllerPpiGuid based on gPeiUsbControllerPpiGuid +which is used to enable recovery function from USB Drivers. + +Copyright (c) 2014, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions +of the BSD License which accompanies this distribution. The +full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "XhcPeim.h" + +/** + Create a command transfer TRB to support XHCI command interfaces. + + @param Xhc The XHCI device. + @param CmdTrb The cmd TRB to be executed. + + @return Created URB or NULL. + +**/ +URB* +XhcPeiCreateCmdTrb ( + IN PEI_XHC_DEV *Xhc, + IN TRB_TEMPLATE *CmdTrb + ) +{ + URB *Urb; + + Urb = AllocateZeroPool (sizeof (URB)); + if (Urb == NULL) { + return NULL; + } + + Urb->Signature = XHC_URB_SIG; + + Urb->Ring = &Xhc->CmdRing; + XhcPeiSyncTrsRing (Xhc, Urb->Ring); + Urb->TrbNum = 1; + Urb->TrbStart = Urb->Ring->RingEnqueue; + CopyMem (Urb->TrbStart, CmdTrb, sizeof (TRB_TEMPLATE)); + Urb->TrbStart->CycleBit = Urb->Ring->RingPCS & BIT0; + Urb->TrbEnd = Urb->TrbStart; + + return Urb; +} + +/** + Execute a XHCI cmd TRB pointed by CmdTrb. + + @param Xhc The XHCI device. + @param CmdTrb The cmd TRB to be executed. + @param Timeout Indicates the maximum time, in millisecond, which the + transfer is allowed to complete. + @param EvtTrb The event TRB corresponding to the cmd TRB. + + @retval EFI_SUCCESS The transfer was completed successfully. + @retval EFI_INVALID_PARAMETER Some parameters are invalid. + @retval EFI_TIMEOUT The transfer failed due to timeout. + @retval EFI_DEVICE_ERROR The transfer failed due to host controller error. + +**/ +EFI_STATUS +XhcPeiCmdTransfer ( + IN PEI_XHC_DEV *Xhc, + IN TRB_TEMPLATE *CmdTrb, + IN UINTN Timeout, + OUT TRB_TEMPLATE **EvtTrb + ) +{ + EFI_STATUS Status; + URB *Urb; + + // + // Validate the parameters + // + if ((Xhc == NULL) || (CmdTrb == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Status = EFI_DEVICE_ERROR; + + if (XhcPeiIsHalt (Xhc) || XhcPeiIsSysError (Xhc)) { + DEBUG ((EFI_D_ERROR, "XhcPeiCmdTransfer: HC is halted or has system error\n")); + goto ON_EXIT; + } + + // + // Create a new URB, then poll the execution status. + // + Urb = XhcPeiCreateCmdTrb (Xhc, CmdTrb); + if (Urb == NULL) { + DEBUG ((EFI_D_ERROR, "XhcPeiCmdTransfer: failed to create URB\n")); + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + Status = XhcPeiExecTransfer (Xhc, TRUE, Urb, Timeout); + *EvtTrb = Urb->EvtTrb; + + if (Urb->Result == EFI_USB_NOERROR) { + Status = EFI_SUCCESS; + } + + XhcPeiFreeUrb (Xhc, Urb); + +ON_EXIT: + return Status; +} + +/** + Create a new URB for a new transaction. + + @param Xhc The XHCI device + @param BusAddr The logical device address assigned by UsbBus driver + @param EpAddr Endpoint addrress + @param DevSpeed The device speed + @param MaxPacket The max packet length of the endpoint + @param Type The transaction type + @param Request The standard USB request for control transfer + @param Data The user data to transfer + @param DataLen The length of data buffer + @param Callback The function to call when data is transferred + @param Context The context to the callback + + @return Created URB or NULL + +**/ +URB* +XhcPeiCreateUrb ( + IN PEI_XHC_DEV *Xhc, + IN UINT8 BusAddr, + IN UINT8 EpAddr, + IN UINT8 DevSpeed, + IN UINTN MaxPacket, + IN UINTN Type, + IN EFI_USB_DEVICE_REQUEST *Request, + IN VOID *Data, + IN UINTN DataLen, + IN EFI_ASYNC_USB_TRANSFER_CALLBACK Callback, + IN VOID *Context + ) +{ + USB_ENDPOINT *Ep; + EFI_STATUS Status; + URB *Urb; + + Urb = AllocateZeroPool (sizeof (URB)); + if (Urb == NULL) { + return NULL; + } + + Urb->Signature = XHC_URB_SIG; + + Ep = &Urb->Ep; + Ep->BusAddr = BusAddr; + Ep->EpAddr = (UINT8) (EpAddr & 0x0F); + Ep->Direction = ((EpAddr & 0x80) != 0) ? EfiUsbDataIn : EfiUsbDataOut; + Ep->DevSpeed = DevSpeed; + Ep->MaxPacket = MaxPacket; + Ep->Type = Type; + + Urb->Request = Request; + Urb->Data = Data; + Urb->DataLen = DataLen; + Urb->Callback = Callback; + Urb->Context = Context; + + Status = XhcPeiCreateTransferTrb (Xhc, Urb); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "XhcPeiCreateUrb: XhcPeiCreateTransferTrb Failed, Status = %r\n", Status)); + FreePool (Urb); + Urb = NULL; + } + + return Urb; +} + +/** + Free an allocated URB. + + @param Xhc The XHCI device. + @param Urb The URB to free. + +**/ +VOID +XhcPeiFreeUrb ( + IN PEI_XHC_DEV *Xhc, + IN URB *Urb + ) +{ + if ((Xhc == NULL) || (Urb == NULL)) { + return; + } + + FreePool (Urb); +} + +/** + Create a transfer TRB. + + @param Xhc The XHCI device + @param Urb The urb used to construct the transfer TRB. + + @return Created TRB or NULL + +**/ +EFI_STATUS +XhcPeiCreateTransferTrb ( + IN PEI_XHC_DEV *Xhc, + IN URB *Urb + ) +{ + VOID *OutputContext; + TRANSFER_RING *EPRing; + UINT8 EPType; + UINT8 SlotId; + UINT8 Dci; + TRB *TrbStart; + UINTN TotalLen; + UINTN Len; + UINTN TrbNum; + + SlotId = XhcPeiBusDevAddrToSlotId (Xhc, Urb->Ep.BusAddr); + if (SlotId == 0) { + return EFI_DEVICE_ERROR; + } + + Urb->Finished = FALSE; + Urb->StartDone = FALSE; + Urb->EndDone = FALSE; + Urb->Completed = 0; + Urb->Result = EFI_USB_NOERROR; + + Dci = XhcPeiEndpointToDci (Urb->Ep.EpAddr, (UINT8)(Urb->Ep.Direction)); + EPRing = (TRANSFER_RING *) (UINTN) Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1]; + Urb->Ring = EPRing; + OutputContext = Xhc->UsbDevContext[SlotId].OutputContext; + if (Xhc->HcCParams.Data.Csz == 0) { + EPType = (UINT8) ((DEVICE_CONTEXT *)OutputContext)->EP[Dci-1].EPType; + } else { + EPType = (UINT8) ((DEVICE_CONTEXT_64 *)OutputContext)->EP[Dci-1].EPType; + } + + Urb->DataPhy = Urb->Data; + + // + // Construct the TRB + // + XhcPeiSyncTrsRing (Xhc, EPRing); + Urb->TrbStart = EPRing->RingEnqueue; + switch (EPType) { + case ED_CONTROL_BIDIR: + // + // For control transfer, create SETUP_STAGE_TRB first. + // + TrbStart = (TRB *) (UINTN) EPRing->RingEnqueue; + TrbStart->TrbCtrSetup.bmRequestType = Urb->Request->RequestType; + TrbStart->TrbCtrSetup.bRequest = Urb->Request->Request; + TrbStart->TrbCtrSetup.wValue = Urb->Request->Value; + TrbStart->TrbCtrSetup.wIndex = Urb->Request->Index; + TrbStart->TrbCtrSetup.wLength = Urb->Request->Length; + TrbStart->TrbCtrSetup.Length = 8; + TrbStart->TrbCtrSetup.IntTarget = 0; + TrbStart->TrbCtrSetup.IOC = 1; + TrbStart->TrbCtrSetup.IDT = 1; + TrbStart->TrbCtrSetup.Type = TRB_TYPE_SETUP_STAGE; + if (Urb->Ep.Direction == EfiUsbDataIn) { + TrbStart->TrbCtrSetup.TRT = 3; + } else if (Urb->Ep.Direction == EfiUsbDataOut) { + TrbStart->TrbCtrSetup.TRT = 2; + } else { + TrbStart->TrbCtrSetup.TRT = 0; + } + // + // Update the cycle bit + // + TrbStart->TrbCtrSetup.CycleBit = EPRing->RingPCS & BIT0; + Urb->TrbNum++; + + // + // For control transfer, create DATA_STAGE_TRB. + // + if (Urb->DataLen > 0) { + XhcPeiSyncTrsRing (Xhc, EPRing); + TrbStart = (TRB *) (UINTN) EPRing->RingEnqueue; + TrbStart->TrbCtrData.TRBPtrLo = XHC_LOW_32BIT (Urb->DataPhy); + TrbStart->TrbCtrData.TRBPtrHi = XHC_HIGH_32BIT (Urb->DataPhy); + TrbStart->TrbCtrData.Length = (UINT32) Urb->DataLen; + TrbStart->TrbCtrData.TDSize = 0; + TrbStart->TrbCtrData.IntTarget = 0; + TrbStart->TrbCtrData.ISP = 1; + TrbStart->TrbCtrData.IOC = 1; + TrbStart->TrbCtrData.IDT = 0; + TrbStart->TrbCtrData.CH = 0; + TrbStart->TrbCtrData.Type = TRB_TYPE_DATA_STAGE; + if (Urb->Ep.Direction == EfiUsbDataIn) { + TrbStart->TrbCtrData.DIR = 1; + } else if (Urb->Ep.Direction == EfiUsbDataOut) { + TrbStart->TrbCtrData.DIR = 0; + } else { + TrbStart->TrbCtrData.DIR = 0; + } + // + // Update the cycle bit + // + TrbStart->TrbCtrData.CycleBit = EPRing->RingPCS & BIT0; + Urb->TrbNum++; + } + // + // For control transfer, create STATUS_STAGE_TRB. + // Get the pointer to next TRB for status stage use + // + XhcPeiSyncTrsRing (Xhc, EPRing); + TrbStart = (TRB *) (UINTN) EPRing->RingEnqueue; + TrbStart->TrbCtrStatus.IntTarget = 0; + TrbStart->TrbCtrStatus.IOC = 1; + TrbStart->TrbCtrStatus.CH = 0; + TrbStart->TrbCtrStatus.Type = TRB_TYPE_STATUS_STAGE; + if (Urb->Ep.Direction == EfiUsbDataIn) { + TrbStart->TrbCtrStatus.DIR = 0; + } else if (Urb->Ep.Direction == EfiUsbDataOut) { + TrbStart->TrbCtrStatus.DIR = 1; + } else { + TrbStart->TrbCtrStatus.DIR = 0; + } + // + // Update the cycle bit + // + TrbStart->TrbCtrStatus.CycleBit = EPRing->RingPCS & BIT0; + // + // Update the enqueue pointer + // + XhcPeiSyncTrsRing (Xhc, EPRing); + Urb->TrbNum++; + Urb->TrbEnd = (TRB_TEMPLATE *) (UINTN) TrbStart; + + break; + + case ED_BULK_OUT: + case ED_BULK_IN: + TotalLen = 0; + Len = 0; + TrbNum = 0; + TrbStart = (TRB *) (UINTN) EPRing->RingEnqueue; + while (TotalLen < Urb->DataLen) { + if ((TotalLen + 0x10000) >= Urb->DataLen) { + Len = Urb->DataLen - TotalLen; + } else { + Len = 0x10000; + } + TrbStart = (TRB *)(UINTN)EPRing->RingEnqueue; + TrbStart->TrbNormal.TRBPtrLo = XHC_LOW_32BIT((UINT8 *) Urb->DataPhy + TotalLen); + TrbStart->TrbNormal.TRBPtrHi = XHC_HIGH_32BIT((UINT8 *) Urb->DataPhy + TotalLen); + TrbStart->TrbNormal.Length = (UINT32) Len; + TrbStart->TrbNormal.TDSize = 0; + TrbStart->TrbNormal.IntTarget = 0; + TrbStart->TrbNormal.ISP = 1; + TrbStart->TrbNormal.IOC = 1; + TrbStart->TrbNormal.Type = TRB_TYPE_NORMAL; + // + // Update the cycle bit + // + TrbStart->TrbNormal.CycleBit = EPRing->RingPCS & BIT0; + + XhcPeiSyncTrsRing (Xhc, EPRing); + TrbNum++; + TotalLen += Len; + } + + Urb->TrbNum = TrbNum; + Urb->TrbEnd = (TRB_TEMPLATE *)(UINTN)TrbStart; + break; + + case ED_INTERRUPT_OUT: + case ED_INTERRUPT_IN: + TotalLen = 0; + Len = 0; + TrbNum = 0; + TrbStart = (TRB *) (UINTN) EPRing->RingEnqueue; + while (TotalLen < Urb->DataLen) { + if ((TotalLen + 0x10000) >= Urb->DataLen) { + Len = Urb->DataLen - TotalLen; + } else { + Len = 0x10000; + } + TrbStart = (TRB *)(UINTN)EPRing->RingEnqueue; + TrbStart->TrbNormal.TRBPtrLo = XHC_LOW_32BIT((UINT8 *) Urb->DataPhy + TotalLen); + TrbStart->TrbNormal.TRBPtrHi = XHC_HIGH_32BIT((UINT8 *) Urb->DataPhy + TotalLen); + TrbStart->TrbNormal.Length = (UINT32) Len; + TrbStart->TrbNormal.TDSize = 0; + TrbStart->TrbNormal.IntTarget = 0; + TrbStart->TrbNormal.ISP = 1; + TrbStart->TrbNormal.IOC = 1; + TrbStart->TrbNormal.Type = TRB_TYPE_NORMAL; + // + // Update the cycle bit + // + TrbStart->TrbNormal.CycleBit = EPRing->RingPCS & BIT0; + + XhcPeiSyncTrsRing (Xhc, EPRing); + TrbNum++; + TotalLen += Len; + } + + Urb->TrbNum = TrbNum; + Urb->TrbEnd = (TRB_TEMPLATE *)(UINTN)TrbStart; + break; + + default: + DEBUG ((EFI_D_INFO, "Not supported EPType 0x%x!\n",EPType)); + ASSERT (FALSE); + break; + } + + return EFI_SUCCESS; +} + +/** + System software shall use a Reset Endpoint Command (section 4.11.4.7) to remove the Halted + condition in the xHC. After the successful completion of the Reset Endpoint Command, the Endpoint + Context is transitioned from the Halted to the Stopped state and the Transfer Ring of the endpoint is + reenabled. The next write to the Doorbell of the Endpoint will transition the Endpoint Context from the + Stopped to the Running state. + + @param Xhc The XHCI device. + @param Urb The urb which makes the endpoint halted. + + @retval EFI_SUCCESS The recovery is successful. + @retval Others Failed to recovery halted endpoint. + +**/ +EFI_STATUS +XhcPeiRecoverHaltedEndpoint ( + IN PEI_XHC_DEV *Xhc, + IN URB *Urb + ) +{ + EFI_STATUS Status; + EVT_TRB_COMMAND_COMPLETION *EvtTrb; + CMD_TRB_RESET_ENDPOINT CmdTrbResetED; + CMD_SET_TR_DEQ_POINTER CmdSetTRDeq; + UINT8 Dci; + UINT8 SlotId; + EFI_PHYSICAL_ADDRESS PhyAddr; + + Status = EFI_SUCCESS; + SlotId = XhcPeiBusDevAddrToSlotId (Xhc, Urb->Ep.BusAddr); + if (SlotId == 0) { + return EFI_DEVICE_ERROR; + } + Dci = XhcPeiEndpointToDci (Urb->Ep.EpAddr, (UINT8) (Urb->Ep.Direction)); + + DEBUG ((EFI_D_INFO, "XhcPeiRecoverHaltedEndpoint: Recovery Halted Slot = %x, Dci = %x\n", SlotId, Dci)); + + // + // 1) Send Reset endpoint command to transit from halt to stop state + // + ZeroMem (&CmdTrbResetED, sizeof (CmdTrbResetED)); + CmdTrbResetED.CycleBit = 1; + CmdTrbResetED.Type = TRB_TYPE_RESET_ENDPOINT; + CmdTrbResetED.EDID = Dci; + CmdTrbResetED.SlotId = SlotId; + Status = XhcPeiCmdTransfer ( + Xhc, + (TRB_TEMPLATE *) (UINTN) &CmdTrbResetED, + XHC_GENERIC_TIMEOUT, + (TRB_TEMPLATE **) (UINTN) &EvtTrb + ); + if (EFI_ERROR(Status)) { + DEBUG ((EFI_D_ERROR, "XhcPeiRecoverHaltedEndpoint: Reset Endpoint Failed, Status = %r\n", Status)); + goto Done; + } + + // + // 2) Set dequeue pointer + // + ZeroMem (&CmdSetTRDeq, sizeof (CmdSetTRDeq)); + PhyAddr = UsbHcGetPciAddrForHostAddr (Xhc->MemPool, Urb->Ring->RingEnqueue, sizeof (CMD_SET_TR_DEQ_POINTER)); + CmdSetTRDeq.PtrLo = XHC_LOW_32BIT (PhyAddr) | Urb->Ring->RingPCS; + CmdSetTRDeq.PtrHi = XHC_HIGH_32BIT (PhyAddr); + CmdSetTRDeq.CycleBit = 1; + CmdSetTRDeq.Type = TRB_TYPE_SET_TR_DEQUE; + CmdSetTRDeq.Endpoint = Dci; + CmdSetTRDeq.SlotId = SlotId; + Status = XhcPeiCmdTransfer ( + Xhc, + (TRB_TEMPLATE *) (UINTN) &CmdSetTRDeq, + XHC_GENERIC_TIMEOUT, + (TRB_TEMPLATE **) (UINTN) &EvtTrb + ); + if (EFI_ERROR(Status)) { + DEBUG ((EFI_D_ERROR, "XhcPeiRecoverHaltedEndpoint: Set Dequeue Pointer Failed, Status = %r\n", Status)); + goto Done; + } + + // + // 3) Ring the doorbell to transit from stop to active + // + XhcPeiRingDoorBell (Xhc, SlotId, Dci); + +Done: + return Status; +} + +/** + Check if the Trb is a transaction of the URB. + + @param Trb The TRB to be checked + @param Urb The transfer ring to be checked. + + @retval TRUE It is a transaction of the URB. + @retval FALSE It is not any transaction of the URB. + +**/ +BOOLEAN +XhcPeiIsTransferRingTrb ( + IN TRB_TEMPLATE *Trb, + IN URB *Urb + ) +{ + TRB_TEMPLATE *CheckedTrb; + UINTN Index; + + CheckedTrb = Urb->Ring->RingSeg0; + + ASSERT (Urb->Ring->TrbNumber == CMD_RING_TRB_NUMBER || Urb->Ring->TrbNumber == TR_RING_TRB_NUMBER); + + for (Index = 0; Index < Urb->Ring->TrbNumber; Index++) { + if (Trb == CheckedTrb) { + return TRUE; + } + CheckedTrb++; + } + + return FALSE; +} + +/** + Check the URB's execution result and update the URB's + result accordingly. + + @param Xhc The XHCI device. + @param Urb The URB to check result. + + @return Whether the result of URB transfer is finialized. + +**/ +EFI_STATUS +XhcPeiCheckUrbResult ( + IN PEI_XHC_DEV *Xhc, + IN URB *Urb + ) +{ + EVT_TRB_TRANSFER *EvtTrb; + TRB_TEMPLATE *TRBPtr; + UINTN Index; + UINT8 TRBType; + EFI_STATUS Status; + URB *CheckedUrb; + UINT64 XhcDequeue; + UINT32 High; + UINT32 Low; + EFI_PHYSICAL_ADDRESS PhyAddr; + + ASSERT ((Xhc != NULL) && (Urb != NULL)); + + Status = EFI_SUCCESS; + + if (Urb->Finished) { + goto EXIT; + } + + EvtTrb = NULL; + + if (XhcPeiIsHalt (Xhc) || XhcPeiIsSysError (Xhc)) { + Urb->Result |= EFI_USB_ERR_SYSTEM; + Status = EFI_DEVICE_ERROR; + goto EXIT; + } + + // + // Traverse the event ring to find out all new events from the previous check. + // + XhcPeiSyncEventRing (Xhc, &Xhc->EventRing); + for (Index = 0; Index < Xhc->EventRing.TrbNumber; Index++) { + Status = XhcPeiCheckNewEvent (Xhc, &Xhc->EventRing, ((TRB_TEMPLATE **) &EvtTrb)); + if (Status == EFI_NOT_READY) { + // + // All new events are handled, return directly. + // + goto EXIT; + } + + // + // Only handle COMMAND_COMPLETETION_EVENT and TRANSFER_EVENT. + // + if ((EvtTrb->Type != TRB_TYPE_COMMAND_COMPLT_EVENT) && (EvtTrb->Type != TRB_TYPE_TRANS_EVENT)) { + continue; + } + + // + // Need convert pci device address to host address + // + PhyAddr = (EFI_PHYSICAL_ADDRESS) (EvtTrb->TRBPtrLo | LShiftU64 ((UINT64) EvtTrb->TRBPtrHi, 32)); + TRBPtr = (TRB_TEMPLATE *) (UINTN) UsbHcGetHostAddrForPciAddr (Xhc->MemPool, (VOID *) (UINTN) PhyAddr, sizeof (TRB_TEMPLATE)); + + // + // Update the status of Urb according to the finished event regardless of whether + // the urb is current checked one or in the XHCI's async transfer list. + // This way is used to avoid that those completed async transfer events don't get + // handled in time and are flushed by newer coming events. + // + if (XhcPeiIsTransferRingTrb (TRBPtr, Urb)) { + CheckedUrb = Urb; + } else { + continue; + } + + switch (EvtTrb->Completecode) { + case TRB_COMPLETION_STALL_ERROR: + CheckedUrb->Result |= EFI_USB_ERR_STALL; + CheckedUrb->Finished = TRUE; + DEBUG ((EFI_D_ERROR, "XhcPeiCheckUrbResult: STALL_ERROR! Completecode = %x\n", EvtTrb->Completecode)); + goto EXIT; + + case TRB_COMPLETION_BABBLE_ERROR: + CheckedUrb->Result |= EFI_USB_ERR_BABBLE; + CheckedUrb->Finished = TRUE; + DEBUG ((EFI_D_ERROR, "XhcPeiCheckUrbResult: BABBLE_ERROR! Completecode = %x\n", EvtTrb->Completecode)); + goto EXIT; + + case TRB_COMPLETION_DATA_BUFFER_ERROR: + CheckedUrb->Result |= EFI_USB_ERR_BUFFER; + CheckedUrb->Finished = TRUE; + DEBUG ((EFI_D_ERROR, "XhcPeiCheckUrbResult: ERR_BUFFER! Completecode = %x\n", EvtTrb->Completecode)); + goto EXIT; + + case TRB_COMPLETION_USB_TRANSACTION_ERROR: + CheckedUrb->Result |= EFI_USB_ERR_TIMEOUT; + CheckedUrb->Finished = TRUE; + DEBUG ((EFI_D_ERROR, "XhcPeiCheckUrbResult: TRANSACTION_ERROR! Completecode = %x\n", EvtTrb->Completecode)); + goto EXIT; + + case TRB_COMPLETION_SHORT_PACKET: + case TRB_COMPLETION_SUCCESS: + if (EvtTrb->Completecode == TRB_COMPLETION_SHORT_PACKET) { + DEBUG ((EFI_D_ERROR, "XhcPeiCheckUrbResult: short packet happens!\n")); + } + + TRBType = (UINT8) (TRBPtr->Type); + if ((TRBType == TRB_TYPE_DATA_STAGE) || + (TRBType == TRB_TYPE_NORMAL) || + (TRBType == TRB_TYPE_ISOCH)) { + CheckedUrb->Completed += (CheckedUrb->DataLen - EvtTrb->Length); + } + + break; + + default: + DEBUG ((EFI_D_ERROR, "XhcPeiCheckUrbResult: Transfer Default Error Occur! Completecode = 0x%x!\n", EvtTrb->Completecode)); + CheckedUrb->Result |= EFI_USB_ERR_TIMEOUT; + CheckedUrb->Finished = TRUE; + goto EXIT; + } + + // + // Only check first and end Trb event address + // + if (TRBPtr == CheckedUrb->TrbStart) { + CheckedUrb->StartDone = TRUE; + } + + if (TRBPtr == CheckedUrb->TrbEnd) { + CheckedUrb->EndDone = TRUE; + } + + if (CheckedUrb->StartDone && CheckedUrb->EndDone) { + CheckedUrb->Finished = TRUE; + CheckedUrb->EvtTrb = (TRB_TEMPLATE *) EvtTrb; + } + } + +EXIT: + + // + // Advance event ring to last available entry + // + // Some 3rd party XHCI external cards don't support single 64-bytes width register access, + // So divide it to two 32-bytes width register access. + // + Low = XhcPeiReadRuntimeReg (Xhc, XHC_ERDP_OFFSET); + High = XhcPeiReadRuntimeReg (Xhc, XHC_ERDP_OFFSET + 4); + XhcDequeue = (UINT64) (LShiftU64((UINT64) High, 32) | Low); + + PhyAddr = UsbHcGetPciAddrForHostAddr (Xhc->MemPool, Xhc->EventRing.EventRingDequeue, sizeof (TRB_TEMPLATE)); + + if ((XhcDequeue & (~0x0F)) != (PhyAddr & (~0x0F))) { + // + // Some 3rd party XHCI external cards don't support single 64-bytes width register access, + // So divide it to two 32-bytes width register access. + // + XhcPeiWriteRuntimeReg (Xhc, XHC_ERDP_OFFSET, XHC_LOW_32BIT (PhyAddr) | BIT3); + XhcPeiWriteRuntimeReg (Xhc, XHC_ERDP_OFFSET + 4, XHC_HIGH_32BIT (PhyAddr)); + } + + return Status; +} + +/** + Execute the transfer by polling the URB. This is a synchronous operation. + + @param Xhc The XHCI device. + @param CmdTransfer The executed URB is for cmd transfer or not. + @param Urb The URB to execute. + @param Timeout The time to wait before abort, in millisecond. + + @return EFI_DEVICE_ERROR The transfer failed due to transfer error. + @return EFI_TIMEOUT The transfer failed due to time out. + @return EFI_SUCCESS The transfer finished OK. + +**/ +EFI_STATUS +XhcPeiExecTransfer ( + IN PEI_XHC_DEV *Xhc, + IN BOOLEAN CmdTransfer, + IN URB *Urb, + IN UINTN Timeout + ) +{ + EFI_STATUS Status; + UINTN Index; + UINTN Loop; + UINT8 SlotId; + UINT8 Dci; + + if (CmdTransfer) { + SlotId = 0; + Dci = 0; + } else { + SlotId = XhcPeiBusDevAddrToSlotId (Xhc, Urb->Ep.BusAddr); + if (SlotId == 0) { + return EFI_DEVICE_ERROR; + } + Dci = XhcPeiEndpointToDci (Urb->Ep.EpAddr, (UINT8)(Urb->Ep.Direction)); + } + + Status = EFI_SUCCESS; + Loop = Timeout * XHC_1_MILLISECOND; + if (Timeout == 0) { + Loop = 0xFFFFFFFF; + } + + XhcPeiRingDoorBell (Xhc, SlotId, Dci); + + for (Index = 0; Index < Loop; Index++) { + Status = XhcPeiCheckUrbResult (Xhc, Urb); + if (Urb->Finished) { + break; + } + MicroSecondDelay (XHC_1_MICROSECOND); + } + + if (Index == Loop) { + Urb->Result = EFI_USB_ERR_TIMEOUT; + } + + return Status; +} + +/** + Monitor the port status change. Enable/Disable device slot if there is a device attached/detached. + + @param Xhc The XHCI device. + @param ParentRouteChart The route string pointed to the parent device if it exists. + @param Port The port to be polled. + @param PortState The port state. + + @retval EFI_SUCCESS Successfully enable/disable device slot according to port state. + @retval Others Should not appear. + +**/ +EFI_STATUS +XhcPeiPollPortStatusChange ( + IN PEI_XHC_DEV *Xhc, + IN USB_DEV_ROUTE ParentRouteChart, + IN UINT8 Port, + IN EFI_USB_PORT_STATUS *PortState + ) +{ + EFI_STATUS Status; + UINT8 Speed; + UINT8 SlotId; + USB_DEV_ROUTE RouteChart; + + DEBUG ((EFI_D_INFO, "XhcPeiPollPortStatusChange: PortChangeStatus: %x PortStatus: %x\n", PortState->PortChangeStatus, PortState->PortStatus)); + + Status = EFI_SUCCESS; + + if ((PortState->PortChangeStatus & (USB_PORT_STAT_C_CONNECTION | USB_PORT_STAT_C_ENABLE | USB_PORT_STAT_C_OVERCURRENT | USB_PORT_STAT_C_RESET)) == 0) { + return EFI_SUCCESS; + } + + if (ParentRouteChart.Dword == 0) { + RouteChart.Route.RouteString = 0; + RouteChart.Route.RootPortNum = Port + 1; + RouteChart.Route.TierNum = 1; + } else { + if(Port < 14) { + RouteChart.Route.RouteString = ParentRouteChart.Route.RouteString | (Port << (4 * (ParentRouteChart.Route.TierNum - 1))); + } else { + RouteChart.Route.RouteString = ParentRouteChart.Route.RouteString | (15 << (4 * (ParentRouteChart.Route.TierNum - 1))); + } + RouteChart.Route.RootPortNum = ParentRouteChart.Route.RootPortNum; + RouteChart.Route.TierNum = ParentRouteChart.Route.TierNum + 1; + } + + SlotId = XhcPeiRouteStringToSlotId (Xhc, RouteChart); + if (SlotId != 0) { + if (Xhc->HcCParams.Data.Csz == 0) { + Status = XhcPeiDisableSlotCmd (Xhc, SlotId); + } else { + Status = XhcPeiDisableSlotCmd64 (Xhc, SlotId); + } + } + + if (((PortState->PortStatus & USB_PORT_STAT_ENABLE) != 0) && + ((PortState->PortStatus & USB_PORT_STAT_CONNECTION) != 0)) { + // + // Has a device attached, Identify device speed after port is enabled. + // + Speed = EFI_USB_SPEED_FULL; + if ((PortState->PortStatus & USB_PORT_STAT_LOW_SPEED) != 0) { + Speed = EFI_USB_SPEED_LOW; + } else if ((PortState->PortStatus & USB_PORT_STAT_HIGH_SPEED) != 0) { + Speed = EFI_USB_SPEED_HIGH; + } else if ((PortState->PortStatus & USB_PORT_STAT_SUPER_SPEED) != 0) { + Speed = EFI_USB_SPEED_SUPER; + } + // + // Execute Enable_Slot cmd for attached device, initialize device context and assign device address. + // + SlotId = XhcPeiRouteStringToSlotId (Xhc, RouteChart); + if ((SlotId == 0) && ((PortState->PortChangeStatus & USB_PORT_STAT_C_RESET) != 0)) { + if (Xhc->HcCParams.Data.Csz == 0) { + Status = XhcPeiInitializeDeviceSlot (Xhc, ParentRouteChart, Port, RouteChart, Speed); + } else { + Status = XhcPeiInitializeDeviceSlot64 (Xhc, ParentRouteChart, Port, RouteChart, Speed); + } + } + } + + return Status; +} + +/** + Calculate the device context index by endpoint address and direction. + + @param EpAddr The target endpoint number. + @param Direction The direction of the target endpoint. + + @return The device context index of endpoint. + +**/ +UINT8 +XhcPeiEndpointToDci ( + IN UINT8 EpAddr, + IN EFI_USB_DATA_DIRECTION Direction + ) +{ + UINT8 Index; + + ASSERT (EpAddr <= 15); + + if (EpAddr == 0) { + return 1; + } else { + Index = (UINT8) (2 * EpAddr); + if (Direction == EfiUsbDataIn) { + Index += 1; + } + return Index; + } +} + +/** + Find out the actual device address according to the requested device address from UsbBus. + + @param Xhc The XHCI device. + @param BusDevAddr The requested device address by UsbBus upper driver. + + @return The actual device address assigned to the device. + +**/ +UINT8 +XhcPeiBusDevAddrToSlotId ( + IN PEI_XHC_DEV *Xhc, + IN UINT8 BusDevAddr + ) +{ + UINT8 Index; + + for (Index = 0; Index < 255; Index++) { + if (Xhc->UsbDevContext[Index + 1].Enabled && + (Xhc->UsbDevContext[Index + 1].SlotId != 0) && + (Xhc->UsbDevContext[Index + 1].BusDevAddr == BusDevAddr)) { + break; + } + } + + if (Index == 255) { + return 0; + } + + return Xhc->UsbDevContext[Index + 1].SlotId; +} + +/** + Find out the slot id according to the device's route string. + + @param Xhc The XHCI device. + @param RouteString The route string described the device location. + + @return The slot id used by the device. + +**/ +UINT8 +XhcPeiRouteStringToSlotId ( + IN PEI_XHC_DEV *Xhc, + IN USB_DEV_ROUTE RouteString + ) +{ + UINT8 Index; + + for (Index = 0; Index < 255; Index++) { + if (Xhc->UsbDevContext[Index + 1].Enabled && + (Xhc->UsbDevContext[Index + 1].SlotId != 0) && + (Xhc->UsbDevContext[Index + 1].RouteString.Dword == RouteString.Dword)) { + break; + } + } + + if (Index == 255) { + return 0; + } + + return Xhc->UsbDevContext[Index + 1].SlotId; +} + +/** + Ring the door bell to notify XHCI there is a transaction to be executed. + + @param Xhc The XHCI device. + @param SlotId The slot id of the target device. + @param Dci The device context index of the target slot or endpoint. + +**/ +VOID +XhcPeiRingDoorBell ( + IN PEI_XHC_DEV *Xhc, + IN UINT8 SlotId, + IN UINT8 Dci + ) +{ + if (SlotId == 0) { + XhcPeiWriteDoorBellReg (Xhc, 0, 0); + } else { + XhcPeiWriteDoorBellReg (Xhc, SlotId * sizeof (UINT32), Dci); + } +} + +/** + Assign and initialize the device slot for a new device. + + @param Xhc The XHCI device. + @param ParentRouteChart The route string pointed to the parent device. + @param ParentPort The port at which the device is located. + @param RouteChart The route string pointed to the device. + @param DeviceSpeed The device speed. + + @retval EFI_SUCCESS Successfully assign a slot to the device and assign an address to it. + @retval Others Fail to initialize device slot. + +**/ +EFI_STATUS +XhcPeiInitializeDeviceSlot ( + IN PEI_XHC_DEV *Xhc, + IN USB_DEV_ROUTE ParentRouteChart, + IN UINT16 ParentPort, + IN USB_DEV_ROUTE RouteChart, + IN UINT8 DeviceSpeed + ) +{ + EFI_STATUS Status; + EVT_TRB_COMMAND_COMPLETION *EvtTrb; + INPUT_CONTEXT *InputContext; + DEVICE_CONTEXT *OutputContext; + TRANSFER_RING *EndpointTransferRing; + CMD_TRB_ADDRESS_DEVICE CmdTrbAddr; + UINT8 DeviceAddress; + CMD_TRB_ENABLE_SLOT CmdTrb; + UINT8 SlotId; + UINT8 ParentSlotId; + DEVICE_CONTEXT *ParentDeviceContext; + EFI_PHYSICAL_ADDRESS PhyAddr; + + ZeroMem (&CmdTrb, sizeof (CMD_TRB_ENABLE_SLOT)); + CmdTrb.CycleBit = 1; + CmdTrb.Type = TRB_TYPE_EN_SLOT; + + Status = XhcPeiCmdTransfer ( + Xhc, + (TRB_TEMPLATE *) (UINTN) &CmdTrb, + XHC_GENERIC_TIMEOUT, + (TRB_TEMPLATE **) (UINTN) &EvtTrb + ); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "XhcPeiInitializeDeviceSlot: Enable Slot Failed, Status = %r\n", Status)); + return Status; + } + ASSERT (EvtTrb->SlotId <= Xhc->MaxSlotsEn); + DEBUG ((EFI_D_INFO, "XhcPeiInitializeDeviceSlot: Enable Slot Successfully, The Slot ID = 0x%x\n", EvtTrb->SlotId)); + SlotId = (UINT8) EvtTrb->SlotId; + ASSERT (SlotId != 0); + + ZeroMem (&Xhc->UsbDevContext[SlotId], sizeof (USB_DEV_CONTEXT)); + Xhc->UsbDevContext[SlotId].Enabled = TRUE; + Xhc->UsbDevContext[SlotId].SlotId = SlotId; + Xhc->UsbDevContext[SlotId].RouteString.Dword = RouteChart.Dword; + Xhc->UsbDevContext[SlotId].ParentRouteString.Dword = ParentRouteChart.Dword; + + // + // 4.3.3 Device Slot Initialization + // 1) Allocate an Input Context data structure (6.2.5) and initialize all fields to '0'. + // + InputContext = UsbHcAllocateMem (Xhc->MemPool, sizeof (INPUT_CONTEXT)); + ASSERT (InputContext != NULL); + ASSERT (((UINTN) InputContext & 0x3F) == 0); + ZeroMem (InputContext, sizeof (INPUT_CONTEXT)); + + Xhc->UsbDevContext[SlotId].InputContext = (VOID *) InputContext; + + // + // 2) Initialize the Input Control Context (6.2.5.1) of the Input Context by setting the A0 and A1 + // flags to '1'. These flags indicate that the Slot Context and the Endpoint 0 Context of the Input + // Context are affected by the command. + // + InputContext->InputControlContext.Dword2 |= (BIT0 | BIT1); + + // + // 3) Initialize the Input Slot Context data structure + // + InputContext->Slot.RouteString = RouteChart.Route.RouteString; + InputContext->Slot.Speed = DeviceSpeed + 1; + InputContext->Slot.ContextEntries = 1; + InputContext->Slot.RootHubPortNum = RouteChart.Route.RootPortNum; + + if (RouteChart.Route.RouteString != 0) { + // + // The device is behind of hub device. + // + ParentSlotId = XhcPeiRouteStringToSlotId (Xhc, ParentRouteChart); + ASSERT (ParentSlotId != 0); + // + // If the Full/Low device attached to a High Speed Hub, init the TTPortNum and TTHubSlotId field of slot context + // + ParentDeviceContext = (DEVICE_CONTEXT *) Xhc->UsbDevContext[ParentSlotId].OutputContext; + if ((ParentDeviceContext->Slot.TTPortNum == 0) && + (ParentDeviceContext->Slot.TTHubSlotId == 0)) { + if ((ParentDeviceContext->Slot.Speed == (EFI_USB_SPEED_HIGH + 1)) && (DeviceSpeed < EFI_USB_SPEED_HIGH)) { + // + // Full/Low device attached to High speed hub port that isolates the high speed signaling + // environment from Full/Low speed signaling environment for a device + // + InputContext->Slot.TTPortNum = ParentPort; + InputContext->Slot.TTHubSlotId = ParentSlotId; + } + } else { + // + // Inherit the TT parameters from parent device. + // + InputContext->Slot.TTPortNum = ParentDeviceContext->Slot.TTPortNum; + InputContext->Slot.TTHubSlotId = ParentDeviceContext->Slot.TTHubSlotId; + // + // If the device is a High speed device then down the speed to be the same as its parent Hub + // + if (DeviceSpeed == EFI_USB_SPEED_HIGH) { + InputContext->Slot.Speed = ParentDeviceContext->Slot.Speed; + } + } + } + + // + // 4) Allocate and initialize the Transfer Ring for the Default Control Endpoint. + // + EndpointTransferRing = AllocateZeroPool (sizeof (TRANSFER_RING)); + Xhc->UsbDevContext[SlotId].EndpointTransferRing[0] = EndpointTransferRing; + XhcPeiCreateTransferRing (Xhc, TR_RING_TRB_NUMBER, (TRANSFER_RING *) Xhc->UsbDevContext[SlotId].EndpointTransferRing[0]); + // + // 5) Initialize the Input default control Endpoint 0 Context (6.2.3). + // + InputContext->EP[0].EPType = ED_CONTROL_BIDIR; + + if (DeviceSpeed == EFI_USB_SPEED_SUPER) { + InputContext->EP[0].MaxPacketSize = 512; + } else if (DeviceSpeed == EFI_USB_SPEED_HIGH) { + InputContext->EP[0].MaxPacketSize = 64; + } else { + InputContext->EP[0].MaxPacketSize = 8; + } + // + // Initial value of Average TRB Length for Control endpoints would be 8B, Interrupt endpoints + // 1KB, and Bulk and Isoch endpoints 3KB. + // + InputContext->EP[0].AverageTRBLength = 8; + InputContext->EP[0].MaxBurstSize = 0; + InputContext->EP[0].Interval = 0; + InputContext->EP[0].MaxPStreams = 0; + InputContext->EP[0].Mult = 0; + InputContext->EP[0].CErr = 3; + + // + // Init the DCS(dequeue cycle state) as the transfer ring's CCS + // + PhyAddr = UsbHcGetPciAddrForHostAddr ( + Xhc->MemPool, + ((TRANSFER_RING *) (UINTN) Xhc->UsbDevContext[SlotId].EndpointTransferRing[0])->RingSeg0, + sizeof (TRB_TEMPLATE) * TR_RING_TRB_NUMBER + ); + InputContext->EP[0].PtrLo = XHC_LOW_32BIT (PhyAddr) | BIT0; + InputContext->EP[0].PtrHi = XHC_HIGH_32BIT (PhyAddr); + + // + // 6) Allocate the Output Device Context data structure (6.2.1) and initialize it to '0'. + // + OutputContext = UsbHcAllocateMem (Xhc->MemPool, sizeof (DEVICE_CONTEXT)); + ASSERT (OutputContext != NULL); + ASSERT (((UINTN) OutputContext & 0x3F) == 0); + ZeroMem (OutputContext, sizeof (DEVICE_CONTEXT)); + + Xhc->UsbDevContext[SlotId].OutputContext = OutputContext; + // + // 7) Load the appropriate (Device Slot ID) entry in the Device Context Base Address Array (5.4.6) with + // a pointer to the Output Device Context data structure (6.2.1). + // + PhyAddr = UsbHcGetPciAddrForHostAddr (Xhc->MemPool, OutputContext, sizeof (DEVICE_CONTEXT)); + // + // Fill DCBAA with PCI device address + // + Xhc->DCBAA[SlotId] = (UINT64) (UINTN) PhyAddr; + + // + // 8) Issue an Address Device Command for the Device Slot, where the command points to the Input + // Context data structure described above. + // + ZeroMem (&CmdTrbAddr, sizeof (CmdTrbAddr)); + PhyAddr = UsbHcGetPciAddrForHostAddr (Xhc->MemPool, Xhc->UsbDevContext[SlotId].InputContext, sizeof (INPUT_CONTEXT)); + CmdTrbAddr.PtrLo = XHC_LOW_32BIT (PhyAddr); + CmdTrbAddr.PtrHi = XHC_HIGH_32BIT (PhyAddr); + CmdTrbAddr.CycleBit = 1; + CmdTrbAddr.Type = TRB_TYPE_ADDRESS_DEV; + CmdTrbAddr.SlotId = Xhc->UsbDevContext[SlotId].SlotId; + Status = XhcPeiCmdTransfer ( + Xhc, + (TRB_TEMPLATE *) (UINTN) &CmdTrbAddr, + XHC_GENERIC_TIMEOUT, + (TRB_TEMPLATE **) (UINTN) &EvtTrb + ); + if (!EFI_ERROR (Status)) { + DeviceAddress = (UINT8) OutputContext->Slot.DeviceAddress; + DEBUG ((EFI_D_INFO, "XhcPeiInitializeDeviceSlot: Address %d assigned successfully\n", DeviceAddress)); + Xhc->UsbDevContext[SlotId].XhciDevAddr = DeviceAddress; + } + + DEBUG ((EFI_D_INFO, "XhcPeiInitializeDeviceSlot: Enable Slot, Status = %r\n", Status)); + return Status; +} + +/** + Assign and initialize the device slot for a new device. + + @param Xhc The XHCI device. + @param ParentRouteChart The route string pointed to the parent device. + @param ParentPort The port at which the device is located. + @param RouteChart The route string pointed to the device. + @param DeviceSpeed The device speed. + + @retval EFI_SUCCESS Successfully assign a slot to the device and assign an address to it. + @retval Others Fail to initialize device slot. + +**/ +EFI_STATUS +XhcPeiInitializeDeviceSlot64 ( + IN PEI_XHC_DEV *Xhc, + IN USB_DEV_ROUTE ParentRouteChart, + IN UINT16 ParentPort, + IN USB_DEV_ROUTE RouteChart, + IN UINT8 DeviceSpeed + ) +{ + EFI_STATUS Status; + EVT_TRB_COMMAND_COMPLETION *EvtTrb; + INPUT_CONTEXT_64 *InputContext; + DEVICE_CONTEXT_64 *OutputContext; + TRANSFER_RING *EndpointTransferRing; + CMD_TRB_ADDRESS_DEVICE CmdTrbAddr; + UINT8 DeviceAddress; + CMD_TRB_ENABLE_SLOT CmdTrb; + UINT8 SlotId; + UINT8 ParentSlotId; + DEVICE_CONTEXT_64 *ParentDeviceContext; + EFI_PHYSICAL_ADDRESS PhyAddr; + + ZeroMem (&CmdTrb, sizeof (CMD_TRB_ENABLE_SLOT)); + CmdTrb.CycleBit = 1; + CmdTrb.Type = TRB_TYPE_EN_SLOT; + + Status = XhcPeiCmdTransfer ( + Xhc, + (TRB_TEMPLATE *) (UINTN) &CmdTrb, + XHC_GENERIC_TIMEOUT, + (TRB_TEMPLATE **) (UINTN) &EvtTrb + ); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "XhcPeiInitializeDeviceSlot64: Enable Slot Failed, Status = %r\n", Status)); + return Status; + } + ASSERT (EvtTrb->SlotId <= Xhc->MaxSlotsEn); + DEBUG ((EFI_D_INFO, "XhcPeiInitializeDeviceSlot64: Enable Slot Successfully, The Slot ID = 0x%x\n", EvtTrb->SlotId)); + SlotId = (UINT8)EvtTrb->SlotId; + ASSERT (SlotId != 0); + + ZeroMem (&Xhc->UsbDevContext[SlotId], sizeof (USB_DEV_CONTEXT)); + Xhc->UsbDevContext[SlotId].Enabled = TRUE; + Xhc->UsbDevContext[SlotId].SlotId = SlotId; + Xhc->UsbDevContext[SlotId].RouteString.Dword = RouteChart.Dword; + Xhc->UsbDevContext[SlotId].ParentRouteString.Dword = ParentRouteChart.Dword; + + // + // 4.3.3 Device Slot Initialization + // 1) Allocate an Input Context data structure (6.2.5) and initialize all fields to '0'. + // + InputContext = UsbHcAllocateMem (Xhc->MemPool, sizeof (INPUT_CONTEXT_64)); + ASSERT (InputContext != NULL); + ASSERT (((UINTN) InputContext & 0x3F) == 0); + ZeroMem (InputContext, sizeof (INPUT_CONTEXT_64)); + + Xhc->UsbDevContext[SlotId].InputContext = (VOID *) InputContext; + + // + // 2) Initialize the Input Control Context (6.2.5.1) of the Input Context by setting the A0 and A1 + // flags to '1'. These flags indicate that the Slot Context and the Endpoint 0 Context of the Input + // Context are affected by the command. + // + InputContext->InputControlContext.Dword2 |= (BIT0 | BIT1); + + // + // 3) Initialize the Input Slot Context data structure + // + InputContext->Slot.RouteString = RouteChart.Route.RouteString; + InputContext->Slot.Speed = DeviceSpeed + 1; + InputContext->Slot.ContextEntries = 1; + InputContext->Slot.RootHubPortNum = RouteChart.Route.RootPortNum; + + if (RouteChart.Route.RouteString != 0) { + // + // The device is behind of hub device. + // + ParentSlotId = XhcPeiRouteStringToSlotId (Xhc, ParentRouteChart); + ASSERT (ParentSlotId != 0); + // + //if the Full/Low device attached to a High Speed Hub, Init the TTPortNum and TTHubSlotId field of slot context + // + ParentDeviceContext = (DEVICE_CONTEXT_64 *) Xhc->UsbDevContext[ParentSlotId].OutputContext; + if ((ParentDeviceContext->Slot.TTPortNum == 0) && + (ParentDeviceContext->Slot.TTHubSlotId == 0)) { + if ((ParentDeviceContext->Slot.Speed == (EFI_USB_SPEED_HIGH + 1)) && (DeviceSpeed < EFI_USB_SPEED_HIGH)) { + // + // Full/Low device attached to High speed hub port that isolates the high speed signaling + // environment from Full/Low speed signaling environment for a device + // + InputContext->Slot.TTPortNum = ParentPort; + InputContext->Slot.TTHubSlotId = ParentSlotId; + } + } else { + // + // Inherit the TT parameters from parent device. + // + InputContext->Slot.TTPortNum = ParentDeviceContext->Slot.TTPortNum; + InputContext->Slot.TTHubSlotId = ParentDeviceContext->Slot.TTHubSlotId; + // + // If the device is a High speed device then down the speed to be the same as its parent Hub + // + if (DeviceSpeed == EFI_USB_SPEED_HIGH) { + InputContext->Slot.Speed = ParentDeviceContext->Slot.Speed; + } + } + } + + // + // 4) Allocate and initialize the Transfer Ring for the Default Control Endpoint. + // + EndpointTransferRing = AllocateZeroPool (sizeof (TRANSFER_RING)); + Xhc->UsbDevContext[SlotId].EndpointTransferRing[0] = EndpointTransferRing; + XhcPeiCreateTransferRing(Xhc, TR_RING_TRB_NUMBER, (TRANSFER_RING *) Xhc->UsbDevContext[SlotId].EndpointTransferRing[0]); + // + // 5) Initialize the Input default control Endpoint 0 Context (6.2.3). + // + InputContext->EP[0].EPType = ED_CONTROL_BIDIR; + + if (DeviceSpeed == EFI_USB_SPEED_SUPER) { + InputContext->EP[0].MaxPacketSize = 512; + } else if (DeviceSpeed == EFI_USB_SPEED_HIGH) { + InputContext->EP[0].MaxPacketSize = 64; + } else { + InputContext->EP[0].MaxPacketSize = 8; + } + // + // Initial value of Average TRB Length for Control endpoints would be 8B, Interrupt endpoints + // 1KB, and Bulk and Isoch endpoints 3KB. + // + InputContext->EP[0].AverageTRBLength = 8; + InputContext->EP[0].MaxBurstSize = 0; + InputContext->EP[0].Interval = 0; + InputContext->EP[0].MaxPStreams = 0; + InputContext->EP[0].Mult = 0; + InputContext->EP[0].CErr = 3; + + // + // Init the DCS(dequeue cycle state) as the transfer ring's CCS + // + PhyAddr = UsbHcGetPciAddrForHostAddr ( + Xhc->MemPool, + ((TRANSFER_RING *) (UINTN) Xhc->UsbDevContext[SlotId].EndpointTransferRing[0])->RingSeg0, + sizeof (TRB_TEMPLATE) * TR_RING_TRB_NUMBER + ); + InputContext->EP[0].PtrLo = XHC_LOW_32BIT (PhyAddr) | BIT0; + InputContext->EP[0].PtrHi = XHC_HIGH_32BIT (PhyAddr); + + // + // 6) Allocate the Output Device Context data structure (6.2.1) and initialize it to '0'. + // + OutputContext = UsbHcAllocateMem (Xhc->MemPool, sizeof (DEVICE_CONTEXT_64)); + ASSERT (OutputContext != NULL); + ASSERT (((UINTN) OutputContext & 0x3F) == 0); + ZeroMem (OutputContext, sizeof (DEVICE_CONTEXT_64)); + + Xhc->UsbDevContext[SlotId].OutputContext = OutputContext; + // + // 7) Load the appropriate (Device Slot ID) entry in the Device Context Base Address Array (5.4.6) with + // a pointer to the Output Device Context data structure (6.2.1). + // + PhyAddr = UsbHcGetPciAddrForHostAddr (Xhc->MemPool, OutputContext, sizeof (DEVICE_CONTEXT_64)); + // + // Fill DCBAA with PCI device address + // + Xhc->DCBAA[SlotId] = (UINT64) (UINTN) PhyAddr; + + // + // 8) Issue an Address Device Command for the Device Slot, where the command points to the Input + // Context data structure described above. + // + ZeroMem (&CmdTrbAddr, sizeof (CmdTrbAddr)); + PhyAddr = UsbHcGetPciAddrForHostAddr (Xhc->MemPool, Xhc->UsbDevContext[SlotId].InputContext, sizeof (INPUT_CONTEXT_64)); + CmdTrbAddr.PtrLo = XHC_LOW_32BIT (PhyAddr); + CmdTrbAddr.PtrHi = XHC_HIGH_32BIT (PhyAddr); + CmdTrbAddr.CycleBit = 1; + CmdTrbAddr.Type = TRB_TYPE_ADDRESS_DEV; + CmdTrbAddr.SlotId = Xhc->UsbDevContext[SlotId].SlotId; + Status = XhcPeiCmdTransfer ( + Xhc, + (TRB_TEMPLATE *) (UINTN) &CmdTrbAddr, + XHC_GENERIC_TIMEOUT, + (TRB_TEMPLATE **) (UINTN) &EvtTrb + ); + if (!EFI_ERROR (Status)) { + DeviceAddress = (UINT8) OutputContext->Slot.DeviceAddress; + DEBUG ((EFI_D_INFO, "XhcPeiInitializeDeviceSlot64: Address %d assigned successfully\n", DeviceAddress)); + Xhc->UsbDevContext[SlotId].XhciDevAddr = DeviceAddress; + } + + DEBUG ((EFI_D_INFO, "XhcPeiInitializeDeviceSlot64: Enable Slot, Status = %r\n", Status)); + return Status; +} + + +/** + Disable the specified device slot. + + @param Xhc The XHCI device. + @param SlotId The slot id to be disabled. + + @retval EFI_SUCCESS Successfully disable the device slot. + +**/ +EFI_STATUS +XhcPeiDisableSlotCmd ( + IN PEI_XHC_DEV *Xhc, + IN UINT8 SlotId + ) +{ + EFI_STATUS Status; + TRB_TEMPLATE *EvtTrb; + CMD_TRB_DISABLE_SLOT CmdTrbDisSlot; + UINT8 Index; + VOID *RingSeg; + + // + // Disable the device slots occupied by these devices on its downstream ports. + // Entry 0 is reserved. + // + for (Index = 0; Index < 255; Index++) { + if (!Xhc->UsbDevContext[Index + 1].Enabled || + (Xhc->UsbDevContext[Index + 1].SlotId == 0) || + (Xhc->UsbDevContext[Index + 1].ParentRouteString.Dword != Xhc->UsbDevContext[SlotId].RouteString.Dword)) { + continue; + } + + Status = XhcPeiDisableSlotCmd (Xhc, Xhc->UsbDevContext[Index + 1].SlotId); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "XhcPeiDisableSlotCmd: failed to disable child, ignore error\n")); + Xhc->UsbDevContext[Index + 1].SlotId = 0; + } + } + + // + // Construct the disable slot command + // + DEBUG ((EFI_D_INFO, "XhcPeiDisableSlotCmd: Disable device slot %d!\n", SlotId)); + + ZeroMem (&CmdTrbDisSlot, sizeof (CmdTrbDisSlot)); + CmdTrbDisSlot.CycleBit = 1; + CmdTrbDisSlot.Type = TRB_TYPE_DIS_SLOT; + CmdTrbDisSlot.SlotId = SlotId; + Status = XhcPeiCmdTransfer ( + Xhc, + (TRB_TEMPLATE *) (UINTN) &CmdTrbDisSlot, + XHC_GENERIC_TIMEOUT, + (TRB_TEMPLATE **) (UINTN) &EvtTrb + ); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "XhcPeiDisableSlotCmd: Disable Slot Command Failed, Status = %r\n", Status)); + return Status; + } + // + // Free the slot's device context entry + // + Xhc->DCBAA[SlotId] = 0; + + // + // Free the slot related data structure + // + for (Index = 0; Index < 31; Index++) { + if (Xhc->UsbDevContext[SlotId].EndpointTransferRing[Index] != NULL) { + RingSeg = ((TRANSFER_RING *) (UINTN) Xhc->UsbDevContext[SlotId].EndpointTransferRing[Index])->RingSeg0; + if (RingSeg != NULL) { + UsbHcFreeMem (Xhc->MemPool, RingSeg, sizeof (TRB_TEMPLATE) * TR_RING_TRB_NUMBER); + } + FreePool (Xhc->UsbDevContext[SlotId].EndpointTransferRing[Index]); + Xhc->UsbDevContext[SlotId].EndpointTransferRing[Index] = NULL; + } + } + + for (Index = 0; Index < Xhc->UsbDevContext[SlotId].DevDesc.NumConfigurations; Index++) { + if (Xhc->UsbDevContext[SlotId].ConfDesc[Index] != NULL) { + FreePool (Xhc->UsbDevContext[SlotId].ConfDesc[Index]); + } + } + + if (Xhc->UsbDevContext[SlotId].InputContext != NULL) { + UsbHcFreeMem (Xhc->MemPool, Xhc->UsbDevContext[SlotId].InputContext, sizeof (INPUT_CONTEXT)); + } + + if (Xhc->UsbDevContext[SlotId].OutputContext != NULL) { + UsbHcFreeMem (Xhc->MemPool, Xhc->UsbDevContext[SlotId].OutputContext, sizeof (DEVICE_CONTEXT)); + } + // + // Doesn't zero the entry because XhcAsyncInterruptTransfer() may be invoked to remove the established + // asynchronous interrupt pipe after the device is disabled. It needs the device address mapping info to + // remove urb from XHCI's asynchronous transfer list. + // + Xhc->UsbDevContext[SlotId].Enabled = FALSE; + Xhc->UsbDevContext[SlotId].SlotId = 0; + + DEBUG ((EFI_D_INFO, "XhcPeiDisableSlotCmd: Disable Slot Command, Status = %r\n", Status)); + return Status; +} + +/** + Disable the specified device slot. + + @param Xhc The XHCI device. + @param SlotId The slot id to be disabled. + + @retval EFI_SUCCESS Successfully disable the device slot. + +**/ +EFI_STATUS +XhcPeiDisableSlotCmd64 ( + IN PEI_XHC_DEV *Xhc, + IN UINT8 SlotId + ) +{ + EFI_STATUS Status; + TRB_TEMPLATE *EvtTrb; + CMD_TRB_DISABLE_SLOT CmdTrbDisSlot; + UINT8 Index; + VOID *RingSeg; + + // + // Disable the device slots occupied by these devices on its downstream ports. + // Entry 0 is reserved. + // + for (Index = 0; Index < 255; Index++) { + if (!Xhc->UsbDevContext[Index + 1].Enabled || + (Xhc->UsbDevContext[Index + 1].SlotId == 0) || + (Xhc->UsbDevContext[Index + 1].ParentRouteString.Dword != Xhc->UsbDevContext[SlotId].RouteString.Dword)) { + continue; + } + + Status = XhcPeiDisableSlotCmd64 (Xhc, Xhc->UsbDevContext[Index + 1].SlotId); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "XhcPeiDisableSlotCmd64: failed to disable child, ignore error\n")); + Xhc->UsbDevContext[Index + 1].SlotId = 0; + } + } + + // + // Construct the disable slot command + // + DEBUG ((EFI_D_INFO, "XhcPeiDisableSlotCmd64: Disable device slot %d!\n", SlotId)); + + ZeroMem (&CmdTrbDisSlot, sizeof (CmdTrbDisSlot)); + CmdTrbDisSlot.CycleBit = 1; + CmdTrbDisSlot.Type = TRB_TYPE_DIS_SLOT; + CmdTrbDisSlot.SlotId = SlotId; + Status = XhcPeiCmdTransfer ( + Xhc, + (TRB_TEMPLATE *) (UINTN) &CmdTrbDisSlot, + XHC_GENERIC_TIMEOUT, + (TRB_TEMPLATE **) (UINTN) &EvtTrb + ); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "XhcPeiDisableSlotCmd64: Disable Slot Command Failed, Status = %r\n", Status)); + return Status; + } + // + // Free the slot's device context entry + // + Xhc->DCBAA[SlotId] = 0; + + // + // Free the slot related data structure + // + for (Index = 0; Index < 31; Index++) { + if (Xhc->UsbDevContext[SlotId].EndpointTransferRing[Index] != NULL) { + RingSeg = ((TRANSFER_RING *) (UINTN) Xhc->UsbDevContext[SlotId].EndpointTransferRing[Index])->RingSeg0; + if (RingSeg != NULL) { + UsbHcFreeMem (Xhc->MemPool, RingSeg, sizeof (TRB_TEMPLATE) * TR_RING_TRB_NUMBER); + } + FreePool (Xhc->UsbDevContext[SlotId].EndpointTransferRing[Index]); + Xhc->UsbDevContext[SlotId].EndpointTransferRing[Index] = NULL; + } + } + + for (Index = 0; Index < Xhc->UsbDevContext[SlotId].DevDesc.NumConfigurations; Index++) { + if (Xhc->UsbDevContext[SlotId].ConfDesc[Index] != NULL) { + FreePool (Xhc->UsbDevContext[SlotId].ConfDesc[Index]); + } + } + + if (Xhc->UsbDevContext[SlotId].InputContext != NULL) { + UsbHcFreeMem (Xhc->MemPool, Xhc->UsbDevContext[SlotId].InputContext, sizeof (INPUT_CONTEXT_64)); + } + + if (Xhc->UsbDevContext[SlotId].OutputContext != NULL) { + UsbHcFreeMem (Xhc->MemPool, Xhc->UsbDevContext[SlotId].OutputContext, sizeof (DEVICE_CONTEXT_64)); + } + // + // Doesn't zero the entry because XhcAsyncInterruptTransfer() may be invoked to remove the established + // asynchronous interrupt pipe after the device is disabled. It needs the device address mapping info to + // remove urb from XHCI's asynchronous transfer list. + // + Xhc->UsbDevContext[SlotId].Enabled = FALSE; + Xhc->UsbDevContext[SlotId].SlotId = 0; + + DEBUG ((EFI_D_INFO, "XhcPeiDisableSlotCmd64: Disable Slot Command, Status = %r\n", Status)); + return Status; +} + +/** + Configure all the device endpoints through XHCI's Configure_Endpoint cmd. + + @param Xhc The XHCI device. + @param SlotId The slot id to be configured. + @param DeviceSpeed The device's speed. + @param ConfigDesc The pointer to the usb device configuration descriptor. + + @retval EFI_SUCCESS Successfully configure all the device endpoints. + +**/ +EFI_STATUS +XhcPeiSetConfigCmd ( + IN PEI_XHC_DEV *Xhc, + IN UINT8 SlotId, + IN UINT8 DeviceSpeed, + IN USB_CONFIG_DESCRIPTOR *ConfigDesc + ) +{ + EFI_STATUS Status; + USB_INTERFACE_DESCRIPTOR *IfDesc; + USB_ENDPOINT_DESCRIPTOR *EpDesc; + UINT8 Index; + UINTN NumEp; + UINTN EpIndex; + UINT8 EpAddr; + EFI_USB_DATA_DIRECTION Direction; + UINT8 Dci; + UINT8 MaxDci; + EFI_PHYSICAL_ADDRESS PhyAddr; + UINT8 Interval; + + TRANSFER_RING *EndpointTransferRing; + CMD_TRB_CONFIG_ENDPOINT CmdTrbCfgEP; + INPUT_CONTEXT *InputContext; + DEVICE_CONTEXT *OutputContext; + EVT_TRB_COMMAND_COMPLETION *EvtTrb; + // + // 4.6.6 Configure Endpoint + // + InputContext = Xhc->UsbDevContext[SlotId].InputContext; + OutputContext = Xhc->UsbDevContext[SlotId].OutputContext; + ZeroMem (InputContext, sizeof (INPUT_CONTEXT)); + CopyMem (&InputContext->Slot, &OutputContext->Slot, sizeof (SLOT_CONTEXT)); + + ASSERT (ConfigDesc != NULL); + + MaxDci = 0; + + IfDesc = (USB_INTERFACE_DESCRIPTOR *) (ConfigDesc + 1); + for (Index = 0; Index < ConfigDesc->NumInterfaces; Index++) { + while ((IfDesc->DescriptorType != USB_DESC_TYPE_INTERFACE) || (IfDesc->AlternateSetting != 0)) { + IfDesc = (USB_INTERFACE_DESCRIPTOR *) ((UINTN) IfDesc + IfDesc->Length); + } + + NumEp = IfDesc->NumEndpoints; + + EpDesc = (USB_ENDPOINT_DESCRIPTOR *) (IfDesc + 1); + for (EpIndex = 0; EpIndex < NumEp; EpIndex++) { + while (EpDesc->DescriptorType != USB_DESC_TYPE_ENDPOINT) { + EpDesc = (USB_ENDPOINT_DESCRIPTOR *) ((UINTN) EpDesc + EpDesc->Length); + } + + EpAddr = (UINT8) (EpDesc->EndpointAddress & 0x0F); + Direction = (UINT8) ((EpDesc->EndpointAddress & 0x80) ? EfiUsbDataIn : EfiUsbDataOut); + + Dci = XhcPeiEndpointToDci (EpAddr, Direction); + if (Dci > MaxDci) { + MaxDci = Dci; + } + + InputContext->InputControlContext.Dword2 |= (BIT0 << Dci); + InputContext->EP[Dci-1].MaxPacketSize = EpDesc->MaxPacketSize; + + if (DeviceSpeed == EFI_USB_SPEED_SUPER) { + // + // 6.2.3.4, shall be set to the value defined in the bMaxBurst field of the SuperSpeed Endpoint Companion Descriptor. + // + InputContext->EP[Dci-1].MaxBurstSize = 0x0; + } else { + InputContext->EP[Dci-1].MaxBurstSize = 0x0; + } + + switch (EpDesc->Attributes & USB_ENDPOINT_TYPE_MASK) { + case USB_ENDPOINT_BULK: + if (Direction == EfiUsbDataIn) { + InputContext->EP[Dci-1].CErr = 3; + InputContext->EP[Dci-1].EPType = ED_BULK_IN; + } else { + InputContext->EP[Dci-1].CErr = 3; + InputContext->EP[Dci-1].EPType = ED_BULK_OUT; + } + + InputContext->EP[Dci-1].AverageTRBLength = 0x1000; + if (Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1] == NULL) { + EndpointTransferRing = AllocateZeroPool (sizeof (TRANSFER_RING)); + Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1] = (VOID *) EndpointTransferRing; + XhcPeiCreateTransferRing (Xhc, TR_RING_TRB_NUMBER, (TRANSFER_RING *) Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1]); + } + + break; + case USB_ENDPOINT_ISO: + if (Direction == EfiUsbDataIn) { + InputContext->EP[Dci-1].CErr = 0; + InputContext->EP[Dci-1].EPType = ED_ISOCH_IN; + } else { + InputContext->EP[Dci-1].CErr = 0; + InputContext->EP[Dci-1].EPType = ED_ISOCH_OUT; + } + break; + case USB_ENDPOINT_INTERRUPT: + if (Direction == EfiUsbDataIn) { + InputContext->EP[Dci-1].CErr = 3; + InputContext->EP[Dci-1].EPType = ED_INTERRUPT_IN; + } else { + InputContext->EP[Dci-1].CErr = 3; + InputContext->EP[Dci-1].EPType = ED_INTERRUPT_OUT; + } + InputContext->EP[Dci-1].AverageTRBLength = 0x1000; + InputContext->EP[Dci-1].MaxESITPayload = EpDesc->MaxPacketSize; + // + // Get the bInterval from descriptor and init the interval field of endpoint context + // + if ((DeviceSpeed == EFI_USB_SPEED_FULL) || (DeviceSpeed == EFI_USB_SPEED_LOW)) { + Interval = EpDesc->Interval; + // + // Calculate through the bInterval field of Endpoint descriptor. + // + ASSERT (Interval != 0); + InputContext->EP[Dci-1].Interval = (UINT32) HighBitSet32 ((UINT32) Interval) + 3; + } else if ((DeviceSpeed == EFI_USB_SPEED_HIGH) || (DeviceSpeed == EFI_USB_SPEED_SUPER)) { + Interval = EpDesc->Interval; + ASSERT (Interval >= 1 && Interval <= 16); + // + // Refer to XHCI 1.0 spec section 6.2.3.6, table 61 + // + InputContext->EP[Dci-1].Interval = Interval - 1; + } + + if (Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1] == NULL) { + EndpointTransferRing = AllocateZeroPool (sizeof (TRANSFER_RING)); + Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1] = (VOID *) EndpointTransferRing; + XhcPeiCreateTransferRing (Xhc, TR_RING_TRB_NUMBER, (TRANSFER_RING *) Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1]); + } + break; + + case USB_ENDPOINT_CONTROL: + default: + ASSERT (FALSE); + break; + } + + PhyAddr = UsbHcGetPciAddrForHostAddr ( + Xhc->MemPool, + ((TRANSFER_RING *) (UINTN) Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1])->RingSeg0, + sizeof (TRB_TEMPLATE) * TR_RING_TRB_NUMBER + ); + PhyAddr &= ~(0x0F); + PhyAddr |= ((TRANSFER_RING *) (UINTN) Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1])->RingPCS; + InputContext->EP[Dci-1].PtrLo = XHC_LOW_32BIT (PhyAddr); + InputContext->EP[Dci-1].PtrHi = XHC_HIGH_32BIT (PhyAddr); + + EpDesc = (USB_ENDPOINT_DESCRIPTOR *) ((UINTN) EpDesc + EpDesc->Length); + } + IfDesc = (USB_INTERFACE_DESCRIPTOR *) ((UINTN) IfDesc + IfDesc->Length); + } + + InputContext->InputControlContext.Dword2 |= BIT0; + InputContext->Slot.ContextEntries = MaxDci; + // + // configure endpoint + // + ZeroMem (&CmdTrbCfgEP, sizeof (CmdTrbCfgEP)); + PhyAddr = UsbHcGetPciAddrForHostAddr (Xhc->MemPool, InputContext, sizeof (INPUT_CONTEXT)); + CmdTrbCfgEP.PtrLo = XHC_LOW_32BIT (PhyAddr); + CmdTrbCfgEP.PtrHi = XHC_HIGH_32BIT (PhyAddr); + CmdTrbCfgEP.CycleBit = 1; + CmdTrbCfgEP.Type = TRB_TYPE_CON_ENDPOINT; + CmdTrbCfgEP.SlotId = Xhc->UsbDevContext[SlotId].SlotId; + DEBUG ((EFI_D_INFO, "XhcSetConfigCmd: Configure Endpoint\n")); + Status = XhcPeiCmdTransfer ( + Xhc, + (TRB_TEMPLATE *) (UINTN) &CmdTrbCfgEP, + XHC_GENERIC_TIMEOUT, + (TRB_TEMPLATE **) (UINTN) &EvtTrb + ); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "XhcSetConfigCmd: Config Endpoint Failed, Status = %r\n", Status)); + } + return Status; +} + +/** + Configure all the device endpoints through XHCI's Configure_Endpoint cmd. + + @param Xhc The XHCI device. + @param SlotId The slot id to be configured. + @param DeviceSpeed The device's speed. + @param ConfigDesc The pointer to the usb device configuration descriptor. + + @retval EFI_SUCCESS Successfully configure all the device endpoints. + +**/ +EFI_STATUS +XhcPeiSetConfigCmd64 ( + IN PEI_XHC_DEV *Xhc, + IN UINT8 SlotId, + IN UINT8 DeviceSpeed, + IN USB_CONFIG_DESCRIPTOR *ConfigDesc + ) +{ + EFI_STATUS Status; + USB_INTERFACE_DESCRIPTOR *IfDesc; + USB_ENDPOINT_DESCRIPTOR *EpDesc; + UINT8 Index; + UINTN NumEp; + UINTN EpIndex; + UINT8 EpAddr; + EFI_USB_DATA_DIRECTION Direction; + UINT8 Dci; + UINT8 MaxDci; + EFI_PHYSICAL_ADDRESS PhyAddr; + UINT8 Interval; + + TRANSFER_RING *EndpointTransferRing; + CMD_TRB_CONFIG_ENDPOINT CmdTrbCfgEP; + INPUT_CONTEXT_64 *InputContext; + DEVICE_CONTEXT_64 *OutputContext; + EVT_TRB_COMMAND_COMPLETION *EvtTrb; + // + // 4.6.6 Configure Endpoint + // + InputContext = Xhc->UsbDevContext[SlotId].InputContext; + OutputContext = Xhc->UsbDevContext[SlotId].OutputContext; + ZeroMem (InputContext, sizeof (INPUT_CONTEXT_64)); + CopyMem (&InputContext->Slot, &OutputContext->Slot, sizeof (SLOT_CONTEXT_64)); + + ASSERT (ConfigDesc != NULL); + + MaxDci = 0; + + IfDesc = (USB_INTERFACE_DESCRIPTOR *) (ConfigDesc + 1); + for (Index = 0; Index < ConfigDesc->NumInterfaces; Index++) { + while ((IfDesc->DescriptorType != USB_DESC_TYPE_INTERFACE) || (IfDesc->AlternateSetting != 0)) { + IfDesc = (USB_INTERFACE_DESCRIPTOR *) ((UINTN) IfDesc + IfDesc->Length); + } + + NumEp = IfDesc->NumEndpoints; + + EpDesc = (USB_ENDPOINT_DESCRIPTOR *) (IfDesc + 1); + for (EpIndex = 0; EpIndex < NumEp; EpIndex++) { + while (EpDesc->DescriptorType != USB_DESC_TYPE_ENDPOINT) { + EpDesc = (USB_ENDPOINT_DESCRIPTOR *) ((UINTN) EpDesc + EpDesc->Length); + } + + EpAddr = (UINT8) (EpDesc->EndpointAddress & 0x0F); + Direction = (UINT8) ((EpDesc->EndpointAddress & 0x80) ? EfiUsbDataIn : EfiUsbDataOut); + + Dci = XhcPeiEndpointToDci (EpAddr, Direction); + ASSERT (Dci < 32); + if (Dci > MaxDci) { + MaxDci = Dci; + } + + InputContext->InputControlContext.Dword2 |= (BIT0 << Dci); + InputContext->EP[Dci-1].MaxPacketSize = EpDesc->MaxPacketSize; + + if (DeviceSpeed == EFI_USB_SPEED_SUPER) { + // + // 6.2.3.4, shall be set to the value defined in the bMaxBurst field of the SuperSpeed Endpoint Companion Descriptor. + // + InputContext->EP[Dci-1].MaxBurstSize = 0x0; + } else { + InputContext->EP[Dci-1].MaxBurstSize = 0x0; + } + + switch (EpDesc->Attributes & USB_ENDPOINT_TYPE_MASK) { + case USB_ENDPOINT_BULK: + if (Direction == EfiUsbDataIn) { + InputContext->EP[Dci-1].CErr = 3; + InputContext->EP[Dci-1].EPType = ED_BULK_IN; + } else { + InputContext->EP[Dci-1].CErr = 3; + InputContext->EP[Dci-1].EPType = ED_BULK_OUT; + } + + InputContext->EP[Dci-1].AverageTRBLength = 0x1000; + if (Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1] == NULL) { + EndpointTransferRing = AllocateZeroPool (sizeof (TRANSFER_RING)); + Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1] = (VOID *) EndpointTransferRing; + XhcPeiCreateTransferRing (Xhc, TR_RING_TRB_NUMBER, (TRANSFER_RING *) Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1]); + } + + break; + case USB_ENDPOINT_ISO: + if (Direction == EfiUsbDataIn) { + InputContext->EP[Dci-1].CErr = 0; + InputContext->EP[Dci-1].EPType = ED_ISOCH_IN; + } else { + InputContext->EP[Dci-1].CErr = 0; + InputContext->EP[Dci-1].EPType = ED_ISOCH_OUT; + } + break; + case USB_ENDPOINT_INTERRUPT: + if (Direction == EfiUsbDataIn) { + InputContext->EP[Dci-1].CErr = 3; + InputContext->EP[Dci-1].EPType = ED_INTERRUPT_IN; + } else { + InputContext->EP[Dci-1].CErr = 3; + InputContext->EP[Dci-1].EPType = ED_INTERRUPT_OUT; + } + InputContext->EP[Dci-1].AverageTRBLength = 0x1000; + InputContext->EP[Dci-1].MaxESITPayload = EpDesc->MaxPacketSize; + // + // Get the bInterval from descriptor and init the the interval field of endpoint context + // + if ((DeviceSpeed == EFI_USB_SPEED_FULL) || (DeviceSpeed == EFI_USB_SPEED_LOW)) { + Interval = EpDesc->Interval; + // + // Calculate through the bInterval field of Endpoint descriptor. + // + ASSERT (Interval != 0); + InputContext->EP[Dci-1].Interval = (UINT32) HighBitSet32( (UINT32) Interval) + 3; + } else if ((DeviceSpeed == EFI_USB_SPEED_HIGH) || (DeviceSpeed == EFI_USB_SPEED_SUPER)) { + Interval = EpDesc->Interval; + ASSERT (Interval >= 1 && Interval <= 16); + // + // Refer to XHCI 1.0 spec section 6.2.3.6, table 61 + // + InputContext->EP[Dci-1].Interval = Interval - 1; + } + + if (Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1] == NULL) { + EndpointTransferRing = AllocateZeroPool (sizeof (TRANSFER_RING)); + Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1] = (VOID *) EndpointTransferRing; + XhcPeiCreateTransferRing (Xhc, TR_RING_TRB_NUMBER, (TRANSFER_RING *) Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1]); + } + break; + + case USB_ENDPOINT_CONTROL: + default: + ASSERT (0); + break; + } + + PhyAddr = UsbHcGetPciAddrForHostAddr ( + Xhc->MemPool, + ((TRANSFER_RING *) (UINTN) Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1])->RingSeg0, + sizeof (TRB_TEMPLATE) * TR_RING_TRB_NUMBER + ); + + PhyAddr &= ~(0x0F); + PhyAddr |= ((TRANSFER_RING *) (UINTN) Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1])->RingPCS; + + InputContext->EP[Dci-1].PtrLo = XHC_LOW_32BIT (PhyAddr); + InputContext->EP[Dci-1].PtrHi = XHC_HIGH_32BIT (PhyAddr); + + EpDesc = (USB_ENDPOINT_DESCRIPTOR *) ((UINTN)EpDesc + EpDesc->Length); + } + IfDesc = (USB_INTERFACE_DESCRIPTOR *) ((UINTN)IfDesc + IfDesc->Length); + } + + InputContext->InputControlContext.Dword2 |= BIT0; + InputContext->Slot.ContextEntries = MaxDci; + // + // configure endpoint + // + ZeroMem (&CmdTrbCfgEP, sizeof (CmdTrbCfgEP)); + PhyAddr = UsbHcGetPciAddrForHostAddr (Xhc->MemPool, InputContext, sizeof (INPUT_CONTEXT_64)); + CmdTrbCfgEP.PtrLo = XHC_LOW_32BIT (PhyAddr); + CmdTrbCfgEP.PtrHi = XHC_HIGH_32BIT (PhyAddr); + CmdTrbCfgEP.CycleBit = 1; + CmdTrbCfgEP.Type = TRB_TYPE_CON_ENDPOINT; + CmdTrbCfgEP.SlotId = Xhc->UsbDevContext[SlotId].SlotId; + DEBUG ((EFI_D_INFO, "XhcSetConfigCmd64: Configure Endpoint\n")); + Status = XhcPeiCmdTransfer ( + Xhc, + (TRB_TEMPLATE *) (UINTN) &CmdTrbCfgEP, + XHC_GENERIC_TIMEOUT, + (TRB_TEMPLATE **) (UINTN) &EvtTrb + ); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "XhcSetConfigCmd64: Config Endpoint Failed, Status = %r\n", Status)); + } + + return Status; +} + + +/** + Evaluate the endpoint 0 context through XHCI's Evaluate_Context cmd. + + @param Xhc The XHCI device. + @param SlotId The slot id to be evaluated. + @param MaxPacketSize The max packet size supported by the device control transfer. + + @retval EFI_SUCCESS Successfully evaluate the device endpoint 0. + +**/ +EFI_STATUS +XhcPeiEvaluateContext ( + IN PEI_XHC_DEV *Xhc, + IN UINT8 SlotId, + IN UINT32 MaxPacketSize + ) +{ + EFI_STATUS Status; + CMD_TRB_EVALUATE_CONTEXT CmdTrbEvalu; + EVT_TRB_COMMAND_COMPLETION *EvtTrb; + INPUT_CONTEXT *InputContext; + EFI_PHYSICAL_ADDRESS PhyAddr; + + ASSERT (Xhc->UsbDevContext[SlotId].SlotId != 0); + + // + // 4.6.7 Evaluate Context + // + InputContext = Xhc->UsbDevContext[SlotId].InputContext; + ZeroMem (InputContext, sizeof (INPUT_CONTEXT)); + + InputContext->InputControlContext.Dword2 |= BIT1; + InputContext->EP[0].MaxPacketSize = MaxPacketSize; + + ZeroMem (&CmdTrbEvalu, sizeof (CmdTrbEvalu)); + PhyAddr = UsbHcGetPciAddrForHostAddr (Xhc->MemPool, InputContext, sizeof (INPUT_CONTEXT)); + CmdTrbEvalu.PtrLo = XHC_LOW_32BIT (PhyAddr); + CmdTrbEvalu.PtrHi = XHC_HIGH_32BIT (PhyAddr); + CmdTrbEvalu.CycleBit = 1; + CmdTrbEvalu.Type = TRB_TYPE_EVALU_CONTXT; + CmdTrbEvalu.SlotId = Xhc->UsbDevContext[SlotId].SlotId; + DEBUG ((EFI_D_INFO, "XhcEvaluateContext: Evaluate context\n")); + Status = XhcPeiCmdTransfer ( + Xhc, + (TRB_TEMPLATE *) (UINTN) &CmdTrbEvalu, + XHC_GENERIC_TIMEOUT, + (TRB_TEMPLATE **) (UINTN) &EvtTrb + ); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "XhcEvaluateContext: Evaluate Context Failed, Status = %r\n", Status)); + } + return Status; +} + +/** + Evaluate the endpoint 0 context through XHCI's Evaluate_Context cmd. + + @param Xhc The XHCI device. + @param SlotId The slot id to be evaluated. + @param MaxPacketSize The max packet size supported by the device control transfer. + + @retval EFI_SUCCESS Successfully evaluate the device endpoint 0. + +**/ +EFI_STATUS +XhcPeiEvaluateContext64 ( + IN PEI_XHC_DEV *Xhc, + IN UINT8 SlotId, + IN UINT32 MaxPacketSize + ) +{ + EFI_STATUS Status; + CMD_TRB_EVALUATE_CONTEXT CmdTrbEvalu; + EVT_TRB_COMMAND_COMPLETION *EvtTrb; + INPUT_CONTEXT_64 *InputContext; + EFI_PHYSICAL_ADDRESS PhyAddr; + + ASSERT (Xhc->UsbDevContext[SlotId].SlotId != 0); + + // + // 4.6.7 Evaluate Context + // + InputContext = Xhc->UsbDevContext[SlotId].InputContext; + ZeroMem (InputContext, sizeof (INPUT_CONTEXT_64)); + + InputContext->InputControlContext.Dword2 |= BIT1; + InputContext->EP[0].MaxPacketSize = MaxPacketSize; + + ZeroMem (&CmdTrbEvalu, sizeof (CmdTrbEvalu)); + PhyAddr = UsbHcGetPciAddrForHostAddr (Xhc->MemPool, InputContext, sizeof (INPUT_CONTEXT_64)); + CmdTrbEvalu.PtrLo = XHC_LOW_32BIT (PhyAddr); + CmdTrbEvalu.PtrHi = XHC_HIGH_32BIT (PhyAddr); + CmdTrbEvalu.CycleBit = 1; + CmdTrbEvalu.Type = TRB_TYPE_EVALU_CONTXT; + CmdTrbEvalu.SlotId = Xhc->UsbDevContext[SlotId].SlotId; + DEBUG ((EFI_D_INFO, "XhcEvaluateContext64: Evaluate context 64\n")); + Status = XhcPeiCmdTransfer ( + Xhc, + (TRB_TEMPLATE *) (UINTN) &CmdTrbEvalu, + XHC_GENERIC_TIMEOUT, + (TRB_TEMPLATE **) (UINTN) &EvtTrb + ); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "XhcEvaluateContext64: Evaluate Context Failed, Status = %r\n", Status)); + } + return Status; +} + +/** + Evaluate the slot context for hub device through XHCI's Configure_Endpoint cmd. + + @param Xhc The XHCI device. + @param SlotId The slot id to be configured. + @param PortNum The total number of downstream port supported by the hub. + @param TTT The TT think time of the hub device. + @param MTT The multi-TT of the hub device. + + @retval EFI_SUCCESS Successfully configure the hub device's slot context. + +**/ +EFI_STATUS +XhcPeiConfigHubContext ( + IN PEI_XHC_DEV *Xhc, + IN UINT8 SlotId, + IN UINT8 PortNum, + IN UINT8 TTT, + IN UINT8 MTT + ) +{ + EFI_STATUS Status; + EVT_TRB_COMMAND_COMPLETION *EvtTrb; + INPUT_CONTEXT *InputContext; + DEVICE_CONTEXT *OutputContext; + CMD_TRB_CONFIG_ENDPOINT CmdTrbCfgEP; + EFI_PHYSICAL_ADDRESS PhyAddr; + + ASSERT (Xhc->UsbDevContext[SlotId].SlotId != 0); + InputContext = Xhc->UsbDevContext[SlotId].InputContext; + OutputContext = Xhc->UsbDevContext[SlotId].OutputContext; + + // + // 4.6.7 Evaluate Context + // + ZeroMem (InputContext, sizeof (INPUT_CONTEXT)); + + InputContext->InputControlContext.Dword2 |= BIT0; + + // + // Copy the slot context from OutputContext to Input context + // + CopyMem(&(InputContext->Slot), &(OutputContext->Slot), sizeof (SLOT_CONTEXT)); + InputContext->Slot.Hub = 1; + InputContext->Slot.PortNum = PortNum; + InputContext->Slot.TTT = TTT; + InputContext->Slot.MTT = MTT; + + ZeroMem (&CmdTrbCfgEP, sizeof (CmdTrbCfgEP)); + PhyAddr = UsbHcGetPciAddrForHostAddr (Xhc->MemPool, InputContext, sizeof (INPUT_CONTEXT)); + CmdTrbCfgEP.PtrLo = XHC_LOW_32BIT (PhyAddr); + CmdTrbCfgEP.PtrHi = XHC_HIGH_32BIT (PhyAddr); + CmdTrbCfgEP.CycleBit = 1; + CmdTrbCfgEP.Type = TRB_TYPE_CON_ENDPOINT; + CmdTrbCfgEP.SlotId = Xhc->UsbDevContext[SlotId].SlotId; + DEBUG ((EFI_D_INFO, "Configure Hub Slot Context\n")); + Status = XhcPeiCmdTransfer ( + Xhc, + (TRB_TEMPLATE *) (UINTN) &CmdTrbCfgEP, + XHC_GENERIC_TIMEOUT, + (TRB_TEMPLATE **) (UINTN) &EvtTrb + ); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "XhcConfigHubContext: Config Endpoint Failed, Status = %r\n", Status)); + } + return Status; +} + +/** + Evaluate the slot context for hub device through XHCI's Configure_Endpoint cmd. + + @param Xhc The XHCI device. + @param SlotId The slot id to be configured. + @param PortNum The total number of downstream port supported by the hub. + @param TTT The TT think time of the hub device. + @param MTT The multi-TT of the hub device. + + @retval EFI_SUCCESS Successfully configure the hub device's slot context. + +**/ +EFI_STATUS +XhcPeiConfigHubContext64 ( + IN PEI_XHC_DEV *Xhc, + IN UINT8 SlotId, + IN UINT8 PortNum, + IN UINT8 TTT, + IN UINT8 MTT + ) +{ + EFI_STATUS Status; + EVT_TRB_COMMAND_COMPLETION *EvtTrb; + INPUT_CONTEXT_64 *InputContext; + DEVICE_CONTEXT_64 *OutputContext; + CMD_TRB_CONFIG_ENDPOINT CmdTrbCfgEP; + EFI_PHYSICAL_ADDRESS PhyAddr; + + ASSERT (Xhc->UsbDevContext[SlotId].SlotId != 0); + InputContext = Xhc->UsbDevContext[SlotId].InputContext; + OutputContext = Xhc->UsbDevContext[SlotId].OutputContext; + + // + // 4.6.7 Evaluate Context + // + ZeroMem (InputContext, sizeof (INPUT_CONTEXT_64)); + + InputContext->InputControlContext.Dword2 |= BIT0; + + // + // Copy the slot context from OutputContext to Input context + // + CopyMem(&(InputContext->Slot), &(OutputContext->Slot), sizeof (SLOT_CONTEXT_64)); + InputContext->Slot.Hub = 1; + InputContext->Slot.PortNum = PortNum; + InputContext->Slot.TTT = TTT; + InputContext->Slot.MTT = MTT; + + ZeroMem (&CmdTrbCfgEP, sizeof (CmdTrbCfgEP)); + PhyAddr = UsbHcGetPciAddrForHostAddr (Xhc->MemPool, InputContext, sizeof (INPUT_CONTEXT_64)); + CmdTrbCfgEP.PtrLo = XHC_LOW_32BIT (PhyAddr); + CmdTrbCfgEP.PtrHi = XHC_HIGH_32BIT (PhyAddr); + CmdTrbCfgEP.CycleBit = 1; + CmdTrbCfgEP.Type = TRB_TYPE_CON_ENDPOINT; + CmdTrbCfgEP.SlotId = Xhc->UsbDevContext[SlotId].SlotId; + DEBUG ((EFI_D_INFO, "Configure Hub Slot Context 64\n")); + Status = XhcPeiCmdTransfer ( + Xhc, + (TRB_TEMPLATE *) (UINTN) &CmdTrbCfgEP, + XHC_GENERIC_TIMEOUT, + (TRB_TEMPLATE **) (UINTN) &EvtTrb + ); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "XhcConfigHubContext64: Config Endpoint Failed, Status = %r\n", Status)); + } + return Status; +} + +/** + Check if there is a new generated event. + + @param Xhc The XHCI device. + @param EvtRing The event ring to check. + @param NewEvtTrb The new event TRB found. + + @retval EFI_SUCCESS Found a new event TRB at the event ring. + @retval EFI_NOT_READY The event ring has no new event. + +**/ +EFI_STATUS +XhcPeiCheckNewEvent ( + IN PEI_XHC_DEV *Xhc, + IN EVENT_RING *EvtRing, + OUT TRB_TEMPLATE **NewEvtTrb + ) +{ + ASSERT (EvtRing != NULL); + + *NewEvtTrb = EvtRing->EventRingDequeue; + + if (EvtRing->EventRingDequeue == EvtRing->EventRingEnqueue) { + return EFI_NOT_READY; + } + + EvtRing->EventRingDequeue++; + // + // If the dequeue pointer is beyond the ring, then roll-back it to the begining of the ring. + // + if ((UINTN) EvtRing->EventRingDequeue >= ((UINTN) EvtRing->EventRingSeg0 + sizeof (TRB_TEMPLATE) * EvtRing->TrbNumber)) { + EvtRing->EventRingDequeue = EvtRing->EventRingSeg0; + } + + return EFI_SUCCESS; +} + +/** + Synchronize the specified event ring to update the enqueue and dequeue pointer. + + @param Xhc The XHCI device. + @param EvtRing The event ring to sync. + + @retval EFI_SUCCESS The event ring is synchronized successfully. + +**/ +EFI_STATUS +XhcPeiSyncEventRing ( + IN PEI_XHC_DEV *Xhc, + IN EVENT_RING *EvtRing + ) +{ + UINTN Index; + TRB_TEMPLATE *EvtTrb; + + ASSERT (EvtRing != NULL); + + // + // Calculate the EventRingEnqueue and EventRingCCS. + // Note: only support single Segment + // + EvtTrb = EvtRing->EventRingDequeue; + + for (Index = 0; Index < EvtRing->TrbNumber; Index++) { + if (EvtTrb->CycleBit != EvtRing->EventRingCCS) { + break; + } + + EvtTrb++; + + if ((UINTN) EvtTrb >= ((UINTN) EvtRing->EventRingSeg0 + sizeof (TRB_TEMPLATE) * EvtRing->TrbNumber)) { + EvtTrb = EvtRing->EventRingSeg0; + EvtRing->EventRingCCS = (EvtRing->EventRingCCS) ? 0 : 1; + } + } + + if (Index < EvtRing->TrbNumber) { + EvtRing->EventRingEnqueue = EvtTrb; + } else { + ASSERT (FALSE); + } + + return EFI_SUCCESS; +} + +/** + Free XHCI event ring. + + @param Xhc The XHCI device. + @param EventRing The event ring to be freed. + +**/ +VOID +XhcPeiFreeEventRing ( + IN PEI_XHC_DEV *Xhc, + IN EVENT_RING *EventRing + ) +{ + if(EventRing->EventRingSeg0 == NULL) { + return; + } + + // + // Free EventRing Segment 0 + // + UsbHcFreeMem (Xhc->MemPool, EventRing->EventRingSeg0, sizeof (TRB_TEMPLATE) * EVENT_RING_TRB_NUMBER); + + // + // Free ERST table + // + UsbHcFreeMem (Xhc->MemPool, EventRing->ERSTBase, sizeof (EVENT_RING_SEG_TABLE_ENTRY) * ERST_NUMBER); +} + +/** + Create XHCI event ring. + + @param Xhc The XHCI device. + @param EventRing The created event ring. + +**/ +VOID +XhcPeiCreateEventRing ( + IN PEI_XHC_DEV *Xhc, + OUT EVENT_RING *EventRing + ) +{ + VOID *Buf; + EVENT_RING_SEG_TABLE_ENTRY *ERSTBase; + UINTN Size; + EFI_PHYSICAL_ADDRESS ERSTPhy; + EFI_PHYSICAL_ADDRESS DequeuePhy; + + ASSERT (EventRing != NULL); + + Size = sizeof (TRB_TEMPLATE) * EVENT_RING_TRB_NUMBER; + Buf = UsbHcAllocateMem (Xhc->MemPool, Size); + ASSERT (Buf != NULL); + ASSERT (((UINTN) Buf & 0x3F) == 0); + ZeroMem (Buf, Size); + + DequeuePhy = UsbHcGetPciAddrForHostAddr (Xhc->MemPool, Buf, Size); + + EventRing->EventRingSeg0 = Buf; + EventRing->TrbNumber = EVENT_RING_TRB_NUMBER; + EventRing->EventRingDequeue = (TRB_TEMPLATE *) EventRing->EventRingSeg0; + EventRing->EventRingEnqueue = (TRB_TEMPLATE *) EventRing->EventRingSeg0; + + // + // Software maintains an Event Ring Consumer Cycle State (CCS) bit, initializing it to '1' + // and toggling it every time the Event Ring Dequeue Pointer wraps back to the beginning of the Event Ring. + // + EventRing->EventRingCCS = 1; + + Size = sizeof (EVENT_RING_SEG_TABLE_ENTRY) * ERST_NUMBER; + Buf = UsbHcAllocateMem (Xhc->MemPool, Size); + ASSERT (Buf != NULL); + ASSERT (((UINTN) Buf & 0x3F) == 0); + ZeroMem (Buf, Size); + + ERSTBase = (EVENT_RING_SEG_TABLE_ENTRY *) Buf; + EventRing->ERSTBase = ERSTBase; + ERSTBase->PtrLo = XHC_LOW_32BIT (DequeuePhy); + ERSTBase->PtrHi = XHC_HIGH_32BIT (DequeuePhy); + ERSTBase->RingTrbSize = EVENT_RING_TRB_NUMBER; + + ERSTPhy = UsbHcGetPciAddrForHostAddr (Xhc->MemPool, Buf, Size); + + // + // Program the Interrupter Event Ring Segment Table Size (ERSTSZ) register (5.5.2.3.1) + // + XhcPeiWriteRuntimeReg ( + Xhc, + XHC_ERSTSZ_OFFSET, + ERST_NUMBER + ); + // + // Program the Interrupter Event Ring Dequeue Pointer (ERDP) register (5.5.2.3.3) + // + // Some 3rd party XHCI external cards don't support single 64-bytes width register access, + // So divide it to two 32-bytes width register access. + // + XhcPeiWriteRuntimeReg ( + Xhc, + XHC_ERDP_OFFSET, + XHC_LOW_32BIT ((UINT64) (UINTN) DequeuePhy) + ); + XhcPeiWriteRuntimeReg ( + Xhc, + XHC_ERDP_OFFSET + 4, + XHC_HIGH_32BIT ((UINT64) (UINTN) DequeuePhy) + ); + // + // Program the Interrupter Event Ring Segment Table Base Address (ERSTBA) register (5.5.2.3.2) + // + // Some 3rd party XHCI external cards don't support single 64-bytes width register access, + // So divide it to two 32-bytes width register access. + // + XhcPeiWriteRuntimeReg ( + Xhc, + XHC_ERSTBA_OFFSET, + XHC_LOW_32BIT ((UINT64) (UINTN) ERSTPhy) + ); + XhcPeiWriteRuntimeReg ( + Xhc, + XHC_ERSTBA_OFFSET + 4, + XHC_HIGH_32BIT ((UINT64) (UINTN) ERSTPhy) + ); + // + // Need set IMAN IE bit to enable the ring interrupt + // + XhcPeiSetRuntimeRegBit (Xhc, XHC_IMAN_OFFSET, XHC_IMAN_IE); +} + +/** + Synchronize the specified transfer ring to update the enqueue and dequeue pointer. + + @param Xhc The XHCI device. + @param TrsRing The transfer ring to sync. + + @retval EFI_SUCCESS The transfer ring is synchronized successfully. + +**/ +EFI_STATUS +XhcPeiSyncTrsRing ( + IN PEI_XHC_DEV *Xhc, + IN TRANSFER_RING *TrsRing + ) +{ + UINTN Index; + TRB_TEMPLATE *TrsTrb; + + ASSERT (TrsRing != NULL); + // + // Calculate the latest RingEnqueue and RingPCS + // + TrsTrb = TrsRing->RingEnqueue; + ASSERT (TrsTrb != NULL); + + for (Index = 0; Index < TrsRing->TrbNumber; Index++) { + if (TrsTrb->CycleBit != (TrsRing->RingPCS & BIT0)) { + break; + } + TrsTrb++; + if ((UINT8) TrsTrb->Type == TRB_TYPE_LINK) { + ASSERT (((LINK_TRB *) TrsTrb)->TC != 0); + // + // set cycle bit in Link TRB as normal + // + ((LINK_TRB*)TrsTrb)->CycleBit = TrsRing->RingPCS & BIT0; + // + // Toggle PCS maintained by software + // + TrsRing->RingPCS = (TrsRing->RingPCS & BIT0) ? 0 : 1; + TrsTrb = (TRB_TEMPLATE *) TrsRing->RingSeg0; // Use host address + } + } + + ASSERT (Index != TrsRing->TrbNumber); + + if (TrsTrb != TrsRing->RingEnqueue) { + TrsRing->RingEnqueue = TrsTrb; + } + + // + // Clear the Trb context for enqueue, but reserve the PCS bit + // + TrsTrb->Parameter1 = 0; + TrsTrb->Parameter2 = 0; + TrsTrb->Status = 0; + TrsTrb->RsvdZ1 = 0; + TrsTrb->Type = 0; + TrsTrb->Control = 0; + + return EFI_SUCCESS; +} + +/** + Create XHCI transfer ring. + + @param Xhc The XHCI Device. + @param TrbNum The number of TRB in the ring. + @param TransferRing The created transfer ring. + +**/ +VOID +XhcPeiCreateTransferRing ( + IN PEI_XHC_DEV *Xhc, + IN UINTN TrbNum, + OUT TRANSFER_RING *TransferRing + ) +{ + VOID *Buf; + LINK_TRB *EndTrb; + EFI_PHYSICAL_ADDRESS PhyAddr; + + Buf = UsbHcAllocateMem (Xhc->MemPool, sizeof (TRB_TEMPLATE) * TrbNum); + ASSERT (Buf != NULL); + ASSERT (((UINTN) Buf & 0x3F) == 0); + ZeroMem (Buf, sizeof (TRB_TEMPLATE) * TrbNum); + + TransferRing->RingSeg0 = Buf; + TransferRing->TrbNumber = TrbNum; + TransferRing->RingEnqueue = (TRB_TEMPLATE *) TransferRing->RingSeg0; + TransferRing->RingDequeue = (TRB_TEMPLATE *) TransferRing->RingSeg0; + TransferRing->RingPCS = 1; + // + // 4.9.2 Transfer Ring Management + // To form a ring (or circular queue) a Link TRB may be inserted at the end of a ring to + // point to the first TRB in the ring. + // + EndTrb = (LINK_TRB *) ((UINTN) Buf + sizeof (TRB_TEMPLATE) * (TrbNum - 1)); + EndTrb->Type = TRB_TYPE_LINK; + PhyAddr = UsbHcGetPciAddrForHostAddr (Xhc->MemPool, Buf, sizeof (TRB_TEMPLATE) * TrbNum); + EndTrb->PtrLo = XHC_LOW_32BIT (PhyAddr); + EndTrb->PtrHi = XHC_HIGH_32BIT (PhyAddr); + // + // Toggle Cycle (TC). When set to '1', the xHC shall toggle its interpretation of the Cycle bit. + // + EndTrb->TC = 1; + // + // Set Cycle bit as other TRB PCS init value + // + EndTrb->CycleBit = 0; +} + +/** + Initialize the XHCI host controller for schedule. + + @param Xhc The XHCI device to be initialized. + +**/ +VOID +XhcPeiInitSched ( + IN PEI_XHC_DEV *Xhc + ) +{ + VOID *Dcbaa; + EFI_PHYSICAL_ADDRESS DcbaaPhy; + UINTN Size; + EFI_PHYSICAL_ADDRESS CmdRingPhy; + UINT32 MaxScratchpadBufs; + UINT64 *ScratchBuf; + EFI_PHYSICAL_ADDRESS ScratchPhy; + UINT64 *ScratchEntry; + EFI_PHYSICAL_ADDRESS ScratchEntryPhy; + UINT32 Index; + EFI_STATUS Status; + + // + // Initialize memory management. + // + Xhc->MemPool = UsbHcInitMemPool (); + ASSERT (Xhc->MemPool != NULL); + + // + // Program the Max Device Slots Enabled (MaxSlotsEn) field in the CONFIG register (5.4.7) + // to enable the device slots that system software is going to use. + // + Xhc->MaxSlotsEn = Xhc->HcSParams1.Data.MaxSlots; + ASSERT (Xhc->MaxSlotsEn >= 1 && Xhc->MaxSlotsEn <= 255); + XhcPeiWriteOpReg (Xhc, XHC_CONFIG_OFFSET, (XhcPeiReadOpReg (Xhc, XHC_CONFIG_OFFSET) & ~XHC_CONFIG_MASK) | Xhc->MaxSlotsEn); + + // + // The Device Context Base Address Array entry associated with each allocated Device Slot + // shall contain a 64-bit pointer to the base of the associated Device Context. + // The Device Context Base Address Array shall contain MaxSlotsEn + 1 entries. + // Software shall set Device Context Base Address Array entries for unallocated Device Slots to '0'. + // + Size = (Xhc->MaxSlotsEn + 1) * sizeof (UINT64); + Dcbaa = UsbHcAllocateMem (Xhc->MemPool, Size); + ASSERT (Dcbaa != NULL); + + // + // A Scratchpad Buffer is a PAGESIZE block of system memory located on a PAGESIZE boundary. + // System software shall allocate the Scratchpad Buffer(s) before placing the xHC in to Run + // mode (Run/Stop(R/S) ='1'). + // + MaxScratchpadBufs = ((Xhc->HcSParams2.Data.ScratchBufHi) << 5) | (Xhc->HcSParams2.Data.ScratchBufLo); + Xhc->MaxScratchpadBufs = MaxScratchpadBufs; + ASSERT (MaxScratchpadBufs <= 1023); + if (MaxScratchpadBufs != 0) { + // + // Allocate the buffer to record the host address for each entry + // + ScratchEntry = AllocateZeroPool (sizeof (UINT64) * MaxScratchpadBufs); + ASSERT (ScratchEntry != NULL); + Xhc->ScratchEntry = ScratchEntry; + + Status = UsbHcAllocateAlignedPages ( + EFI_SIZE_TO_PAGES (MaxScratchpadBufs * sizeof (UINT64)), + Xhc->PageSize, + (VOID **) &ScratchBuf, + &ScratchPhy + ); + ASSERT_EFI_ERROR (Status); + + ZeroMem (ScratchBuf, MaxScratchpadBufs * sizeof (UINT64)); + Xhc->ScratchBuf = ScratchBuf; + + // + // Allocate each scratch buffer + // + for (Index = 0; Index < MaxScratchpadBufs; Index++) { + Status = UsbHcAllocateAlignedPages ( + EFI_SIZE_TO_PAGES (Xhc->PageSize), + Xhc->PageSize, + (VOID **) &ScratchEntry[Index], + &ScratchEntryPhy + ); + ASSERT_EFI_ERROR (Status); + ZeroMem ((VOID *) (UINTN) ScratchEntry[Index], Xhc->PageSize); + // + // Fill with the PCI device address + // + *ScratchBuf++ = ScratchEntryPhy; + } + // + // The Scratchpad Buffer Array contains pointers to the Scratchpad Buffers. Entry 0 of the + // Device Context Base Address Array points to the Scratchpad Buffer Array. + // + *(UINT64 *) Dcbaa = (UINT64) (UINTN) ScratchPhy; + } + + // + // Program the Device Context Base Address Array Pointer (DCBAAP) register (5.4.6) with + // a 64-bit address pointing to where the Device Context Base Address Array is located. + // + Xhc->DCBAA = (UINT64 *) (UINTN) Dcbaa; + // + // Some 3rd party XHCI external cards don't support single 64-bytes width register access, + // So divide it to two 32-bytes width register access. + // + DcbaaPhy = UsbHcGetPciAddrForHostAddr (Xhc->MemPool, Dcbaa, Size); + XhcPeiWriteOpReg (Xhc, XHC_DCBAAP_OFFSET, XHC_LOW_32BIT (DcbaaPhy)); + XhcPeiWriteOpReg (Xhc, XHC_DCBAAP_OFFSET + 4, XHC_HIGH_32BIT (DcbaaPhy)); + + DEBUG ((EFI_D_INFO, "XhcPeiInitSched:DCBAA=0x%x\n", Xhc->DCBAA)); + + // + // Define the Command Ring Dequeue Pointer by programming the Command Ring Control Register + // (5.4.5) with a 64-bit address pointing to the starting address of the first TRB of the Command Ring. + // Note: The Command Ring is 64 byte aligned, so the low order 6 bits of the Command Ring Pointer shall + // always be '0'. + // + XhcPeiCreateTransferRing (Xhc, CMD_RING_TRB_NUMBER, &Xhc->CmdRing); + // + // The xHC uses the Enqueue Pointer to determine when a Transfer Ring is empty. As it fetches TRBs from a + // Transfer Ring it checks for a Cycle bit transition. If a transition detected, the ring is empty. + // So we set RCS as inverted PCS init value to let Command Ring empty + // + CmdRingPhy = UsbHcGetPciAddrForHostAddr (Xhc->MemPool, Xhc->CmdRing.RingSeg0, sizeof (TRB_TEMPLATE) * CMD_RING_TRB_NUMBER); + ASSERT ((CmdRingPhy & 0x3F) == 0); + CmdRingPhy |= XHC_CRCR_RCS; + // + // Some 3rd party XHCI external cards don't support single 64-bytes width register access, + // So divide it to two 32-bytes width register access. + // + XhcPeiWriteOpReg (Xhc, XHC_CRCR_OFFSET, XHC_LOW_32BIT (CmdRingPhy)); + XhcPeiWriteOpReg (Xhc, XHC_CRCR_OFFSET + 4, XHC_HIGH_32BIT (CmdRingPhy)); + + DEBUG ((EFI_D_INFO, "XhcPeiInitSched:XHC_CRCR=0x%x\n", Xhc->CmdRing.RingSeg0)); + + // + // Disable the 'interrupter enable' bit in USB_CMD + // and clear IE & IP bit in all Interrupter X Management Registers. + // + XhcPeiClearOpRegBit (Xhc, XHC_USBCMD_OFFSET, XHC_USBCMD_INTE); + for (Index = 0; Index < (UINT16)(Xhc->HcSParams1.Data.MaxIntrs); Index++) { + XhcPeiClearRuntimeRegBit (Xhc, XHC_IMAN_OFFSET + (Index * 32), XHC_IMAN_IE); + XhcPeiSetRuntimeRegBit (Xhc, XHC_IMAN_OFFSET + (Index * 32), XHC_IMAN_IP); + } + + // + // Allocate EventRing for Cmd, Ctrl, Bulk, Interrupt, AsynInterrupt transfer + // + XhcPeiCreateEventRing (Xhc, &Xhc->EventRing); + DEBUG ((EFI_D_INFO, "XhcPeiInitSched:XHC_EVENTRING=0x%x\n", Xhc->EventRing.EventRingSeg0)); +} + +/** + Free the resouce allocated at initializing schedule. + + @param Xhc The XHCI device. + +**/ +VOID +XhcPeiFreeSched ( + IN PEI_XHC_DEV *Xhc + ) +{ + UINT32 Index; + UINT64 *ScratchEntry; + + if (Xhc->ScratchBuf != NULL) { + ScratchEntry = Xhc->ScratchEntry; + for (Index = 0; Index < Xhc->MaxScratchpadBufs; Index++) { + // + // Free Scratchpad Buffers + // + UsbHcFreeAlignedPages ((VOID*) (UINTN) ScratchEntry[Index], EFI_SIZE_TO_PAGES (Xhc->PageSize)); + } + // + // Free Scratchpad Buffer Array + // + UsbHcFreeAlignedPages (Xhc->ScratchBuf, EFI_SIZE_TO_PAGES (Xhc->MaxScratchpadBufs * sizeof (UINT64))); + FreePool (Xhc->ScratchEntry); + } + + if (Xhc->CmdRing.RingSeg0 != NULL) { + UsbHcFreeMem (Xhc->MemPool, Xhc->CmdRing.RingSeg0, sizeof (TRB_TEMPLATE) * CMD_RING_TRB_NUMBER); + Xhc->CmdRing.RingSeg0 = NULL; + } + + XhcPeiFreeEventRing (Xhc,&Xhc->EventRing); + + if (Xhc->DCBAA != NULL) { + UsbHcFreeMem (Xhc->MemPool, Xhc->DCBAA, (Xhc->MaxSlotsEn + 1) * sizeof (UINT64)); + Xhc->DCBAA = NULL; + } + + // + // Free memory pool at last + // + if (Xhc->MemPool != NULL) { + UsbHcFreeMemPool (Xhc->MemPool); + Xhc->MemPool = NULL; + } +} + diff --git a/MdeModulePkg/Bus/Pci/XhciPei/XhciSched.h b/MdeModulePkg/Bus/Pci/XhciPei/XhciSched.h new file mode 100644 index 0000000000..19672d046a --- /dev/null +++ b/MdeModulePkg/Bus/Pci/XhciPei/XhciSched.h @@ -0,0 +1,1228 @@ +/** @file +Private Header file for Usb Host Controller PEIM + +Copyright (c) 2014, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions +of the BSD License which accompanies this distribution. The +full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _EFI_PEI_XHCI_SCHED_H_ +#define _EFI_PEI_XHCI_SCHED_H_ + +// +// Transfer types, used in URB to identify the transfer type +// +#define XHC_CTRL_TRANSFER 0x01 +#define XHC_BULK_TRANSFER 0x02 + +// +// 6.4.6 TRB Types +// +#define TRB_TYPE_NORMAL 1 +#define TRB_TYPE_SETUP_STAGE 2 +#define TRB_TYPE_DATA_STAGE 3 +#define TRB_TYPE_STATUS_STAGE 4 +#define TRB_TYPE_ISOCH 5 +#define TRB_TYPE_LINK 6 +#define TRB_TYPE_EVENT_DATA 7 +#define TRB_TYPE_NO_OP 8 +#define TRB_TYPE_EN_SLOT 9 +#define TRB_TYPE_DIS_SLOT 10 +#define TRB_TYPE_ADDRESS_DEV 11 +#define TRB_TYPE_CON_ENDPOINT 12 +#define TRB_TYPE_EVALU_CONTXT 13 +#define TRB_TYPE_RESET_ENDPOINT 14 +#define TRB_TYPE_STOP_ENDPOINT 15 +#define TRB_TYPE_SET_TR_DEQUE 16 +#define TRB_TYPE_RESET_DEV 17 +#define TRB_TYPE_GET_PORT_BANW 21 +#define TRB_TYPE_FORCE_HEADER 22 +#define TRB_TYPE_NO_OP_COMMAND 23 +#define TRB_TYPE_TRANS_EVENT 32 +#define TRB_TYPE_COMMAND_COMPLT_EVENT 33 +#define TRB_TYPE_PORT_STATUS_CHANGE_EVENT 34 +#define TRB_TYPE_HOST_CONTROLLER_EVENT 37 +#define TRB_TYPE_DEVICE_NOTIFI_EVENT 38 +#define TRB_TYPE_MFINDEX_WRAP_EVENT 39 + +// +// Endpoint Type (EP Type). +// +#define ED_NOT_VALID 0 +#define ED_ISOCH_OUT 1 +#define ED_BULK_OUT 2 +#define ED_INTERRUPT_OUT 3 +#define ED_CONTROL_BIDIR 4 +#define ED_ISOCH_IN 5 +#define ED_BULK_IN 6 +#define ED_INTERRUPT_IN 7 + +// +// 6.4.5 TRB Completion Codes +// +#define TRB_COMPLETION_INVALID 0 +#define TRB_COMPLETION_SUCCESS 1 +#define TRB_COMPLETION_DATA_BUFFER_ERROR 2 +#define TRB_COMPLETION_BABBLE_ERROR 3 +#define TRB_COMPLETION_USB_TRANSACTION_ERROR 4 +#define TRB_COMPLETION_TRB_ERROR 5 +#define TRB_COMPLETION_STALL_ERROR 6 +#define TRB_COMPLETION_SHORT_PACKET 13 + +// +// The topology string used to present usb device location +// +typedef struct _USB_DEV_TOPOLOGY { + // + // The tier concatenation of down stream port. + // + UINT32 RouteString:20; + // + // The root port number of the chain. + // + UINT32 RootPortNum:8; + // + // The Tier the device reside. + // + UINT32 TierNum:4; +} USB_DEV_TOPOLOGY; + +// +// USB Device's RouteChart +// +typedef union _USB_DEV_ROUTE { + UINT32 Dword; + USB_DEV_TOPOLOGY Route; +} USB_DEV_ROUTE; + +// +// Endpoint address and its capabilities +// +typedef struct _USB_ENDPOINT { + // + // Store logical device address assigned by UsbBus + // It's because some XHCI host controllers may assign the same physcial device + // address for those devices inserted at different root port. + // + UINT8 BusAddr; + UINT8 DevAddr; + UINT8 EpAddr; + EFI_USB_DATA_DIRECTION Direction; + UINT8 DevSpeed; + UINTN MaxPacket; + UINTN Type; +} USB_ENDPOINT; + +// +// TRB Template +// +typedef struct _TRB_TEMPLATE { + UINT32 Parameter1; + + UINT32 Parameter2; + + UINT32 Status; + + UINT32 CycleBit:1; + UINT32 RsvdZ1:9; + UINT32 Type:6; + UINT32 Control:16; +} TRB_TEMPLATE; + +typedef struct _TRANSFER_RING { + VOID *RingSeg0; + UINTN TrbNumber; + TRB_TEMPLATE *RingEnqueue; + TRB_TEMPLATE *RingDequeue; + UINT32 RingPCS; +} TRANSFER_RING; + +typedef struct _EVENT_RING { + VOID *ERSTBase; + VOID *EventRingSeg0; + UINTN TrbNumber; + TRB_TEMPLATE *EventRingEnqueue; + TRB_TEMPLATE *EventRingDequeue; + UINT32 EventRingCCS; +} EVENT_RING; + +#define XHC_URB_SIG SIGNATURE_32 ('U', 'S', 'B', 'R') + +// +// URB (Usb Request Block) contains information for all kinds of +// usb requests. +// +typedef struct _URB { + UINT32 Signature; + // + // Usb Device URB related information + // + USB_ENDPOINT Ep; + EFI_USB_DEVICE_REQUEST *Request; + VOID *Data; + UINTN DataLen; + VOID *DataPhy; + EFI_ASYNC_USB_TRANSFER_CALLBACK Callback; + VOID *Context; + // + // Execute result + // + UINT32 Result; + // + // completed data length + // + UINTN Completed; + // + // Command/Tranfer Ring info + // + TRANSFER_RING *Ring; + TRB_TEMPLATE *TrbStart; + TRB_TEMPLATE *TrbEnd; + UINTN TrbNum; + BOOLEAN StartDone; + BOOLEAN EndDone; + BOOLEAN Finished; + + TRB_TEMPLATE *EvtTrb; +} URB; + +// +// 6.5 Event Ring Segment Table +// The Event Ring Segment Table is used to define multi-segment Event Rings and to enable runtime +// expansion and shrinking of the Event Ring. The location of the Event Ring Segment Table is defined by the +// Event Ring Segment Table Base Address Register (5.5.2.3.2). The size of the Event Ring Segment Table +// is defined by the Event Ring Segment Table Base Size Register (5.5.2.3.1). +// +typedef struct _EVENT_RING_SEG_TABLE_ENTRY { + UINT32 PtrLo; + UINT32 PtrHi; + UINT32 RingTrbSize:16; + UINT32 RsvdZ1:16; + UINT32 RsvdZ2; +} EVENT_RING_SEG_TABLE_ENTRY; + +// +// 6.4.1.1 Normal TRB +// A Normal TRB is used in several ways; exclusively on Bulk and Interrupt Transfer Rings for normal and +// Scatter/Gather operations, to define additional data buffers for Scatter/Gather operations on Isoch Transfer +// Rings, and to define the Data stage information for Control Transfer Rings. +// +typedef struct _TRANSFER_TRB_NORMAL { + UINT32 TRBPtrLo; + + UINT32 TRBPtrHi; + + UINT32 Length:17; + UINT32 TDSize:5; + UINT32 IntTarget:10; + + UINT32 CycleBit:1; + UINT32 ENT:1; + UINT32 ISP:1; + UINT32 NS:1; + UINT32 CH:1; + UINT32 IOC:1; + UINT32 IDT:1; + UINT32 RsvdZ1:2; + UINT32 BEI:1; + UINT32 Type:6; + UINT32 RsvdZ2:16; +} TRANSFER_TRB_NORMAL; + +// +// 6.4.1.2.1 Setup Stage TRB +// A Setup Stage TRB is created by system software to initiate a USB Setup packet on a control endpoint. +// +typedef struct _TRANSFER_TRB_CONTROL_SETUP { + UINT32 bmRequestType:8; + UINT32 bRequest:8; + UINT32 wValue:16; + + UINT32 wIndex:16; + UINT32 wLength:16; + + UINT32 Length:17; + UINT32 RsvdZ1:5; + UINT32 IntTarget:10; + + UINT32 CycleBit:1; + UINT32 RsvdZ2:4; + UINT32 IOC:1; + UINT32 IDT:1; + UINT32 RsvdZ3:3; + UINT32 Type:6; + UINT32 TRT:2; + UINT32 RsvdZ4:14; +} TRANSFER_TRB_CONTROL_SETUP; + +// +// 6.4.1.2.2 Data Stage TRB +// A Data Stage TRB is used generate the Data stage transaction of a USB Control transfer. +// +typedef struct _TRANSFER_TRB_CONTROL_DATA { + UINT32 TRBPtrLo; + + UINT32 TRBPtrHi; + + UINT32 Length:17; + UINT32 TDSize:5; + UINT32 IntTarget:10; + + UINT32 CycleBit:1; + UINT32 ENT:1; + UINT32 ISP:1; + UINT32 NS:1; + UINT32 CH:1; + UINT32 IOC:1; + UINT32 IDT:1; + UINT32 RsvdZ1:3; + UINT32 Type:6; + UINT32 DIR:1; + UINT32 RsvdZ2:15; +} TRANSFER_TRB_CONTROL_DATA; + +// +// 6.4.1.2.2 Data Stage TRB +// A Data Stage TRB is used generate the Data stage transaction of a USB Control transfer. +// +typedef struct _TRANSFER_TRB_CONTROL_STATUS { + UINT32 RsvdZ1; + UINT32 RsvdZ2; + + UINT32 RsvdZ3:22; + UINT32 IntTarget:10; + + UINT32 CycleBit:1; + UINT32 ENT:1; + UINT32 RsvdZ4:2; + UINT32 CH:1; + UINT32 IOC:1; + UINT32 RsvdZ5:4; + UINT32 Type:6; + UINT32 DIR:1; + UINT32 RsvdZ6:15; +} TRANSFER_TRB_CONTROL_STATUS; + +// +// 6.4.2.1 Transfer Event TRB +// A Transfer Event provides the completion status associated with a Transfer TRB. Refer to section 4.11.3.1 +// for more information on the use and operation of Transfer Events. +// +typedef struct _EVT_TRB_TRANSFER { + UINT32 TRBPtrLo; + + UINT32 TRBPtrHi; + + UINT32 Length:24; + UINT32 Completecode:8; + + UINT32 CycleBit:1; + UINT32 RsvdZ1:1; + UINT32 ED:1; + UINT32 RsvdZ2:7; + UINT32 Type:6; + UINT32 EndpointId:5; + UINT32 RsvdZ3:3; + UINT32 SlotId:8; +} EVT_TRB_TRANSFER; + +// +// 6.4.2.2 Command Completion Event TRB +// A Command Completion Event TRB shall be generated by the xHC when a command completes on the +// Command Ring. Refer to section 4.11.4 for more information on the use of Command Completion Events. +// +typedef struct _EVT_TRB_COMMAND_COMPLETION { + UINT32 TRBPtrLo; + + UINT32 TRBPtrHi; + + UINT32 RsvdZ2:24; + UINT32 Completecode:8; + + UINT32 CycleBit:1; + UINT32 RsvdZ3:9; + UINT32 Type:6; + UINT32 VFID:8; + UINT32 SlotId:8; +} EVT_TRB_COMMAND_COMPLETION; + +typedef union _TRB { + TRB_TEMPLATE TrbTemplate; + TRANSFER_TRB_NORMAL TrbNormal; + TRANSFER_TRB_CONTROL_SETUP TrbCtrSetup; + TRANSFER_TRB_CONTROL_DATA TrbCtrData; + TRANSFER_TRB_CONTROL_STATUS TrbCtrStatus; +} TRB; + +// +// 6.4.3.1 No Op Command TRB +// The No Op Command TRB provides a simple means for verifying the operation of the Command Ring +// mechanisms offered by the xHCI. +// +typedef struct _CMD_TRB_NO_OP { + UINT32 RsvdZ0; + UINT32 RsvdZ1; + UINT32 RsvdZ2; + + UINT32 CycleBit:1; + UINT32 RsvdZ3:9; + UINT32 Type:6; + UINT32 RsvdZ4:16; +} CMD_TRB_NO_OP; + +// +// 6.4.3.2 Enable Slot Command TRB +// The Enable Slot Command TRB causes the xHC to select an available Device Slot and return the ID of the +// selected slot to the host in a Command Completion Event. +// +typedef struct _CMD_TRB_ENABLE_SLOT { + UINT32 RsvdZ0; + UINT32 RsvdZ1; + UINT32 RsvdZ2; + + UINT32 CycleBit:1; + UINT32 RsvdZ3:9; + UINT32 Type:6; + UINT32 RsvdZ4:16; +} CMD_TRB_ENABLE_SLOT; + +// +// 6.4.3.3 Disable Slot Command TRB +// The Disable Slot Command TRB releases any bandwidth assigned to the disabled slot and frees any +// internal xHC resources assigned to the slot. +// +typedef struct _CMD_TRB_DISABLE_SLOT { + UINT32 RsvdZ0; + UINT32 RsvdZ1; + UINT32 RsvdZ2; + + UINT32 CycleBit:1; + UINT32 RsvdZ3:9; + UINT32 Type:6; + UINT32 RsvdZ4:8; + UINT32 SlotId:8; +} CMD_TRB_DISABLE_SLOT; + +// +// 6.4.3.4 Address Device Command TRB +// The Address Device Command TRB transitions the selected Device Context from the Default to the +// Addressed state and causes the xHC to select an address for the USB device in the Default State and +// issue a SET_ADDRESS request to the USB device. +// +typedef struct _CMD_TRB_ADDRESS_DEVICE { + UINT32 PtrLo; + + UINT32 PtrHi; + + UINT32 RsvdZ1; + + UINT32 CycleBit:1; + UINT32 RsvdZ2:8; + UINT32 BSR:1; + UINT32 Type:6; + UINT32 RsvdZ3:8; + UINT32 SlotId:8; +} CMD_TRB_ADDRESS_DEVICE; + +// +// 6.4.3.5 Configure Endpoint Command TRB +// The Configure Endpoint Command TRB evaluates the bandwidth and resource requirements of the +// endpoints selected by the command. +// +typedef struct _CMD_TRB_CONFIG_ENDPOINT { + UINT32 PtrLo; + + UINT32 PtrHi; + + UINT32 RsvdZ1; + + UINT32 CycleBit:1; + UINT32 RsvdZ2:8; + UINT32 DC:1; + UINT32 Type:6; + UINT32 RsvdZ3:8; + UINT32 SlotId:8; +} CMD_TRB_CONFIG_ENDPOINT; + +// +// 6.4.3.6 Evaluate Context Command TRB +// The Evaluate Context Command TRB is used by system software to inform the xHC that the selected +// Context data structures in the Device Context have been modified by system software and that the xHC +// shall evaluate any changes +// +typedef struct _CMD_TRB_EVALUATE_CONTEXT { + UINT32 PtrLo; + + UINT32 PtrHi; + + UINT32 RsvdZ1; + + UINT32 CycleBit:1; + UINT32 RsvdZ2:9; + UINT32 Type:6; + UINT32 RsvdZ3:8; + UINT32 SlotId:8; +} CMD_TRB_EVALUATE_CONTEXT; + +// +// 6.4.3.7 Reset Endpoint Command TRB +// The Reset Endpoint Command TRB is used by system software to reset a specified Transfer Ring +// +typedef struct _CMD_TRB_RESET_ENDPOINT { + UINT32 RsvdZ0; + UINT32 RsvdZ1; + UINT32 RsvdZ2; + + UINT32 CycleBit:1; + UINT32 RsvdZ3:8; + UINT32 TSP:1; + UINT32 Type:6; + UINT32 EDID:5; + UINT32 RsvdZ4:3; + UINT32 SlotId:8; +} CMD_TRB_RESET_ENDPOINT; + +// +// 6.4.3.8 Stop Endpoint Command TRB +// The Stop Endpoint Command TRB command allows software to stop the xHC execution of the TDs on a +// Transfer Ring and temporarily take ownership of TDs that had previously been passed to the xHC. +// +typedef struct _CMD_TRB_STOP_ENDPOINT { + UINT32 RsvdZ0; + UINT32 RsvdZ1; + UINT32 RsvdZ2; + + UINT32 CycleBit:1; + UINT32 RsvdZ3:9; + UINT32 Type:6; + UINT32 EDID:5; + UINT32 RsvdZ4:2; + UINT32 SP:1; + UINT32 SlotId:8; +} CMD_TRB_STOP_ENDPOINT; + +// +// 6.4.3.9 Set TR Dequeue Pointer Command TRB +// The Set TR Dequeue Pointer Command TRB is used by system software to modify the TR Dequeue +// Pointer and DCS fields of an Endpoint or Stream Context. +// +typedef struct _CMD_SET_TR_DEQ_POINTER { + UINT32 PtrLo; + + UINT32 PtrHi; + + UINT32 RsvdZ1:16; + UINT32 StreamID:16; + + UINT32 CycleBit:1; + UINT32 RsvdZ2:9; + UINT32 Type:6; + UINT32 Endpoint:5; + UINT32 RsvdZ3:3; + UINT32 SlotId:8; +} CMD_SET_TR_DEQ_POINTER; + +// +// 6.4.4.1 Link TRB +// A Link TRB provides support for non-contiguous TRB Rings. +// +typedef struct _LINK_TRB { + UINT32 PtrLo; + + UINT32 PtrHi; + + UINT32 RsvdZ1:22; + UINT32 InterTarget:10; + + UINT32 CycleBit:1; + UINT32 TC:1; + UINT32 RsvdZ2:2; + UINT32 CH:1; + UINT32 IOC:1; + UINT32 RsvdZ3:4; + UINT32 Type:6; + UINT32 RsvdZ4:16; +} LINK_TRB; + +// +// 6.2.2 Slot Context +// +typedef struct _SLOT_CONTEXT { + UINT32 RouteString:20; + UINT32 Speed:4; + UINT32 RsvdZ1:1; + UINT32 MTT:1; + UINT32 Hub:1; + UINT32 ContextEntries:5; + + UINT32 MaxExitLatency:16; + UINT32 RootHubPortNum:8; + UINT32 PortNum:8; + + UINT32 TTHubSlotId:8; + UINT32 TTPortNum:8; + UINT32 TTT:2; + UINT32 RsvdZ2:4; + UINT32 InterTarget:10; + + UINT32 DeviceAddress:8; + UINT32 RsvdZ3:19; + UINT32 SlotState:5; + + UINT32 RsvdZ4; + UINT32 RsvdZ5; + UINT32 RsvdZ6; + UINT32 RsvdZ7; +} SLOT_CONTEXT; + +typedef struct _SLOT_CONTEXT_64 { + UINT32 RouteString:20; + UINT32 Speed:4; + UINT32 RsvdZ1:1; + UINT32 MTT:1; + UINT32 Hub:1; + UINT32 ContextEntries:5; + + UINT32 MaxExitLatency:16; + UINT32 RootHubPortNum:8; + UINT32 PortNum:8; + + UINT32 TTHubSlotId:8; + UINT32 TTPortNum:8; + UINT32 TTT:2; + UINT32 RsvdZ2:4; + UINT32 InterTarget:10; + + UINT32 DeviceAddress:8; + UINT32 RsvdZ3:19; + UINT32 SlotState:5; + + UINT32 RsvdZ4; + UINT32 RsvdZ5; + UINT32 RsvdZ6; + UINT32 RsvdZ7; + + UINT32 RsvdZ8; + UINT32 RsvdZ9; + UINT32 RsvdZ10; + UINT32 RsvdZ11; + + UINT32 RsvdZ12; + UINT32 RsvdZ13; + UINT32 RsvdZ14; + UINT32 RsvdZ15; + +} SLOT_CONTEXT_64; + + +// +// 6.2.3 Endpoint Context +// +typedef struct _ENDPOINT_CONTEXT { + UINT32 EPState:3; + UINT32 RsvdZ1:5; + UINT32 Mult:2; + UINT32 MaxPStreams:5; + UINT32 LSA:1; + UINT32 Interval:8; + UINT32 RsvdZ2:8; + + UINT32 RsvdZ3:1; + UINT32 CErr:2; + UINT32 EPType:3; + UINT32 RsvdZ4:1; + UINT32 HID:1; + UINT32 MaxBurstSize:8; + UINT32 MaxPacketSize:16; + + UINT32 PtrLo; + + UINT32 PtrHi; + + UINT32 AverageTRBLength:16; + UINT32 MaxESITPayload:16; + + UINT32 RsvdZ5; + UINT32 RsvdZ6; + UINT32 RsvdZ7; +} ENDPOINT_CONTEXT; + +typedef struct _ENDPOINT_CONTEXT_64 { + UINT32 EPState:3; + UINT32 RsvdZ1:5; + UINT32 Mult:2; + UINT32 MaxPStreams:5; + UINT32 LSA:1; + UINT32 Interval:8; + UINT32 RsvdZ2:8; + + UINT32 RsvdZ3:1; + UINT32 CErr:2; + UINT32 EPType:3; + UINT32 RsvdZ4:1; + UINT32 HID:1; + UINT32 MaxBurstSize:8; + UINT32 MaxPacketSize:16; + + UINT32 PtrLo; + + UINT32 PtrHi; + + UINT32 AverageTRBLength:16; + UINT32 MaxESITPayload:16; + + UINT32 RsvdZ5; + UINT32 RsvdZ6; + UINT32 RsvdZ7; + + UINT32 RsvdZ8; + UINT32 RsvdZ9; + UINT32 RsvdZ10; + UINT32 RsvdZ11; + + UINT32 RsvdZ12; + UINT32 RsvdZ13; + UINT32 RsvdZ14; + UINT32 RsvdZ15; + +} ENDPOINT_CONTEXT_64; + + +// +// 6.2.5.1 Input Control Context +// +typedef struct _INPUT_CONTRL_CONTEXT { + UINT32 Dword1; + UINT32 Dword2; + UINT32 RsvdZ1; + UINT32 RsvdZ2; + UINT32 RsvdZ3; + UINT32 RsvdZ4; + UINT32 RsvdZ5; + UINT32 RsvdZ6; +} INPUT_CONTRL_CONTEXT; + +typedef struct _INPUT_CONTRL_CONTEXT_64 { + UINT32 Dword1; + UINT32 Dword2; + UINT32 RsvdZ1; + UINT32 RsvdZ2; + UINT32 RsvdZ3; + UINT32 RsvdZ4; + UINT32 RsvdZ5; + UINT32 RsvdZ6; + UINT32 RsvdZ7; + UINT32 RsvdZ8; + UINT32 RsvdZ9; + UINT32 RsvdZ10; + UINT32 RsvdZ11; + UINT32 RsvdZ12; + UINT32 RsvdZ13; + UINT32 RsvdZ14; +} INPUT_CONTRL_CONTEXT_64; + +// +// 6.2.1 Device Context +// +typedef struct _DEVICE_CONTEXT { + SLOT_CONTEXT Slot; + ENDPOINT_CONTEXT EP[31]; +} DEVICE_CONTEXT; + +typedef struct _DEVICE_CONTEXT_64 { + SLOT_CONTEXT_64 Slot; + ENDPOINT_CONTEXT_64 EP[31]; +} DEVICE_CONTEXT_64; + +// +// 6.2.5 Input Context +// +typedef struct _INPUT_CONTEXT { + INPUT_CONTRL_CONTEXT InputControlContext; + SLOT_CONTEXT Slot; + ENDPOINT_CONTEXT EP[31]; +} INPUT_CONTEXT; + +typedef struct _INPUT_CONTEXT_64 { + INPUT_CONTRL_CONTEXT_64 InputControlContext; + SLOT_CONTEXT_64 Slot; + ENDPOINT_CONTEXT_64 EP[31]; +} INPUT_CONTEXT_64; + +/** + Execute the transfer by polling the URB. This is a synchronous operation. + + @param Xhc The XHCI device. + @param CmdTransfer The executed URB is for cmd transfer or not. + @param Urb The URB to execute. + @param Timeout The time to wait before abort, in millisecond. + + @return EFI_DEVICE_ERROR The transfer failed due to transfer error. + @return EFI_TIMEOUT The transfer failed due to time out. + @return EFI_SUCCESS The transfer finished OK. + +**/ +EFI_STATUS +XhcPeiExecTransfer ( + IN PEI_XHC_DEV *Xhc, + IN BOOLEAN CmdTransfer, + IN URB *Urb, + IN UINTN Timeout + ); + +/** + Find out the actual device address according to the requested device address from UsbBus. + + @param Xhc The XHCI device. + @param BusDevAddr The requested device address by UsbBus upper driver. + + @return The actual device address assigned to the device. + +**/ +UINT8 +XhcPeiBusDevAddrToSlotId ( + IN PEI_XHC_DEV *Xhc, + IN UINT8 BusDevAddr + ); + +/** + Find out the slot id according to the device's route string. + + @param Xhc The XHCI device. + @param RouteString The route string described the device location. + + @return The slot id used by the device. + +**/ +UINT8 +XhcPeiRouteStringToSlotId ( + IN PEI_XHC_DEV *Xhc, + IN USB_DEV_ROUTE RouteString + ); + +/** + Calculate the device context index by endpoint address and direction. + + @param EpAddr The target endpoint number. + @param Direction The direction of the target endpoint. + + @return The device context index of endpoint. + +**/ +UINT8 +XhcPeiEndpointToDci ( + IN UINT8 EpAddr, + IN EFI_USB_DATA_DIRECTION Direction + ); + +/** + Ring the door bell to notify XHCI there is a transaction to be executed. + + @param Xhc The XHCI device. + @param SlotId The slot id of the target device. + @param Dci The device context index of the target slot or endpoint. + +**/ +VOID +XhcPeiRingDoorBell ( + IN PEI_XHC_DEV *Xhc, + IN UINT8 SlotId, + IN UINT8 Dci + ); + +/** + Monitor the port status change. Enable/Disable device slot if there is a device attached/detached. + + @param Xhc The XHCI device. + @param ParentRouteChart The route string pointed to the parent device if it exists. + @param Port The port to be polled. + @param PortState The port state. + + @retval EFI_SUCCESS Successfully enable/disable device slot according to port state. + @retval Others Should not appear. + +**/ +EFI_STATUS +XhcPeiPollPortStatusChange ( + IN PEI_XHC_DEV *Xhc, + IN USB_DEV_ROUTE ParentRouteChart, + IN UINT8 Port, + IN EFI_USB_PORT_STATUS *PortState + ); + +/** + Evaluate the slot context for hub device through XHCI's Configure_Endpoint cmd. + + @param Xhc The XHCI device. + @param SlotId The slot id to be configured. + @param PortNum The total number of downstream port supported by the hub. + @param TTT The TT think time of the hub device. + @param MTT The multi-TT of the hub device. + + @retval EFI_SUCCESS Successfully configure the hub device's slot context. + +**/ +EFI_STATUS +XhcPeiConfigHubContext ( + IN PEI_XHC_DEV *Xhc, + IN UINT8 SlotId, + IN UINT8 PortNum, + IN UINT8 TTT, + IN UINT8 MTT + ); + +/** + Evaluate the slot context for hub device through XHCI's Configure_Endpoint cmd. + + @param Xhc The XHCI device. + @param SlotId The slot id to be configured. + @param PortNum The total number of downstream port supported by the hub. + @param TTT The TT think time of the hub device. + @param MTT The multi-TT of the hub device. + + @retval EFI_SUCCESS Successfully configure the hub device's slot context. + +**/ +EFI_STATUS +XhcPeiConfigHubContext64 ( + IN PEI_XHC_DEV *Xhc, + IN UINT8 SlotId, + IN UINT8 PortNum, + IN UINT8 TTT, + IN UINT8 MTT + ); + +/** + Configure all the device endpoints through XHCI's Configure_Endpoint cmd. + + @param Xhc The XHCI device. + @param SlotId The slot id to be configured. + @param DeviceSpeed The device's speed. + @param ConfigDesc The pointer to the usb device configuration descriptor. + + @retval EFI_SUCCESS Successfully configure all the device endpoints. + +**/ +EFI_STATUS +XhcPeiSetConfigCmd ( + IN PEI_XHC_DEV *Xhc, + IN UINT8 SlotId, + IN UINT8 DeviceSpeed, + IN USB_CONFIG_DESCRIPTOR *ConfigDesc + ); + +/** + Configure all the device endpoints through XHCI's Configure_Endpoint cmd. + + @param Xhc The XHCI device. + @param SlotId The slot id to be configured. + @param DeviceSpeed The device's speed. + @param ConfigDesc The pointer to the usb device configuration descriptor. + + @retval EFI_SUCCESS Successfully configure all the device endpoints. + +**/ +EFI_STATUS +XhcPeiSetConfigCmd64 ( + IN PEI_XHC_DEV *Xhc, + IN UINT8 SlotId, + IN UINT8 DeviceSpeed, + IN USB_CONFIG_DESCRIPTOR *ConfigDesc + ); + +/** + Assign and initialize the device slot for a new device. + + @param Xhc The XHCI device. + @param ParentRouteChart The route string pointed to the parent device. + @param ParentPort The port at which the device is located. + @param RouteChart The route string pointed to the device. + @param DeviceSpeed The device speed. + + @retval EFI_SUCCESS Successfully assign a slot to the device and assign an address to it. + @retval Others Fail to initialize device slot. + +**/ +EFI_STATUS +XhcPeiInitializeDeviceSlot ( + IN PEI_XHC_DEV *Xhc, + IN USB_DEV_ROUTE ParentRouteChart, + IN UINT16 ParentPort, + IN USB_DEV_ROUTE RouteChart, + IN UINT8 DeviceSpeed + ); + +/** + Assign and initialize the device slot for a new device. + + @param Xhc The XHCI device. + @param ParentRouteChart The route string pointed to the parent device. + @param ParentPort The port at which the device is located. + @param RouteChart The route string pointed to the device. + @param DeviceSpeed The device speed. + + @retval EFI_SUCCESS Successfully assign a slot to the device and assign an address to it. + @retval Others Fail to initialize device slot. + +**/ +EFI_STATUS +XhcPeiInitializeDeviceSlot64 ( + IN PEI_XHC_DEV *Xhc, + IN USB_DEV_ROUTE ParentRouteChart, + IN UINT16 ParentPort, + IN USB_DEV_ROUTE RouteChart, + IN UINT8 DeviceSpeed + ); + +/** + Evaluate the endpoint 0 context through XHCI's Evaluate_Context cmd. + + @param Xhc The XHCI device. + @param SlotId The slot id to be evaluated. + @param MaxPacketSize The max packet size supported by the device control transfer. + + @retval EFI_SUCCESS Successfully evaluate the device endpoint 0. + +**/ +EFI_STATUS +XhcPeiEvaluateContext ( + IN PEI_XHC_DEV *Xhc, + IN UINT8 SlotId, + IN UINT32 MaxPacketSize + ); + +/** + Evaluate the endpoint 0 context through XHCI's Evaluate_Context cmd. + + @param Xhc The XHCI device. + @param SlotId The slot id to be evaluated. + @param MaxPacketSize The max packet size supported by the device control transfer. + + @retval EFI_SUCCESS Successfully evaluate the device endpoint 0. + +**/ +EFI_STATUS +XhcPeiEvaluateContext64 ( + IN PEI_XHC_DEV *Xhc, + IN UINT8 SlotId, + IN UINT32 MaxPacketSize + ); + +/** + Disable the specified device slot. + + @param Xhc The XHCI device. + @param SlotId The slot id to be disabled. + + @retval EFI_SUCCESS Successfully disable the device slot. + +**/ +EFI_STATUS +XhcPeiDisableSlotCmd ( + IN PEI_XHC_DEV *Xhc, + IN UINT8 SlotId + ); + +/** + Disable the specified device slot. + + @param Xhc The XHCI device. + @param SlotId The slot id to be disabled. + + @retval EFI_SUCCESS Successfully disable the device slot. + +**/ +EFI_STATUS +XhcPeiDisableSlotCmd64 ( + IN PEI_XHC_DEV *Xhc, + IN UINT8 SlotId + ); + +/** + System software shall use a Reset Endpoint Command (section 4.11.4.7) to remove the Halted + condition in the xHC. After the successful completion of the Reset Endpoint Command, the Endpoint + Context is transitioned from the Halted to the Stopped state and the Transfer Ring of the endpoint is + reenabled. The next write to the Doorbell of the Endpoint will transition the Endpoint Context from the + Stopped to the Running state. + + @param Xhc The XHCI device. + @param Urb The urb which makes the endpoint halted. + + @retval EFI_SUCCESS The recovery is successful. + @retval Others Failed to recovery halted endpoint. + +**/ +EFI_STATUS +XhcPeiRecoverHaltedEndpoint ( + IN PEI_XHC_DEV *Xhc, + IN URB *Urb + ); + +/** + Create a new URB for a new transaction. + + @param Xhc The XHCI device + @param DevAddr The device address + @param EpAddr Endpoint addrress + @param DevSpeed The device speed + @param MaxPacket The max packet length of the endpoint + @param Type The transaction type + @param Request The standard USB request for control transfer + @param Data The user data to transfer + @param DataLen The length of data buffer + @param Callback The function to call when data is transferred + @param Context The context to the callback + + @return Created URB or NULL + +**/ +URB* +XhcPeiCreateUrb ( + IN PEI_XHC_DEV *Xhc, + IN UINT8 DevAddr, + IN UINT8 EpAddr, + IN UINT8 DevSpeed, + IN UINTN MaxPacket, + IN UINTN Type, + IN EFI_USB_DEVICE_REQUEST *Request, + IN VOID *Data, + IN UINTN DataLen, + IN EFI_ASYNC_USB_TRANSFER_CALLBACK Callback, + IN VOID *Context + ); + +/** + Free an allocated URB. + + @param Xhc The XHCI device. + @param Urb The URB to free. + +**/ +VOID +XhcPeiFreeUrb ( + IN PEI_XHC_DEV *Xhc, + IN URB *Urb + ); + +/** + Create a transfer TRB. + + @param Xhc The XHCI device + @param Urb The urb used to construct the transfer TRB. + + @return Created TRB or NULL + +**/ +EFI_STATUS +XhcPeiCreateTransferTrb ( + IN PEI_XHC_DEV *Xhc, + IN URB *Urb + ); + +/** + Synchronize the specified transfer ring to update the enqueue and dequeue pointer. + + @param Xhc The XHCI device. + @param TrsRing The transfer ring to sync. + + @retval EFI_SUCCESS The transfer ring is synchronized successfully. + +**/ +EFI_STATUS +XhcPeiSyncTrsRing ( + IN PEI_XHC_DEV *Xhc, + IN TRANSFER_RING *TrsRing + ); + +/** + Create XHCI transfer ring. + + @param Xhc The XHCI Device. + @param TrbNum The number of TRB in the ring. + @param TransferRing The created transfer ring. + +**/ +VOID +XhcPeiCreateTransferRing ( + IN PEI_XHC_DEV *Xhc, + IN UINTN TrbNum, + OUT TRANSFER_RING *TransferRing + ); + +/** + Check if there is a new generated event. + + @param Xhc The XHCI device. + @param EvtRing The event ring to check. + @param NewEvtTrb The new event TRB found. + + @retval EFI_SUCCESS Found a new event TRB at the event ring. + @retval EFI_NOT_READY The event ring has no new event. + +**/ +EFI_STATUS +XhcPeiCheckNewEvent ( + IN PEI_XHC_DEV *Xhc, + IN EVENT_RING *EvtRing, + OUT TRB_TEMPLATE **NewEvtTrb + ); + +/** + Synchronize the specified event ring to update the enqueue and dequeue pointer. + + @param Xhc The XHCI device. + @param EvtRing The event ring to sync. + + @retval EFI_SUCCESS The event ring is synchronized successfully. + +**/ +EFI_STATUS +XhcPeiSyncEventRing ( + IN PEI_XHC_DEV *Xhc, + IN EVENT_RING *EvtRing + ); + +/** + Create XHCI event ring. + + @param Xhc The XHCI device. + @param EventRing The created event ring. + +**/ +VOID +XhcPeiCreateEventRing ( + IN PEI_XHC_DEV *Xhc, + OUT EVENT_RING *EventRing + ); + +/** + Initialize the XHCI host controller for schedule. + + @param Xhc The XHCI device to be initialized. + +**/ +VOID +XhcPeiInitSched ( + IN PEI_XHC_DEV *Xhc + ); + +/** + Free the resouce allocated at initializing schedule. + + @param Xhc The XHCI device. + +**/ +VOID +XhcPeiFreeSched ( + IN PEI_XHC_DEV *Xhc + ); + +#endif diff --git a/MdeModulePkg/Bus/Usb/UsbBusPei/HubPeim.c b/MdeModulePkg/Bus/Usb/UsbBusPei/HubPeim.c index 5b7ebfad90..16a7b589c1 100644 --- a/MdeModulePkg/Bus/Usb/UsbBusPei/HubPeim.c +++ b/MdeModulePkg/Bus/Usb/UsbBusPei/HubPeim.c @@ -1,7 +1,7 @@ /** @file Usb Hub Request Support In PEI Phase -Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.
+Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.
This program and the accompanying materials are licensed and made available under the terms and conditions @@ -319,6 +319,139 @@ PeiGetHubDescriptor ( ); } +/** + Get a given SuperSpeed hub descriptor. + + @param PeiServices General-purpose services that are available to every PEIM. + @param UsbIoPpi Indicates the PEI_USB_IO_PPI instance. + @param HubDescriptor Caller allocated buffer to store the hub descriptor if + successfully returned. + + @retval EFI_SUCCESS Hub descriptor is obtained successfully. + @retval EFI_DEVICE_ERROR Cannot get the hub descriptor due to a hardware error. + @retval Others Other failure occurs. + +**/ +EFI_STATUS +PeiGetSuperSpeedHubDesc ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_IO_PPI *UsbIoPpi, + OUT EFI_USB_HUB_DESCRIPTOR *HubDescriptor + ) +{ + EFI_USB_DEVICE_REQUEST DevReq; + ZeroMem (&DevReq, sizeof (EFI_USB_DEVICE_REQUEST)); + + // + // Fill Device request packet + // + DevReq.RequestType = USB_RT_HUB | 0x80; + DevReq.Request = USB_HUB_GET_DESCRIPTOR; + DevReq.Value = USB_DT_SUPERSPEED_HUB << 8; + DevReq.Length = 12; + + return UsbIoPpi->UsbControlTransfer ( + PeiServices, + UsbIoPpi, + &DevReq, + EfiUsbDataIn, + PcdGet32 (PcdUsbTransferTimeoutValue), + HubDescriptor, + 12 + ); +} + +/** + Read the whole usb hub descriptor. It is necessary + to do it in two steps because hub descriptor is of + variable length. + + @param PeiServices General-purpose services that are available to every PEIM. + @param PeiUsbDevice Indicates the hub controller device. + @param UsbIoPpi Indicates the PEI_USB_IO_PPI instance. + @param HubDescriptor Caller allocated buffer to store the hub descriptor if + successfully returned. + + @retval EFI_SUCCESS Hub descriptor is obtained successfully. + @retval EFI_DEVICE_ERROR Cannot get the hub descriptor due to a hardware error. + @retval Others Other failure occurs. + +**/ +EFI_STATUS +PeiUsbHubReadDesc ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_DEVICE *PeiUsbDevice, + IN PEI_USB_IO_PPI *UsbIoPpi, + OUT EFI_USB_HUB_DESCRIPTOR *HubDescriptor + ) +{ + EFI_STATUS Status; + + if (PeiUsbDevice->DeviceSpeed == EFI_USB_SPEED_SUPER) { + // + // Get the super speed hub descriptor + // + Status = PeiGetSuperSpeedHubDesc (PeiServices, UsbIoPpi, HubDescriptor); + } else { + + // + // First get the hub descriptor length + // + Status = PeiGetHubDescriptor (PeiServices, UsbIoPpi, 2, HubDescriptor); + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Get the whole hub descriptor + // + Status = PeiGetHubDescriptor (PeiServices, UsbIoPpi, HubDescriptor->Length, HubDescriptor); + } + + return Status; +} + +/** + USB hub control transfer to set the hub depth. + + @param PeiServices General-purpose services that are available to every PEIM. + @param PeiUsbDevice Indicates the hub controller device. + @param UsbIoPpi Indicates the PEI_USB_IO_PPI instance. + + @retval EFI_SUCCESS Depth of the hub is set. + @retval Others Failed to set the depth. + +**/ +EFI_STATUS +PeiUsbHubCtrlSetHubDepth ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_DEVICE *PeiUsbDevice, + IN PEI_USB_IO_PPI *UsbIoPpi + ) +{ + EFI_USB_DEVICE_REQUEST DevReq; + ZeroMem (&DevReq, sizeof (EFI_USB_DEVICE_REQUEST)); + + // + // Fill Device request packet + // + DevReq.RequestType = USB_RT_HUB; + DevReq.Request = USB_HUB_REQ_SET_DEPTH; + DevReq.Value = PeiUsbDevice->Tier; + DevReq.Length = 0; + + return UsbIoPpi->UsbControlTransfer ( + PeiServices, + UsbIoPpi, + &DevReq, + EfiUsbNoData, + PcdGet32 (PcdUsbTransferTimeoutValue), + NULL, + 0 + ); +} + /** Configure a given hub. @@ -339,32 +472,18 @@ PeiDoHubConfig ( EFI_STATUS Status; EFI_USB_HUB_STATUS HubStatus; UINTN Index; - UINT32 PortStatus; PEI_USB_IO_PPI *UsbIoPpi; ZeroMem (&HubDescriptor, sizeof (HubDescriptor)); UsbIoPpi = &PeiUsbDevice->UsbIoPpi; // - // First get the hub descriptor length - // - Status = PeiGetHubDescriptor ( - PeiServices, - UsbIoPpi, - 2, - &HubDescriptor - ); - if (EFI_ERROR (Status)) { - return EFI_DEVICE_ERROR; - } - // - // First get the whole descriptor, then - // get the number of hub ports + // Get the hub descriptor // - Status = PeiGetHubDescriptor ( + Status = PeiUsbHubReadDesc ( PeiServices, + PeiUsbDevice, UsbIoPpi, - HubDescriptor.Length, &HubDescriptor ); if (EFI_ERROR (Status)) { @@ -373,74 +492,66 @@ PeiDoHubConfig ( PeiUsbDevice->DownStreamPortNo = HubDescriptor.NbrPorts; - Status = PeiHubGetHubStatus ( - PeiServices, - UsbIoPpi, - (UINT32 *) &HubStatus - ); - - if (EFI_ERROR (Status)) { - return EFI_DEVICE_ERROR; - } - // - // Get all hub ports status - // - for (Index = 0; Index < PeiUsbDevice->DownStreamPortNo; Index++) { - - Status = PeiHubGetPortStatus ( - PeiServices, - UsbIoPpi, - (UINT8) (Index + 1), - &PortStatus - ); - if (EFI_ERROR (Status)) { - continue; - } - } - // - // Power all the hub ports - // - for (Index = 0; Index < PeiUsbDevice->DownStreamPortNo; Index++) { - Status = PeiHubSetPortFeature ( - PeiServices, - UsbIoPpi, - (UINT8) (Index + 1), - EfiUsbPortPower - ); - if (EFI_ERROR (Status)) { - continue; - } - } - // - // Clear Hub Status Change - // - Status = PeiHubGetHubStatus ( - PeiServices, - UsbIoPpi, - (UINT32 *) &HubStatus - ); - if (EFI_ERROR (Status)) { - return EFI_DEVICE_ERROR; + if (PeiUsbDevice->DeviceSpeed == EFI_USB_SPEED_SUPER) { + DEBUG ((EFI_D_INFO, "PeiDoHubConfig: Set Hub Depth as 0x%x\n", PeiUsbDevice->Tier)); + PeiUsbHubCtrlSetHubDepth ( + PeiServices, + PeiUsbDevice, + UsbIoPpi + ); } else { // - // Hub power supply change happens + // Power all the hub ports // - if ((HubStatus.HubChangeStatus & HUB_CHANGE_LOCAL_POWER) != 0) { - PeiHubClearHubFeature ( - PeiServices, - UsbIoPpi, - C_HUB_LOCAL_POWER - ); + for (Index = 0; Index < PeiUsbDevice->DownStreamPortNo; Index++) { + Status = PeiHubSetPortFeature ( + PeiServices, + UsbIoPpi, + (UINT8) (Index + 1), + EfiUsbPortPower + ); + if (EFI_ERROR (Status)) { + DEBUG (( EFI_D_ERROR, "PeiDoHubConfig: PeiHubSetPortFeature EfiUsbPortPower failed %x\n", Index)); + continue; + } } + + DEBUG (( EFI_D_INFO, "PeiDoHubConfig: HubDescriptor.PwrOn2PwrGood: 0x%x\n", HubDescriptor.PwrOn2PwrGood)); + if (HubDescriptor.PwrOn2PwrGood > 0) { + MicroSecondDelay (HubDescriptor.PwrOn2PwrGood * USB_SET_PORT_POWER_STALL); + } + // - // Hub change overcurrent happens + // Clear Hub Status Change // - if ((HubStatus.HubChangeStatus & HUB_CHANGE_OVERCURRENT) != 0) { - PeiHubClearHubFeature ( - PeiServices, - UsbIoPpi, - C_HUB_OVER_CURRENT - ); + Status = PeiHubGetHubStatus ( + PeiServices, + UsbIoPpi, + (UINT32 *) &HubStatus + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } else { + // + // Hub power supply change happens + // + if ((HubStatus.HubChangeStatus & HUB_CHANGE_LOCAL_POWER) != 0) { + PeiHubClearHubFeature ( + PeiServices, + UsbIoPpi, + C_HUB_LOCAL_POWER + ); + } + // + // Hub change overcurrent happens + // + if ((HubStatus.HubChangeStatus & HUB_CHANGE_OVERCURRENT) != 0) { + PeiHubClearHubFeature ( + PeiServices, + UsbIoPpi, + C_HUB_OVER_CURRENT + ); + } } } @@ -462,10 +573,10 @@ PeiResetHubPort ( IN UINT8 PortNum ) { - UINT8 Try; + EFI_STATUS Status; + UINTN Index; EFI_USB_PORT_STATUS HubPortStatus; - MicroSecondDelay (100 * 1000); // @@ -478,27 +589,49 @@ PeiResetHubPort ( EfiUsbPortReset ); - Try = 10; - do { - PeiHubGetPortStatus ( - PeiServices, - UsbIoPpi, - PortNum, - (UINT32 *) &HubPortStatus - ); + // + // Drive the reset signal for worst 20ms. Check USB 2.0 Spec + // section 7.1.7.5 for timing requirements. + // + MicroSecondDelay (USB_SET_PORT_RESET_STALL); - MicroSecondDelay (2 * 1000); - Try -= 1; - } while ((HubPortStatus.PortChangeStatus & USB_PORT_STAT_C_RESET) == 0 && Try > 0); + // + // Check USB_PORT_STAT_C_RESET bit to see if the resetting state is done. + // + ZeroMem (&HubPortStatus, sizeof (EFI_USB_PORT_STATUS)); + + for (Index = 0; Index < USB_WAIT_PORT_STS_CHANGE_LOOP; Index++) { + Status = PeiHubGetPortStatus ( + PeiServices, + UsbIoPpi, + PortNum, + (UINT32 *) &HubPortStatus + ); + + if (EFI_ERROR (Status)) { + return; + } + + if (USB_BIT_IS_SET (HubPortStatus.PortChangeStatus, USB_PORT_STAT_C_RESET)) { + break; + } + + MicroSecondDelay (USB_WAIT_PORT_STS_CHANGE_STALL); + } + + if (Index == USB_WAIT_PORT_STS_CHANGE_LOOP) { + DEBUG ((EFI_D_ERROR, "PeiResetHubPort: reset not finished in time on port %d\n", PortNum)); + return; + } // - // clear reset root port + // clear reset change root port // PeiHubClearPortFeature ( PeiServices, UsbIoPpi, PortNum, - EfiUsbPortReset + EfiUsbPortResetChange ); MicroSecondDelay (1 * 1000); diff --git a/MdeModulePkg/Bus/Usb/UsbBusPei/HubPeim.h b/MdeModulePkg/Bus/Usb/UsbBusPei/HubPeim.h index 273a26c1ae..f50bc63501 100644 --- a/MdeModulePkg/Bus/Usb/UsbBusPei/HubPeim.h +++ b/MdeModulePkg/Bus/Usb/UsbBusPei/HubPeim.h @@ -1,7 +1,7 @@ /** @file Constants definitions for Usb Hub Peim -Copyright (c) 2006, Intel Corporation. All rights reserved.
+Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.
This program and the accompanying materials are licensed and made available under the terms and conditions @@ -80,6 +80,8 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #define USB_RT_HUB (USB_TYPE_CLASS | USB_RECIP_DEVICE) #define USB_RT_PORT (USB_TYPE_CLASS | USB_RECIP_OTHER) +#define USB_HUB_REQ_SET_DEPTH 12 + #define MAXBYTES 8 #pragma pack(1) // diff --git a/MdeModulePkg/Bus/Usb/UsbBusPei/PeiUsbLib.c b/MdeModulePkg/Bus/Usb/UsbBusPei/PeiUsbLib.c index 6fef61e565..42be13ac3b 100644 --- a/MdeModulePkg/Bus/Usb/UsbBusPei/PeiUsbLib.c +++ b/MdeModulePkg/Bus/Usb/UsbBusPei/PeiUsbLib.c @@ -221,26 +221,24 @@ IsPortConnect ( } /** - Judge if the port is connected with a low-speed usb device or not. + Get device speed according to port status. - @param PortStatus The usb port status gotten. + @param PortStatus The usb port status gotten. - @retval TRUE A low-speed usb device is connected with the port. - @retval FALSE No low-speed usb device is connected with the port. + @return Device speed value. **/ UINTN -IsPortLowSpeedDeviceAttached ( - IN UINT16 PortStatus +PeiUsbGetDeviceSpeed ( + IN UINT16 PortStatus ) { - // - // return the bit 9 value of PortStatus - // if ((PortStatus & USB_PORT_STAT_LOW_SPEED) != 0) { return EFI_USB_SPEED_LOW; } else if ((PortStatus & USB_PORT_STAT_HIGH_SPEED) != 0){ return EFI_USB_SPEED_HIGH; + } else if ((PortStatus & USB_PORT_STAT_SUPER_SPEED) != 0) { + return EFI_USB_SPEED_SUPER; } else { return EFI_USB_SPEED_FULL; } diff --git a/MdeModulePkg/Bus/Usb/UsbBusPei/PeiUsbLib.h b/MdeModulePkg/Bus/Usb/UsbBusPei/PeiUsbLib.h index e0557f8eea..1ace89fbc3 100644 --- a/MdeModulePkg/Bus/Usb/UsbBusPei/PeiUsbLib.h +++ b/MdeModulePkg/Bus/Usb/UsbBusPei/PeiUsbLib.h @@ -70,6 +70,7 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #define USB_DT_INTERFACE 0x04 #define USB_DT_ENDPOINT 0x05 #define USB_DT_HUB 0x29 +#define USB_DT_SUPERSPEED_HUB 0x2A #define USB_DT_HID 0x21 // @@ -202,17 +203,16 @@ IsPortConnect ( ); /** - Judge if the port is connected with a low-speed usb device or not. + Get device speed according to port status. - @param PortStatus The usb port status gotten. + @param PortStatus The usb port status gotten. - @retval TRUE A low-speed usb device is connected with the port. - @retval FALSE No low-speed usb device is connected with the port. + @return Device speed value. **/ UINTN -IsPortLowSpeedDeviceAttached ( - IN UINT16 PortStatus +PeiUsbGetDeviceSpeed ( + IN UINT16 PortStatus ); /** diff --git a/MdeModulePkg/Bus/Usb/UsbBusPei/UsbIoPeim.c b/MdeModulePkg/Bus/Usb/UsbBusPei/UsbIoPeim.c index 492f124296..d13a7ee0a3 100644 --- a/MdeModulePkg/Bus/Usb/UsbBusPei/UsbIoPeim.c +++ b/MdeModulePkg/Bus/Usb/UsbBusPei/UsbIoPeim.c @@ -105,7 +105,7 @@ PeiUsbControlTransfer ( PeiUsbDev->UsbHcPpi, PeiUsbDev->DeviceAddress, PeiUsbDev->DeviceSpeed, - PeiUsbDev->MaxPacketSize0, + (UINT8) PeiUsbDev->MaxPacketSize0, Request, Direction, Data, @@ -126,6 +126,7 @@ PeiUsbControlTransfer ( } } + DEBUG ((EFI_D_INFO, "PeiUsbControlTransfer: %r\n", Status)); return Status; } @@ -238,6 +239,7 @@ PeiUsbBulkTransfer ( PeiUsbDev->DataToggle = (UINT16) (PeiUsbDev->DataToggle ^ (1 << EndpointIndex)); } + DEBUG ((EFI_D_INFO, "PeiUsbBulkTransfer: %r\n", Status)); return Status; } diff --git a/MdeModulePkg/Bus/Usb/UsbBusPei/UsbPeim.c b/MdeModulePkg/Bus/Usb/UsbBusPei/UsbPeim.c index 23090f68f3..947864bd27 100644 --- a/MdeModulePkg/Bus/Usb/UsbBusPei/UsbPeim.c +++ b/MdeModulePkg/Bus/Usb/UsbBusPei/UsbPeim.c @@ -228,6 +228,8 @@ PeiHubEnumeration ( UsbIoPpi = &PeiUsbDevice->UsbIoPpi; + DEBUG ((EFI_D_INFO, "PeiHubEnumeration: DownStreamPortNo: %x\n", PeiUsbDevice->DownStreamPortNo)); + for (Index = 0; Index < PeiUsbDevice->DownStreamPortNo; Index++) { Status = PeiHubGetPortStatus ( @@ -241,25 +243,14 @@ PeiHubEnumeration ( continue; } - if (IsPortConnectChange (PortStatus.PortChangeStatus)) { - PeiHubClearPortFeature ( - PeiServices, - UsbIoPpi, - (UINT8) (Index + 1), - EfiUsbPortConnectChange - ); - - MicroSecondDelay (100 * 1000); - + DEBUG ((EFI_D_INFO, "USB Status --- Port: %x ConnectChange[%04x] Status[%04x]\n", Index, PortStatus.PortChangeStatus, PortStatus.PortStatus)); + // + // Only handle connection/enable/overcurrent/reset change. + // + if ((PortStatus.PortChangeStatus & (USB_PORT_STAT_C_CONNECTION | USB_PORT_STAT_C_ENABLE | USB_PORT_STAT_C_OVERCURRENT | USB_PORT_STAT_C_RESET)) == 0) { + continue; + } else { if (IsPortConnect (PortStatus.PortStatus)) { - - PeiHubGetPortStatus ( - PeiServices, - UsbIoPpi, - (UINT8) (Index + 1), - (UINT32 *) &PortStatus - ); - // // Begin to deal with the new device // @@ -294,19 +285,44 @@ PeiHubEnumeration ( NewPeiUsbDevice->AllocateAddress = (UINTN) AllocateAddress; NewPeiUsbDevice->UsbHcPpi = PeiUsbDevice->UsbHcPpi; NewPeiUsbDevice->Usb2HcPpi = PeiUsbDevice->Usb2HcPpi; + NewPeiUsbDevice->Tier = (UINT8) (PeiUsbDevice->Tier + 1); NewPeiUsbDevice->IsHub = 0x0; NewPeiUsbDevice->DownStreamPortNo = 0x0; - PeiResetHubPort (PeiServices, UsbIoPpi, (UINT8)(Index + 1)); + if (((PortStatus.PortChangeStatus & USB_PORT_STAT_C_RESET) == 0) || + ((PortStatus.PortStatus & (USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE)) == 0)) { + // + // If the port already has reset change flag and is connected and enabled, skip the port reset logic. + // + PeiResetHubPort (PeiServices, UsbIoPpi, (UINT8)(Index + 1)); + + PeiHubGetPortStatus ( + PeiServices, + UsbIoPpi, + (UINT8) (Index + 1), + (UINT32 *) &PortStatus + ); + } else { + PeiHubClearPortFeature ( + PeiServices, + UsbIoPpi, + (UINT8) (Index + 1), + EfiUsbPortResetChange + ); + } - PeiHubGetPortStatus ( - PeiServices, - UsbIoPpi, - (UINT8) (Index + 1), - (UINT32 *) &PortStatus - ); + NewPeiUsbDevice->DeviceSpeed = (UINT8) PeiUsbGetDeviceSpeed (PortStatus.PortStatus); + DEBUG ((EFI_D_INFO, "Device Speed =%d\n", PeiUsbDevice->DeviceSpeed)); - NewPeiUsbDevice->DeviceSpeed = (UINT8)IsPortLowSpeedDeviceAttached (PortStatus.PortStatus); + if (USB_BIT_IS_SET (PortStatus.PortStatus, USB_PORT_STAT_SUPER_SPEED)){ + NewPeiUsbDevice->MaxPacketSize0 = 512; + } else if (USB_BIT_IS_SET (PortStatus.PortStatus, USB_PORT_STAT_HIGH_SPEED)) { + NewPeiUsbDevice->MaxPacketSize0 = 64; + } else if (USB_BIT_IS_SET (PortStatus.PortStatus, USB_PORT_STAT_LOW_SPEED)) { + NewPeiUsbDevice->MaxPacketSize0 = 8; + } else { + NewPeiUsbDevice->MaxPacketSize0 = 8; + } if(NewPeiUsbDevice->DeviceSpeed != EFI_USB_SPEED_HIGH) { if (PeiUsbDevice->DeviceSpeed == EFI_USB_SPEED_HIGH) { @@ -330,6 +346,7 @@ PeiHubEnumeration ( if (EFI_ERROR (Status)) { continue; } + DEBUG ((EFI_D_INFO, "PeiHubEnumeration: PeiConfigureUsbDevice Success\n")); Status = PeiServicesInstallPpi (&NewPeiUsbDevice->UsbIoPpiList); @@ -435,6 +452,8 @@ PeiUsbEnumeration ( return EFI_INVALID_PARAMETER; } + DEBUG ((EFI_D_INFO, "PeiUsbEnumeration: NumOfRootPort: %x\n", NumOfRootPort)); + for (Index = 0; Index < NumOfRootPort; Index++) { // // First get root port status to detect changes happen @@ -454,48 +473,14 @@ PeiUsbEnumeration ( &PortStatus ); } - DEBUG ((EFI_D_INFO, "USB Status --- ConnectChange[%04x] Status[%04x]\n", PortStatus.PortChangeStatus, PortStatus.PortStatus)); - if (IsPortConnectChange (PortStatus.PortChangeStatus)) { - // - // Changes happen, first clear this change status - // - if (Usb2HcPpi != NULL) { - Usb2HcPpi->ClearRootHubPortFeature ( - PeiServices, - Usb2HcPpi, - (UINT8) Index, - EfiUsbPortConnectChange - ); - } else { - UsbHcPpi->ClearRootHubPortFeature ( - PeiServices, - UsbHcPpi, - (UINT8) Index, - EfiUsbPortConnectChange - ); - } - MicroSecondDelay (100 * 1000); - + DEBUG ((EFI_D_INFO, "USB Status --- Port: %x ConnectChange[%04x] Status[%04x]\n", Index, PortStatus.PortChangeStatus, PortStatus.PortStatus)); + // + // Only handle connection/enable/overcurrent/reset change. + // + if ((PortStatus.PortChangeStatus & (USB_PORT_STAT_C_CONNECTION | USB_PORT_STAT_C_ENABLE | USB_PORT_STAT_C_OVERCURRENT | USB_PORT_STAT_C_RESET)) == 0) { + continue; + } else { if (IsPortConnect (PortStatus.PortStatus)) { - if (Usb2HcPpi != NULL) { - Usb2HcPpi->GetRootHubPortStatus ( - PeiServices, - Usb2HcPpi, - (UINT8) Index, - &PortStatus - ); - } else { - UsbHcPpi->GetRootHubPortStatus ( - PeiServices, - UsbHcPpi, - (UINT8) Index, - &PortStatus - ); - } - - // - // Connect change happen - // MemPages = sizeof (PEI_USB_DEVICE) / EFI_PAGE_SIZE + 1; Status = PeiServicesAllocatePages ( EfiBootServicesCode, @@ -530,33 +515,65 @@ PeiUsbEnumeration ( PeiUsbDevice->IsHub = 0x0; PeiUsbDevice->DownStreamPortNo = 0x0; - ResetRootPort ( - PeiServices, - PeiUsbDevice->UsbHcPpi, - PeiUsbDevice->Usb2HcPpi, - Index, - 0 - ); + if (((PortStatus.PortChangeStatus & USB_PORT_STAT_C_RESET) == 0) || + ((PortStatus.PortStatus & (USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE)) == 0)) { + // + // If the port already has reset change flag and is connected and enabled, skip the port reset logic. + // + ResetRootPort ( + PeiServices, + PeiUsbDevice->UsbHcPpi, + PeiUsbDevice->Usb2HcPpi, + Index, + 0 + ); - if (Usb2HcPpi != NULL) { - Usb2HcPpi->GetRootHubPortStatus ( - PeiServices, - Usb2HcPpi, - (UINT8) Index, - &PortStatus - ); + if (Usb2HcPpi != NULL) { + Usb2HcPpi->GetRootHubPortStatus ( + PeiServices, + Usb2HcPpi, + (UINT8) Index, + &PortStatus + ); + } else { + UsbHcPpi->GetRootHubPortStatus ( + PeiServices, + UsbHcPpi, + (UINT8) Index, + &PortStatus + ); + } } else { - UsbHcPpi->GetRootHubPortStatus ( - PeiServices, - UsbHcPpi, - (UINT8) Index, - &PortStatus - ); + if (Usb2HcPpi != NULL) { + Usb2HcPpi->ClearRootHubPortFeature ( + PeiServices, + Usb2HcPpi, + (UINT8) Index, + EfiUsbPortResetChange + ); + } else { + UsbHcPpi->ClearRootHubPortFeature ( + PeiServices, + UsbHcPpi, + (UINT8) Index, + EfiUsbPortResetChange + ); + } } - PeiUsbDevice->DeviceSpeed = (UINT8)IsPortLowSpeedDeviceAttached (PortStatus.PortStatus); + PeiUsbDevice->DeviceSpeed = (UINT8) PeiUsbGetDeviceSpeed (PortStatus.PortStatus); DEBUG ((EFI_D_INFO, "Device Speed =%d\n", PeiUsbDevice->DeviceSpeed)); + if (USB_BIT_IS_SET (PortStatus.PortStatus, USB_PORT_STAT_SUPER_SPEED)){ + PeiUsbDevice->MaxPacketSize0 = 512; + } else if (USB_BIT_IS_SET (PortStatus.PortStatus, USB_PORT_STAT_HIGH_SPEED)) { + PeiUsbDevice->MaxPacketSize0 = 64; + } else if (USB_BIT_IS_SET (PortStatus.PortStatus, USB_PORT_STAT_LOW_SPEED)) { + PeiUsbDevice->MaxPacketSize0 = 8; + } else { + PeiUsbDevice->MaxPacketSize0 = 8; + } + // // Configure that Usb Device // @@ -570,7 +587,7 @@ PeiUsbEnumeration ( if (EFI_ERROR (Status)) { continue; } - DEBUG ((EFI_D_INFO, "PeiConfigureUsbDevice Success\n")); + DEBUG ((EFI_D_INFO, "PeiUsbEnumeration: PeiConfigureUsbDevice Success\n")); Status = PeiServicesInstallPpi (&PeiUsbDevice->UsbIoPpiList); @@ -665,9 +682,6 @@ PeiConfigureUsbDevice ( // for (Retry = 0; Retry < 3; Retry ++) { - - PeiUsbDevice->MaxPacketSize0 = 8; - Status = PeiUsbGetDescriptor ( PeiServices, UsbIoPpi, @@ -678,17 +692,21 @@ PeiConfigureUsbDevice ( ); if (!EFI_ERROR (Status)) { - DEBUG ((EFI_D_INFO, "PeiUsbGet Device Descriptor the %d time Sucess\n", Retry)); + DEBUG ((EFI_D_INFO, "PeiUsbGet Device Descriptor the %d time Success\n", Retry)); break; } } if (Retry == 3) { - DEBUG ((EFI_D_ERROR, "PeiUsbGet Device Descriptor fail\n", Retry)); + DEBUG ((EFI_D_ERROR, "PeiUsbGet Device Descriptor fail: %x %r\n", Retry, Status)); return Status; } - PeiUsbDevice->MaxPacketSize0 = DeviceDescriptor.MaxPacketSize0; + if ((DeviceDescriptor.BcdUSB == 0x0300) && (DeviceDescriptor.MaxPacketSize0 == 9)) { + PeiUsbDevice->MaxPacketSize0 = 1 << 9; + } else { + PeiUsbDevice->MaxPacketSize0 = DeviceDescriptor.MaxPacketSize0; + } (*DeviceAddress) ++; @@ -699,7 +717,7 @@ PeiConfigureUsbDevice ( ); if (EFI_ERROR (Status)) { - DEBUG ((EFI_D_ERROR, "PeiUsbSetDeviceAddress Failed\n")); + DEBUG ((EFI_D_ERROR, "PeiUsbSetDeviceAddress Failed: %r\n", Status)); return Status; } @@ -995,6 +1013,8 @@ ResetRootPort ( ) { EFI_STATUS Status; + UINTN Index; + EFI_USB_PORT_STATUS PortStatus; if (Usb2HcPpi != NULL) { @@ -1015,8 +1035,12 @@ ResetRootPort ( return; } - MicroSecondDelay (200 * 1000); - + // + // Drive the reset signal for at least 50ms. Check USB 2.0 Spec + // section 7.1.7.5 for timing requirements. + // + MicroSecondDelay (USB_SET_ROOT_PORT_RESET_STALL); + // // clear reset root port // @@ -1031,9 +1055,45 @@ ResetRootPort ( DEBUG ((EFI_D_ERROR, "ClearRootHubPortFeature EfiUsbPortReset Failed\n")); return; } - - MicroSecondDelay (1 * 1000); - + + MicroSecondDelay (USB_CLR_ROOT_PORT_RESET_STALL); + + // + // USB host controller won't clear the RESET bit until + // reset is actually finished. + // + ZeroMem (&PortStatus, sizeof (EFI_USB_PORT_STATUS)); + + for (Index = 0; Index < USB_WAIT_PORT_STS_CHANGE_LOOP; Index++) { + Status = Usb2HcPpi->GetRootHubPortStatus ( + PeiServices, + Usb2HcPpi, + PortNum, + &PortStatus + ); + if (EFI_ERROR (Status)) { + return; + } + + if (!USB_BIT_IS_SET (PortStatus.PortStatus, USB_PORT_STAT_RESET)) { + break; + } + + MicroSecondDelay (USB_WAIT_PORT_STS_CHANGE_STALL); + } + + if (Index == USB_WAIT_PORT_STS_CHANGE_LOOP) { + DEBUG ((EFI_D_ERROR, "ResetRootPort: reset not finished in time on port %d\n", PortNum)); + return; + } + + Usb2HcPpi->ClearRootHubPortFeature ( + PeiServices, + Usb2HcPpi, + PortNum, + EfiUsbPortResetChange + ); + Usb2HcPpi->ClearRootHubPortFeature ( PeiServices, Usb2HcPpi, @@ -1077,7 +1137,11 @@ ResetRootPort ( return; } - MicroSecondDelay (200 * 1000); + // + // Drive the reset signal for at least 50ms. Check USB 2.0 Spec + // section 7.1.7.5 for timing requirements. + // + MicroSecondDelay (USB_SET_ROOT_PORT_RESET_STALL); // // clear reset root port @@ -1094,8 +1158,44 @@ ResetRootPort ( return; } - MicroSecondDelay (1 * 1000); - + MicroSecondDelay (USB_CLR_ROOT_PORT_RESET_STALL); + + // + // USB host controller won't clear the RESET bit until + // reset is actually finished. + // + ZeroMem (&PortStatus, sizeof (EFI_USB_PORT_STATUS)); + + for (Index = 0; Index < USB_WAIT_PORT_STS_CHANGE_LOOP; Index++) { + Status = UsbHcPpi->GetRootHubPortStatus ( + PeiServices, + UsbHcPpi, + PortNum, + &PortStatus + ); + if (EFI_ERROR (Status)) { + return; + } + + if (!USB_BIT_IS_SET (PortStatus.PortStatus, USB_PORT_STAT_RESET)) { + break; + } + + MicroSecondDelay (USB_WAIT_PORT_STS_CHANGE_STALL); + } + + if (Index == USB_WAIT_PORT_STS_CHANGE_LOOP) { + DEBUG ((EFI_D_ERROR, "ResetRootPort: reset not finished in time on port %d\n", PortNum)); + return; + } + + UsbHcPpi->ClearRootHubPortFeature ( + PeiServices, + UsbHcPpi, + PortNum, + EfiUsbPortResetChange + ); + UsbHcPpi->ClearRootHubPortFeature ( PeiServices, UsbHcPpi, diff --git a/MdeModulePkg/Bus/Usb/UsbBusPei/UsbPeim.h b/MdeModulePkg/Bus/Usb/UsbBusPei/UsbPeim.h index 4685034a5c..df459e7a6e 100644 --- a/MdeModulePkg/Bus/Usb/UsbBusPei/UsbPeim.h +++ b/MdeModulePkg/Bus/Usb/UsbBusPei/UsbPeim.h @@ -33,25 +33,20 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include -#define MAX_ROOT_PORT 2 #define MAX_INTERFACE 8 #define MAX_ENDPOINT 16 -#define USB_SLOW_SPEED_DEVICE 0x01 -#define USB_FULL_SPEED_DEVICE 0x02 - #define PEI_USB_DEVICE_SIGNATURE SIGNATURE_32 ('U', 's', 'b', 'D') typedef struct { UINTN Signature; PEI_USB_IO_PPI UsbIoPpi; EFI_PEI_PPI_DESCRIPTOR UsbIoPpiList; + UINT16 MaxPacketSize0; + UINT16 DataToggle; UINT8 DeviceAddress; - UINT8 MaxPacketSize0; UINT8 DeviceSpeed; UINT8 IsHub; - UINT16 DataToggle; UINT8 DownStreamPortNo; - UINT8 Reserved; // Padding for IPF UINTN AllocateAddress; PEI_USB_HOST_CONTROLLER_PPI *UsbHcPpi; PEI_USB2_HOST_CONTROLLER_PPI *Usb2HcPpi; @@ -61,11 +56,48 @@ typedef struct { EFI_USB_INTERFACE_DESCRIPTOR *InterfaceDescList[MAX_INTERFACE]; EFI_USB_ENDPOINT_DESCRIPTOR *EndpointDesc[MAX_ENDPOINT]; EFI_USB_ENDPOINT_DESCRIPTOR *EndpointDescList[MAX_INTERFACE][MAX_ENDPOINT]; - EFI_USB2_HC_TRANSACTION_TRANSLATOR Translator; + EFI_USB2_HC_TRANSACTION_TRANSLATOR Translator; + UINT8 Tier; } PEI_USB_DEVICE; #define PEI_USB_DEVICE_FROM_THIS(a) CR (a, PEI_USB_DEVICE, UsbIoPpi, PEI_USB_DEVICE_SIGNATURE) +#define USB_BIT_IS_SET(Data, Bit) ((BOOLEAN)(((Data) & (Bit)) == (Bit))) + +#define USB_BUS_1_MILLISECOND 1000 + +// +// Wait for port reset, refers to specification +// [USB20-7.1.7.5, it says 10ms for hub and 50ms for +// root hub] +// +// According to USB2.0, Chapter 11.5.1.5 Resetting, +// the worst case for TDRST is 20ms +// +#define USB_SET_PORT_RESET_STALL (20 * USB_BUS_1_MILLISECOND) +#define USB_SET_ROOT_PORT_RESET_STALL (50 * USB_BUS_1_MILLISECOND) + +// +// Wait for clear roothub port reset, set by experience +// +#define USB_CLR_ROOT_PORT_RESET_STALL (20 * USB_BUS_1_MILLISECOND) + +// +// Wait for port statue reg change, set by experience +// +#define USB_WAIT_PORT_STS_CHANGE_STALL (100) + +// +// Host software return timeout if port status doesn't change +// after 500ms(LOOP * STALL = 5000 * 0.1ms), set by experience +// +#define USB_WAIT_PORT_STS_CHANGE_LOOP 5000 + +// +// Wait for hub port power-on, refers to specification +// [USB20-11.23.2] +// +#define USB_SET_PORT_POWER_STALL (2 * USB_BUS_1_MILLISECOND) /** Submits control transfer to a target USB device. diff --git a/MdeModulePkg/Include/Ppi/UsbController.h b/MdeModulePkg/Include/Ppi/UsbController.h index 10e025df55..f0537ecb66 100644 --- a/MdeModulePkg/Include/Ppi/UsbController.h +++ b/MdeModulePkg/Include/Ppi/UsbController.h @@ -2,7 +2,7 @@ Define APIs to retrieve USB Host Controller Info such as controller type and I/O Port Base Address. -Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.
+Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.
This program and the accompanying materials are licensed and made available under the terms and conditions @@ -49,6 +49,12 @@ typedef struct _PEI_USB_CONTROLLER_PPI PEI_USB_CONTROLLER_PPI; /// #define PEI_EHCI_CONTROLLER 0x03 +/// +/// This bit is used in the ControllerType return parameter of GetUsbController() +/// to identify the USB Host Controller type as XHCI +/// +#define PEI_XHCI_CONTROLLER 0x04 + /** Retrieve USB Host Controller Info such as controller type and I/O Base Address. diff --git a/MdeModulePkg/MdeModulePkg.dsc b/MdeModulePkg/MdeModulePkg.dsc index f473325344..1abff59075 100644 --- a/MdeModulePkg/MdeModulePkg.dsc +++ b/MdeModulePkg/MdeModulePkg.dsc @@ -202,6 +202,7 @@ MdeModulePkg/Bus/Pci/UhciDxe/UhciDxe.inf MdeModulePkg/Bus/Pci/UhciPei/UhciPei.inf MdeModulePkg/Bus/Pci/EhciPei/EhciPei.inf + MdeModulePkg/Bus/Pci/XhciPei/XhciPei.inf MdeModulePkg/Bus/Pci/IdeBusPei/IdeBusPei.inf MdeModulePkg/Bus/Usb/UsbBusPei/UsbBusPei.inf MdeModulePkg/Bus/Usb/UsbBotPei/UsbBotPei.inf -- 2.39.2