audk/MdeModulePkg/Bus/Usb/UsbBusPei/HubPeim.c

587 lines
16 KiB
C

/** @file
Usb Hub Request Support In PEI Phase
Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "UsbPeim.h"
#include "HubPeim.h"
#include "PeiUsbLib.h"
/**
Get a given hub port status.
@param PeiServices General-purpose services that are available to every PEIM.
@param UsbIoPpi Indicates the PEI_USB_IO_PPI instance.
@param Port Usb hub port number (starting from 1).
@param PortStatus Current Hub port status and change status.
@retval EFI_SUCCESS Port status is obtained successfully.
@retval EFI_DEVICE_ERROR Cannot get the port status due to a hardware error.
@retval Others Other failure occurs.
**/
EFI_STATUS
PeiHubGetPortStatus (
IN EFI_PEI_SERVICES **PeiServices,
IN PEI_USB_IO_PPI *UsbIoPpi,
IN UINT8 Port,
OUT UINT32 *PortStatus
)
{
EFI_USB_DEVICE_REQUEST DeviceRequest;
ZeroMem (&DeviceRequest, sizeof (EFI_USB_DEVICE_REQUEST));
//
// Fill Device request packet
//
DeviceRequest.RequestType = USB_HUB_GET_PORT_STATUS_REQ_TYPE;
DeviceRequest.Request = USB_HUB_GET_PORT_STATUS;
DeviceRequest.Index = Port;
DeviceRequest.Length = (UINT16) sizeof (UINT32);
return UsbIoPpi->UsbControlTransfer (
PeiServices,
UsbIoPpi,
&DeviceRequest,
EfiUsbDataIn,
PcdGet32 (PcdUsbTransferTimeoutValue),
PortStatus,
sizeof (UINT32)
);
}
/**
Set specified feature to a given hub port.
@param PeiServices General-purpose services that are available to every PEIM.
@param UsbIoPpi Indicates the PEI_USB_IO_PPI instance.
@param Port Usb hub port number (starting from 1).
@param Value New feature value.
@retval EFI_SUCCESS Port feature is set successfully.
@retval EFI_DEVICE_ERROR Cannot set the port feature due to a hardware error.
@retval Others Other failure occurs.
**/
EFI_STATUS
PeiHubSetPortFeature (
IN EFI_PEI_SERVICES **PeiServices,
IN PEI_USB_IO_PPI *UsbIoPpi,
IN UINT8 Port,
IN UINT8 Value
)
{
EFI_USB_DEVICE_REQUEST DeviceRequest;
ZeroMem (&DeviceRequest, sizeof (EFI_USB_DEVICE_REQUEST));
//
// Fill Device request packet
//
DeviceRequest.RequestType = USB_HUB_SET_PORT_FEATURE_REQ_TYPE;
DeviceRequest.Request = USB_HUB_SET_PORT_FEATURE;
DeviceRequest.Value = Value;
DeviceRequest.Index = Port;
return UsbIoPpi->UsbControlTransfer (
PeiServices,
UsbIoPpi,
&DeviceRequest,
EfiUsbNoData,
PcdGet32 (PcdUsbTransferTimeoutValue),
NULL,
0
);
}
/**
Clear specified feature on a given hub port.
@param PeiServices General-purpose services that are available to every PEIM.
@param UsbIoPpi Indicates the PEI_USB_IO_PPI instance.
@param Port Usb hub port number (starting from 1).
@param Value Feature value that will be cleared from the hub port.
@retval EFI_SUCCESS Port feature is cleared successfully.
@retval EFI_DEVICE_ERROR Cannot clear the port feature due to a hardware error.
@retval Others Other failure occurs.
**/
EFI_STATUS
PeiHubClearPortFeature (
IN EFI_PEI_SERVICES **PeiServices,
IN PEI_USB_IO_PPI *UsbIoPpi,
IN UINT8 Port,
IN UINT8 Value
)
{
EFI_USB_DEVICE_REQUEST DeviceRequest;
ZeroMem (&DeviceRequest, sizeof (EFI_USB_DEVICE_REQUEST));
//
// Fill Device request packet
//
DeviceRequest.RequestType = USB_HUB_CLEAR_FEATURE_PORT_REQ_TYPE;
DeviceRequest.Request = USB_HUB_CLEAR_FEATURE_PORT;
DeviceRequest.Value = Value;
DeviceRequest.Index = Port;
return UsbIoPpi->UsbControlTransfer (
PeiServices,
UsbIoPpi,
&DeviceRequest,
EfiUsbNoData,
PcdGet32 (PcdUsbTransferTimeoutValue),
NULL,
0
);
}
/**
Get a given hub status.
@param PeiServices General-purpose services that are available to every PEIM.
@param UsbIoPpi Indicates the PEI_USB_IO_PPI instance.
@param HubStatus Current Hub status and change status.
@retval EFI_SUCCESS Hub status is obtained successfully.
@retval EFI_DEVICE_ERROR Cannot get the hub status due to a hardware error.
@retval Others Other failure occurs.
**/
EFI_STATUS
PeiHubGetHubStatus (
IN EFI_PEI_SERVICES **PeiServices,
IN PEI_USB_IO_PPI *UsbIoPpi,
OUT UINT32 *HubStatus
)
{
EFI_USB_DEVICE_REQUEST DeviceRequest;
ZeroMem (&DeviceRequest, sizeof (EFI_USB_DEVICE_REQUEST));
//
// Fill Device request packet
//
DeviceRequest.RequestType = USB_HUB_GET_HUB_STATUS_REQ_TYPE;
DeviceRequest.Request = USB_HUB_GET_HUB_STATUS;
DeviceRequest.Length = (UINT16) sizeof (UINT32);
return UsbIoPpi->UsbControlTransfer (
PeiServices,
UsbIoPpi,
&DeviceRequest,
EfiUsbDataIn,
PcdGet32 (PcdUsbTransferTimeoutValue),
HubStatus,
sizeof (UINT32)
);
}
/**
Clear specified feature on a given hub.
@param PeiServices General-purpose services that are available to every PEIM.
@param UsbIoPpi Indicates the PEI_USB_IO_PPI instance.
@param Value Feature value that will be cleared from the hub port.
@retval EFI_SUCCESS Hub feature is cleared successfully.
@retval EFI_DEVICE_ERROR Cannot clear the hub feature due to a hardware error.
@retval Others Other failure occurs.
**/
EFI_STATUS
PeiHubClearHubFeature (
IN EFI_PEI_SERVICES **PeiServices,
IN PEI_USB_IO_PPI *UsbIoPpi,
IN UINT8 Value
)
{
EFI_USB_DEVICE_REQUEST DeviceRequest;
ZeroMem (&DeviceRequest, sizeof (EFI_USB_DEVICE_REQUEST));
//
// Fill Device request packet
//
DeviceRequest.RequestType = USB_HUB_CLEAR_FEATURE_REQ_TYPE;
DeviceRequest.Request = USB_HUB_CLEAR_FEATURE;
DeviceRequest.Value = Value;
return UsbIoPpi->UsbControlTransfer (
PeiServices,
UsbIoPpi,
&DeviceRequest,
EfiUsbNoData,
PcdGet32 (PcdUsbTransferTimeoutValue),
NULL,
0
);
}
/**
Get a given (SuperSpeed) hub descriptor.
@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 DescriptorSize The length of Hub Descriptor buffer.
@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
PeiGetHubDescriptor (
IN EFI_PEI_SERVICES **PeiServices,
IN PEI_USB_DEVICE *PeiUsbDevice,
IN PEI_USB_IO_PPI *UsbIoPpi,
IN UINTN DescriptorSize,
OUT EFI_USB_HUB_DESCRIPTOR *HubDescriptor
)
{
EFI_USB_DEVICE_REQUEST DevReq;
UINT8 DescType;
ZeroMem (&DevReq, sizeof (EFI_USB_DEVICE_REQUEST));
DescType = (PeiUsbDevice->DeviceSpeed == EFI_USB_SPEED_SUPER) ?
USB_DT_SUPERSPEED_HUB :
USB_DT_HUB;
//
// Fill Device request packet
//
DevReq.RequestType = USB_RT_HUB | 0x80;
DevReq.Request = USB_HUB_GET_DESCRIPTOR;
DevReq.Value = (UINT16) (DescType << 8);
DevReq.Length = (UINT16) DescriptorSize;
return UsbIoPpi->UsbControlTransfer (
PeiServices,
UsbIoPpi,
&DevReq,
EfiUsbDataIn,
PcdGet32 (PcdUsbTransferTimeoutValue),
HubDescriptor,
(UINT16)DescriptorSize
);
}
/**
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;
//
// First get the hub descriptor length
//
Status = PeiGetHubDescriptor (PeiServices, PeiUsbDevice, UsbIoPpi, 2, HubDescriptor);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Get the whole hub descriptor
//
return PeiGetHubDescriptor (PeiServices, PeiUsbDevice, UsbIoPpi, HubDescriptor->Length, HubDescriptor);
}
/**
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.
@param PeiServices General-purpose services that are available to every PEIM.
@param PeiUsbDevice Indicating the hub controller device that will be configured
@retval EFI_SUCCESS Hub configuration is done successfully.
@retval EFI_DEVICE_ERROR Cannot configure the hub due to a hardware error.
**/
EFI_STATUS
PeiDoHubConfig (
IN EFI_PEI_SERVICES **PeiServices,
IN PEI_USB_DEVICE *PeiUsbDevice
)
{
UINT8 HubDescBuffer[256];
EFI_USB_HUB_DESCRIPTOR *HubDescriptor;
EFI_STATUS Status;
EFI_USB_HUB_STATUS HubStatus;
UINTN Index;
PEI_USB_IO_PPI *UsbIoPpi;
UsbIoPpi = &PeiUsbDevice->UsbIoPpi;
//
// The length field of descriptor is UINT8 type, so the buffer
// with 256 bytes is enough to hold the descriptor data.
//
HubDescriptor = (EFI_USB_HUB_DESCRIPTOR *) HubDescBuffer;
//
// Get the hub descriptor
//
Status = PeiUsbHubReadDesc (
PeiServices,
PeiUsbDevice,
UsbIoPpi,
HubDescriptor
);
if (EFI_ERROR (Status)) {
return EFI_DEVICE_ERROR;
}
PeiUsbDevice->DownStreamPortNo = HubDescriptor->NbrPorts;
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 {
//
// Power all the hub ports
//
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);
}
//
// Clear Hub Status Change
//
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
);
}
}
}
return EFI_SUCCESS;
}
/**
Send reset signal over the given root hub port.
@param PeiServices General-purpose services that are available to every PEIM.
@param UsbIoPpi Indicates the PEI_USB_IO_PPI instance.
@param PortNum Usb hub port number (starting from 1).
**/
VOID
PeiResetHubPort (
IN EFI_PEI_SERVICES **PeiServices,
IN PEI_USB_IO_PPI *UsbIoPpi,
IN UINT8 PortNum
)
{
EFI_STATUS Status;
UINTN Index;
EFI_USB_PORT_STATUS HubPortStatus;
MicroSecondDelay (100 * 1000);
//
// reset root port
//
PeiHubSetPortFeature (
PeiServices,
UsbIoPpi,
PortNum,
EfiUsbPortReset
);
//
// 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);
//
// 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 change root port
//
PeiHubClearPortFeature (
PeiServices,
UsbIoPpi,
PortNum,
EfiUsbPortResetChange
);
MicroSecondDelay (1 * 1000);
PeiHubClearPortFeature (
PeiServices,
UsbIoPpi,
PortNum,
EfiUsbPortConnectChange
);
//
// Set port enable
//
PeiHubSetPortFeature (
PeiServices,
UsbIoPpi,
PortNum,
EfiUsbPortEnable
);
//
// Clear any change status
//
PeiHubClearPortFeature (
PeiServices,
UsbIoPpi,
PortNum,
EfiUsbPortEnableChange
);
MicroSecondDelay (10 * 1000);
return;
}