]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/EmmcDevice.c
MdeModulePkg/SdMmcPciHcDxe: Send SEND_STATUS at lower frequency
[mirror_edk2.git] / MdeModulePkg / Bus / Pci / SdMmcPciHcDxe / EmmcDevice.c
index bd704902863db50400fc9755dc3d7692cefdc172..8b5f8e8ee747305015ea464961f0ef0457be1231 100644 (file)
@@ -558,6 +558,43 @@ EmmcTuningClkForHs200 (
   return EFI_DEVICE_ERROR;\r
 }\r
 \r
+/**\r
+  Check the SWITCH operation status.\r
+\r
+  @param[in] PassThru  A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL instance.\r
+  @param[in] Slot      The slot number on which command should be sent.\r
+  @param[in] Rca       The relative device address.\r
+\r
+  @retval EFI_SUCCESS  The SWITCH finished siccessfully.\r
+  @retval others       The SWITCH failed.\r
+**/\r
+EFI_STATUS\r
+EmmcCheckSwitchStatus (\r
+  IN EFI_SD_MMC_PASS_THRU_PROTOCOL  *PassThru,\r
+  IN UINT8                          Slot,\r
+  IN UINT16                         Rca\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+  UINT32      DevStatus;\r
+\r
+  Status = EmmcSendStatus (PassThru, Slot, Rca, &DevStatus);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((DEBUG_ERROR, "EmmcCheckSwitchStatus: Send status fails with %r\n", Status));\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Check the switch operation is really successful or not.\r
+  //\r
+  if ((DevStatus & BIT7) != 0) {\r
+    DEBUG ((DEBUG_ERROR, "EmmcCheckSwitchStatus: The switch operation fails as DevStatus is 0x%08x\n", DevStatus));\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
 /**\r
   Switch the bus width to specified width.\r
 \r
@@ -591,7 +628,6 @@ EmmcSwitchBusWidth (
   UINT8               Index;\r
   UINT8               Value;\r
   UINT8               CmdSet;\r
-  UINT32              DevStatus;\r
 \r
   //\r
   // Write Byte, the Value field is written into the byte pointed by Index.\r
@@ -617,18 +653,10 @@ EmmcSwitchBusWidth (
     return Status;\r
   }\r
 \r
-  Status = EmmcSendStatus (PassThru, Slot, Rca, &DevStatus);\r
+  Status = EmmcCheckSwitchStatus (PassThru, Slot, Rca);\r
   if (EFI_ERROR (Status)) {\r
-    DEBUG ((DEBUG_ERROR, "EmmcSwitchBusWidth: Send status fails with %r\n", Status));\r
     return Status;\r
   }\r
-  //\r
-  // Check the switch operation is really successful or not.\r
-  //\r
-  if ((DevStatus & BIT7) != 0) {\r
-    DEBUG ((DEBUG_ERROR, "EmmcSwitchBusWidth: The switch operation fails as DevStatus is 0x%08x\n", DevStatus));\r
-    return EFI_DEVICE_ERROR;\r
-  }\r
 \r
   Status = SdMmcHcSetBusWidth (PciIo, Slot, BusWidth);\r
 \r
@@ -669,8 +697,9 @@ EmmcSwitchBusTiming (
   UINT8                     Index;\r
   UINT8                     Value;\r
   UINT8                     CmdSet;\r
-  UINT32                    DevStatus;\r
   SD_MMC_HC_PRIVATE_DATA    *Private;\r
+  UINT8                     HostCtrl1;\r
+  BOOLEAN                   DelaySendStatus;\r
 \r
   Private = SD_MMC_HC_PRIVATE_FROM_THIS (PassThru);\r
   //\r
@@ -694,7 +723,7 @@ EmmcSwitchBusTiming (
       Value = 0;\r
       break;\r
     default:\r
-      DEBUG ((DEBUG_ERROR, "EmmcSwitchBusTiming: Unsupported BusTiming(%d\n)", BusTiming));\r
+      DEBUG ((DEBUG_ERROR, "EmmcSwitchBusTiming: Unsupported BusTiming(%d)\n", BusTiming));\r
       return EFI_INVALID_PARAMETER;\r
   }\r
 \r
@@ -704,41 +733,56 @@ EmmcSwitchBusTiming (
     return Status;\r
   }\r
 \r
-  //\r
-  // Convert the clock freq unit from MHz to KHz.\r
-  //\r
-  Status = SdMmcHcClockSupply (PciIo, Slot, ClockFreq * 1000, Private->BaseClkFreq[Slot], Private->ControllerVersion[Slot]);\r
-  if (EFI_ERROR (Status)) {\r
-    return Status;\r
+  if (BusTiming == SdMmcMmcHsSdr || BusTiming == SdMmcMmcHsDdr) {\r
+    HostCtrl1 = BIT2;\r
+    Status = SdMmcHcOrMmio (PciIo, Slot, SD_MMC_HC_HOST_CTRL1, sizeof (HostCtrl1), &HostCtrl1);\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+  } else {\r
+    HostCtrl1 = (UINT8)~BIT2;\r
+    Status = SdMmcHcAndMmio (PciIo, Slot, SD_MMC_HC_HOST_CTRL1, sizeof (HostCtrl1), &HostCtrl1);\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
   }\r
 \r
-  Status = EmmcSendStatus (PassThru, Slot, Rca, &DevStatus);\r
+  Status = SdMmcHcUhsSignaling (Private->ControllerHandle, PciIo, Slot, BusTiming);\r
   if (EFI_ERROR (Status)) {\r
-    DEBUG ((DEBUG_ERROR, "EmmcSwitchBusTiming: Send status fails with %r\n", Status));\r
     return Status;\r
   }\r
+\r
   //\r
-  // Check the switch operation is really successful or not.\r
+  // For cases when we switch bus timing to higher mode from current we want to\r
+  // send SEND_STATUS at current, lower, frequency then the target frequency to avoid\r
+  // stability issues. It has been observed that some designs are unable to process the\r
+  // SEND_STATUS at higher frequency during switch to HS200 @200MHz irrespective of the number of retries\r
+  // and only running the clock tuning is able to make them work at target frequency.\r
   //\r
-  if ((DevStatus & BIT7) != 0) {\r
-    DEBUG ((DEBUG_ERROR, "EmmcSwitchBusTiming: The switch operation fails as DevStatus is 0x%08x\n", DevStatus));\r
-    return EFI_DEVICE_ERROR;\r
+  // For cases when we are downgrading the frequency and current high frequency is invalid\r
+  // we have to first change the frequency to target frequency and then send the SEND_STATUS.\r
+  //\r
+  if (Private->Slot[Slot].CurrentFreq < (ClockFreq * 1000)) {\r
+    Status = EmmcCheckSwitchStatus (PassThru, Slot, Rca);\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+    DelaySendStatus = FALSE;\r
+  } else {\r
+    DelaySendStatus = TRUE;\r
+  }\r
+\r
+  //\r
+  // Convert the clock freq unit from MHz to KHz.\r
+  //\r
+  Status = SdMmcHcClockSupply (Private, Slot, BusTiming, FALSE, ClockFreq * 1000);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
   }\r
 \r
-  if (mOverride != NULL && mOverride->NotifyPhase != NULL) {\r
-    Status = mOverride->NotifyPhase (\r
-                          Private->ControllerHandle,\r
-                          Slot,\r
-                          EdkiiSdMmcSwitchClockFreqPost,\r
-                          &BusTiming\r
-                          );\r
+  if (DelaySendStatus) {\r
+    Status = EmmcCheckSwitchStatus (PassThru, Slot, Rca);\r
     if (EFI_ERROR (Status)) {\r
-      DEBUG ((\r
-        DEBUG_ERROR,\r
-        "%a: SD/MMC switch clock freq post notifier callback failed - %r\n",\r
-        __FUNCTION__,\r
-        Status\r
-        ));\r
       return Status;\r
     }\r
   }\r
@@ -771,14 +815,10 @@ EmmcSwitchToHighSpeed (
   IN SD_MMC_BUS_SETTINGS                *BusMode\r
   )\r
 {\r
-  EFI_STATUS              Status;\r
-  UINT8                   HostCtrl1;\r
-  SD_MMC_HC_PRIVATE_DATA  *Private;\r
-  BOOLEAN                 IsDdr;\r
+  EFI_STATUS  Status;\r
+  BOOLEAN     IsDdr;\r
 \r
-  Private = SD_MMC_HC_PRIVATE_FROM_THIS (PassThru);\r
-\r
-  if ((BusMode->BusTiming != SdMmcMmcHsSdr && BusMode->BusTiming != SdMmcMmcHsDdr) ||\r
+  if ((BusMode->BusTiming != SdMmcMmcHsSdr && BusMode->BusTiming != SdMmcMmcHsDdr && BusMode->BusTiming != SdMmcMmcLegacy) ||\r
       BusMode->ClockFreq > 52) {\r
     return EFI_INVALID_PARAMETER;\r
   }\r
@@ -794,20 +834,6 @@ EmmcSwitchToHighSpeed (
     return Status;\r
   }\r
 \r
-  //\r
-  // Set to High Speed timing\r
-  //\r
-  HostCtrl1 = BIT2;\r
-  Status = SdMmcHcOrMmio (PciIo, Slot, SD_MMC_HC_HOST_CTRL1, sizeof (HostCtrl1), &HostCtrl1);\r
-  if (EFI_ERROR (Status)) {\r
-    return Status;\r
-  }\r
-\r
-  Status = SdMmcHcUhsSignaling (Private->ControllerHandle, PciIo, Slot, BusMode->BusTiming);\r
-  if (EFI_ERROR (Status)) {\r
-    return Status;\r
-  }\r
-\r
   return EmmcSwitchBusTiming (PciIo, PassThru, Slot, Rca, BusMode->DriverStrength, BusMode->BusTiming, BusMode->ClockFreq);\r
 }\r
 \r
@@ -836,10 +862,7 @@ EmmcSwitchToHS200 (
   IN SD_MMC_BUS_SETTINGS                *BusMode\r
   )\r
 {\r
-  EFI_STATUS               Status;\r
-  SD_MMC_HC_PRIVATE_DATA  *Private;\r
-\r
-  Private = SD_MMC_HC_PRIVATE_FROM_THIS (PassThru);\r
+  EFI_STATUS  Status;\r
 \r
   if (BusMode->BusTiming != SdMmcMmcHs200 ||\r
       (BusMode->BusWidth != 4 && BusMode->BusWidth != 8)) {\r
@@ -851,11 +874,6 @@ EmmcSwitchToHS200 (
     return Status;\r
   }\r
 \r
-  Status = SdMmcHcUhsSignaling (Private->ControllerHandle, PciIo, Slot, BusMode->BusTiming);\r
-  if (EFI_ERROR (Status)) {\r
-    return Status;\r
-  }\r
-\r
   Status = EmmcSwitchBusTiming (PciIo, PassThru, Slot, Rca, BusMode->DriverStrength, BusMode->BusTiming, BusMode->ClockFreq);\r
   if (EFI_ERROR (Status)) {\r
     return Status;\r
@@ -891,17 +909,15 @@ EmmcSwitchToHS400 (
   IN SD_MMC_BUS_SETTINGS                *BusMode\r
   )\r
 {\r
-  EFI_STATUS                 Status;\r
-  SD_MMC_HC_PRIVATE_DATA     *Private;\r
-  SD_MMC_BUS_SETTINGS        Hs200BusMode;\r
-  UINT32                     HsFreq;\r
+  EFI_STATUS           Status;\r
+  SD_MMC_BUS_SETTINGS  Hs200BusMode;\r
+  UINT32               HsFreq;\r
 \r
   if (BusMode->BusTiming != SdMmcMmcHs400 ||\r
       BusMode->BusWidth != 8) {\r
     return EFI_INVALID_PARAMETER;\r
   }\r
 \r
-  Private = SD_MMC_HC_PRIVATE_FROM_THIS (PassThru);\r
   Hs200BusMode.BusTiming = SdMmcMmcHs200;\r
   Hs200BusMode.BusWidth = BusMode->BusWidth;\r
   Hs200BusMode.ClockFreq = BusMode->ClockFreq;\r
@@ -916,11 +932,6 @@ EmmcSwitchToHS400 (
   // Set to High Speed timing and set the clock frequency to a value less than or equal to 52MHz.\r
   // This step is necessary to be able to switch Bus into 8 bit DDR mode which is unsupported in HS200.\r
   //\r
-  Status = SdMmcHcUhsSignaling (Private->ControllerHandle, PciIo, Slot, SdMmcMmcHsSdr);\r
-  if (EFI_ERROR (Status)) {\r
-    return Status;\r
-  }\r
-\r
   HsFreq = BusMode->ClockFreq < 52 ? BusMode->ClockFreq : 52;\r
   Status = EmmcSwitchBusTiming (PciIo, PassThru, Slot, Rca, BusMode->DriverStrength, SdMmcMmcHsSdr, HsFreq);\r
   if (EFI_ERROR (Status)) {\r
@@ -932,11 +943,6 @@ EmmcSwitchToHS400 (
     return Status;\r
   }\r
 \r
-  Status = SdMmcHcUhsSignaling (Private->ControllerHandle, PciIo, Slot, BusMode->BusTiming);\r
-  if (EFI_ERROR (Status)) {\r
-    return Status;\r
-  }\r
-\r
   return EmmcSwitchBusTiming (PciIo, PassThru, Slot, Rca, BusMode->DriverStrength, BusMode->BusTiming, BusMode->ClockFreq);\r
 }\r
 \r
@@ -1262,6 +1268,12 @@ EmmcSetBusMode (
   } else if (BusMode.BusTiming == SdMmcMmcHs200) {\r
     Status = EmmcSwitchToHS200 (PciIo, PassThru, Slot, Rca, &BusMode);\r
   } else {\r
+    //\r
+    // Note that EmmcSwitchToHighSpeed is also called for SdMmcMmcLegacy\r
+    // bus timing. This is because even though we might not want to\r
+    // change the timing itself we still want to allow customization of\r
+    // bus parameters such as clock frequency and bus width.\r
+    //\r
     Status = EmmcSwitchToHighSpeed (PciIo, PassThru, Slot, Rca, &BusMode);\r
   }\r
 \r