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