Status = AhciResetPort (PciIo, Port);\r
if (EFI_ERROR (Status)) {\r
DEBUG ((DEBUG_ERROR, "Failed to reset the port %d\n", Port));\r
+ return EFI_DEVICE_ERROR;\r
}\r
}\r
\r
return EFI_SUCCESS;\r
}\r
\r
+/**\r
+ This function will check if the failed command should be retired. Only error\r
+ conditions which are a result of transient conditions on a link(either to system or to device).\r
+\r
+ @param[in] PciIo Pointer to AHCI controller PciIo.\r
+ @param[in] Port SATA port index on which to check.\r
+\r
+ @retval TRUE Command failure was caused by transient condition and should be retried\r
+ @retval FALSE Command should not be retried\r
+**/\r
+BOOLEAN\r
+AhciShouldCmdBeRetried (\r
+ IN EFI_PCI_IO_PROTOCOL *PciIo,\r
+ IN UINT8 Port\r
+ )\r
+{\r
+ UINT32 Offset;\r
+ UINT32 PortInterrupt;\r
+ UINT32 Serr;\r
+ UINT32 Tfd;\r
+\r
+ Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_IS;\r
+ PortInterrupt = AhciReadReg (PciIo, Offset);\r
+ Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SERR;\r
+ Serr = AhciReadReg (PciIo, Offset);\r
+ Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;\r
+ Tfd = AhciReadReg (PciIo, Offset);\r
+\r
+ //\r
+ // This can occur if there was a CRC error on a path from system memory to\r
+ // host controller.\r
+ //\r
+ if (PortInterrupt & EFI_AHCI_PORT_IS_HBDS) {\r
+ return TRUE;\r
+ //\r
+ // This can occur if there was a CRC error detected by host during communication\r
+ // with the device\r
+ //\r
+ } else if ((PortInterrupt & (EFI_AHCI_PORT_IS_IFS | EFI_AHCI_PORT_IS_INFS)) &&\r
+ (Serr & EFI_AHCI_PORT_SERR_CRCE))\r
+ {\r
+ return TRUE;\r
+ //\r
+ // This can occur if there was a CRC error detected by device during communication\r
+ // with the host. Device returns error status to host with D2H FIS.\r
+ //\r
+ } else if ((PortInterrupt & EFI_AHCI_PORT_IS_TFES) &&\r
+ (Tfd & EFI_AHCI_PORT_TFD_ERR_INT_CRC))\r
+ {\r
+ return TRUE;\r
+ }\r
+\r
+ return FALSE;\r
+}\r
+\r
/**\r
Checks if specified FIS has been received.\r
\r
UINT32 PrdCount;\r
UINT32 Retry;\r
EFI_STATUS RecoveryStatus;\r
+ BOOLEAN DoRetry;\r
\r
if (Read) {\r
Flag = EfiPciIoOperationBusMasterWrite;\r
\r
if (Status == EFI_DEVICE_ERROR) {\r
DEBUG ((DEBUG_ERROR, "PIO command failed at retry %d\n", Retry));\r
+ DoRetry = AhciShouldCmdBeRetried (PciIo, Port); // needs to be called before error recovery\r
RecoveryStatus = AhciRecoverPortError (PciIo, Port);\r
- if (EFI_ERROR (RecoveryStatus)) {\r
+ if (!DoRetry || EFI_ERROR (RecoveryStatus)) {\r
break;\r
}\r
} else {\r
EFI_TPL OldTpl;\r
UINT32 Retry;\r
EFI_STATUS RecoveryStatus;\r
+ BOOLEAN DoRetry;\r
\r
Map = NULL;\r
PciIo = Instance->PciIo;\r
Status = AhciWaitUntilFisReceived (PciIo, Port, Timeout, SataFisD2H);\r
if (Status == EFI_DEVICE_ERROR) {\r
DEBUG ((DEBUG_ERROR, "DMA command failed at retry: %d\n", Retry));\r
+ DoRetry = AhciShouldCmdBeRetried (PciIo, Port); // needs to be called before error recovery\r
RecoveryStatus = AhciRecoverPortError (PciIo, Port);\r
- if (EFI_ERROR (RecoveryStatus)) {\r
+ if (!DoRetry || EFI_ERROR (RecoveryStatus)) {\r
break;\r
}\r
} else {\r
Status = AhciCheckFisReceived (PciIo, Port, SataFisD2H);\r
if (Status == EFI_DEVICE_ERROR) {\r
DEBUG ((DEBUG_ERROR, "DMA command failed at retry: %d\n", Task->RetryTimes));\r
+ DoRetry = AhciShouldCmdBeRetried (PciIo, Port); // call this before error recovery\r
RecoveryStatus = AhciRecoverPortError (PciIo, Port);\r
//\r
// If recovery passed mark the Task as not started and change the status\r
// and on next call the command will be re-issued due to IsStart being FALSE.\r
// This also makes the next condition decrement the RetryTimes.\r
//\r
- if (RecoveryStatus == EFI_SUCCESS) {\r
+ if (DoRetry && (RecoveryStatus == EFI_SUCCESS)) {\r
Task->IsStart = FALSE;\r
Status = EFI_NOT_READY;\r
}\r
EFI_AHCI_COMMAND_LIST CmdList;\r
UINT32 Retry;\r
EFI_STATUS RecoveryStatus;\r
+ BOOLEAN DoRetry;\r
\r
//\r
// Package read needed\r
Status = AhciWaitUntilFisReceived (PciIo, Port, Timeout, SataFisD2H);\r
if (Status == EFI_DEVICE_ERROR) {\r
DEBUG ((DEBUG_ERROR, "Non data transfer failed at retry %d\n", Retry));\r
+ DoRetry = AhciShouldCmdBeRetried (PciIo, Port); // call this before error recovery\r
RecoveryStatus = AhciRecoverPortError (PciIo, Port);\r
- if (EFI_ERROR (RecoveryStatus)) {\r
+ if (!DoRetry || EFI_ERROR (RecoveryStatus)) {\r
break;\r
}\r
} else {\r