/** @file
CEATA specific functions implementation
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"
/**
Send RW_MULTIPLE_REGISTER command
@param CardData Pointer to CARD_DATA.
@param Address Register address.
@param ByteCount Buffer size.
@param Write TRUE means write, FALSE means read.
@param Buffer Buffer pointer.
@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
ReadWriteMultipleRegister (
IN CARD_DATA *CardData,
IN UINT16 Address,
IN UINT8 ByteCount,
IN BOOLEAN Write,
IN UINT8 *Buffer
)
{
EFI_STATUS Status;
UINT32 Argument;
Status = EFI_SUCCESS;
if ((Address % 4 != 0) || (ByteCount % 4 != 0)) {
Status = EFI_INVALID_PARAMETER;
goto Exit;
}
Argument = (Address << 16) | ByteCount;
if (Write) {
Argument |= BIT31;
}
if (Write) {
CopyMem (CardData->AlignedBuffer, Buffer, ByteCount);
Status = SendCommand (
CardData,
RW_MULTIPLE_REGISTER,
Argument,
OutData,
CardData->AlignedBuffer,
ByteCount,
ResponseR1b,
TIMEOUT_DATA,
(UINT32*)&(CardData->CardStatus)
);
} else {
Status = SendCommand (
CardData,
RW_MULTIPLE_REGISTER,
Argument,
InData,
CardData->AlignedBuffer,
ByteCount,
ResponseR1,
TIMEOUT_DATA,
(UINT32*)&(CardData->CardStatus)
);
if (!EFI_ERROR (Status)) {
CopyMem (Buffer, CardData->AlignedBuffer, ByteCount);
}
}
Exit:
return Status;
}
/**
Send ReadWriteMultipleBlock command with RW_MULTIPLE_REGISTER command
@param CardData Pointer to CARD_DATA.
@param DataUnitCount Buffer size in 512 bytes unit.
@param Write TRUE means write, FALSE means read.
@param Buffer Buffer pointer.
@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
ReadWriteMultipleBlock (
IN CARD_DATA *CardData,
IN UINT16 DataUnitCount,
IN BOOLEAN Write,
IN UINT8 *Buffer
)
{
EFI_STATUS Status;
EFI_SD_HOST_IO_PROTOCOL *SDHostIo;
UINT32 TransferLength;
Status = EFI_SUCCESS;
SDHostIo = CardData->SDHostIo;
TransferLength = DataUnitCount * DATA_UNIT_SIZE;
if (TransferLength > SDHostIo->HostCapability.BoundarySize) {
return EFI_INVALID_PARAMETER;
}
if (Write) {
CopyMem (CardData->AlignedBuffer, Buffer, TransferLength);
Status = SendCommand (
CardData,
RW_MULTIPLE_BLOCK,
(DataUnitCount | BIT31),
OutData,
CardData->AlignedBuffer,
TransferLength,
ResponseR1b,
TIMEOUT_DATA,
(UINT32*)&(CardData->CardStatus)
);
} else {
Status = SendCommand (
CardData,
RW_MULTIPLE_BLOCK,
DataUnitCount,
InData,
CardData->AlignedBuffer,
TransferLength,
ResponseR1,
TIMEOUT_DATA,
(UINT32*)&(CardData->CardStatus)
);
if (!EFI_ERROR (Status)) {
CopyMem (Buffer, CardData->AlignedBuffer, TransferLength);
}
}
return Status;
}
/**
Send software reset
@param CardData Pointer to CARD_DATA.
@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
SoftwareReset (
IN CARD_DATA *CardData
)
{
EFI_STATUS Status;
UINT8 Data;
UINT32 TimeOut;
Data = BIT2;
Status = FastIO (CardData, Reg_Control, &Data, TRUE);
if (EFI_ERROR (Status)) {
goto Exit;
}
TimeOut = 5 * 1000;
do {
gBS->Stall (1 * 1000);
Status = FastIO (CardData, Reg_Control, &Data, FALSE);
if (EFI_ERROR (Status)) {
goto Exit;
}
if ((Data & BIT2) == BIT2) {
break;
}
TimeOut--;
} while (TimeOut > 0);
if (TimeOut == 0) {
Status = EFI_TIMEOUT;
goto Exit;
}
Data &= ~BIT2;
Status = FastIO (CardData, Reg_Control, &Data, TRUE);
TimeOut = 5 * 1000;
do {
gBS->Stall (1 * 1000);
Status = FastIO (CardData, Reg_Control, &Data, FALSE);
if (EFI_ERROR (Status)) {
goto Exit;
}
if ((Data & BIT2) != BIT2) {
break;
}
TimeOut--;
} while (TimeOut > 0);
if (TimeOut == 0) {
Status = EFI_TIMEOUT;
goto Exit;
}
Exit:
return Status;
}
/**
SendATACommand specificed in Taskfile
@param CardData Pointer to CARD_DATA.
@param TaskFile Pointer to TASK_FILE.
@param Write TRUE means write, FALSE means read.
@param Buffer If NULL, means no data transfer, neither read nor write.
@param SectorCount Buffer size in 512 bytes unit.
@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
SendATACommand (
IN CARD_DATA *CardData,
IN TASK_FILE *TaskFile,
IN BOOLEAN Write,
IN UINT8 *Buffer,
IN UINT16 SectorCount
)
{
EFI_STATUS Status;
UINT8 Data;
UINT32 TimeOut;
//
//Write register
//
Status = ReadWriteMultipleRegister (
CardData,
0,
sizeof (TASK_FILE),
TRUE,
(UINT8*)TaskFile
);
if (EFI_ERROR (Status)) {
DEBUG((EFI_D_ERROR, "ReadWriteMultipleRegister 0x%x\n", Status));
goto Exit;
}
TimeOut = 5000;
do {
gBS->Stall (1 * 1000);
Data = 0;
Status = FastIO (
CardData,
Reg_Command_Status,
&Data,
FALSE
);
if (EFI_ERROR (Status)) {
return Status;
}
if (((Data & BIT7) == 0) && ((Data & BIT6) == BIT6)) {
break;
}
TimeOut --;
} while (TimeOut > 0);
if (TimeOut == 0) {
DEBUG((EFI_D_ERROR, "ReadWriteMultipleRegister FastIO EFI_TIMEOUT 0x%x\n", Data));
Status = EFI_TIMEOUT;
goto Exit;
}
if (Buffer != NULL) {
Status = ReadWriteMultipleBlock (
CardData,
SectorCount,
Write,
(UINT8*)Buffer
);
if (EFI_ERROR (Status)) {
DEBUG((EFI_D_ERROR, "ReadWriteMultipleBlock EFI_TIMEOUT 0x%x\n", Status));
goto Exit;
}
TimeOut = 5 * 1000;
do {
gBS->Stall (1 * 1000);
Data = 0;
Status = FastIO (
CardData,
Reg_Command_Status,
&Data,
FALSE
);
if (EFI_ERROR (Status)) {
return Status;
}
if (((Data & BIT7) == 0) && ((Data & BIT3) == 0)) {
break;
}
TimeOut --;
} while (TimeOut > 0);
if (TimeOut == 0) {
DEBUG((EFI_D_ERROR, "ReadWriteMultipleBlock FastIO EFI_TIMEOUT 0x%x\n", Data));
Status = EFI_TIMEOUT;
goto Exit;
}
if (((Data & BIT6) == BIT6) && (Data & BIT0) == 0) {
Status = EFI_SUCCESS;
} else {
Status = EFI_DEVICE_ERROR;
}
}
Exit:
if (EFI_ERROR (Status)) {
SoftwareReset (CardData);
}
return Status;
}
/**
IDENTIFY_DEVICE command
@param CardData Pointer to CARD_DATA.
@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
IndentifyDevice (
IN CARD_DATA *CardData
)
{
EFI_STATUS Status;
ZeroMem (&CardData->TaskFile, sizeof (TASK_FILE));
//
//The host only supports nIEN = 0
//
CardData->TaskFile.Command_Status = IDENTIFY_DEVICE;
Status = SendATACommand (
CardData,
&CardData->TaskFile,
FALSE,
(UINT8*)&(CardData->IndentifyDeviceData),
1
);
return Status;
}
/**
FLUSH_CACHE_EXT command
@param CardData Pointer to CARD_DATA.
@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
FlushCache (
IN CARD_DATA *CardData
)
{
//
//Hitachi CE-ATA will always make the busy high after
//receving this command
//
/*
EFI_STATUS Status;
ZeroMem (&CardData->TaskFile, sizeof (TASK_FILE));
//
//The host only supports nIEN = 0
//
CardData->TaskFile.Command_Status = FLUSH_CACHE_EXT;
Status = SendATACommand (
CardData,
&CardData->TaskFile,
FALSE,
NULL,
0
);
*/
return EFI_SUCCESS;
}
/**
STANDBY_IMMEDIATE command
@param CardData Pointer to CARD_DATA.
@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
StandByImmediate (
IN CARD_DATA *CardData
)
{
EFI_STATUS Status;
ZeroMem (&CardData->TaskFile, sizeof (TASK_FILE));
//
//The host only supports nIEN = 0
//
CardData->TaskFile.Command_Status = STANDBY_IMMEDIATE;
Status = SendATACommand (
CardData,
&CardData->TaskFile,
FALSE,
NULL,
0
);
return Status;
}
/**
READ_DMA_EXT command
@param CardData Pointer to CARD_DATA.
@param LBA The starting logical block address to read from on 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.
@param SectorCount Size in 512 bytes unit.
@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
ReadDMAExt (
IN CARD_DATA *CardData,
IN EFI_LBA LBA,
IN UINT8 *Buffer,
IN UINT16 SectorCount
)
{
EFI_STATUS Status;
ZeroMem (&CardData->TaskFile, sizeof (TASK_FILE));
//
//The host only supports nIEN = 0
//
CardData->TaskFile.Command_Status = READ_DMA_EXT;
CardData->TaskFile.SectorCount = (UINT8)SectorCount;
CardData->TaskFile.SectorCount_Exp = (UINT8)(SectorCount >> 8);
CardData->TaskFile.LBALow = (UINT8)LBA;
CardData->TaskFile.LBAMid = (UINT8)RShiftU64(LBA, 8);
CardData->TaskFile.LBAHigh = (UINT8)RShiftU64(LBA, 16);
CardData->TaskFile.LBALow_Exp = (UINT8)RShiftU64(LBA, 24);
CardData->TaskFile.LBAMid_Exp = (UINT8)RShiftU64(LBA, 32);
CardData->TaskFile.LBAHigh_Exp = (UINT8)RShiftU64(LBA, 40);
Status = SendATACommand (
CardData,
&CardData->TaskFile,
FALSE,
Buffer,
SectorCount
);
return Status;
}
/**
WRITE_DMA_EXT command
@param CardData Pointer to CARD_DATA.
@param LBA The starting logical block address to read from on 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.
@param SectorCount Size in 512 bytes unit.
@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
WriteDMAExt (
IN CARD_DATA *CardData,
IN EFI_LBA LBA,
IN UINT8 *Buffer,
IN UINT16 SectorCount
)
{
EFI_STATUS Status;
ZeroMem (&CardData->TaskFile, sizeof (TASK_FILE));
//
//The host only supports nIEN = 0
//
CardData->TaskFile.Command_Status = WRITE_DMA_EXT;
CardData->TaskFile.SectorCount = (UINT8)SectorCount;
CardData->TaskFile.SectorCount_Exp = (UINT8)(SectorCount >> 8);
CardData->TaskFile.LBALow = (UINT8)LBA;
CardData->TaskFile.LBAMid = (UINT8)RShiftU64(LBA, 8);
CardData->TaskFile.LBAHigh = (UINT8)RShiftU64(LBA, 16);
CardData->TaskFile.LBALow_Exp = (UINT8)RShiftU64(LBA, 24);
CardData->TaskFile.LBAMid_Exp = (UINT8)RShiftU64(LBA, 32);
CardData->TaskFile.LBAHigh_Exp = (UINT8)RShiftU64(LBA, 40);
Status = SendATACommand (
CardData,
&CardData->TaskFile,
TRUE,
Buffer,
SectorCount
);
return Status;
}
/**
Judge whether it is CE-ATA device or not.
@param CardData Pointer to CARD_DATA.
@retval TRUE
@retval FALSE
**/
BOOLEAN
IsCEATADevice (
IN CARD_DATA *CardData
)
{
EFI_STATUS Status;
Status = ReadWriteMultipleRegister (
CardData,
0,
sizeof (TASK_FILE),
FALSE,
(UINT8*)&CardData->TaskFile
);
if (EFI_ERROR (Status)) {
//
//To bring back the normal MMC card to work
//
CardData->SDHostIo->ResetSDHost (CardData->SDHostIo, Reset_DAT_CMD);
return FALSE;
}
if (CardData->TaskFile.LBAMid == CE_ATA_SIG_CE &&
CardData->TaskFile.LBAHigh == CE_ATA_SIG_AA
) {
//
//Disable Auto CMD for CE-ATA
//
CardData->SDHostIo->EnableAutoStopCmd (CardData->SDHostIo, FALSE);
return TRUE;
}
return FALSE;
}