/** @file Usb Hub Request Support In PEI Phase Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
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 ((DEBUG_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 ((DEBUG_ERROR, "PeiDoHubConfig: PeiHubSetPortFeature EfiUsbPortPower failed %x\n", Index)); continue; } } DEBUG ((DEBUG_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 ((DEBUG_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; }