From d987459f8e0b78831c95188b5b0d712ed6a54c88 Mon Sep 17 00:00:00 2001 From: Star Zeng Date: Wed, 2 Jul 2014 03:20:49 +0000 Subject: 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/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 ++++- 7 files changed, 491 insertions(+), 224 deletions(-) (limited to 'MdeModulePkg/Bus/Usb') 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. -- cgit v1.2.3