@@ -49,6 +49,7 @@ typedef UINT32 MMC_CMD;
#define MMC_CMD2 (MMC_INDX(2) | MMC_CMD_WAIT_RESPONSE | MMC_CMD_LONG_RESPONSE)
#define MMC_CMD3 (MMC_INDX(3) | MMC_CMD_WAIT_RESPONSE)
#define MMC_CMD5 (MMC_INDX(5) | MMC_CMD_WAIT_RESPONSE | MMC_CMD_NO_CRC_RESPONSE)
+#define MMC_CMD6 (MMC_INDX(6) | MMC_CMD_WAIT_RESPONSE)
#define MMC_CMD7 (MMC_INDX(7) | MMC_CMD_WAIT_RESPONSE)
#define MMC_CMD8 (MMC_INDX(8) | MMC_CMD_WAIT_RESPONSE)
#define MMC_CMD9 (MMC_INDX(9) | MMC_CMD_WAIT_RESPONSE | MMC_CMD_LONG_RESPONSE)
@@ -82,6 +83,16 @@ typedef enum _MMC_STATE {
MmcDisconnectState,
} MMC_STATE;
+#define EMMCBACKWARD (0)
+#define EMMCHS26 (1 << 0) // High-Speed @26MHz at rated device voltages
+#define EMMCHS52 (1 << 1) // High-Speed @52MHz at rated device voltages
+#define EMMCHS52DDR1V8 (1 << 2) // High-Speed Dual Data Rate @52MHz 1.8V or 3V I/O
+#define EMMCHS52DDR1V2 (1 << 3) // High-Speed Dual Data Rate @52MHz 1.2V I/O
+#define EMMCHS200SDR1V8 (1 << 4) // HS200 Single Data Rate @200MHz 1.8V I/O
+#define EMMCHS200SDR1V2 (1 << 5) // HS200 Single Data Rate @200MHz 1.2V I/O
+#define EMMCHS400DDR1V8 (1 << 6) // HS400 Dual Data Rate @400MHz 1.8V I/O
+#define EMMCHS400DDR1V2 (1 << 7) // HS400 Dual Data Rate @400MHz 1.2V I/O
+
///
/// Forward declaration for EFI_MMC_HOST_PROTOCOL
///
@@ -131,6 +142,16 @@ typedef EFI_STATUS (EFIAPI *MMC_WRITEBLOCKDATA) (
IN UINT32 *Buffer
);
+typedef EFI_STATUS (EFIAPI *MMC_SETIOS) (
+ IN EFI_MMC_HOST_PROTOCOL *This,
+ IN UINT32 BusClockFreq,
+ IN UINT32 BusWidth,
+ IN UINT32 TimingMode
+ );
+
+typedef BOOLEAN (EFIAPI *MMC_ISMULTIBLOCK) (
+ IN EFI_MMC_HOST_PROTOCOL *This
+ );
struct _EFI_MMC_HOST_PROTOCOL {
@@ -147,9 +168,17 @@ struct _EFI_MMC_HOST_PROTOCOL {
MMC_READBLOCKDATA ReadBlockData;
MMC_WRITEBLOCKDATA WriteBlockData;
+ MMC_SETIOS SetIos;
+ MMC_ISMULTIBLOCK IsMultiBlock;
+
};
-#define MMC_HOST_PROTOCOL_REVISION 0x00010001 // 1.1
+#define MMC_HOST_PROTOCOL_REVISION 0x00010002 // 1.2
+
+#define MMC_HOST_HAS_SETIOS(Host) (Host->Revision >= MMC_HOST_PROTOCOL_REVISION && \
+ Host->SetIos != NULL)
+#define MMC_HOST_HAS_ISMULTIBLOCK(Host) (Host->Revision >= MMC_HOST_PROTOCOL_REVISION && \
+ Host->IsMultiBlock != NULL)
extern EFI_GUID gEfiMmcHostProtocolGuid;
@@ -55,6 +55,11 @@
#define MMC_R0_STATE_TRAN 4
#define MMC_R0_STATE_DATA 5
+#define EMMC_CMD6_ARG_ACCESS(x) (((x) & 0x3) << 24)
+#define EMMC_CMD6_ARG_INDEX(x) (((x) & 0xFF) << 16)
+#define EMMC_CMD6_ARG_VALUE(x) (((x) & 0xFF) << 8)
+#define EMMC_CMD6_ARG_CMD_SET(x) (((x) & 0x7) << 0)
+
typedef enum {
UNKNOWN_CARD,
MMC_CARD, //MMC card
@@ -25,11 +25,111 @@ typedef union {
#define EMMC_CARD_SIZE 512
#define EMMC_ECSD_SIZE_OFFSET 53
+#define EXTCSD_BUS_WIDTH 183
+#define EXTCSD_HS_TIMING 185
+
+#define EMMC_TIMING_BACKWARD 0
+#define EMMC_TIMING_HS 1
+#define EMMC_TIMING_HS200 2
+#define EMMC_TIMING_HS400 3
+
+#define EMMC_BUS_WIDTH_1BIT 0
+#define EMMC_BUS_WIDTH_4BIT 1
+#define EMMC_BUS_WIDTH_8BIT 2
+#define EMMC_BUS_WIDTH_DDR_4BIT 5
+#define EMMC_BUS_WIDTH_DDR_8BIT 6
+
+#define EMMC_SWITCH_ERROR (1 << 7)
+
+#define DEVICE_STATE(x) (((x) >> 9) & 0xf)
+typedef enum _EMMC_DEVICE_STATE {
+ EMMC_IDLE_STATE = 0,
+ EMMC_READY_STATE,
+ EMMC_IDENT_STATE,
+ EMMC_STBY_STATE,
+ EMMC_TRAN_STATE,
+ EMMC_DATA_STATE,
+ EMMC_RCV_STATE,
+ EMMC_PRG_STATE,
+ EMMC_DIS_STATE,
+ EMMC_BTST_STATE,
+ EMMC_SLP_STATE
+} EMMC_DEVICE_STATE;
+
UINT32 mEmmcRcaCount = 0;
STATIC
EFI_STATUS
EFIAPI
+EmmcGetDeviceState (
+ IN MMC_HOST_INSTANCE *MmcHostInstance,
+ OUT EMMC_DEVICE_STATE *State
+ )
+{
+ EFI_MMC_HOST_PROTOCOL *Host;
+ EFI_STATUS Status;
+ UINT32 Data, RCA;
+
+ if (State == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Host = MmcHostInstance->MmcHost;
+ RCA = MmcHostInstance->CardInfo.RCA << RCA_SHIFT_OFFSET;
+ Status = Host->SendCommand (Host, MMC_CMD13, RCA);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "EmmcGetDeviceState(): Failed to get card status, Status=%r.\n", Status));
+ return Status;
+ }
+ Status = Host->ReceiveResponse (Host, MMC_RESPONSE_TYPE_R1, &Data);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "EmmcGetDeviceState(): Failed to get response of CMD13, Status=%r.\n", Status));
+ return Status;
+ }
+ if (Data & EMMC_SWITCH_ERROR) {
+ DEBUG ((EFI_D_ERROR, "EmmcGetDeviceState(): Failed to switch expected mode, Status=%r.\n", Status));
+ return EFI_DEVICE_ERROR;
+ }
+ *State = DEVICE_STATE(Data);
+ return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+EmmcSetEXTCSD (
+ IN MMC_HOST_INSTANCE *MmcHostInstance,
+ UINT32 ExtCmdIndex,
+ UINT32 Value
+ )
+{
+ EFI_MMC_HOST_PROTOCOL *Host;
+ EMMC_DEVICE_STATE State;
+ EFI_STATUS Status;
+ UINT32 Argument;
+
+ Host = MmcHostInstance->MmcHost;
+ Argument = EMMC_CMD6_ARG_ACCESS(3) | EMMC_CMD6_ARG_INDEX(ExtCmdIndex) |
+ EMMC_CMD6_ARG_VALUE(Value) | EMMC_CMD6_ARG_CMD_SET(1);
+ Status = Host->SendCommand (Host, MMC_CMD6, Argument);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "EmmcSetEXTCSD(): Failed to send CMD6, Status=%r.\n", Status));
+ return Status;
+ }
+ // Make sure device exiting prog mode
+ do {
+ Status = EmmcGetDeviceState (MmcHostInstance, &State);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "EmmcSetEXTCSD(): Failed to get device state, Status=%r.\n", Status));
+ return Status;
+ }
+ } while (State == EMMC_PRG_STATE);
+ return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
EmmcIdentificationMode (
IN MMC_HOST_INSTANCE *MmcHostInstance,
IN OCR_RESPONSE Response
@@ -38,6 +138,7 @@ EmmcIdentificationMode (
EFI_MMC_HOST_PROTOCOL *Host;
EFI_BLOCK_IO_MEDIA *Media;
EFI_STATUS Status;
+ EMMC_DEVICE_STATE State;
UINT32 RCA;
Host = MmcHostInstance->MmcHost;
@@ -84,6 +185,22 @@ EmmcIdentificationMode (
DEBUG ((EFI_D_ERROR, "EmmcIdentificationMode(): Card selection error, Status=%r.\n", Status));
}
+ if (MMC_HOST_HAS_SETIOS(Host)) {
+ // Set 1-bit bus width
+ Status = Host->SetIos (Host, 0, 1, EMMCBACKWARD);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "EmmcIdentificationMode(): Set 1-bit bus width error, Status=%r.\n", Status));
+ return Status;
+ }
+
+ // Set 1-bit bus width for EXTCSD
+ Status = EmmcSetEXTCSD (MmcHostInstance, EXTCSD_BUS_WIDTH, EMMC_BUS_WIDTH_1BIT);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "EmmcIdentificationMode(): Set extcsd bus width error, Status=%r.\n", Status));
+ return Status;
+ }
+ }
+
// Fetch ECSD
Status = Host->SendCommand (Host, MMC_CMD8, RCA);
if (EFI_ERROR (Status)) {
@@ -96,6 +213,15 @@ EmmcIdentificationMode (
return Status;
}
+ // Make sure device exiting data mode
+ do {
+ Status = EmmcGetDeviceState (MmcHostInstance, &State);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "EmmcIdentificationMode(): Failed to get device state, Status=%r.\n", Status));
+ return Status;
+ }
+ } while (State == EMMC_DATA_STATE);
+
// Set up media
Media->BlockSize = EMMC_CARD_SIZE; // 512-byte support is mandatory for eMMC cards
Media->MediaId = MmcHostInstance->CardInfo.CIDData.PSN;
@@ -113,6 +239,57 @@ EmmcIdentificationMode (
STATIC
EFI_STATUS
+InitializeEmmcDevice (
+ IN MMC_HOST_INSTANCE *MmcHostInstance
+ )
+{
+ EFI_MMC_HOST_PROTOCOL *Host;
+ EFI_STATUS Status = EFI_SUCCESS;
+ ECSD *ECSDData;
+ UINT32 BusClockFreq, Idx;
+ UINT32 TimingMode[4] = {EMMCHS52DDR1V2, EMMCHS52DDR1V8, EMMCHS52, EMMCHS26};
+
+ Host = MmcHostInstance->MmcHost;
+ ECSDData = &MmcHostInstance->CardInfo.ECSDData;
+ if (ECSDData->DEVICE_TYPE == EMMCBACKWARD)
+ return EFI_SUCCESS;
+
+ if (!MMC_HOST_HAS_SETIOS(Host)) {
+ return EFI_SUCCESS;
+ }
+ Status = EmmcSetEXTCSD (MmcHostInstance, EXTCSD_HS_TIMING, EMMC_TIMING_HS);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "InitializeEmmcDevice(): Failed to switch high speed mode, Status:%r.\n", Status));
+ return Status;
+ }
+
+ for (Idx = 0; Idx < 4; Idx++) {
+ switch (TimingMode[Idx]) {
+ case EMMCHS52DDR1V2:
+ case EMMCHS52DDR1V8:
+ case EMMCHS52:
+ BusClockFreq = 52000000;
+ break;
+ case EMMCHS26:
+ BusClockFreq = 26000000;
+ break;
+ default:
+ return EFI_UNSUPPORTED;
+ }
+ Status = Host->SetIos (Host, BusClockFreq, 8, TimingMode[Idx]);
+ if (!EFI_ERROR (Status)) {
+ Status = EmmcSetEXTCSD (MmcHostInstance, EXTCSD_BUS_WIDTH, EMMC_BUS_WIDTH_DDR_8BIT);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "InitializeEmmcDevice(): Failed to set EXTCSD bus width, Status:%r\n", Status));
+ }
+ return Status;
+ }
+ }
+ return Status;
+}
+
+STATIC
+EFI_STATUS
InitializeSdMmcDevice (
IN MMC_HOST_INSTANCE *MmcHostInstance
)
@@ -431,9 +608,11 @@ InitializeMmcDevice (
if (MmcHostInstance->CardInfo.CardType != EMMC_CARD) {
Status = InitializeSdMmcDevice (MmcHostInstance);
- if (EFI_ERROR (Status)) {
- return Status;
- }
+ } else {
+ Status = InitializeEmmcDevice (MmcHostInstance);
+ }
+ if (EFI_ERROR (Status)) {
+ return Status;
}
// Set Block Length