REF: https://bugzilla.tianocore.org/show_bug.cgi?id=3737 Apply uncrustify changes to .c/.h files in the MdeModulePkg package Cc: Andrew Fish <afish@apple.com> Cc: Leif Lindholm <leif@nuviainc.com> Cc: Michael D Kinney <michael.d.kinney@intel.com> Signed-off-by: Michael Kubacki <michael.kubacki@microsoft.com> Reviewed-by: Liming Gao <gaoliming@byosoft.com.cn>
		
			
				
	
	
		
			3147 lines
		
	
	
		
			95 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			3147 lines
		
	
	
		
			95 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
|   The file for AHCI mode of ATA host controller.
 | |
| 
 | |
|   Copyright (c) 2010 - 2020, Intel Corporation. All rights reserved.<BR>
 | |
|   (C) Copyright 2015 Hewlett Packard Enterprise Development LP<BR>
 | |
|   SPDX-License-Identifier: BSD-2-Clause-Patent
 | |
| 
 | |
| **/
 | |
| 
 | |
| #include "AtaAtapiPassThru.h"
 | |
| 
 | |
| /**
 | |
|   Read AHCI Operation register.
 | |
| 
 | |
|   @param  PciIo        The PCI IO protocol instance.
 | |
|   @param  Offset       The operation register offset.
 | |
| 
 | |
|   @return The register content read.
 | |
| 
 | |
| **/
 | |
| UINT32
 | |
| EFIAPI
 | |
| AhciReadReg (
 | |
|   IN EFI_PCI_IO_PROTOCOL  *PciIo,
 | |
|   IN  UINT32              Offset
 | |
|   )
 | |
| {
 | |
|   UINT32  Data;
 | |
| 
 | |
|   ASSERT (PciIo != NULL);
 | |
| 
 | |
|   Data = 0;
 | |
| 
 | |
|   PciIo->Mem.Read (
 | |
|                PciIo,
 | |
|                EfiPciIoWidthUint32,
 | |
|                EFI_AHCI_BAR_INDEX,
 | |
|                (UINT64)Offset,
 | |
|                1,
 | |
|                &Data
 | |
|                );
 | |
| 
 | |
|   return Data;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Write AHCI Operation register.
 | |
| 
 | |
|   @param  PciIo        The PCI IO protocol instance.
 | |
|   @param  Offset       The operation register offset.
 | |
|   @param  Data         The data used to write down.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| EFIAPI
 | |
| AhciWriteReg (
 | |
|   IN EFI_PCI_IO_PROTOCOL  *PciIo,
 | |
|   IN UINT32               Offset,
 | |
|   IN UINT32               Data
 | |
|   )
 | |
| {
 | |
|   ASSERT (PciIo != NULL);
 | |
| 
 | |
|   PciIo->Mem.Write (
 | |
|                PciIo,
 | |
|                EfiPciIoWidthUint32,
 | |
|                EFI_AHCI_BAR_INDEX,
 | |
|                (UINT64)Offset,
 | |
|                1,
 | |
|                &Data
 | |
|                );
 | |
| 
 | |
|   return;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Do AND operation with the value of AHCI Operation register.
 | |
| 
 | |
|   @param  PciIo        The PCI IO protocol instance.
 | |
|   @param  Offset       The operation register offset.
 | |
|   @param  AndData      The data used to do AND operation.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| EFIAPI
 | |
| AhciAndReg (
 | |
|   IN EFI_PCI_IO_PROTOCOL  *PciIo,
 | |
|   IN UINT32               Offset,
 | |
|   IN UINT32               AndData
 | |
|   )
 | |
| {
 | |
|   UINT32  Data;
 | |
| 
 | |
|   ASSERT (PciIo != NULL);
 | |
| 
 | |
|   Data = AhciReadReg (PciIo, Offset);
 | |
| 
 | |
|   Data &= AndData;
 | |
| 
 | |
|   AhciWriteReg (PciIo, Offset, Data);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Do OR operation with the value of AHCI Operation register.
 | |
| 
 | |
|   @param  PciIo        The PCI IO protocol instance.
 | |
|   @param  Offset       The operation register offset.
 | |
|   @param  OrData       The data used to do OR operation.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| EFIAPI
 | |
| AhciOrReg (
 | |
|   IN EFI_PCI_IO_PROTOCOL  *PciIo,
 | |
|   IN UINT32               Offset,
 | |
|   IN UINT32               OrData
 | |
|   )
 | |
| {
 | |
|   UINT32  Data;
 | |
| 
 | |
|   ASSERT (PciIo != NULL);
 | |
| 
 | |
|   Data = AhciReadReg (PciIo, Offset);
 | |
| 
 | |
|   Data |= OrData;
 | |
| 
 | |
|   AhciWriteReg (PciIo, Offset, Data);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Wait for the value of the specified MMIO register set to the test value.
 | |
| 
 | |
|   @param  PciIo             The PCI IO protocol instance.
 | |
|   @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, uses 100ns as a unit.
 | |
| 
 | |
|   @retval EFI_TIMEOUT       The MMIO setting is time out.
 | |
|   @retval EFI_SUCCESS       The MMIO is correct set.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| AhciWaitMmioSet (
 | |
|   IN  EFI_PCI_IO_PROTOCOL  *PciIo,
 | |
|   IN  UINTN                Offset,
 | |
|   IN  UINT32               MaskValue,
 | |
|   IN  UINT32               TestValue,
 | |
|   IN  UINT64               Timeout
 | |
|   )
 | |
| {
 | |
|   UINT32   Value;
 | |
|   UINT64   Delay;
 | |
|   BOOLEAN  InfiniteWait;
 | |
| 
 | |
|   if (Timeout == 0) {
 | |
|     InfiniteWait = TRUE;
 | |
|   } else {
 | |
|     InfiniteWait = FALSE;
 | |
|   }
 | |
| 
 | |
|   Delay = DivU64x32 (Timeout, 1000) + 1;
 | |
| 
 | |
|   do {
 | |
|     //
 | |
|     // 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;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Stall for 100 microseconds.
 | |
|     //
 | |
|     MicroSecondDelay (100);
 | |
| 
 | |
|     Delay--;
 | |
|   } while (InfiniteWait || (Delay > 0));
 | |
| 
 | |
|   return EFI_TIMEOUT;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   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;
 | |
|   UINT64   Delay;
 | |
|   BOOLEAN  InfiniteWait;
 | |
| 
 | |
|   if (Timeout == 0) {
 | |
|     InfiniteWait = TRUE;
 | |
|   } else {
 | |
|     InfiniteWait = FALSE;
 | |
|   }
 | |
| 
 | |
|   Delay =  DivU64x32 (Timeout, 1000) + 1;
 | |
| 
 | |
|   do {
 | |
|     //
 | |
|     // Access system 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 (InfiniteWait || (Delay > 0));
 | |
| 
 | |
|   return EFI_TIMEOUT;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Check the memory status to the test value.
 | |
| 
 | |
|   @param[in] Address    The memory address to test.
 | |
|   @param[in] MaskValue  The mask value of memory.
 | |
|   @param[in] TestValue  The test value of memory.
 | |
| 
 | |
|   @retval EFI_NOT_READY     The memory is not set.
 | |
|   @retval EFI_SUCCESS       The memory is correct set.
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| AhciCheckMemSet (
 | |
|   IN     UINTN   Address,
 | |
|   IN     UINT32  MaskValue,
 | |
|   IN     UINT32  TestValue
 | |
|   )
 | |
| {
 | |
|   UINT32  Value;
 | |
| 
 | |
|   Value  = *(volatile UINT32 *)Address;
 | |
|   Value &= MaskValue;
 | |
| 
 | |
|   if (Value == TestValue) {
 | |
|     return EFI_SUCCESS;
 | |
|   }
 | |
| 
 | |
|   return EFI_NOT_READY;
 | |
| }
 | |
| 
 | |
| /**
 | |
| 
 | |
|   Clear the port interrupt and error status. It will also clear
 | |
|   HBA interrupt status.
 | |
| 
 | |
|   @param      PciIo          The PCI IO protocol instance.
 | |
|   @param      Port           The number of port.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| EFIAPI
 | |
| AhciClearPortStatus (
 | |
|   IN  EFI_PCI_IO_PROTOCOL  *PciIo,
 | |
|   IN  UINT8                Port
 | |
|   )
 | |
| {
 | |
|   UINT32  Offset;
 | |
| 
 | |
|   //
 | |
|   // Clear any error status
 | |
|   //
 | |
|   Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SERR;
 | |
|   AhciWriteReg (PciIo, Offset, AhciReadReg (PciIo, Offset));
 | |
| 
 | |
|   //
 | |
|   // Clear any port interrupt status
 | |
|   //
 | |
|   Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_IS;
 | |
|   AhciWriteReg (PciIo, Offset, AhciReadReg (PciIo, Offset));
 | |
| 
 | |
|   //
 | |
|   // Clear any HBA interrupt status
 | |
|   //
 | |
|   AhciWriteReg (PciIo, EFI_AHCI_IS_OFFSET, AhciReadReg (PciIo, EFI_AHCI_IS_OFFSET));
 | |
| }
 | |
| 
 | |
| /**
 | |
|   This function is used to dump the Status Registers and if there is ERR bit set
 | |
|   in the Status Register, the Error Register's value is also be dumped.
 | |
| 
 | |
|   @param  PciIo            The PCI IO protocol instance.
 | |
|   @param  AhciRegisters    The pointer to the EFI_AHCI_REGISTERS.
 | |
|   @param  Port             The number of port.
 | |
|   @param  AtaStatusBlock   A pointer to EFI_ATA_STATUS_BLOCK data structure.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| EFIAPI
 | |
| AhciDumpPortStatus (
 | |
|   IN     EFI_PCI_IO_PROTOCOL   *PciIo,
 | |
|   IN     EFI_AHCI_REGISTERS    *AhciRegisters,
 | |
|   IN     UINT8                 Port,
 | |
|   IN OUT EFI_ATA_STATUS_BLOCK  *AtaStatusBlock
 | |
|   )
 | |
| {
 | |
|   UINTN       Offset;
 | |
|   UINT32      Data;
 | |
|   UINTN       FisBaseAddr;
 | |
|   EFI_STATUS  Status;
 | |
| 
 | |
|   ASSERT (PciIo != NULL);
 | |
| 
 | |
|   if (AtaStatusBlock != NULL) {
 | |
|     ZeroMem (AtaStatusBlock, sizeof (EFI_ATA_STATUS_BLOCK));
 | |
| 
 | |
|     FisBaseAddr = (UINTN)AhciRegisters->AhciRFis + Port * sizeof (EFI_AHCI_RECEIVED_FIS);
 | |
|     Offset      = FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET;
 | |
| 
 | |
|     Status = AhciCheckMemSet (Offset, EFI_AHCI_FIS_TYPE_MASK, EFI_AHCI_FIS_REGISTER_D2H);
 | |
|     if (!EFI_ERROR (Status)) {
 | |
|       //
 | |
|       // If D2H FIS is received, update StatusBlock with its content.
 | |
|       //
 | |
|       CopyMem (AtaStatusBlock, (UINT8 *)Offset, sizeof (EFI_ATA_STATUS_BLOCK));
 | |
|     } else {
 | |
|       //
 | |
|       // If D2H FIS is not received, only update Status & Error field through PxTFD
 | |
|       // as there is no other way to get the content of the Shadow Register Block.
 | |
|       //
 | |
|       Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;
 | |
|       Data   = AhciReadReg (PciIo, (UINT32)Offset);
 | |
| 
 | |
|       AtaStatusBlock->AtaStatus = (UINT8)Data;
 | |
|       if ((AtaStatusBlock->AtaStatus & BIT0) != 0) {
 | |
|         AtaStatusBlock->AtaError = (UINT8)(Data >> 8);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Enable the FIS running for giving port.
 | |
| 
 | |
|   @param      PciIo          The PCI IO protocol instance.
 | |
|   @param      Port           The number of port.
 | |
|   @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.
 | |
|   @retval EFI_SUCCESS        The FIS enable successfully.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| AhciEnableFisReceive (
 | |
|   IN  EFI_PCI_IO_PROTOCOL  *PciIo,
 | |
|   IN  UINT8                Port,
 | |
|   IN  UINT64               Timeout
 | |
|   )
 | |
| {
 | |
|   UINT32  Offset;
 | |
| 
 | |
|   Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;
 | |
|   AhciOrReg (PciIo, Offset, EFI_AHCI_PORT_CMD_FRE);
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Disable the FIS running for giving port.
 | |
| 
 | |
|   @param      PciIo          The PCI IO protocol instance.
 | |
|   @param      Port           The number of port.
 | |
|   @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.
 | |
|   @retval EFI_UNSUPPORTED    The port is in running state.
 | |
|   @retval EFI_SUCCESS        The FIS disable successfully.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| AhciDisableFisReceive (
 | |
|   IN  EFI_PCI_IO_PROTOCOL  *PciIo,
 | |
|   IN  UINT8                Port,
 | |
|   IN  UINT64               Timeout
 | |
|   )
 | |
| {
 | |
|   UINT32  Offset;
 | |
|   UINT32  Data;
 | |
| 
 | |
|   Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;
 | |
|   Data   = AhciReadReg (PciIo, Offset);
 | |
| 
 | |
|   //
 | |
|   // Before disabling Fis receive, the DMA engine of the port should NOT be in running status.
 | |
|   //
 | |
|   if ((Data & (EFI_AHCI_PORT_CMD_ST | EFI_AHCI_PORT_CMD_CR)) != 0) {
 | |
|     return EFI_UNSUPPORTED;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Check if the Fis receive DMA engine for the port is running.
 | |
|   //
 | |
|   if ((Data & EFI_AHCI_PORT_CMD_FR) != EFI_AHCI_PORT_CMD_FR) {
 | |
|     return EFI_SUCCESS;
 | |
|   }
 | |
| 
 | |
|   AhciAndReg (PciIo, Offset, (UINT32) ~(EFI_AHCI_PORT_CMD_FRE));
 | |
| 
 | |
|   return AhciWaitMmioSet (
 | |
|            PciIo,
 | |
|            Offset,
 | |
|            EFI_AHCI_PORT_CMD_FR,
 | |
|            0,
 | |
|            Timeout
 | |
|            );
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Build the command list, command table and prepare the fis receiver.
 | |
| 
 | |
|   @param    PciIo                 The PCI IO protocol instance.
 | |
|   @param    AhciRegisters         The pointer to the EFI_AHCI_REGISTERS.
 | |
|   @param    Port                  The number of port.
 | |
|   @param    PortMultiplier        The timeout value of stop.
 | |
|   @param    CommandFis            The control fis will be used for the transfer.
 | |
|   @param    CommandList           The command list will be used for the transfer.
 | |
|   @param    AtapiCommand          The atapi command will be used for the transfer.
 | |
|   @param    AtapiCommandLength    The length of the atapi command.
 | |
|   @param    CommandSlotNumber     The command slot will be used for the transfer.
 | |
|   @param    DataPhysicalAddr      The pointer to the data buffer pci bus master address.
 | |
|   @param    DataLength            The data count to be transferred.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| EFIAPI
 | |
| AhciBuildCommand (
 | |
|   IN     EFI_PCI_IO_PROTOCOL     *PciIo,
 | |
|   IN     EFI_AHCI_REGISTERS      *AhciRegisters,
 | |
|   IN     UINT8                   Port,
 | |
|   IN     UINT8                   PortMultiplier,
 | |
|   IN     EFI_AHCI_COMMAND_FIS    *CommandFis,
 | |
|   IN     EFI_AHCI_COMMAND_LIST   *CommandList,
 | |
|   IN     EFI_AHCI_ATAPI_COMMAND  *AtapiCommand OPTIONAL,
 | |
|   IN     UINT8                   AtapiCommandLength,
 | |
|   IN     UINT8                   CommandSlotNumber,
 | |
|   IN OUT VOID                    *DataPhysicalAddr,
 | |
|   IN     UINT32                  DataLength
 | |
|   )
 | |
| {
 | |
|   UINT64   BaseAddr;
 | |
|   UINT32   PrdtNumber;
 | |
|   UINT32   PrdtIndex;
 | |
|   UINTN    RemainedData;
 | |
|   UINTN    MemAddr;
 | |
|   DATA_64  Data64;
 | |
|   UINT32   Offset;
 | |
| 
 | |
|   //
 | |
|   // Filling the PRDT
 | |
|   //
 | |
|   PrdtNumber = (UINT32)DivU64x32 (((UINT64)DataLength + EFI_AHCI_MAX_DATA_PER_PRDT - 1), EFI_AHCI_MAX_DATA_PER_PRDT);
 | |
| 
 | |
|   //
 | |
|   // According to AHCI 1.3 spec, a PRDT entry can point to a maximum 4MB data block.
 | |
|   // It also limits that the maximum amount of the PRDT entry in the command table
 | |
|   // is 65535.
 | |
|   //
 | |
|   ASSERT (PrdtNumber <= 65535);
 | |
| 
 | |
|   Data64.Uint64 = (UINTN)(AhciRegisters->AhciRFis) + sizeof (EFI_AHCI_RECEIVED_FIS) * Port;
 | |
| 
 | |
|   BaseAddr = Data64.Uint64;
 | |
| 
 | |
|   ZeroMem ((VOID *)((UINTN)BaseAddr), sizeof (EFI_AHCI_RECEIVED_FIS));
 | |
| 
 | |
|   ZeroMem (AhciRegisters->AhciCommandTable, sizeof (EFI_AHCI_COMMAND_TABLE));
 | |
| 
 | |
|   CommandFis->AhciCFisPmNum = PortMultiplier;
 | |
| 
 | |
|   CopyMem (&AhciRegisters->AhciCommandTable->CommandFis, CommandFis, sizeof (EFI_AHCI_COMMAND_FIS));
 | |
| 
 | |
|   Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;
 | |
|   if (AtapiCommand != NULL) {
 | |
|     CopyMem (
 | |
|       &AhciRegisters->AhciCommandTable->AtapiCmd,
 | |
|       AtapiCommand,
 | |
|       AtapiCommandLength
 | |
|       );
 | |
| 
 | |
|     CommandList->AhciCmdA = 1;
 | |
|     CommandList->AhciCmdP = 1;
 | |
| 
 | |
|     AhciOrReg (PciIo, Offset, (EFI_AHCI_PORT_CMD_DLAE | EFI_AHCI_PORT_CMD_ATAPI));
 | |
|   } else {
 | |
|     AhciAndReg (PciIo, Offset, (UINT32) ~(EFI_AHCI_PORT_CMD_DLAE | EFI_AHCI_PORT_CMD_ATAPI));
 | |
|   }
 | |
| 
 | |
|   RemainedData              = (UINTN)DataLength;
 | |
|   MemAddr                   = (UINTN)DataPhysicalAddr;
 | |
|   CommandList->AhciCmdPrdtl = PrdtNumber;
 | |
| 
 | |
|   for (PrdtIndex = 0; PrdtIndex < PrdtNumber; PrdtIndex++) {
 | |
|     if (RemainedData < EFI_AHCI_MAX_DATA_PER_PRDT) {
 | |
|       AhciRegisters->AhciCommandTable->PrdtTable[PrdtIndex].AhciPrdtDbc = (UINT32)RemainedData - 1;
 | |
|     } else {
 | |
|       AhciRegisters->AhciCommandTable->PrdtTable[PrdtIndex].AhciPrdtDbc = EFI_AHCI_MAX_DATA_PER_PRDT - 1;
 | |
|     }
 | |
| 
 | |
|     Data64.Uint64                                                      = (UINT64)MemAddr;
 | |
|     AhciRegisters->AhciCommandTable->PrdtTable[PrdtIndex].AhciPrdtDba  = Data64.Uint32.Lower32;
 | |
|     AhciRegisters->AhciCommandTable->PrdtTable[PrdtIndex].AhciPrdtDbau = Data64.Uint32.Upper32;
 | |
|     RemainedData                                                      -= EFI_AHCI_MAX_DATA_PER_PRDT;
 | |
|     MemAddr                                                           += EFI_AHCI_MAX_DATA_PER_PRDT;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Set the last PRDT to Interrupt On Complete
 | |
|   //
 | |
|   if (PrdtNumber > 0) {
 | |
|     AhciRegisters->AhciCommandTable->PrdtTable[PrdtNumber - 1].AhciPrdtIoc = 1;
 | |
|   }
 | |
| 
 | |
|   CopyMem (
 | |
|     (VOID *)((UINTN)AhciRegisters->AhciCmdList + (UINTN)CommandSlotNumber * sizeof (EFI_AHCI_COMMAND_LIST)),
 | |
|     CommandList,
 | |
|     sizeof (EFI_AHCI_COMMAND_LIST)
 | |
|     );
 | |
| 
 | |
|   Data64.Uint64                                              = (UINT64)(UINTN)AhciRegisters->AhciCommandTablePciAddr;
 | |
|   AhciRegisters->AhciCmdList[CommandSlotNumber].AhciCmdCtba  = Data64.Uint32.Lower32;
 | |
|   AhciRegisters->AhciCmdList[CommandSlotNumber].AhciCmdCtbau = Data64.Uint32.Upper32;
 | |
|   AhciRegisters->AhciCmdList[CommandSlotNumber].AhciCmdPmp   = PortMultiplier;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Build a command FIS.
 | |
| 
 | |
|   @param  CmdFis            A pointer to the EFI_AHCI_COMMAND_FIS data structure.
 | |
|   @param  AtaCommandBlock   A pointer to the AhciBuildCommandFis data structure.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| EFIAPI
 | |
| AhciBuildCommandFis (
 | |
|   IN OUT EFI_AHCI_COMMAND_FIS   *CmdFis,
 | |
|   IN     EFI_ATA_COMMAND_BLOCK  *AtaCommandBlock
 | |
|   )
 | |
| {
 | |
|   ZeroMem (CmdFis, sizeof (EFI_AHCI_COMMAND_FIS));
 | |
| 
 | |
|   CmdFis->AhciCFisType = EFI_AHCI_FIS_REGISTER_H2D;
 | |
|   //
 | |
|   // Indicator it's a command
 | |
|   //
 | |
|   CmdFis->AhciCFisCmdInd = 0x1;
 | |
|   CmdFis->AhciCFisCmd    = AtaCommandBlock->AtaCommand;
 | |
| 
 | |
|   CmdFis->AhciCFisFeature    = AtaCommandBlock->AtaFeatures;
 | |
|   CmdFis->AhciCFisFeatureExp = AtaCommandBlock->AtaFeaturesExp;
 | |
| 
 | |
|   CmdFis->AhciCFisSecNum    = AtaCommandBlock->AtaSectorNumber;
 | |
|   CmdFis->AhciCFisSecNumExp = AtaCommandBlock->AtaSectorNumberExp;
 | |
| 
 | |
|   CmdFis->AhciCFisClyLow    = AtaCommandBlock->AtaCylinderLow;
 | |
|   CmdFis->AhciCFisClyLowExp = AtaCommandBlock->AtaCylinderLowExp;
 | |
| 
 | |
|   CmdFis->AhciCFisClyHigh    = AtaCommandBlock->AtaCylinderHigh;
 | |
|   CmdFis->AhciCFisClyHighExp = AtaCommandBlock->AtaCylinderHighExp;
 | |
| 
 | |
|   CmdFis->AhciCFisSecCount    = AtaCommandBlock->AtaSectorCount;
 | |
|   CmdFis->AhciCFisSecCountExp = AtaCommandBlock->AtaSectorCountExp;
 | |
| 
 | |
|   CmdFis->AhciCFisDevHead = (UINT8)(AtaCommandBlock->AtaDeviceHead | 0xE0);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Wait until SATA device reports it is ready for operation.
 | |
| 
 | |
|   @param[in] PciIo    Pointer to AHCI controller PciIo.
 | |
|   @param[in] Port     SATA port index on which to reset.
 | |
| 
 | |
|   @retval EFI_SUCCESS  Device ready for operation.
 | |
|   @retval EFI_TIMEOUT  Device failed to get ready within required period.
 | |
| **/
 | |
| EFI_STATUS
 | |
| AhciWaitDeviceReady (
 | |
|   IN EFI_PCI_IO_PROTOCOL  *PciIo,
 | |
|   IN UINT8                Port
 | |
|   )
 | |
| {
 | |
|   UINT32  PhyDetectDelay;
 | |
|   UINT32  Data;
 | |
|   UINT32  Offset;
 | |
| 
 | |
|   //
 | |
|   // According to SATA1.0a spec section 5.2, we need to wait for PxTFD.BSY and PxTFD.DRQ
 | |
|   // and PxTFD.ERR to be zero. The maximum wait time is 16s which is defined at ATA spec.
 | |
|   //
 | |
|   PhyDetectDelay = 16 * 1000;
 | |
|   do {
 | |
|     Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SERR;
 | |
|     if (AhciReadReg (PciIo, Offset) != 0) {
 | |
|       AhciWriteReg (PciIo, Offset, AhciReadReg (PciIo, Offset));
 | |
|     }
 | |
| 
 | |
|     Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;
 | |
| 
 | |
|     Data = AhciReadReg (PciIo, Offset) & EFI_AHCI_PORT_TFD_MASK;
 | |
|     if (Data == 0) {
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     MicroSecondDelay (1000);
 | |
|     PhyDetectDelay--;
 | |
|   } while (PhyDetectDelay > 0);
 | |
| 
 | |
|   if (PhyDetectDelay == 0) {
 | |
|     DEBUG ((DEBUG_ERROR, "Port %d Device not ready (TFD=0x%X)\n", Port, Data));
 | |
|     return EFI_TIMEOUT;
 | |
|   } else {
 | |
|     return EFI_SUCCESS;
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Reset the SATA port. Algorithm follows AHCI spec 1.3.1 section 10.4.2
 | |
| 
 | |
|   @param[in] PciIo    Pointer to AHCI controller PciIo.
 | |
|   @param[in] Port     SATA port index on which to reset.
 | |
| 
 | |
|   @retval EFI_SUCCESS  Port reset.
 | |
|   @retval Others       Failed to reset the port.
 | |
| **/
 | |
| EFI_STATUS
 | |
| AhciResetPort (
 | |
|   IN EFI_PCI_IO_PROTOCOL  *PciIo,
 | |
|   IN UINT8                Port
 | |
|   )
 | |
| {
 | |
|   UINT32      Offset;
 | |
|   EFI_STATUS  Status;
 | |
| 
 | |
|   Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SCTL;
 | |
|   AhciOrReg (PciIo, Offset, EFI_AHCI_PORT_SCTL_DET_INIT);
 | |
|   //
 | |
|   // SW is required to keep DET set to 0x1 at least for 1 milisecond to ensure that
 | |
|   // at least one COMRESET signal is sent.
 | |
|   //
 | |
|   MicroSecondDelay (1000);
 | |
|   AhciAndReg (PciIo, Offset, ~(UINT32)EFI_AHCI_PORT_SSTS_DET_MASK);
 | |
| 
 | |
|   Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SSTS;
 | |
|   Status = AhciWaitMmioSet (PciIo, Offset, EFI_AHCI_PORT_SSTS_DET_MASK, EFI_AHCI_PORT_SSTS_DET_PCE, ATA_ATAPI_TIMEOUT);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   return AhciWaitDeviceReady (PciIo, Port);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Recovers the SATA port from error condition.
 | |
|   This function implements algorithm described in
 | |
|   AHCI spec 1.3.1 section 6.2.2
 | |
| 
 | |
|   @param[in] PciIo    Pointer to AHCI controller PciIo.
 | |
|   @param[in] Port     SATA port index on which to check.
 | |
| 
 | |
|   @retval EFI_SUCCESS  Port recovered.
 | |
|   @retval Others       Failed to recover port.
 | |
| **/
 | |
| EFI_STATUS
 | |
| AhciRecoverPortError (
 | |
|   IN EFI_PCI_IO_PROTOCOL  *PciIo,
 | |
|   IN UINT8                Port
 | |
|   )
 | |
| {
 | |
|   UINT32      Offset;
 | |
|   UINT32      PortInterrupt;
 | |
|   UINT32      PortTfd;
 | |
|   EFI_STATUS  Status;
 | |
| 
 | |
|   Offset        = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_IS;
 | |
|   PortInterrupt = AhciReadReg (PciIo, Offset);
 | |
|   if ((PortInterrupt & EFI_AHCI_PORT_IS_FATAL_ERROR_MASK) == 0) {
 | |
|     //
 | |
|     // No fatal error detected. Exit with success as port should still be operational.
 | |
|     // No need to clear IS as it will be cleared when the next command starts.
 | |
|     //
 | |
|     return EFI_SUCCESS;
 | |
|   }
 | |
| 
 | |
|   Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;
 | |
|   AhciAndReg (PciIo, Offset, ~(UINT32)EFI_AHCI_PORT_CMD_ST);
 | |
| 
 | |
|   Status = AhciWaitMmioSet (PciIo, Offset, EFI_AHCI_PORT_CMD_CR, 0, ATA_ATAPI_TIMEOUT);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     DEBUG ((DEBUG_ERROR, "Ahci port %d is in hung state, aborting recovery\n", Port));
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // If TFD.BSY or TFD.DRQ is still set it means that drive is hung and software has
 | |
|   // to reset it before sending any additional commands.
 | |
|   //
 | |
|   Offset  = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;
 | |
|   PortTfd = AhciReadReg (PciIo, Offset);
 | |
|   if ((PortTfd & (EFI_AHCI_PORT_TFD_BSY | EFI_AHCI_PORT_TFD_DRQ)) != 0) {
 | |
|     Status = AhciResetPort (PciIo, Port);
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       DEBUG ((DEBUG_ERROR, "Failed to reset the port %d\n", Port));
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Checks if specified FIS has been received.
 | |
| 
 | |
|   @param[in] PciIo    Pointer to AHCI controller PciIo.
 | |
|   @param[in] Port     SATA port index on which to check.
 | |
|   @param[in] FisType  FIS type for which to check.
 | |
| 
 | |
|   @retval EFI_SUCCESS       FIS received.
 | |
|   @retval EFI_NOT_READY     FIS not received yet.
 | |
|   @retval EFI_DEVICE_ERROR  AHCI controller reported an error on port.
 | |
| **/
 | |
| EFI_STATUS
 | |
| AhciCheckFisReceived (
 | |
|   IN EFI_PCI_IO_PROTOCOL  *PciIo,
 | |
|   IN UINT8                Port,
 | |
|   IN SATA_FIS_TYPE        FisType
 | |
|   )
 | |
| {
 | |
|   UINT32  Offset;
 | |
|   UINT32  PortInterrupt;
 | |
|   UINT32  PortTfd;
 | |
| 
 | |
|   Offset        = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_IS;
 | |
|   PortInterrupt = AhciReadReg (PciIo, Offset);
 | |
|   if ((PortInterrupt & EFI_AHCI_PORT_IS_ERROR_MASK) != 0) {
 | |
|     DEBUG ((DEBUG_ERROR, "AHCI: Error interrupt reported PxIS: %X\n", PortInterrupt));
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // For PIO setup FIS - According to SATA 2.6 spec section 11.7, D2h FIS means an error encountered.
 | |
|   // But Qemu and Marvel 9230 sata controller may just receive a D2h FIS from device
 | |
|   // after the transaction is finished successfully.
 | |
|   // To get better device compatibilities, we further check if the PxTFD's ERR bit is set.
 | |
|   // By this way, we can know if there is a real error happened.
 | |
|   //
 | |
|   if (((FisType == SataFisD2H) && ((PortInterrupt & EFI_AHCI_PORT_IS_DHRS) != 0)) ||
 | |
|       ((FisType == SataFisPioSetup) && ((PortInterrupt & (EFI_AHCI_PORT_IS_PSS | EFI_AHCI_PORT_IS_DHRS)) != 0)) ||
 | |
|       ((FisType == SataFisDmaSetup) && ((PortInterrupt & (EFI_AHCI_PORT_IS_DSS | EFI_AHCI_PORT_IS_DHRS)) != 0)))
 | |
|   {
 | |
|     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) {
 | |
|       return EFI_DEVICE_ERROR;
 | |
|     } else {
 | |
|       return EFI_SUCCESS;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return EFI_NOT_READY;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Waits until specified FIS has been received.
 | |
| 
 | |
|   @param[in] PciIo    Pointer to AHCI controller PciIo.
 | |
|   @param[in] Port     SATA port index on which to check.
 | |
|   @param[in] Timeout  Time after which function should stop polling.
 | |
|   @param[in] FisType  FIS type for which to check.
 | |
| 
 | |
|   @retval EFI_SUCCESS       FIS received.
 | |
|   @retval EFI_TIMEOUT       FIS failed to arrive within a specified time period.
 | |
|   @retval EFI_DEVICE_ERROR  AHCI controller reported an error on port.
 | |
| **/
 | |
| EFI_STATUS
 | |
| AhciWaitUntilFisReceived (
 | |
|   IN EFI_PCI_IO_PROTOCOL  *PciIo,
 | |
|   IN UINT8                Port,
 | |
|   IN UINT64               Timeout,
 | |
|   IN SATA_FIS_TYPE        FisType
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
|   BOOLEAN     InfiniteWait;
 | |
|   UINT64      Delay;
 | |
| 
 | |
|   Delay =  DivU64x32 (Timeout, 1000) + 1;
 | |
|   if (Timeout == 0) {
 | |
|     InfiniteWait = TRUE;
 | |
|   } else {
 | |
|     InfiniteWait = FALSE;
 | |
|   }
 | |
| 
 | |
|   do {
 | |
|     Status = AhciCheckFisReceived (PciIo, Port, FisType);
 | |
|     if (Status != EFI_NOT_READY) {
 | |
|       return Status;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Stall for 100 microseconds.
 | |
|     //
 | |
|     MicroSecondDelay (100);
 | |
|     Delay--;
 | |
|   } while (InfiniteWait || (Delay > 0));
 | |
| 
 | |
|   return EFI_TIMEOUT;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Prints contents of the ATA command block into the debug port.
 | |
| 
 | |
|   @param[in] AtaCommandBlock  AtaCommandBlock to print.
 | |
|   @param[in] DebugLevel       Debug level on which to print.
 | |
| **/
 | |
| VOID
 | |
| AhciPrintCommandBlock (
 | |
|   IN EFI_ATA_COMMAND_BLOCK  *AtaCommandBlock,
 | |
|   IN UINT32                 DebugLevel
 | |
|   )
 | |
| {
 | |
|   DEBUG ((DebugLevel, "ATA COMMAND BLOCK:\n"));
 | |
|   DEBUG ((DebugLevel, "AtaCommand: %d\n", AtaCommandBlock->AtaCommand));
 | |
|   DEBUG ((DebugLevel, "AtaFeatures: %X\n", AtaCommandBlock->AtaFeatures));
 | |
|   DEBUG ((DebugLevel, "AtaSectorNumber: %d\n", AtaCommandBlock->AtaSectorNumber));
 | |
|   DEBUG ((DebugLevel, "AtaCylinderLow: %X\n", AtaCommandBlock->AtaCylinderHigh));
 | |
|   DEBUG ((DebugLevel, "AtaCylinderHigh: %X\n", AtaCommandBlock->AtaCylinderHigh));
 | |
|   DEBUG ((DebugLevel, "AtaDeviceHead: %d\n", AtaCommandBlock->AtaDeviceHead));
 | |
|   DEBUG ((DebugLevel, "AtaSectorNumberExp: %d\n", AtaCommandBlock->AtaSectorNumberExp));
 | |
|   DEBUG ((DebugLevel, "AtaCylinderLowExp: %X\n", AtaCommandBlock->AtaCylinderLowExp));
 | |
|   DEBUG ((DebugLevel, "AtaCylinderHighExp: %X\n", AtaCommandBlock->AtaCylinderHighExp));
 | |
|   DEBUG ((DebugLevel, "AtaFeaturesExp: %X\n", AtaCommandBlock->AtaFeaturesExp));
 | |
|   DEBUG ((DebugLevel, "AtaSectorCount: %d\n", AtaCommandBlock->AtaSectorCount));
 | |
|   DEBUG ((DebugLevel, "AtaSectorCountExp: %d\n", AtaCommandBlock->AtaSectorCountExp));
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Prints contents of the ATA status block into the debug port.
 | |
| 
 | |
|   @param[in] AtaStatusBlock   AtaStatusBlock to print.
 | |
|   @param[in] DebugLevel       Debug level on which to print.
 | |
| **/
 | |
| VOID
 | |
| AhciPrintStatusBlock (
 | |
|   IN EFI_ATA_STATUS_BLOCK  *AtaStatusBlock,
 | |
|   IN UINT32                DebugLevel
 | |
|   )
 | |
| {
 | |
|   //
 | |
|   // Skip NULL pointer
 | |
|   //
 | |
|   if (AtaStatusBlock == NULL) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Only print status and error since we have all of the rest printed as
 | |
|   // a part of command block print.
 | |
|   //
 | |
|   DEBUG ((DebugLevel, "ATA STATUS BLOCK:\n"));
 | |
|   DEBUG ((DebugLevel, "AtaStatus: %d\n", AtaStatusBlock->AtaStatus));
 | |
|   DEBUG ((DebugLevel, "AtaError: %d\n", AtaStatusBlock->AtaError));
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Start a PIO data transfer on specific port.
 | |
| 
 | |
|   @param[in]       PciIo               The PCI IO protocol instance.
 | |
|   @param[in]       AhciRegisters       The pointer to the EFI_AHCI_REGISTERS.
 | |
|   @param[in]       Port                The number of port.
 | |
|   @param[in]       PortMultiplier      The timeout value of stop.
 | |
|   @param[in]       AtapiCommand        The atapi command will be used for the
 | |
|                                        transfer.
 | |
|   @param[in]       AtapiCommandLength  The length of the atapi command.
 | |
|   @param[in]       Read                The transfer direction.
 | |
|   @param[in]       AtaCommandBlock     The EFI_ATA_COMMAND_BLOCK data.
 | |
|   @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, uses 100ns as a unit.
 | |
|   @param[in]       Task                Optional. Pointer to the ATA_NONBLOCK_TASK
 | |
|                                        used by non-blocking mode.
 | |
| 
 | |
|   @retval EFI_DEVICE_ERROR    The PIO data transfer abort with error occurs.
 | |
|   @retval EFI_TIMEOUT         The operation is time out.
 | |
|   @retval EFI_UNSUPPORTED     The device is not ready for transfer.
 | |
|   @retval EFI_SUCCESS         The PIO data transfer executes successfully.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| AhciPioTransfer (
 | |
|   IN     EFI_PCI_IO_PROTOCOL     *PciIo,
 | |
|   IN     EFI_AHCI_REGISTERS      *AhciRegisters,
 | |
|   IN     UINT8                   Port,
 | |
|   IN     UINT8                   PortMultiplier,
 | |
|   IN     EFI_AHCI_ATAPI_COMMAND  *AtapiCommand OPTIONAL,
 | |
|   IN     UINT8                   AtapiCommandLength,
 | |
|   IN     BOOLEAN                 Read,
 | |
|   IN     EFI_ATA_COMMAND_BLOCK   *AtaCommandBlock,
 | |
|   IN OUT EFI_ATA_STATUS_BLOCK    *AtaStatusBlock,
 | |
|   IN OUT VOID                    *MemoryAddr,
 | |
|   IN     UINT32                  DataCount,
 | |
|   IN     UINT64                  Timeout,
 | |
|   IN     ATA_NONBLOCK_TASK       *Task
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS                     Status;
 | |
|   EFI_PHYSICAL_ADDRESS           PhyAddr;
 | |
|   VOID                           *Map;
 | |
|   UINTN                          MapLength;
 | |
|   EFI_PCI_IO_PROTOCOL_OPERATION  Flag;
 | |
|   EFI_AHCI_COMMAND_FIS           CFis;
 | |
|   EFI_AHCI_COMMAND_LIST          CmdList;
 | |
|   UINT32                         PrdCount;
 | |
|   UINT32                         Retry;
 | |
| 
 | |
|   if (Read) {
 | |
|     Flag = EfiPciIoOperationBusMasterWrite;
 | |
|   } else {
 | |
|     Flag = EfiPciIoOperationBusMasterRead;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // construct command list and command table with pci bus address
 | |
|   //
 | |
|   MapLength = DataCount;
 | |
|   Status    = PciIo->Map (
 | |
|                        PciIo,
 | |
|                        Flag,
 | |
|                        MemoryAddr,
 | |
|                        &MapLength,
 | |
|                        &PhyAddr,
 | |
|                        &Map
 | |
|                        );
 | |
| 
 | |
|   if (EFI_ERROR (Status) || (DataCount != MapLength)) {
 | |
|     return EFI_BAD_BUFFER_SIZE;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Package read needed
 | |
|   //
 | |
|   AhciBuildCommandFis (&CFis, AtaCommandBlock);
 | |
| 
 | |
|   ZeroMem (&CmdList, sizeof (EFI_AHCI_COMMAND_LIST));
 | |
| 
 | |
|   CmdList.AhciCmdCfl = EFI_AHCI_FIS_REGISTER_H2D_LENGTH / 4;
 | |
|   CmdList.AhciCmdW   = Read ? 0 : 1;
 | |
| 
 | |
|   for (Retry = 0; Retry < AHCI_COMMAND_RETRIES; Retry++) {
 | |
|     AhciBuildCommand (
 | |
|       PciIo,
 | |
|       AhciRegisters,
 | |
|       Port,
 | |
|       PortMultiplier,
 | |
|       &CFis,
 | |
|       &CmdList,
 | |
|       AtapiCommand,
 | |
|       AtapiCommandLength,
 | |
|       0,
 | |
|       (VOID *)(UINTN)PhyAddr,
 | |
|       DataCount
 | |
|       );
 | |
| 
 | |
|     DEBUG ((DEBUG_VERBOSE, "Starting command for PIO transfer:\n"));
 | |
|     AhciPrintCommandBlock (AtaCommandBlock, DEBUG_VERBOSE);
 | |
|     Status = AhciStartCommand (
 | |
|                PciIo,
 | |
|                Port,
 | |
|                0,
 | |
|                Timeout
 | |
|                );
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     if (Read && (AtapiCommand == 0)) {
 | |
|       Status = AhciWaitUntilFisReceived (PciIo, Port, Timeout, SataFisPioSetup);
 | |
|       if (Status == EFI_SUCCESS) {
 | |
|         PrdCount = *(volatile UINT32 *)(&(AhciRegisters->AhciCmdList[0].AhciCmdPrdbc));
 | |
|         if (PrdCount == DataCount) {
 | |
|           Status = EFI_SUCCESS;
 | |
|         } else {
 | |
|           Status = EFI_DEVICE_ERROR;
 | |
|         }
 | |
|       }
 | |
|     } else {
 | |
|       Status = AhciWaitUntilFisReceived (PciIo, Port, Timeout, SataFisD2H);
 | |
|     }
 | |
| 
 | |
|     if (Status == EFI_DEVICE_ERROR) {
 | |
|       DEBUG ((DEBUG_ERROR, "PIO command failed at retry %d\n", Retry));
 | |
|       Status = AhciRecoverPortError (PciIo, Port);
 | |
|       if (EFI_ERROR (Status)) {
 | |
|         break;
 | |
|       }
 | |
|     } else {
 | |
|       break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   AhciStopCommand (
 | |
|     PciIo,
 | |
|     Port,
 | |
|     Timeout
 | |
|     );
 | |
| 
 | |
|   AhciDisableFisReceive (
 | |
|     PciIo,
 | |
|     Port,
 | |
|     Timeout
 | |
|     );
 | |
| 
 | |
|   PciIo->Unmap (
 | |
|            PciIo,
 | |
|            Map
 | |
|            );
 | |
| 
 | |
|   AhciDumpPortStatus (PciIo, AhciRegisters, Port, AtaStatusBlock);
 | |
| 
 | |
|   if (Status == EFI_DEVICE_ERROR) {
 | |
|     DEBUG ((DEBUG_ERROR, "Failed to execute command for PIO transfer:\n"));
 | |
|     //
 | |
|     // Repeat command block here to make sure it is printed on
 | |
|     // device error debug level.
 | |
|     //
 | |
|     AhciPrintCommandBlock (AtaCommandBlock, DEBUG_ERROR);
 | |
|     AhciPrintStatusBlock (AtaStatusBlock, DEBUG_ERROR);
 | |
|   } else {
 | |
|     AhciPrintStatusBlock (AtaStatusBlock, DEBUG_VERBOSE);
 | |
|   }
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Start a DMA data transfer on specific port
 | |
| 
 | |
|   @param[in]       Instance            The ATA_ATAPI_PASS_THRU_INSTANCE protocol instance.
 | |
|   @param[in]       AhciRegisters       The pointer to the EFI_AHCI_REGISTERS.
 | |
|   @param[in]       Port                The number of port.
 | |
|   @param[in]       PortMultiplier      The timeout value of stop.
 | |
|   @param[in]       AtapiCommand        The atapi command will be used for the
 | |
|                                        transfer.
 | |
|   @param[in]       AtapiCommandLength  The length of the atapi command.
 | |
|   @param[in]       Read                The transfer direction.
 | |
|   @param[in]       AtaCommandBlock     The EFI_ATA_COMMAND_BLOCK data.
 | |
|   @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, uses 100ns as a unit.
 | |
|   @param[in]       Task                Optional. Pointer to the ATA_NONBLOCK_TASK
 | |
|                                        used by non-blocking mode.
 | |
| 
 | |
|   @retval EFI_DEVICE_ERROR    The DMA data transfer abort with error occurs.
 | |
|   @retval EFI_TIMEOUT         The operation is time out.
 | |
|   @retval EFI_UNSUPPORTED     The device is not ready for transfer.
 | |
|   @retval EFI_SUCCESS         The DMA data transfer executes successfully.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| AhciDmaTransfer (
 | |
|   IN     ATA_ATAPI_PASS_THRU_INSTANCE  *Instance,
 | |
|   IN     EFI_AHCI_REGISTERS            *AhciRegisters,
 | |
|   IN     UINT8                         Port,
 | |
|   IN     UINT8                         PortMultiplier,
 | |
|   IN     EFI_AHCI_ATAPI_COMMAND        *AtapiCommand OPTIONAL,
 | |
|   IN     UINT8                         AtapiCommandLength,
 | |
|   IN     BOOLEAN                       Read,
 | |
|   IN     EFI_ATA_COMMAND_BLOCK         *AtaCommandBlock,
 | |
|   IN OUT EFI_ATA_STATUS_BLOCK          *AtaStatusBlock,
 | |
|   IN OUT VOID                          *MemoryAddr,
 | |
|   IN     UINT32                        DataCount,
 | |
|   IN     UINT64                        Timeout,
 | |
|   IN     ATA_NONBLOCK_TASK             *Task
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS                     Status;
 | |
|   EFI_PHYSICAL_ADDRESS           PhyAddr;
 | |
|   VOID                           *Map;
 | |
|   UINTN                          MapLength;
 | |
|   EFI_PCI_IO_PROTOCOL_OPERATION  Flag;
 | |
|   EFI_AHCI_COMMAND_FIS           CFis;
 | |
|   EFI_AHCI_COMMAND_LIST          CmdList;
 | |
|   EFI_PCI_IO_PROTOCOL            *PciIo;
 | |
|   EFI_TPL                        OldTpl;
 | |
|   UINT32                         Retry;
 | |
| 
 | |
|   Map   = NULL;
 | |
|   PciIo = Instance->PciIo;
 | |
| 
 | |
|   if (PciIo == NULL) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Set Status to suppress incorrect compiler/analyzer warnings
 | |
|   //
 | |
|   Status = EFI_SUCCESS;
 | |
| 
 | |
|   //
 | |
|   // DMA buffer allocation. Needs to be done only once for both sync and async
 | |
|   // DMA transfers irrespective of number of retries.
 | |
|   //
 | |
|   if ((Task == NULL) || ((Task != NULL) && (Task->Map == NULL))) {
 | |
|     if (Read) {
 | |
|       Flag = EfiPciIoOperationBusMasterWrite;
 | |
|     } else {
 | |
|       Flag = EfiPciIoOperationBusMasterRead;
 | |
|     }
 | |
| 
 | |
|     MapLength = DataCount;
 | |
|     Status    = PciIo->Map (
 | |
|                          PciIo,
 | |
|                          Flag,
 | |
|                          MemoryAddr,
 | |
|                          &MapLength,
 | |
|                          &PhyAddr,
 | |
|                          &Map
 | |
|                          );
 | |
| 
 | |
|     if (EFI_ERROR (Status) || (DataCount != MapLength)) {
 | |
|       return EFI_BAD_BUFFER_SIZE;
 | |
|     }
 | |
| 
 | |
|     if (Task != NULL) {
 | |
|       Task->Map = Map;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if ((Task == NULL) || ((Task != NULL) && !Task->IsStart)) {
 | |
|     AhciBuildCommandFis (&CFis, AtaCommandBlock);
 | |
| 
 | |
|     ZeroMem (&CmdList, sizeof (EFI_AHCI_COMMAND_LIST));
 | |
| 
 | |
|     CmdList.AhciCmdCfl = EFI_AHCI_FIS_REGISTER_H2D_LENGTH / 4;
 | |
|     CmdList.AhciCmdW   = Read ? 0 : 1;
 | |
|   }
 | |
| 
 | |
|   if (Task == NULL) {
 | |
|     //
 | |
|     // Before starting the Blocking BlockIO operation, push to finish all non-blocking
 | |
|     // BlockIO tasks.
 | |
|     // Delay 100us to simulate the blocking time out checking.
 | |
|     //
 | |
|     OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
 | |
|     while (!IsListEmpty (&Instance->NonBlockingTaskList)) {
 | |
|       AsyncNonBlockingTransferRoutine (NULL, Instance);
 | |
|       //
 | |
|       // Stall for 100us.
 | |
|       //
 | |
|       MicroSecondDelay (100);
 | |
|     }
 | |
| 
 | |
|     gBS->RestoreTPL (OldTpl);
 | |
|     for (Retry = 0; Retry < AHCI_COMMAND_RETRIES; Retry++) {
 | |
|       AhciBuildCommand (
 | |
|         PciIo,
 | |
|         AhciRegisters,
 | |
|         Port,
 | |
|         PortMultiplier,
 | |
|         &CFis,
 | |
|         &CmdList,
 | |
|         AtapiCommand,
 | |
|         AtapiCommandLength,
 | |
|         0,
 | |
|         (VOID *)(UINTN)PhyAddr,
 | |
|         DataCount
 | |
|         );
 | |
| 
 | |
|       DEBUG ((DEBUG_VERBOSE, "Starting command for sync DMA transfer:\n"));
 | |
|       AhciPrintCommandBlock (AtaCommandBlock, DEBUG_VERBOSE);
 | |
|       Status = AhciStartCommand (
 | |
|                  PciIo,
 | |
|                  Port,
 | |
|                  0,
 | |
|                  Timeout
 | |
|                  );
 | |
|       if (EFI_ERROR (Status)) {
 | |
|         break;
 | |
|       }
 | |
| 
 | |
|       Status = AhciWaitUntilFisReceived (PciIo, Port, Timeout, SataFisD2H);
 | |
|       if (Status == EFI_DEVICE_ERROR) {
 | |
|         DEBUG ((DEBUG_ERROR, "DMA command failed at retry: %d\n", Retry));
 | |
|         Status = AhciRecoverPortError (PciIo, Port);
 | |
|         if (EFI_ERROR (Status)) {
 | |
|           break;
 | |
|         }
 | |
|       } else {
 | |
|         break;
 | |
|       }
 | |
|     }
 | |
|   } else {
 | |
|     if (!Task->IsStart) {
 | |
|       AhciBuildCommand (
 | |
|         PciIo,
 | |
|         AhciRegisters,
 | |
|         Port,
 | |
|         PortMultiplier,
 | |
|         &CFis,
 | |
|         &CmdList,
 | |
|         AtapiCommand,
 | |
|         AtapiCommandLength,
 | |
|         0,
 | |
|         (VOID *)(UINTN)PhyAddr,
 | |
|         DataCount
 | |
|         );
 | |
| 
 | |
|       DEBUG ((DEBUG_VERBOSE, "Starting command for async DMA transfer:\n"));
 | |
|       AhciPrintCommandBlock (AtaCommandBlock, DEBUG_VERBOSE);
 | |
|       Status = AhciStartCommand (
 | |
|                  PciIo,
 | |
|                  Port,
 | |
|                  0,
 | |
|                  Timeout
 | |
|                  );
 | |
|       if (!EFI_ERROR (Status)) {
 | |
|         Task->IsStart = TRUE;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     if (Task->IsStart) {
 | |
|       Status = AhciCheckFisReceived (PciIo, Port, SataFisD2H);
 | |
|       if (Status == EFI_DEVICE_ERROR) {
 | |
|         DEBUG ((DEBUG_ERROR, "DMA command failed at retry: %d\n", Task->RetryTimes));
 | |
|         Status = AhciRecoverPortError (PciIo, Port);
 | |
|         //
 | |
|         // If recovery passed mark the Task as not started and change the status
 | |
|         // to EFI_NOT_READY. This will make the higher level call this function again
 | |
|         // and on next call the command will be re-issued due to IsStart being FALSE.
 | |
|         // This also makes the next condition decrement the RetryTimes.
 | |
|         //
 | |
|         if (Status == EFI_SUCCESS) {
 | |
|           Task->IsStart = FALSE;
 | |
|           Status        = EFI_NOT_READY;
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       if (Status == EFI_NOT_READY) {
 | |
|         if (!Task->InfiniteWait && (Task->RetryTimes == 0)) {
 | |
|           Status = EFI_TIMEOUT;
 | |
|         } else {
 | |
|           Task->RetryTimes--;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // For Blocking mode, the command should be stopped, the Fis should be disabled
 | |
|   // and the PciIo should be unmapped.
 | |
|   // For non-blocking mode, only when a error is happened (if the return status is
 | |
|   // EFI_NOT_READY that means the command doesn't finished, try again.), first do the
 | |
|   // context cleanup, then set the packet's Asb status.
 | |
|   //
 | |
|   if ((Task == NULL) ||
 | |
|       ((Task != NULL) && (Status != EFI_NOT_READY))
 | |
|       )
 | |
|   {
 | |
|     AhciStopCommand (
 | |
|       PciIo,
 | |
|       Port,
 | |
|       Timeout
 | |
|       );
 | |
| 
 | |
|     AhciDisableFisReceive (
 | |
|       PciIo,
 | |
|       Port,
 | |
|       Timeout
 | |
|       );
 | |
| 
 | |
|     PciIo->Unmap (
 | |
|              PciIo,
 | |
|              (Task != NULL) ? Task->Map : Map
 | |
|              );
 | |
| 
 | |
|     if (Task != NULL) {
 | |
|       Task->Packet->Asb->AtaStatus = 0x01;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   AhciDumpPortStatus (PciIo, AhciRegisters, Port, AtaStatusBlock);
 | |
| 
 | |
|   if (Status == EFI_DEVICE_ERROR) {
 | |
|     DEBUG ((DEBUG_ERROR, "Failed to execute command for DMA transfer:\n"));
 | |
|     //
 | |
|     // Repeat command block here to make sure it is printed on
 | |
|     // device error debug level.
 | |
|     //
 | |
|     AhciPrintCommandBlock (AtaCommandBlock, DEBUG_ERROR);
 | |
|     AhciPrintStatusBlock (AtaStatusBlock, DEBUG_ERROR);
 | |
|   } else {
 | |
|     AhciPrintStatusBlock (AtaStatusBlock, DEBUG_VERBOSE);
 | |
|   }
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Start a non data transfer on specific port.
 | |
| 
 | |
|   @param[in]       PciIo               The PCI IO protocol instance.
 | |
|   @param[in]       AhciRegisters       The pointer to the EFI_AHCI_REGISTERS.
 | |
|   @param[in]       Port                The number of port.
 | |
|   @param[in]       PortMultiplier      The timeout value of stop.
 | |
|   @param[in]       AtapiCommand        The atapi command will be used for the
 | |
|                                        transfer.
 | |
|   @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, uses 100ns as a unit.
 | |
|   @param[in]       Task                Optional. Pointer to the ATA_NONBLOCK_TASK
 | |
|                                        used by non-blocking mode.
 | |
| 
 | |
|   @retval EFI_DEVICE_ERROR    The non data transfer abort with error occurs.
 | |
|   @retval EFI_TIMEOUT         The operation is time out.
 | |
|   @retval EFI_UNSUPPORTED     The device is not ready for transfer.
 | |
|   @retval EFI_SUCCESS         The non data transfer executes successfully.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| AhciNonDataTransfer (
 | |
|   IN     EFI_PCI_IO_PROTOCOL     *PciIo,
 | |
|   IN     EFI_AHCI_REGISTERS      *AhciRegisters,
 | |
|   IN     UINT8                   Port,
 | |
|   IN     UINT8                   PortMultiplier,
 | |
|   IN     EFI_AHCI_ATAPI_COMMAND  *AtapiCommand OPTIONAL,
 | |
|   IN     UINT8                   AtapiCommandLength,
 | |
|   IN     EFI_ATA_COMMAND_BLOCK   *AtaCommandBlock,
 | |
|   IN OUT EFI_ATA_STATUS_BLOCK    *AtaStatusBlock,
 | |
|   IN     UINT64                  Timeout,
 | |
|   IN     ATA_NONBLOCK_TASK       *Task
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS             Status;
 | |
|   EFI_AHCI_COMMAND_FIS   CFis;
 | |
|   EFI_AHCI_COMMAND_LIST  CmdList;
 | |
|   UINT32                 Retry;
 | |
| 
 | |
|   //
 | |
|   // Package read needed
 | |
|   //
 | |
|   AhciBuildCommandFis (&CFis, AtaCommandBlock);
 | |
| 
 | |
|   ZeroMem (&CmdList, sizeof (EFI_AHCI_COMMAND_LIST));
 | |
| 
 | |
|   CmdList.AhciCmdCfl = EFI_AHCI_FIS_REGISTER_H2D_LENGTH / 4;
 | |
| 
 | |
|   for (Retry = 0; Retry < AHCI_COMMAND_RETRIES; Retry++) {
 | |
|     AhciBuildCommand (
 | |
|       PciIo,
 | |
|       AhciRegisters,
 | |
|       Port,
 | |
|       PortMultiplier,
 | |
|       &CFis,
 | |
|       &CmdList,
 | |
|       AtapiCommand,
 | |
|       AtapiCommandLength,
 | |
|       0,
 | |
|       NULL,
 | |
|       0
 | |
|       );
 | |
| 
 | |
|     DEBUG ((DEBUG_VERBOSE, "Starting command for non data transfer:\n"));
 | |
|     AhciPrintCommandBlock (AtaCommandBlock, DEBUG_VERBOSE);
 | |
|     Status = AhciStartCommand (
 | |
|                PciIo,
 | |
|                Port,
 | |
|                0,
 | |
|                Timeout
 | |
|                );
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     Status = AhciWaitUntilFisReceived (PciIo, Port, Timeout, SataFisD2H);
 | |
|     if (Status == EFI_DEVICE_ERROR) {
 | |
|       DEBUG ((DEBUG_ERROR, "Non data transfer failed at retry %d\n", Retry));
 | |
|       Status = AhciRecoverPortError (PciIo, Port);
 | |
|       if (EFI_ERROR (Status)) {
 | |
|         break;
 | |
|       }
 | |
|     } else {
 | |
|       break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   AhciStopCommand (
 | |
|     PciIo,
 | |
|     Port,
 | |
|     Timeout
 | |
|     );
 | |
| 
 | |
|   AhciDisableFisReceive (
 | |
|     PciIo,
 | |
|     Port,
 | |
|     Timeout
 | |
|     );
 | |
| 
 | |
|   AhciDumpPortStatus (PciIo, AhciRegisters, Port, AtaStatusBlock);
 | |
| 
 | |
|   if (Status == EFI_DEVICE_ERROR) {
 | |
|     DEBUG ((DEBUG_ERROR, "Failed to execute command for non data transfer:\n"));
 | |
|     //
 | |
|     // Repeat command block here to make sure it is printed on
 | |
|     // device error debug level.
 | |
|     //
 | |
|     AhciPrintCommandBlock (AtaCommandBlock, DEBUG_ERROR);
 | |
|     AhciPrintStatusBlock (AtaStatusBlock, DEBUG_ERROR);
 | |
|   } else {
 | |
|     AhciPrintStatusBlock (AtaStatusBlock, DEBUG_VERBOSE);
 | |
|   }
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Stop command running for giving port
 | |
| 
 | |
|   @param  PciIo              The PCI IO protocol instance.
 | |
|   @param  Port               The number of port.
 | |
|   @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.
 | |
|   @retval EFI_SUCCESS        The command stop successfully.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| AhciStopCommand (
 | |
|   IN  EFI_PCI_IO_PROTOCOL  *PciIo,
 | |
|   IN  UINT8                Port,
 | |
|   IN  UINT64               Timeout
 | |
|   )
 | |
| {
 | |
|   UINT32  Offset;
 | |
|   UINT32  Data;
 | |
| 
 | |
|   Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;
 | |
|   Data   = AhciReadReg (PciIo, Offset);
 | |
| 
 | |
|   if ((Data & (EFI_AHCI_PORT_CMD_ST |  EFI_AHCI_PORT_CMD_CR)) == 0) {
 | |
|     return EFI_SUCCESS;
 | |
|   }
 | |
| 
 | |
|   if ((Data & EFI_AHCI_PORT_CMD_ST) != 0) {
 | |
|     AhciAndReg (PciIo, Offset, (UINT32) ~(EFI_AHCI_PORT_CMD_ST));
 | |
|   }
 | |
| 
 | |
|   return AhciWaitMmioSet (
 | |
|            PciIo,
 | |
|            Offset,
 | |
|            EFI_AHCI_PORT_CMD_CR,
 | |
|            0,
 | |
|            Timeout
 | |
|            );
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Start command for give slot on specific port.
 | |
| 
 | |
|   @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, uses 100ns as a unit.
 | |
| 
 | |
|   @retval EFI_DEVICE_ERROR   The command start unsuccessfully.
 | |
|   @retval EFI_TIMEOUT        The operation is time out.
 | |
|   @retval EFI_SUCCESS        The command start successfully.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| AhciStartCommand (
 | |
|   IN  EFI_PCI_IO_PROTOCOL  *PciIo,
 | |
|   IN  UINT8                Port,
 | |
|   IN  UINT8                CommandSlot,
 | |
|   IN  UINT64               Timeout
 | |
|   )
 | |
| {
 | |
|   UINT32      CmdSlotBit;
 | |
|   EFI_STATUS  Status;
 | |
|   UINT32      PortStatus;
 | |
|   UINT32      StartCmd;
 | |
|   UINT32      PortTfd;
 | |
|   UINT32      Offset;
 | |
|   UINT32      Capability;
 | |
| 
 | |
|   //
 | |
|   // Collect AHCI controller information
 | |
|   //
 | |
|   Capability = AhciReadReg (PciIo, EFI_AHCI_CAPABILITY_OFFSET);
 | |
| 
 | |
|   CmdSlotBit = (UINT32)(1 << CommandSlot);
 | |
| 
 | |
|   AhciClearPortStatus (
 | |
|     PciIo,
 | |
|     Port
 | |
|     );
 | |
| 
 | |
|   Status = AhciEnableFisReceive (
 | |
|              PciIo,
 | |
|              Port,
 | |
|              Timeout
 | |
|              );
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   Offset     = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;
 | |
|   PortStatus = AhciReadReg (PciIo, Offset);
 | |
| 
 | |
|   StartCmd = 0;
 | |
|   if ((PortStatus & EFI_AHCI_PORT_CMD_ALPE) != 0) {
 | |
|     StartCmd  = AhciReadReg (PciIo, Offset);
 | |
|     StartCmd &= ~EFI_AHCI_PORT_CMD_ICC_MASK;
 | |
|     StartCmd |= EFI_AHCI_PORT_CMD_ACTIVE;
 | |
|   }
 | |
| 
 | |
|   Offset  = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;
 | |
|   PortTfd = AhciReadReg (PciIo, Offset);
 | |
| 
 | |
|   if ((PortTfd & (EFI_AHCI_PORT_TFD_BSY | EFI_AHCI_PORT_TFD_DRQ)) != 0) {
 | |
|     if ((Capability & BIT24) != 0) {
 | |
|       Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;
 | |
|       AhciOrReg (PciIo, Offset, EFI_AHCI_PORT_CMD_CLO);
 | |
| 
 | |
|       AhciWaitMmioSet (
 | |
|         PciIo,
 | |
|         Offset,
 | |
|         EFI_AHCI_PORT_CMD_CLO,
 | |
|         0,
 | |
|         Timeout
 | |
|         );
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;
 | |
|   AhciOrReg (PciIo, Offset, EFI_AHCI_PORT_CMD_ST | StartCmd);
 | |
| 
 | |
|   //
 | |
|   // Setting the command
 | |
|   //
 | |
|   Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CI;
 | |
|   AhciAndReg (PciIo, Offset, 0);
 | |
|   AhciOrReg (PciIo, Offset, CmdSlotBit);
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Do AHCI HBA reset.
 | |
| 
 | |
|   @param  PciIo              The PCI IO protocol instance.
 | |
|   @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.
 | |
|   @retval EFI_SUCCESS        AHCI controller is reset successfully.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| AhciReset (
 | |
|   IN  EFI_PCI_IO_PROTOCOL  *PciIo,
 | |
|   IN  UINT64               Timeout
 | |
|   )
 | |
| {
 | |
|   UINT64  Delay;
 | |
|   UINT32  Value;
 | |
| 
 | |
|   //
 | |
|   // Make sure that GHC.AE bit is set before accessing any AHCI registers.
 | |
|   //
 | |
|   Value = AhciReadReg (PciIo, EFI_AHCI_GHC_OFFSET);
 | |
| 
 | |
|   if ((Value & EFI_AHCI_GHC_ENABLE) == 0) {
 | |
|     AhciOrReg (PciIo, EFI_AHCI_GHC_OFFSET, EFI_AHCI_GHC_ENABLE);
 | |
|   }
 | |
| 
 | |
|   AhciOrReg (PciIo, EFI_AHCI_GHC_OFFSET, EFI_AHCI_GHC_RESET);
 | |
| 
 | |
|   Delay = DivU64x32 (Timeout, 1000) + 1;
 | |
| 
 | |
|   do {
 | |
|     Value = AhciReadReg (PciIo, EFI_AHCI_GHC_OFFSET);
 | |
| 
 | |
|     if ((Value & EFI_AHCI_GHC_RESET) == 0) {
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Stall for 100 microseconds.
 | |
|     //
 | |
|     MicroSecondDelay (100);
 | |
| 
 | |
|     Delay--;
 | |
|   } while (Delay > 0);
 | |
| 
 | |
|   if (Delay == 0) {
 | |
|     return EFI_TIMEOUT;
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Send SMART Return Status command to check if the execution of SMART cmd is successful or not.
 | |
| 
 | |
|   @param  PciIo               The PCI IO protocol instance.
 | |
|   @param  AhciRegisters       The pointer to the EFI_AHCI_REGISTERS.
 | |
|   @param  Port                The number of port.
 | |
|   @param  PortMultiplier      The port multiplier port number.
 | |
|   @param  AtaStatusBlock      A pointer to EFI_ATA_STATUS_BLOCK data structure.
 | |
| 
 | |
|   @retval EFI_SUCCESS     Successfully get the return status of S.M.A.R.T command execution.
 | |
|   @retval Others          Fail to get return status data.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| AhciAtaSmartReturnStatusCheck (
 | |
|   IN EFI_PCI_IO_PROTOCOL       *PciIo,
 | |
|   IN EFI_AHCI_REGISTERS        *AhciRegisters,
 | |
|   IN UINT8                     Port,
 | |
|   IN UINT8                     PortMultiplier,
 | |
|   IN OUT EFI_ATA_STATUS_BLOCK  *AtaStatusBlock
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS             Status;
 | |
|   EFI_ATA_COMMAND_BLOCK  AtaCommandBlock;
 | |
|   UINT8                  LBAMid;
 | |
|   UINT8                  LBAHigh;
 | |
|   UINTN                  FisBaseAddr;
 | |
|   UINT32                 Value;
 | |
| 
 | |
|   ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK));
 | |
| 
 | |
|   AtaCommandBlock.AtaCommand      = ATA_CMD_SMART;
 | |
|   AtaCommandBlock.AtaFeatures     = ATA_SMART_RETURN_STATUS;
 | |
|   AtaCommandBlock.AtaCylinderLow  = ATA_CONSTANT_4F;
 | |
|   AtaCommandBlock.AtaCylinderHigh = ATA_CONSTANT_C2;
 | |
| 
 | |
|   //
 | |
|   // Send S.M.A.R.T Read Return Status command to device
 | |
|   //
 | |
|   Status = AhciNonDataTransfer (
 | |
|              PciIo,
 | |
|              AhciRegisters,
 | |
|              (UINT8)Port,
 | |
|              (UINT8)PortMultiplier,
 | |
|              NULL,
 | |
|              0,
 | |
|              &AtaCommandBlock,
 | |
|              AtaStatusBlock,
 | |
|              ATA_ATAPI_TIMEOUT,
 | |
|              NULL
 | |
|              );
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     REPORT_STATUS_CODE (
 | |
|       EFI_ERROR_CODE | EFI_ERROR_MINOR,
 | |
|       (EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_DISABLED)
 | |
|       );
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
| 
 | |
|   REPORT_STATUS_CODE (
 | |
|     EFI_PROGRESS_CODE,
 | |
|     (EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_ENABLE)
 | |
|     );
 | |
| 
 | |
|   FisBaseAddr = (UINTN)AhciRegisters->AhciRFis + Port * sizeof (EFI_AHCI_RECEIVED_FIS);
 | |
| 
 | |
|   Value = *(UINT32 *)(FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET);
 | |
| 
 | |
|   if ((Value & EFI_AHCI_FIS_TYPE_MASK) == EFI_AHCI_FIS_REGISTER_D2H) {
 | |
|     LBAMid  = ((UINT8 *)(UINTN)(FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET))[5];
 | |
|     LBAHigh = ((UINT8 *)(UINTN)(FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET))[6];
 | |
| 
 | |
|     if ((LBAMid == 0x4f) && (LBAHigh == 0xc2)) {
 | |
|       //
 | |
|       // The threshold exceeded condition is not detected by the device
 | |
|       //
 | |
|       DEBUG ((DEBUG_INFO, "The S.M.A.R.T threshold exceeded condition is not detected\n"));
 | |
|       REPORT_STATUS_CODE (
 | |
|         EFI_PROGRESS_CODE,
 | |
|         (EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_UNDERTHRESHOLD)
 | |
|         );
 | |
|     } else if ((LBAMid == 0xf4) && (LBAHigh == 0x2c)) {
 | |
|       //
 | |
|       // The threshold exceeded condition is detected by the device
 | |
|       //
 | |
|       DEBUG ((DEBUG_INFO, "The S.M.A.R.T threshold exceeded condition is detected\n"));
 | |
|       REPORT_STATUS_CODE (
 | |
|         EFI_PROGRESS_CODE,
 | |
|         (EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_OVERTHRESHOLD)
 | |
|         );
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Enable SMART command of the disk if supported.
 | |
| 
 | |
|   @param  PciIo               The PCI IO protocol instance.
 | |
|   @param  AhciRegisters       The pointer to the EFI_AHCI_REGISTERS.
 | |
|   @param  Port                The number of port.
 | |
|   @param  PortMultiplier      The port multiplier port number.
 | |
|   @param  IdentifyData        A pointer to data buffer which is used to contain IDENTIFY data.
 | |
|   @param  AtaStatusBlock      A pointer to EFI_ATA_STATUS_BLOCK data structure.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| EFIAPI
 | |
| AhciAtaSmartSupport (
 | |
|   IN EFI_PCI_IO_PROTOCOL       *PciIo,
 | |
|   IN EFI_AHCI_REGISTERS        *AhciRegisters,
 | |
|   IN UINT8                     Port,
 | |
|   IN UINT8                     PortMultiplier,
 | |
|   IN EFI_IDENTIFY_DATA         *IdentifyData,
 | |
|   IN OUT EFI_ATA_STATUS_BLOCK  *AtaStatusBlock
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS             Status;
 | |
|   EFI_ATA_COMMAND_BLOCK  AtaCommandBlock;
 | |
| 
 | |
|   //
 | |
|   // Detect if the device supports S.M.A.R.T.
 | |
|   //
 | |
|   if ((IdentifyData->AtaData.command_set_supported_82 & 0x0001) != 0x0001) {
 | |
|     //
 | |
|     // S.M.A.R.T is not supported by the device
 | |
|     //
 | |
|     DEBUG ((
 | |
|       DEBUG_INFO,
 | |
|       "S.M.A.R.T feature is not supported at port [%d] PortMultiplier [%d]!\n",
 | |
|       Port,
 | |
|       PortMultiplier
 | |
|       ));
 | |
|     REPORT_STATUS_CODE (
 | |
|       EFI_ERROR_CODE | EFI_ERROR_MINOR,
 | |
|       (EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_NOTSUPPORTED)
 | |
|       );
 | |
|   } else {
 | |
|     //
 | |
|     // Check if the feature is enabled. If not, then enable S.M.A.R.T.
 | |
|     //
 | |
|     if ((IdentifyData->AtaData.command_set_feature_enb_85 & 0x0001) != 0x0001) {
 | |
|       REPORT_STATUS_CODE (
 | |
|         EFI_PROGRESS_CODE,
 | |
|         (EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_DISABLE)
 | |
|         );
 | |
| 
 | |
|       ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK));
 | |
| 
 | |
|       AtaCommandBlock.AtaCommand      = ATA_CMD_SMART;
 | |
|       AtaCommandBlock.AtaFeatures     = ATA_SMART_ENABLE_OPERATION;
 | |
|       AtaCommandBlock.AtaCylinderLow  = ATA_CONSTANT_4F;
 | |
|       AtaCommandBlock.AtaCylinderHigh = ATA_CONSTANT_C2;
 | |
| 
 | |
|       //
 | |
|       // Send S.M.A.R.T Enable command to device
 | |
|       //
 | |
|       Status = AhciNonDataTransfer (
 | |
|                  PciIo,
 | |
|                  AhciRegisters,
 | |
|                  (UINT8)Port,
 | |
|                  (UINT8)PortMultiplier,
 | |
|                  NULL,
 | |
|                  0,
 | |
|                  &AtaCommandBlock,
 | |
|                  AtaStatusBlock,
 | |
|                  ATA_ATAPI_TIMEOUT,
 | |
|                  NULL
 | |
|                  );
 | |
| 
 | |
|       if (!EFI_ERROR (Status)) {
 | |
|         //
 | |
|         // Send S.M.A.R.T AutoSave command to device
 | |
|         //
 | |
|         ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK));
 | |
| 
 | |
|         AtaCommandBlock.AtaCommand      = ATA_CMD_SMART;
 | |
|         AtaCommandBlock.AtaFeatures     = 0xD2;
 | |
|         AtaCommandBlock.AtaSectorCount  = 0xF1;
 | |
|         AtaCommandBlock.AtaCylinderLow  = ATA_CONSTANT_4F;
 | |
|         AtaCommandBlock.AtaCylinderHigh = ATA_CONSTANT_C2;
 | |
| 
 | |
|         Status = AhciNonDataTransfer (
 | |
|                    PciIo,
 | |
|                    AhciRegisters,
 | |
|                    (UINT8)Port,
 | |
|                    (UINT8)PortMultiplier,
 | |
|                    NULL,
 | |
|                    0,
 | |
|                    &AtaCommandBlock,
 | |
|                    AtaStatusBlock,
 | |
|                    ATA_ATAPI_TIMEOUT,
 | |
|                    NULL
 | |
|                    );
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     AhciAtaSmartReturnStatusCheck (
 | |
|       PciIo,
 | |
|       AhciRegisters,
 | |
|       (UINT8)Port,
 | |
|       (UINT8)PortMultiplier,
 | |
|       AtaStatusBlock
 | |
|       );
 | |
| 
 | |
|     DEBUG ((
 | |
|       DEBUG_INFO,
 | |
|       "Enabled S.M.A.R.T feature at port [%d] PortMultiplier [%d]!\n",
 | |
|       Port,
 | |
|       PortMultiplier
 | |
|       ));
 | |
|   }
 | |
| 
 | |
|   return;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Send Buffer cmd to specific device.
 | |
| 
 | |
|   @param  PciIo               The PCI IO protocol instance.
 | |
|   @param  AhciRegisters       The pointer to the EFI_AHCI_REGISTERS.
 | |
|   @param  Port                The number of port.
 | |
|   @param  PortMultiplier      The port multiplier port number.
 | |
|   @param  Buffer              The data buffer to store IDENTIFY PACKET data.
 | |
| 
 | |
|   @retval EFI_DEVICE_ERROR    The cmd abort with error occurs.
 | |
|   @retval EFI_TIMEOUT         The operation is time out.
 | |
|   @retval EFI_UNSUPPORTED     The device is not ready for executing.
 | |
|   @retval EFI_SUCCESS         The cmd executes successfully.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| AhciIdentify (
 | |
|   IN EFI_PCI_IO_PROTOCOL    *PciIo,
 | |
|   IN EFI_AHCI_REGISTERS     *AhciRegisters,
 | |
|   IN UINT8                  Port,
 | |
|   IN UINT8                  PortMultiplier,
 | |
|   IN OUT EFI_IDENTIFY_DATA  *Buffer
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS             Status;
 | |
|   EFI_ATA_COMMAND_BLOCK  AtaCommandBlock;
 | |
|   EFI_ATA_STATUS_BLOCK   AtaStatusBlock;
 | |
| 
 | |
|   if ((PciIo == NULL) || (AhciRegisters == NULL) || (Buffer == NULL)) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK));
 | |
|   ZeroMem (&AtaStatusBlock, sizeof (EFI_ATA_STATUS_BLOCK));
 | |
| 
 | |
|   AtaCommandBlock.AtaCommand     = ATA_CMD_IDENTIFY_DRIVE;
 | |
|   AtaCommandBlock.AtaSectorCount = 1;
 | |
| 
 | |
|   Status = AhciPioTransfer (
 | |
|              PciIo,
 | |
|              AhciRegisters,
 | |
|              Port,
 | |
|              PortMultiplier,
 | |
|              NULL,
 | |
|              0,
 | |
|              TRUE,
 | |
|              &AtaCommandBlock,
 | |
|              &AtaStatusBlock,
 | |
|              Buffer,
 | |
|              sizeof (EFI_IDENTIFY_DATA),
 | |
|              ATA_ATAPI_TIMEOUT,
 | |
|              NULL
 | |
|              );
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Send Buffer cmd to specific device.
 | |
| 
 | |
|   @param  PciIo               The PCI IO protocol instance.
 | |
|   @param  AhciRegisters       The pointer to the EFI_AHCI_REGISTERS.
 | |
|   @param  Port                The number of port.
 | |
|   @param  PortMultiplier      The port multiplier port number.
 | |
|   @param  Buffer              The data buffer to store IDENTIFY PACKET data.
 | |
| 
 | |
|   @retval EFI_DEVICE_ERROR    The cmd abort with error occurs.
 | |
|   @retval EFI_TIMEOUT         The operation is time out.
 | |
|   @retval EFI_UNSUPPORTED     The device is not ready for executing.
 | |
|   @retval EFI_SUCCESS         The cmd executes successfully.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| AhciIdentifyPacket (
 | |
|   IN EFI_PCI_IO_PROTOCOL    *PciIo,
 | |
|   IN EFI_AHCI_REGISTERS     *AhciRegisters,
 | |
|   IN UINT8                  Port,
 | |
|   IN UINT8                  PortMultiplier,
 | |
|   IN OUT EFI_IDENTIFY_DATA  *Buffer
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS             Status;
 | |
|   EFI_ATA_COMMAND_BLOCK  AtaCommandBlock;
 | |
|   EFI_ATA_STATUS_BLOCK   AtaStatusBlock;
 | |
| 
 | |
|   if ((PciIo == NULL) || (AhciRegisters == NULL)) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK));
 | |
|   ZeroMem (&AtaStatusBlock, sizeof (EFI_ATA_STATUS_BLOCK));
 | |
| 
 | |
|   AtaCommandBlock.AtaCommand     = ATA_CMD_IDENTIFY_DEVICE;
 | |
|   AtaCommandBlock.AtaSectorCount = 1;
 | |
| 
 | |
|   Status = AhciPioTransfer (
 | |
|              PciIo,
 | |
|              AhciRegisters,
 | |
|              Port,
 | |
|              PortMultiplier,
 | |
|              NULL,
 | |
|              0,
 | |
|              TRUE,
 | |
|              &AtaCommandBlock,
 | |
|              &AtaStatusBlock,
 | |
|              Buffer,
 | |
|              sizeof (EFI_IDENTIFY_DATA),
 | |
|              ATA_ATAPI_TIMEOUT,
 | |
|              NULL
 | |
|              );
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Send SET FEATURE cmd on specific device.
 | |
| 
 | |
|   @param  PciIo               The PCI IO protocol instance.
 | |
|   @param  AhciRegisters       The pointer to the EFI_AHCI_REGISTERS.
 | |
|   @param  Port                The number of port.
 | |
|   @param  PortMultiplier      The port multiplier port number.
 | |
|   @param  Feature             The data to send Feature register.
 | |
|   @param  FeatureSpecificData The specific data for SET FEATURE cmd.
 | |
|   @param  Timeout             The timeout value of SET FEATURE cmd, uses 100ns as a unit.
 | |
| 
 | |
|   @retval EFI_DEVICE_ERROR    The cmd abort with error occurs.
 | |
|   @retval EFI_TIMEOUT         The operation is time out.
 | |
|   @retval EFI_UNSUPPORTED     The device is not ready for executing.
 | |
|   @retval EFI_SUCCESS         The cmd executes successfully.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| AhciDeviceSetFeature (
 | |
|   IN EFI_PCI_IO_PROTOCOL  *PciIo,
 | |
|   IN EFI_AHCI_REGISTERS   *AhciRegisters,
 | |
|   IN UINT8                Port,
 | |
|   IN UINT8                PortMultiplier,
 | |
|   IN UINT16               Feature,
 | |
|   IN UINT32               FeatureSpecificData,
 | |
|   IN UINT64               Timeout
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS             Status;
 | |
|   EFI_ATA_COMMAND_BLOCK  AtaCommandBlock;
 | |
|   EFI_ATA_STATUS_BLOCK   AtaStatusBlock;
 | |
| 
 | |
|   ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK));
 | |
|   ZeroMem (&AtaStatusBlock, sizeof (EFI_ATA_STATUS_BLOCK));
 | |
| 
 | |
|   AtaCommandBlock.AtaCommand      = ATA_CMD_SET_FEATURES;
 | |
|   AtaCommandBlock.AtaFeatures     = (UINT8)Feature;
 | |
|   AtaCommandBlock.AtaFeaturesExp  = (UINT8)(Feature >> 8);
 | |
|   AtaCommandBlock.AtaSectorCount  = (UINT8)FeatureSpecificData;
 | |
|   AtaCommandBlock.AtaSectorNumber = (UINT8)(FeatureSpecificData >> 8);
 | |
|   AtaCommandBlock.AtaCylinderLow  = (UINT8)(FeatureSpecificData >> 16);
 | |
|   AtaCommandBlock.AtaCylinderHigh = (UINT8)(FeatureSpecificData >> 24);
 | |
| 
 | |
|   Status = AhciNonDataTransfer (
 | |
|              PciIo,
 | |
|              AhciRegisters,
 | |
|              (UINT8)Port,
 | |
|              (UINT8)PortMultiplier,
 | |
|              NULL,
 | |
|              0,
 | |
|              &AtaCommandBlock,
 | |
|              &AtaStatusBlock,
 | |
|              Timeout,
 | |
|              NULL
 | |
|              );
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   This function is used to send out ATAPI commands conforms to the Packet Command
 | |
|   with PIO Protocol.
 | |
| 
 | |
|   @param PciIo              The PCI IO protocol instance.
 | |
|   @param AhciRegisters      The pointer to the EFI_AHCI_REGISTERS.
 | |
|   @param Port               The number of port.
 | |
|   @param PortMultiplier     The number of port multiplier.
 | |
|   @param Packet             A pointer to EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET structure.
 | |
| 
 | |
|   @retval EFI_SUCCESS       send out the ATAPI packet command successfully
 | |
|                             and device sends data successfully.
 | |
|   @retval EFI_DEVICE_ERROR  the device failed to send data.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| AhciPacketCommandExecute (
 | |
|   IN  EFI_PCI_IO_PROTOCOL                         *PciIo,
 | |
|   IN  EFI_AHCI_REGISTERS                          *AhciRegisters,
 | |
|   IN  UINT8                                       Port,
 | |
|   IN  UINT8                                       PortMultiplier,
 | |
|   IN  EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET  *Packet
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS             Status;
 | |
|   VOID                   *Buffer;
 | |
|   UINT32                 Length;
 | |
|   EFI_ATA_COMMAND_BLOCK  AtaCommandBlock;
 | |
|   EFI_ATA_STATUS_BLOCK   AtaStatusBlock;
 | |
|   BOOLEAN                Read;
 | |
| 
 | |
|   if ((Packet == NULL) || (Packet->Cdb == NULL)) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK));
 | |
|   ZeroMem (&AtaStatusBlock, sizeof (EFI_ATA_STATUS_BLOCK));
 | |
|   AtaCommandBlock.AtaCommand = ATA_CMD_PACKET;
 | |
|   //
 | |
|   // No OVL; No DMA
 | |
|   //
 | |
|   AtaCommandBlock.AtaFeatures = 0x00;
 | |
|   //
 | |
|   // set the transfersize to ATAPI_MAX_BYTE_COUNT to let the device
 | |
|   // determine how many data should be transferred.
 | |
|   //
 | |
|   AtaCommandBlock.AtaCylinderLow  = (UINT8)(ATAPI_MAX_BYTE_COUNT & 0x00ff);
 | |
|   AtaCommandBlock.AtaCylinderHigh = (UINT8)(ATAPI_MAX_BYTE_COUNT >> 8);
 | |
| 
 | |
|   if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) {
 | |
|     Buffer = Packet->InDataBuffer;
 | |
|     Length = Packet->InTransferLength;
 | |
|     Read   = TRUE;
 | |
|   } else {
 | |
|     Buffer = Packet->OutDataBuffer;
 | |
|     Length = Packet->OutTransferLength;
 | |
|     Read   = FALSE;
 | |
|   }
 | |
| 
 | |
|   if (Length == 0) {
 | |
|     Status = AhciNonDataTransfer (
 | |
|                PciIo,
 | |
|                AhciRegisters,
 | |
|                Port,
 | |
|                PortMultiplier,
 | |
|                Packet->Cdb,
 | |
|                Packet->CdbLength,
 | |
|                &AtaCommandBlock,
 | |
|                &AtaStatusBlock,
 | |
|                Packet->Timeout,
 | |
|                NULL
 | |
|                );
 | |
|   } else {
 | |
|     Status = AhciPioTransfer (
 | |
|                PciIo,
 | |
|                AhciRegisters,
 | |
|                Port,
 | |
|                PortMultiplier,
 | |
|                Packet->Cdb,
 | |
|                Packet->CdbLength,
 | |
|                Read,
 | |
|                &AtaCommandBlock,
 | |
|                &AtaStatusBlock,
 | |
|                Buffer,
 | |
|                Length,
 | |
|                Packet->Timeout,
 | |
|                NULL
 | |
|                );
 | |
|   }
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Allocate transfer-related data struct which is used at AHCI mode.
 | |
| 
 | |
|   @param  PciIo                 The PCI IO protocol instance.
 | |
|   @param  AhciRegisters         The pointer to the EFI_AHCI_REGISTERS.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| AhciCreateTransferDescriptor (
 | |
|   IN     EFI_PCI_IO_PROTOCOL  *PciIo,
 | |
|   IN OUT EFI_AHCI_REGISTERS   *AhciRegisters
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
|   UINTN       Bytes;
 | |
|   VOID        *Buffer;
 | |
| 
 | |
|   UINT32                Capability;
 | |
|   UINT32                PortImplementBitMap;
 | |
|   UINT8                 MaxPortNumber;
 | |
|   UINT8                 MaxCommandSlotNumber;
 | |
|   BOOLEAN               Support64Bit;
 | |
|   UINT64                MaxReceiveFisSize;
 | |
|   UINT64                MaxCommandListSize;
 | |
|   UINT64                MaxCommandTableSize;
 | |
|   EFI_PHYSICAL_ADDRESS  AhciRFisPciAddr;
 | |
|   EFI_PHYSICAL_ADDRESS  AhciCmdListPciAddr;
 | |
|   EFI_PHYSICAL_ADDRESS  AhciCommandTablePciAddr;
 | |
| 
 | |
|   Buffer = NULL;
 | |
|   //
 | |
|   // Collect AHCI controller information
 | |
|   //
 | |
|   Capability = AhciReadReg (PciIo, EFI_AHCI_CAPABILITY_OFFSET);
 | |
|   //
 | |
|   // Get the number of command slots per port supported by this HBA.
 | |
|   //
 | |
|   MaxCommandSlotNumber = (UINT8)(((Capability & 0x1F00) >> 8) + 1);
 | |
|   Support64Bit         = (BOOLEAN)(((Capability & BIT31) != 0) ? TRUE : FALSE);
 | |
| 
 | |
|   PortImplementBitMap = AhciReadReg (PciIo, EFI_AHCI_PI_OFFSET);
 | |
|   //
 | |
|   // Get the highest bit of implemented ports which decides how many bytes are allocated for received FIS.
 | |
|   //
 | |
|   MaxPortNumber = (UINT8)(UINTN)(HighBitSet32 (PortImplementBitMap) + 1);
 | |
|   if (MaxPortNumber == 0) {
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
| 
 | |
|   MaxReceiveFisSize = MaxPortNumber * sizeof (EFI_AHCI_RECEIVED_FIS);
 | |
|   Status            = PciIo->AllocateBuffer (
 | |
|                                PciIo,
 | |
|                                AllocateAnyPages,
 | |
|                                EfiBootServicesData,
 | |
|                                EFI_SIZE_TO_PAGES ((UINTN)MaxReceiveFisSize),
 | |
|                                &Buffer,
 | |
|                                0
 | |
|                                );
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return EFI_OUT_OF_RESOURCES;
 | |
|   }
 | |
| 
 | |
|   ZeroMem (Buffer, (UINTN)MaxReceiveFisSize);
 | |
| 
 | |
|   AhciRegisters->AhciRFis          = Buffer;
 | |
|   AhciRegisters->MaxReceiveFisSize = MaxReceiveFisSize;
 | |
|   Bytes                            = (UINTN)MaxReceiveFisSize;
 | |
| 
 | |
|   Status = PciIo->Map (
 | |
|                     PciIo,
 | |
|                     EfiPciIoOperationBusMasterCommonBuffer,
 | |
|                     Buffer,
 | |
|                     &Bytes,
 | |
|                     &AhciRFisPciAddr,
 | |
|                     &AhciRegisters->MapRFis
 | |
|                     );
 | |
| 
 | |
|   if (EFI_ERROR (Status) || (Bytes != MaxReceiveFisSize)) {
 | |
|     //
 | |
|     // Map error or unable to map the whole RFis buffer into a contiguous region.
 | |
|     //
 | |
|     Status = EFI_OUT_OF_RESOURCES;
 | |
|     goto Error6;
 | |
|   }
 | |
| 
 | |
|   if ((!Support64Bit) && (AhciRFisPciAddr > 0x100000000ULL)) {
 | |
|     //
 | |
|     // The AHCI HBA doesn't support 64bit addressing, so should not get a >4G pci bus master address.
 | |
|     //
 | |
|     Status = EFI_DEVICE_ERROR;
 | |
|     goto Error5;
 | |
|   }
 | |
| 
 | |
|   AhciRegisters->AhciRFisPciAddr = (EFI_AHCI_RECEIVED_FIS *)(UINTN)AhciRFisPciAddr;
 | |
| 
 | |
|   //
 | |
|   // Allocate memory for command list
 | |
|   // Note that the implementation is a single task model which only use a command list for all ports.
 | |
|   //
 | |
|   Buffer             = NULL;
 | |
|   MaxCommandListSize = MaxCommandSlotNumber * sizeof (EFI_AHCI_COMMAND_LIST);
 | |
|   Status             = PciIo->AllocateBuffer (
 | |
|                                 PciIo,
 | |
|                                 AllocateAnyPages,
 | |
|                                 EfiBootServicesData,
 | |
|                                 EFI_SIZE_TO_PAGES ((UINTN)MaxCommandListSize),
 | |
|                                 &Buffer,
 | |
|                                 0
 | |
|                                 );
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     //
 | |
|     // Free mapped resource.
 | |
|     //
 | |
|     Status = EFI_OUT_OF_RESOURCES;
 | |
|     goto Error5;
 | |
|   }
 | |
| 
 | |
|   ZeroMem (Buffer, (UINTN)MaxCommandListSize);
 | |
| 
 | |
|   AhciRegisters->AhciCmdList        = Buffer;
 | |
|   AhciRegisters->MaxCommandListSize = MaxCommandListSize;
 | |
|   Bytes                             = (UINTN)MaxCommandListSize;
 | |
| 
 | |
|   Status = PciIo->Map (
 | |
|                     PciIo,
 | |
|                     EfiPciIoOperationBusMasterCommonBuffer,
 | |
|                     Buffer,
 | |
|                     &Bytes,
 | |
|                     &AhciCmdListPciAddr,
 | |
|                     &AhciRegisters->MapCmdList
 | |
|                     );
 | |
| 
 | |
|   if (EFI_ERROR (Status) || (Bytes != MaxCommandListSize)) {
 | |
|     //
 | |
|     // Map error or unable to map the whole cmd list buffer into a contiguous region.
 | |
|     //
 | |
|     Status = EFI_OUT_OF_RESOURCES;
 | |
|     goto Error4;
 | |
|   }
 | |
| 
 | |
|   if ((!Support64Bit) && (AhciCmdListPciAddr > 0x100000000ULL)) {
 | |
|     //
 | |
|     // The AHCI HBA doesn't support 64bit addressing, so should not get a >4G pci bus master address.
 | |
|     //
 | |
|     Status = EFI_DEVICE_ERROR;
 | |
|     goto Error3;
 | |
|   }
 | |
| 
 | |
|   AhciRegisters->AhciCmdListPciAddr = (EFI_AHCI_COMMAND_LIST *)(UINTN)AhciCmdListPciAddr;
 | |
| 
 | |
|   //
 | |
|   // Allocate memory for command table
 | |
|   // According to AHCI 1.3 spec, a PRD table can contain maximum 65535 entries.
 | |
|   //
 | |
|   Buffer              = NULL;
 | |
|   MaxCommandTableSize = sizeof (EFI_AHCI_COMMAND_TABLE);
 | |
| 
 | |
|   Status = PciIo->AllocateBuffer (
 | |
|                     PciIo,
 | |
|                     AllocateAnyPages,
 | |
|                     EfiBootServicesData,
 | |
|                     EFI_SIZE_TO_PAGES ((UINTN)MaxCommandTableSize),
 | |
|                     &Buffer,
 | |
|                     0
 | |
|                     );
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     //
 | |
|     // Free mapped resource.
 | |
|     //
 | |
|     Status = EFI_OUT_OF_RESOURCES;
 | |
|     goto Error3;
 | |
|   }
 | |
| 
 | |
|   ZeroMem (Buffer, (UINTN)MaxCommandTableSize);
 | |
| 
 | |
|   AhciRegisters->AhciCommandTable    = Buffer;
 | |
|   AhciRegisters->MaxCommandTableSize = MaxCommandTableSize;
 | |
|   Bytes                              = (UINTN)MaxCommandTableSize;
 | |
| 
 | |
|   Status = PciIo->Map (
 | |
|                     PciIo,
 | |
|                     EfiPciIoOperationBusMasterCommonBuffer,
 | |
|                     Buffer,
 | |
|                     &Bytes,
 | |
|                     &AhciCommandTablePciAddr,
 | |
|                     &AhciRegisters->MapCommandTable
 | |
|                     );
 | |
| 
 | |
|   if (EFI_ERROR (Status) || (Bytes != MaxCommandTableSize)) {
 | |
|     //
 | |
|     // Map error or unable to map the whole cmd list buffer into a contiguous region.
 | |
|     //
 | |
|     Status = EFI_OUT_OF_RESOURCES;
 | |
|     goto Error2;
 | |
|   }
 | |
| 
 | |
|   if ((!Support64Bit) && (AhciCommandTablePciAddr > 0x100000000ULL)) {
 | |
|     //
 | |
|     // The AHCI HBA doesn't support 64bit addressing, so should not get a >4G pci bus master address.
 | |
|     //
 | |
|     Status = EFI_DEVICE_ERROR;
 | |
|     goto Error1;
 | |
|   }
 | |
| 
 | |
|   AhciRegisters->AhciCommandTablePciAddr = (EFI_AHCI_COMMAND_TABLE *)(UINTN)AhciCommandTablePciAddr;
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
|   //
 | |
|   // Map error or unable to map the whole CmdList buffer into a contiguous region.
 | |
|   //
 | |
| Error1:
 | |
|   PciIo->Unmap (
 | |
|            PciIo,
 | |
|            AhciRegisters->MapCommandTable
 | |
|            );
 | |
| Error2:
 | |
|   PciIo->FreeBuffer (
 | |
|            PciIo,
 | |
|            EFI_SIZE_TO_PAGES ((UINTN)MaxCommandTableSize),
 | |
|            AhciRegisters->AhciCommandTable
 | |
|            );
 | |
| Error3:
 | |
|   PciIo->Unmap (
 | |
|            PciIo,
 | |
|            AhciRegisters->MapCmdList
 | |
|            );
 | |
| Error4:
 | |
|   PciIo->FreeBuffer (
 | |
|            PciIo,
 | |
|            EFI_SIZE_TO_PAGES ((UINTN)MaxCommandListSize),
 | |
|            AhciRegisters->AhciCmdList
 | |
|            );
 | |
| Error5:
 | |
|   PciIo->Unmap (
 | |
|            PciIo,
 | |
|            AhciRegisters->MapRFis
 | |
|            );
 | |
| Error6:
 | |
|   PciIo->FreeBuffer (
 | |
|            PciIo,
 | |
|            EFI_SIZE_TO_PAGES ((UINTN)MaxReceiveFisSize),
 | |
|            AhciRegisters->AhciRFis
 | |
|            );
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Read logs from SATA device.
 | |
| 
 | |
|   @param  PciIo               The PCI IO protocol instance.
 | |
|   @param  AhciRegisters       The pointer to the EFI_AHCI_REGISTERS.
 | |
|   @param  Port                The number of port.
 | |
|   @param  PortMultiplier      The multiplier of port.
 | |
|   @param  Buffer              The data buffer to store SATA logs.
 | |
|   @param  LogNumber           The address of the log.
 | |
|   @param  PageNumber          The page number of the log.
 | |
| 
 | |
|   @retval EFI_INVALID_PARAMETER  PciIo, AhciRegisters or Buffer is NULL.
 | |
|   @retval others                 Return status of AhciPioTransfer().
 | |
| **/
 | |
| EFI_STATUS
 | |
| AhciReadLogExt (
 | |
|   IN EFI_PCI_IO_PROTOCOL  *PciIo,
 | |
|   IN EFI_AHCI_REGISTERS   *AhciRegisters,
 | |
|   IN UINT8                Port,
 | |
|   IN UINT8                PortMultiplier,
 | |
|   IN OUT UINT8            *Buffer,
 | |
|   IN UINT8                LogNumber,
 | |
|   IN UINT8                PageNumber
 | |
|   )
 | |
| {
 | |
|   EFI_ATA_COMMAND_BLOCK  AtaCommandBlock;
 | |
|   EFI_ATA_STATUS_BLOCK   AtaStatusBlock;
 | |
| 
 | |
|   if ((PciIo == NULL) || (AhciRegisters == NULL) || (Buffer == NULL)) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   ///
 | |
|   /// Read log from device
 | |
|   ///
 | |
|   ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK));
 | |
|   ZeroMem (&AtaStatusBlock, sizeof (EFI_ATA_STATUS_BLOCK));
 | |
|   ZeroMem (Buffer, 512);
 | |
| 
 | |
|   AtaCommandBlock.AtaCommand      = ATA_CMD_READ_LOG_EXT;
 | |
|   AtaCommandBlock.AtaSectorCount  = 1;
 | |
|   AtaCommandBlock.AtaSectorNumber = LogNumber;
 | |
|   AtaCommandBlock.AtaCylinderLow  = PageNumber;
 | |
| 
 | |
|   return AhciPioTransfer (
 | |
|            PciIo,
 | |
|            AhciRegisters,
 | |
|            Port,
 | |
|            PortMultiplier,
 | |
|            NULL,
 | |
|            0,
 | |
|            TRUE,
 | |
|            &AtaCommandBlock,
 | |
|            &AtaStatusBlock,
 | |
|            Buffer,
 | |
|            512,
 | |
|            ATA_ATAPI_TIMEOUT,
 | |
|            NULL
 | |
|            );
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Enable DEVSLP of the disk if supported.
 | |
| 
 | |
|   @param  PciIo               The PCI IO protocol instance.
 | |
|   @param  AhciRegisters       The pointer to the EFI_AHCI_REGISTERS.
 | |
|   @param  Port                The number of port.
 | |
|   @param  PortMultiplier      The multiplier of port.
 | |
|   @param  IdentifyData        A pointer to data buffer which is used to contain IDENTIFY data.
 | |
| 
 | |
|   @retval EFI_SUCCESS         The DEVSLP is enabled per policy successfully.
 | |
|   @retval EFI_UNSUPPORTED     The DEVSLP isn't supported by the controller/device and policy requires to enable it.
 | |
| **/
 | |
| EFI_STATUS
 | |
| AhciEnableDevSlp (
 | |
|   IN EFI_PCI_IO_PROTOCOL  *PciIo,
 | |
|   IN EFI_AHCI_REGISTERS   *AhciRegisters,
 | |
|   IN UINT8                Port,
 | |
|   IN UINT8                PortMultiplier,
 | |
|   IN EFI_IDENTIFY_DATA    *IdentifyData
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS               Status;
 | |
|   UINT32                   Offset;
 | |
|   UINT32                   Capability2;
 | |
|   UINT8                    LogData[512];
 | |
|   DEVSLP_TIMING_VARIABLES  DevSlpTiming;
 | |
|   UINT32                   PortCmd;
 | |
|   UINT32                   PortDevSlp;
 | |
| 
 | |
|   if (mAtaAtapiPolicy->DeviceSleepEnable != 1) {
 | |
|     return EFI_SUCCESS;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Do not enable DevSlp if DevSlp is not supported.
 | |
|   //
 | |
|   Capability2 = AhciReadReg (PciIo, AHCI_CAPABILITY2_OFFSET);
 | |
|   DEBUG ((DEBUG_INFO, "AHCI CAPABILITY2 = %08x\n", Capability2));
 | |
|   if ((Capability2 & AHCI_CAP2_SDS) == 0) {
 | |
|     return EFI_UNSUPPORTED;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Do not enable DevSlp if DevSlp is not present
 | |
|   // Do not enable DevSlp if Hot Plug or Mechanical Presence Switch is supported
 | |
|   //
 | |
|   Offset     = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH;
 | |
|   PortCmd    = AhciReadReg (PciIo, Offset + EFI_AHCI_PORT_CMD);
 | |
|   PortDevSlp = AhciReadReg (PciIo, Offset + AHCI_PORT_DEVSLP);
 | |
|   DEBUG ((DEBUG_INFO, "Port CMD/DEVSLP = %08x / %08x\n", PortCmd, PortDevSlp));
 | |
|   if (((PortDevSlp & AHCI_PORT_DEVSLP_DSP) == 0) ||
 | |
|       ((PortCmd & (EFI_AHCI_PORT_CMD_HPCP | EFI_AHCI_PORT_CMD_MPSP)) != 0)
 | |
|       )
 | |
|   {
 | |
|     return EFI_UNSUPPORTED;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Do not enable DevSlp if the device doesn't support DevSlp
 | |
|   //
 | |
|   DEBUG ((
 | |
|     DEBUG_INFO,
 | |
|     "IDENTIFY DEVICE: [77] = %04x, [78] = %04x, [79] = %04x\n",
 | |
|     IdentifyData->AtaData.reserved_77,
 | |
|     IdentifyData->AtaData.serial_ata_features_supported,
 | |
|     IdentifyData->AtaData.serial_ata_features_enabled
 | |
|     ));
 | |
|   if ((IdentifyData->AtaData.serial_ata_features_supported & BIT8) == 0) {
 | |
|     DEBUG ((
 | |
|       DEBUG_INFO,
 | |
|       "DevSlp feature is not supported for device at port [%d] PortMultiplier [%d]!\n",
 | |
|       Port,
 | |
|       PortMultiplier
 | |
|       ));
 | |
|     return EFI_UNSUPPORTED;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Enable DevSlp when it is not enabled.
 | |
|   //
 | |
|   if ((IdentifyData->AtaData.serial_ata_features_enabled & BIT8) != 0) {
 | |
|     Status = AhciDeviceSetFeature (
 | |
|                PciIo,
 | |
|                AhciRegisters,
 | |
|                Port,
 | |
|                0,
 | |
|                ATA_SUB_CMD_ENABLE_SATA_FEATURE,
 | |
|                0x09,
 | |
|                ATA_ATAPI_TIMEOUT
 | |
|                );
 | |
|     DEBUG ((
 | |
|       DEBUG_INFO,
 | |
|       "DevSlp set feature for device at port [%d] PortMultiplier [%d] - %r\n",
 | |
|       Port,
 | |
|       PortMultiplier,
 | |
|       Status
 | |
|       ));
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       return Status;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   Status = AhciReadLogExt (PciIo, AhciRegisters, Port, PortMultiplier, LogData, 0x30, 0x08);
 | |
| 
 | |
|   //
 | |
|   // Clear PxCMD.ST and PxDEVSLP.ADSE before updating PxDEVSLP.DITO and PxDEVSLP.MDAT.
 | |
|   //
 | |
|   AhciWriteReg (PciIo, Offset + EFI_AHCI_PORT_CMD, PortCmd & ~EFI_AHCI_PORT_CMD_ST);
 | |
|   PortDevSlp &= ~AHCI_PORT_DEVSLP_ADSE;
 | |
|   AhciWriteReg (PciIo, Offset + AHCI_PORT_DEVSLP, PortDevSlp);
 | |
| 
 | |
|   //
 | |
|   // Set PxDEVSLP.DETO and PxDEVSLP.MDAT to 0.
 | |
|   //
 | |
|   PortDevSlp &= ~AHCI_PORT_DEVSLP_DETO_MASK;
 | |
|   PortDevSlp &= ~AHCI_PORT_DEVSLP_MDAT_MASK;
 | |
|   AhciWriteReg (PciIo, Offset + AHCI_PORT_DEVSLP, PortDevSlp);
 | |
|   DEBUG ((DEBUG_INFO, "Read Log Ext at port [%d] PortMultiplier [%d] - %r\n", Port, PortMultiplier, Status));
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     //
 | |
|     // Assume DEVSLP TIMING VARIABLES is not supported if the Identify Device Data log (30h, 8) fails
 | |
|     //
 | |
|     ZeroMem (&DevSlpTiming, sizeof (DevSlpTiming));
 | |
|   } else {
 | |
|     CopyMem (&DevSlpTiming, &LogData[48], sizeof (DevSlpTiming));
 | |
|     DEBUG ((
 | |
|       DEBUG_INFO,
 | |
|       "DevSlpTiming: Supported(%d), Deto(%d), Madt(%d)\n",
 | |
|       DevSlpTiming.Supported,
 | |
|       DevSlpTiming.Deto,
 | |
|       DevSlpTiming.Madt
 | |
|       ));
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Use 20ms as default DETO when DEVSLP TIMING VARIABLES is not supported or the DETO is 0.
 | |
|   //
 | |
|   if ((DevSlpTiming.Supported == 0) || (DevSlpTiming.Deto == 0)) {
 | |
|     DevSlpTiming.Deto = 20;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Use 10ms as default MADT when DEVSLP TIMING VARIABLES is not supported or the MADT is 0.
 | |
|   //
 | |
|   if ((DevSlpTiming.Supported == 0) || (DevSlpTiming.Madt == 0)) {
 | |
|     DevSlpTiming.Madt = 10;
 | |
|   }
 | |
| 
 | |
|   PortDevSlp |= DevSlpTiming.Deto << 2;
 | |
|   PortDevSlp |= DevSlpTiming.Madt << 10;
 | |
|   AhciOrReg (PciIo, Offset + AHCI_PORT_DEVSLP, PortDevSlp);
 | |
| 
 | |
|   if (mAtaAtapiPolicy->AggressiveDeviceSleepEnable == 1) {
 | |
|     if ((Capability2 & AHCI_CAP2_SADM) != 0) {
 | |
|       PortDevSlp &= ~AHCI_PORT_DEVSLP_DITO_MASK;
 | |
|       PortDevSlp |= (625 << 15);
 | |
|       AhciWriteReg (PciIo, Offset + AHCI_PORT_DEVSLP, PortDevSlp);
 | |
| 
 | |
|       PortDevSlp |= AHCI_PORT_DEVSLP_ADSE;
 | |
|       AhciWriteReg (PciIo, Offset + AHCI_PORT_DEVSLP, PortDevSlp);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   AhciWriteReg (PciIo, Offset + EFI_AHCI_PORT_CMD, PortCmd);
 | |
| 
 | |
|   DEBUG ((
 | |
|     DEBUG_INFO,
 | |
|     "Enabled DevSlp feature at port [%d] PortMultiplier [%d], Port CMD/DEVSLP = %08x / %08x\n",
 | |
|     Port,
 | |
|     PortMultiplier,
 | |
|     PortCmd,
 | |
|     PortDevSlp
 | |
|     ));
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Spin-up disk if IDD was incomplete or PUIS feature is enabled
 | |
| 
 | |
|   @param  PciIo               The PCI IO protocol instance.
 | |
|   @param  AhciRegisters       The pointer to the EFI_AHCI_REGISTERS.
 | |
|   @param  Port                The number of port.
 | |
|   @param  PortMultiplier      The multiplier of port.
 | |
|   @param  IdentifyData        A pointer to data buffer which is used to contain IDENTIFY data.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| AhciSpinUpDisk (
 | |
|   IN EFI_PCI_IO_PROTOCOL    *PciIo,
 | |
|   IN EFI_AHCI_REGISTERS     *AhciRegisters,
 | |
|   IN UINT8                  Port,
 | |
|   IN UINT8                  PortMultiplier,
 | |
|   IN OUT EFI_IDENTIFY_DATA  *IdentifyData
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS             Status;
 | |
|   EFI_ATA_COMMAND_BLOCK  AtaCommandBlock;
 | |
|   EFI_ATA_STATUS_BLOCK   AtaStatusBlock;
 | |
|   UINT8                  Buffer[512];
 | |
| 
 | |
|   if (IdentifyData->AtaData.specific_config == ATA_SPINUP_CFG_REQUIRED_IDD_INCOMPLETE) {
 | |
|     //
 | |
|     // Use SET_FEATURE subcommand to spin up the device.
 | |
|     //
 | |
|     Status = AhciDeviceSetFeature (
 | |
|                PciIo,
 | |
|                AhciRegisters,
 | |
|                Port,
 | |
|                PortMultiplier,
 | |
|                ATA_SUB_CMD_PUIS_SET_DEVICE_SPINUP,
 | |
|                0x00,
 | |
|                ATA_SPINUP_TIMEOUT
 | |
|                );
 | |
|     DEBUG ((
 | |
|       DEBUG_INFO,
 | |
|       "CMD_PUIS_SET_DEVICE_SPINUP for device at port [%d] PortMultiplier [%d] - %r!\n",
 | |
|       Port,
 | |
|       PortMultiplier,
 | |
|       Status
 | |
|       ));
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       return Status;
 | |
|     }
 | |
|   } else {
 | |
|     ASSERT (IdentifyData->AtaData.specific_config == ATA_SPINUP_CFG_NOT_REQUIRED_IDD_INCOMPLETE);
 | |
| 
 | |
|     //
 | |
|     // Use READ_SECTORS to spin up the device if SpinUp SET FEATURE subcommand is not supported
 | |
|     //
 | |
|     ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK));
 | |
|     ZeroMem (&AtaStatusBlock, sizeof (EFI_ATA_STATUS_BLOCK));
 | |
|     //
 | |
|     // Perform READ SECTORS PIO Data-In command to Read LBA 0
 | |
|     //
 | |
|     AtaCommandBlock.AtaCommand     = ATA_CMD_READ_SECTORS;
 | |
|     AtaCommandBlock.AtaSectorCount = 0x1;
 | |
| 
 | |
|     Status = AhciPioTransfer (
 | |
|                PciIo,
 | |
|                AhciRegisters,
 | |
|                Port,
 | |
|                PortMultiplier,
 | |
|                NULL,
 | |
|                0,
 | |
|                TRUE,
 | |
|                &AtaCommandBlock,
 | |
|                &AtaStatusBlock,
 | |
|                &Buffer,
 | |
|                sizeof (Buffer),
 | |
|                ATA_SPINUP_TIMEOUT,
 | |
|                NULL
 | |
|                );
 | |
|     DEBUG ((
 | |
|       DEBUG_INFO,
 | |
|       "Read LBA 0 for device at port [%d] PortMultiplier [%d] - %r!\n",
 | |
|       Port,
 | |
|       PortMultiplier,
 | |
|       Status
 | |
|       ));
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       return Status;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Read the complete IDENTIFY DEVICE data.
 | |
|   //
 | |
|   ZeroMem (IdentifyData, sizeof (*IdentifyData));
 | |
|   Status = AhciIdentify (PciIo, AhciRegisters, Port, PortMultiplier, IdentifyData);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     DEBUG ((
 | |
|       DEBUG_ERROR,
 | |
|       "Read IDD failed for device at port [%d] PortMultiplier [%d] - %r!\n",
 | |
|       Port,
 | |
|       PortMultiplier,
 | |
|       Status
 | |
|       ));
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   DEBUG ((
 | |
|     DEBUG_INFO,
 | |
|     "IDENTIFY DEVICE: [0] = %016x, [2] = %016x, [83] = %016x, [86] = %016x\n",
 | |
|     IdentifyData->AtaData.config,
 | |
|     IdentifyData->AtaData.specific_config,
 | |
|     IdentifyData->AtaData.command_set_supported_83,
 | |
|     IdentifyData->AtaData.command_set_feature_enb_86
 | |
|     ));
 | |
|   //
 | |
|   // Check if IDD is incomplete
 | |
|   //
 | |
|   if ((IdentifyData->AtaData.config & BIT2) != 0) {
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Enable/disable/skip PUIS of the disk according to policy.
 | |
| 
 | |
|   @param  PciIo               The PCI IO protocol instance.
 | |
|   @param  AhciRegisters       The pointer to the EFI_AHCI_REGISTERS.
 | |
|   @param  Port                The number of port.
 | |
|   @param  PortMultiplier      The multiplier of port.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| AhciPuisEnable (
 | |
|   IN EFI_PCI_IO_PROTOCOL  *PciIo,
 | |
|   IN EFI_AHCI_REGISTERS   *AhciRegisters,
 | |
|   IN UINT8                Port,
 | |
|   IN UINT8                PortMultiplier
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
| 
 | |
|   Status = EFI_SUCCESS;
 | |
|   if (mAtaAtapiPolicy->PuisEnable == 0) {
 | |
|     Status = AhciDeviceSetFeature (PciIo, AhciRegisters, Port, PortMultiplier, ATA_SUB_CMD_DISABLE_PUIS, 0x00, ATA_ATAPI_TIMEOUT);
 | |
|   } else if (mAtaAtapiPolicy->PuisEnable == 1) {
 | |
|     Status = AhciDeviceSetFeature (PciIo, AhciRegisters, Port, PortMultiplier, ATA_SUB_CMD_ENABLE_PUIS, 0x00, ATA_ATAPI_TIMEOUT);
 | |
|   }
 | |
| 
 | |
|   DEBUG ((
 | |
|     DEBUG_INFO,
 | |
|     "%a PUIS feature at port [%d] PortMultiplier [%d] - %r!\n",
 | |
|     (mAtaAtapiPolicy->PuisEnable == 0) ? "Disable" : (
 | |
|                                                       (mAtaAtapiPolicy->PuisEnable == 1) ? "Enable" : "Skip"
 | |
|                                                       ),
 | |
|     Port,
 | |
|     PortMultiplier,
 | |
|     Status
 | |
|     ));
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Initialize ATA host controller at AHCI mode.
 | |
| 
 | |
|   The function is designed to initialize ATA host controller.
 | |
| 
 | |
|   @param[in]  Instance          A pointer to the ATA_ATAPI_PASS_THRU_INSTANCE instance.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| AhciModeInitialization (
 | |
|   IN  ATA_ATAPI_PASS_THRU_INSTANCE  *Instance
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS                        Status;
 | |
|   EFI_PCI_IO_PROTOCOL               *PciIo;
 | |
|   EFI_IDE_CONTROLLER_INIT_PROTOCOL  *IdeInit;
 | |
|   UINT32                            Capability;
 | |
|   UINT8                             MaxPortNumber;
 | |
|   UINT32                            PortImplementBitMap;
 | |
| 
 | |
|   EFI_AHCI_REGISTERS  *AhciRegisters;
 | |
| 
 | |
|   UINT8                    Port;
 | |
|   DATA_64                  Data64;
 | |
|   UINT32                   Offset;
 | |
|   UINT32                   Data;
 | |
|   EFI_IDENTIFY_DATA        Buffer;
 | |
|   EFI_ATA_DEVICE_TYPE      DeviceType;
 | |
|   EFI_ATA_COLLECTIVE_MODE  *SupportedModes;
 | |
|   EFI_ATA_TRANSFER_MODE    TransferMode;
 | |
|   UINT32                   PhyDetectDelay;
 | |
|   UINT32                   Value;
 | |
| 
 | |
|   if (Instance == NULL) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   PciIo   = Instance->PciIo;
 | |
|   IdeInit = Instance->IdeControllerInit;
 | |
| 
 | |
|   Status = AhciReset (PciIo, EFI_AHCI_BUS_RESET_TIMEOUT);
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Collect AHCI controller information
 | |
|   //
 | |
|   Capability = AhciReadReg (PciIo, EFI_AHCI_CAPABILITY_OFFSET);
 | |
| 
 | |
|   //
 | |
|   // Make sure that GHC.AE bit is set before accessing any AHCI registers.
 | |
|   //
 | |
|   Value = AhciReadReg (PciIo, EFI_AHCI_GHC_OFFSET);
 | |
| 
 | |
|   if ((Value & EFI_AHCI_GHC_ENABLE) == 0) {
 | |
|     AhciOrReg (PciIo, EFI_AHCI_GHC_OFFSET, EFI_AHCI_GHC_ENABLE);
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Enable 64-bit DMA support in the PCI layer if this controller
 | |
|   // supports it.
 | |
|   //
 | |
|   if ((Capability & EFI_AHCI_CAP_S64A) != 0) {
 | |
|     Status = PciIo->Attributes (
 | |
|                       PciIo,
 | |
|                       EfiPciIoAttributeOperationEnable,
 | |
|                       EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE,
 | |
|                       NULL
 | |
|                       );
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       DEBUG ((
 | |
|         DEBUG_WARN,
 | |
|         "AhciModeInitialization: failed to enable 64-bit DMA on 64-bit capable controller (%r)\n",
 | |
|         Status
 | |
|         ));
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Get the number of command slots per port supported by this HBA.
 | |
|   //
 | |
|   MaxPortNumber = (UINT8)((Capability & 0x1F) + 1);
 | |
| 
 | |
|   //
 | |
|   // Get the bit map of those ports exposed by this HBA.
 | |
|   // It indicates which ports that the HBA supports are available for software to use.
 | |
|   //
 | |
|   PortImplementBitMap = AhciReadReg (PciIo, EFI_AHCI_PI_OFFSET);
 | |
| 
 | |
|   AhciRegisters = &Instance->AhciRegisters;
 | |
|   Status        = AhciCreateTransferDescriptor (PciIo, AhciRegisters);
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return EFI_OUT_OF_RESOURCES;
 | |
|   }
 | |
| 
 | |
|   for (Port = 0; Port < EFI_AHCI_MAX_PORTS; Port++) {
 | |
|     if ((PortImplementBitMap & (((UINT32)BIT0) << Port)) != 0) {
 | |
|       //
 | |
|       // According to AHCI spec, MaxPortNumber should be equal or greater than the number of implemented ports.
 | |
|       //
 | |
|       if ((MaxPortNumber--) == 0) {
 | |
|         //
 | |
|         // Should never be here.
 | |
|         //
 | |
|         ASSERT (FALSE);
 | |
|         return EFI_SUCCESS;
 | |
|       }
 | |
| 
 | |
|       IdeInit->NotifyPhase (IdeInit, EfiIdeBeforeChannelEnumeration, Port);
 | |
| 
 | |
|       //
 | |
|       // Initialize FIS Base Address Register and Command List Base Address Register for use.
 | |
|       //
 | |
|       Data64.Uint64 = (UINTN)(AhciRegisters->AhciRFisPciAddr) + sizeof (EFI_AHCI_RECEIVED_FIS) * Port;
 | |
|       Offset        = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FB;
 | |
|       AhciWriteReg (PciIo, Offset, Data64.Uint32.Lower32);
 | |
|       Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FBU;
 | |
|       AhciWriteReg (PciIo, Offset, Data64.Uint32.Upper32);
 | |
| 
 | |
|       Data64.Uint64 = (UINTN)(AhciRegisters->AhciCmdListPciAddr);
 | |
|       Offset        = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLB;
 | |
|       AhciWriteReg (PciIo, Offset, Data64.Uint32.Lower32);
 | |
|       Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLBU;
 | |
|       AhciWriteReg (PciIo, Offset, Data64.Uint32.Upper32);
 | |
| 
 | |
|       Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;
 | |
|       Data   = AhciReadReg (PciIo, Offset);
 | |
|       if ((Data & EFI_AHCI_PORT_CMD_CPD) != 0) {
 | |
|         AhciOrReg (PciIo, Offset, EFI_AHCI_PORT_CMD_POD);
 | |
|       }
 | |
| 
 | |
|       if ((Capability & EFI_AHCI_CAP_SSS) != 0) {
 | |
|         AhciOrReg (PciIo, Offset, EFI_AHCI_PORT_CMD_SUD);
 | |
|       }
 | |
| 
 | |
|       //
 | |
|       // Disable aggressive power management.
 | |
|       //
 | |
|       Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SCTL;
 | |
|       AhciOrReg (PciIo, Offset, EFI_AHCI_PORT_SCTL_IPM_INIT);
 | |
|       //
 | |
|       // Disable the reporting of the corresponding interrupt to system software.
 | |
|       //
 | |
|       Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_IE;
 | |
|       AhciAndReg (PciIo, Offset, 0);
 | |
| 
 | |
|       //
 | |
|       // Now inform the IDE Controller Init Module.
 | |
|       //
 | |
|       IdeInit->NotifyPhase (IdeInit, EfiIdeBusBeforeDevicePresenceDetection, Port);
 | |
| 
 | |
|       //
 | |
|       // Enable FIS Receive DMA engine for the first D2H FIS.
 | |
|       //
 | |
|       Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;
 | |
|       AhciOrReg (PciIo, Offset, EFI_AHCI_PORT_CMD_FRE);
 | |
| 
 | |
|       //
 | |
|       // Wait for the Phy to detect the presence of a device.
 | |
|       //
 | |
|       PhyDetectDelay = EFI_AHCI_BUS_PHY_DETECT_TIMEOUT;
 | |
|       Offset         = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SSTS;
 | |
|       do {
 | |
|         Data = AhciReadReg (PciIo, Offset) & EFI_AHCI_PORT_SSTS_DET_MASK;
 | |
|         if ((Data == EFI_AHCI_PORT_SSTS_DET_PCE) || (Data == EFI_AHCI_PORT_SSTS_DET)) {
 | |
|           break;
 | |
|         }
 | |
| 
 | |
|         MicroSecondDelay (1000);
 | |
|         PhyDetectDelay--;
 | |
|       } while (PhyDetectDelay > 0);
 | |
| 
 | |
|       if (PhyDetectDelay == 0) {
 | |
|         //
 | |
|         // No device detected at this port.
 | |
|         // Clear PxCMD.SUD for those ports at which there are no device present.
 | |
|         //
 | |
|         Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;
 | |
|         AhciAndReg (PciIo, Offset, (UINT32) ~(EFI_AHCI_PORT_CMD_SUD));
 | |
|         continue;
 | |
|       }
 | |
| 
 | |
|       Status = AhciWaitDeviceReady (PciIo, Port);
 | |
|       if (EFI_ERROR (Status)) {
 | |
|         continue;
 | |
|       }
 | |
| 
 | |
|       //
 | |
|       // 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 = AhciWaitMmioSet (
 | |
|                  PciIo,
 | |
|                  Offset,
 | |
|                  0x0000FFFF,
 | |
|                  0x00000101,
 | |
|                  EFI_TIMER_PERIOD_SECONDS (16)
 | |
|                  );
 | |
|       if (EFI_ERROR (Status)) {
 | |
|         continue;
 | |
|       }
 | |
| 
 | |
|       Data = AhciReadReg (PciIo, Offset);
 | |
|       if ((Data & EFI_AHCI_ATAPI_SIG_MASK) == EFI_AHCI_ATAPI_DEVICE_SIG) {
 | |
|         Status = AhciIdentifyPacket (PciIo, AhciRegisters, Port, 0, &Buffer);
 | |
| 
 | |
|         if (EFI_ERROR (Status)) {
 | |
|           continue;
 | |
|         }
 | |
| 
 | |
|         DeviceType = EfiIdeCdrom;
 | |
|       } else if ((Data & EFI_AHCI_ATAPI_SIG_MASK) == EFI_AHCI_ATA_DEVICE_SIG) {
 | |
|         Status = AhciIdentify (PciIo, AhciRegisters, Port, 0, &Buffer);
 | |
| 
 | |
|         if (EFI_ERROR (Status)) {
 | |
|           REPORT_STATUS_CODE (EFI_PROGRESS_CODE, (EFI_PERIPHERAL_FIXED_MEDIA | EFI_P_EC_NOT_DETECTED));
 | |
|           continue;
 | |
|         }
 | |
| 
 | |
|         DEBUG ((
 | |
|           DEBUG_INFO,
 | |
|           "IDENTIFY DEVICE: [0] = %016x, [2] = %016x, [83] = %016x, [86] = %016x\n",
 | |
|           Buffer.AtaData.config,
 | |
|           Buffer.AtaData.specific_config,
 | |
|           Buffer.AtaData.command_set_supported_83,
 | |
|           Buffer.AtaData.command_set_feature_enb_86
 | |
|           ));
 | |
|         if ((Buffer.AtaData.config & BIT2) != 0) {
 | |
|           //
 | |
|           // SpinUp disk if device reported incomplete IDENTIFY DEVICE.
 | |
|           //
 | |
|           Status = AhciSpinUpDisk (
 | |
|                      PciIo,
 | |
|                      AhciRegisters,
 | |
|                      Port,
 | |
|                      0,
 | |
|                      &Buffer
 | |
|                      );
 | |
|           if (EFI_ERROR (Status)) {
 | |
|             DEBUG ((DEBUG_ERROR, "Spin up standby device failed - %r\n", Status));
 | |
|             continue;
 | |
|           }
 | |
|         }
 | |
| 
 | |
|         DeviceType = EfiIdeHarddisk;
 | |
|       } else {
 | |
|         continue;
 | |
|       }
 | |
| 
 | |
|       DEBUG ((
 | |
|         DEBUG_INFO,
 | |
|         "port [%d] port multitplier [%d] has a [%a]\n",
 | |
|         Port,
 | |
|         0,
 | |
|         DeviceType == EfiIdeCdrom ? "cdrom" : "harddisk"
 | |
|         ));
 | |
| 
 | |
|       //
 | |
|       // If the device is a hard disk, then try to enable S.M.A.R.T feature
 | |
|       //
 | |
|       if ((DeviceType == EfiIdeHarddisk) && PcdGetBool (PcdAtaSmartEnable)) {
 | |
|         AhciAtaSmartSupport (
 | |
|           PciIo,
 | |
|           AhciRegisters,
 | |
|           Port,
 | |
|           0,
 | |
|           &Buffer,
 | |
|           NULL
 | |
|           );
 | |
|       }
 | |
| 
 | |
|       //
 | |
|       // Submit identify data to IDE controller init driver
 | |
|       //
 | |
|       IdeInit->SubmitData (IdeInit, Port, 0, &Buffer);
 | |
| 
 | |
|       //
 | |
|       // Now start to config ide device parameter and transfer mode.
 | |
|       //
 | |
|       Status = IdeInit->CalculateMode (
 | |
|                           IdeInit,
 | |
|                           Port,
 | |
|                           0,
 | |
|                           &SupportedModes
 | |
|                           );
 | |
|       if (EFI_ERROR (Status)) {
 | |
|         DEBUG ((DEBUG_ERROR, "Calculate Mode Fail, Status = %r\n", Status));
 | |
|         continue;
 | |
|       }
 | |
| 
 | |
|       //
 | |
|       // Set best supported PIO mode on this IDE device
 | |
|       //
 | |
|       if (SupportedModes->PioMode.Mode <= EfiAtaPioMode2) {
 | |
|         TransferMode.ModeCategory = EFI_ATA_MODE_DEFAULT_PIO;
 | |
|       } else {
 | |
|         TransferMode.ModeCategory = EFI_ATA_MODE_FLOW_PIO;
 | |
|       }
 | |
| 
 | |
|       TransferMode.ModeNumber = (UINT8)(SupportedModes->PioMode.Mode);
 | |
| 
 | |
|       //
 | |
|       // Set supported DMA mode on this IDE device. Note that UDMA & MDMA can't
 | |
|       // be set together. Only one DMA mode can be set to a device. If setting
 | |
|       // DMA mode operation fails, we can continue moving on because we only use
 | |
|       // PIO mode at boot time. DMA modes are used by certain kind of OS booting
 | |
|       //
 | |
|       if (SupportedModes->UdmaMode.Valid) {
 | |
|         TransferMode.ModeCategory = EFI_ATA_MODE_UDMA;
 | |
|         TransferMode.ModeNumber   = (UINT8)(SupportedModes->UdmaMode.Mode);
 | |
|       } else if (SupportedModes->MultiWordDmaMode.Valid) {
 | |
|         TransferMode.ModeCategory = EFI_ATA_MODE_MDMA;
 | |
|         TransferMode.ModeNumber   = (UINT8)SupportedModes->MultiWordDmaMode.Mode;
 | |
|       }
 | |
| 
 | |
|       Status = AhciDeviceSetFeature (PciIo, AhciRegisters, Port, 0, 0x03, (UINT32)(*(UINT8 *)&TransferMode), ATA_ATAPI_TIMEOUT);
 | |
|       if (EFI_ERROR (Status)) {
 | |
|         DEBUG ((DEBUG_ERROR, "Set transfer Mode Fail, Status = %r\n", Status));
 | |
|         continue;
 | |
|       }
 | |
| 
 | |
|       //
 | |
|       // Found a ATA or ATAPI device, add it into the device list.
 | |
|       //
 | |
|       CreateNewDeviceInfo (Instance, Port, 0xFFFF, DeviceType, &Buffer);
 | |
|       if (DeviceType == EfiIdeHarddisk) {
 | |
|         REPORT_STATUS_CODE (EFI_PROGRESS_CODE, (EFI_PERIPHERAL_FIXED_MEDIA | EFI_P_PC_ENABLE));
 | |
|         AhciEnableDevSlp (
 | |
|           PciIo,
 | |
|           AhciRegisters,
 | |
|           Port,
 | |
|           0,
 | |
|           &Buffer
 | |
|           );
 | |
|       }
 | |
| 
 | |
|       //
 | |
|       // Enable/disable PUIS according to policy setting if PUIS is capable (Word[83].BIT5 is set).
 | |
|       //
 | |
|       if ((Buffer.AtaData.command_set_supported_83 & BIT5) != 0) {
 | |
|         Status = AhciPuisEnable (
 | |
|                    PciIo,
 | |
|                    AhciRegisters,
 | |
|                    Port,
 | |
|                    0
 | |
|                    );
 | |
|         if (EFI_ERROR (Status)) {
 | |
|           DEBUG ((DEBUG_ERROR, "PUIS enable/disable failed, Status = %r\n", Status));
 | |
|           continue;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 |