/** @file
Block I/O protocol for MMC/SD device
Copyright (c) 2013-2015 Intel Corporation.
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
**/
#include "SDMediaDevice.h"
/**
Implements EFI_BLOCK_IO_PROTOCOL.Reset() function.
@param This The EFI_BLOCK_IO_PROTOCOL instance.
@param ExtendedVerification Indicates that the driver may perform a more exhaustive.
verification operation of the device during reset.
(This parameter is ingored in this driver.)
@retval EFI_SUCCESS Success
**/
EFI_STATUS
EFIAPI
MMCSDBlockReset (
IN EFI_BLOCK_IO_PROTOCOL *This,
IN BOOLEAN ExtendedVerification
)
{
CARD_DATA *CardData;
EFI_SD_HOST_IO_PROTOCOL *SDHostIo;
CardData = CARD_DATA_FROM_THIS(This);
SDHostIo = CardData->SDHostIo;
return SDHostIo->ResetSDHost (SDHostIo, Reset_DAT_CMD);
}
/**
Implements EFI_BLOCK_IO_PROTOCOL.ReadBlocks() function.
@param This The EFI_BLOCK_IO_PROTOCOL instance.
@param MediaId The media id that the write request is for.
@param LBA The starting logical block address to read from on the device.
The caller is responsible for writing to only legitimate locations.
@param BufferSize The size of the Buffer in bytes. This must be a multiple of the
intrinsic block size of the device.
@param Buffer A pointer to the destination buffer for the data. The caller
is responsible for either having implicit or explicit ownership
of the buffer.
@retval EFI_SUCCESS Success
@retval EFI_DEVICE_ERROR Hardware Error
@retval EFI_INVALID_PARAMETER Parameter is error
@retval EFI_NO_MEDIA No media
@retval EFI_MEDIA_CHANGED Media Change
@retval EFI_BAD_BUFFER_SIZE Buffer size is bad
**/
EFI_STATUS
EFIAPI
MMCSDBlockReadBlocks (
IN EFI_BLOCK_IO_PROTOCOL *This,
IN UINT32 MediaId,
IN EFI_LBA LBA,
IN UINTN BufferSize,
OUT VOID *Buffer
)
{
EFI_STATUS Status;
UINT32 Address;
CARD_DATA *CardData;
EFI_SD_HOST_IO_PROTOCOL *SDHostIo;
UINT32 RemainingLength;
UINT32 TransferLength;
UINT8 *BufferPointer;
BOOLEAN SectorAddressing;
UINTN TotalBlock;
DEBUG((EFI_D_INFO, "Read(LBA=%08lx, Buffer=%08x, Size=%08x)\n", LBA, Buffer, BufferSize));
Status = EFI_SUCCESS;
CardData = CARD_DATA_FROM_THIS(This);
SDHostIo = CardData->SDHostIo;
if (MediaId != CardData->BlockIoMedia.MediaId) {
return EFI_MEDIA_CHANGED;
}
if (ModU64x32 (BufferSize,CardData->BlockIoMedia.BlockSize) != 0) {
return EFI_BAD_BUFFER_SIZE;
}
if ((CardData->CardType == SDMemoryCard2High) || (CardData->CardType == MMCCardHighCap)) {
SectorAddressing = TRUE;
} else {
SectorAddressing = FALSE;
}
if (SectorAddressing) {
//
//Block Address
//
Address = (UINT32)DivU64x32 (MultU64x32 (LBA, CardData->BlockIoMedia.BlockSize), 512);
} else {
//
//Byte Address
//
Address = (UINT32)MultU64x32 (LBA, CardData->BlockIoMedia.BlockSize);
}
TotalBlock = (UINTN) DivU64x32 (BufferSize, CardData->BlockIoMedia.BlockSize);
if (LBA + TotalBlock > CardData->BlockIoMedia.LastBlock + 1) {
return EFI_INVALID_PARAMETER;
}
if (!Buffer) {
Status = EFI_INVALID_PARAMETER;
DEBUG ((EFI_D_ERROR, "MMCSDBlockReadBlocks:Invalid parameter \r\n"));
goto Done;
}
if ((BufferSize % CardData->BlockIoMedia.BlockSize) != 0) {
Status = EFI_BAD_BUFFER_SIZE;
DEBUG ((EFI_D_ERROR, "MMCSDBlockReadBlocks: Bad buffer size \r\n"));
goto Done;
}
if (BufferSize == 0) {
Status = EFI_SUCCESS;
goto Done;
}
BufferPointer = Buffer;
RemainingLength = (UINT32)BufferSize;
while (RemainingLength > 0) {
if ((BufferSize > CardData->BlockIoMedia.BlockSize)) {
if (RemainingLength > SDHostIo->HostCapability.BoundarySize) {
TransferLength = SDHostIo->HostCapability.BoundarySize;
} else {
TransferLength = RemainingLength;
}
if (CardData->CardType == MMCCard || CardData->CardType == MMCCardHighCap) {
if (!(CardData->ExtCSDRegister.CARD_TYPE & (BIT2 | BIT3))) {
Status = SendCommand (
CardData,
SET_BLOCKLEN,
CardData->BlockIoMedia.BlockSize,
NoData,
NULL,
0,
ResponseR1,
TIMEOUT_COMMAND,
(UINT32*)&(CardData->CardStatus)
);
if (EFI_ERROR (Status)) {
break;
}
}
Status = SendCommand (
CardData,
SET_BLOCK_COUNT,
TransferLength / CardData->BlockIoMedia.BlockSize,
NoData,
NULL,
0,
ResponseR1,
TIMEOUT_COMMAND,
(UINT32*)&(CardData->CardStatus)
);
if (EFI_ERROR (Status)) {
break;
}
}
Status = SendCommand (
CardData,
READ_MULTIPLE_BLOCK,
Address,
InData,
CardData->AlignedBuffer,
TransferLength,
ResponseR1,
TIMEOUT_DATA,
(UINT32*)&(CardData->CardStatus)
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "MMCSDBlockReadBlocks: READ_MULTIPLE_BLOCK -> Fail\n"));
break;
}
} else {
if (RemainingLength > CardData->BlockIoMedia.BlockSize) {
TransferLength = CardData->BlockIoMedia.BlockSize;
} else {
TransferLength = RemainingLength;
}
Status = SendCommand (
CardData,
READ_SINGLE_BLOCK,
Address,
InData,
CardData->AlignedBuffer,
(UINT32)TransferLength,
ResponseR1,
TIMEOUT_DATA,
(UINT32*)&(CardData->CardStatus)
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "MMCSDBlockReadBlocks: READ_SINGLE_BLOCK -> Fail\n"));
break;
}
}
CopyMem (BufferPointer, CardData->AlignedBuffer, TransferLength);
if (SectorAddressing) {
//
//Block Address
//
Address += TransferLength / 512;
} else {
//
//Byte Address
//
Address += TransferLength;
}
BufferPointer += TransferLength;
RemainingLength -= TransferLength;
}
if (EFI_ERROR (Status)) {
if ((CardData->CardType == SDMemoryCard) ||
(CardData->CardType == SDMemoryCard2)||
(CardData->CardType == SDMemoryCard2High)) {
SendCommand (
CardData,
STOP_TRANSMISSION,
0,
NoData,
NULL,
0,
ResponseR1b,
TIMEOUT_COMMAND,
(UINT32*)&(CardData->CardStatus)
);
} else {
SendCommand (
CardData,
STOP_TRANSMISSION,
0,
NoData,
NULL,
0,
ResponseR1,
TIMEOUT_COMMAND,
(UINT32*)&(CardData->CardStatus)
);
}
}
Done:
DEBUG((EFI_D_INFO, "MMCSDBlockReadBlocks: Status = %r\n", Status));
return Status;
}
/**
Implements EFI_BLOCK_IO_PROTOCOL.WriteBlocks() function.
@param This The EFI_BLOCK_IO_PROTOCOL instance.
@param MediaId The media id that the write request is for.
@param LBA The starting logical block address to read from on the device.
The caller is responsible for writing to only legitimate locations.
@param BufferSize The size of the Buffer in bytes. This must be a multiple of the
intrinsic block size of the device.
@param Buffer A pointer to the destination buffer for the data. The caller
is responsible for either having implicit or explicit ownership
of the buffer.
@retval EFI_SUCCESS Success
@retval EFI_DEVICE_ERROR Hardware Error
@retval EFI_INVALID_PARAMETER Parameter is error
@retval EFI_NO_MEDIA No media
@retval EFI_MEDIA_CHANGED Media Change
@retval EFI_BAD_BUFFER_SIZE Buffer size is bad
**/
EFI_STATUS
EFIAPI
MMCSDBlockWriteBlocks (
IN EFI_BLOCK_IO_PROTOCOL *This,
IN UINT32 MediaId,
IN EFI_LBA LBA,
IN UINTN BufferSize,
IN VOID *Buffer
)
{
EFI_STATUS Status;
UINT32 Address;
CARD_DATA *CardData;
EFI_SD_HOST_IO_PROTOCOL *SDHostIo;
UINT32 RemainingLength;
UINT32 TransferLength;
UINT8 *BufferPointer;
BOOLEAN SectorAddressing;
DEBUG((EFI_D_INFO, "Write(LBA=%08lx, Buffer=%08x, Size=%08x)\n", LBA, Buffer, BufferSize));
Status = EFI_SUCCESS;
CardData = CARD_DATA_FROM_THIS(This);
SDHostIo = CardData->SDHostIo;
if ((CardData->CardType == SDMemoryCard2High) || (CardData->CardType == MMCCardHighCap)) {
SectorAddressing = TRUE;
} else {
SectorAddressing = FALSE;
}
if (SectorAddressing) {
//
//Block Address
//
Address = (UINT32)DivU64x32 (MultU64x32 (LBA, CardData->BlockIoMedia.BlockSize), 512);
} else {
//
//Byte Address
//
Address = (UINT32)MultU64x32 (LBA, CardData->BlockIoMedia.BlockSize);
}
if (!Buffer) {
Status = EFI_INVALID_PARAMETER;
DEBUG ((EFI_D_ERROR, "MMCSDBlockWriteBlocks: Invalid parameter \r\n"));
goto Done;
}
if ((BufferSize % CardData->BlockIoMedia.BlockSize) != 0) {
Status = EFI_BAD_BUFFER_SIZE;
DEBUG ((EFI_D_ERROR, "MMCSDBlockWriteBlocks: Bad buffer size \r\n"));
goto Done;
}
if (BufferSize == 0) {
Status = EFI_SUCCESS;
goto Done;
}
if (This->Media->ReadOnly == TRUE) {
Status = EFI_WRITE_PROTECTED;
DEBUG ((EFI_D_ERROR, "MMCSDBlockWriteBlocks: Write protected \r\n"));
goto Done;
}
BufferPointer = Buffer;
RemainingLength = (UINT32)BufferSize;
while (RemainingLength > 0) {
if ((BufferSize > CardData->BlockIoMedia.BlockSize) ) {
if (RemainingLength > SDHostIo->HostCapability.BoundarySize) {
TransferLength = SDHostIo->HostCapability.BoundarySize;
} else {
TransferLength = RemainingLength;
}
if (CardData->CardType == MMCCard || CardData->CardType == MMCCardHighCap) {
if (!(CardData->ExtCSDRegister.CARD_TYPE & (BIT2 | BIT3))) {
Status = SendCommand (
CardData,
SET_BLOCKLEN,
CardData->BlockIoMedia.BlockSize,
NoData,
NULL,
0,
ResponseR1,
TIMEOUT_COMMAND,
(UINT32*)&(CardData->CardStatus)
);
if (EFI_ERROR (Status)) {
break;
}
}
Status = SendCommand (
CardData,
SET_BLOCK_COUNT,
TransferLength / CardData->BlockIoMedia.BlockSize,
NoData,
NULL,
0,
ResponseR1,
TIMEOUT_COMMAND,
(UINT32*)&(CardData->CardStatus)
);
if (EFI_ERROR (Status)) {
break;
}
}
CopyMem (CardData->AlignedBuffer, BufferPointer, TransferLength);
Status = SendCommand (
CardData,
WRITE_MULTIPLE_BLOCK,
Address,
OutData,
CardData->AlignedBuffer,
(UINT32)TransferLength,
ResponseR1,
TIMEOUT_DATA,
(UINT32*)&(CardData->CardStatus)
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "MMCSDBlockWriteBlocks: WRITE_MULTIPLE_BLOCK -> Fail\n"));
break;
}
} else {
if (RemainingLength > CardData->BlockIoMedia.BlockSize) {
TransferLength = CardData->BlockIoMedia.BlockSize;
} else {
TransferLength = RemainingLength;
}
CopyMem (CardData->AlignedBuffer, BufferPointer, TransferLength);
Status = SendCommand (
CardData,
WRITE_BLOCK,
Address,
OutData,
CardData->AlignedBuffer,
(UINT32)TransferLength,
ResponseR1,
TIMEOUT_DATA,
(UINT32*)&(CardData->CardStatus)
);
}
if (SectorAddressing) {
//
//Block Address
//
Address += TransferLength / 512;
} else {
//
//Byte Address
//
Address += TransferLength;
}
BufferPointer += TransferLength;
RemainingLength -= TransferLength;
}
if (EFI_ERROR (Status)) {
SendCommand (
CardData,
STOP_TRANSMISSION,
0,
NoData,
NULL,
0,
ResponseR1b,
TIMEOUT_COMMAND,
(UINT32*)&(CardData->CardStatus)
);
}
Done:
return EFI_SUCCESS;
}
/**
Implements EFI_BLOCK_IO_PROTOCOL.FlushBlocks() function.
(In this driver, this function just returns EFI_SUCCESS.)
@param This The EFI_BLOCK_IO_PROTOCOL instance.
@retval EFI_SUCCESS
@retval Others
**/
EFI_STATUS
EFIAPI
MMCSDBlockFlushBlocks (
IN EFI_BLOCK_IO_PROTOCOL *This
)
{
return EFI_SUCCESS;
}
/**
MMC/SD card BlockIo init function.
@param CardData Pointer to CARD_DATA.
@retval EFI_SUCCESS
@retval Others
**/
EFI_STATUS
MMCSDBlockIoInit (
IN CARD_DATA *CardData
)
{
//
//BlockIO protocol
//
CardData->BlockIo.Revision = EFI_BLOCK_IO_PROTOCOL_REVISION;
CardData->BlockIo.Media = &(CardData->BlockIoMedia);
CardData->BlockIo.Reset = MMCSDBlockReset;
CardData->BlockIo.ReadBlocks = MMCSDBlockReadBlocks ;
CardData->BlockIo.WriteBlocks = MMCSDBlockWriteBlocks;
CardData->BlockIo.FlushBlocks = MMCSDBlockFlushBlocks;
CardData->BlockIoMedia.MediaId = 0;
CardData->BlockIoMedia.RemovableMedia = FALSE;
CardData->BlockIoMedia.MediaPresent = TRUE;
CardData->BlockIoMedia.LogicalPartition = FALSE;
if (CardData->CSDRegister.PERM_WRITE_PROTECT || CardData->CSDRegister.TMP_WRITE_PROTECT) {
CardData->BlockIoMedia.ReadOnly = TRUE;
} else {
CardData->BlockIoMedia.ReadOnly = FALSE;
}
CardData->BlockIoMedia.WriteCaching = FALSE;
CardData->BlockIoMedia.BlockSize = CardData->BlockLen;
CardData->BlockIoMedia.IoAlign = 1;
CardData->BlockIoMedia.LastBlock = (EFI_LBA)(CardData->BlockNumber - 1);
return EFI_SUCCESS;
}