Files
system76-edk2/EmbeddedPkg/Universal/MmcDxe/MmcBlockIo.c
Gaurav Jain 567bc4b4ae EmbeddedPkg/MmcDxe: Added MaxBlock Transfer Limit 65535 in R/W.
Moved BlockCount calculation below BufferSize Validation checks.
First Ensure Buffersize is Not Zero and multiple of Media BlockSize.
then calculate BlockCount and perform Block checks.

Corrected BlockCount calculation, as BufferSize is multiple of BlockSize,
So adding (BlockSize-1) bytes to BufferSize and
then divide by BlockSize will have no impact on BlockCount.

Reading Large Images from MMC causes errors.
As per SD Host Controller Spec version 4.20,
Restriction of 16-bit Block Count transfer is 65535.
Max block transfer limit in single cmd is 65535 blocks.
Added Max Block check that can be processed is 0xFFFF.
then Update BlockCount on the basis of MaxBlock.

Signed-off-by: Gaurav Jain <gaurav.jain@nxp.com>
Reviewed-by: "Loh, Tien Hock" <tien.hock.loh@intel.com>
2020-06-12 08:12:27 +00:00

397 lines
11 KiB
C

/** @file
*
* Copyright (c) 2011-2015, ARM Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-2-Clause-Patent
*
**/
#include <Library/BaseMemoryLib.h>
#include "Mmc.h"
EFI_STATUS
MmcNotifyState (
IN MMC_HOST_INSTANCE *MmcHostInstance,
IN MMC_STATE State
)
{
MmcHostInstance->State = State;
return MmcHostInstance->MmcHost->NotifyState (MmcHostInstance->MmcHost, State);
}
EFI_STATUS
EFIAPI
MmcGetCardStatus (
IN MMC_HOST_INSTANCE *MmcHostInstance
)
{
EFI_STATUS Status;
UINT32 Response[4];
UINTN CmdArg;
EFI_MMC_HOST_PROTOCOL *MmcHost;
Status = EFI_SUCCESS;
MmcHost = MmcHostInstance->MmcHost;
CmdArg = 0;
if (MmcHost == NULL) {
return EFI_INVALID_PARAMETER;
}
if (MmcHostInstance->State != MmcHwInitializationState) {
//Get the Status of the card.
CmdArg = MmcHostInstance->CardInfo.RCA << 16;
Status = MmcHost->SendCommand (MmcHost, MMC_CMD13, CmdArg);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "MmcGetCardStatus(MMC_CMD13): Error and Status = %r\n", Status));
return Status;
}
//Read Response
MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_R1, Response);
PrintResponseR1 (Response[0]);
}
return Status;
}
EFI_STATUS
EFIAPI
MmcReset (
IN EFI_BLOCK_IO_PROTOCOL *This,
IN BOOLEAN ExtendedVerification
)
{
MMC_HOST_INSTANCE *MmcHostInstance;
MmcHostInstance = MMC_HOST_INSTANCE_FROM_BLOCK_IO_THIS (This);
if (MmcHostInstance->MmcHost == NULL) {
// Nothing to do
return EFI_SUCCESS;
}
// If a card is not present then clear all media settings
if (!MmcHostInstance->MmcHost->IsCardPresent (MmcHostInstance->MmcHost)) {
MmcHostInstance->BlockIo.Media->MediaPresent = FALSE;
MmcHostInstance->BlockIo.Media->LastBlock = 0;
MmcHostInstance->BlockIo.Media->BlockSize = 512; // Should be zero but there is a bug in DiskIo
MmcHostInstance->BlockIo.Media->ReadOnly = FALSE;
// Indicate that the driver requires initialization
MmcHostInstance->State = MmcHwInitializationState;
return EFI_SUCCESS;
}
// Implement me. Either send a CMD0 (could not work for some MMC host) or just turn off/turn
// on power and restart Identification mode
return EFI_SUCCESS;
}
EFI_STATUS
MmcDetectCard (
EFI_MMC_HOST_PROTOCOL *MmcHost
)
{
if (!MmcHost->IsCardPresent (MmcHost)) {
return EFI_NO_MEDIA;
} else {
return EFI_SUCCESS;
}
}
EFI_STATUS
MmcStopTransmission (
EFI_MMC_HOST_PROTOCOL *MmcHost
)
{
EFI_STATUS Status;
UINT32 Response[4];
// Command 12 - Stop transmission (ends read or write)
// Normally only needed for streaming transfers or after error.
Status = MmcHost->SendCommand (MmcHost, MMC_CMD12, 0);
if (!EFI_ERROR (Status)) {
MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_R1b, Response);
}
return Status;
}
#define MMCI0_BLOCKLEN 512
#define MMCI0_TIMEOUT 10000
STATIC
EFI_STATUS
MmcTransferBlock (
IN EFI_BLOCK_IO_PROTOCOL *This,
IN UINTN Cmd,
IN UINTN Transfer,
IN UINT32 MediaId,
IN EFI_LBA Lba,
IN UINTN BufferSize,
OUT VOID *Buffer
)
{
EFI_STATUS Status;
UINTN CmdArg;
INTN Timeout;
UINT32 Response[4];
MMC_HOST_INSTANCE *MmcHostInstance;
EFI_MMC_HOST_PROTOCOL *MmcHost;
MmcHostInstance = MMC_HOST_INSTANCE_FROM_BLOCK_IO_THIS (This);
MmcHost = MmcHostInstance->MmcHost;
if (MmcHostInstance->CardInfo.CardType != EMMC_CARD) {
//Set command argument based on the card capacity
//if 0 : SDSC card
//if 1 : SDXC/SDHC
if (MmcHostInstance->CardInfo.OCRData.AccessMode & SD_CARD_CAPACITY) {
CmdArg = Lba;
} else {
CmdArg = Lba * This->Media->BlockSize;
}
} else {
//Set command argument based on the card access mode (Byte mode or Block mode)
if ((MmcHostInstance->CardInfo.OCRData.AccessMode & MMC_OCR_ACCESS_MASK) ==
MMC_OCR_ACCESS_SECTOR) {
CmdArg = Lba;
} else {
CmdArg = Lba * This->Media->BlockSize;
}
}
Status = MmcHost->SendCommand (MmcHost, Cmd, CmdArg);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "%a(MMC_CMD%d): Error %r\n", __func__, Cmd, Status));
return Status;
}
if (Transfer == MMC_IOBLOCKS_READ) {
// Read Data
Status = MmcHost->ReadBlockData (MmcHost, Lba, BufferSize, Buffer);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_BLKIO, "%a(): Error Read Block Data and Status = %r\n", __func__, Status));
MmcStopTransmission (MmcHost);
return Status;
}
Status = MmcNotifyState (MmcHostInstance, MmcProgrammingState);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "%a() : Error MmcProgrammingState\n", __func__));
return Status;
}
} else {
// Write Data
Status = MmcHost->WriteBlockData (MmcHost, Lba, BufferSize, Buffer);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_BLKIO, "%a(): Error Write Block Data and Status = %r\n", __func__, Status));
MmcStopTransmission (MmcHost);
return Status;
}
}
// Command 13 - Read status and wait for programming to complete (return to tran)
Timeout = MMCI0_TIMEOUT;
CmdArg = MmcHostInstance->CardInfo.RCA << 16;
Response[0] = 0;
while(!(Response[0] & MMC_R0_READY_FOR_DATA)
&& (MMC_R0_CURRENTSTATE (Response) != MMC_R0_STATE_TRAN)
&& Timeout--) {
Status = MmcHost->SendCommand (MmcHost, MMC_CMD13, CmdArg);
if (!EFI_ERROR (Status)) {
MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_R1, Response);
if (Response[0] & MMC_R0_READY_FOR_DATA) {
break; // Prevents delay once finished
}
}
}
if (BufferSize > This->Media->BlockSize) {
Status = MmcHost->SendCommand (MmcHost, MMC_CMD12, 0);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_BLKIO, "%a(): Error and Status:%r\n", __func__, Status));
}
MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_R1b, Response);
}
Status = MmcNotifyState (MmcHostInstance, MmcTransferState);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "MmcIoBlocks() : Error MmcTransferState\n"));
return Status;
}
return Status;
}
EFI_STATUS
MmcIoBlocks (
IN EFI_BLOCK_IO_PROTOCOL *This,
IN UINTN Transfer,
IN UINT32 MediaId,
IN EFI_LBA Lba,
IN UINTN BufferSize,
OUT VOID *Buffer
)
{
UINT32 Response[4];
EFI_STATUS Status;
UINTN CmdArg;
INTN Timeout;
UINTN Cmd;
MMC_HOST_INSTANCE *MmcHostInstance;
EFI_MMC_HOST_PROTOCOL *MmcHost;
UINTN BytesRemainingToBeTransfered;
UINTN BlockCount;
UINTN ConsumeSize;
UINT32 MaxBlock;
UINTN RemainingBlock;
BlockCount = 1;
MmcHostInstance = MMC_HOST_INSTANCE_FROM_BLOCK_IO_THIS (This);
ASSERT (MmcHostInstance != NULL);
MmcHost = MmcHostInstance->MmcHost;
ASSERT (MmcHost);
if (This->Media->MediaId != MediaId) {
return EFI_MEDIA_CHANGED;
}
if ((MmcHost == NULL) || (Buffer == NULL)) {
return EFI_INVALID_PARAMETER;
}
// Check if a Card is Present
if (!MmcHostInstance->BlockIo.Media->MediaPresent) {
return EFI_NO_MEDIA;
}
// Reading 0 Byte is valid
if (BufferSize == 0) {
return EFI_SUCCESS;
}
// The buffer size must be an exact multiple of the block size
if ((BufferSize % This->Media->BlockSize) != 0) {
return EFI_BAD_BUFFER_SIZE;
}
if (MMC_HOST_HAS_ISMULTIBLOCK(MmcHost) && MmcHost->IsMultiBlock(MmcHost)) {
BlockCount = BufferSize / This->Media->BlockSize;
}
// All blocks must be within the device
if ((Lba + (BufferSize / This->Media->BlockSize)) > (This->Media->LastBlock + 1)) {
return EFI_INVALID_PARAMETER;
}
if ((Transfer == MMC_IOBLOCKS_WRITE) && (This->Media->ReadOnly == TRUE)) {
return EFI_WRITE_PROTECTED;
}
// Check the alignment
if ((This->Media->IoAlign > 2) && (((UINTN)Buffer & (This->Media->IoAlign - 1)) != 0)) {
return EFI_INVALID_PARAMETER;
}
// Max block number in single cmd is 65535 blocks.
MaxBlock = 0xFFFF;
RemainingBlock = BlockCount;
BytesRemainingToBeTransfered = BufferSize;
while (BytesRemainingToBeTransfered > 0) {
if (RemainingBlock <= MaxBlock) {
BlockCount = RemainingBlock;
} else {
BlockCount = MaxBlock;
}
// Check if the Card is in Ready status
CmdArg = MmcHostInstance->CardInfo.RCA << 16;
Response[0] = 0;
Timeout = 20;
while( (!(Response[0] & MMC_R0_READY_FOR_DATA))
&& (MMC_R0_CURRENTSTATE (Response) != MMC_R0_STATE_TRAN)
&& Timeout--) {
Status = MmcHost->SendCommand (MmcHost, MMC_CMD13, CmdArg);
if (!EFI_ERROR (Status)) {
MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_R1, Response);
}
}
if (0 == Timeout) {
DEBUG ((EFI_D_ERROR, "The Card is busy\n"));
return EFI_NOT_READY;
}
if (Transfer == MMC_IOBLOCKS_READ) {
if (BlockCount == 1) {
// Read a single block
Cmd = MMC_CMD17;
} else {
// Read multiple blocks
Cmd = MMC_CMD18;
}
} else {
if (BlockCount == 1) {
// Write a single block
Cmd = MMC_CMD24;
} else {
// Write multiple blocks
Cmd = MMC_CMD25;
}
}
ConsumeSize = BlockCount * This->Media->BlockSize;
if (BytesRemainingToBeTransfered < ConsumeSize) {
ConsumeSize = BytesRemainingToBeTransfered;
}
Status = MmcTransferBlock (This, Cmd, Transfer, MediaId, Lba, ConsumeSize, Buffer);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "%a(): Failed to transfer block and Status:%r\n", __func__, Status));
}
RemainingBlock -= BlockCount;
BytesRemainingToBeTransfered -= ConsumeSize;
if (BytesRemainingToBeTransfered > 0) {
Lba += BlockCount;
Buffer = (UINT8 *)Buffer + ConsumeSize;
}
}
return EFI_SUCCESS;
}
EFI_STATUS
EFIAPI
MmcReadBlocks (
IN EFI_BLOCK_IO_PROTOCOL *This,
IN UINT32 MediaId,
IN EFI_LBA Lba,
IN UINTN BufferSize,
OUT VOID *Buffer
)
{
return MmcIoBlocks (This, MMC_IOBLOCKS_READ, MediaId, Lba, BufferSize, Buffer);
}
EFI_STATUS
EFIAPI
MmcWriteBlocks (
IN EFI_BLOCK_IO_PROTOCOL *This,
IN UINT32 MediaId,
IN EFI_LBA Lba,
IN UINTN BufferSize,
IN VOID *Buffer
)
{
return MmcIoBlocks (This, MMC_IOBLOCKS_WRITE, MediaId, Lba, BufferSize, Buffer);
}
EFI_STATUS
EFIAPI
MmcFlushBlocks (
IN EFI_BLOCK_IO_PROTOCOL *This
)
{
return EFI_SUCCESS;
}