From c3f44a7708fad4831c2a8e022ef1ca8a52f8222f Mon Sep 17 00:00:00 2001 From: Feng Tian Date: Fri, 22 Nov 2013 07:46:00 +0000 Subject: MdeModulePkg/UsbBus&XhciDxe: Solve a bug that 2 or more tiers SS hubs with SS devices may have no response. 1.Port reset process may not be proper for some vendor's SS hubs. If the corresponding port shows the reset has been done by C_RESET bit we have to skip the whole reset process for attached devices. 2.Clean C_BH_RESET bit immediately to avoid usb timer entering too many times when 5 tiers hubs are connected. 3.Stop checking URB if there is an error happened. 4.Better error handling for fast hot-plug. Signed-off-by: Feng Tian Reviewed-by: Elvin Li git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@14889 6f19259b-4bc3-4df7-8a09-765794883524 --- MdeModulePkg/Bus/Pci/XhciDxe/Xhci.c | 95 +++++++++++++++++++++----------- MdeModulePkg/Bus/Pci/XhciDxe/XhciReg.h | 18 +++++- MdeModulePkg/Bus/Pci/XhciDxe/XhciSched.c | 40 +++++++------- MdeModulePkg/Bus/Usb/UsbBusDxe/UsbHub.c | 19 +++++++ 4 files changed, 119 insertions(+), 53 deletions(-) (limited to 'MdeModulePkg/Bus') diff --git a/MdeModulePkg/Bus/Pci/XhciDxe/Xhci.c b/MdeModulePkg/Bus/Pci/XhciDxe/Xhci.c index 443df729d2..5ca58fbb9e 100644 --- a/MdeModulePkg/Bus/Pci/XhciDxe/Xhci.c +++ b/MdeModulePkg/Bus/Pci/XhciDxe/Xhci.c @@ -32,6 +32,13 @@ USB_PORT_STATE_MAP mUsbPortChangeMap[] = { {XHC_PORTSC_PRC, USB_PORT_STAT_C_RESET} }; +USB_CLEAR_PORT_MAP mUsbClearPortChangeMap[] = { + {XHC_PORTSC_CSC, EfiUsbPortConnectChange}, + {XHC_PORTSC_PEC, EfiUsbPortEnableChange}, + {XHC_PORTSC_OCC, EfiUsbPortOverCurrentChange}, + {XHC_PORTSC_PRC, EfiUsbPortResetChange} +}; + USB_PORT_STATE_MAP mUsbHubPortStateMap[] = { {XHC_HUB_PORTSC_CCS, USB_PORT_STAT_CONNECTION}, {XHC_HUB_PORTSC_PED, USB_PORT_STAT_ENABLE}, @@ -46,6 +53,14 @@ USB_PORT_STATE_MAP mUsbHubPortChangeMap[] = { {XHC_HUB_PORTSC_PRC, USB_PORT_STAT_C_RESET} }; +USB_CLEAR_PORT_MAP mUsbHubClearPortChangeMap[] = { + {XHC_HUB_PORTSC_CSC, EfiUsbPortConnectChange}, + {XHC_HUB_PORTSC_PEC, EfiUsbPortEnableChange}, + {XHC_HUB_PORTSC_OCC, EfiUsbPortOverCurrentChange}, + {XHC_HUB_PORTSC_PRC, EfiUsbPortResetChange}, + {XHC_HUB_PORTSC_BHRC, Usb3PortBHPortResetChange} +}; + EFI_DRIVER_BINDING_PROTOCOL gXhciDriverBinding = { XhcDriverBindingSupported, XhcDriverBindingStart, @@ -432,6 +447,14 @@ XhcGetRootHubPortStatus ( } } + MapSize = sizeof (mUsbClearPortChangeMap) / sizeof (USB_CLEAR_PORT_MAP); + + for (Index = 0; Index < MapSize; Index++) { + if (XHC_BIT_IS_SET (State, mUsbClearPortChangeMap[Index].HwState)) { + XhcClearRootHubPortFeature (This, PortNumber, mUsbClearPortChangeMap[Index].Selector); + } + } + // // Poll the root port status register to enable/disable corresponding device slot if there is a device attached/detached. // For those devices behind hub, we get its attach/detach event by hooking Get_Port_Status request at control transfer for those hub. @@ -469,8 +492,6 @@ XhcSetRootHubPortFeature ( UINT32 Offset; UINT32 State; UINT32 TotalPort; - UINT8 SlotId; - USB_DEV_ROUTE RouteChart; EFI_STATUS Status; EFI_TPL OldTpl; @@ -526,24 +547,13 @@ XhcSetRootHubPortFeature ( } } - RouteChart.Route.RouteString = 0; - RouteChart.Route.RootPortNum = PortNumber + 1; - RouteChart.Route.TierNum = 1; // - // If the port reset operation happens after the usb super speed device is enabled, - // The subsequent configuration, such as getting device descriptor, will fail. - // So here a workaround is introduced to skip the reset operation if the device is enabled. + // 4.3.1 Resetting a Root Hub Port + // 1) Write the PORTSC register with the Port Reset (PR) bit set to '1'. // - SlotId = XhcRouteStringToSlotId (Xhc, RouteChart); - if (SlotId == 0) { - // - // 4.3.1 Resetting a Root Hub Port - // 1) Write the PORTSC register with the Port Reset (PR) bit set to '1'. - // - State |= XHC_PORTSC_RESET; - XhcWriteOpReg (Xhc, Offset, State); - XhcWaitOpRegBit(Xhc, Offset, XHC_PORTSC_PRC, TRUE, XHC_GENERIC_TIMEOUT); - } + State |= XHC_PORTSC_RESET; + XhcWriteOpReg (Xhc, Offset, State); + XhcWaitOpRegBit(Xhc, Offset, XHC_PORTSC_PRC, TRUE, XHC_GENERIC_TIMEOUT); break; case EfiUsbPortPower: @@ -763,6 +773,8 @@ XhcControlTransfer ( UINTN MapSize; EFI_USB_PORT_STATUS PortStatus; UINT32 State; + EFI_USB_DEVICE_REQUEST ClearPortRequest; + UINTN Len; // // Validate parameters @@ -808,6 +820,7 @@ XhcControlTransfer ( Status = EFI_DEVICE_ERROR; *TransferResult = EFI_USB_ERR_SYSTEM; + Len = 0; if (XhcIsHalt (Xhc) || XhcIsSysError (Xhc)) { DEBUG ((EFI_D_ERROR, "XhcControlTransfer: HC halted at entrance\n")); @@ -839,6 +852,11 @@ XhcControlTransfer ( Xhc->UsbDevContext[Index + 1].BusDevAddr = 0; } } + + if (Xhc->UsbDevContext[SlotId].XhciDevAddr == 0) { + Status = EFI_DEVICE_ERROR; + goto ON_EXIT; + } // // The actual device address has been assigned by XHCI during initializing the device slot. // So we just need establish the mapping relationship between the device address requested from UsbBus @@ -849,20 +867,6 @@ XhcControlTransfer ( Status = EFI_SUCCESS; goto ON_EXIT; } - - // - // If the port reset operation happens after the usb super speed device is enabled, - // The subsequent configuration, such as getting device descriptor, will fail. - // So here a workaround is introduced to skip the reset operation if the device is enabled. - // - if ((Request->Request == USB_REQ_SET_FEATURE) && - (Request->RequestType == USB_REQUEST_TYPE (EfiUsbNoData, USB_REQ_TYPE_CLASS, USB_TARGET_OTHER)) && - (Request->Value == EfiUsbPortReset)) { - if (DeviceSpeed == EFI_USB_SPEED_SUPER) { - Status = EFI_SUCCESS; - goto ON_EXIT; - } - } // // Create a new URB, insert it into the asynchronous @@ -1050,6 +1054,33 @@ XhcControlTransfer ( } } + MapSize = sizeof (mUsbHubClearPortChangeMap) / sizeof (USB_CLEAR_PORT_MAP); + + for (Index = 0; Index < MapSize; Index++) { + if (XHC_BIT_IS_SET (State, mUsbHubClearPortChangeMap[Index].HwState)) { + ZeroMem (&ClearPortRequest, sizeof (EFI_USB_DEVICE_REQUEST)); + ClearPortRequest.RequestType = USB_REQUEST_TYPE (EfiUsbNoData, USB_REQ_TYPE_CLASS, USB_TARGET_OTHER); + ClearPortRequest.Request = (UINT8) USB_REQ_CLEAR_FEATURE; + ClearPortRequest.Value = mUsbHubClearPortChangeMap[Index].Selector; + ClearPortRequest.Index = Request->Index; + ClearPortRequest.Length = 0; + + XhcControlTransfer ( + This, + DeviceAddress, + DeviceSpeed, + MaximumPacketLength, + &ClearPortRequest, + EfiUsbNoData, + NULL, + &Len, + Timeout, + Translator, + TransferResult + ); + } + } + XhcPollPortStatusChange (Xhc, Xhc->UsbDevContext[SlotId].RouteString, (UINT8)Request->Index, &PortStatus); *(UINT32 *)Data = *(UINT32*)&PortStatus; diff --git a/MdeModulePkg/Bus/Pci/XhciDxe/XhciReg.h b/MdeModulePkg/Bus/Pci/XhciDxe/XhciReg.h index 20a5510bfe..dc82811984 100644 --- a/MdeModulePkg/Bus/Pci/XhciDxe/XhciReg.h +++ b/MdeModulePkg/Bus/Pci/XhciDxe/XhciReg.h @@ -2,7 +2,7 @@ This file contains the register definition of XHCI host controller. -Copyright (c) 2011 - 2012, Intel Corporation. All rights reserved.
+Copyright (c) 2011 - 2013, Intel Corporation. All rights reserved.
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 @@ -169,7 +169,7 @@ typedef union { #define XHC_PORTSC_RESET BIT4 // Port Reset #define XHC_PORTSC_PLS (BIT5|BIT6|BIT7|BIT8) // Port Link State #define XHC_PORTSC_PP BIT9 // Port Power -#define XHC_PORTSC_PS (BIT10|BIT11|BIT12|BIT13) // Port Speed +#define XHC_PORTSC_PS (BIT10|BIT11|BIT12) // Port Speed #define XHC_PORTSC_LWS BIT16 // Port Link State Write Strobe #define XHC_PORTSC_CSC BIT17 // Connect Status Change #define XHC_PORTSC_PEC BIT18 // Port Enabled/Disabled Change @@ -189,12 +189,18 @@ typedef union { #define XHC_HUB_PORTSC_PEC BIT17 // Hub's Port Enabled/Disabled Change #define XHC_HUB_PORTSC_OCC BIT19 // Hub's Over-Current Change #define XHC_HUB_PORTSC_PRC BIT20 // Hub's Port Reset Change +#define XHC_HUB_PORTSC_BHRC BIT21 // Hub's Port Warm Reset Change #define XHC_IMAN_IP BIT0 // Interrupt Pending #define XHC_IMAN_IE BIT1 // Interrupt Enable #define XHC_IMODI_MASK 0x0000FFFF // Interrupt Moderation Interval #define XHC_IMODC_MASK 0xFFFF0000 // Interrupt Moderation Counter +// +// Hub Class Feature Selector for Clear Port Feature Request +// +#define Usb3PortBHPortResetChange 29 + // // Structure to map the hardware port states to the // UEFI's port states. @@ -204,6 +210,14 @@ typedef struct { UINT16 UefiState; } USB_PORT_STATE_MAP; +// +// Structure to map the hardware port states to feature selector for clear port feature request. +// +typedef struct { + UINT32 HwState; + UINT16 Selector; +} USB_CLEAR_PORT_MAP; + /** Read 1-byte width XHCI capability register. diff --git a/MdeModulePkg/Bus/Pci/XhciDxe/XhciSched.c b/MdeModulePkg/Bus/Pci/XhciDxe/XhciSched.c index bcb419403d..ec13e49a7c 100644 --- a/MdeModulePkg/Bus/Pci/XhciDxe/XhciSched.c +++ b/MdeModulePkg/Bus/Pci/XhciDxe/XhciSched.c @@ -1105,25 +1105,25 @@ XhcCheckUrbResult ( CheckedUrb->Result |= EFI_USB_ERR_STALL; CheckedUrb->Finished = TRUE; DEBUG ((EFI_D_ERROR, "XhcCheckUrbResult: STALL_ERROR! Completecode = %x\n",EvtTrb->Completecode)); - break; + goto EXIT; case TRB_COMPLETION_BABBLE_ERROR: CheckedUrb->Result |= EFI_USB_ERR_BABBLE; CheckedUrb->Finished = TRUE; DEBUG ((EFI_D_ERROR, "XhcCheckUrbResult: BABBLE_ERROR! Completecode = %x\n",EvtTrb->Completecode)); - break; + goto EXIT; case TRB_COMPLETION_DATA_BUFFER_ERROR: CheckedUrb->Result |= EFI_USB_ERR_BUFFER; CheckedUrb->Finished = TRUE; DEBUG ((EFI_D_ERROR, "XhcCheckUrbResult: ERR_BUFFER! Completecode = %x\n",EvtTrb->Completecode)); - break; + goto EXIT; case TRB_COMPLETION_USB_TRANSACTION_ERROR: CheckedUrb->Result |= EFI_USB_ERR_TIMEOUT; CheckedUrb->Finished = TRUE; DEBUG ((EFI_D_ERROR, "XhcCheckUrbResult: TRANSACTION_ERROR! Completecode = %x\n",EvtTrb->Completecode)); - break; + goto EXIT; case TRB_COMPLETION_SHORT_PACKET: case TRB_COMPLETION_SUCCESS: @@ -1144,7 +1144,7 @@ XhcCheckUrbResult ( DEBUG ((EFI_D_ERROR, "Transfer Default Error Occur! Completecode = 0x%x!\n",EvtTrb->Completecode)); CheckedUrb->Result |= EFI_USB_ERR_TIMEOUT; CheckedUrb->Finished = TRUE; - break; + goto EXIT; } // @@ -1535,6 +1535,10 @@ XhcPollPortStatusChange ( Status = EFI_SUCCESS; + if ((PortState->PortChangeStatus & (USB_PORT_STAT_C_CONNECTION | USB_PORT_STAT_C_ENABLE | USB_PORT_STAT_C_OVERCURRENT | USB_PORT_STAT_C_RESET)) == 0) { + return EFI_SUCCESS; + } + if (ParentRouteChart.Dword == 0) { RouteChart.Route.RouteString = 0; RouteChart.Route.RootPortNum = Port + 1; @@ -1549,6 +1553,15 @@ XhcPollPortStatusChange ( RouteChart.Route.TierNum = ParentRouteChart.Route.TierNum + 1; } + SlotId = XhcRouteStringToSlotId (Xhc, RouteChart); + if (SlotId != 0) { + if (Xhc->HcCParams.Data.Csz == 0) { + Status = XhcDisableSlotCmd (Xhc, SlotId); + } else { + Status = XhcDisableSlotCmd64 (Xhc, SlotId); + } + } + if (((PortState->PortStatus & USB_PORT_STAT_ENABLE) != 0) && ((PortState->PortStatus & USB_PORT_STAT_CONNECTION) != 0)) { // @@ -1566,26 +1579,15 @@ XhcPollPortStatusChange ( // Execute Enable_Slot cmd for attached device, initialize device context and assign device address. // SlotId = XhcRouteStringToSlotId (Xhc, RouteChart); - if (SlotId == 0) { + if ((SlotId == 0) && ((PortState->PortChangeStatus & USB_PORT_STAT_C_RESET) != 0)) { if (Xhc->HcCParams.Data.Csz == 0) { Status = XhcInitializeDeviceSlot (Xhc, ParentRouteChart, Port, RouteChart, Speed); } else { Status = XhcInitializeDeviceSlot64 (Xhc, ParentRouteChart, Port, RouteChart, Speed); } } - } else if ((PortState->PortStatus & USB_PORT_STAT_CONNECTION) == 0) { - // - // Device is detached. Disable the allocated device slot and release resource. - // - SlotId = XhcRouteStringToSlotId (Xhc, RouteChart); - if (SlotId != 0) { - if (Xhc->HcCParams.Data.Csz == 0) { - Status = XhcDisableSlotCmd (Xhc, SlotId); - } else { - Status = XhcDisableSlotCmd64 (Xhc, SlotId); - } - } - } + } + return Status; } diff --git a/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbHub.c b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbHub.c index 2fcacad46e..e3752d1f83 100644 --- a/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbHub.c +++ b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbHub.c @@ -968,6 +968,15 @@ UsbHubResetPort ( UINTN Index; EFI_STATUS Status; + Status = UsbHubGetPortStatus (HubIf, Port, &PortState); + + if (EFI_ERROR (Status)) { + return Status; + } else if (USB_BIT_IS_SET (PortState.PortChangeStatus, USB_PORT_STAT_C_RESET)) { + DEBUG (( EFI_D_INFO, "UsbHubResetPort: skip reset on hub %p port %d\n", HubIf, Port)); + return EFI_SUCCESS; + } + Status = UsbHubSetPortFeature (HubIf, Port, (EFI_USB_PORT_FEATURE) USB_HUB_PORT_RESET); if (EFI_ERROR (Status)) { @@ -1272,6 +1281,16 @@ UsbRootHubResetPort ( // should be handled in the EHCI driver. // Bus = RootIf->Device->Bus; + + Status = UsbHcGetRootHubPortStatus (Bus, Port, &PortState); + + if (EFI_ERROR (Status)) { + return Status; + } else if (USB_BIT_IS_SET (PortState.PortChangeStatus, USB_PORT_STAT_C_RESET)) { + DEBUG (( EFI_D_INFO, "UsbRootHubResetPort: skip reset on root port %d\n", Port)); + return EFI_SUCCESS; + } + Status = UsbHcSetRootHubPortFeature (Bus, Port, EfiUsbPortReset); if (EFI_ERROR (Status)) { -- cgit v1.2.3