Support designware emmc controller.
Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Haojian Zhuang haojian.zhuang@linaro.org Reviewed-by: Leif Lindholm leif.lindholm@linaro.org --- Drivers/Mmc/DwEmmcDxe/DwEmmc.h | 127 +++++++ Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.c | 648 ++++++++++++++++++++++++++++++++++++ Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.dec | 42 +++ Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.inf | 54 +++ 4 files changed, 871 insertions(+) create mode 100644 Drivers/Mmc/DwEmmcDxe/DwEmmc.h create mode 100644 Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.c create mode 100644 Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.dec create mode 100644 Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.inf
diff --git a/Drivers/Mmc/DwEmmcDxe/DwEmmc.h b/Drivers/Mmc/DwEmmcDxe/DwEmmc.h new file mode 100644 index 0000000..055f1e0 --- /dev/null +++ b/Drivers/Mmc/DwEmmcDxe/DwEmmc.h @@ -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__ diff --git a/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.c b/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.c new file mode 100644 index 0000000..fe23d11 --- /dev/null +++ b/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.c @@ -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; +} diff --git a/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.dec b/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.dec new file mode 100644 index 0000000..c4bf7ed --- /dev/null +++ b/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.dec @@ -0,0 +1,42 @@ +#/** @file +# Framework Module Development Environment Industry Standards +# +# This Package provides headers and libraries that conform to EFI/PI Industry standards. +# Copyright (c) 2007, Intel Corporation. All rights reserved.<BR> +# Copyright (c) 2012-2014, ARM Ltd. All rights reserved.<BR> +# Copyright (c) 2015-2017, Linaro. All rights reserved.<BR> +# +# 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] + DEC_SPECIFICATION = 0x00010019 + PACKAGE_NAME = MmcDwEmmcDxePkg + PACKAGE_GUID = 3869905e-c96c-4d20-9bfb-3b9d71bb900c + PACKAGE_VERSION = 0.1 + + +################################################################################ +# +# Include Section - list of Include Paths that are provided by this package. +# Comments are used for Keywords and Module Types. +# +# Supported Module Types: +# BASE SEC PEI_CORE PEIM DXE_CORE DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SMM_DRIVER DXE_SAL_DRIVER UEFI_DRIVER UEFI_APPLICATION +# +################################################################################ + +[Guids.common] + gDwEmmcDxeTokenSpaceGuid = { 0x6fdd76a9, 0xf220, 0x4f1d, { 0x9c, 0xcf, 0xbc, 0x2d, 0x68, 0x29, 0xab, 0x9c }} + +[PcdsFixedAtBuild.common] + # DwEmmc Driver PCDs + gDwEmmcDxeTokenSpaceGuid.PcdDwEmmcDxeBaseAddress|0x0|UINT32|0x00000001 + gDwEmmcDxeTokenSpaceGuid.PcdDwEmmcDxeClockFrequencyInHz|0x0|UINT32|0x00000002 diff --git a/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.inf b/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.inf new file mode 100644 index 0000000..c0cbba7 --- /dev/null +++ b/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.inf @@ -0,0 +1,54 @@ +#/** @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 + OpenPlatformPkg/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.dec + +[LibraryClasses] + ArmLib + BaseLib + BaseMemoryLib + CacheMaintenanceLib + IoLib + MemoryAllocationLib + TimerLib + UefiDriverEntryPoint + UefiLib + +[Protocols] + gEfiCpuArchProtocolGuid + gEfiDevicePathProtocolGuid + gEfiMmcHostProtocolGuid + +[Pcd] + gDwEmmcDxeTokenSpaceGuid.PcdDwEmmcDxeBaseAddress + gDwEmmcDxeTokenSpaceGuid.PcdDwEmmcDxeClockFrequencyInHz + +[Depex] + TRUE