In EDK2, identifiers carrying the EFI prefix are reserved for ones that are defined in the UEFI or PI specifications. Since the MMC host protocol defined in EmbeddedPkg is not the one that the UEFI spec defines, and given the confusion around this, let's rename it to from gEfiMmcHostProtocolGuid to gEmbeddedMmcHostProtocolGuid. Signed-off-by: Ard Biesheuvel <ard.biesheuvel@arm.com> Reviewed-by: Leif Lindholm <leif@nuviainc.com>
689 lines
17 KiB
C
689 lines
17 KiB
C
/** @file
|
|
This file implement the MMC Host Protocol for the DesignWare eMMC.
|
|
|
|
Copyright (c) 2014-2017, Linaro Limited. All rights reserved.
|
|
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
|
|
**/
|
|
|
|
#include <Library/BaseMemoryLib.h>
|
|
#include <Library/CacheMaintenanceLib.h>
|
|
#include <Library/DebugLib.h>
|
|
#include <Library/DevicePathLib.h>
|
|
#include <Library/IoLib.h>
|
|
#include <Library/MemoryAllocationLib.h>
|
|
#include <Library/PcdLib.h>
|
|
#include <Library/TimerLib.h>
|
|
#include <Library/UefiBootServicesTableLib.h>
|
|
#include <Library/UefiLib.h>
|
|
|
|
#include <Protocol/MmcHost.h>
|
|
|
|
#include "DwEmmc.h"
|
|
|
|
#define DWEMMC_DESC_PAGE 1
|
|
#define DWEMMC_BLOCK_SIZE 512
|
|
#define DWEMMC_DMA_BUF_SIZE (512 * 8)
|
|
#define DWEMMC_MAX_DESC_PAGES 512
|
|
|
|
typedef struct {
|
|
UINT32 Des0;
|
|
UINT32 Des1;
|
|
UINT32 Des2;
|
|
UINT32 Des3;
|
|
} DWEMMC_IDMAC_DESCRIPTOR;
|
|
|
|
EFI_MMC_HOST_PROTOCOL *gpMmcHost;
|
|
DWEMMC_IDMAC_DESCRIPTOR *gpIdmacDesc;
|
|
EFI_GUID mDwEmmcDevicePathGuid = EFI_CALLER_ID_GUID;
|
|
STATIC UINT32 mDwEmmcCommand;
|
|
STATIC UINT32 mDwEmmcArgument;
|
|
|
|
EFI_STATUS
|
|
DwEmmcReadBlockData (
|
|
IN EFI_MMC_HOST_PROTOCOL *This,
|
|
IN EFI_LBA Lba,
|
|
IN UINTN Length,
|
|
IN UINT32* Buffer
|
|
);
|
|
|
|
BOOLEAN
|
|
DwEmmcIsPowerOn (
|
|
VOID
|
|
)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
EFI_STATUS
|
|
DwEmmcInitialize (
|
|
VOID
|
|
)
|
|
{
|
|
DEBUG ((DEBUG_BLKIO, "DwEmmcInitialize()"));
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
BOOLEAN
|
|
DwEmmcIsCardPresent (
|
|
IN EFI_MMC_HOST_PROTOCOL *This
|
|
)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN
|
|
DwEmmcIsReadOnly (
|
|
IN EFI_MMC_HOST_PROTOCOL *This
|
|
)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
BOOLEAN
|
|
DwEmmcIsDmaSupported (
|
|
IN EFI_MMC_HOST_PROTOCOL *This
|
|
)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
EFI_STATUS
|
|
DwEmmcBuildDevicePath (
|
|
IN EFI_MMC_HOST_PROTOCOL *This,
|
|
IN EFI_DEVICE_PATH_PROTOCOL **DevicePath
|
|
)
|
|
{
|
|
EFI_DEVICE_PATH_PROTOCOL *NewDevicePathNode;
|
|
|
|
NewDevicePathNode = CreateDeviceNode (HARDWARE_DEVICE_PATH, HW_VENDOR_DP, sizeof (VENDOR_DEVICE_PATH));
|
|
CopyGuid (& ((VENDOR_DEVICE_PATH*)NewDevicePathNode)->Guid, &mDwEmmcDevicePathGuid);
|
|
|
|
*DevicePath = NewDevicePathNode;
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
EFI_STATUS
|
|
DwEmmcUpdateClock (
|
|
VOID
|
|
)
|
|
{
|
|
UINT32 Data;
|
|
|
|
/* CMD_UPDATE_CLK */
|
|
Data = BIT_CMD_WAIT_PRVDATA_COMPLETE | BIT_CMD_UPDATE_CLOCK_ONLY |
|
|
BIT_CMD_START;
|
|
MmioWrite32 (DWEMMC_CMD, Data);
|
|
while (1) {
|
|
Data = MmioRead32 (DWEMMC_CMD);
|
|
if (!(Data & CMD_START_BIT)) {
|
|
break;
|
|
}
|
|
Data = MmioRead32 (DWEMMC_RINTSTS);
|
|
if (Data & DWEMMC_INT_HLE) {
|
|
Print (L"failed to update mmc clock frequency\n");
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
}
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
EFI_STATUS
|
|
DwEmmcSetClock (
|
|
IN UINTN ClockFreq
|
|
)
|
|
{
|
|
UINT32 Divider, Rate, Data;
|
|
EFI_STATUS Status;
|
|
BOOLEAN Found = FALSE;
|
|
|
|
for (Divider = 1; Divider < 256; Divider++) {
|
|
Rate = PcdGet32 (PcdDwEmmcDxeClockFrequencyInHz);
|
|
if ((Rate / (2 * Divider)) <= ClockFreq) {
|
|
Found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if (Found == FALSE) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
// Wait until MMC is idle
|
|
do {
|
|
Data = MmioRead32 (DWEMMC_STATUS);
|
|
} while (Data & DWEMMC_STS_DATA_BUSY);
|
|
|
|
// Disable MMC clock first
|
|
MmioWrite32 (DWEMMC_CLKENA, 0);
|
|
Status = DwEmmcUpdateClock ();
|
|
ASSERT (!EFI_ERROR (Status));
|
|
|
|
MmioWrite32 (DWEMMC_CLKDIV, Divider);
|
|
Status = DwEmmcUpdateClock ();
|
|
ASSERT (!EFI_ERROR (Status));
|
|
|
|
// Enable MMC clock
|
|
MmioWrite32 (DWEMMC_CLKENA, 1);
|
|
MmioWrite32 (DWEMMC_CLKSRC, 0);
|
|
Status = DwEmmcUpdateClock ();
|
|
ASSERT (!EFI_ERROR (Status));
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
EFI_STATUS
|
|
DwEmmcNotifyState (
|
|
IN EFI_MMC_HOST_PROTOCOL *This,
|
|
IN MMC_STATE State
|
|
)
|
|
{
|
|
UINT32 Data;
|
|
EFI_STATUS Status;
|
|
|
|
switch (State) {
|
|
case MmcInvalidState:
|
|
return EFI_INVALID_PARAMETER;
|
|
case MmcHwInitializationState:
|
|
MmioWrite32 (DWEMMC_PWREN, 1);
|
|
|
|
// If device already turn on then restart it
|
|
Data = DWEMMC_CTRL_RESET_ALL;
|
|
MmioWrite32 (DWEMMC_CTRL, Data);
|
|
do {
|
|
// Wait until reset operation finished
|
|
Data = MmioRead32 (DWEMMC_CTRL);
|
|
} while (Data & DWEMMC_CTRL_RESET_ALL);
|
|
|
|
// Setup clock that could not be higher than 400KHz.
|
|
Status = DwEmmcSetClock (400000);
|
|
ASSERT (!EFI_ERROR (Status));
|
|
// Wait clock stable
|
|
MicroSecondDelay (100);
|
|
|
|
MmioWrite32 (DWEMMC_RINTSTS, ~0);
|
|
MmioWrite32 (DWEMMC_INTMASK, 0);
|
|
MmioWrite32 (DWEMMC_TMOUT, ~0);
|
|
MmioWrite32 (DWEMMC_IDINTEN, 0);
|
|
MmioWrite32 (DWEMMC_BMOD, DWEMMC_IDMAC_SWRESET);
|
|
|
|
MmioWrite32 (DWEMMC_BLKSIZ, DWEMMC_BLOCK_SIZE);
|
|
do {
|
|
Data = MmioRead32 (DWEMMC_BMOD);
|
|
} while (Data & DWEMMC_IDMAC_SWRESET);
|
|
break;
|
|
case MmcIdleState:
|
|
break;
|
|
case MmcReadyState:
|
|
break;
|
|
case MmcIdentificationState:
|
|
break;
|
|
case MmcStandByState:
|
|
break;
|
|
case MmcTransferState:
|
|
break;
|
|
case MmcSendingDataState:
|
|
break;
|
|
case MmcReceiveDataState:
|
|
break;
|
|
case MmcProgrammingState:
|
|
break;
|
|
case MmcDisconnectState:
|
|
break;
|
|
default:
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
// Need to prepare DMA buffer first before sending commands to MMC card
|
|
BOOLEAN
|
|
IsPendingReadCommand (
|
|
IN MMC_CMD MmcCmd
|
|
)
|
|
{
|
|
UINTN Mask;
|
|
|
|
Mask = BIT_CMD_DATA_EXPECTED | BIT_CMD_READ;
|
|
if ((MmcCmd & Mask) == Mask) {
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
BOOLEAN
|
|
IsPendingWriteCommand (
|
|
IN MMC_CMD MmcCmd
|
|
)
|
|
{
|
|
UINTN Mask;
|
|
|
|
Mask = BIT_CMD_DATA_EXPECTED | BIT_CMD_WRITE;
|
|
if ((MmcCmd & Mask) == Mask) {
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
EFI_STATUS
|
|
SendCommand (
|
|
IN MMC_CMD MmcCmd,
|
|
IN UINT32 Argument
|
|
)
|
|
{
|
|
UINT32 Data, ErrMask;
|
|
|
|
// Wait until MMC is idle
|
|
do {
|
|
Data = MmioRead32 (DWEMMC_STATUS);
|
|
} while (Data & DWEMMC_STS_DATA_BUSY);
|
|
|
|
MmioWrite32 (DWEMMC_RINTSTS, ~0);
|
|
MmioWrite32 (DWEMMC_CMDARG, Argument);
|
|
MmioWrite32 (DWEMMC_CMD, MmcCmd);
|
|
|
|
ErrMask = DWEMMC_INT_EBE | DWEMMC_INT_HLE | DWEMMC_INT_RTO |
|
|
DWEMMC_INT_RCRC | DWEMMC_INT_RE;
|
|
ErrMask |= DWEMMC_INT_DCRC | DWEMMC_INT_DRT | DWEMMC_INT_SBE;
|
|
do {
|
|
MicroSecondDelay(500);
|
|
Data = MmioRead32 (DWEMMC_RINTSTS);
|
|
|
|
if (Data & ErrMask) {
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
if (Data & DWEMMC_INT_DTO) { // Transfer Done
|
|
break;
|
|
}
|
|
} while (!(Data & DWEMMC_INT_CMD_DONE));
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
EFI_STATUS
|
|
DwEmmcSendCommand (
|
|
IN EFI_MMC_HOST_PROTOCOL *This,
|
|
IN MMC_CMD MmcCmd,
|
|
IN UINT32 Argument
|
|
)
|
|
{
|
|
UINT32 Cmd = 0;
|
|
EFI_STATUS Status = EFI_SUCCESS;
|
|
|
|
switch (MMC_GET_INDX(MmcCmd)) {
|
|
case MMC_INDX(0):
|
|
Cmd = BIT_CMD_SEND_INIT;
|
|
break;
|
|
case MMC_INDX(1):
|
|
Cmd = BIT_CMD_RESPONSE_EXPECT;
|
|
break;
|
|
case MMC_INDX(2):
|
|
Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_LONG_RESPONSE |
|
|
BIT_CMD_CHECK_RESPONSE_CRC | BIT_CMD_SEND_INIT;
|
|
break;
|
|
case MMC_INDX(3):
|
|
Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
|
|
BIT_CMD_SEND_INIT;
|
|
break;
|
|
case MMC_INDX(7):
|
|
if (Argument)
|
|
Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC;
|
|
else
|
|
Cmd = 0;
|
|
break;
|
|
case MMC_INDX(8):
|
|
Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
|
|
BIT_CMD_DATA_EXPECTED | BIT_CMD_READ |
|
|
BIT_CMD_WAIT_PRVDATA_COMPLETE;
|
|
break;
|
|
case MMC_INDX(9):
|
|
Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
|
|
BIT_CMD_LONG_RESPONSE;
|
|
break;
|
|
case MMC_INDX(12):
|
|
Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
|
|
BIT_CMD_STOP_ABORT_CMD;
|
|
break;
|
|
case MMC_INDX(13):
|
|
Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
|
|
BIT_CMD_WAIT_PRVDATA_COMPLETE;
|
|
break;
|
|
case MMC_INDX(16):
|
|
Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
|
|
BIT_CMD_DATA_EXPECTED | BIT_CMD_READ |
|
|
BIT_CMD_WAIT_PRVDATA_COMPLETE;
|
|
break;
|
|
case MMC_INDX(17):
|
|
case MMC_INDX(18):
|
|
Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
|
|
BIT_CMD_DATA_EXPECTED | BIT_CMD_READ |
|
|
BIT_CMD_WAIT_PRVDATA_COMPLETE;
|
|
break;
|
|
case MMC_INDX(24):
|
|
case MMC_INDX(25):
|
|
Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
|
|
BIT_CMD_DATA_EXPECTED | BIT_CMD_WRITE |
|
|
BIT_CMD_WAIT_PRVDATA_COMPLETE;
|
|
break;
|
|
case MMC_INDX(30):
|
|
Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC |
|
|
BIT_CMD_DATA_EXPECTED;
|
|
break;
|
|
default:
|
|
Cmd = BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC;
|
|
break;
|
|
}
|
|
|
|
Cmd |= MMC_GET_INDX(MmcCmd) | BIT_CMD_USE_HOLD_REG | BIT_CMD_START;
|
|
if (IsPendingReadCommand (Cmd) || IsPendingWriteCommand (Cmd)) {
|
|
mDwEmmcCommand = Cmd;
|
|
mDwEmmcArgument = Argument;
|
|
} else {
|
|
Status = SendCommand (Cmd, Argument);
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
EFI_STATUS
|
|
DwEmmcReceiveResponse (
|
|
IN EFI_MMC_HOST_PROTOCOL *This,
|
|
IN MMC_RESPONSE_TYPE Type,
|
|
IN UINT32* Buffer
|
|
)
|
|
{
|
|
if (Buffer == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if ( (Type == MMC_RESPONSE_TYPE_R1)
|
|
|| (Type == MMC_RESPONSE_TYPE_R1b)
|
|
|| (Type == MMC_RESPONSE_TYPE_R3)
|
|
|| (Type == MMC_RESPONSE_TYPE_R6)
|
|
|| (Type == MMC_RESPONSE_TYPE_R7))
|
|
{
|
|
Buffer[0] = MmioRead32 (DWEMMC_RESP0);
|
|
} else if (Type == MMC_RESPONSE_TYPE_R2) {
|
|
Buffer[0] = MmioRead32 (DWEMMC_RESP0);
|
|
Buffer[1] = MmioRead32 (DWEMMC_RESP1);
|
|
Buffer[2] = MmioRead32 (DWEMMC_RESP2);
|
|
Buffer[3] = MmioRead32 (DWEMMC_RESP3);
|
|
}
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
DwEmmcAdjustFifoThreshold (
|
|
VOID
|
|
)
|
|
{
|
|
/* DMA multiple transaction size map to reg value as array index */
|
|
CONST UINT32 BurstSize[] = {1, 4, 8, 16, 32, 64, 128, 256};
|
|
UINT32 BlkDepthInFifo, FifoThreshold, FifoWidth, FifoDepth;
|
|
UINT32 BlkSize = DWEMMC_BLOCK_SIZE, Idx = 0, RxWatermark = 1, TxWatermark, TxWatermarkInvers;
|
|
|
|
/* Skip FIFO adjustment if we do not have platform FIFO depth info */
|
|
FifoDepth = PcdGet32 (PcdDwEmmcDxeFifoDepth);
|
|
if (!FifoDepth) {
|
|
return;
|
|
}
|
|
|
|
TxWatermark = FifoDepth / 2;
|
|
TxWatermarkInvers = FifoDepth - TxWatermark;
|
|
|
|
FifoWidth = DWEMMC_GET_HDATA_WIDTH (MmioRead32 (DWEMMC_HCON));
|
|
if (!FifoWidth) {
|
|
FifoWidth = 2;
|
|
} else if (FifoWidth == 2) {
|
|
FifoWidth = 8;
|
|
} else {
|
|
FifoWidth = 4;
|
|
}
|
|
|
|
BlkDepthInFifo = BlkSize / FifoWidth;
|
|
|
|
Idx = ARRAY_SIZE (BurstSize) - 1;
|
|
while (Idx && ((BlkDepthInFifo % BurstSize[Idx]) || (TxWatermarkInvers % BurstSize[Idx]))) {
|
|
Idx--;
|
|
}
|
|
|
|
RxWatermark = BurstSize[Idx] - 1;
|
|
FifoThreshold = DWEMMC_DMA_BURST_SIZE (Idx) | DWEMMC_FIFO_TWMARK (TxWatermark)
|
|
| DWEMMC_FIFO_RWMARK (RxWatermark);
|
|
MmioWrite32 (DWEMMC_FIFOTH, FifoThreshold);
|
|
}
|
|
|
|
EFI_STATUS
|
|
PrepareDmaData (
|
|
IN DWEMMC_IDMAC_DESCRIPTOR* IdmacDesc,
|
|
IN UINTN Length,
|
|
IN UINT32* Buffer
|
|
)
|
|
{
|
|
UINTN Cnt, Blks, Idx, LastIdx;
|
|
|
|
Cnt = (Length + DWEMMC_DMA_BUF_SIZE - 1) / DWEMMC_DMA_BUF_SIZE;
|
|
Blks = (Length + DWEMMC_BLOCK_SIZE - 1) / DWEMMC_BLOCK_SIZE;
|
|
Length = DWEMMC_BLOCK_SIZE * Blks;
|
|
|
|
for (Idx = 0; Idx < Cnt; Idx++) {
|
|
(IdmacDesc + Idx)->Des0 = DWEMMC_IDMAC_DES0_OWN | DWEMMC_IDMAC_DES0_CH |
|
|
DWEMMC_IDMAC_DES0_DIC;
|
|
(IdmacDesc + Idx)->Des1 = DWEMMC_IDMAC_DES1_BS1(DWEMMC_DMA_BUF_SIZE);
|
|
/* Buffer Address */
|
|
(IdmacDesc + Idx)->Des2 = (UINT32)((UINTN)Buffer + DWEMMC_DMA_BUF_SIZE * Idx);
|
|
/* Next Descriptor Address */
|
|
(IdmacDesc + Idx)->Des3 = (UINT32)((UINTN)IdmacDesc +
|
|
(sizeof(DWEMMC_IDMAC_DESCRIPTOR) * (Idx + 1)));
|
|
}
|
|
/* First Descriptor */
|
|
IdmacDesc->Des0 |= DWEMMC_IDMAC_DES0_FS;
|
|
/* Last Descriptor */
|
|
LastIdx = Cnt - 1;
|
|
(IdmacDesc + LastIdx)->Des0 |= DWEMMC_IDMAC_DES0_LD;
|
|
(IdmacDesc + LastIdx)->Des0 &= ~(DWEMMC_IDMAC_DES0_DIC | DWEMMC_IDMAC_DES0_CH);
|
|
(IdmacDesc + LastIdx)->Des1 = DWEMMC_IDMAC_DES1_BS1(Length -
|
|
(LastIdx * DWEMMC_DMA_BUF_SIZE));
|
|
/* Set the Next field of Last Descriptor */
|
|
(IdmacDesc + LastIdx)->Des3 = 0;
|
|
MmioWrite32 (DWEMMC_DBADDR, (UINT32)((UINTN)IdmacDesc));
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
StartDma (
|
|
UINTN Length
|
|
)
|
|
{
|
|
UINT32 Data;
|
|
|
|
Data = MmioRead32 (DWEMMC_CTRL);
|
|
Data |= DWEMMC_CTRL_INT_EN | DWEMMC_CTRL_DMA_EN | DWEMMC_CTRL_IDMAC_EN;
|
|
MmioWrite32 (DWEMMC_CTRL, Data);
|
|
Data = MmioRead32 (DWEMMC_BMOD);
|
|
Data |= DWEMMC_IDMAC_ENABLE | DWEMMC_IDMAC_FB;
|
|
MmioWrite32 (DWEMMC_BMOD, Data);
|
|
|
|
MmioWrite32 (DWEMMC_BLKSIZ, DWEMMC_BLOCK_SIZE);
|
|
MmioWrite32 (DWEMMC_BYTCNT, Length);
|
|
}
|
|
|
|
EFI_STATUS
|
|
DwEmmcReadBlockData (
|
|
IN EFI_MMC_HOST_PROTOCOL *This,
|
|
IN EFI_LBA Lba,
|
|
IN UINTN Length,
|
|
IN UINT32* Buffer
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT32 DescPages, CountPerPage, Count;
|
|
EFI_TPL Tpl;
|
|
|
|
Tpl = gBS->RaiseTPL (TPL_NOTIFY);
|
|
|
|
CountPerPage = EFI_PAGE_SIZE / 16;
|
|
Count = (Length + DWEMMC_DMA_BUF_SIZE - 1) / DWEMMC_DMA_BUF_SIZE;
|
|
DescPages = (Count + CountPerPage - 1) / CountPerPage;
|
|
|
|
InvalidateDataCacheRange (Buffer, Length);
|
|
|
|
Status = PrepareDmaData (gpIdmacDesc, Length, Buffer);
|
|
if (EFI_ERROR (Status)) {
|
|
goto out;
|
|
}
|
|
|
|
WriteBackDataCacheRange (gpIdmacDesc, DescPages * EFI_PAGE_SIZE);
|
|
StartDma (Length);
|
|
|
|
Status = SendCommand (mDwEmmcCommand, mDwEmmcArgument);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "Failed to read data, mDwEmmcCommand:%x, mDwEmmcArgument:%x, Status:%r\n", mDwEmmcCommand, mDwEmmcArgument, Status));
|
|
goto out;
|
|
}
|
|
out:
|
|
// Restore Tpl
|
|
gBS->RestoreTPL (Tpl);
|
|
return Status;
|
|
}
|
|
|
|
EFI_STATUS
|
|
DwEmmcWriteBlockData (
|
|
IN EFI_MMC_HOST_PROTOCOL *This,
|
|
IN EFI_LBA Lba,
|
|
IN UINTN Length,
|
|
IN UINT32* Buffer
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT32 DescPages, CountPerPage, Count;
|
|
EFI_TPL Tpl;
|
|
|
|
Tpl = gBS->RaiseTPL (TPL_NOTIFY);
|
|
|
|
CountPerPage = EFI_PAGE_SIZE / 16;
|
|
Count = (Length + DWEMMC_DMA_BUF_SIZE - 1) / DWEMMC_DMA_BUF_SIZE;
|
|
DescPages = (Count + CountPerPage - 1) / CountPerPage;
|
|
|
|
WriteBackDataCacheRange (Buffer, Length);
|
|
|
|
Status = PrepareDmaData (gpIdmacDesc, Length, Buffer);
|
|
if (EFI_ERROR (Status)) {
|
|
goto out;
|
|
}
|
|
|
|
WriteBackDataCacheRange (gpIdmacDesc, DescPages * EFI_PAGE_SIZE);
|
|
StartDma (Length);
|
|
|
|
Status = SendCommand (mDwEmmcCommand, mDwEmmcArgument);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "Failed to write data, mDwEmmcCommand:%x, mDwEmmcArgument:%x, Status:%r\n", mDwEmmcCommand, mDwEmmcArgument, Status));
|
|
goto out;
|
|
}
|
|
out:
|
|
// Restore Tpl
|
|
gBS->RestoreTPL (Tpl);
|
|
return Status;
|
|
}
|
|
|
|
EFI_STATUS
|
|
DwEmmcSetIos (
|
|
IN EFI_MMC_HOST_PROTOCOL *This,
|
|
IN UINT32 BusClockFreq,
|
|
IN UINT32 BusWidth,
|
|
IN UINT32 TimingMode
|
|
)
|
|
{
|
|
EFI_STATUS Status = EFI_SUCCESS;
|
|
UINT32 Data;
|
|
|
|
if ((PcdGet32 (PcdDwEmmcDxeMaxClockFreqInHz) != 0) &&
|
|
(BusClockFreq > PcdGet32 (PcdDwEmmcDxeMaxClockFreqInHz))) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
if (TimingMode != EMMCBACKWARD) {
|
|
Data = MmioRead32 (DWEMMC_UHSREG);
|
|
switch (TimingMode) {
|
|
case EMMCHS52DDR1V2:
|
|
case EMMCHS52DDR1V8:
|
|
Data |= 1 << 16;
|
|
break;
|
|
case EMMCHS52:
|
|
case EMMCHS26:
|
|
Data &= ~(1 << 16);
|
|
break;
|
|
default:
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
MmioWrite32 (DWEMMC_UHSREG, Data);
|
|
}
|
|
|
|
switch (BusWidth) {
|
|
case 1:
|
|
MmioWrite32 (DWEMMC_CTYPE, 0);
|
|
break;
|
|
case 4:
|
|
MmioWrite32 (DWEMMC_CTYPE, 1);
|
|
break;
|
|
case 8:
|
|
MmioWrite32 (DWEMMC_CTYPE, 1 << 16);
|
|
break;
|
|
default:
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
if (BusClockFreq) {
|
|
Status = DwEmmcSetClock (BusClockFreq);
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
BOOLEAN
|
|
DwEmmcIsMultiBlock (
|
|
IN EFI_MMC_HOST_PROTOCOL *This
|
|
)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
EFI_MMC_HOST_PROTOCOL gMciHost = {
|
|
MMC_HOST_PROTOCOL_REVISION,
|
|
DwEmmcIsCardPresent,
|
|
DwEmmcIsReadOnly,
|
|
DwEmmcBuildDevicePath,
|
|
DwEmmcNotifyState,
|
|
DwEmmcSendCommand,
|
|
DwEmmcReceiveResponse,
|
|
DwEmmcReadBlockData,
|
|
DwEmmcWriteBlockData,
|
|
DwEmmcSetIos,
|
|
DwEmmcIsMultiBlock
|
|
};
|
|
|
|
EFI_STATUS
|
|
DwEmmcDxeInitialize (
|
|
IN EFI_HANDLE ImageHandle,
|
|
IN EFI_SYSTEM_TABLE *SystemTable
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_HANDLE Handle;
|
|
|
|
Handle = NULL;
|
|
|
|
DwEmmcAdjustFifoThreshold ();
|
|
gpIdmacDesc = (DWEMMC_IDMAC_DESCRIPTOR *)AllocatePages (DWEMMC_MAX_DESC_PAGES);
|
|
if (gpIdmacDesc == NULL) {
|
|
return EFI_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
DEBUG ((DEBUG_BLKIO, "DwEmmcDxeInitialize()\n"));
|
|
|
|
//Publish Component Name, BlockIO protocol interfaces
|
|
Status = gBS->InstallMultipleProtocolInterfaces (
|
|
&Handle,
|
|
&gEmbeddedMmcHostProtocolGuid, &gMciHost,
|
|
NULL
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|