MdeModulePkg: ATA performance tuning.

1. Boost IDE mode boot when slave device is absent.
2. Use D2H FIS to check if the PIO OUT/DMA IN/DMA OUT transfer is finished or error happened. ATA PIO IN has special check as it may just receive PIO SETUP FIS for success case.
3. Add command status register check for IDE DMA transfer.
4. ScsiDiskReadCapacity() in ScsiDisk should return EFI_SUCCESS if SENSE Data request is success.

Signed-off-by: erictian
Reviewed-by: rsun3
Reviewed-by: xdu2
Reviewed-by: mdkinney

git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@12658 6f19259b-4bc3-4df7-8a09-765794883524
This commit is contained in:
erictian
2011-11-03 12:38:21 +00:00
parent 9c9f585937
commit 8536cc4b37
6 changed files with 346 additions and 339 deletions

View File

@ -133,24 +133,23 @@ AhciOrReg (
}
/**
Wait for memory set to the test value.
Wait for the value of the specified MMIO register set to the test value.
@param PciIo The PCI IO protocol instance.
@param Offset The memory address to test.
@param Offset The MMIO address to test.
@param MaskValue The mask value of memory.
@param TestValue The test value of memory.
@param Timeout The time out value for wait memory set.
@param Timeout The time out value for wait memory set, uses 100ns as a unit.
@retval EFI_DEVICE_ERROR The memory is not set.
@retval EFI_TIMEOUT The memory setting is time out.
@retval EFI_SUCCESS The memory is correct set.
@retval EFI_TIMEOUT The MMIO setting is time out.
@retval EFI_SUCCESS The MMIO is correct set.
**/
EFI_STATUS
EFIAPI
AhciWaitMemSet (
AhciWaitMmioSet (
IN EFI_PCI_IO_PROTOCOL *PciIo,
IN UINT32 Offset,
IN UINTN Offset,
IN UINT32 MaskValue,
IN UINT32 TestValue,
IN UINT64 Timeout
@ -159,10 +158,13 @@ AhciWaitMemSet (
UINT32 Value;
UINT32 Delay;
Delay = (UINT32) (DivU64x32(Timeout, 1000) + 1);
Delay = (UINT32) (DivU64x32 (Timeout, 1000) + 1);
do {
Value = AhciReadReg (PciIo, Offset) & MaskValue;
//
// Access PCI MMIO space to see if the value is the tested one.
//
Value = AhciReadReg (PciIo, (UINT32) Offset) & MaskValue;
if (Value == TestValue) {
return EFI_SUCCESS;
@ -177,21 +179,70 @@ AhciWaitMemSet (
} while (Delay > 0);
if (Delay == 0) {
return EFI_TIMEOUT;
}
return EFI_TIMEOUT;
}
return EFI_DEVICE_ERROR;
/**
Wait for the value of the specified system memory set to the test value.
@param Address The system memory address to test.
@param MaskValue The mask value of memory.
@param TestValue The test value of memory.
@param Timeout The time out value for wait memory set, uses 100ns as a unit.
@retval EFI_TIMEOUT The system memory setting is time out.
@retval EFI_SUCCESS The system memory is correct set.
**/
EFI_STATUS
EFIAPI
AhciWaitMemSet (
IN EFI_PHYSICAL_ADDRESS Address,
IN UINT32 MaskValue,
IN UINT32 TestValue,
IN UINT64 Timeout
)
{
UINT32 Value;
UINT32 Delay;
Delay = (UINT32) (DivU64x32 (Timeout, 1000) + 1);
do {
//
// Access sytem memory to see if the value is the tested one.
//
// The system memory pointed by Address will be updated by the
// SATA Host Controller, "volatile" is introduced to prevent
// compiler from optimizing the access to the memory address
// to only read once.
//
Value = *(volatile UINT32 *) (UINTN) Address;
Value &= MaskValue;
if (Value == TestValue) {
return EFI_SUCCESS;
}
//
// Stall for 100 microseconds.
//
MicroSecondDelay (100);
Delay--;
} while (Delay > 0);
return EFI_TIMEOUT;
}
/**
Check the memory status to the test value.
@param[in] PciIo The PCI IO protocol instance.
@param[in] Offset The memory address to test.
@param[in] Address The memory address to test.
@param[in] MaskValue The mask value of memory.
@param[in] TestValue The test value of memory.
@param[in, out] RetryTimes The retry times value for waitting memory set.
@param[in, out] RetryTimes The retry times value for waitting memory set. If 0, then just try once.
@retval EFI_NOTREADY The memory is not set.
@retval EFI_TIMEOUT The memory setting retry times out.
@ -201,24 +252,26 @@ AhciWaitMemSet (
EFI_STATUS
EFIAPI
AhciCheckMemSet (
IN EFI_PCI_IO_PROTOCOL *PciIo,
IN UINT32 Offset,
IN UINTN Address,
IN UINT32 MaskValue,
IN UINT32 TestValue,
IN OUT UINTN *RetryTimes
IN OUT UINTN *RetryTimes OPTIONAL
)
{
UINT32 Value;
(*RetryTimes) --;
Value = AhciReadReg (PciIo, Offset) & MaskValue;
if (RetryTimes != NULL) {
(*RetryTimes)--;
}
Value = *(volatile UINT32 *) Address;
Value &= MaskValue;
if (Value == TestValue) {
return EFI_SUCCESS;
}
if ((*RetryTimes) == 0) {
if ((RetryTimes != NULL) && (*RetryTimes == 0)) {
return EFI_TIMEOUT;
} else {
return EFI_NOT_READY;
@ -338,7 +391,7 @@ AhciDumpPortStatus (
@param PciIo The PCI IO protocol instance.
@param Port The number of port.
@param Timeout The timeout value of enabling FIS.
@param Timeout The timeout value of enabling FIS, uses 100ns as a unit.
@retval EFI_DEVICE_ERROR The FIS enable setting fails.
@retval EFI_TIMEOUT The FIS enable setting is time out.
@ -358,7 +411,7 @@ AhciEnableFisReceive (
Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;
AhciOrReg (PciIo, Offset, EFI_AHCI_PORT_CMD_FRE);
return AhciWaitMemSet (
return AhciWaitMmioSet (
PciIo,
Offset,
EFI_AHCI_PORT_CMD_FR,
@ -372,7 +425,7 @@ AhciEnableFisReceive (
@param PciIo The PCI IO protocol instance.
@param Port The number of port.
@param Timeout The timeout value of disabling FIS.
@param Timeout The timeout value of disabling FIS, uses 100ns as a unit.
@retval EFI_DEVICE_ERROR The FIS disable setting fails.
@retval EFI_TIMEOUT The FIS disable setting is time out.
@ -410,7 +463,7 @@ AhciDisableFisReceive (
AhciAndReg (PciIo, Offset, (UINT32)~(EFI_AHCI_PORT_CMD_FRE));
return AhciWaitMemSet (
return AhciWaitMmioSet (
PciIo,
Offset,
EFI_AHCI_PORT_CMD_FR,
@ -596,7 +649,7 @@ AhciBuildCommandFis (
@param[in, out] AtaStatusBlock The EFI_ATA_STATUS_BLOCK data.
@param[in, out] MemoryAddr The pointer to the data buffer.
@param[in] DataCount The data count to be transferred.
@param[in] Timeout The timeout value of non data transfer.
@param[in] Timeout The timeout value of non data transfer, uses 100ns as a unit.
@param[in] Task Optional. Pointer to the ATA_NONBLOCK_TASK
used by non-blocking mode.
@ -626,15 +679,16 @@ AhciPioTransfer (
{
EFI_STATUS Status;
UINTN FisBaseAddr;
UINT32 Offset;
UINT32 Value;
UINTN Offset;
EFI_PHYSICAL_ADDRESS PhyAddr;
VOID *Map;
UINTN MapLength;
EFI_PCI_IO_PROTOCOL_OPERATION Flag;
UINT32 Delay;
EFI_AHCI_COMMAND_FIS CFis;
EFI_AHCI_COMMAND_LIST CmdList;
EFI_AHCI_COMMAND_LIST CmdList;
UINT32 PortTfd;
UINT32 PrdCount;
if (Read) {
Flag = EfiPciIoOperationBusMasterWrite;
@ -697,56 +751,66 @@ AhciPioTransfer (
// Check the status and wait the driver sending data
//
FisBaseAddr = (UINTN)AhciRegisters->AhciRFis + Port * sizeof (EFI_AHCI_RECEIVED_FIS);
//
// Wait device sends the PIO setup fis before data transfer
//
Delay = (UINT32) (DivU64x32(Timeout, 1000) + 1);
do {
Value = *(volatile UINT32 *) (FisBaseAddr + EFI_AHCI_PIO_FIS_OFFSET);
if ((Value & EFI_AHCI_FIS_TYPE_MASK) == EFI_AHCI_FIS_PIO_SETUP) {
break;
if (Read && (AtapiCommand == 0)) {
//
// Wait device sends the PIO setup fis before data transfer
//
Status = EFI_TIMEOUT;
Delay = (UINT32) (DivU64x32 (Timeout, 1000) + 1);
do {
Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;
PortTfd = AhciReadReg (PciIo, (UINT32) Offset);
if ((PortTfd & EFI_AHCI_PORT_TFD_ERR) != 0) {
Status = EFI_DEVICE_ERROR;
break;
}
Offset = FisBaseAddr + EFI_AHCI_PIO_FIS_OFFSET;
Status = AhciCheckMemSet (Offset, EFI_AHCI_FIS_TYPE_MASK, EFI_AHCI_FIS_PIO_SETUP, 0);
if (!EFI_ERROR (Status)) {
PrdCount = *(volatile UINT32 *) (&(AhciRegisters->AhciCmdList[0].AhciCmdPrdbc));
if (PrdCount == DataCount) {
break;
}
}
Offset = FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET;
Status = AhciCheckMemSet (Offset, EFI_AHCI_FIS_TYPE_MASK, EFI_AHCI_FIS_REGISTER_D2H, 0);
if (!EFI_ERROR (Status)) {
Status = EFI_DEVICE_ERROR;
break;
}
//
// Stall for 100 microseconds.
//
MicroSecondDelay(100);
Delay--;
} while (Delay > 0);
} else {
//
// Wait for D2H Fis is received
//
Offset = FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET;
Status = AhciWaitMemSet (
Offset,
EFI_AHCI_FIS_TYPE_MASK,
EFI_AHCI_FIS_REGISTER_D2H,
Timeout
);
if (EFI_ERROR (Status)) {
goto Exit;
}
//
// Stall for 100 microseconds.
//
MicroSecondDelay(100);
Delay--;
} while (Delay > 0);
if (Delay == 0) {
Status = EFI_TIMEOUT;
goto Exit;
}
//
// Wait for command compelte
//
Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CI;
Status = AhciWaitMemSet (
PciIo,
Offset,
0xFFFFFFFF,
0,
Timeout
);
if (EFI_ERROR (Status)) {
goto Exit;
}
Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_IS;
Status = AhciWaitMemSet (
PciIo,
Offset,
EFI_AHCI_PORT_IS_PSS,
EFI_AHCI_PORT_IS_PSS,
Timeout
);
if (EFI_ERROR (Status)) {
goto Exit;
Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;
PortTfd = AhciReadReg (PciIo, (UINT32) Offset);
if ((PortTfd & EFI_AHCI_PORT_TFD_ERR) != 0) {
Status = EFI_DEVICE_ERROR;
}
}
Exit:
@ -787,7 +851,7 @@ Exit:
@param[in, out] AtaStatusBlock The EFI_ATA_STATUS_BLOCK data.
@param[in, out] MemoryAddr The pointer to the data buffer.
@param[in] DataCount The data count to be transferred.
@param[in] Timeout The timeout value of non data transfer.
@param[in] Timeout The timeout value of non data transfer, uses 100ns as a unit.
@param[in] Task Optional. Pointer to the ATA_NONBLOCK_TASK
used by non-blocking mode.
@ -810,22 +874,24 @@ AhciDmaTransfer (
IN EFI_ATA_COMMAND_BLOCK *AtaCommandBlock,
IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock,
IN OUT VOID *MemoryAddr,
IN UINTN DataCount,
IN UINT32 DataCount,
IN UINT64 Timeout,
IN ATA_NONBLOCK_TASK *Task
)
{
EFI_STATUS Status;
UINT32 Offset;
UINTN Offset;
EFI_PHYSICAL_ADDRESS PhyAddr;
VOID *Map;
UINTN MapLength;
EFI_PCI_IO_PROTOCOL_OPERATION Flag;
EFI_AHCI_COMMAND_FIS CFis;
EFI_AHCI_COMMAND_LIST CmdList;
UINTN FisBaseAddr;
UINT32 PortTfd;
EFI_PCI_IO_PROTOCOL *PciIo;
EFI_TPL OldTpl;
EFI_PCI_IO_PROTOCOL *PciIo;
EFI_TPL OldTpl;
Map = NULL;
PciIo = Instance->PciIo;
@ -916,45 +982,28 @@ AhciDmaTransfer (
if (EFI_ERROR (Status)) {
goto Exit;
}
//
// Wait device PRD processed
//
Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_IS;
Status = AhciWaitMemSet (
PciIo,
Offset,
EFI_AHCI_PORT_IS_DPS,
EFI_AHCI_PORT_IS_DPS,
Timeout
);
if (EFI_ERROR (Status)) {
goto Exit;
}
}
//
// Wait for command compelte
//
Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CI;
FisBaseAddr = (UINTN)AhciRegisters->AhciRFis + Port * sizeof (EFI_AHCI_RECEIVED_FIS);
Offset = FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET;
if (Task != NULL) {
//
// For Non-blocking
//
Status = AhciCheckMemSet (
PciIo,
Offset,
0xFFFFFFFF,
0,
EFI_AHCI_FIS_TYPE_MASK,
EFI_AHCI_FIS_REGISTER_D2H,
(UINTN *) (&Task->RetryTimes)
);
} else {
Status = AhciWaitMemSet (
PciIo,
Offset,
0xFFFFFFFF,
0,
EFI_AHCI_FIS_TYPE_MASK,
EFI_AHCI_FIS_REGISTER_D2H,
Timeout
);
}
@ -963,31 +1012,10 @@ AhciDmaTransfer (
goto Exit;
}
Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_IS;
if (Task != NULL) {
//
// For Non-blocking
//
Status = AhciCheckMemSet (
PciIo,
Offset,
EFI_AHCI_PORT_IS_DHRS,
EFI_AHCI_PORT_IS_DHRS,
(UINTN *) (&Task->RetryTimes)
);
} else {
Status = AhciWaitMemSet (
PciIo,
Offset,
EFI_AHCI_PORT_IS_DHRS,
EFI_AHCI_PORT_IS_DHRS,
Timeout
);
}
if (EFI_ERROR (Status)) {
goto Exit;
Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;
PortTfd = AhciReadReg (PciIo, (UINT32) Offset);
if ((PortTfd & EFI_AHCI_PORT_TFD_ERR) != 0) {
Status = EFI_DEVICE_ERROR;
}
Exit:
@ -1039,7 +1067,7 @@ Exit:
@param[in] AtapiCommandLength The length of the atapi command.
@param[in] AtaCommandBlock The EFI_ATA_COMMAND_BLOCK data.
@param[in, out] AtaStatusBlock The EFI_ATA_STATUS_BLOCK data.
@param[in] Timeout The timeout value of non data transfer.
@param[in] Timeout The timeout value of non data transfer, uses 100ns as a unit.
@param[in] Task Optional. Pointer to the ATA_NONBLOCK_TASK
used by non-blocking mode.
@ -1066,10 +1094,8 @@ AhciNonDataTransfer (
{
EFI_STATUS Status;
UINTN FisBaseAddr;
UINT32 Offset;
UINT32 Value;
UINT32 Delay;
UINTN Offset;
UINT32 PortTfd;
EFI_AHCI_COMMAND_FIS CFis;
EFI_AHCI_COMMAND_LIST CmdList;
@ -1110,39 +1136,23 @@ AhciNonDataTransfer (
// Wait device sends the Response Fis
//
FisBaseAddr = (UINTN)AhciRegisters->AhciRFis + Port * sizeof (EFI_AHCI_RECEIVED_FIS);
//
// Wait device sends the PIO setup fis before data transfer
//
Delay = (UINT32) (DivU64x32(Timeout, 1000) + 1);
do {
Value = *(volatile UINT32 *) (FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET);
Offset = FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET;
Status = AhciWaitMemSet (
Offset,
EFI_AHCI_FIS_TYPE_MASK,
EFI_AHCI_FIS_REGISTER_D2H,
Timeout
);
if ((Value & EFI_AHCI_FIS_TYPE_MASK) == EFI_AHCI_FIS_REGISTER_D2H) {
break;
}
//
// Stall for 100 microseconds.
//
MicroSecondDelay(100);
Delay --;
} while (Delay > 0);
if (Delay == 0) {
Status = EFI_TIMEOUT;
if (EFI_ERROR (Status)) {
goto Exit;
}
Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CI;
Status = AhciWaitMemSet (
PciIo,
Offset,
0xFFFFFFFF,
0,
Timeout
);
Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;
PortTfd = AhciReadReg (PciIo, (UINT32) Offset);
if ((PortTfd & EFI_AHCI_PORT_TFD_ERR) != 0) {
Status = EFI_DEVICE_ERROR;
}
Exit:
AhciStopCommand (
@ -1167,7 +1177,7 @@ Exit:
@param PciIo The PCI IO protocol instance.
@param Port The number of port.
@param Timeout The timeout value of stop.
@param Timeout The timeout value of stop, uses 100ns as a unit.
@retval EFI_DEVICE_ERROR The command stop unsuccessfully.
@retval EFI_TIMEOUT The operation is time out.
@ -1196,7 +1206,7 @@ AhciStopCommand (
AhciAndReg (PciIo, Offset, (UINT32)~(EFI_AHCI_PORT_CMD_ST));
}
return AhciWaitMemSet (
return AhciWaitMmioSet (
PciIo,
Offset,
EFI_AHCI_PORT_CMD_CR,
@ -1211,7 +1221,7 @@ AhciStopCommand (
@param PciIo The PCI IO protocol instance.
@param Port The number of port.
@param CommandSlot The number of Command Slot.
@param Timeout The timeout value of start.
@param Timeout The timeout value of start, uses 100ns as a unit.
@retval EFI_DEVICE_ERROR The command start unsuccessfully.
@retval EFI_TIMEOUT The operation is time out.
@ -1275,7 +1285,7 @@ AhciStartCommand (
Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;
AhciOrReg (PciIo, Offset, EFI_AHCI_PORT_CMD_CLO);
AhciWaitMemSet (
AhciWaitMmioSet (
PciIo,
Offset,
EFI_AHCI_PORT_CMD_CLO,
@ -1307,7 +1317,7 @@ AhciStartCommand (
@param PciIo The PCI IO protocol instance.
@param Port The number of port.
@param Timeout The timeout value of reset.
@param Timeout The timeout value of reset, uses 100ns as a unit.
@retval EFI_DEVICE_ERROR The port reset unsuccessfully
@retval EFI_TIMEOUT The reset operation is time out.
@ -1353,7 +1363,7 @@ AhciPortReset (
// Wait for communication to be re-established
//
Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SSTS;
Status = AhciWaitMemSet (
Status = AhciWaitMmioSet (
PciIo,
Offset,
EFI_AHCI_PORT_SSTS_DET_MASK,
@ -1375,7 +1385,7 @@ AhciPortReset (
Do AHCI HBA reset.
@param PciIo The PCI IO protocol instance.
@param Timeout The timeout value of reset.
@param Timeout The timeout value of reset, uses 100ns as a unit.
@retval EFI_DEVICE_ERROR AHCI controller is failed to complete hardware reset.
@retval EFI_TIMEOUT The reset operation is time out.
@ -2238,7 +2248,7 @@ AhciModeInitialization (
//
Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;
AhciOrReg (PciIo, Offset, EFI_AHCI_PORT_CMD_FRE);
Status = AhciWaitMemSet (
Status = AhciWaitMmioSet (
PciIo,
Offset,
EFI_AHCI_PORT_CMD_FR,
@ -2301,7 +2311,7 @@ AhciModeInitialization (
// When the first D2H register FIS is received, the content of PxSIG register is updated.
//
Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SIG;
Status = AhciWaitMemSet (
Status = AhciWaitMmioSet (
PciIo,
Offset,
0x0000FFFF,