EmbeddedPkg: import DesignWare EMMC driver

Imported from OpenPlatformPkg 0434ff62e3eb896e6c561dea84cfb8b80391603e.

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Leif Lindholm <leif.lindholm@linaro.org>
Reviewed-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
This commit is contained in:
Leif Lindholm 2017-05-03 17:05:47 +01:00
parent d547b32dcc
commit ee66053106
4 changed files with 832 additions and 0 deletions

View File

@ -0,0 +1,127 @@
/** @file
*
* Copyright (c) 2014-2017, Linaro Limited. All rights reserved.
*
* This program and the accompanying materials
* are licensed and made available under the terms and conditions of the BSD License
* which accompanies this distribution. The full text of the license may be found at
* http://opensource.org/licenses/bsd-license.php
*
* THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
* WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
*
**/
#ifndef __DWEMMC_H__
#define __DWEMMC_H__
#include <Protocol/EmbeddedGpio.h>
// DW MMC Registers
#define DWEMMC_CTRL ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x000)
#define DWEMMC_PWREN ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x004)
#define DWEMMC_CLKDIV ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x008)
#define DWEMMC_CLKSRC ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x00c)
#define DWEMMC_CLKENA ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x010)
#define DWEMMC_TMOUT ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x014)
#define DWEMMC_CTYPE ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x018)
#define DWEMMC_BLKSIZ ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x01c)
#define DWEMMC_BYTCNT ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x020)
#define DWEMMC_INTMASK ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x024)
#define DWEMMC_CMDARG ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x028)
#define DWEMMC_CMD ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x02c)
#define DWEMMC_RESP0 ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x030)
#define DWEMMC_RESP1 ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x034)
#define DWEMMC_RESP2 ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x038)
#define DWEMMC_RESP3 ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x03c)
#define DWEMMC_RINTSTS ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x044)
#define DWEMMC_STATUS ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x048)
#define DWEMMC_FIFOTH ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x04c)
#define DWEMMC_DEBNCE ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x064)
#define DWEMMC_UHSREG ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x074)
#define DWEMMC_BMOD ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x080)
#define DWEMMC_DBADDR ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x088)
#define DWEMMC_IDSTS ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x08c)
#define DWEMMC_IDINTEN ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x090)
#define DWEMMC_DSCADDR ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x094)
#define DWEMMC_BUFADDR ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0x098)
#define DWEMMC_CARDTHRCTL ((UINT32)PcdGet32 (PcdDwEmmcDxeBaseAddress) + 0X100)
#define CMD_UPDATE_CLK 0x80202000
#define CMD_START_BIT (1 << 31)
#define MMC_8BIT_MODE (1 << 16)
#define BIT_CMD_RESPONSE_EXPECT (1 << 6)
#define BIT_CMD_LONG_RESPONSE (1 << 7)
#define BIT_CMD_CHECK_RESPONSE_CRC (1 << 8)
#define BIT_CMD_DATA_EXPECTED (1 << 9)
#define BIT_CMD_READ (0 << 10)
#define BIT_CMD_WRITE (1 << 10)
#define BIT_CMD_BLOCK_TRANSFER (0 << 11)
#define BIT_CMD_STREAM_TRANSFER (1 << 11)
#define BIT_CMD_SEND_AUTO_STOP (1 << 12)
#define BIT_CMD_WAIT_PRVDATA_COMPLETE (1 << 13)
#define BIT_CMD_STOP_ABORT_CMD (1 << 14)
#define BIT_CMD_SEND_INIT (1 << 15)
#define BIT_CMD_UPDATE_CLOCK_ONLY (1 << 21)
#define BIT_CMD_READ_CEATA_DEVICE (1 << 22)
#define BIT_CMD_CCS_EXPECTED (1 << 23)
#define BIT_CMD_ENABLE_BOOT (1 << 24)
#define BIT_CMD_EXPECT_BOOT_ACK (1 << 25)
#define BIT_CMD_DISABLE_BOOT (1 << 26)
#define BIT_CMD_MANDATORY_BOOT (0 << 27)
#define BIT_CMD_ALTERNATE_BOOT (1 << 27)
#define BIT_CMD_VOLT_SWITCH (1 << 28)
#define BIT_CMD_USE_HOLD_REG (1 << 29)
#define BIT_CMD_START (1 << 31)
#define DWEMMC_INT_EBE (1 << 15) /* End-bit Err */
#define DWEMMC_INT_SBE (1 << 13) /* Start-bit Err */
#define DWEMMC_INT_HLE (1 << 12) /* Hardware-lock Err */
#define DWEMMC_INT_FRUN (1 << 11) /* FIFO UN/OV RUN */
#define DWEMMC_INT_DRT (1 << 9) /* Data timeout */
#define DWEMMC_INT_RTO (1 << 8) /* Response timeout */
#define DWEMMC_INT_DCRC (1 << 7) /* Data CRC err */
#define DWEMMC_INT_RCRC (1 << 6) /* Response CRC err */
#define DWEMMC_INT_RXDR (1 << 5)
#define DWEMMC_INT_TXDR (1 << 4)
#define DWEMMC_INT_DTO (1 << 3) /* Data trans over */
#define DWEMMC_INT_CMD_DONE (1 << 2)
#define DWEMMC_INT_RE (1 << 1)
#define DWEMMC_IDMAC_DES0_DIC (1 << 1)
#define DWEMMC_IDMAC_DES0_LD (1 << 2)
#define DWEMMC_IDMAC_DES0_FS (1 << 3)
#define DWEMMC_IDMAC_DES0_CH (1 << 4)
#define DWEMMC_IDMAC_DES0_ER (1 << 5)
#define DWEMMC_IDMAC_DES0_CES (1 << 30)
#define DWEMMC_IDMAC_DES0_OWN (1 << 31)
#define DWEMMC_IDMAC_DES1_BS1(x) ((x) & 0x1fff)
#define DWEMMC_IDMAC_DES2_BS2(x) (((x) & 0x1fff) << 13)
#define DWEMMC_IDMAC_SWRESET (1 << 0)
#define DWEMMC_IDMAC_FB (1 << 1)
#define DWEMMC_IDMAC_ENABLE (1 << 7)
#define EMMC_FIX_RCA 6
/* bits in MMC0_CTRL */
#define DWEMMC_CTRL_RESET (1 << 0)
#define DWEMMC_CTRL_FIFO_RESET (1 << 1)
#define DWEMMC_CTRL_DMA_RESET (1 << 2)
#define DWEMMC_CTRL_INT_EN (1 << 4)
#define DWEMMC_CTRL_DMA_EN (1 << 5)
#define DWEMMC_CTRL_IDMAC_EN (1 << 25)
#define DWEMMC_CTRL_RESET_ALL (DWEMMC_CTRL_RESET | DWEMMC_CTRL_FIFO_RESET | DWEMMC_CTRL_DMA_RESET)
#define DWEMMC_STS_DATA_BUSY (1 << 9)
#define DWEMMC_FIFO_TWMARK(x) (x & 0xfff)
#define DWEMMC_FIFO_RWMARK(x) ((x & 0x1ff) << 16)
#define DWEMMC_DMA_BURST_SIZE(x) ((x & 0x7) << 28)
#define DWEMMC_CARD_RD_THR(x) ((x & 0xfff) << 16)
#define DWEMMC_CARD_RD_THR_EN (1 << 0)
#endif // __DWEMMC_H__

View File

@ -0,0 +1,648 @@
/** @file
This file implement the MMC Host Protocol for the DesignWare eMMC.
Copyright (c) 2014-2017, Linaro Limited. All rights reserved.
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
**/
#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;
}
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 (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;
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,
&gEfiMmcHostProtocolGuid, &gMciHost,
NULL
);
ASSERT_EFI_ERROR (Status);
return EFI_SUCCESS;
}

View File

@ -0,0 +1,53 @@
#/** @file
# INF file for the eMMC Host Protocol implementation for the DesignWare MMC.
#
# Copyright (c) 2014-2017, Linaro Limited. All rights reserved.
#
# This program and the accompanying materials
# are licensed and made available under the terms and conditions of the BSD License
# which accompanies this distribution. The full text of the license may be found at
# http://opensource.org/licenses/bsd-license.php
#
# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
#
#**/
[Defines]
INF_VERSION = 0x00010019
BASE_NAME = DwEmmcDxe
FILE_GUID = b549f005-4bd4-4020-a0cb-06f42bda68c3
MODULE_TYPE = DXE_DRIVER
VERSION_STRING = 1.0
ENTRY_POINT = DwEmmcDxeInitialize
[Sources.common]
DwEmmcDxe.c
[Packages]
EmbeddedPkg/EmbeddedPkg.dec
MdePkg/MdePkg.dec
[LibraryClasses]
ArmLib
BaseLib
BaseMemoryLib
CacheMaintenanceLib
IoLib
MemoryAllocationLib
TimerLib
UefiDriverEntryPoint
UefiLib
[Protocols]
gEfiCpuArchProtocolGuid
gEfiDevicePathProtocolGuid
gEfiMmcHostProtocolGuid
[Pcd]
gEmbeddedTokenSpaceGuid.PcdDwEmmcDxeBaseAddress
gEmbeddedTokenSpaceGuid.PcdDwEmmcDxeClockFrequencyInHz
[Depex]
TRUE

View File

@ -164,6 +164,10 @@
# LAN91x Ethernet Driver PCDs
gEmbeddedTokenSpaceGuid.PcdLan91xDxeBaseAddress|0x0|UINT32|0x00000029
# DwEmmc Driver PCDs
gEmbeddedTokenSpaceGuid.PcdDwEmmcDxeBaseAddress|0x0|UINT32|0x00000035
gEmbeddedTokenSpaceGuid.PcdDwEmmcDxeClockFrequencyInHz|0x0|UINT32|0x00000036
#
# Android FastBoot
#