/** @file\r
The file for AHCI mode of ATA host controller.\r
\r
- Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.<BR>\r
+ Copyright (c) 2010 - 2020, Intel Corporation. All rights reserved.<BR>\r
(C) Copyright 2015 Hewlett Packard Enterprise Development LP<BR>\r
SPDX-License-Identifier: BSD-2-Clause-Patent\r
\r
/**\r
Check the memory status to the test value.\r
\r
- @param[in] Address The memory address to test.\r
- @param[in] MaskValue The mask value of memory.\r
- @param[in] TestValue The test value of memory.\r
- @param[in, out] Task Optional. Pointer to the ATA_NONBLOCK_TASK used by\r
- non-blocking mode. If NULL, then just try once.\r
+ @param[in] Address The memory address to test.\r
+ @param[in] MaskValue The mask value of memory.\r
+ @param[in] TestValue The test value of memory.\r
\r
- @retval EFI_NOTREADY The memory is not set.\r
- @retval EFI_TIMEOUT The memory setting retry times out.\r
+ @retval EFI_NOT_READY The memory is not set.\r
@retval EFI_SUCCESS The memory is correct set.\r
-\r
**/\r
EFI_STATUS\r
EFIAPI\r
AhciCheckMemSet (\r
IN UINTN Address,\r
IN UINT32 MaskValue,\r
- IN UINT32 TestValue,\r
- IN OUT ATA_NONBLOCK_TASK *Task\r
+ IN UINT32 TestValue\r
)\r
{\r
UINT32 Value;\r
\r
- if (Task != NULL) {\r
- Task->RetryTimes--;\r
- }\r
-\r
Value = *(volatile UINT32 *) Address;\r
Value &= MaskValue;\r
\r
return EFI_SUCCESS;\r
}\r
\r
- if ((Task != NULL) && !Task->InfiniteWait && (Task->RetryTimes == 0)) {\r
- return EFI_TIMEOUT;\r
- } else {\r
- return EFI_NOT_READY;\r
- }\r
+ return EFI_NOT_READY;\r
}\r
\r
\r
FisBaseAddr = (UINTN)AhciRegisters->AhciRFis + Port * sizeof (EFI_AHCI_RECEIVED_FIS);\r
Offset = FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET;\r
\r
- Status = AhciCheckMemSet (Offset, EFI_AHCI_FIS_TYPE_MASK, EFI_AHCI_FIS_REGISTER_D2H, NULL);\r
+ Status = AhciCheckMemSet (Offset, EFI_AHCI_FIS_TYPE_MASK, EFI_AHCI_FIS_REGISTER_D2H);\r
if (!EFI_ERROR (Status)) {\r
//\r
// If D2H FIS is received, update StatusBlock with its content.\r
CmdFis->AhciCFisDevHead = (UINT8) (AtaCommandBlock->AtaDeviceHead | 0xE0);\r
}\r
\r
+/**\r
+ Checks if specified FIS has been received.\r
+\r
+ @param[in] PciIo Pointer to AHCI controller PciIo.\r
+ @param[in] Port SATA port index on which to check.\r
+ @param[in] FisType FIS type for which to check.\r
+\r
+ @retval EFI_SUCCESS FIS received.\r
+ @retval EFI_NOT_READY FIS not received yet.\r
+ @retval EFI_DEVICE_ERROR AHCI controller reported an error on port.\r
+**/\r
+EFI_STATUS\r
+AhciCheckFisReceived (\r
+ IN EFI_PCI_IO_PROTOCOL *PciIo,\r
+ IN UINT8 Port,\r
+ IN SATA_FIS_TYPE FisType\r
+ )\r
+{\r
+ UINT32 Offset;\r
+ UINT32 PortInterrupt;\r
+ UINT32 PortTfd;\r
+\r
+ Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_IS;\r
+ PortInterrupt = AhciReadReg (PciIo, Offset);\r
+ if ((PortInterrupt & EFI_AHCI_PORT_IS_ERROR_MASK) != 0) {\r
+ DEBUG ((DEBUG_ERROR, "AHCI: Error interrupt reported PxIS: %X\n", PortInterrupt));\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+ //\r
+ // For PIO setup FIS - 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
+ if (((FisType == SataFisD2H) && ((PortInterrupt & EFI_AHCI_PORT_IS_DHRS) != 0)) ||\r
+ ((FisType == SataFisPioSetup) && (PortInterrupt & (EFI_AHCI_PORT_IS_PSS | EFI_AHCI_PORT_IS_DHRS)) != 0) ||\r
+ ((FisType == SataFisDmaSetup) && (PortInterrupt & (EFI_AHCI_PORT_IS_DSS | EFI_AHCI_PORT_IS_DHRS)) != 0)) {\r
+ Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;\r
+ PortTfd = AhciReadReg (PciIo, (UINT32) Offset);\r
+ if ((PortTfd & EFI_AHCI_PORT_TFD_ERR) != 0) {\r
+ return EFI_DEVICE_ERROR;\r
+ } else {\r
+ return EFI_SUCCESS;\r
+ }\r
+ }\r
+\r
+ return EFI_NOT_READY;\r
+}\r
+\r
+/**\r
+ Waits until specified FIS has been received.\r
+\r
+ @param[in] PciIo Pointer to AHCI controller PciIo.\r
+ @param[in] Port SATA port index on which to check.\r
+ @param[in] Timeout Time after which function should stop polling.\r
+ @param[in] FisType FIS type for which to check.\r
+\r
+ @retval EFI_SUCCESS FIS received.\r
+ @retval EFI_TIMEOUT FIS failed to arrive within a specified time period.\r
+ @retval EFI_DEVICE_ERROR AHCI controller reported an error on port.\r
+**/\r
+EFI_STATUS\r
+AhciWaitUntilFisReceived (\r
+ IN EFI_PCI_IO_PROTOCOL *PciIo,\r
+ IN UINT8 Port,\r
+ IN UINT64 Timeout,\r
+ IN SATA_FIS_TYPE FisType\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ BOOLEAN InfiniteWait;\r
+ UINT64 Delay;\r
+\r
+ Delay = DivU64x32 (Timeout, 1000) + 1;\r
+ if (Timeout == 0) {\r
+ InfiniteWait = TRUE;\r
+ } else {\r
+ InfiniteWait = FALSE;\r
+ }\r
+\r
+ do {\r
+ Status = AhciCheckFisReceived (PciIo, Port, FisType);\r
+ if (Status != EFI_NOT_READY) {\r
+ return Status;\r
+ }\r
+ //\r
+ // Stall for 100 microseconds.\r
+ //\r
+ MicroSecondDelay (100);\r
+ Delay--;\r
+ } while (InfiniteWait || (Delay > 0));\r
+\r
+ return EFI_TIMEOUT;\r
+}\r
+\r
/**\r
Start a PIO data transfer on specific port.\r
\r
)\r
{\r
EFI_STATUS Status;\r
- UINTN FisBaseAddr;\r
- UINTN Offset;\r
EFI_PHYSICAL_ADDRESS PhyAddr;\r
VOID *Map;\r
UINTN MapLength;\r
EFI_PCI_IO_PROTOCOL_OPERATION Flag;\r
- UINT64 Delay;\r
EFI_AHCI_COMMAND_FIS CFis;\r
EFI_AHCI_COMMAND_LIST CmdList;\r
- UINT32 PortTfd;\r
UINT32 PrdCount;\r
- BOOLEAN InfiniteWait;\r
- BOOLEAN PioFisReceived;\r
- BOOLEAN D2hFisReceived;\r
-\r
- if (Timeout == 0) {\r
- InfiniteWait = TRUE;\r
- } else {\r
- InfiniteWait = FALSE;\r
- }\r
\r
if (Read) {\r
Flag = EfiPciIoOperationBusMasterWrite;\r
goto Exit;\r
}\r
\r
- //\r
- // Check the status and wait the driver sending data\r
- //\r
- FisBaseAddr = (UINTN)AhciRegisters->AhciRFis + Port * sizeof (EFI_AHCI_RECEIVED_FIS);\r
-\r
if (Read && (AtapiCommand == 0)) {\r
- //\r
- // Wait device sends the PIO setup fis before data transfer\r
- //\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
- 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
- //\r
- if ((PortTfd & EFI_AHCI_PORT_TFD_ERR) != 0) {\r
- Status = EFI_DEVICE_ERROR;\r
- break;\r
- }\r
-\r
- PrdCount = *(volatile UINT32 *) (&(AhciRegisters->AhciCmdList[0].AhciCmdPrdbc));\r
- if (PrdCount == DataCount) {\r
- Status = EFI_SUCCESS;\r
- break;\r
- }\r
- }\r
-\r
- //\r
- // Stall for 100 microseconds.\r
- //\r
- MicroSecondDelay(100);\r
-\r
- Delay--;\r
- if (Delay == 0) {\r
- Status = EFI_TIMEOUT;\r
+ Status = AhciWaitUntilFisReceived (PciIo, Port, Timeout, SataFisPioSetup);\r
+ if (Status == EFI_SUCCESS) {\r
+ PrdCount = *(volatile UINT32 *) (&(AhciRegisters->AhciCmdList[0].AhciCmdPrdbc));\r
+ if (PrdCount == DataCount) {\r
+ Status = EFI_SUCCESS;\r
+ } else {\r
+ Status = EFI_DEVICE_ERROR;\r
}\r
- } while (InfiniteWait || (Delay > 0));\r
- } else {\r
- //\r
- // Wait for D2H Fis is received\r
- //\r
- Offset = FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET;\r
- Status = AhciWaitMemSet (\r
- Offset,\r
- EFI_AHCI_FIS_TYPE_MASK,\r
- EFI_AHCI_FIS_REGISTER_D2H,\r
- Timeout\r
- );\r
-\r
- if (EFI_ERROR (Status)) {\r
- goto Exit;\r
- }\r
-\r
- Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;\r
- PortTfd = AhciReadReg (PciIo, (UINT32) Offset);\r
- if ((PortTfd & EFI_AHCI_PORT_TFD_ERR) != 0) {\r
- Status = EFI_DEVICE_ERROR;\r
}\r
+ } else {\r
+ Status = AhciWaitUntilFisReceived (PciIo, Port, Timeout, SataFisD2H);\r
}\r
\r
Exit:\r
)\r
{\r
EFI_STATUS Status;\r
- UINTN Offset;\r
EFI_PHYSICAL_ADDRESS PhyAddr;\r
VOID *Map;\r
UINTN MapLength;\r
EFI_PCI_IO_PROTOCOL_OPERATION Flag;\r
EFI_AHCI_COMMAND_FIS CFis;\r
EFI_AHCI_COMMAND_LIST CmdList;\r
- UINTN FisBaseAddr;\r
- UINT32 PortTfd;\r
\r
EFI_PCI_IO_PROTOCOL *PciIo;\r
EFI_TPL OldTpl;\r
}\r
}\r
\r
- //\r
- // Wait for command complete\r
- //\r
- FisBaseAddr = (UINTN)AhciRegisters->AhciRFis + Port * sizeof (EFI_AHCI_RECEIVED_FIS);\r
- Offset = FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET;\r
if (Task != NULL) {\r
- //\r
- // For Non-blocking\r
- //\r
- Status = AhciCheckMemSet (\r
- Offset,\r
- EFI_AHCI_FIS_TYPE_MASK,\r
- EFI_AHCI_FIS_REGISTER_D2H,\r
- Task\r
- );\r
+ Status = AhciCheckFisReceived (PciIo, Port, SataFisD2H);\r
+ if (Status == EFI_NOT_READY) {\r
+ if (!Task->InfiniteWait && Task->RetryTimes == 0) {\r
+ Status = EFI_TIMEOUT;\r
+ } else {\r
+ Task->RetryTimes--;\r
+ }\r
+ }\r
} else {\r
- Status = AhciWaitMemSet (\r
- Offset,\r
- EFI_AHCI_FIS_TYPE_MASK,\r
- EFI_AHCI_FIS_REGISTER_D2H,\r
- Timeout\r
- );\r
- }\r
-\r
- if (EFI_ERROR (Status)) {\r
- goto Exit;\r
- }\r
-\r
- Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;\r
- PortTfd = AhciReadReg (PciIo, (UINT32) Offset);\r
- if ((PortTfd & EFI_AHCI_PORT_TFD_ERR) != 0) {\r
- Status = EFI_DEVICE_ERROR;\r
+ Status = AhciWaitUntilFisReceived (PciIo, Port, Timeout, SataFisD2H);\r
}\r
\r
Exit:\r
)\r
{\r
EFI_STATUS Status;\r
- UINTN FisBaseAddr;\r
- UINTN Offset;\r
- UINT32 PortTfd;\r
EFI_AHCI_COMMAND_FIS CFis;\r
EFI_AHCI_COMMAND_LIST CmdList;\r
\r
goto Exit;\r
}\r
\r
- //\r
- // Wait device sends the Response Fis\r
- //\r
- FisBaseAddr = (UINTN)AhciRegisters->AhciRFis + Port * sizeof (EFI_AHCI_RECEIVED_FIS);\r
- Offset = FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET;\r
- Status = AhciWaitMemSet (\r
- Offset,\r
- EFI_AHCI_FIS_TYPE_MASK,\r
- EFI_AHCI_FIS_REGISTER_D2H,\r
- Timeout\r
- );\r
-\r
- if (EFI_ERROR (Status)) {\r
- goto Exit;\r
- }\r
-\r
- Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;\r
- PortTfd = AhciReadReg (PciIo, (UINT32) Offset);\r
- if ((PortTfd & EFI_AHCI_PORT_TFD_ERR) != 0) {\r
- Status = EFI_DEVICE_ERROR;\r
- }\r
+ Status = AhciWaitUntilFisReceived (PciIo, Port, Timeout, SataFisD2H);\r
\r
Exit:\r
AhciStopCommand (\r