| /** @file | |
| Usb Hub Request Support In PEI Phase | |
| Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR> | |
| 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 "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) | |
| ); | |
| } | |
| /** | |
| Set specified feature to 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 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 | |
| PeiHubSetHubFeature ( | |
| 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_SET_HUB_FEATURE_REQ_TYPE; | |
| DeviceRequest.Request = USB_HUB_SET_HUB_FEATURE; | |
| DeviceRequest.Value = Value; | |
| return UsbIoPpi->UsbControlTransfer ( | |
| PeiServices, | |
| UsbIoPpi, | |
| &DeviceRequest, | |
| EfiUsbNoData, | |
| PcdGet32 (PcdUsbTransferTimeoutValue), | |
| NULL, | |
| 0 | |
| ); | |
| } | |
| /** | |
| 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 hub descriptor. | |
| @param PeiServices General-purpose services that are available to every PEIM. | |
| @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_IO_PPI *UsbIoPpi, | |
| IN UINTN DescriptorSize, | |
| 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_HUB << 8; | |
| DevReq.Length = (UINT16)DescriptorSize; | |
| return UsbIoPpi->UsbControlTransfer ( | |
| PeiServices, | |
| UsbIoPpi, | |
| &DevReq, | |
| EfiUsbDataIn, | |
| PcdGet32 (PcdUsbTransferTimeoutValue), | |
| HubDescriptor, | |
| (UINT16)DescriptorSize | |
| ); | |
| } | |
| /** | |
| 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. | |
| @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 | |
| ) | |
| { | |
| EFI_USB_HUB_DESCRIPTOR HubDescriptor; | |
| EFI_STATUS Status; | |
| EFI_USB_HUB_STATUS HubStatus; | |
| UINTN Index; | |
| PEI_USB_IO_PPI *UsbIoPpi; | |
| ZeroMem (&HubDescriptor, sizeof (HubDescriptor)); | |
| UsbIoPpi = &PeiUsbDevice->UsbIoPpi; | |
| // | |
| // 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; | |
| } |