/** @file
Block I/O protocol for CE-ATA 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
CEATABlockReset (
IN EFI_BLOCK_IO_PROTOCOL *This,
IN BOOLEAN ExtendedVerification
)
{
EFI_STATUS Status;
CARD_DATA *CardData;
EFI_SD_HOST_IO_PROTOCOL *SDHostIo;
CardData = CARD_DATA_FROM_THIS(This);
SDHostIo = CardData->SDHostIo;
if (!ExtendedVerification) {
Status = SoftwareReset (CardData);
} else {
Status = SDHostIo->ResetSDHost (SDHostIo, Reset_DAT_CMD);
if (EFI_ERROR (Status)) {
DEBUG((EFI_D_ERROR, "CEATABlockReset: Fail to ResetSDHost\n" ));
return Status;
}
Status = MMCSDCardInit (CardData);
}
return Status;
}
/**
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
CEATABlockReadBlocks (
IN EFI_BLOCK_IO_PROTOCOL *This,
IN UINT32 MediaId,
IN EFI_LBA LBA,
IN UINTN BufferSize,
OUT VOID *Buffer
)
{
EFI_STATUS Status;
CARD_DATA *CardData;
UINT32 TransferSize;
UINT8 *pBuf;
UINT32 Index;
UINT64 Address;
UINT32 Remainder;
UINT64 CEATALBA;
UINT32 BoundarySize;
Status = EFI_SUCCESS;
CardData = CARD_DATA_FROM_THIS(This);
pBuf = Buffer;
Index = 0;
Address = MultU64x32(LBA, CardData->BlockIoMedia.BlockSize);
BoundarySize = CardData->SDHostIo->HostCapability.BoundarySize;
if (!Buffer) {
Status = EFI_INVALID_PARAMETER;
DEBUG((EFI_D_ERROR, "CEATABlockReadBlocks:Invalid parameter\n" ));
goto Exit;
}
if (MediaId != CardData->BlockIoMedia.MediaId) {
Status = EFI_MEDIA_CHANGED;
DEBUG((EFI_D_ERROR, "CEATABlockReadBlocks:Media changed\n" ));
goto Exit;
}
if ((BufferSize % CardData->BlockIoMedia.BlockSize) != 0) {
Status = EFI_BAD_BUFFER_SIZE;
DEBUG((EFI_D_ERROR, "CEATABlockReadBlocks:Bad buffer size\n" ));
goto Exit;
}
if (BufferSize == 0) {
Status = EFI_SUCCESS;
goto Exit;
}
if ((Address + BufferSize) > MultU64x32 (CardData->BlockIoMedia.LastBlock + 1, CardData->BlockIoMedia.BlockSize)) {
Status = EFI_INVALID_PARAMETER;
DEBUG((EFI_D_ERROR, "CEATABlockReadBlocks:Invalid parameter\n" ));
goto Exit;
}
do {
if (BufferSize < BoundarySize) {
TransferSize = (UINT32)BufferSize;
} else {
TransferSize = BoundarySize;
}
Address += Index * TransferSize;
CEATALBA = DivU64x32Remainder (Address, DATA_UNIT_SIZE, &Remainder);
ASSERT(Remainder == 0);
Status = ReadDMAExt (
CardData,
CEATALBA,
pBuf,
(UINT16)(TransferSize / DATA_UNIT_SIZE)
);
if (EFI_ERROR (Status)) {
DEBUG((EFI_D_ERROR, "Read Failed at 0x%x, Index %d, Size 0x%x\n", Address, Index, TransferSize));
This->Reset (This, TRUE);
goto Exit;
}
BufferSize -= TransferSize;
pBuf += TransferSize;
Index ++;
} while (BufferSize != 0);
Exit:
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
CEATABlockWriteBlocks (
IN EFI_BLOCK_IO_PROTOCOL *This,
IN UINT32 MediaId,
IN EFI_LBA LBA,
IN UINTN BufferSize,
IN VOID *Buffer
)
{
EFI_STATUS Status;
CARD_DATA *CardData;
UINT32 TransferSize;
UINT8 *pBuf;
UINT32 Index;
UINT64 Address;
UINT32 Remainder;
UINT64 CEATALBA;
UINT32 BoundarySize;
Status = EFI_SUCCESS;
CardData = CARD_DATA_FROM_THIS(This);
pBuf = Buffer;
Index = 0;
Address = MultU64x32(LBA, CardData->BlockIoMedia.BlockSize);
BoundarySize = CardData->SDHostIo->HostCapability.BoundarySize;
if (!Buffer) {
Status = EFI_INVALID_PARAMETER;
goto Exit;
}
if (MediaId != CardData->BlockIoMedia.MediaId) {
Status = EFI_MEDIA_CHANGED;
goto Exit;
}
if ((BufferSize % CardData->BlockIoMedia.BlockSize) != 0) {
Status = EFI_BAD_BUFFER_SIZE;
goto Exit;
}
if (BufferSize == 0) {
Status = EFI_SUCCESS;
goto Exit;
}
if (CardData->BlockIoMedia.ReadOnly) {
Status = EFI_WRITE_PROTECTED;
goto Exit;
}
if ((Address + BufferSize) > MultU64x32 (CardData->BlockIoMedia.LastBlock + 1, CardData->BlockIoMedia.BlockSize)) {
Status = EFI_INVALID_PARAMETER;
goto Exit;
}
CardData->NeedFlush = TRUE;
do {
if (BufferSize < BoundarySize) {
TransferSize = (UINT32)BufferSize;
} else {
TransferSize = BoundarySize;
}
Address += Index * TransferSize;
CEATALBA = DivU64x32Remainder (Address, DATA_UNIT_SIZE, &Remainder);
ASSERT(Remainder == 0);
Status = WriteDMAExt (
CardData,
CEATALBA,
pBuf,
(UINT16)(TransferSize / DATA_UNIT_SIZE)
);
if (EFI_ERROR (Status)) {
DEBUG((EFI_D_ERROR, "Write Failed at 0x%x, Index %d, Size 0x%x\n", Address, Index, TransferSize));
This->Reset (This, TRUE);
goto Exit;
}
BufferSize -= TransferSize;
pBuf += TransferSize;
Index ++;
} while (BufferSize != 0);
Exit:
return Status;
}
/**
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
CEATABlockFlushBlocks (
IN EFI_BLOCK_IO_PROTOCOL *This
)
{
CARD_DATA *CardData;
CardData = CARD_DATA_FROM_THIS(This);
if (CardData->NeedFlush) {
CardData->NeedFlush = FALSE;
FlushCache (CardData);
}
return EFI_SUCCESS;
}
/**
CEATA card BlockIo init function.
@param CardData Pointer to CARD_DATA.
@retval EFI_SUCCESS
@retval Others
**/
EFI_STATUS
CEATABlockIoInit (
IN CARD_DATA *CardData
)
/*++
Routine Description:
CEATA card BlockIo init function
Arguments:
CardData - Pointer to CARD_DATA
Returns:
EFI_SUCCESS - Success
--*/
{
EFI_STATUS Status;
UINT64 MaxSize;
UINT32 Remainder;
//
//BlockIO protocol
//
CardData->BlockIo.Revision = EFI_BLOCK_IO_PROTOCOL_REVISION;
CardData->BlockIo.Media = &(CardData->BlockIoMedia);
CardData->BlockIo.Reset = CEATABlockReset;
CardData->BlockIo.ReadBlocks = CEATABlockReadBlocks ;
CardData->BlockIo.WriteBlocks = CEATABlockWriteBlocks;
CardData->BlockIo.FlushBlocks = CEATABlockFlushBlocks;
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.IoAlign = 1;
Status = IndentifyDevice (CardData);
if (EFI_ERROR (Status)) {
goto Exit;
}
//
//Some device does not support this feature
//
if (CardData->IndentifyDeviceData.MaxWritesPerAddress == 0) {
CardData->BlockIoMedia.ReadOnly = TRUE;
}
CardData->BlockIoMedia.BlockSize = (1 << CardData->IndentifyDeviceData.Sectorsize);
ASSERT(CardData->BlockIoMedia.BlockSize >= 12);
MaxSize = *(UINT64*)(CardData->IndentifyDeviceData.MaximumLBA);
MaxSize = MultU64x32 (MaxSize, 512);
Remainder = 0;
CardData->BlockNumber = DivU64x32Remainder (MaxSize, CardData->BlockIoMedia.BlockSize, &Remainder);
ASSERT(Remainder == 0);
CardData->BlockIoMedia.LastBlock = (EFI_LBA)(CardData->BlockNumber - 1);
Exit:
return Status;
}