diff options
Diffstat (limited to 'Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.c')
-rw-r--r-- | Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.c | 1291 |
1 files changed, 648 insertions, 643 deletions
diff --git a/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.c b/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.c index 6f43677..fe23d11 100644 --- a/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.c +++ b/Drivers/Mmc/DwEmmcDxe/DwEmmcDxe.c @@ -1,643 +1,648 @@ -/** @file - This file implement the MMC Host Protocol for the DesignWare eMMC. - - Copyright (c) 2014-2016, Linaro Limited. All rights reserved. - Copyright (c) 2014-2016, Hisilicon 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 <Library/PrintLib.h> -#include <Library/SerialPortLib.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 ((EFI_D_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: - ASSERT (0); - break; - 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)); - 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: - ASSERT (0); - } - 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 ((EFI_D_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 ((EFI_D_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 ((EFI_D_BLKIO, "DwEmmcDxeInitialize()\n")); - - //Publish Component Name, BlockIO protocol interfaces - Status = gBS->InstallMultipleProtocolInterfaces ( - &Handle, - &gEfiMmcHostProtocolGuid, &gMciHost, - NULL - ); - ASSERT_EFI_ERROR (Status); - - return EFI_SUCCESS; -} +/** @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;
+}
|