You've already forked slimbootloader
mirror of
https://github.com/Dasharo/slimbootloader.git
synced 2026-03-06 15:26:20 -08:00
Addressed unsigned compared against 0 (CWE 570) Signed-off-by: Kevin Tsai <kevin.tsai@intel.com>
1001 lines
33 KiB
C
1001 lines
33 KiB
C
/** @file
|
|
This file provides some helper functions which are specific for EMMC device.
|
|
|
|
Copyright (c) 2015 - 2023, Intel Corporation. All rights reserved.<BR>
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
|
|
**/
|
|
|
|
#include <Library/BaseLib.h>
|
|
#include <Library/MmcTuningLib.h>
|
|
#include <Library/MmcAccessLib.h>
|
|
#include <Library/BaseMemoryLib.h>
|
|
#include <Library/MemoryAllocationLib.h>
|
|
#include <Library/BootloaderCommonLib.h>
|
|
#include <Library/HobLib.h>
|
|
#include <Library/IoLib.h>
|
|
#include <Library/TimerLib.h>
|
|
#include <Library/VariableLib.h>
|
|
#include <Library/DebugPrintErrorLevelLib.h>
|
|
#include "MmcTuningLibPrivate.h"
|
|
|
|
CONST UINT32 EmmcWorstCasePattern[] = {
|
|
0x60FEFE00, 0xEDFF10FE, 0x60FEFE00, 0xEDFF10FE, 0xDE08216A, 0xE25F20DF, 0xDE08216A, 0xE25F20DF, // spt_a0_sandisk_50ohm
|
|
0xFF00CFFF, 0x1014B5CF, 0xFF00CFFF, 0x1014B5CF, 0x225F25DF, 0x20DFDE61, 0x225F25DF, 0x20DFDE61, // spt_a0_sandisk_40ohm
|
|
0xEFE770BD, 0x4AFF0000, 0xEFE770BD, 0x4AFF0000, 0x7B84FF5B, 0x5B655B0E, 0x7B84FF5B, 0x5B655B0E, // spt_a0_hynix_50ohm
|
|
0x7B93D620, 0xDFFF00E8, 0x7B93D620, 0xDFFF00E8, 0x3DC2FF3D, 0x38EA7DF3, 0x3DC2FF3D, 0x38EA7DF3, // spt_a0_hynix_40ohm
|
|
0x50127FAC, 0x4400FD00, 0x50127FAC, 0x4400FD00, 0x7887FF78, 0x109A394F, 0x7887FF78, 0x109A394F, // spt_a0_hynix_33ohm
|
|
0x50F20DBF, 0x3832CDFD, 0x50F20DBF, 0x3832CDFD, 0xFB04D720, 0xE69DC704, 0xFB04D720, 0xE69DC704, // spt_a0_samsung19nm_33ohm
|
|
0x40EFFF00, 0x40FFFF00, 0x40EFFF00, 0x40FFFF00, 0xE85F20DF, 0x20DF003F, 0xE85F20DF, 0x20DF003F, // spt_a0_toshiba_50ohm
|
|
0xFF00FF00, 0xFF00FF00, 0xFF00FF00, 0xFF00FF00, 0x20DFA0FF, 0x58DB20DF, 0x20DFA0FF, 0x58DB20DF, // spt_a0_toshiba_40ohm
|
|
0x50FF0008, 0x7E10FFFF, 0x50FF0008, 0x7E10FFFF, 0x3B8F7A85, 0xFF7A5AFF, 0x3B8F7A85, 0xFF7A5AFF, // spt_b0_hynix_40ohm
|
|
0x8A7575FF, 0x9ACA7F54, 0x8A7575FF, 0x9ACA7F54, 0xDF649B92, 0x7B24AFA0, 0xDF649B92, 0x7B24AFA0, // spt_b0_samsung16nm_50ohm
|
|
0xFF00FF00, 0xFF00FF00, 0xFF00FF00, 0xFF00FF00, 0xDAD6EF50, 0x6B1C6B25, 0xDAD6EF50, 0x6B1C6B25, // spt_b0_samsung16nm_40ohm
|
|
0x7f52ff02, 0xff02ff02, 0x7f52ff02, 0xff02ff02, 0x72bc63a5, 0x679afa9d, 0x72bc63a5, 0x679afa9d, // spt_b0_samsung16nm_33ohm
|
|
0xff00ff00, 0xff00ff00, 0xff00ff00, 0xff00ff00, 0xff00ff00, 0xff00ff00, 0xff00ff00, 0xff00ff00, // baseline_pattern_8
|
|
0xff00ff00, 0xff00ff00, 0xff00ff00, 0xff00ff00, 0xff00ff00, 0xff00ff00, 0xff00ff00, 0xff00ff00,
|
|
0xff00ff00, 0xff00ff00, 0xff00ff00, 0xff00ff00, 0xff00ff00, 0xff00ff00, 0xff00ff00, 0xff00ff00,
|
|
0xff00ff00, 0xff00ff00, 0xff00ff00, 0xff00ff00, 0xff00ff00, 0xff00ff00, 0xff00ff00, 0xff00ff00
|
|
};
|
|
|
|
/**
|
|
Populates the dll tuning register list library data.
|
|
|
|
@param[in] RegList DLL Tuning Register list
|
|
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
SetEmmcDllTuningLibData (
|
|
IN EMMC_DLL_TUNING_REG_LIST *RegList
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EMMC_DLL_TUNING_REG_LIST *EmmcLibData;
|
|
|
|
Status = GetLibraryData (PcdGet8 (PcdMmcTuningLibId), (VOID **)&EmmcLibData);
|
|
if (EFI_ERROR (Status)) {
|
|
EmmcLibData = AllocatePool (sizeof (*EmmcLibData));
|
|
}
|
|
|
|
CopyMem (EmmcLibData, RegList, sizeof (*EmmcLibData));
|
|
Status = SetLibraryData (PcdGet8 (PcdMmcTuningLibId), EmmcLibData, sizeof (*EmmcLibData));
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_WARN, "EmmcDllTuningLibData not generated!\n"));
|
|
}
|
|
}
|
|
|
|
/**
|
|
Get the dll tuning register list.
|
|
|
|
@param[out] RegList DLL Tuning Register list
|
|
|
|
@retval EFI_SUCCESS Getting lib data successfully.
|
|
@retval EFI_NOT_FOUND The Lib data is not found.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
GetEmmcDllTuningLibData (
|
|
OUT EMMC_DLL_TUNING_REG_LIST *RegList
|
|
)
|
|
{
|
|
EMMC_DLL_TUNING_REG_LIST *MmcLibData;
|
|
EFI_STATUS Status;
|
|
|
|
Status = GetLibraryData (PcdGet8 (PcdMmcTuningLibId), (VOID **)&MmcLibData);
|
|
if (EFI_ERROR (Status)) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
RegList->TxDataDllCntl1 = MmcLibData->TxDataDllCntl1;
|
|
RegList->RxStrobeDllCntl = MmcLibData->RxStrobeDllCntl;
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Set Tx Data Delay Control 1.
|
|
|
|
@param[in] EmmcBaseAddress Base address of Device MMIO registers
|
|
@param[in] Value Value (0 - 79)
|
|
@param[in] RegList EMMC DLL tuning relevant register List
|
|
|
|
**/
|
|
VOID
|
|
EmmcSetTxDllCtrl1 (
|
|
IN UINTN EmmcBaseAddress,
|
|
IN UINT8 Value,
|
|
IN EMMC_DLL_TUNING_REG_LIST *RegList
|
|
)
|
|
{
|
|
MmioAndThenOr8 (EmmcBaseAddress + (RegList->TxDataDllCntl1 + 1), 0, Value);
|
|
}
|
|
|
|
/**
|
|
Get Tx Data Delay Control 1.
|
|
|
|
@param[in] EmmcBaseAddress Base address of Device MMIO registers
|
|
@param[in] RegList EMMC DLL tuning relevant register List
|
|
|
|
@retval UINT8 DLL Value
|
|
**/
|
|
UINT8
|
|
EmmcGetTxDllCtrl1 (
|
|
IN UINTN EmmcBaseAddress,
|
|
IN EMMC_DLL_TUNING_REG_LIST *RegList
|
|
)
|
|
{
|
|
UINT8 Value;
|
|
|
|
Value = MmioRead8 (EmmcBaseAddress + (RegList->TxDataDllCntl1 + 1));
|
|
|
|
return Value;
|
|
}
|
|
|
|
/**
|
|
Set Rx Strobe Delay Control DLL1 for HS400.
|
|
|
|
@param[in] EmmcBaseAddress Base address of Device MMIO registers
|
|
@param[in] RxDll To program RxDll1 or RxDll2 register
|
|
@param[in] Value Value (0 - 39)
|
|
@param[in] RegList EMMC DLL tuning relevant register List
|
|
|
|
**/
|
|
VOID
|
|
EmmcSetRxDllCtrl (
|
|
IN UINTN EmmcBaseAddress,
|
|
IN UINT8 RxDll,
|
|
IN UINT8 Value,
|
|
IN EMMC_DLL_TUNING_REG_LIST *RegList
|
|
)
|
|
{
|
|
if (RxDll == RxDll1) {
|
|
MmioAndThenOr8 (EmmcBaseAddress + (RegList->RxStrobeDllCntl + 1), 0, Value);
|
|
} else {
|
|
MmioAndThenOr8 (EmmcBaseAddress + RegList->RxStrobeDllCntl, 0, Value);
|
|
}
|
|
}
|
|
|
|
/**
|
|
Get Rx Strobe Delay Control DLL1 for HS400.
|
|
|
|
@param[in] EmmcBaseAddress Base address of Device MMIO registers
|
|
@param[in] RegList EMMC DLL tuning relevant register List
|
|
|
|
@retval UINT8 DLL Value
|
|
**/
|
|
UINT8
|
|
EmmcGetRxDllCtrl (
|
|
IN UINTN EmmcBaseAddress,
|
|
IN EMMC_DLL_TUNING_REG_LIST *RegList
|
|
)
|
|
{
|
|
UINT8 Value;
|
|
|
|
Value = MmioRead8 (EmmcBaseAddress + RegList->RxStrobeDllCntl);
|
|
|
|
return Value;
|
|
}
|
|
|
|
/**
|
|
To perform HS400 Rx Data Path Training
|
|
|
|
@param[in] EmmcBaseAddress Base address of device MMIO registers
|
|
@param[in] BlockLen Block length of device
|
|
@param[in] RegList EMMC DLL tuning relevant register List
|
|
@param[in, out] EmmcTuningData A pointer to EMMC_TUNING_DATA structure
|
|
|
|
@retval EFI_SUCCESS HS400 Rx Data Path Training is successful.
|
|
@retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.
|
|
@retval EFI_INVALID_PARAMETER A parameter was incorrect.
|
|
@retval EFI_DEVICE_ERROR Hardware Error
|
|
@retval EFI_NO_MEDIA No media
|
|
@retval EFI_MEDIA_CHANGED Media Change
|
|
@retval EFI_BAD_BUFFER_SIZE Buffer size is bad
|
|
@retval EFI_CRC_ERROR Command or Data CRC Error
|
|
**/
|
|
EFI_STATUS
|
|
EmmcRxHs400Tuning (
|
|
IN UINTN EmmcBaseAddress,
|
|
IN UINT32 BlockLen,
|
|
IN EMMC_DLL_TUNING_REG_LIST *RegList,
|
|
IN OUT EMMC_TUNING_DATA *EmmcTuningData
|
|
)
|
|
{
|
|
UINT8 *Buffer;
|
|
UINT8 DllCount;
|
|
UINT8 Smin;
|
|
UINT8 Smax;
|
|
UINT8 Sopt;
|
|
EFI_STATUS Status;
|
|
BLOCK_READ_WRITE_STATUS FirstRead;
|
|
UINT32 TuningPatternSize;
|
|
CONST UINT8 DllMax = RX_STROBE_DLL1_TAP_MAX_RANGE;
|
|
|
|
DEBUG ((DEBUG_VERBOSE, "EmmcRxHs400Tuning() Start\n"));
|
|
|
|
Status = EFI_SUCCESS;
|
|
Smin = RX_STROBE_DLL1_TAP_MIN_MEPT;
|
|
Smax = RX_STROBE_DLL1_TAP_MAX_MEPT;
|
|
|
|
TuningPatternSize = BlockLen * EMMC_HS400_TUNING_PATTERN_BLOCKS_NUMBER;
|
|
|
|
Buffer = (VOID *) AllocatePool (TuningPatternSize);
|
|
if (Buffer == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// 1. Read Tuning Block
|
|
//
|
|
Status = MmcReadBlocks (
|
|
0,
|
|
PcdGet32 (PcdMmcTuningLba),
|
|
TuningPatternSize,
|
|
Buffer
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "EmmcRxHs400Tuning: Tuning Blocks Read at HS200 Mode Failed!\n"));
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// 2. Move to HS400 Mode
|
|
//
|
|
Status = EmmcModeSelection (Hs400);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "EmmcRxHs400Tuning: eMMC HS400 Mode Selection Failed!\n"));
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// 3. Set Rx Strobe DLL1 to the Minimal Expected Passing Tap (Smin)
|
|
// Offset 830h: Rx Strobe Delay DLL 1(HS400 Mode), bits [14:8]
|
|
// Set Rx Data Strobe DLL2 to middle point 0x13
|
|
// Offset 830h: Rx Strobe Delay DLL 2(HS400 Mode), bits [6:0]
|
|
DEBUG ((DEBUG_VERBOSE, "EmmcRxHs400Tuning: Set Rx Data DLL1 to the Minimal Expected Passing Tap (Smin = 0x%x)\n",
|
|
Smin));
|
|
DllCount = RX_STROBE_DLL1_TAP_MIN_MEPT;
|
|
EmmcSetRxDllCtrl (EmmcBaseAddress, RxDll1, DllCount, RegList);
|
|
EmmcSetRxDllCtrl (EmmcBaseAddress, RxDll2, DllCount, RegList);
|
|
|
|
//
|
|
// 4. Read the block that was stored
|
|
// 5. If CRC fails on first read, increase the DLL Step and repeat block read until passed
|
|
// Else if CRC passed on first read, decrease the DLL Step and repeat block read until failed
|
|
// 6. Store the Rx Path min DLL passing step number
|
|
//
|
|
FirstRead = NotAvailable;
|
|
|
|
while (DllCount <= DllMax) {
|
|
DEBUG ((DEBUG_VERBOSE, "[ EmmcRxHs400Tuning: Rx Min DLL1 (DllCount) = 0x%x ]\n", DllCount));
|
|
MicroSecondDelay (1 * 1000);
|
|
Status = MmcReadBlocks (
|
|
0,
|
|
PcdGet32 (PcdMmcTuningLba),
|
|
TuningPatternSize,
|
|
Buffer
|
|
);
|
|
|
|
if (Status == EFI_SUCCESS) {
|
|
if (FirstRead == NotAvailable) {
|
|
FirstRead = Passed;
|
|
} else if (FirstRead == Failed) {
|
|
Smin = DllCount;
|
|
break;
|
|
}
|
|
if (DllCount == RX_STROBE_DLL1_TAP_MIN_RANGE) {
|
|
Smin = RX_STROBE_DLL1_TAP_MIN_RANGE;
|
|
break;
|
|
}
|
|
} else if (Status == EFI_CRC_ERROR || Status == EFI_DEVICE_ERROR || Status == EFI_TIMEOUT) {
|
|
if (FirstRead == NotAvailable) {
|
|
FirstRead = Failed;
|
|
} else if (FirstRead == Passed) {
|
|
Smin = DllCount + 1;
|
|
break;
|
|
}
|
|
if (DllCount == RX_STROBE_DLL1_TAP_MAX_RANGE) {
|
|
Status = EFI_CRC_ERROR;
|
|
DEBUG ((DEBUG_ERROR, "[Rx DLL Tuning Failed] DllCount == TAP_MAX_RANGE\n"));
|
|
goto Exit;
|
|
}
|
|
} else {
|
|
DEBUG ((DEBUG_ERROR, "[Rx DLL Tuning Failed] Smin - Tuning Blocks Read Error!\n"));
|
|
goto Exit;
|
|
}
|
|
|
|
if (FirstRead == Failed) {
|
|
DllCount++;
|
|
} else {
|
|
DllCount--;
|
|
}
|
|
EmmcSetRxDllCtrl (EmmcBaseAddress, RxDll1, DllCount, RegList);
|
|
EmmcSetRxDllCtrl (EmmcBaseAddress, RxDll2, DllCount, RegList);
|
|
}
|
|
|
|
DEBUG ((DEBUG_VERBOSE, "[Rx DLL Tuning] Found Minimal Passing Tap = 0x%x\n", Smin));
|
|
|
|
//
|
|
// 7. Set the Rx Strobe DLL1 to the Maximal Expected Passing Tap (Smax)
|
|
// Offset 830h: Rx Strobe Delay DLL 1(HS400 Mode), bits [14:8]
|
|
// 8. Read the block that was stored
|
|
// 9. If CRC fails on first read, decrease the DLL Step and repeat step 8 until pass
|
|
// Else if CRC passed on first read, increase the DLL Step and repeat step 8 until failed
|
|
//
|
|
DEBUG ((DEBUG_VERBOSE, "EmmcRxHs400Tuning: Set Rx Data DLL1 to the Maximal Expected Passing Tap (Smax = 0x%x)\n",
|
|
Smax));
|
|
DllCount = RX_STROBE_DLL1_TAP_MAX_MEPT;
|
|
EmmcSetRxDllCtrl (EmmcBaseAddress, RxDll1, DllCount, RegList);
|
|
EmmcSetRxDllCtrl (EmmcBaseAddress, RxDll2, DllCount, RegList);
|
|
|
|
FirstRead = NotAvailable;
|
|
while (DllCount <= DllMax) {
|
|
DEBUG ((DEBUG_VERBOSE, "[ EmmcRxHs400Tuning: Rx Max DLL1 (DllCount) = 0x%x ]\n", DllCount));
|
|
MicroSecondDelay (1 * 1000);
|
|
Status = MmcReadBlocks (
|
|
0,
|
|
PcdGet32 (PcdMmcTuningLba),
|
|
TuningPatternSize,
|
|
Buffer
|
|
);
|
|
|
|
if (Status == EFI_SUCCESS) {
|
|
if (FirstRead == NotAvailable) {
|
|
FirstRead = Passed;
|
|
} else if (FirstRead == Failed) {
|
|
Smax = DllCount;
|
|
break;
|
|
}
|
|
if (DllCount == RX_STROBE_DLL1_TAP_MAX_RANGE) {
|
|
Smax = DllCount;
|
|
break;
|
|
}
|
|
} else if (Status == EFI_CRC_ERROR || Status == EFI_DEVICE_ERROR || Status == EFI_TIMEOUT) {
|
|
if (FirstRead == NotAvailable) {
|
|
FirstRead = Failed;
|
|
} else if (FirstRead == Passed) {
|
|
Smax = DllCount - 1;
|
|
break;
|
|
}
|
|
if (DllCount == RX_STROBE_DLL1_TAP_MIN_RANGE) {
|
|
Status = EFI_CRC_ERROR;
|
|
goto Exit;
|
|
}
|
|
} else {
|
|
DEBUG ((DEBUG_ERROR, "[Rx DLL Tuning Failed] Smax - Tuning Blocks Read Error!\n"));
|
|
goto Exit;
|
|
}
|
|
if (FirstRead == Failed) {
|
|
DllCount--;
|
|
} else {
|
|
DllCount++;
|
|
}
|
|
EmmcSetRxDllCtrl (EmmcBaseAddress, RxDll1, DllCount, RegList);
|
|
EmmcSetRxDllCtrl (EmmcBaseAddress, RxDll2, DllCount, RegList);
|
|
}
|
|
|
|
//
|
|
// 10. Store the Rx Path max DLL Passing Step number
|
|
//
|
|
|
|
//
|
|
// 11. Compute the Rx DLL Optimal Point (Sopt) = (Smax - Smin)/2 + Smin
|
|
//
|
|
Sopt = (Smax - Smin) / 2 + Smin;
|
|
DEBUG ((DEBUG_VERBOSE, "[Rx DLL Tuning] Optimal Point (Sopt = (Smax[0x%x] - Smin[0x%x]) / 2 + Smin[0x%x]) = 0x%x\n",
|
|
Smax, Smin, Smin, Sopt));
|
|
//
|
|
// 12. Store the Rx DLL optimal value (Sopt)
|
|
//
|
|
EmmcSetRxDllCtrl (EmmcBaseAddress, RxDll1, Sopt, RegList);
|
|
EmmcSetRxDllCtrl (EmmcBaseAddress, RxDll2, Sopt, RegList);
|
|
|
|
Status = EFI_SUCCESS;
|
|
EmmcTuningData->Hs400RxStrobe1Dll = Sopt;
|
|
|
|
Exit:
|
|
if (Buffer != NULL) {
|
|
FreePool (Buffer);
|
|
}
|
|
DEBUG ((DEBUG_VERBOSE, "EmmcRxHs400Tuning() End, Status = %r\n", Status));
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
To perform HS400 Tx Data Path Training
|
|
|
|
@param[in] EmmcBaseAddress Base address of device MMIO registers
|
|
@param[in] BlockLen Block length of device
|
|
@param[in] RegList EMMC DLL tuning relevant register List
|
|
@param[in, out] EmmcTuningData A pointer to EMMC_TUNING_DATA structure
|
|
|
|
@retval EFI_SUCCESS HS400 Rx Data Path Training is successful.
|
|
@retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.
|
|
@retval EFI_INVALID_PARAMETER A parameter was incorrect.
|
|
@retval EFI_DEVICE_ERROR Hardware Error
|
|
@retval EFI_NO_MEDIA No media
|
|
@retval EFI_MEDIA_CHANGED Media Change
|
|
@retval EFI_BAD_BUFFER_SIZE Buffer size is bad
|
|
@retval EFI_CRC_ERROR Command or Data CRC Error
|
|
**/
|
|
EFI_STATUS
|
|
EmmcTxHs400Tuning (
|
|
IN UINTN EmmcBaseAddress,
|
|
IN UINT32 BlockLen,
|
|
IN EMMC_DLL_TUNING_REG_LIST *RegList,
|
|
IN OUT EMMC_TUNING_DATA *EmmcTuningData
|
|
)
|
|
{
|
|
UINT8 *Buffer;
|
|
UINT8 DllCount;
|
|
UINT8 Smin;
|
|
UINT8 Smax;
|
|
UINT8 Sopt;
|
|
UINT8 Num;
|
|
EFI_STATUS Status;
|
|
BLOCK_READ_WRITE_STATUS FirstWrite;
|
|
CONST UINT8 DllMax = TX_DATA_DLL_TAP_MAX_RANGE;
|
|
|
|
DEBUG ((DEBUG_VERBOSE, "EmmcTxHs400Tuning() Start\n"));
|
|
|
|
Status = EFI_SUCCESS;
|
|
Smin = TX_DATA_DLL_TAP_MIN_MEPT;
|
|
Smax = TX_DATA_DLL_TAP_MAX_MEPT;
|
|
Num = 0;
|
|
|
|
Buffer = (VOID *) AllocatePool (BlockLen);
|
|
if (Buffer == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto Exit;
|
|
}
|
|
//
|
|
// 1. Read Tuning Block that used at Rx HS400 Tuning
|
|
//
|
|
Status = MmcReadBlocks (
|
|
0,
|
|
PcdGet32 (PcdMmcTuningLba),
|
|
BlockLen,
|
|
Buffer
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "EmmcTxHs400Tuning: Tuning Block Read at HS400 Mode Failed!\n"));
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// 2. Set Tx Data DLL1 to the Minimal Expected Passing Tap (Smin)
|
|
// Offset 824h: Tx Data Delay Control 1
|
|
// Tx Data Delay (HS400 Mode), BIT[14:8]
|
|
//
|
|
DEBUG ((DEBUG_VERBOSE, "EmmcTxHs400Tuning: Set Tx Data DLL to the Minimal Expected Passing Tap (Smin = 0x%x)\n", Smin));
|
|
|
|
DllCount = TX_DATA_DLL_TAP_MIN_MEPT;
|
|
EmmcSetTxDllCtrl1 (EmmcBaseAddress, DllCount, RegList);
|
|
|
|
//
|
|
// 2. Write Single Block
|
|
//
|
|
FirstWrite = NotAvailable;
|
|
|
|
while (DllCount <= DllMax) {
|
|
DEBUG ((DEBUG_VERBOSE, "[ EmmcTxHs400Tuning: Tx Min DLL (DllCount) = 0x%x ]\n", DllCount));
|
|
MicroSecondDelay (1 * 1000);
|
|
Status = MmcWriteBlocks (
|
|
0,
|
|
PcdGet32 (PcdMmcTuningLba),
|
|
BlockLen,
|
|
Buffer
|
|
);
|
|
|
|
if (Status == EFI_SUCCESS) {
|
|
if (FirstWrite == NotAvailable) {
|
|
FirstWrite = Passed;
|
|
} else if (FirstWrite == Failed) {
|
|
Smin = DllCount;
|
|
break;
|
|
}
|
|
if (DllCount == TX_DATA_DLL_TAP_MIN_RANGE) {
|
|
Smin = TX_DATA_DLL_TAP_MIN_RANGE;
|
|
break;
|
|
}
|
|
//
|
|
// 3. If CRC fails increment DLL Step and repeat step 2
|
|
//
|
|
} else if (Status == EFI_CRC_ERROR || Status == EFI_DEVICE_ERROR || Status == EFI_TIMEOUT) {
|
|
if (FirstWrite == NotAvailable) {
|
|
FirstWrite = Failed;
|
|
} else if (FirstWrite == Passed) {
|
|
Smin = DllCount + 1;
|
|
break;
|
|
}
|
|
if (DllCount == TX_DATA_DLL_TAP_MAX_RANGE) {
|
|
DEBUG ((DEBUG_ERROR, "[Tx DLL Tuning Failed] DllCount == TAP_MAX_RANGE\n"));
|
|
goto Exit;
|
|
}
|
|
} else {
|
|
DEBUG ((DEBUG_ERROR, "[Tx DLL Tuning Failed] Smin - Tuning Block Write Error!\n"));
|
|
goto Exit;
|
|
}
|
|
if (FirstWrite == Failed) {
|
|
DllCount++;
|
|
} else {
|
|
DllCount--;
|
|
}
|
|
EmmcSetTxDllCtrl1 (EmmcBaseAddress, DllCount, RegList);
|
|
}
|
|
//
|
|
// 4. Store the Tx Path min DLL passing step number
|
|
//
|
|
DEBUG ((DEBUG_VERBOSE, "[Tx DLL Tuning] Found Minimal Passing Tap = 0x%x\n", Smin));
|
|
|
|
//
|
|
// 5. Set the DLL to max expected passing step (Smax)
|
|
//
|
|
DEBUG ((DEBUG_VERBOSE, "EmmcTxHs400Tuning: Set Tx Data DLL to the Maximal Expected Passing Tap (Smax = 0x%x)\n", Smax));
|
|
DllCount = TX_DATA_DLL_TAP_MAX_MEPT;
|
|
EmmcSetTxDllCtrl1 (EmmcBaseAddress, DllCount, RegList);
|
|
//
|
|
// 6. Write Single Block
|
|
//
|
|
FirstWrite = NotAvailable;
|
|
|
|
while (DllCount <= DllMax) {
|
|
DEBUG ((DEBUG_VERBOSE, "[ EmmcTxHs400Tuning: Tx Max DLL1 (DllCount) = 0x%x ]\n", DllCount));
|
|
MicroSecondDelay (1 * 1000);
|
|
Status = MmcWriteBlocks (
|
|
0,
|
|
PcdGet32 (PcdMmcTuningLba),
|
|
BlockLen,
|
|
Buffer
|
|
);
|
|
|
|
if (Status == EFI_SUCCESS) {
|
|
if (FirstWrite == NotAvailable) {
|
|
FirstWrite = Passed;
|
|
} else if (FirstWrite == Failed) {
|
|
Smax = DllCount;
|
|
break;
|
|
}
|
|
if (DllCount == TX_DATA_DLL_TAP_MAX_RANGE) {
|
|
Smax = TX_DATA_DLL_TAP_MAX_RANGE;
|
|
break;
|
|
}
|
|
//
|
|
// 7. If CRC fails decrement DLL Step and repeat step 6
|
|
//
|
|
} else if (Status == EFI_CRC_ERROR || Status == EFI_DEVICE_ERROR || Status == EFI_TIMEOUT) {
|
|
if (FirstWrite == NotAvailable) {
|
|
FirstWrite = Failed;
|
|
} else if (FirstWrite == Passed) {
|
|
Smax = DllCount - 1;
|
|
break;
|
|
}
|
|
if (DllCount == TX_DATA_DLL_TAP_MIN_RANGE) {
|
|
DEBUG ((DEBUG_ERROR, "[Tx DLL Tuning Failed] DllCount == TAP_MIN_RANGE\n"));
|
|
goto Exit;
|
|
}
|
|
} else {
|
|
DEBUG ((DEBUG_ERROR, "[Tx DLL Tuning Failed] Smax - Tuning Block Write Error!\n"));
|
|
goto Exit;
|
|
}
|
|
if (FirstWrite == Failed) {
|
|
DllCount--;
|
|
} else {
|
|
DllCount++;
|
|
}
|
|
EmmcSetTxDllCtrl1 (EmmcBaseAddress, DllCount, RegList);
|
|
}
|
|
//
|
|
// 8. Store the DLL passing step number (Smax)
|
|
//
|
|
DEBUG ((DEBUG_VERBOSE, "[Tx DLL Tuning] Found Maximal Passing Tap = 0x%x\n", Smax));
|
|
|
|
Num = Smax - Smin;
|
|
if (Num <= 0) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto Exit;
|
|
}
|
|
//
|
|
// 9. Compute the Tx DLL Optimal point (Sopt) = (Smax - Smin) / 2 + Smin
|
|
//
|
|
Sopt = (Smax - Smin) / 2 + Smin;
|
|
DEBUG ((DEBUG_VERBOSE, "[Tx DLL Tuning] Optimal Point (Sopt = (Smax[0x%x] - Smin[0x%x]) / 2 + Smin[0x%x]) = 0x%x\n",
|
|
Smax, Smin, Smin, Sopt));
|
|
|
|
//
|
|
// 10. Store the Tx Strobe DLL Optimal point value
|
|
//
|
|
EmmcSetTxDllCtrl1 (EmmcBaseAddress, Sopt, RegList);
|
|
|
|
Status = EFI_SUCCESS;
|
|
EmmcTuningData->Hs400TxDataDll = Sopt;
|
|
|
|
Exit:
|
|
if (Buffer != NULL) {
|
|
FreePool (Buffer);
|
|
}
|
|
DEBUG ((DEBUG_VERBOSE, "EmmcTxHs400Tuning() End, Status = %r\n", Status));
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Writes worst case pattern used for HS400 taining
|
|
|
|
@param[in] BlockLen Block length of device
|
|
@param[in] TuningPattern A pointer to EMMC_TUNING_DATA structure
|
|
|
|
@retval EFI_SUCCESS HS400 Rx Data Path Training is successful.
|
|
@retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.
|
|
@retval EFI_INVALID_PARAMETER A parameter was incorrect.
|
|
@retval EFI_DEVICE_ERROR Hardware Error
|
|
@retval EFI_NO_MEDIA No media
|
|
@retval EFI_MEDIA_CHANGED Media Change
|
|
@retval EFI_BAD_BUFFER_SIZE Buffer size is bad
|
|
**/
|
|
EFI_STATUS
|
|
EmmcWriteHs400TuningPattern (
|
|
IN UINT32 BlockLen,
|
|
IN UINT8 *TuningPattern
|
|
)
|
|
{
|
|
UINT8 *Buffer;
|
|
UINT32 Index;
|
|
UINT32 TuningPatternSize;
|
|
EFI_STATUS Status;
|
|
|
|
Status = EFI_SUCCESS;
|
|
TuningPatternSize = BlockLen * EMMC_HS400_TUNING_PATTERN_BLOCKS_NUMBER;
|
|
|
|
DEBUG ((DEBUG_VERBOSE, "EmmcWriteHs400TuningPattern() Start\n"));
|
|
|
|
Buffer = (VOID *) AllocatePool (TuningPatternSize);
|
|
if (Buffer == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto Exit;
|
|
}
|
|
|
|
for (Index = 0; Index < EMMC_HS400_TUNING_PATTERN_BLOCKS_NUMBER; Index++) {
|
|
CopyMem (Buffer + (Index * BlockLen), TuningPattern, BlockLen);
|
|
}
|
|
|
|
Status = MmcWriteBlocks (
|
|
0,
|
|
PcdGet32 (PcdMmcTuningLba),
|
|
TuningPatternSize,
|
|
Buffer
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "EmmcWriteHs400TuningPattern: Multiple Blocks Write at HS200 Mode Failed!\n"));
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
if (Buffer != NULL) {
|
|
FreePool (Buffer);
|
|
}
|
|
DEBUG ((DEBUG_VERBOSE, "EmmcWriteHs400TuningPattern() End, Status = %r\n", Status));
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Configure eMMC DLLs to HS400 Mode
|
|
|
|
@param[in] EmmcHcPciBase Base address of device MMIO registers
|
|
@param[in] BlockLen Block length of device
|
|
@param[in] RegList EMMC DLL tuning relevant register List
|
|
@param[out] EmmcTuningData A pointer to EMMC_TUNING_DATA structure
|
|
|
|
@retval EFI_SUCCESS The function completed successfully
|
|
@retval EFI_NOT_FOUND The item was not found
|
|
@retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.
|
|
@retval EFI_INVALID_PARAMETER A parameter was incorrect.
|
|
@retval EFI_DEVICE_ERROR Hardware Error
|
|
@retval EFI_NO_MEDIA No media
|
|
@retval EFI_MEDIA_CHANGED Media Change
|
|
@retval EFI_BAD_BUFFER_SIZE Buffer size is bad
|
|
@retval EFI_CRC_ERROR Command or Data CRC Error
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
ConfigureEmmcHs400Mode (
|
|
IN UINTN EmmcHcPciBase,
|
|
IN UINT32 BlockLen,
|
|
IN EMMC_DLL_TUNING_REG_LIST *RegList,
|
|
OUT EMMC_TUNING_DATA *EmmcTuningData
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_STATUS ModeStatus;
|
|
UINTN EmmcBaseAddress;
|
|
|
|
Status = EFI_SUCCESS;
|
|
ModeStatus = EFI_SUCCESS;
|
|
|
|
DEBUG ((DEBUG_VERBOSE, "ConfigureEmmcHs400Mode() Start\n"));
|
|
EmmcTuningData->Hs400DataValid = FALSE;
|
|
EmmcBaseAddress = MmioRead32 (EmmcHcPciBase + PCI_BASE_ADDRESSREG_OFFSET) & 0xFFFFF000;
|
|
|
|
DEBUG ((DEBUG_VERBOSE, "Initial HS400 DLL values :\n"));
|
|
DEBUG ((DEBUG_VERBOSE, "Tx Data Delay Control 1 = 0x%08x\n", MmioRead32 (EmmcBaseAddress + RegList->TxDataDllCntl1)));
|
|
DEBUG ((DEBUG_VERBOSE, "Rx Strobe Delay Control = 0x%08x\n", MmioRead32 (EmmcBaseAddress + RegList->RxStrobeDllCntl)));
|
|
|
|
Status = EmmcWriteHs400TuningPattern (BlockLen, (UINT8 *)EmmcWorstCasePattern);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "ConfigureEmmcHs400Mode: EmmcWriteHs400TuningPattern Failed!\n"));
|
|
}
|
|
|
|
//
|
|
// Rx HS400 Auto Tuning
|
|
//
|
|
Status = EmmcRxHs400Tuning (EmmcBaseAddress, BlockLen, RegList, EmmcTuningData);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "ConfigureEmmcHs400Mode: Rx HS400 Auto Tuning Failed!\n"));
|
|
ModeStatus = EmmcModeSelection (Hs200);
|
|
if (EFI_ERROR (ModeStatus)) {
|
|
DEBUG ((DEBUG_ERROR, "EmmcRxHs400Tuning: eMMC HS200 Mode Selection Failed!\n"));
|
|
}
|
|
EmmcTuningData->Hs400DataValid = FALSE;
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Tx HS400 Auto Tuning
|
|
//
|
|
Status = EmmcTxHs400Tuning (EmmcBaseAddress, BlockLen, RegList, EmmcTuningData);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "ConfigureEmmcHs400Mode: Tx HS400 Auto Tuning Failed!\n"));
|
|
ModeStatus = EmmcModeSelection (Hs200);
|
|
if (EFI_ERROR (ModeStatus)) {
|
|
DEBUG ((DEBUG_ERROR, "EmmcTxHs400Tuning: eMMC HS200 Mode Selection Failed!\n"));
|
|
}
|
|
EmmcTuningData->Hs400DataValid = FALSE;
|
|
return Status;
|
|
}
|
|
|
|
DEBUG ((DEBUG_VERBOSE, "ConfigureEmmcHs400Mode () End\n"));
|
|
EmmcTuningData->Hs400DataValid = TRUE;
|
|
return Status;
|
|
}
|
|
|
|
|
|
/**
|
|
Configure eMMC DLLs with given Tuning Data.
|
|
|
|
@param[in] EmmcHcPciBase Base address of device MMIO registers
|
|
@param[in] RegList EMMC DLL tuning relevant register List
|
|
@param[in] EmmcTuningData A pointer to EMMC_TUNING_DATA structure
|
|
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
MmcSetDlls (
|
|
IN UINTN EmmcHcPciBase,
|
|
IN EMMC_DLL_TUNING_REG_LIST *RegList,
|
|
IN EMMC_TUNING_DATA *EmmcTuningData
|
|
)
|
|
{
|
|
UINTN EmmcBaseAddress;
|
|
|
|
if ((EmmcTuningData != NULL) && (EmmcTuningData->Hs400DataValid)) {
|
|
EmmcBaseAddress = MmioRead32 (EmmcHcPciBase + PCI_BASE_ADDRESSREG_OFFSET) & 0xFFFFF000;
|
|
|
|
EmmcSetRxDllCtrl (EmmcBaseAddress, RxDll1, EmmcTuningData->Hs400RxStrobe1Dll, RegList);
|
|
EmmcSetRxDllCtrl (EmmcBaseAddress, RxDll2, EmmcTuningData->Hs400RxStrobe1Dll, RegList);
|
|
EmmcSetTxDllCtrl1 (EmmcBaseAddress, EmmcTuningData->Hs400TxDataDll, RegList);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
Populate Tuning Data with existing programmed eMMC DLLs.
|
|
|
|
@param[in] EmmcHcPciBase Base address of device MMIO registers
|
|
@param[in] RegList EMMC DLL tuning relevant register List
|
|
@param[out] EmmcTuningData A pointer to EMMC_TUNING_DATA structure
|
|
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
MmcGetDlls (
|
|
IN UINTN EmmcHcPciBase,
|
|
IN EMMC_DLL_TUNING_REG_LIST *RegList,
|
|
OUT EMMC_TUNING_DATA *EmmcTuningData
|
|
)
|
|
{
|
|
UINTN EmmcBaseAddress;
|
|
|
|
EmmcBaseAddress = MmioRead32 (EmmcHcPciBase + PCI_BASE_ADDRESSREG_OFFSET) & 0xFFFFF000;
|
|
EmmcTuningData->Hs400DataValid = TRUE;
|
|
EmmcTuningData->Hs400RxStrobe1Dll = EmmcGetRxDllCtrl (EmmcBaseAddress, RegList);
|
|
EmmcTuningData->Hs400TxDataDll = EmmcGetTxDllCtrl1 (EmmcBaseAddress, RegList);
|
|
}
|
|
|
|
|
|
/**
|
|
This function tunes EMMC device to HS400
|
|
|
|
@param[in] EmmcHcPciBase Base address of device MMIO registers
|
|
@param[in] BlockLen Block length of device
|
|
@param[in] RegList EMMC DLL tuning relevant register List
|
|
@param[out] EmmcTuningData EMMC DLL tuning data
|
|
|
|
@retval EFI_SUCCESS The function completed successfully
|
|
@retval EFI_NOT_FOUND The item was not found
|
|
@retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.
|
|
@retval EFI_INVALID_PARAMETER A parameter was incorrect.
|
|
@retval EFI_DEVICE_ERROR Hardware Error
|
|
@retval EFI_NO_MEDIA No media
|
|
@retval EFI_MEDIA_CHANGED Media Change
|
|
@retval EFI_BAD_BUFFER_SIZE Buffer size is bad
|
|
@retval EFI_CRC_ERROR Command or Data CRC Error
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
MmcTuneDllsToHs400 (
|
|
IN UINTN EmmcHcPciBase,
|
|
IN UINT32 BlockLen,
|
|
IN EMMC_DLL_TUNING_REG_LIST *RegList,
|
|
OUT EMMC_TUNING_DATA *EmmcTuningData
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
DEBUG ((DEBUG_INFO, "Mmc Dll Tuning for HS400 ...\n"));
|
|
Status = ConfigureEmmcHs400Mode (EmmcHcPciBase, BlockLen, RegList, EmmcTuningData);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "MmcTuneDllsToHs400: Tuning Failed!\n"));
|
|
goto Done;
|
|
}
|
|
DEBUG ((DEBUG_INFO, "Mmc Dll Tuning for HS400 Successful\n"));
|
|
|
|
Done:
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
This function tunes EMMC device
|
|
|
|
@param[in] EmmcHcPciBase Base address of device MMIO registers
|
|
@param[out] EmmcTuningData EMMC DLL tuning data
|
|
|
|
@retval EFI_SUCCESS The function completed successfully
|
|
@retval EFI_ABORTED The function aborted in the middle
|
|
@retval EFI_NOT_FOUND The item was not found
|
|
@retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.
|
|
@retval EFI_INVALID_PARAMETER A parameter was incorrect.
|
|
@retval EFI_DEVICE_ERROR Hardware Error
|
|
@retval EFI_NO_MEDIA No media
|
|
@retval EFI_MEDIA_CHANGED Media Change
|
|
@retval EFI_BAD_BUFFER_SIZE Buffer size is bad
|
|
@retval EFI_CRC_ERROR Command or Data CRC Error
|
|
**/
|
|
EFI_STATUS
|
|
DoMmcTuneDlls (
|
|
IN UINTN EmmcHcPciBase,
|
|
OUT EMMC_TUNING_DATA *EmmcTuningData
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EMMC_DLL_TUNING_REG_LIST EmmcDllTuningRegList;
|
|
EMMC_TUNING_DATA *EmmcTuningDataCurrent;
|
|
EMMC_TUNING_DATA EmmcTuningDataInitial;
|
|
DEVICE_BLOCK_INFO DevBlockInfo;
|
|
|
|
Status = EFI_SUCCESS;
|
|
EmmcTuningDataCurrent = EmmcTuningData;
|
|
|
|
GetEmmcDllTuningLibData (&EmmcDllTuningRegList);
|
|
MmcGetDlls (EmmcHcPciBase, &EmmcDllTuningRegList, &EmmcTuningDataInitial);
|
|
|
|
MmcGetMediaInfo (0, &DevBlockInfo);
|
|
Status = MmcTuneDllsToHs400 (EmmcHcPciBase, DevBlockInfo.BlockSize, &EmmcDllTuningRegList, EmmcTuningData);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "MMC DLL tuning failed, status = %r, restoring initial dlls\n", Status));
|
|
EmmcTuningDataCurrent = &EmmcTuningDataInitial;
|
|
}
|
|
|
|
MmcSetDlls (EmmcHcPciBase, &EmmcDllTuningRegList, EmmcTuningDataCurrent);
|
|
DEBUG ((DEBUG_INFO, "MMC DLL data (initial) - TX 0x%02x | RX 0x%02x\n", EmmcTuningDataInitial.Hs400TxDataDll,
|
|
EmmcTuningDataInitial.Hs400RxStrobe1Dll));
|
|
DEBUG ((DEBUG_INFO, "MMC DLL data (current) - TX 0x%02x | RX 0x%02x\n", EmmcTuningDataCurrent->Hs400TxDataDll,
|
|
EmmcTuningDataCurrent->Hs400RxStrobe1Dll));
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/**
|
|
This function tuning the device.
|
|
|
|
@param[in] DevHcPciBase Device Host Controller's PCI ConfigSpace Base address
|
|
|
|
|
|
@retval EFI_SUCCESS The request is executed successfully.
|
|
@retval Others The request could not be executed successfully.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
MmcTuning (
|
|
IN UINTN MmcHcPciBase
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EMMC_TUNING_DATA EmmcTuningData;
|
|
UINTN VariableLen;
|
|
UINT32 PrintLevel;
|
|
UINT32 FeatureCfg;
|
|
EMMC_DLL_TUNING_REG_LIST EmmcDllTuningRegList;
|
|
|
|
FeatureCfg = GetFeatureCfg ();
|
|
|
|
|
|
if ((FeatureCfg & FEATURE_MMC_TUNING) == 0) {
|
|
DEBUG ((DEBUG_WARN, "eMMC Tuning feature is disabled!\n"));
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Get emmc tuning regesters.
|
|
//
|
|
Status = GetEmmcDllTuningLibData (&EmmcDllTuningRegList);
|
|
if (EFI_ERROR (Status)) {
|
|
EmmcDllTuningRegList.TxDataDllCntl1 = 0x824;
|
|
EmmcDllTuningRegList.RxStrobeDllCntl = 0x830;
|
|
SetEmmcDllTuningLibData (&EmmcDllTuningRegList);
|
|
}
|
|
|
|
//
|
|
// Check if eMMC tuning is required.
|
|
//
|
|
VariableLen = sizeof (EmmcTuningData);
|
|
ZeroMem (&EmmcTuningData, VariableLen);
|
|
Status = GetVariable (L"MMCDLL", NULL, NULL, &VariableLen, &EmmcTuningData);
|
|
if (!EFI_ERROR (Status)) {
|
|
if ((FeatureCfg & FEATURE_MMC_FORCE_TUNING) == 0) {
|
|
DEBUG ((DEBUG_INFO, "Found eMMC tunning data, re-tuning is not required.\n"));
|
|
MmcSetDlls (MmcHcPciBase, &EmmcDllTuningRegList, &EmmcTuningData);
|
|
return EFI_SUCCESS;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now need do eMMC tunning.
|
|
// Mask of all output since errors are expected during tuning
|
|
//
|
|
if (FeaturePcdGet (PcdEmmcHs400SupportEnabled)) {
|
|
// Only do eMMC tunning when HS400 is enabled.
|
|
DEBUG ((DEBUG_INFO, "Execute eMMC tunning\n"));
|
|
PrintLevel = GetDebugPrintErrorLevel ();
|
|
SetDebugPrintErrorLevel (0);
|
|
Status = DoMmcTuneDlls (MmcHcPciBase, &EmmcTuningData);
|
|
SetDebugPrintErrorLevel (PrintLevel);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "MMC MmcTuneDlls error, status = %r\n", Status));
|
|
return Status;
|
|
}
|
|
|
|
DEBUG ((DEBUG_INFO, "eMMC tunning done, RxDll=%02X TxDll=%02X\n",
|
|
EmmcTuningData.Hs400RxStrobe1Dll, EmmcTuningData.Hs400TxDataDll));
|
|
}
|
|
|
|
// Todo: Serial number should be removed from this function later.
|
|
Status = EmmcGetSerialNumber (EmmcTuningData.SerialNumber, sizeof(EmmcTuningData.SerialNumber));
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "ERRORL: MMC serial number invalid, status = %r\n", Status));
|
|
}
|
|
|
|
Status = SetVariable (L"MMCDLL", NULL, 0, sizeof(EMMC_TUNING_DATA), &EmmcTuningData);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "MMC DLL data save error, status = %r\n", Status));
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|