/** @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; | |
} | |