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>
This commit is contained in:
Haojian Zhuang 2016-11-23 21:36:21 +08:00 committed by Ard Biesheuvel
parent 1df2fe1433
commit 752ae80514
2 changed files with 187 additions and 3 deletions

View File

@ -55,6 +55,11 @@
#define MMC_R0_STATE_TRAN 4 #define MMC_R0_STATE_TRAN 4
#define MMC_R0_STATE_DATA 5 #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 { typedef enum {
UNKNOWN_CARD, UNKNOWN_CARD,
MMC_CARD, //MMC card MMC_CARD, //MMC card

View File

@ -25,8 +25,108 @@ typedef union {
#define EMMC_CARD_SIZE 512 #define EMMC_CARD_SIZE 512
#define EMMC_ECSD_SIZE_OFFSET 53 #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; 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 STATIC
EFI_STATUS EFI_STATUS
EFIAPI EFIAPI
@ -38,6 +138,7 @@ EmmcIdentificationMode (
EFI_MMC_HOST_PROTOCOL *Host; EFI_MMC_HOST_PROTOCOL *Host;
EFI_BLOCK_IO_MEDIA *Media; EFI_BLOCK_IO_MEDIA *Media;
EFI_STATUS Status; EFI_STATUS Status;
EMMC_DEVICE_STATE State;
UINT32 RCA; UINT32 RCA;
Host = MmcHostInstance->MmcHost; Host = MmcHostInstance->MmcHost;
@ -84,6 +185,22 @@ EmmcIdentificationMode (
DEBUG ((EFI_D_ERROR, "EmmcIdentificationMode(): Card selection error, Status=%r.\n", Status)); 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 // Fetch ECSD
Status = Host->SendCommand (Host, MMC_CMD8, RCA); Status = Host->SendCommand (Host, MMC_CMD8, RCA);
if (EFI_ERROR (Status)) { if (EFI_ERROR (Status)) {
@ -96,6 +213,15 @@ EmmcIdentificationMode (
return Status; 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 // Set up media
Media->BlockSize = EMMC_CARD_SIZE; // 512-byte support is mandatory for eMMC cards Media->BlockSize = EMMC_CARD_SIZE; // 512-byte support is mandatory for eMMC cards
Media->MediaId = MmcHostInstance->CardInfo.CIDData.PSN; Media->MediaId = MmcHostInstance->CardInfo.CIDData.PSN;
@ -111,6 +237,57 @@ EmmcIdentificationMode (
return EFI_SUCCESS; return EFI_SUCCESS;
} }
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 STATIC
EFI_STATUS EFI_STATUS
InitializeSdMmcDevice ( InitializeSdMmcDevice (
@ -431,9 +608,11 @@ InitializeMmcDevice (
if (MmcHostInstance->CardInfo.CardType != EMMC_CARD) { if (MmcHostInstance->CardInfo.CardType != EMMC_CARD) {
Status = InitializeSdMmcDevice (MmcHostInstance); Status = InitializeSdMmcDevice (MmcHostInstance);
if (EFI_ERROR (Status)) { } else {
return Status; Status = InitializeEmmcDevice (MmcHostInstance);
} }
if (EFI_ERROR (Status)) {
return Status;
} }
// Set Block Length // Set Block Length