UINT32 PortTfd;\r
UINT32 PrdCount;\r
BOOLEAN InfiniteWait;\r
+ BOOLEAN PioFisReceived;\r
+ BOOLEAN D2hFisReceived;\r
\r
if (Timeout == 0) {\r
InfiniteWait = TRUE;\r
Status = EFI_TIMEOUT;\r
Delay = DivU64x32 (Timeout, 1000) + 1;\r
do {\r
+ PioFisReceived = FALSE;\r
+ D2hFisReceived = FALSE;\r
Offset = FisBaseAddr + EFI_AHCI_PIO_FIS_OFFSET;\r
-\r
Status = AhciCheckMemSet (Offset, EFI_AHCI_FIS_TYPE_MASK, EFI_AHCI_FIS_PIO_SETUP, NULL);\r
if (!EFI_ERROR (Status)) {\r
+ PioFisReceived = TRUE;\r
+ }\r
+ //\r
+ // According to SATA 2.6 spec section 11.7, D2h FIS means an error encountered.\r
+ // But Qemu and Marvel 9230 sata controller may just receive a D2h FIS from device\r
+ // after the transaction is finished successfully.\r
+ // To get better device compatibilities, we further check if the PxTFD's ERR bit is set.\r
+ // By this way, we can know if there is a real error happened.\r
+ //\r
+ Offset = FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET;\r
+ Status = AhciCheckMemSet (Offset, EFI_AHCI_FIS_TYPE_MASK, EFI_AHCI_FIS_REGISTER_D2H, NULL);\r
+ if (!EFI_ERROR (Status)) {\r
+ D2hFisReceived = TRUE;\r
+ }\r
+\r
+ if (PioFisReceived || D2hFisReceived) {\r
Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;\r
PortTfd = AhciReadReg (PciIo, (UINT32) Offset);\r
//\r
// PxTFD will be updated if there is a D2H or SetupFIS received. \r
- // For PIO IN transfer, D2H means a device error. Therefore we only need to check the TFD after receiving a SetupFIS.\r
//\r
if ((PortTfd & EFI_AHCI_PORT_TFD_ERR) != 0) {\r
Status = EFI_DEVICE_ERROR;\r
\r
PrdCount = *(volatile UINT32 *) (&(AhciRegisters->AhciCmdList[0].AhciCmdPrdbc));\r
if (PrdCount == DataCount) {\r
+ Status = EFI_SUCCESS;\r
break;\r
}\r
}\r
\r
- Offset = FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET;\r
- Status = AhciCheckMemSet (Offset, EFI_AHCI_FIS_TYPE_MASK, EFI_AHCI_FIS_REGISTER_D2H, NULL);\r
- if (!EFI_ERROR (Status)) {\r
- Status = EFI_DEVICE_ERROR;\r
- break;\r
- }\r
-\r
//\r
// Stall for 100 microseconds.\r
//\r
MicroSecondDelay(100);\r
\r
Delay--;\r
+ if (Delay == 0) {\r
+ Status = EFI_TIMEOUT;\r
+ }\r
} while (InfiniteWait || (Delay > 0));\r
} else {\r
//\r