alder_lake_bios/Insyde/InsydeModulePkg/Bus/Usb/UsbBusPei/UsbBus.c

1160 lines
33 KiB
C

/** @file
Usb Bus Peim
;******************************************************************************
;* Copyright (c) 2012 - 2019, Insyde Software Corp. All Rights Reserved.
;*
;* You may not reproduce, distribute, publish, display, perform, modify, adapt,
;* transmit, broadcast, present, recite, release, license or otherwise exploit
;* any part of this publication in any form, by any means, without the prior
;* written permission of Insyde Software Corporation.
;*
;******************************************************************************
*/
#include "UsbBus.h"
#include "Hub.h"
#include "UsbHelper.h"
//
// Internal functions
//
STATIC
EFI_STATUS
EFIAPI
NotifyOnUsbHcPpi (
IN EFI_PEI_SERVICES **PeiServices,
IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDesc,
IN VOID *InvokePpi
);
STATIC
EFI_STATUS
PeiUsbEnumeration (
IN EFI_PEI_SERVICES **PeiServices,
IN PEI_USB3_HOST_CONTROLLER_PPI *UsbHcPpi
);
STATIC EFI_PEI_PPI_DESCRIPTOR mUsbIoPpiList = {
(EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
&gPeiUsbIoPpiGuid,
NULL
};
STATIC EFI_PEI_PPI_DESCRIPTOR mUsb3IoPpiList = {
(EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
&gPeiUsb3IoPpiGuid,
NULL
};
STATIC EFI_PEI_NOTIFY_DESCRIPTOR mNotifyList[] = {
{
EFI_PEI_PPI_DESCRIPTOR_NOTIFY_DISPATCH,
&gPeiUsb2HostControllerPpiGuid,
NotifyOnUsbHcPpi
},
{
EFI_PEI_PPI_DESCRIPTOR_NOTIFY_DISPATCH | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST,
&gPeiUsb3HostControllerPpiGuid,
NotifyOnUsbHcPpi
}
};
//
// UsbIo PPI interface function
//
STATIC PEI_USB_IO_PPI mUsbIoPpi = {
PeiUsbControlTransfer,
PeiUsbBulkTransfer,
PeiUsbGetInterfaceDescriptor,
PeiUsbGetEndpointDescriptor,
PeiUsbPortReset
};
STATIC PEI_USB3_IO_PPI mUsb3IoPpi = {
PeiUsb3ControlTransfer,
PeiUsb3BulkTransfer,
PeiUsb3AsyncInterruptTransfer,
PeiUsb3SyncInterruptTransfer,
PeiUsb3GetInterfaceDescriptor,
PeiUsb3GetEndpointDescriptor,
PeiUsb3PortReset,
PeiUsb3ClearEndpointHalt
};
STATIC LIST_ENTRY mUsbHcListHead;
/**
Initializes the Usb Io PPI
@param PeiServices General purpose services available to every PEIM.
@retval EFI_UNSUPPORTED Can't find required PPI
@retval EFI_OUT_OF_RESOURCES Can't allocate memory resource
@retval EFI_SUCCESS Success
**/
EFI_STATUS
UsbBusPeimEntry (
IN EFI_PEI_FILE_HANDLE FileHandle,
IN CONST EFI_PEI_SERVICES **PeiServices
)
{
//
// Shadow this PEIM to run from memory
//
if (!EFI_ERROR (PeiServicesRegisterForShadow (FileHandle))) {
return EFI_SUCCESS;
}
InitializeListHead(&mUsbHcListHead);
//
// Use UsbHc notification callback to enumerate USB port
//
PeiServicesNotifyPpi (mNotifyList);
return EFI_SUCCESS;
}
/**
Check is the UsbHcPpi has been enumerated
@param UsbHcPpi The instance of UsbHcPpi
@retval TRUE The UsbHcPpi dose enumerated
@retval FALSE The UsbHcPpi dosen't enumerated
**/
STATIC
BOOLEAN
IsUsbHcEnumerated (
IN VOID *UsbHcPpi,
IN OUT VOID **Usb3HcPpi
)
{
EFI_STATUS Status;
PEI_USB_HC_TOKEN *UsbHcToken;
LIST_ENTRY *Node;
if (!IsListEmpty (&mUsbHcListHead)) {
//
// Searching for existing USB host controller
//
Node = &mUsbHcListHead;
do {
Node = GetFirstNode (Node);
if (((PEI_USB_HC_TOKEN*)Node)->UsbHcPpi == UsbHcPpi) {
return TRUE;
}
} while (!IsNodeAtEnd (&mUsbHcListHead, Node));
}
Status = PeiServicesAllocatePool (
sizeof (PEI_USB_HC_TOKEN),
(VOID **)&UsbHcToken
);
if (EFI_ERROR (Status)) {
return TRUE;
}
UsbHcToken->UsbHcPpi = UsbHcPpi;
if (Usb3HcPpi) {
//
// Create the thunk of Usb3HcPpi for Usb2HcPpi only PPI
//
UsbHcToken->Usb3HcPpi.ControlTransfer = Usb3HcControlTransfer;
UsbHcToken->Usb3HcPpi.BulkTransfer = Usb3HcBulkTransfer;
UsbHcToken->Usb3HcPpi.InterruptTransfer = Usb3HcInterruptTransfer;
UsbHcToken->Usb3HcPpi.GetRootHubPortNumber = Usb3HcGetRootHubPortNumber;
UsbHcToken->Usb3HcPpi.GetRootHubPortStatus = Usb3HcGetRootHubPortStatus;
UsbHcToken->Usb3HcPpi.SetRootHubPortFeature = Usb3HcSetRootHubPortFeature;
UsbHcToken->Usb3HcPpi.ClearRootHubPortFeature = Usb3HcClearRootHubPortFeature;
*Usb3HcPpi = &UsbHcToken->Usb3HcPpi;
}
InsertTailList (&mUsbHcListHead, (LIST_ENTRY*)UsbHcToken);
return FALSE;
}
/**
UsbHc installation notification function.
This function finds out all the current USB HC PPIs in the system and add them
into private data.
@param PeiServices Indirect reference to the PEI Services Table.
@param NotifyDesc Address of the notification descriptor data structure.
@param InvokePpi Address of the PPI that was invoked.
@retval EFI_SUCCESS The function completes successfully.
**/
STATIC
EFI_STATUS
EFIAPI
NotifyOnUsbHcPpi (
IN EFI_PEI_SERVICES **PeiServices,
IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDesc,
IN VOID *InvokePpi
)
{
EFI_STATUS Status;
UINTN Index;
VOID *UsbHcPpi;
//
// Looking for Usb3HcPpi
//
for (Index = 0, Status = EFI_SUCCESS; Status == EFI_SUCCESS; Index ++) {
Status = (**PeiServices).LocatePpi (
(CONST EFI_PEI_SERVICES **)PeiServices,
&gPeiUsb3HostControllerPpiGuid,
Index,
NULL,
&UsbHcPpi
);
if (!EFI_ERROR (Status) && !IsUsbHcEnumerated (UsbHcPpi, NULL)) {
//
// Root port enumeration
//
PeiUsbEnumeration ((EFI_PEI_SERVICES**)PeiServices, UsbHcPpi);
}
}
//
// Looking for Usb2HcPpi
//
for (Index = 0, Status = EFI_SUCCESS; Status == EFI_SUCCESS; Index ++) {
Status = (**PeiServices).LocatePpi (
(CONST EFI_PEI_SERVICES **)PeiServices,
&gPeiUsb2HostControllerPpiGuid,
Index,
NULL,
&UsbHcPpi
);
if (!EFI_ERROR (Status) && !IsUsbHcEnumerated (UsbHcPpi, &UsbHcPpi)) {
//
// Root port enumeration
//
PeiUsbEnumeration ((EFI_PEI_SERVICES**)PeiServices, UsbHcPpi);
}
}
return EFI_SUCCESS;
}
/**
Set Transaction Translator parameter
@param ParentHub Parent Hub device
@param ParentPort Number of parent port
@param Device This device
@retval EFI_SUCCESS Success
@retval EFI_OUT_OF_RESOURCES Cannot allocate resources
**/
STATIC
EFI_STATUS
UsbSetTransactionTranslator (
IN PEI_USB_DEVICE *ParentHub,
IN UINT8 ParentPort,
IN OUT PEI_USB_DEVICE *Device
)
{
UINT8 DeviceAddress;
UINT8 Port;
UINT8 MultiTT;
//
// Inherit Route String from parent
//
if (!ParentHub) {
//
// Level 1
//
Device->Translator.RouteString = 0;
Device->HubDepth = 0;
} else {
//
// Below Level 1
//
Device->Translator.RouteString = ParentHub->Translator.RouteString;
Device->HubDepth = ParentHub->HubDepth + 1;
//
// Initial the route string for XHC
//
if (ParentHub->IsHub && Device->HubDepth != 0) {
Device->Translator.RouteString &= ~(0x0f << (4 * (Device->HubDepth - 1)));
Device->Translator.RouteString |= (ParentPort + 1) << (4 * (Device->HubDepth - 1));
}
}
//
// Inherit RootHub Port Number from parent
//
if (Device->HubDepth == 0) {
//
// Level 1 device, records the RootHub Port Number
//
Device->Translator.RootHubPortNumber = ParentPort + 1;
} else {
//
// Not level 1 device, inherit RootHub Port Number from parent
//
Device->Translator.RootHubPortNumber = ParentHub->Translator.RootHubPortNumber;
if (Device->DeviceSpeed == EFI_USB_SPEED_LOW || Device->DeviceSpeed == EFI_USB_SPEED_FULL) {
//
// Only full/low speed device need
//
if (EFI_USB_SPEED_HIGH == ParentHub->DeviceSpeed) {
//
// Parent is high speed, then parent is our translator
//
DeviceAddress = ParentHub->DeviceAddress;
Port = ParentPort + 1;
MultiTT = ParentHub->MultiTT;
} else {
//
// Use parent's translator.
//
DeviceAddress = ParentHub->Translator.TranslatorHubAddress;
Port = ParentHub->Translator.TranslatorPortNumber;
MultiTT = ParentHub->Translator.MultiTT;
}
Device->Translator.TranslatorHubAddress = DeviceAddress;
Device->Translator.TranslatorPortNumber = Port;
Device->Translator.MultiTT = MultiTT;
}
}
return EFI_SUCCESS;
}
/**
Get the start position of next wanted descriptor.
@param Buffer Buffer containing data to parse
@param Length Buffer length
@param DescType Descriptor type
@param DescLength Descriptor length
@param ParsedBytes Bytes has been parsed
@retval EFI_SUCCESS
@retval EFI_DEVICE_ERROR
**/
STATIC
EFI_STATUS
GetExpectedDescriptor (
IN UINT8 *Buffer,
IN UINTN Length,
IN UINT8 DescType,
IN UINT8 DescLength,
OUT UINTN *ParsedBytes
)
{
UINT16 DescriptorHeader;
UINT8 Len;
UINT8 *ptr;
UINTN Parsed;
Parsed = 0;
ptr = Buffer;
while (TRUE) {
//
// Buffer length should not less than Desc length
//
if (Length < DescLength) {
return EFI_DEVICE_ERROR;
}
//
// DescriptorHeader = *((UINT16 *)ptr), compatible with IPF
//
DescriptorHeader = (UINT16) ((*(ptr + 1) << 8) | *ptr);
Len = ptr[0];
//
// Check to see if it is a start of expected descriptor
//
if ((UINT8) (DescriptorHeader >> 8) == DescType) {
if (Len > DescLength && DescType != USB_DESC_TYPE_ENDPOINT && DescType != USB_DESC_TYPE_HID) {
return EFI_DEVICE_ERROR;
}
break;
}
//
// Descriptor length should be at least 2
// and should not exceed the buffer length
//
if (Len < 2) {
return EFI_DEVICE_ERROR;
}
if (Len > Length) {
return EFI_DEVICE_ERROR;
}
//
// Skip this mismatch descriptor
//
Length -= Len;
ptr += Len;
Parsed += Len;
}
*ParsedBytes = Parsed;
return EFI_SUCCESS;
}
/**
Pei Usb Get All Configuration
@param PeiServices EFI_PEI_SERVICES
@param PeiUsbDevice PEI_USB_DEVICE
@retval EFI_SUCCESS Success
**/
STATIC
EFI_STATUS
PeiUsbGetAllConfiguration (
IN EFI_PEI_SERVICES **PeiServices,
IN PEI_USB_DEVICE *PeiUsbDevice
)
{
EFI_STATUS Status;
EFI_USB_CONFIG_DESCRIPTOR *ConfigDesc;
PEI_USB3_IO_PPI *UsbIoPpi;
UINT16 ConfigDescLength;
UINT8 *ptr;
UINTN SkipBytes;
UINTN LengthLeft;
UINTN i;
UINTN NumOfEndpoint;
UsbIoPpi = &PeiUsbDevice->Usb3IoPpi;
//
// First get its 4-byte configuration descriptor
//
Status = PeiUsbGetDescriptor (
PeiServices,
UsbIoPpi,
0x0200, // Value
0, // Index
4, // Length
PeiUsbDevice->ConfigurationData
);
if (EFI_ERROR (Status)) {
return Status;
}
ConfigDesc = (EFI_USB_CONFIG_DESCRIPTOR *) PeiUsbDevice->ConfigurationData;
ConfigDescLength = ConfigDesc->TotalLength;
//
// Then we get the total descriptors for this configuration
//
Status = PeiUsbGetDescriptor (
PeiServices,
UsbIoPpi,
0x0200,
0,
ConfigDescLength,
PeiUsbDevice->ConfigurationData
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Parse this configuration descriptor
// First get the current config descriptor;
//
Status = GetExpectedDescriptor (
PeiUsbDevice->ConfigurationData,
ConfigDescLength,
USB_DESC_TYPE_CONFIG,
sizeof (EFI_USB_CONFIG_DESCRIPTOR),
&SkipBytes
);
if (EFI_ERROR (Status)) {
return Status;
}
ptr = PeiUsbDevice->ConfigurationData + SkipBytes;
PeiUsbDevice->ConfigDesc = (EFI_USB_CONFIG_DESCRIPTOR *) ptr;
ptr += sizeof (EFI_USB_CONFIG_DESCRIPTOR);
LengthLeft = ConfigDescLength - SkipBytes - sizeof (EFI_USB_CONFIG_DESCRIPTOR);
//
// Get the first interface descriptor
//
Status = GetExpectedDescriptor (
ptr,
LengthLeft,
USB_DESC_TYPE_INTERFACE,
sizeof (EFI_USB_INTERFACE_DESCRIPTOR),
&SkipBytes
);
if (EFI_ERROR (Status)) {
return Status;
}
ptr += SkipBytes;
PeiUsbDevice->InterfaceDesc = (EFI_USB_INTERFACE_DESCRIPTOR *) ptr;
ptr += sizeof (EFI_USB_INTERFACE_DESCRIPTOR);
LengthLeft -= SkipBytes;
LengthLeft -= sizeof (EFI_USB_INTERFACE_DESCRIPTOR);
//
// Parse all the endpoint descriptor within this interface
//
NumOfEndpoint = PeiUsbDevice->InterfaceDesc->NumEndpoints;
for (i = 0; i < NumOfEndpoint; i++) {
//
// Get the endpoint descriptor
//
Status = GetExpectedDescriptor (
ptr,
LengthLeft,
USB_DESC_TYPE_ENDPOINT,
sizeof (EFI_USB_ENDPOINT_DESCRIPTOR),
&SkipBytes
);
if (EFI_ERROR (Status)) {
return Status;
}
ptr += SkipBytes;
PeiUsbDevice->EndpointDesc[i] = (EFI_USB_ENDPOINT_DESCRIPTOR *) ptr;
ptr += sizeof (EFI_USB_ENDPOINT_DESCRIPTOR);
LengthLeft -= SkipBytes;
LengthLeft -= sizeof (EFI_USB_ENDPOINT_DESCRIPTOR);
}
return EFI_SUCCESS;
}
/**
Pei Configure new detected Usb Device
@param PeiServices EFI_PEI_SERVICES
@param PeiUsbDevice PEI_USB_DEVICE
@param Port Port number
@param DeviceAddress Device Address
@retval EFI_SUCCESS Success
**/
STATIC
EFI_STATUS
PeiConfigureUsbDevice (
IN PEI_USB_DEVICE *PeiUsbDevice,
IN UINT8 Port,
IN OUT UINT8 *DeviceAddress
)
{
EFI_PEI_SERVICES **PeiServices;
EFI_USB_DEVICE_DESCRIPTOR DeviceDescriptor;
EFI_STATUS Status;
PEI_USB3_IO_PPI *UsbIoPpi;
UINT8 Retry;
PeiServices = PeiUsbDevice->PeiServices;
UsbIoPpi = &PeiUsbDevice->Usb3IoPpi;
Status = EFI_SUCCESS;
ZeroMem (&DeviceDescriptor, sizeof (EFI_USB_DEVICE_DESCRIPTOR));
//
// Get USB device descriptor
//
if (PeiUsbDevice->DeviceSpeed == EFI_USB_SPEED_SUPER) {
PeiUsbDevice->MaxPacketSize0 = 9;
} else {
PeiUsbDevice->MaxPacketSize0 = 8;
}
for (Retry = 0; Retry < 3; Retry ++) {
Status = PeiUsbGetDescriptor (
PeiServices,
UsbIoPpi,
0x0100,
0,
8,
&DeviceDescriptor
);
if (!EFI_ERROR (Status)) {
break;
}
}
if (Retry == 3) {
return Status;
}
//
// Stall 1ms after GetDescriptor for specific device compatibility(0.1ms minimum)
//
PeiUsbDevice->StallPpi->Stall (
(CONST EFI_PEI_SERVICES **)PeiServices,
PeiUsbDevice->StallPpi,
1000
);
PeiUsbDevice->MaxPacketSize0 = DeviceDescriptor.MaxPacketSize0;
(*DeviceAddress) ++;
Status = PeiUsbSetDeviceAddress (
PeiServices,
UsbIoPpi,
*DeviceAddress
);
if (EFI_ERROR (Status)) {
return Status;
}
PeiUsbDevice->DeviceAddress = *DeviceAddress;
//
// According to spec, stall 10 millisecond after SetAddress
//
PeiUsbDevice->StallPpi->Stall (
(CONST EFI_PEI_SERVICES **)PeiServices,
PeiUsbDevice->StallPpi,
10 * 1000
);
//
// Get whole USB device descriptor
//
Status = PeiUsbGetDescriptor (
PeiServices,
UsbIoPpi,
0x0100,
0,
sizeof (EFI_USB_DEVICE_DESCRIPTOR),
&DeviceDescriptor
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Get its default configuration and its first interface
//
Status = PeiUsbGetAllConfiguration (
PeiServices,
PeiUsbDevice
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// According to spec, stall 1 millisecond after GetAllConfiguration
//
PeiUsbDevice->StallPpi->Stall (
(CONST EFI_PEI_SERVICES **)PeiServices,
PeiUsbDevice->StallPpi,
1000
);
Status = PeiUsbSetConfiguration (
PeiServices,
UsbIoPpi
);
if (EFI_ERROR (Status)) {
return Status;
}
return EFI_SUCCESS;
}
/**
The Hub Enumeration just scans the hub ports one time. It also
doesn't support hot-plug.
@param PeiUsbDevice PEI_USB_DEVICE
@param CurrentAddress DeviceAddress
@retval EFI_UNSUPPORTED Can't find required PPI
@retval EFI_OUT_OF_RESOURCES Can't allocate memory resource
@retval EFI_SUCCESS Success
**/
STATIC
EFI_STATUS
PeiHubEnumeration (
IN PEI_USB_DEVICE *PeiUsbDevice,
IN UINT8 *CurrentAddress
)
{
UINTN i;
EFI_STATUS Status;
PEI_USB3_IO_PPI *UsbIoPpi;
EFI_USB_PORT_STATUS PortStatus;
BOOLEAN PortChanged;
UINTN MemPages;
EFI_PHYSICAL_ADDRESS AllocateAddress;
PEI_USB_DEVICE *NewPeiUsbDevice;
EFI_PEI_SERVICES **PeiServices;
PortChanged = FALSE;
PeiServices = PeiUsbDevice->PeiServices;
UsbIoPpi = &PeiUsbDevice->Usb3IoPpi;
for (i = 0; i < PeiUsbDevice->DownStreamPortNo; i++) {
Status = PeiHubGetPortStatus (
PeiServices,
UsbIoPpi,
(UINT8) (i + 1),
(UINT32 *) &PortStatus
);
if (EFI_ERROR (Status)) {
continue;
}
if (IsPortConnectChange (PortStatus.PortChangeStatus) || IsPortResetChange (PortStatus.PortChangeStatus) || IsPortConnect (PortStatus.PortStatus)) {
PortChanged = TRUE;
PeiHubClearPortFeature (
PeiServices,
UsbIoPpi,
(UINT8) (i + 1),
EfiUsbPortConnectChange
);
PeiUsbDevice->StallPpi->Stall (
(CONST EFI_PEI_SERVICES **)PeiServices,
PeiUsbDevice->StallPpi,
100 * 1000
);
if (IsPortConnect (PortStatus.PortStatus)) {
PeiHubGetPortStatus (
PeiServices,
UsbIoPpi,
(UINT8) (i + 1),
(UINT32 *) &PortStatus
);
//
// Begin to deal with the new device
//
MemPages = sizeof (PEI_USB_DEVICE) / EFI_PAGE_SIZE + 1;
Status = (*PeiServices)->AllocatePages (
(CONST EFI_PEI_SERVICES **)PeiServices,
EfiBootServicesData,
MemPages,
&AllocateAddress
);
if (EFI_ERROR (Status)) {
return EFI_OUT_OF_RESOURCES;
}
NewPeiUsbDevice = (PEI_USB_DEVICE *) ((UINTN) AllocateAddress);
ZeroMem (NewPeiUsbDevice, sizeof (PEI_USB_DEVICE));
NewPeiUsbDevice->Signature = PEI_USB_DEVICE_SIGNATURE;
NewPeiUsbDevice->PeiServices = PeiServices;
NewPeiUsbDevice->StallPpi = PeiUsbDevice->StallPpi;
NewPeiUsbDevice->DeviceAddress = 0;
NewPeiUsbDevice->MaxPacketSize0 = 8;
NewPeiUsbDevice->DataToggle = 0;
NewPeiUsbDevice->UsbIoPpi = mUsbIoPpi;
NewPeiUsbDevice->UsbIoPpiList = mUsbIoPpiList;
NewPeiUsbDevice->UsbIoPpiList.Ppi = &NewPeiUsbDevice->UsbIoPpi;
NewPeiUsbDevice->Usb3IoPpi = mUsb3IoPpi;
NewPeiUsbDevice->Usb3IoPpiList = mUsb3IoPpiList;
NewPeiUsbDevice->Usb3IoPpiList.Ppi = &NewPeiUsbDevice->Usb3IoPpi;
NewPeiUsbDevice->AllocateAddress = (UINTN) AllocateAddress;
NewPeiUsbDevice->UsbHcPpi = PeiUsbDevice->UsbHcPpi;
NewPeiUsbDevice->DeviceSpeed = EFI_USB_SPEED_FULL;
NewPeiUsbDevice->IsHub = 0x0;
NewPeiUsbDevice->DownStreamPortNo = 0x0;
ResetHubPort (PeiUsbDevice, (UINT8)(i + 1));
//
// Get port status again due to it will be changed by hub port reset
//
PeiHubGetPortStatus (
PeiServices,
UsbIoPpi,
(UINT8) (i + 1),
(UINT32 *) &PortStatus
);
if (PortStatus.PortStatus & USB_PORT_STAT_LOW_SPEED) {
NewPeiUsbDevice->DeviceSpeed = EFI_USB_SPEED_LOW;
} else if (PortStatus.PortStatus & USB_PORT_STAT_HIGH_SPEED) {
NewPeiUsbDevice->DeviceSpeed = EFI_USB_SPEED_HIGH;
} else if (PortStatus.PortStatus & USB_PORT_STAT_SUPER_SPEED) {
NewPeiUsbDevice->DeviceSpeed = EFI_USB_SPEED_SUPER;
} else {
NewPeiUsbDevice->DeviceSpeed = EFI_USB_SPEED_FULL;
}
UsbSetTransactionTranslator (
PeiUsbDevice,
(UINT8)i,
NewPeiUsbDevice
);
//
// Configure that Usb Device
//
Status = PeiConfigureUsbDevice (
NewPeiUsbDevice,
(UINT8) (i + 1),
CurrentAddress
);
if (EFI_ERROR (Status)) {
continue;
}
Status = (**PeiServices).InstallPpi (
(CONST EFI_PEI_SERVICES **)PeiServices,
&NewPeiUsbDevice->UsbIoPpiList
);
Status = (**PeiServices).InstallPpi (
(CONST EFI_PEI_SERVICES **)PeiServices,
&NewPeiUsbDevice->Usb3IoPpiList
);
if (NewPeiUsbDevice->InterfaceDesc->InterfaceClass == CLASS_CODE_HUB) {
NewPeiUsbDevice->IsHub = 0x1;
Status = DoHubConfig (NewPeiUsbDevice);
if (EFI_ERROR (Status)) {
return Status;
}
PeiHubEnumeration (NewPeiUsbDevice, CurrentAddress);
}
}
}
}
return EFI_SUCCESS;
}
/**
Enumeration routine to detect device change
@param PeiServices EFI_PEI_SERVICES
@param UsbHcPpi PEI_USB3_HOST_CONTROLLER_PPI
@retval EFI_UNSUPPORTED Can't find required PPI
@retval EFI_OUT_OF_RESOURCES Can't allocate memory resource
@retval EFI_SUCCESS Success
**/
STATIC
EFI_STATUS
PeiUsbEnumeration (
IN EFI_PEI_SERVICES **PeiServices,
IN PEI_USB3_HOST_CONTROLLER_PPI *UsbHcPpi
)
{
UINT8 NumOfRootPort;
EFI_STATUS Status;
UINT8 i;
EFI_USB_PORT_STATUS PortStatus;
PEI_USB_DEVICE *PeiUsbDevice;
UINTN MemPages;
EFI_PHYSICAL_ADDRESS AllocateAddress;
UINT8 CurrentAddress;
EFI_PEI_STALL_PPI *StallPpi;
UINTN RetryCount;
(**PeiServices).LocatePpi (
(CONST EFI_PEI_SERVICES **)PeiServices,
&gEfiPeiStallPpiGuid,
0,
NULL,
(VOID **)&StallPpi
);
CurrentAddress = 0;
UsbHcPpi->GetRootHubPortNumber (
PeiServices,
UsbHcPpi,
(UINT8 *) &NumOfRootPort
);
for (i = 0; i < NumOfRootPort; i++) {
//
// First get root port status to detect changes happen
//
UsbHcPpi->GetRootHubPortStatus (
PeiServices,
UsbHcPpi,
(UINT8) i,
&PortStatus
);
if (IsPortConnectChange (PortStatus.PortChangeStatus) || IsPortResetChange (PortStatus.PortChangeStatus)) {
//
// Changes happen, first clear this change status
//
UsbHcPpi->ClearRootHubPortFeature (
PeiServices,
UsbHcPpi,
(UINT8) i,
EfiUsbPortConnectChange
);
StallPpi->Stall (
(CONST EFI_PEI_SERVICES **)PeiServices,
StallPpi,
100 * 1000
);
if (IsPortConnect (PortStatus.PortStatus)) {
UsbHcPpi->GetRootHubPortStatus (
PeiServices,
UsbHcPpi,
(UINT8) i,
&PortStatus
);
//
// Connect change happen
//
MemPages = sizeof (PEI_USB_DEVICE) / EFI_PAGE_SIZE + 1;
Status = (*PeiServices)->AllocatePages (
(CONST EFI_PEI_SERVICES **)PeiServices,
EfiBootServicesData,
MemPages,
&AllocateAddress
);
if (EFI_ERROR (Status)) {
return EFI_OUT_OF_RESOURCES;
}
PeiUsbDevice = (PEI_USB_DEVICE *) ((UINTN) AllocateAddress);
ZeroMem (PeiUsbDevice, sizeof (PEI_USB_DEVICE));
PeiUsbDevice->Signature = PEI_USB_DEVICE_SIGNATURE;
PeiUsbDevice->PeiServices = PeiServices;
PeiUsbDevice->StallPpi = StallPpi;
PeiUsbDevice->DeviceAddress = 0;
PeiUsbDevice->MaxPacketSize0 = 8;
PeiUsbDevice->DataToggle = 0;
PeiUsbDevice->UsbIoPpi = mUsbIoPpi;
PeiUsbDevice->UsbIoPpiList = mUsbIoPpiList;
PeiUsbDevice->UsbIoPpiList.Ppi = &PeiUsbDevice->UsbIoPpi;
PeiUsbDevice->Usb3IoPpi = mUsb3IoPpi;
PeiUsbDevice->Usb3IoPpiList = mUsb3IoPpiList;
PeiUsbDevice->Usb3IoPpiList.Ppi = &PeiUsbDevice->Usb3IoPpi;
PeiUsbDevice->AllocateAddress = (UINTN) AllocateAddress;
PeiUsbDevice->UsbHcPpi = UsbHcPpi;
PeiUsbDevice->DeviceSpeed = EFI_USB_SPEED_FULL;
PeiUsbDevice->IsHub = 0x0;
PeiUsbDevice->DownStreamPortNo = 0x0;
//
// Set retry once for the situation of device configuration error
//
RetryCount = 1;
FAIL_RETRY:
//
// Configure that Usb Device
//
ResetRootPort (PeiUsbDevice, i);
//
// Get port status again due to it will be changed by port reset
//
UsbHcPpi->GetRootHubPortStatus (
PeiServices,
UsbHcPpi,
(UINT8) i,
&PortStatus
);
//
// Re-enumerate the port due to XHCI may route the port to USB 3.0 after port reset
//
if (!IsPortConnect (PortStatus.PortStatus)) {
i = 0xff;
continue;
}
if (PortStatus.PortStatus & USB_PORT_STAT_LOW_SPEED) {
PeiUsbDevice->DeviceSpeed = EFI_USB_SPEED_LOW;
} else if (PortStatus.PortStatus & USB_PORT_STAT_HIGH_SPEED && PortStatus.PortStatus & USB_PORT_STAT_ENABLE) {
PeiUsbDevice->DeviceSpeed = EFI_USB_SPEED_HIGH;
} else if (PortStatus.PortStatus & USB_PORT_STAT_SUPER_SPEED) {
PeiUsbDevice->DeviceSpeed = EFI_USB_SPEED_SUPER;
} else {
PeiUsbDevice->DeviceSpeed = EFI_USB_SPEED_FULL;
}
UsbSetTransactionTranslator (
NULL,
i,
PeiUsbDevice
);
Status = PeiConfigureUsbDevice (
PeiUsbDevice,
i,
&CurrentAddress
);
if (EFI_ERROR (Status)) {
//
// Sometime the device will reconnected after first time port reset and caused device config error
// This is device firmware failure, we make a port reset again to workaround this issue
//
if (Status == EFI_DEVICE_ERROR) {
UsbHcPpi->GetRootHubPortStatus (
PeiServices,
UsbHcPpi,
(UINT8) i,
&PortStatus
);
if (IsPortConnectChange (PortStatus.PortChangeStatus)) {
i --;
} else if (RetryCount != 0) {
//
// Retry once upon device configuration error to workaround some USB 3.0 devices
// plugged in USB 2.0 port after reset from OS
//
RetryCount --;
goto FAIL_RETRY;
}
}
continue;
}
Status = (**PeiServices).InstallPpi (
(CONST EFI_PEI_SERVICES **)PeiServices,
&PeiUsbDevice->UsbIoPpiList
);
Status = (**PeiServices).InstallPpi (
(CONST EFI_PEI_SERVICES **)PeiServices,
&PeiUsbDevice->Usb3IoPpiList
);
if (PeiUsbDevice->InterfaceDesc->InterfaceClass == CLASS_CODE_HUB) {
PeiUsbDevice->IsHub = 0x1;
Status = DoHubConfig (PeiUsbDevice);
if (EFI_ERROR (Status)) {
return Status;
}
PeiHubEnumeration (PeiUsbDevice, &CurrentAddress);
}
}
}
}
return EFI_SUCCESS;
}
/**
Reset RootPort
@param PeiUsbDevice PEI_USB_DEVICE
@param PortNum PortNum
@param RetryIndex Retry times
**/
VOID
ResetRootPort (
IN PEI_USB_DEVICE *PeiUsbDevice,
IN UINT8 PortNum
)
{
EFI_STATUS Status;
EFI_PEI_SERVICES **PeiServices;
PEI_USB3_HOST_CONTROLLER_PPI *UsbHcPpi;
UINTN Timeout;
EFI_USB_PORT_STATUS PortStatus;
PeiServices = PeiUsbDevice->PeiServices;
UsbHcPpi = PeiUsbDevice->UsbHcPpi;
PeiUsbDevice->StallPpi->Stall (
(CONST EFI_PEI_SERVICES **)PeiServices,
PeiUsbDevice->StallPpi,
200 * 1000
);
//
// reset root port
//
Status = UsbHcPpi->SetRootHubPortFeature (
PeiServices,
UsbHcPpi,
PortNum,
EfiUsbPortReset
);
if (EFI_ERROR (Status)) {
return;
}
PeiUsbDevice->StallPpi->Stall (
(CONST EFI_PEI_SERVICES **)PeiServices,
PeiUsbDevice->StallPpi,
200 * 1000
);
//
// clear reset root port
//
Status = UsbHcPpi->ClearRootHubPortFeature (
PeiServices,
UsbHcPpi,
PortNum,
EfiUsbPortReset
);
if (EFI_ERROR (Status)) {
return;
}
//
// For XHCI and OHCI, the port reset is cleared by host controller.
// It is needed to waiting for this bit for the completion of
// port reset in 50ms timeout
//
Timeout = 50;
do {
UsbHcPpi->GetRootHubPortStatus (
PeiServices,
UsbHcPpi,
PortNum,
&PortStatus
);
if (!(PortStatus.PortStatus & USB_PORT_STAT_RESET)) break;
PeiUsbDevice->StallPpi->Stall (
(CONST EFI_PEI_SERVICES **)PeiServices,
PeiUsbDevice->StallPpi,
1 * 1000
);
Timeout --;
} while (Timeout > 0);
UsbHcPpi->ClearRootHubPortFeature (
PeiServices,
UsbHcPpi,
PortNum,
EfiUsbPortConnectChange
);
//
// Set port enable
//
UsbHcPpi->SetRootHubPortFeature(
PeiServices,
UsbHcPpi,
PortNum,
EfiUsbPortEnable
);
UsbHcPpi->ClearRootHubPortFeature (
PeiServices,
UsbHcPpi,
PortNum,
EfiUsbPortEnableChange
);
PeiUsbDevice->StallPpi->Stall (
(CONST EFI_PEI_SERVICES **)PeiServices,
PeiUsbDevice->StallPpi,
500 * 1000
);
//
// Clear reset port change
//
Status = UsbHcPpi->ClearRootHubPortFeature (
PeiServices,
UsbHcPpi,
PortNum,
EfiUsbPortResetChange
);
}