/** @file
|
Initializes PCH Storage and Communications Subsystem Controllers.
|
|
Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
**/
|
#include <PchInit.h>
|
|
//
|
// HS400 Tuning Definitions
|
//
|
#define RX_STROBE_DLL1_TAP_MAX_RANGE 39
|
#define RX_STROBE_DLL1_TAP_MIN_RANGE 0
|
#define RX_STROBE_DLL1_TAP_MIN_MEPT 5
|
#define RX_STROBE_DLL1_TAP_MAX_MEPT 16
|
#define TX_DATA_DLL_TAP_MAX_RANGE 79
|
#define TX_DATA_DLL_TAP_MIN_RANGE 0
|
#define TX_DATA_DLL_TAP_MIN_MEPT 4
|
#define TX_DATA_DLL_TAP_MAX_MEPT 22
|
|
//
|
// Command Definitions
|
//
|
#define CMD6 6
|
#define CMD8 8
|
#define CMD13 13
|
#define CMD31 31
|
#define SWITCH CMD6
|
#define SEND_EXT_CSD CMD8
|
#define SEND_STATUS CMD13
|
#define SEND_WRITE_PROT_TYPE CMD31
|
#define WRITE_BYTE_MODE 3
|
#define BLOCK_LENGTH 512
|
#define TIMEOUT_COMMAND 100
|
#define TIMEOUT_DATA 5000
|
#define HS_TIMING_INDEX 185
|
#define BUS_WIDTH_INDEX 183
|
|
//
|
// Card Status Definitions
|
//
|
#define SWITCH_ERROR BIT7
|
#define ERASE_RESET BIT13
|
#define WP_ERASE_SKIP BIT15
|
#define CID_CSD_OVERWRITE BIT16
|
#define ERROR BIT19
|
#define CC_ERROR BIT20
|
#define CARD_ECC_FAILED BIT21
|
#define ILLEGAL_COMMAND BIT22
|
#define COM_CRC_ERROR BIT23
|
#define LOCK_UNLOCK_FAILED BIT24
|
#define CARD_IS_LOCKED BIT25
|
#define WP_VIOLATION BIT26
|
#define ERASE_PARAM BIT27
|
#define ERASE_SEQ_ERROR BIT28
|
#define BLOCK_LEN_ERROR BIT29
|
#define ADDRESS_MISALIGN BIT30
|
#define ADDRESS_OUT_OF_RANGE BIT31
|
|
typedef enum {
|
Hs200 = 0,
|
Hs400,
|
DDR50,
|
SDR25
|
} EMMC_MODE;
|
|
typedef enum {
|
RxDll1 = 0,
|
RxDll2
|
} RX_STROBE_DLL_REG;
|
|
typedef enum {
|
NotAvailable = 0,
|
Passed,
|
Failed
|
} BLOCK_READ_WRITE_STATUS;
|
|
typedef enum {
|
ResponseNo = 0,
|
ResponseR1,
|
ResponseR1b,
|
ResponseR2,
|
ResponseR3,
|
ResponseR4,
|
ResponseR5,
|
ResponseR5b,
|
ResponseR6,
|
ResponseR7
|
} RESPONSE_TYPE;
|
|
typedef enum {
|
NoData = 0,
|
InData,
|
OutData
|
} TRANSFER_TYPE;
|
|
typedef struct {
|
UINT32 CmdSet: 3;
|
UINT32 Reserved0: 5;
|
UINT32 Value: 8;
|
UINT32 Index: 8;
|
UINT32 Access: 2;
|
UINT32 Reserved1: 6;
|
} SWITCH_ARGUMENT;
|
|
//
|
// PCH_EMMC_TUNING PROTOCOL Global Variable
|
//
|
GLOBAL_REMOVE_IF_UNREFERENCED PCH_EMMC_TUNING_PROTOCOL PchEmmcTuningProtocol = {
|
ConfigureEmmcHs400Mode
|
};
|
|
#define EMMC_HS400_TUNING_PATTERN_BLOCKS_NUMBER 5
|
|
GLOBAL_REMOVE_IF_UNREFERENCED
|
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
|
};
|
|
GLOBAL_REMOVE_IF_UNREFERENCED
|
CONST UINT32 EmmcWorstCasePatternSize = sizeof (EmmcWorstCasePattern);
|
|
|
|
/**
|
Set max clock frequency of the host, the actual frequency may not be the same
|
as MaxFrequency. It depends on the max frequency the host can support, divider,
|
and host speed mode.
|
|
@param[in] This Pointer to EFI_SD_HOST_IO_PROTOCOL
|
@param[in] MaxFrequency Max frequency in HZ
|
|
@retval EFI_SUCCESS The function completed successfully
|
@retval EFI_TIMEOUT The timeout time expired.
|
**/
|
EFI_STATUS
|
EFIAPI
|
SetClockFrequency (
|
IN UINTN EmmcBaseAddress,
|
IN UINT32 MaxFrequency
|
)
|
{
|
UINT16 Data16;
|
UINT32 Frequency;
|
UINT32 Divider = 0;
|
UINT32 TimeOutCount;
|
UINT32 CurrentClockInKHz;
|
|
DEBUG ((DEBUG_INFO, "SetClockFrequency: BaseClockInMHz = %d \n", 200));
|
|
Frequency = (200 * 1000 * 1000) / MaxFrequency;
|
DEBUG ((DEBUG_INFO, "SetClockFrequency: FrequencyInHz = %d \n", Frequency));
|
|
if ((200 * 1000 * 1000 % MaxFrequency) != 0) {
|
Frequency += 1;
|
}
|
|
Divider = 1;
|
while (Frequency > Divider) {
|
Divider = Divider * 2;
|
}
|
if (Divider >= 0x400) {
|
Divider = 0x200;
|
}
|
Divider = Divider >> 1;
|
|
DEBUG ((DEBUG_INFO, "SetClockFrequency: after shift: Base Clock Divider = 0x%x \n", Divider));
|
|
CurrentClockInKHz = (200 * 1000);
|
if (Divider != 0) {
|
CurrentClockInKHz = CurrentClockInKHz / (Divider * 2);
|
}
|
//
|
//Set frequency
|
// Bit[15:8] SDCLK Frequency Select at offset 2Ch
|
// 80h - base clock divided by 256
|
// 40h - base clock divided by 128
|
// 20h - base clock divided by 64
|
// 10h - base clock divided by 32
|
// 08h - base clock divided by 16
|
// 04h - base clock divided by 8
|
// 02h - base clock divided by 4
|
// 01h - base clock divided by 2
|
// 00h - Highest Frequency the target support (10MHz-63MHz)
|
//
|
// Bit [07:06] are assigned to bit 09-08 of clock divider in SDCLK Frequency Select on SD controller 3.0
|
//
|
|
Data16 = (UINT16) ((Divider & 0xFF) << 8 | (((Divider & 0xFF00) >> 8) << 6));
|
|
DEBUG ((DEBUG_INFO,
|
"SetClockFrequency: base=%dMHz, clkctl=0x%04x, f=%dKHz\n",
|
200,
|
Data16,
|
CurrentClockInKHz
|
));
|
DEBUG ((DEBUG_INFO, "SetClockFrequency: set MMIO_CLKCTL value = 0x%x \n", Data16));
|
|
MmioWrite16 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_CLKCTL, Data16);
|
Data16 |= BIT0;
|
|
MmioWrite16 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_CLKCTL, Data16);
|
TimeOutCount = 1000;
|
do {
|
Data16 = MmioRead16 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_CLKCTL);
|
MicroSecondDelay (100);
|
TimeOutCount--;
|
if (TimeOutCount == 0) {
|
DEBUG ((DEBUG_INFO, "SetClockFrequency: Timeout\n"));
|
return EFI_TIMEOUT;
|
}
|
} while ((Data16 & BIT1) != BIT1);
|
|
Data16 |= BIT2;
|
MmioWrite16 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_CLKCTL, Data16);
|
return EFI_SUCCESS;
|
}
|
|
/**
|
Get Error Reason from Host
|
|
@param[in] CommandIndex Command Index which return error
|
@param[in] ErrorCode Command Error Code
|
|
@retval EFI_SUCCESS The function completed successfully
|
@retval EFI_TIMEOUT Command Timeout Error
|
@retval EFI_TIMEOUT Data Timeout Error
|
@retval EFI_CRC_ERROR Command or Data CRC Error
|
@retval EFI_DEVICE_ERROR Command End Bit Error
|
Command Index Error
|
Data End Bit Error
|
Current Limit Error
|
Auto CMD12 Error
|
ADMA Error
|
**/
|
STATIC
|
EFI_STATUS
|
EmmcGetErrorReason (
|
IN UINT16 CommandIndex,
|
IN UINT16 ErrorCode
|
)
|
{
|
EFI_STATUS Status;
|
|
Status = EFI_DEVICE_ERROR;
|
|
DEBUG ((DEBUG_ERROR, "[%2d] -- ", CommandIndex));
|
|
if (ErrorCode & BIT0) {
|
Status = EFI_TIMEOUT;
|
DEBUG ((DEBUG_ERROR, "Command Timeout Error\n"));
|
}
|
|
if (ErrorCode & BIT1) {
|
Status = EFI_CRC_ERROR;
|
DEBUG ((DEBUG_ERROR, "Command CRC Error\n"));
|
}
|
|
if (ErrorCode & BIT2) {
|
DEBUG ((DEBUG_ERROR, "Command End Bit Error\n"));
|
}
|
|
if (ErrorCode & BIT3) {
|
DEBUG ((DEBUG_ERROR, "Command Index Error\n"));
|
}
|
|
return Status;
|
}
|
|
/**
|
Reset the host CMD and DATA Line
|
|
@param[in] EmmcBaseAddress Base address of MMIO register
|
|
@retval EFI_SUCCESS The function completed successfully
|
@retval EFI_TIMEOUT The timeout time expired.
|
**/
|
EFI_STATUS
|
EmmcReset (
|
IN UINTN EmmcBaseAddress
|
)
|
{
|
UINT8 Data8;
|
UINT8 ResetType;
|
UINT16 SaveClkCtl;
|
UINT32 Data;
|
UINT32 TimeOutCount;
|
|
ResetType = (B_PCH_SCS_DEV_MEM_SWRST_CMDLINE | B_PCH_SCS_DEV_MEM_SWRST_DATALINE);
|
|
//
|
// To improve eMMC stability, we zero the R_PCH_SCS_DEV_MEM_CLKCTL register and
|
// stall for 50 microsecond before reseting the controller. We
|
// restore the register setting following the reset operation.
|
//
|
SaveClkCtl = MmioRead16 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_CLKCTL);
|
MmioWrite16 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_CLKCTL, 0);
|
MicroSecondDelay (50);
|
//
|
// Reset the SD host controller
|
//
|
MmioWrite8 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_SWRST, ResetType);
|
|
Data = 0;
|
TimeOutCount = 1000; // 1 second timeout
|
do {
|
MicroSecondDelay (1 * 1000);
|
|
TimeOutCount --;
|
|
Data8 = MmioRead8 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_SWRST);
|
if ((Data8 & ResetType) == 0) {
|
break;
|
}
|
} while (TimeOutCount > 0);
|
|
//
|
// We now restore the R_PCH_SCS_DEV_MEM_CLKCTL register which we set to 0 above.
|
//
|
MmioWrite16 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_CLKCTL, SaveClkCtl);
|
|
if (TimeOutCount == 0) {
|
DEBUG ((DEBUG_ERROR, "EmmcReset: Time out \n"));
|
return EFI_TIMEOUT;
|
}
|
|
return EFI_SUCCESS;
|
}
|
|
/**
|
Check card status, print the DEBUG info and check the error
|
|
@param[in] CardStatus Status got from card status register
|
|
@retval EFI_SUCCESS The data was read from or written to the PCI controller.
|
@retval EFI_DEVICE_ERROR Device failed during operation
|
**/
|
EFI_STATUS
|
EmmcCheckCardStatus (
|
IN UINT32 CardStatus
|
)
|
{
|
|
EFI_STATUS Status;
|
Status = EFI_SUCCESS;
|
DEBUG ((DEBUG_ERROR, "CardStatus:"));
|
|
if (CardStatus & ADDRESS_OUT_OF_RANGE) {
|
DEBUG ((DEBUG_ERROR, " ADDRESS_OUT_OF_RANGE"));
|
Status = EFI_DEVICE_ERROR;
|
}
|
|
if (CardStatus & ADDRESS_MISALIGN) {
|
DEBUG ((DEBUG_ERROR, " ADDRESS_MISALIGN"));
|
Status = EFI_DEVICE_ERROR;
|
}
|
|
if (CardStatus & BLOCK_LEN_ERROR) {
|
DEBUG ((DEBUG_ERROR, " BLOCK_LEN_ERROR"));
|
Status = EFI_DEVICE_ERROR;
|
}
|
|
if (CardStatus & ERASE_SEQ_ERROR) {
|
DEBUG ((DEBUG_ERROR, " ERASE_SEQ_ERROR"));
|
Status = EFI_DEVICE_ERROR;
|
}
|
|
if (CardStatus & ERASE_PARAM) {
|
DEBUG ((DEBUG_ERROR, " ERASE_PARAM"));
|
Status = EFI_DEVICE_ERROR;
|
}
|
|
if (CardStatus & WP_VIOLATION) {
|
DEBUG ((DEBUG_ERROR, " WP_VIOLATION"));
|
Status = EFI_DEVICE_ERROR;
|
}
|
|
if (CardStatus & CARD_IS_LOCKED) {
|
DEBUG ((DEBUG_ERROR, " CARD_IS_LOCKED"));
|
Status = EFI_DEVICE_ERROR;
|
}
|
|
if (CardStatus & LOCK_UNLOCK_FAILED) {
|
DEBUG ((DEBUG_ERROR, " LOCK_UNLOCK_FAILED"));
|
Status = EFI_DEVICE_ERROR;
|
}
|
|
if (CardStatus & COM_CRC_ERROR) {
|
DEBUG ((DEBUG_ERROR, " COM_CRC_ERROR"));
|
Status = EFI_DEVICE_ERROR;
|
}
|
|
if (CardStatus & ILLEGAL_COMMAND) {
|
DEBUG ((DEBUG_ERROR, " ILLEGAL_COMMAND"));
|
Status = EFI_DEVICE_ERROR;
|
}
|
|
if (CardStatus & CARD_ECC_FAILED) {
|
DEBUG ((DEBUG_ERROR, " CARD_ECC_FAILED"));
|
Status = EFI_DEVICE_ERROR;
|
}
|
|
if (CardStatus & CC_ERROR) {
|
DEBUG ((DEBUG_ERROR, " CC_ERROR"));
|
Status = EFI_DEVICE_ERROR;
|
}
|
|
if (CardStatus & ERROR) {
|
DEBUG ((DEBUG_ERROR, " ERROR"));
|
Status = EFI_DEVICE_ERROR;
|
}
|
|
if (CardStatus & CID_CSD_OVERWRITE) {
|
DEBUG ((DEBUG_ERROR, " CID_CSD_OVERWRITE"));
|
Status = EFI_DEVICE_ERROR;
|
}
|
|
if (CardStatus & WP_ERASE_SKIP) {
|
DEBUG ((DEBUG_ERROR, " WP_ERASE_SKIP"));
|
Status = EFI_DEVICE_ERROR;
|
}
|
|
if (CardStatus & ERASE_RESET) {
|
DEBUG ((DEBUG_ERROR, " ERASE_RESET"));
|
Status = EFI_DEVICE_ERROR;
|
}
|
|
if (CardStatus & SWITCH_ERROR) {
|
DEBUG ((DEBUG_ERROR, " SWITCH_ERROR"));
|
Status = EFI_DEVICE_ERROR;
|
}
|
|
DEBUG ((DEBUG_ERROR, " \n"));
|
|
return Status;
|
}
|
/**
|
The main function used to send the command to the card inserted into the SD/MMC host
|
slot. It will assemble the arguments to set the command register and wait for the command
|
and transfer completed until timeout. Then it will read the response register to fill
|
the ResponseData.
|
|
@param[in] EmmcBaseAddress Base address of MMIO register
|
@param[in] CommandIndex The command index to set the command index field of command register
|
@param[in] Argument Command argument to set the argument field of command register
|
@param[in] DataType TRANSFER_TYPE, indicates no data, data in or data out
|
@param[in] Buffer Contains the data read from / write to the device
|
@param[in] BufferSize The size of the buffer
|
@param[in] ResponseType RESPONSE_TYPE
|
@param[in] TimeOut Time out value in 1 ms unit
|
@param[out] ResponseData Depending on the ResponseType, such as CSD or card status
|
|
@retval EFI_SUCCESS The function completed successfully
|
@retval EFI_INVALID_PARAMETER A parameter was incorrect.
|
@retval EFI_OUT_OF_RESOURCES A resource has run out.
|
@retval EFI_TIMEOUT The timeout time expired.
|
@retval EFI_DEVICE_ERROR The physical device reported an error while attempting the operation
|
**/
|
EFI_STATUS
|
SendCommand (
|
IN UINTN EmmcBaseAddress,
|
IN UINT16 CommandIndex,
|
IN UINT32 Argument,
|
IN TRANSFER_TYPE DataType,
|
IN UINT8 *Buffer, OPTIONAL
|
IN UINT32 BufferSize,
|
IN RESPONSE_TYPE ResponseType,
|
IN UINT32 TimeOut,
|
OUT UINT32 *ResponseData
|
)
|
{
|
EFI_STATUS Status;
|
UINT32 ResponseDataCount;
|
UINT16 Data16;
|
UINT32 Data32;
|
UINT64 Data64;
|
UINT32 BlockLength;
|
UINT32 Index;
|
BOOLEAN CommandCompleted;
|
BOOLEAN BufferReadReady;
|
INT32 Timeout;
|
|
Status = EFI_SUCCESS;
|
BlockLength = BLOCK_LENGTH;
|
|
if (Buffer != NULL && DataType == NoData) {
|
Status = EFI_INVALID_PARAMETER;
|
return Status;
|
}
|
|
//
|
// Check CMD INHIBIT and DATA INHIBIT before send command
|
//
|
Timeout = 1000;
|
do {
|
Data32 = MmioRead32 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_PSTATE);
|
MicroSecondDelay (100);
|
} while ((Timeout-- > 0) && (Data32 & BIT0));
|
|
Timeout = 1000;
|
do {
|
Data32 = MmioRead32 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_PSTATE);
|
MicroSecondDelay (100);
|
} while ((Timeout-- > 0) && (Data32 & BIT1));
|
|
|
//
|
// Enable Interrupts
|
//
|
MmioWrite16 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_NINTEN, B_PCH_SCS_DEV_MEM_NINTEN_MASK);
|
|
|
MmioWrite16 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_ERINTEN, B_PCH_SCS_DEV_MEM_ERINTEN_MASK);
|
|
|
MmioWrite16 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_NINTSIGNEN, B_PCH_SCS_DEV_MEM_NINTSIGNEN_MASK);
|
|
|
MmioWrite16 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_ERINTSIGNEN, B_PCH_SCS_DEV_MEM_ERINTSIGNEN_MASK);
|
|
//
|
// Clear status bits
|
//
|
MmioWrite16 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_NINTSTS, B_PCH_SCS_DEV_MEM_NINTSTS_CLEAR_MASK);
|
|
MmioWrite16 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_ERINTSTS, B_PCH_SCS_DEV_MEM_ERINTSTS_CLEAR_MASK);
|
|
if (Buffer != NULL) {
|
Data16 = 0;
|
if (BufferSize <= BlockLength) {
|
Data16 |= BufferSize;
|
} else {
|
Data16 |= BlockLength;
|
}
|
} else {
|
Data16 = 0;
|
}
|
|
MmioWrite16 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_BLKSZ, Data16);
|
|
if (Buffer != NULL) {
|
if (BufferSize <= BlockLength) {
|
Data16 = 1;
|
} else {
|
Data16 = (UINT16) (BufferSize / BlockLength);
|
}
|
} else {
|
Data16 = 0;
|
}
|
|
MmioWrite16 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_BLKCNT, Data16);
|
|
//
|
// Argument
|
//
|
MmioWrite32 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_CMDARG, Argument);
|
|
//
|
// Transfer Mode
|
//
|
Data16 = MmioRead16 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_XFRMODE);
|
|
//
|
// Data Transfer Direction Select
|
//
|
Data16 = 0;
|
if (DataType == InData) {
|
Data16 |= B_PCH_SCS_DEV_MEM_XFRMODE_DATA_TRANS_DIR;
|
}
|
|
if (CommandIndex == SEND_EXT_CSD) {
|
Data16 |= B_PCH_SCS_DEV_MEM_XFRMODE_BLKCNT_EN;
|
}
|
|
MmioWrite16 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_XFRMODE, Data16);
|
//
|
//Command
|
//
|
//ResponseTypeSelect IndexCheck CRCCheck ResponseType
|
// 00 0 0 NoResponse
|
// 01 0 1 R2
|
// 10 0 0 R3, R4
|
// 10 1 1 R1, R5, R6, R7
|
// 11 1 1 R1b, R5b
|
//
|
switch (ResponseType) {
|
case ResponseNo:
|
Data16 = (CommandIndex << 8);
|
ResponseDataCount = 0;
|
break;
|
|
case ResponseR1:
|
case ResponseR5:
|
case ResponseR6:
|
case ResponseR7:
|
Data16 = (CommandIndex << 8) | V_PCH_SCS_DEV_MEM_SDCMD_RESP_TYPE_SEL_RESP48 |
|
B_PCH_SCS_DEV_MEM_SDCMD_CMD_INDEX_CHECK_EN | B_PCH_SCS_DEV_MEM_SDCMD_CMD_CRC_CHECK_EN;
|
ResponseDataCount = 1;
|
break;
|
|
case ResponseR1b:
|
case ResponseR5b:
|
Data16 = (CommandIndex << 8) | V_PCH_SCS_DEV_MEM_SDCMD_RESP_TYPE_SEL_RESP48_CHK |
|
B_PCH_SCS_DEV_MEM_SDCMD_CMD_INDEX_CHECK_EN | B_PCH_SCS_DEV_MEM_SDCMD_CMD_CRC_CHECK_EN;
|
ResponseDataCount = 1;
|
break;
|
|
case ResponseR2:
|
Data16 = (CommandIndex << 8) | V_PCH_SCS_DEV_MEM_SDCMD_RESP_TYPE_SEL_RESP136 |
|
B_PCH_SCS_DEV_MEM_SDCMD_CMD_CRC_CHECK_EN;
|
ResponseDataCount = 4;
|
break;
|
|
case ResponseR3:
|
case ResponseR4:
|
Data16 = (CommandIndex << 8) | V_PCH_SCS_DEV_MEM_SDCMD_RESP_TYPE_SEL_RESP48;
|
ResponseDataCount = 1;
|
break;
|
|
default:
|
ASSERT (0);
|
Status = EFI_INVALID_PARAMETER;
|
return Status;
|
}
|
|
if (DataType != NoData) {
|
Data16 |= B_PCH_SCS_DEV_MEM_SDCMD_DATA_PRESENT_SEL;
|
}
|
|
MmioWrite16 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_SDCMD, Data16);
|
CommandCompleted = FALSE;
|
BufferReadReady = FALSE;
|
TimeOut = 1000;
|
do {
|
Data16 = MmioRead16 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_ERINTSTS);
|
if ((Data16 & B_PCH_SCS_DEV_MEM_ERINTSTS_MASK) != 0) {
|
Status = EmmcGetErrorReason (CommandIndex, Data16);
|
return Status;
|
}
|
|
Data16 = MmioRead16 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_NINTSTS) & 0x1ff;
|
if (Data16 & B_PCH_SCS_DEV_MEM_NINTSTS_CMD_COMPLETE) {
|
//
|
// Command completed
|
//
|
CommandCompleted = TRUE;
|
|
MmioWrite16 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_NINTSTS, B_PCH_SCS_DEV_MEM_NINTSTS_CMD_COMPLETE);
|
Data16 = MmioRead16 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_NINTSTS);
|
if ((DataType == NoData) & (ResponseType != ResponseR1b)) {
|
break;
|
}
|
}
|
|
if ((CommandCompleted) && (ResponseType == ResponseR1b)) {
|
Data32 = MmioRead32 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_PSTATE);
|
if (Data32 & B_PCH_SCS_DEV_MEM_PSTATE_DAT0) {
|
break;
|
}
|
}
|
|
if ((CommandCompleted) && (Buffer!= NULL)) {
|
if (!(Data16 & B_PCH_SCS_DEV_MEM_NINTSTS_CMD_COMPLETE)) {
|
|
if (Data16 & B_PCH_SCS_DEV_MEM_NINTSTS_BUF_READ_READY_INTR) {
|
BufferReadReady = TRUE;
|
MmioOr16 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_NINTSTS, B_PCH_SCS_DEV_MEM_NINTSTS_BUF_READ_READY_INTR);
|
Data16 = MmioRead16 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_NINTSTS);
|
}
|
|
if (BufferReadReady) {
|
if (!(Data16 & B_PCH_SCS_DEV_MEM_NINTSTS_BUF_READ_READY_INTR)) {
|
for (Index = 0; Index < BufferSize; Index = Index + 4) {
|
Data32 = MmioRead32 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_BUFDATAPORT);
|
if ((Index + 4) < BufferSize) {
|
CopyMem ((Buffer + Index), &Data32, 4);
|
} else {
|
CopyMem ((Buffer + Index), &Data32, (BufferSize - Index));
|
}
|
}
|
}
|
Data16 = MmioRead16 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_NINTSTS);
|
}
|
|
if (Data16 & B_PCH_SCS_DEV_MEM_NINTSTS_TRANSFER_COMPLETE) {
|
MmioWrite16 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_NINTSTS, B_PCH_SCS_DEV_MEM_NINTSTS_TRANSFER_COMPLETE);
|
break;
|
}
|
}
|
}
|
MicroSecondDelay (1*1000);
|
|
TimeOut --;
|
|
} while (TimeOut > 0);
|
|
if (TimeOut == 0) {
|
Status = EFI_TIMEOUT;
|
return Status;
|
}
|
|
if (ResponseData != NULL) {
|
UINT32 *ResDataPtr = NULL;
|
|
ResDataPtr = ResponseData;
|
for (Index = 0; Index < ResponseDataCount; Index++) {
|
*ResDataPtr = MmioRead32 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_RESP + Index * 4);
|
ResDataPtr++;
|
}
|
|
if (ResponseType == ResponseR2) {
|
//
|
// Adjustment for R2 response
|
//
|
Data32 = 1;
|
for (Index = 0; Index < ResponseDataCount; Index++) {
|
Data64 = LShiftU64 (*ResponseData, 8);
|
*ResponseData = (UINT32) ((Data64 & 0xFFFFFFFF) | Data32);
|
Data32 = (UINT32) RShiftU64 (Data64, 32);
|
ResponseData++;
|
}
|
}
|
}
|
return Status;
|
}
|
|
/**
|
This main function is to send command to Emmc
|
|
@param[in] EmmcBaseAddress Base address of MMIO register
|
@param[in] CommandIndex The command index to set the command index field of command register
|
@param[in] Argument Command argument to set the argument field of command register
|
@param[in] DataType TRANSFER_TYPE, indicates no data, data in or data out
|
@param[in] Buffer Contains the data read from / write to the device
|
@param[in] BufferSize The size of the buffer
|
@param[in] ResponseType RESPONSE_TYPE
|
@param[in] TimeOut Time out value in 1 ms unit
|
@param[out] ResponseData Depending on the ResponseType, such as CSD or card status
|
|
@retval EFI_SUCCESS The function completed successfully
|
@retval EFI_INVALID_PARAMETER A parameter was incorrect.
|
@retval EFI_OUT_OF_RESOURCES A resource has run out.
|
@retval EFI_TIMEOUT The timeout time expired.
|
@retval EFI_DEVICE_ERROR The physical device reported an error while attempting the operation
|
**/
|
EFI_STATUS
|
EmmcSendCommand (
|
IN UINTN EmmcBaseAddress,
|
IN UINT16 CommandIndex,
|
IN UINT32 Argument,
|
IN TRANSFER_TYPE DataType,
|
IN UINT8 *Buffer, OPTIONAL
|
IN UINT32 BufferSize,
|
IN RESPONSE_TYPE ResponseType,
|
IN UINT32 TimeOut,
|
OUT UINT32 *ResponseData
|
)
|
{
|
EFI_STATUS Status;
|
|
Status = SendCommand (
|
EmmcBaseAddress,
|
CommandIndex,
|
Argument,
|
DataType,
|
Buffer,
|
BufferSize,
|
ResponseType,
|
TimeOut,
|
ResponseData
|
);
|
|
if (!EFI_ERROR (Status)) {
|
if (ResponseType == ResponseR1 || ResponseType == ResponseR1b) {
|
ASSERT (ResponseData != NULL);
|
Status = EmmcCheckCardStatus (*ResponseData);
|
if (EFI_ERROR (Status)) {
|
EmmcReset (EmmcBaseAddress);
|
}
|
}
|
} else {
|
//
|
// Reset Host Controller CMD and DATA
|
//
|
EmmcReset (EmmcBaseAddress);
|
}
|
|
return Status;
|
}
|
/**
|
Set Tx Data Delay Control 1
|
|
@param[in] EmmcBaseAddress Base address of MMIO register
|
@param[in] Value Value (0 - 79)
|
|
@retval VOID
|
**/
|
VOID
|
EmmcSetTxDllCtrl1 (
|
IN UINTN EmmcBaseAddress,
|
IN UINT8 Value
|
)
|
{
|
MmioAndThenOr8 (EmmcBaseAddress + (R_PCH_SCS_DEV_MEM_TX_DATA_DLL_CNTL1 + 1), 0, Value);
|
}
|
|
/**
|
Set Rx Strobe Delay Control DLL1 for HS400
|
|
@param[in] EmmcBaseAddress Base address of MMIO register
|
@param[in] RxDll To program RxDll1 or RxDll2 register
|
@param[in] Value Value (0 - 39)
|
|
@retval VOID
|
**/
|
VOID
|
EmmcSetRxDllCtrl (
|
IN UINTN EmmcBaseAddress,
|
IN UINT8 RxDll,
|
IN UINT8 Value
|
)
|
{
|
if (RxDll == RxDll1) {
|
MmioAndThenOr8 (EmmcBaseAddress + (R_PCH_SCS_DEV_MEM_RX_STROBE_DLL_CNTL + 1), 0, Value);
|
} else {
|
MmioAndThenOr8 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_RX_STROBE_DLL_CNTL, 0, Value);
|
}
|
}
|
|
/**
|
Disable eMMC Host HS400 Support
|
|
@param[in] EmmcBaseAddress Base address of MMIO register
|
|
@retval N/A
|
**/
|
VOID
|
EmmcHostHs400Disabled (
|
IN UINTN EmmcBaseAddress
|
)
|
{
|
DEBUG ((DEBUG_INFO, "ConfigureEmmcHs400Mode()::EmmcHostHs400Disabled()\n"));
|
MmioAnd32 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_CAP_BYPASS_REG1, (UINT32) ~B_PCH_SCS_DEV_MEM_CAP_BYPASS_REG1_HS400);
|
}
|
/**
|
Set Host Mode
|
|
@param[in] EmmcBaseAddress Base address of MMIO register
|
@param[in] Mode Set Host Mode, 0: HS200, 1: HS400
|
|
@retval N/A
|
**/
|
VOID
|
EmmcSetHostMode (
|
IN UINTN EmmcBaseAddress,
|
IN UINT32 Mode
|
)
|
{
|
UINT16 ModeSet;
|
UINT32 HostCapabilities;
|
|
ModeSet = MmioRead16 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_HOST_CTL2);
|
HostCapabilities = MmioRead32 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_CAP2);
|
|
ModeSet &= ~B_PCH_SCS_DEV_MEM_HOST_CTL2_MODE_MASK;
|
if ((Mode == Hs200) && (HostCapabilities & B_PCH_SCS_DEV_MEM_CAP2_SDR104_SUPPORT)) {
|
ModeSet |= V_PCH_SCS_DEV_MEM_HOST_CTL2_MODE_SDR104;
|
} else if ((Mode == Hs400) && (HostCapabilities & B_PCH_SCS_DEV_MEM_CAP2_HS400_SUPPORT)) {
|
ModeSet |= V_PCH_SCS_DEV_MEM_HOST_CTL2_MODE_HS400;
|
} else if (Mode == DDR50) {
|
ModeSet |= V_PCH_SCS_DEV_MEM_HOST_CTL2_MODE_DDR50;
|
} else if (Mode == SDR25) {
|
ModeSet |= V_PCH_SCS_DEV_MEM_HOST_CTL2_MODE_SDR25;
|
}
|
|
DEBUG ((DEBUG_INFO, "ConfigureEmmcHs400Mode(): SetHostDdrMode = 0x%x\n", ModeSet));
|
|
MmioWrite16 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_HOST_CTL2, ModeSet);
|
}
|
|
/**
|
To select eMMC card operating mode HS200/HS400
|
|
@param[in] EmmcInfo A pointer to EMMC_INFO structure
|
@param[in] EmmcBaseAddress Base address of MMIO register
|
@param[in] EmmcMode To select HS200 or HS400 mode
|
|
@retval EFI_SUCCESS Emmc Mode Select successful.
|
@retval EFI_INVALID_PARAMETER A parameter was incorrect.
|
**/
|
EFI_STATUS
|
EmmcModeSelection (
|
IN EMMC_INFO *EmmcInfo,
|
IN UINTN EmmcBaseAddress,
|
IN EMMC_MODE EmmcMode
|
)
|
{
|
SWITCH_ARGUMENT SwitchArgument;
|
UINT32 CardStatus;
|
UINT32 HsTimingValue;
|
UINT32 BusWidthValue;
|
EFI_STATUS Status;
|
|
Status = EFI_SUCCESS;
|
|
DEBUG ((DEBUG_INFO, "ConfigureEmmcHs400Mode()::EmmcModeSelection() Start\n"));
|
|
if (EmmcMode == Hs200) {
|
HsTimingValue = 0x02;
|
BusWidthValue = EmmcInfo->HS200BusWidth;
|
DEBUG ((DEBUG_INFO, "HS200 Driver Strength: 50 Ohm\n"));
|
} else {
|
DEBUG ((DEBUG_INFO, "HS400 Driver Strength: "));
|
switch (mPchConfigHob->Scs.ScsEmmcHs400DriverStrength) {
|
case DriverStrength33Ohm:
|
HsTimingValue = 0x13;
|
DEBUG ((DEBUG_INFO, "33 Ohm\n"));
|
break;
|
case DriverStrength40Ohm:
|
HsTimingValue = 0x43;
|
DEBUG ((DEBUG_INFO, "40 Ohm\n"));
|
break;
|
case DriverStrength50Ohm:
|
HsTimingValue = 0x03;
|
DEBUG ((DEBUG_INFO, "50 Ohm\n"));
|
break;
|
default:
|
HsTimingValue = 0x13;
|
DEBUG ((DEBUG_INFO, "default - 33 Ohm\n"));
|
}
|
|
BusWidthValue = 6;
|
}
|
SetClockFrequency (EmmcBaseAddress, (50 * 1000 * 1000)); // Set 50MHz
|
//
|
// 1. Set HS_TIMING to 0x01 for High Speed interface timing. This is required prior DDR 8 bit bus width setting (CMD6)
|
//
|
ZeroMem (&SwitchArgument, sizeof (SWITCH_ARGUMENT));
|
SwitchArgument.CmdSet = 0;
|
SwitchArgument.Value = 1;
|
SwitchArgument.Index = HS_TIMING_INDEX;
|
SwitchArgument.Access = WRITE_BYTE_MODE;
|
Status = EmmcSendCommand (
|
EmmcBaseAddress,
|
SWITCH,
|
*(UINT32*)&SwitchArgument,
|
NoData,
|
NULL,
|
0,
|
ResponseR1b,
|
TIMEOUT_COMMAND,
|
&CardStatus
|
);
|
if (EFI_ERROR (Status)) {
|
return Status;
|
}
|
|
EmmcSetHostMode (EmmcBaseAddress, SDR25);
|
|
//
|
// 2. Set desired bus width to DDR 8 bit CMD6
|
//
|
ZeroMem (&SwitchArgument, sizeof (SWITCH_ARGUMENT));
|
SwitchArgument.CmdSet = 0;
|
SwitchArgument.Value = BusWidthValue;
|
SwitchArgument.Index = BUS_WIDTH_INDEX;
|
SwitchArgument.Access = WRITE_BYTE_MODE;
|
Status = EmmcSendCommand (
|
EmmcBaseAddress,
|
SWITCH,
|
*(UINT32*)&SwitchArgument,
|
NoData,
|
NULL,
|
0,
|
ResponseR1b,
|
TIMEOUT_COMMAND,
|
&CardStatus
|
);
|
if (EFI_ERROR (Status)) {
|
return Status;
|
}
|
|
EmmcSetHostMode (EmmcBaseAddress, DDR50);
|
|
//
|
// 3. Set HS400 bit and Driver Strentgh value in HS_TIMING (CMD6)
|
//
|
ZeroMem (&SwitchArgument, sizeof (SWITCH_ARGUMENT));
|
SwitchArgument.CmdSet = 0;
|
SwitchArgument.Value = HsTimingValue;
|
SwitchArgument.Index = HS_TIMING_INDEX;
|
SwitchArgument.Access = WRITE_BYTE_MODE;
|
Status = EmmcSendCommand (
|
EmmcBaseAddress,
|
SWITCH,
|
*(UINT32*)&SwitchArgument,
|
NoData,
|
NULL,
|
0,
|
ResponseR1b,
|
TIMEOUT_COMMAND,
|
&CardStatus
|
);
|
if (EFI_ERROR (Status)) {
|
return Status;
|
}
|
|
if (EmmcMode == Hs200) {
|
DEBUG ((DEBUG_INFO, "Set eMMC Mode: HS200\n"));
|
EmmcSetHostMode (EmmcBaseAddress, Hs200);
|
} else {
|
DEBUG ((DEBUG_INFO, "Set eMMC Mode: HS400\n"));
|
EmmcSetHostMode (EmmcBaseAddress, Hs400);
|
}
|
SetClockFrequency (EmmcBaseAddress, (200 * 1000 * 1000)); // Set 200MHz
|
|
DEBUG ((DEBUG_INFO, "ConfigureEmmcHs400Mode()::EmmcModeSelection() End\n"));
|
return EFI_SUCCESS;
|
}
|
|
/**
|
Prints worst case pattern used for HS400 taining
|
|
@param[in] BlockIo A pointer to EFI_BLOCK_IO_PROTOCOL structure
|
@param[in] TuningPattern Data buffer containg pattern for training
|
@param[in] TuningPatternSize Size of the buffer with pattern
|
|
@retval N/A
|
**/
|
VOID
|
EmmcPrintHs400TuningPattern (
|
IN EFI_BLOCK_IO_PROTOCOL *BlockIo,
|
IN UINT8 *TuningPattern,
|
IN UINT32 TuningPatternSize
|
)
|
{
|
DEBUG_CODE_BEGIN ();
|
UINT32 Index;
|
|
DEBUG ((DEBUG_VERBOSE, "--------------- TUNING PATTERN ----------------\n"));
|
DEBUG ((DEBUG_VERBOSE, "BlockIo->Media->MediaId = 0x%08x\n", BlockIo->Media->MediaId));
|
DEBUG ((DEBUG_VERBOSE, "BlockIo->Media->BlockSize = 0x%08x\n", BlockIo->Media->BlockSize));
|
DEBUG ((DEBUG_VERBOSE, "Tuning Pattern Size = 0x%08x\n", TuningPatternSize));
|
for (Index = 0; Index < TuningPatternSize; Index++) {
|
if (Index % 16 == 0) {
|
DEBUG ((DEBUG_VERBOSE, "\n"));
|
}
|
if (Index % (BlockIo->Media->BlockSize) == 0) {
|
DEBUG ((DEBUG_VERBOSE, "-------------------- BLOCK --------------------\n"));
|
}
|
DEBUG ((DEBUG_VERBOSE, "%02x ", TuningPattern[Index]));
|
}
|
DEBUG ((DEBUG_VERBOSE, "\n-----------------------------------------------\n"));
|
DEBUG_CODE_END ();
|
}
|
|
/**
|
Writes worst case pattern used for HS400 taining
|
|
@param[in] BlockIo A pointer to EFI_BLOCK_IO_PROTOCOL structure
|
@param[in] TuningPattern Data buffer containg pattern for training
|
@param[in] TuningPatternSize Size of the buffer with pattern
|
|
@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 EMMC_INFO *EmmcInfo,
|
IN EFI_BLOCK_IO_PROTOCOL *BlockIo,
|
IN UINTN EmmcBaseAddress,
|
IN UINT8 *TuningPattern
|
)
|
{
|
UINT8 *Buffer;
|
UINT32 Index;
|
UINT32 TuningPatternSize;
|
EFI_STATUS Status;
|
|
Status = EFI_SUCCESS;
|
TuningPatternSize = BlockIo->Media->BlockSize * EMMC_HS400_TUNING_PATTERN_BLOCKS_NUMBER;
|
|
DEBUG ((DEBUG_INFO, "EmmcWriteHs400TuningPattern() Start\n"));
|
|
Buffer = (VOID *) AllocateZeroPool (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 * BlockIo->Media->BlockSize), TuningPattern, BlockIo->Media->BlockSize);
|
}
|
|
Status = BlockIo->WriteBlocks (
|
BlockIo,
|
BlockIo->Media->MediaId,
|
EmmcInfo->Lba,
|
TuningPatternSize,
|
Buffer
|
);
|
if (EFI_ERROR (Status)) {
|
DEBUG ((DEBUG_ERROR, "EmmcWriteHs400TuningPattern: Multiple Blocks Write at HS200 Mode Failed!\n"));
|
goto Exit;
|
}
|
|
Exit:
|
FreePool (Buffer);
|
DEBUG ((DEBUG_INFO, "EmmcWriteHs400TuningPattern() End, Status = %r\n", Status));
|
return Status;
|
}
|
|
/**
|
To perform HS400 Rx Data Path Training
|
|
@param[in] EmmcInfo A pointer to EMMC_INFO structure
|
@param[in] BlockIo A pointer to EFI_BLOCK_IO_PROTOCOL structure
|
@param[in] EmmcBaseAddress Base address of MMIO register
|
@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 EMMC_INFO *EmmcInfo,
|
IN EFI_BLOCK_IO_PROTOCOL *BlockIo,
|
IN UINTN EmmcBaseAddress,
|
IN OUT EMMC_TUNING_DATA *EmmcTuningData
|
)
|
{
|
UINT8 *Buffer;
|
UINT8 DllCount;
|
UINT8 DllMax;
|
UINT8 DllMin;
|
UINT8 Smin;
|
UINT8 Smax;
|
UINT8 Sopt;
|
EFI_STATUS Status;
|
EFI_STATUS ModeStatus;
|
BLOCK_READ_WRITE_STATUS FirstRead;
|
UINT32 TuningPatternSize;
|
|
DEBUG ((DEBUG_INFO, "EmmcRxHs400Tuning() Start\n"));
|
|
Status = EFI_SUCCESS;
|
ModeStatus = EFI_SUCCESS;
|
|
Smin = RX_STROBE_DLL1_TAP_MIN_MEPT;
|
Smax = RX_STROBE_DLL1_TAP_MAX_MEPT;
|
TuningPatternSize = BlockIo->Media->BlockSize * EMMC_HS400_TUNING_PATTERN_BLOCKS_NUMBER;
|
|
Buffer = (VOID *) AllocateZeroPool (TuningPatternSize);
|
if (Buffer == NULL) {
|
Status = EFI_OUT_OF_RESOURCES;
|
goto Exit;
|
}
|
|
//
|
// 1. Read Tuning Block
|
//
|
Status = BlockIo->ReadBlocks (
|
BlockIo,
|
BlockIo->Media->MediaId,
|
EmmcInfo->Lba,
|
TuningPatternSize,
|
Buffer
|
);
|
if (EFI_ERROR (Status)) {
|
DEBUG ((DEBUG_ERROR, "EmmcRxHs400Tuning: Tuning Blocks Read at HS200 Mode Failed!\n"));
|
goto Exit;
|
}
|
|
DEBUG_CODE ( EmmcPrintHs400TuningPattern (BlockIo, Buffer, TuningPatternSize); );
|
|
//
|
// 2. Move to HS400 Mode
|
//
|
Status = EmmcModeSelection (EmmcInfo, EmmcBaseAddress, 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_INFO, "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);
|
EmmcSetRxDllCtrl (EmmcBaseAddress, RxDll2, DllCount);
|
|
//
|
// 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;
|
DllMax = RX_STROBE_DLL1_TAP_MAX_RANGE;
|
DllMin = RX_STROBE_DLL1_TAP_MIN_RANGE;
|
|
while ((DllCount <= DllMax) && (DllCount >= DllMin)) {
|
DEBUG ((DEBUG_INFO, "[ EmmcRxHs400Tuning: Rx Min DLL1 (DllCount) = 0x%x ]\n", DllCount));
|
Status = BlockIo->ReadBlocks (
|
BlockIo,
|
BlockIo->Media->MediaId,
|
EmmcInfo->Lba,
|
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) { // Rely on the driver to return ReadBlocks status on CRC error
|
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);
|
EmmcSetRxDllCtrl (EmmcBaseAddress, RxDll2, DllCount);
|
}
|
|
DEBUG ((DEBUG_INFO, "[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_INFO, "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);
|
EmmcSetRxDllCtrl (EmmcBaseAddress, RxDll2, DllCount);
|
|
FirstRead = NotAvailable;
|
DllMax = RX_STROBE_DLL1_TAP_MAX_RANGE;
|
DllMin = RX_STROBE_DLL1_TAP_MIN_RANGE;
|
while ((DllCount <= DllMax) && (DllCount >= DllMin)) {
|
DEBUG ((DEBUG_INFO, "[ EmmcRxHs400Tuning: Rx Max DLL1 (DllCount) = 0x%x ]\n", DllCount));
|
Status = BlockIo->ReadBlocks (
|
BlockIo,
|
BlockIo->Media->MediaId,
|
EmmcInfo->Lba,
|
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) { // Rely on the driver to return ReadBlocks status on CRC error
|
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);
|
EmmcSetRxDllCtrl (EmmcBaseAddress, RxDll2, DllCount);
|
}
|
//
|
// 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_INFO, "[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);
|
EmmcSetRxDllCtrl (EmmcBaseAddress, RxDll2, Sopt);
|
|
DEBUG ((DEBUG_INFO, "[Rx HS400 Tuning Success] Rx Strobe Delay Control (830h) = 0x%08x\n",
|
MmioRead32 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_RX_STROBE_DLL_CNTL)));
|
|
Status = EFI_SUCCESS;
|
EmmcTuningData->Hs400RxStrobe1Dll = Sopt;
|
|
Exit:
|
FreePool (Buffer);
|
DEBUG ((DEBUG_INFO, "EmmcRxHs400Tuning() End, Status = %r\n", Status));
|
return Status;
|
}
|
|
/**
|
To perform HS400 Tx Data Path Training
|
|
@param[in] EmmcInfo A pointer to EMMC_INFO structure
|
@param[in] EmmcBaseAddress Base address of MMIO register
|
@param[in] BlockIo A pointer to EFI_BLOCK_IO_PROTOCOL structure
|
@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 EMMC_INFO *EmmcInfo,
|
IN EFI_BLOCK_IO_PROTOCOL *BlockIo,
|
IN UINTN EmmcBaseAddress,
|
IN OUT EMMC_TUNING_DATA *EmmcTuningData
|
)
|
{
|
UINT8 *Buffer;
|
UINT8 DllCount;
|
UINT8 DllMax;
|
UINT8 DllMin;
|
UINT8 Smin;
|
UINT8 Smax;
|
UINT8 Sopt;
|
UINT8 N;
|
EFI_STATUS Status;
|
EFI_STATUS ModeStatus;
|
BLOCK_READ_WRITE_STATUS FirstWrite;
|
|
DEBUG ((DEBUG_INFO, "EmmcTxHs400Tuning() Start\n"));
|
Status = EFI_SUCCESS;
|
ModeStatus = EFI_SUCCESS;
|
|
Smin = TX_DATA_DLL_TAP_MIN_MEPT;
|
Smax = TX_DATA_DLL_TAP_MAX_MEPT;
|
N = 0;
|
|
Buffer = (VOID *) AllocateZeroPool (BlockIo->Media->BlockSize);
|
if (Buffer == NULL) {
|
Status = EFI_OUT_OF_RESOURCES;
|
goto Exit;
|
}
|
//
|
// 1. Read Tuning Block that used at Rx HS400 Tuning
|
//
|
Status = BlockIo->ReadBlocks (
|
BlockIo,
|
BlockIo->Media->MediaId,
|
EmmcInfo->Lba,
|
BlockIo->Media->BlockSize,
|
Buffer
|
);
|
if (EFI_ERROR (Status)) {
|
DEBUG ((DEBUG_ERROR, "EmmcTxHs400Tuning: Tuning Block Read at HS400 Mode Failed!\n"));
|
goto Exit;
|
}
|
DEBUG_CODE ( EmmcPrintHs400TuningPattern (BlockIo, Buffer, BlockIo->Media->BlockSize); );
|
|
//
|
// 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_INFO, "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);
|
|
//
|
// 2. Write Single Block
|
//
|
FirstWrite = NotAvailable;
|
DllMax = TX_DATA_DLL_TAP_MAX_RANGE;
|
DllMin = TX_DATA_DLL_TAP_MIN_RANGE;
|
|
while ((DllCount <= DllMax) && (DllCount >= DllMin)) {
|
DEBUG ((DEBUG_INFO, "[ EmmcTxHs400Tuning: Tx Min DLL (DllCount) = 0x%x ]\n", DllCount));
|
Status = BlockIo->WriteBlocks (
|
BlockIo,
|
BlockIo->Media->MediaId,
|
EmmcInfo->Lba,
|
BlockIo->Media->BlockSize,
|
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) { // Rely on the driver to return ReadBlocks status on CRC error
|
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);
|
}
|
//
|
// 4. Store the Tx Path min DLL passing step number
|
//
|
DEBUG ((DEBUG_INFO, "[Tx DLL Tuning] Found Minimal Passing Tap = 0x%x\n", Smin));
|
|
//
|
// 5. Set the DLL to max expected passing step (Smax)
|
//
|
DEBUG ((DEBUG_INFO, "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);
|
//
|
// 6. Write Single Block
|
//
|
FirstWrite = NotAvailable;
|
DllMax = TX_DATA_DLL_TAP_MAX_RANGE;
|
DllMin = TX_DATA_DLL_TAP_MIN_RANGE;
|
|
while ((DllCount <= DllMax) && (DllCount >= DllMin)) {
|
DEBUG ((DEBUG_INFO, "[ EmmcTxHs400Tuning: Tx Max DLL1 (DllCount) = 0x%x ]\n", DllCount));
|
Status = BlockIo->WriteBlocks (
|
BlockIo,
|
BlockIo->Media->MediaId,
|
EmmcInfo->Lba,
|
BlockIo->Media->BlockSize,
|
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) { // Rely on the driver to return ReadBlocks status on CRC error
|
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);
|
}
|
//
|
// 8. Store the DLL passing step number (Smax)
|
//
|
DEBUG ((DEBUG_INFO, "[Tx DLL Tuning] Found Maximal Passing Tap = 0x%x\n", Smax));
|
|
N = Smax - Smin;
|
if (N <= 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_INFO, "[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);
|
|
DEBUG ((DEBUG_INFO, "[Tx HS400 Tuning Success] Tx Data Delay Control 1 (824h) = 0x%08x\n",
|
MmioRead32 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_TX_DATA_DLL_CNTL1)));
|
|
Status = EFI_SUCCESS;
|
EmmcTuningData->Hs400TxDataDll = Sopt;
|
|
Exit:
|
FreePool (Buffer);
|
DEBUG ((DEBUG_INFO, "EmmcTxHs400Tuning() End, Status = %r\n", Status));
|
return Status;
|
}
|
|
/**
|
To perform write protection checking on the address to write
|
|
@param[in] EmmcInfo A pointer to EMMC_INFO structure
|
@param[in] BlockIo A pointer to EFI_BLOCK_IO_PROTOCOL structure
|
@param[in] EmmcBaseAddress Base address of MMIO register
|
|
@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
|
EmmcWriteProtectCheck (
|
IN EMMC_INFO *EmmcInfo,
|
IN EFI_BLOCK_IO_PROTOCOL *BlockIo,
|
IN UINTN EmmcBaseAddress
|
)
|
{
|
EFI_STATUS Status;
|
UINT32 DataSize;
|
UINT32 CardStatus;
|
UINT32 Address;
|
UINT32 Timeout;
|
UINT16 CommandIndex;
|
UINT8 *Buffer;
|
|
Status = EFI_SUCCESS;
|
DataSize = 8;
|
CommandIndex = SEND_WRITE_PROT_TYPE;
|
Timeout = TIMEOUT_DATA;
|
|
Buffer = (VOID *) AllocateZeroPool (DataSize);
|
Address = (UINT32) DivU64x32 (MultU64x32 (EmmcInfo->Lba, BlockIo->Media->BlockSize), 512);
|
|
DEBUG ((DEBUG_INFO, "EmmcWriteProtectCheck() Start\n"));
|
|
if (BlockIo->Media->ReadOnly == TRUE) {
|
Status = EFI_WRITE_PROTECTED;
|
goto Exit;
|
}
|
|
if (Buffer == NULL) {
|
Status = EFI_OUT_OF_RESOURCES;
|
goto Exit;
|
}
|
Status = EmmcSendCommand (
|
EmmcBaseAddress,
|
CommandIndex,
|
Address,
|
InData,
|
Buffer,
|
DataSize,
|
ResponseR1,
|
Timeout,
|
&CardStatus
|
);
|
|
if (EFI_ERROR (Status)) {
|
goto Exit;
|
}
|
|
if ((Buffer[7] & (BIT0 | BIT1)) !=0) {
|
Status = EFI_WRITE_PROTECTED;
|
goto Exit;
|
}
|
|
Exit:
|
FreePool (Buffer);
|
DEBUG ((DEBUG_INFO, "EmmcWriteProtectCheck End, Status = %r\n", Status));
|
return Status;
|
}
|
|
/**
|
Configure eMMC in HS400 Mode
|
|
@param[in] This A pointer to PCH_EMMC_TUNING_PROTOCOL structure
|
@param[in] Revision Revision parameter used to verify the layout of EMMC_INFO and TUNINGDATA.
|
@param[in] EmmcInfo A pointer to EMMC_INFO structure
|
@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 PCH_EMMC_TUNING_PROTOCOL *This,
|
IN UINT8 Revision,
|
IN EMMC_INFO *EmmcInfo,
|
OUT EMMC_TUNING_DATA *EmmcTuningData
|
)
|
{
|
EFI_BLOCK_IO_PROTOCOL *BlockIo;
|
EFI_STATUS Status;
|
EFI_STATUS ModeStatus;
|
UINTN EmmcBaseAddress;
|
UINTN EmmcPciBaseAddress;
|
ModeStatus = EFI_SUCCESS;
|
|
DEBUG ((DEBUG_INFO, "ConfigureEmmcHs400Mode() Start\n"));
|
EmmcTuningData->Hs400DataValid = FALSE;
|
//
|
// Check PCH_EMMC_TUNING_PROTOCOL_REVISION
|
//
|
if (Revision != PCH_EMMC_TUNING_PROTOCOL_REVISION) {
|
DEBUG ((DEBUG_ERROR, "ConfigureEmmcHs400Mode: PCH eMMC Tuning Protocol Revision Not Match! Tuning Aborted\n"));
|
return EFI_UNSUPPORTED;
|
}
|
//
|
// Get eMMC Host Controller Mmio base register
|
//
|
EmmcPciBaseAddress = MmPciBase (
|
DEFAULT_PCI_BUS_NUMBER_PCH,
|
PCI_DEVICE_NUMBER_PCH_SCS_EMMC,
|
PCI_FUNCTION_NUMBER_PCH_SCS_EMMC
|
);
|
|
if (MmioRead16 (EmmcPciBaseAddress + PCI_VENDOR_ID_OFFSET) == 0xFFFF) {
|
DEBUG ((DEBUG_ERROR, "ConfigureEmmcHs400Mode: eMMC Host Controller Unavailable!\n"));
|
return EFI_UNSUPPORTED;
|
}
|
|
//
|
// Assume BAR is ready since it's executed after blockio ready, handling 64bit BAR in DXE is not required
|
//
|
EmmcBaseAddress = MmioRead32 (EmmcPciBaseAddress + PCI_BASE_ADDRESSREG_OFFSET) & 0xFFFFF000;
|
//
|
// Enable Memory Decode
|
//
|
if ((MmioRead8 (EmmcPciBaseAddress + PCI_COMMAND_OFFSET) & EFI_PCI_COMMAND_MEMORY_SPACE) == 0) {
|
MmioOr8 (EmmcPciBaseAddress + PCI_COMMAND_OFFSET, EFI_PCI_COMMAND_MEMORY_SPACE);
|
}
|
|
DEBUG ((DEBUG_INFO, "Initial HS400 DLL values (set in PEI):\n"));
|
DEBUG ((DEBUG_INFO, "Tx Data Delay Control 1 (824h) = 0x%08x\n", MmioRead32 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_TX_DATA_DLL_CNTL1)));
|
DEBUG ((DEBUG_INFO, "Rx Strobe Delay Control (830h) = 0x%08x\n", MmioRead32 (EmmcBaseAddress + R_PCH_SCS_DEV_MEM_RX_STROBE_DLL_CNTL)));
|
|
if (mPchConfigHob->Scs.ScsEmmcHs400TuningRequired == FALSE) {
|
if (mPchConfigHob->Scs.ScsEmmcHs400DllDataValid == TRUE) {
|
DEBUG ((DEBUG_INFO, "ConfigureEmmcHs400Mode: SCS eMMC 5.0 HS400 Tuning Not Required, set device to HS400 mode\n"));
|
Status = EmmcModeSelection (EmmcInfo, EmmcBaseAddress, Hs400);
|
if (EFI_ERROR (Status)) {
|
DEBUG ((DEBUG_ERROR, "ConfigureEmmcHs400Mode: eMMC HS400 Mode Selection Failed!\n"));
|
}
|
return Status;
|
} else {
|
DEBUG ((DEBUG_INFO, "ConfigureEmmcHs400Mode: SCS eMMC 5.0 HS400 Mode Selection Not Required.\n"));
|
return EFI_ABORTED;
|
}
|
}
|
//
|
// Handle Platform Emmc Info Protocol for Efi Block Io Protocol
|
//
|
Status = gBS->HandleProtocol (
|
EmmcInfo->PartitionHandle,
|
&gEfiBlockIoProtocolGuid,
|
(VOID**) &BlockIo
|
);
|
if (EFI_ERROR (Status)) {
|
DEBUG ((DEBUG_ERROR, "ConfigureEmmcHs400Mode: BlockIo: Platform Emmc Info Protocol Handle Not Found!\n"));
|
EmmcHostHs400Disabled (EmmcBaseAddress);
|
return Status;
|
}
|
|
//
|
// Write Protection checking on the region
|
//
|
Status = EmmcWriteProtectCheck (EmmcInfo, BlockIo, EmmcBaseAddress);
|
if (EFI_ERROR (Status)) {
|
DEBUG ((DEBUG_ERROR, "ConfigureEmmcHs400Mode: Region is write protected! HS400 Tuning Abort!\n"));
|
EmmcHostHs400Disabled (EmmcBaseAddress);
|
return Status;
|
}
|
|
Status = EmmcWriteHs400TuningPattern (EmmcInfo, BlockIo, EmmcBaseAddress, (UINT8*)EmmcWorstCasePattern);
|
|
//
|
// Rx HS400 Auto Tuning
|
//
|
Status = EmmcRxHs400Tuning (EmmcInfo, BlockIo, EmmcBaseAddress, EmmcTuningData);
|
if (EFI_ERROR (Status)) {
|
DEBUG ((DEBUG_ERROR, "ConfigureEmmcHs400Mode: Rx HS400 Auto Tuning Failed!\n"));
|
ModeStatus = EmmcModeSelection (EmmcInfo, EmmcBaseAddress, Hs200);
|
if (EFI_ERROR (ModeStatus)) {
|
DEBUG ((DEBUG_ERROR, "EmmcTxHs400Tuning: eMMC HS200 Mode Selection Failed!\n"));
|
}
|
EmmcTuningData->Hs400DataValid = FALSE;
|
return Status;
|
}
|
|
//
|
// Tx HS400 Auto Tuning
|
//
|
Status = EmmcTxHs400Tuning (EmmcInfo, BlockIo, EmmcBaseAddress, EmmcTuningData);
|
if (EFI_ERROR (Status)) {
|
DEBUG ((DEBUG_ERROR, "ConfigureEmmcHs400Mode: Tx HS400 Auto Tuning Failed!\n"));
|
ModeStatus = EmmcModeSelection (EmmcInfo, EmmcBaseAddress, Hs200);
|
if (EFI_ERROR (ModeStatus)) {
|
DEBUG ((DEBUG_ERROR, "EmmcTxHs400Tuning: eMMC HS200 Mode Selection Failed!\n"));
|
}
|
EmmcTuningData->Hs400DataValid = FALSE;
|
return Status;
|
}
|
|
//
|
// Set current HS400 Driver Strength in tuning data structre
|
// Set HS400 Data Valid Tuning Bit to TRUE
|
//
|
EmmcTuningData->Hs400DriverStrength = (UINT8) mPchConfigHob->Scs.ScsEmmcHs400DriverStrength;
|
EmmcTuningData->Hs400DataValid = TRUE;
|
|
DEBUG ((DEBUG_INFO, "ConfigureEmmcHs400Mode() End\n"));
|
return Status;
|
}
|
|
/**
|
Install PCH EMMC TUNING PROTOCOL
|
|
**/
|
VOID
|
InstallPchEmmcTuningProtocol (
|
VOID
|
)
|
{
|
|
EFI_HANDLE Handle;
|
EFI_STATUS Status;
|
|
Handle = NULL;
|
|
///
|
/// For normal boot flow
|
/// 1. If ScsEmmcEnabled and ScsEmmcHs400Enabled policy set,
|
/// a) Set ScsEmmcHs400TuningRequired policy to state tuning required in PEI,
|
/// - if RTC_PWR_STS bit is set which indicates a new coin-cell battery insertion, a battery failure or CMOS clear.(Boot with default settings)
|
/// - if non-volatile variable 'Hs400TuningData' does not exist
|
/// b) RC installed Pch Emmc Tuning Protocol regardless of ScsEmmcHs400TuningRequired policy setting.in DXE
|
/// c) If boot with default settings after CMOS cleared, platform delete variable 'Hs400TuningData' in DXE
|
/// 2. Once RC successfully installed Pch Emmc Tuning Protocol, it will be used to perform EmmcTune for Hs400.
|
/// 3. Then, platform must set the variable with returned EmmcTuningData no matter tuning pass of fail
|
/// 4. Platform shall set variable 'Hs400TuningData' for one time only or after CMOS clear
|
///
|
/// For fast boot flow
|
/// 1. If ScsEmmcEnabled and ScsEmmcHs400Enabled policy set,
|
/// a) Set ScsEmmcHs400TuningRequired policy to state tuning not required, if non-volatile variable 'Hs400TuningData' exist
|
/// b) RC installed Pch Emmc Tuning Protocol regardless of ScsEmmcHs400TuningRequired policy setting in DXE
|
/// 2. Once RC successfully installed Pch Emmc Tuning Protocol, it will be used to perform EmmcTune
|
/// 3. Since ScsEmmcHs400TuningRequired state tuning not required, RC will not perform Emmc Hs400 Tuning but just set the device to operate in HS400 mode if data is valid
|
/// 4. Platform shall not set variable 'Hs400TuningData'
|
///
|
//
|
// Install PchEmmcTuningProtocol Protocol
|
//
|
Status = gBS->InstallProtocolInterface (
|
&Handle,
|
&gPchEmmcTuningProtocolGuid,
|
EFI_NATIVE_INTERFACE,
|
&PchEmmcTuningProtocol
|
);
|
ASSERT_EFI_ERROR (Status);
|
}
|