]> git.proxmox.com Git - mirror_edk2.git/blobdiff - EmbeddedPkg/Universal/MmcDxe/MmcIdentification.c
EmbeddedPkg/MmcDxe: set I/O speed and bus width in SD stack
[mirror_edk2.git] / EmbeddedPkg / Universal / MmcDxe / MmcIdentification.c
index 2d8038ffe02f14ff66e0fb0d2958279eda2ca2ff..574a77e0fbb56b717d1243571ddf820fe2b83404 100644 (file)
@@ -12,6 +12,9 @@
 *\r
 **/\r
 \r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/TimerLib.h>\r
+\r
 #include "Mmc.h"\r
 \r
 typedef union {\r
@@ -25,8 +28,113 @@ typedef union {
 #define EMMC_CARD_SIZE          512\r
 #define EMMC_ECSD_SIZE_OFFSET   53\r
 \r
+#define EXTCSD_BUS_WIDTH        183\r
+#define EXTCSD_HS_TIMING        185\r
+\r
+#define EMMC_TIMING_BACKWARD    0\r
+#define EMMC_TIMING_HS          1\r
+#define EMMC_TIMING_HS200       2\r
+#define EMMC_TIMING_HS400       3\r
+\r
+#define EMMC_BUS_WIDTH_1BIT     0\r
+#define EMMC_BUS_WIDTH_4BIT     1\r
+#define EMMC_BUS_WIDTH_8BIT     2\r
+#define EMMC_BUS_WIDTH_DDR_4BIT 5\r
+#define EMMC_BUS_WIDTH_DDR_8BIT 6\r
+\r
+#define EMMC_SWITCH_ERROR       (1 << 7)\r
+\r
+#define SD_BUS_WIDTH_1BIT       (1 << 0)\r
+#define SD_BUS_WIDTH_4BIT       (1 << 2)\r
+\r
+#define SD_CCC_SWITCH           (1 << 10)\r
+\r
+#define DEVICE_STATE(x)         (((x) >> 9) & 0xf)\r
+typedef enum _EMMC_DEVICE_STATE {\r
+  EMMC_IDLE_STATE = 0,\r
+  EMMC_READY_STATE,\r
+  EMMC_IDENT_STATE,\r
+  EMMC_STBY_STATE,\r
+  EMMC_TRAN_STATE,\r
+  EMMC_DATA_STATE,\r
+  EMMC_RCV_STATE,\r
+  EMMC_PRG_STATE,\r
+  EMMC_DIS_STATE,\r
+  EMMC_BTST_STATE,\r
+  EMMC_SLP_STATE\r
+} EMMC_DEVICE_STATE;\r
+\r
 UINT32 mEmmcRcaCount = 0;\r
 \r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+EmmcGetDeviceState (\r
+  IN  MMC_HOST_INSTANCE    *MmcHostInstance,\r
+  OUT EMMC_DEVICE_STATE    *State\r
+  )\r
+{\r
+  EFI_MMC_HOST_PROTOCOL *Host;\r
+  EFI_STATUS Status;\r
+  UINT32     Data, RCA;\r
+\r
+  if (State == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Host = MmcHostInstance->MmcHost;\r
+  RCA = MmcHostInstance->CardInfo.RCA << RCA_SHIFT_OFFSET;\r
+  Status = Host->SendCommand (Host, MMC_CMD13, RCA);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((EFI_D_ERROR, "EmmcGetDeviceState(): Failed to get card status, Status=%r.\n", Status));\r
+    return Status;\r
+  }\r
+  Status = Host->ReceiveResponse (Host, MMC_RESPONSE_TYPE_R1, &Data);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((EFI_D_ERROR, "EmmcGetDeviceState(): Failed to get response of CMD13, Status=%r.\n", Status));\r
+    return Status;\r
+  }\r
+  if (Data & EMMC_SWITCH_ERROR) {\r
+    DEBUG ((EFI_D_ERROR, "EmmcGetDeviceState(): Failed to switch expected mode, Status=%r.\n", Status));\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+  *State = DEVICE_STATE(Data);\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+EmmcSetEXTCSD (\r
+  IN MMC_HOST_INSTANCE     *MmcHostInstance,\r
+  UINT32                   ExtCmdIndex,\r
+  UINT32                   Value\r
+  )\r
+{\r
+  EFI_MMC_HOST_PROTOCOL *Host;\r
+  EMMC_DEVICE_STATE     State;\r
+  EFI_STATUS Status;\r
+  UINT32     Argument;\r
+\r
+  Host  = MmcHostInstance->MmcHost;\r
+  Argument = EMMC_CMD6_ARG_ACCESS(3) | EMMC_CMD6_ARG_INDEX(ExtCmdIndex) |\r
+             EMMC_CMD6_ARG_VALUE(Value) | EMMC_CMD6_ARG_CMD_SET(1);\r
+  Status = Host->SendCommand (Host, MMC_CMD6, Argument);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((EFI_D_ERROR, "EmmcSetEXTCSD(): Failed to send CMD6, Status=%r.\n", Status));\r
+    return Status;\r
+  }\r
+  // Make sure device exiting prog mode\r
+  do {\r
+    Status = EmmcGetDeviceState (MmcHostInstance, &State);\r
+    if (EFI_ERROR (Status)) {\r
+      DEBUG ((EFI_D_ERROR, "EmmcSetEXTCSD(): Failed to get device state, Status=%r.\n", Status));\r
+      return Status;\r
+    }\r
+  } while (State == EMMC_PRG_STATE);\r
+  return EFI_SUCCESS;\r
+}\r
+\r
 STATIC\r
 EFI_STATUS\r
 EFIAPI\r
@@ -38,8 +146,8 @@ EmmcIdentificationMode (
   EFI_MMC_HOST_PROTOCOL *Host;\r
   EFI_BLOCK_IO_MEDIA    *Media;\r
   EFI_STATUS Status;\r
+  EMMC_DEVICE_STATE     State;\r
   UINT32     RCA;\r
-  UINT32     ECSD[128];\r
 \r
   Host  = MmcHostInstance->MmcHost;\r
   Media = MmcHostInstance->BlockIo.Media;\r
@@ -85,18 +193,43 @@ EmmcIdentificationMode (
     DEBUG ((EFI_D_ERROR, "EmmcIdentificationMode(): Card selection error, Status=%r.\n", Status));\r
   }\r
 \r
+  if (MMC_HOST_HAS_SETIOS(Host)) {\r
+    // Set 1-bit bus width\r
+    Status = Host->SetIos (Host, 0, 1, EMMCBACKWARD);\r
+    if (EFI_ERROR (Status)) {\r
+      DEBUG ((EFI_D_ERROR, "EmmcIdentificationMode(): Set 1-bit bus width error, Status=%r.\n", Status));\r
+      return Status;\r
+    }\r
+\r
+    // Set 1-bit bus width for EXTCSD\r
+    Status = EmmcSetEXTCSD (MmcHostInstance, EXTCSD_BUS_WIDTH, EMMC_BUS_WIDTH_1BIT);\r
+    if (EFI_ERROR (Status)) {\r
+      DEBUG ((EFI_D_ERROR, "EmmcIdentificationMode(): Set extcsd bus width error, Status=%r.\n", Status));\r
+      return Status;\r
+    }\r
+  }\r
+\r
   // Fetch ECSD\r
   Status = Host->SendCommand (Host, MMC_CMD8, RCA);\r
   if (EFI_ERROR (Status)) {\r
     DEBUG ((EFI_D_ERROR, "EmmcIdentificationMode(): ECSD fetch error, Status=%r.\n", Status));\r
   }\r
 \r
-  Status = Host->ReadBlockData (Host, 0, 512, ECSD);\r
+  Status = Host->ReadBlockData (Host, 0, 512, (UINT32 *)&(MmcHostInstance->CardInfo.ECSDData));\r
   if (EFI_ERROR (Status)) {\r
     DEBUG ((EFI_D_ERROR, "EmmcIdentificationMode(): ECSD read error, Status=%r.\n", Status));\r
     return Status;\r
   }\r
 \r
+  // Make sure device exiting data mode\r
+  do {\r
+    Status = EmmcGetDeviceState (MmcHostInstance, &State);\r
+    if (EFI_ERROR (Status)) {\r
+      DEBUG ((EFI_D_ERROR, "EmmcIdentificationMode(): Failed to get device state, Status=%r.\n", Status));\r
+      return Status;\r
+    }\r
+  } while (State == EMMC_DATA_STATE);\r
+\r
   // Set up media\r
   Media->BlockSize = EMMC_CARD_SIZE; // 512-byte support is mandatory for eMMC cards\r
   Media->MediaId = MmcHostInstance->CardInfo.CIDData.PSN;\r
@@ -104,7 +237,7 @@ EmmcIdentificationMode (
   Media->LogicalBlocksPerPhysicalBlock = 1;\r
   Media->IoAlign = 4;\r
   // Compute last block using bits [215:212] of the ECSD\r
-  Media->LastBlock = ECSD[EMMC_ECSD_SIZE_OFFSET] - 1; // eMMC isn't supposed to report this for\r
+  Media->LastBlock = MmcHostInstance->CardInfo.ECSDData.SECTOR_COUNT - 1; // eMMC isn't supposed to report this for\r
   // Cards <2GB in size, but the model does.\r
 \r
   // Setup card type\r
@@ -112,6 +245,57 @@ EmmcIdentificationMode (
   return EFI_SUCCESS;\r
 }\r
 \r
+STATIC\r
+EFI_STATUS\r
+InitializeEmmcDevice (\r
+  IN  MMC_HOST_INSTANCE   *MmcHostInstance\r
+  )\r
+{\r
+  EFI_MMC_HOST_PROTOCOL *Host;\r
+  EFI_STATUS Status = EFI_SUCCESS;\r
+  ECSD       *ECSDData;\r
+  UINT32     BusClockFreq, Idx;\r
+  UINT32     TimingMode[4] = {EMMCHS52DDR1V2, EMMCHS52DDR1V8, EMMCHS52, EMMCHS26};\r
+\r
+  Host  = MmcHostInstance->MmcHost;\r
+  ECSDData = &MmcHostInstance->CardInfo.ECSDData;\r
+  if (ECSDData->DEVICE_TYPE == EMMCBACKWARD)\r
+    return EFI_SUCCESS;\r
+\r
+  if (!MMC_HOST_HAS_SETIOS(Host)) {\r
+    return EFI_SUCCESS;\r
+  }\r
+  Status = EmmcSetEXTCSD (MmcHostInstance, EXTCSD_HS_TIMING, EMMC_TIMING_HS);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((DEBUG_ERROR, "InitializeEmmcDevice(): Failed to switch high speed mode, Status:%r.\n", Status));\r
+    return Status;\r
+  }\r
+\r
+  for (Idx = 0; Idx < 4; Idx++) {\r
+    switch (TimingMode[Idx]) {\r
+    case EMMCHS52DDR1V2:\r
+    case EMMCHS52DDR1V8:\r
+    case EMMCHS52:\r
+      BusClockFreq = 52000000;\r
+      break;\r
+    case EMMCHS26:\r
+      BusClockFreq = 26000000;\r
+      break;\r
+    default:\r
+      return EFI_UNSUPPORTED;\r
+    }\r
+    Status = Host->SetIos (Host, BusClockFreq, 8, TimingMode[Idx]);\r
+    if (!EFI_ERROR (Status)) {\r
+      Status = EmmcSetEXTCSD (MmcHostInstance, EXTCSD_BUS_WIDTH, EMMC_BUS_WIDTH_DDR_8BIT);\r
+      if (EFI_ERROR (Status)) {\r
+        DEBUG ((DEBUG_ERROR, "InitializeEmmcDevice(): Failed to set EXTCSD bus width, Status:%r\n", Status));\r
+      }\r
+      return Status;\r
+    }\r
+  }\r
+  return Status;\r
+}\r
+\r
 STATIC\r
 EFI_STATUS\r
 InitializeSdMmcDevice (\r
@@ -120,9 +304,12 @@ InitializeSdMmcDevice (
 {\r
   UINT32        CmdArg;\r
   UINT32        Response[4];\r
+  UINT32        Buffer[128];\r
   UINTN         BlockSize;\r
   UINTN         CardSize;\r
   UINTN         NumBlocks;\r
+  BOOLEAN       CccSwitch;\r
+  SCR           Scr;\r
   EFI_STATUS    Status;\r
   EFI_MMC_HOST_PROTOCOL     *MmcHost;\r
 \r
@@ -143,6 +330,11 @@ InitializeSdMmcDevice (
     return Status;\r
   }\r
   PrintCSD (Response);\r
+  if (MMC_CSD_GET_CCC(Response) & SD_CCC_SWITCH) {\r
+    CccSwitch = TRUE;\r
+  } else {\r
+    CccSwitch = FALSE;\r
+  }\r
 \r
   if (MmcHostInstance->CardInfo.CardType == SD_CARD_2_HIGH) {\r
     CardSize = HC_MMC_CSD_GET_DEVICESIZE (Response);\r
@@ -173,6 +365,96 @@ InitializeSdMmcDevice (
     return Status;\r
   }\r
 \r
+  Status = MmcHost->SendCommand (MmcHost, MMC_CMD55, CmdArg);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((EFI_D_ERROR, "%a(MMC_CMD55): Error and Status = %r\n", Status));\r
+    return Status;\r
+  }\r
+  Status = MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_R1, Response);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((EFI_D_ERROR, "%a(MMC_CMD55): Error and Status = %r\n", Status));\r
+    return Status;\r
+  }\r
+  if ((Response[0] & MMC_STATUS_APP_CMD) == 0) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  /* SCR */\r
+  Status = MmcHost->SendCommand (MmcHost, MMC_ACMD51, 0);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((EFI_D_ERROR, "%a(MMC_ACMD51): Error and Status = %r\n", __func__, Status));\r
+    return Status;\r
+  } else {\r
+    Status = MmcHost->ReadBlockData (MmcHost, 0, 8, Buffer);\r
+    if (EFI_ERROR (Status)) {\r
+      DEBUG ((EFI_D_ERROR, "%a(MMC_ACMD51): ReadBlockData Error and Status = %r\n", __func__, Status));\r
+      return Status;\r
+    }\r
+    CopyMem (&Scr, Buffer, 8);\r
+    if (Scr.SD_SPEC == 2) {\r
+      if (Scr.SD_SPEC3 == 1) {\r
+       if (Scr.SD_SPEC4 == 1) {\r
+          DEBUG ((EFI_D_INFO, "Found SD Card for Spec Version 4.xx\n"));\r
+       } else {\r
+          DEBUG ((EFI_D_INFO, "Found SD Card for Spec Version 3.0x\n"));\r
+       }\r
+      } else {\r
+       if (Scr.SD_SPEC4 == 0) {\r
+          DEBUG ((EFI_D_INFO, "Found SD Card for Spec Version 2.0\n"));\r
+       } else {\r
+         DEBUG ((EFI_D_ERROR, "Found invalid SD Card\n"));\r
+       }\r
+      }\r
+    } else {\r
+      if ((Scr.SD_SPEC3 == 0) && (Scr.SD_SPEC4 == 0)) {\r
+        if (Scr.SD_SPEC == 1) {\r
+         DEBUG ((EFI_D_INFO, "Found SD Card for Spec Version 1.10\n"));\r
+       } else {\r
+         DEBUG ((EFI_D_INFO, "Found SD Card for Spec Version 1.0\n"));\r
+       }\r
+      } else {\r
+        DEBUG ((EFI_D_ERROR, "Found invalid SD Card\n"));\r
+      }\r
+    }\r
+  }\r
+  if (CccSwitch) {\r
+    /* SD Switch, Mode:1, Group:0, Value:1 */\r
+    CmdArg = 1 << 31 | 0x00FFFFFF;\r
+    CmdArg &= ~(0xF << (0 * 4));\r
+    CmdArg |= 1 << (0 * 4);\r
+    Status = MmcHost->SendCommand (MmcHost, MMC_CMD6, CmdArg);\r
+    if (EFI_ERROR (Status)) {\r
+      DEBUG ((EFI_D_ERROR, "%a(MMC_CMD6): Error and Status = %r\n", Status));\r
+       return Status;\r
+    } else {\r
+      Status = MmcHost->ReadBlockData (MmcHost, 0, 64, Buffer);\r
+      if (EFI_ERROR (Status)) {\r
+        DEBUG ((EFI_D_ERROR, "%a(MMC_CMD6): ReadBlockData Error and Status = %r\n", Status));\r
+        return Status;\r
+      }\r
+    }\r
+  }\r
+  if (Scr.SD_BUS_WIDTHS & SD_BUS_WIDTH_4BIT) {\r
+    CmdArg = MmcHostInstance->CardInfo.RCA << 16;\r
+    Status = MmcHost->SendCommand (MmcHost, MMC_CMD55, CmdArg);\r
+    if (EFI_ERROR (Status)) {\r
+      DEBUG ((EFI_D_ERROR, "%a(MMC_CMD55): Error and Status = %r\n", Status));\r
+      return Status;\r
+    }\r
+    /* Width: 4 */\r
+    Status = MmcHost->SendCommand (MmcHost, MMC_CMD6, 2);\r
+    if (EFI_ERROR (Status)) {\r
+      DEBUG ((EFI_D_ERROR, "%a(MMC_CMD6): Error and Status = %r\n", Status));\r
+      return Status;\r
+    }\r
+  }\r
+  if (MMC_HOST_HAS_SETIOS(MmcHost)) {\r
+    Status = MmcHost->SetIos (MmcHost, 26 * 1000 * 1000, 4, EMMCBACKWARD);\r
+    if (EFI_ERROR (Status)) {\r
+      DEBUG ((EFI_D_ERROR, "%a(SetIos): Error and Status = %r\n", Status));\r
+      return Status;\r
+    }\r
+  }\r
   return EFI_SUCCESS;\r
 }\r
 \r
@@ -222,14 +504,19 @@ MmcIdentificationMode (
 \r
   // Send CMD1 to get OCR (MMC)\r
   // This command only valid for MMC and eMMC\r
-  Status = MmcHost->SendCommand (MmcHost, MMC_CMD1, EMMC_CMD1_CAPACITY_GREATER_THAN_2GB);\r
-  if (Status == EFI_SUCCESS) {\r
+  Timeout = MAX_RETRY_COUNT;\r
+  do {\r
+    Status = MmcHost->SendCommand (MmcHost, MMC_CMD1, EMMC_CMD1_CAPACITY_GREATER_THAN_2GB);\r
+    if (EFI_ERROR (Status))\r
+      break;\r
     Status = MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_OCR, (UINT32 *)&OcrResponse);\r
     if (EFI_ERROR (Status)) {\r
       DEBUG ((EFI_D_ERROR, "MmcIdentificationMode() : Failed to receive OCR, Status=%r.\n", Status));\r
       return Status;\r
     }\r
-\r
+    Timeout--;\r
+  } while (!OcrResponse.Ocr.PowerUp && (Timeout > 0));\r
+  if (Status == EFI_SUCCESS) {\r
     if (!OcrResponse.Ocr.PowerUp) {\r
       DEBUG ((EFI_D_ERROR, "MmcIdentificationMode(MMC_CMD1): Card initialisation failure, Status=%r.\n", Status));\r
       return EFI_DEVICE_ERROR;\r
@@ -427,9 +714,11 @@ InitializeMmcDevice (
 \r
   if (MmcHostInstance->CardInfo.CardType != EMMC_CARD) {\r
     Status = InitializeSdMmcDevice (MmcHostInstance);\r
-    if (EFI_ERROR (Status)) {\r
-      return Status;\r
-    }\r
+  } else {\r
+    Status = InitializeEmmcDevice (MmcHostInstance);\r
+  }\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
   }\r
 \r
   // Set Block Length\r