]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Bus/Usb/UsbBusPei/HubPeim.c
MdeModulePkg XhciPei/UsbBusPei: Add XHCI recovery support.
[mirror_edk2.git] / MdeModulePkg / Bus / Usb / UsbBusPei / HubPeim.c
index 5b7ebfad909688353a9fe4184e082db1bf99e4d4..16a7b589c1c2ca66ba58b16cc87a8fb6ee269242 100644 (file)
@@ -1,7 +1,7 @@
 /** @file\r
 Usb Hub Request Support In PEI Phase\r
 \r
 /** @file\r
 Usb Hub Request Support In PEI Phase\r
 \r
-Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>\r
+Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>\r
   \r
 This program and the accompanying materials\r
 are licensed and made available under the terms and conditions\r
   \r
 This program and the accompanying materials\r
 are licensed and made available under the terms and conditions\r
@@ -319,6 +319,139 @@ PeiGetHubDescriptor (
                       );\r
 }\r
 \r
                       );\r
 }\r
 \r
+/**\r
+  Get a given SuperSpeed hub descriptor.\r
+\r
+  @param  PeiServices       General-purpose services that are available to every PEIM.\r
+  @param  UsbIoPpi          Indicates the PEI_USB_IO_PPI instance.\r
+  @param  HubDescriptor     Caller allocated buffer to store the hub descriptor if\r
+                            successfully returned.\r
+\r
+  @retval EFI_SUCCESS       Hub descriptor is obtained successfully.\r
+  @retval EFI_DEVICE_ERROR  Cannot get the hub descriptor due to a hardware error.\r
+  @retval Others            Other failure occurs.\r
+\r
+**/\r
+EFI_STATUS\r
+PeiGetSuperSpeedHubDesc (\r
+  IN  EFI_PEI_SERVICES          **PeiServices,\r
+  IN  PEI_USB_IO_PPI            *UsbIoPpi,\r
+  OUT EFI_USB_HUB_DESCRIPTOR    *HubDescriptor\r
+  )\r
+{\r
+  EFI_USB_DEVICE_REQUEST        DevReq;\r
+  ZeroMem (&DevReq, sizeof (EFI_USB_DEVICE_REQUEST));\r
+\r
+  //\r
+  // Fill Device request packet\r
+  //\r
+  DevReq.RequestType = USB_RT_HUB | 0x80;\r
+  DevReq.Request     = USB_HUB_GET_DESCRIPTOR;\r
+  DevReq.Value       = USB_DT_SUPERSPEED_HUB << 8;\r
+  DevReq.Length      = 12;\r
+\r
+  return  UsbIoPpi->UsbControlTransfer (\r
+                      PeiServices,\r
+                      UsbIoPpi,\r
+                      &DevReq,\r
+                      EfiUsbDataIn,\r
+                      PcdGet32 (PcdUsbTransferTimeoutValue),\r
+                      HubDescriptor,\r
+                      12\r
+                      );\r
+}\r
+\r
+/**\r
+  Read the whole usb hub descriptor. It is necessary\r
+  to do it in two steps because hub descriptor is of\r
+  variable length.\r
+\r
+  @param  PeiServices       General-purpose services that are available to every PEIM.\r
+  @param  PeiUsbDevice      Indicates the hub controller device.\r
+  @param  UsbIoPpi          Indicates the PEI_USB_IO_PPI instance.\r
+  @param  HubDescriptor     Caller allocated buffer to store the hub descriptor if\r
+                            successfully returned.\r
+\r
+  @retval EFI_SUCCESS       Hub descriptor is obtained successfully.\r
+  @retval EFI_DEVICE_ERROR  Cannot get the hub descriptor due to a hardware error.\r
+  @retval Others            Other failure occurs.\r
+\r
+**/\r
+EFI_STATUS\r
+PeiUsbHubReadDesc (\r
+  IN EFI_PEI_SERVICES           **PeiServices,\r
+  IN PEI_USB_DEVICE             *PeiUsbDevice,\r
+  IN PEI_USB_IO_PPI             *UsbIoPpi,\r
+  OUT EFI_USB_HUB_DESCRIPTOR    *HubDescriptor\r
+  )\r
+{\r
+  EFI_STATUS Status;\r
+\r
+  if (PeiUsbDevice->DeviceSpeed == EFI_USB_SPEED_SUPER) {\r
+    //\r
+    // Get the super speed hub descriptor\r
+    //\r
+    Status = PeiGetSuperSpeedHubDesc (PeiServices, UsbIoPpi, HubDescriptor);\r
+  } else {\r
+\r
+    //\r
+    // First get the hub descriptor length\r
+    //\r
+    Status = PeiGetHubDescriptor (PeiServices, UsbIoPpi, 2, HubDescriptor);\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+\r
+    //\r
+    // Get the whole hub descriptor\r
+    //\r
+    Status = PeiGetHubDescriptor (PeiServices, UsbIoPpi, HubDescriptor->Length, HubDescriptor);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  USB hub control transfer to set the hub depth.\r
+\r
+  @param  PeiServices       General-purpose services that are available to every PEIM.\r
+  @param  PeiUsbDevice      Indicates the hub controller device.\r
+  @param  UsbIoPpi          Indicates the PEI_USB_IO_PPI instance.\r
+\r
+  @retval EFI_SUCCESS       Depth of the hub is set.\r
+  @retval Others            Failed to set the depth.\r
+\r
+**/\r
+EFI_STATUS\r
+PeiUsbHubCtrlSetHubDepth (\r
+  IN EFI_PEI_SERVICES           **PeiServices,\r
+  IN PEI_USB_DEVICE             *PeiUsbDevice,\r
+  IN PEI_USB_IO_PPI             *UsbIoPpi\r
+  )\r
+{\r
+  EFI_USB_DEVICE_REQUEST        DevReq;\r
+  ZeroMem (&DevReq, sizeof (EFI_USB_DEVICE_REQUEST));\r
+\r
+  //\r
+  // Fill Device request packet\r
+  //\r
+  DevReq.RequestType = USB_RT_HUB;\r
+  DevReq.Request     = USB_HUB_REQ_SET_DEPTH;\r
+  DevReq.Value       = PeiUsbDevice->Tier;\r
+  DevReq.Length      = 0;\r
+\r
+  return  UsbIoPpi->UsbControlTransfer (\r
+                      PeiServices,\r
+                      UsbIoPpi,\r
+                      &DevReq,\r
+                      EfiUsbNoData,\r
+                      PcdGet32 (PcdUsbTransferTimeoutValue),\r
+                      NULL,\r
+                      0\r
+                      );\r
+}\r
+\r
 /**\r
   Configure a given hub.\r
 \r
 /**\r
   Configure a given hub.\r
 \r
@@ -339,32 +472,18 @@ PeiDoHubConfig (
   EFI_STATUS              Status;\r
   EFI_USB_HUB_STATUS      HubStatus;\r
   UINTN                   Index;\r
   EFI_STATUS              Status;\r
   EFI_USB_HUB_STATUS      HubStatus;\r
   UINTN                   Index;\r
-  UINT32                  PortStatus;\r
   PEI_USB_IO_PPI          *UsbIoPpi;\r
 \r
   ZeroMem (&HubDescriptor, sizeof (HubDescriptor));\r
   UsbIoPpi = &PeiUsbDevice->UsbIoPpi;\r
 \r
   //\r
   PEI_USB_IO_PPI          *UsbIoPpi;\r
 \r
   ZeroMem (&HubDescriptor, sizeof (HubDescriptor));\r
   UsbIoPpi = &PeiUsbDevice->UsbIoPpi;\r
 \r
   //\r
-  // First get the hub descriptor length\r
-  //\r
-  Status = PeiGetHubDescriptor (\r
-            PeiServices,\r
-            UsbIoPpi,\r
-            2,\r
-            &HubDescriptor\r
-            );\r
-  if (EFI_ERROR (Status)) {\r
-    return EFI_DEVICE_ERROR;\r
-  }\r
-  //\r
-  // First get the whole descriptor, then\r
-  // get the number of hub ports\r
+  // Get the hub descriptor \r
   //\r
   //\r
-  Status = PeiGetHubDescriptor (\r
+  Status = PeiUsbHubReadDesc (\r
             PeiServices,\r
             PeiServices,\r
+            PeiUsbDevice,\r
             UsbIoPpi,\r
             UsbIoPpi,\r
-            HubDescriptor.Length,\r
             &HubDescriptor\r
             );\r
   if (EFI_ERROR (Status)) {\r
             &HubDescriptor\r
             );\r
   if (EFI_ERROR (Status)) {\r
@@ -373,74 +492,66 @@ PeiDoHubConfig (
 \r
   PeiUsbDevice->DownStreamPortNo = HubDescriptor.NbrPorts;\r
 \r
 \r
   PeiUsbDevice->DownStreamPortNo = HubDescriptor.NbrPorts;\r
 \r
-  Status = PeiHubGetHubStatus (\r
-            PeiServices,\r
-            UsbIoPpi,\r
-            (UINT32 *) &HubStatus\r
-            );\r
-\r
-  if (EFI_ERROR (Status)) {\r
-    return EFI_DEVICE_ERROR;\r
-  }\r
-  //\r
-  //  Get all hub ports status\r
-  //\r
-  for (Index = 0; Index < PeiUsbDevice->DownStreamPortNo; Index++) {\r
-\r
-    Status = PeiHubGetPortStatus (\r
-              PeiServices,\r
-              UsbIoPpi,\r
-              (UINT8) (Index + 1),\r
-              &PortStatus\r
-              );\r
-    if (EFI_ERROR (Status)) {\r
-      continue;\r
-    }\r
-  }\r
-  //\r
-  //  Power all the hub ports\r
-  //\r
-  for (Index = 0; Index < PeiUsbDevice->DownStreamPortNo; Index++) {\r
-    Status = PeiHubSetPortFeature (\r
-              PeiServices,\r
-              UsbIoPpi,\r
-              (UINT8) (Index + 1),\r
-              EfiUsbPortPower\r
-              );\r
-    if (EFI_ERROR (Status)) {\r
-      continue;\r
-    }\r
-  }\r
-  //\r
-  // Clear Hub Status Change\r
-  //\r
-  Status = PeiHubGetHubStatus (\r
-            PeiServices,\r
-            UsbIoPpi,\r
-            (UINT32 *) &HubStatus\r
-            );\r
-  if (EFI_ERROR (Status)) {\r
-    return EFI_DEVICE_ERROR;\r
+  if (PeiUsbDevice->DeviceSpeed == EFI_USB_SPEED_SUPER) {\r
+    DEBUG ((EFI_D_INFO, "PeiDoHubConfig: Set Hub Depth as 0x%x\n", PeiUsbDevice->Tier));\r
+    PeiUsbHubCtrlSetHubDepth (\r
+      PeiServices,\r
+      PeiUsbDevice,\r
+      UsbIoPpi\r
+      );\r
   } else {\r
     //\r
   } else {\r
     //\r
-    // Hub power supply change happens\r
+    //  Power all the hub ports\r
     //\r
     //\r
-    if ((HubStatus.HubChangeStatus & HUB_CHANGE_LOCAL_POWER) != 0) {\r
-      PeiHubClearHubFeature (\r
-        PeiServices,\r
-        UsbIoPpi,\r
-        C_HUB_LOCAL_POWER\r
-        );\r
+    for (Index = 0; Index < PeiUsbDevice->DownStreamPortNo; Index++) {\r
+      Status = PeiHubSetPortFeature (\r
+                PeiServices,\r
+                UsbIoPpi,\r
+                (UINT8) (Index + 1),\r
+                EfiUsbPortPower\r
+                );\r
+      if (EFI_ERROR (Status)) {\r
+        DEBUG (( EFI_D_ERROR, "PeiDoHubConfig: PeiHubSetPortFeature EfiUsbPortPower failed %x\n", Index));\r
+        continue;\r
+      }\r
     }\r
     }\r
+\r
+    DEBUG (( EFI_D_INFO, "PeiDoHubConfig: HubDescriptor.PwrOn2PwrGood: 0x%x\n", HubDescriptor.PwrOn2PwrGood));\r
+    if (HubDescriptor.PwrOn2PwrGood > 0) {\r
+      MicroSecondDelay (HubDescriptor.PwrOn2PwrGood * USB_SET_PORT_POWER_STALL);\r
+    }\r
+\r
     //\r
     //\r
-    // Hub change overcurrent happens\r
+    // Clear Hub Status Change\r
     //\r
     //\r
-    if ((HubStatus.HubChangeStatus & HUB_CHANGE_OVERCURRENT) != 0) {\r
-      PeiHubClearHubFeature (\r
-        PeiServices,\r
-        UsbIoPpi,\r
-        C_HUB_OVER_CURRENT\r
-        );\r
+    Status = PeiHubGetHubStatus (\r
+              PeiServices,\r
+              UsbIoPpi,\r
+              (UINT32 *) &HubStatus\r
+              );\r
+    if (EFI_ERROR (Status)) {\r
+      return EFI_DEVICE_ERROR;\r
+    } else {\r
+      //\r
+      // Hub power supply change happens\r
+      //\r
+      if ((HubStatus.HubChangeStatus & HUB_CHANGE_LOCAL_POWER) != 0) {\r
+        PeiHubClearHubFeature (\r
+          PeiServices,\r
+          UsbIoPpi,\r
+          C_HUB_LOCAL_POWER\r
+          );\r
+      }\r
+      //\r
+      // Hub change overcurrent happens\r
+      //\r
+      if ((HubStatus.HubChangeStatus & HUB_CHANGE_OVERCURRENT) != 0) {\r
+        PeiHubClearHubFeature (\r
+          PeiServices,\r
+          UsbIoPpi,\r
+          C_HUB_OVER_CURRENT\r
+          );\r
+      }\r
     }\r
   }\r
 \r
     }\r
   }\r
 \r
@@ -462,10 +573,10 @@ PeiResetHubPort (
   IN UINT8               PortNum\r
   )\r
 {\r
   IN UINT8               PortNum\r
   )\r
 {\r
-  UINT8               Try;\r
+  EFI_STATUS          Status;\r
+  UINTN               Index;\r
   EFI_USB_PORT_STATUS HubPortStatus;\r
 \r
   EFI_USB_PORT_STATUS HubPortStatus;\r
 \r
-\r
   MicroSecondDelay (100 * 1000);\r
 \r
   //\r
   MicroSecondDelay (100 * 1000);\r
 \r
   //\r
@@ -478,27 +589,49 @@ PeiResetHubPort (
     EfiUsbPortReset\r
     );\r
 \r
     EfiUsbPortReset\r
     );\r
 \r
-  Try = 10;\r
-  do {\r
-    PeiHubGetPortStatus (\r
-      PeiServices,\r
-      UsbIoPpi,\r
-      PortNum,\r
-      (UINT32 *) &HubPortStatus\r
-      );\r
+  //\r
+  // Drive the reset signal for worst 20ms. Check USB 2.0 Spec\r
+  // section 7.1.7.5 for timing requirements.\r
+  //\r
+  MicroSecondDelay (USB_SET_PORT_RESET_STALL);\r
 \r
 \r
-    MicroSecondDelay (2 * 1000);\r
-    Try -= 1;\r
-  } while ((HubPortStatus.PortChangeStatus & USB_PORT_STAT_C_RESET) == 0 && Try > 0);\r
+  //\r
+  // Check USB_PORT_STAT_C_RESET bit to see if the resetting state is done.\r
+  //\r
+  ZeroMem (&HubPortStatus, sizeof (EFI_USB_PORT_STATUS));\r
+\r
+  for (Index = 0; Index < USB_WAIT_PORT_STS_CHANGE_LOOP; Index++) {\r
+    Status = PeiHubGetPortStatus (\r
+               PeiServices,\r
+               UsbIoPpi,\r
+               PortNum,\r
+               (UINT32 *) &HubPortStatus\r
+               );\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      return;\r
+    }\r
+\r
+    if (USB_BIT_IS_SET (HubPortStatus.PortChangeStatus, USB_PORT_STAT_C_RESET)) {\r
+      break;\r
+    }\r
+\r
+    MicroSecondDelay (USB_WAIT_PORT_STS_CHANGE_STALL);\r
+  }\r
+\r
+  if (Index == USB_WAIT_PORT_STS_CHANGE_LOOP) {\r
+    DEBUG ((EFI_D_ERROR, "PeiResetHubPort: reset not finished in time on port %d\n", PortNum));\r
+    return;\r
+  }\r
 \r
   //\r
 \r
   //\r
-  // clear reset root port\r
+  // clear reset change root port\r
   //\r
   PeiHubClearPortFeature (\r
     PeiServices,\r
     UsbIoPpi,\r
     PortNum,\r
   //\r
   PeiHubClearPortFeature (\r
     PeiServices,\r
     UsbIoPpi,\r
     PortNum,\r
-    EfiUsbPortReset\r
+    EfiUsbPortResetChange\r
     );\r
 \r
   MicroSecondDelay (1 * 1000);\r
     );\r
 \r
   MicroSecondDelay (1 * 1000);\r