diff options
author | Haojian Zhuang <haojian.zhuang@linaro.org> | 2016-11-23 21:36:21 +0800 |
---|---|---|
committer | Ard Biesheuvel <ard.biesheuvel@linaro.org> | 2016-11-24 16:29:03 +0000 |
commit | 752ae80514ccb3532a6d6d16a122642573eabdee (patch) | |
tree | 3bc06c60df241c5c7af623b1b682570d92c362c1 | |
parent | 1df2fe1433b2420b7e11591e3d46ed1a2c146072 (diff) | |
download | edk2-752ae80514ccb3532a6d6d16a122642573eabdee.zip edk2-752ae80514ccb3532a6d6d16a122642573eabdee.tar.gz edk2-752ae80514ccb3532a6d6d16a122642573eabdee.tar.bz2 |
EmbeddedPkg/MmcDxe: invoke SetIos() protocol method to set speed and width
Add the interface to change the bus width and speed.
By default, MMC is initialized with 1-bit mode and less than 400KHz bus
clock. It causes MMC working inefficiently.
Set I/O bus width on both MMC controller and EXTCSD. Otherwise, it may
cause unmatched failure case. And support more timing mode, high speed,
HS200 & HS400 mode.
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Haojian Zhuang <haojian.zhuang@linaro.org>
Tested-by: Ryan Harkin <ryan.harkin@linaro.org>
Reviewed-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
-rw-r--r-- | EmbeddedPkg/Universal/MmcDxe/Mmc.h | 5 | ||||
-rw-r--r-- | EmbeddedPkg/Universal/MmcDxe/MmcIdentification.c | 185 |
2 files changed, 187 insertions, 3 deletions
diff --git a/EmbeddedPkg/Universal/MmcDxe/Mmc.h b/EmbeddedPkg/Universal/MmcDxe/Mmc.h index 112bc47..fb3f6c9 100644 --- a/EmbeddedPkg/Universal/MmcDxe/Mmc.h +++ b/EmbeddedPkg/Universal/MmcDxe/Mmc.h @@ -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
diff --git a/EmbeddedPkg/Universal/MmcDxe/MmcIdentification.c b/EmbeddedPkg/Universal/MmcDxe/MmcIdentification.c index 9aefb26..decfd54 100644 --- a/EmbeddedPkg/Universal/MmcDxe/MmcIdentification.c +++ b/EmbeddedPkg/Universal/MmcDxe/MmcIdentification.c @@ -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
|