2010-09-29 07:50:45 +02:00
|
|
|
/** @file
|
|
|
|
The file for AHCI mode of ATA host controller.
|
2011-11-15 14:44:06 +01:00
|
|
|
|
2020-11-05 13:48:44 +01:00
|
|
|
Copyright (c) 2010 - 2020, Intel Corporation. All rights reserved.<BR>
|
2016-01-06 03:49:47 +01:00
|
|
|
(C) Copyright 2015 Hewlett Packard Enterprise Development LP<BR>
|
2019-04-04 01:05:13 +02:00
|
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
2010-09-29 07:50:45 +02:00
|
|
|
|
|
|
|
**/
|
|
|
|
|
|
|
|
#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);
|
2011-11-15 14:44:06 +01:00
|
|
|
|
2010-09-29 07:50:45 +02:00
|
|
|
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;
|
2011-11-15 14:44:06 +01:00
|
|
|
|
2010-09-29 07:50:45 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2011-11-03 13:38:21 +01:00
|
|
|
Wait for the value of the specified MMIO register set to the test value.
|
2011-11-15 14:44:06 +01:00
|
|
|
|
2010-09-30 10:44:59 +02:00
|
|
|
@param PciIo The PCI IO protocol instance.
|
2011-11-03 13:38:21 +01:00
|
|
|
@param Offset The MMIO address to test.
|
2010-09-30 10:44:59 +02:00
|
|
|
@param MaskValue The mask value of memory.
|
|
|
|
@param TestValue The test value of memory.
|
2011-11-03 13:38:21 +01:00
|
|
|
@param Timeout The time out value for wait memory set, uses 100ns as a unit.
|
2010-09-29 07:50:45 +02:00
|
|
|
|
2011-11-03 13:38:21 +01:00
|
|
|
@retval EFI_TIMEOUT The MMIO setting is time out.
|
|
|
|
@retval EFI_SUCCESS The MMIO is correct set.
|
2010-09-29 07:50:45 +02:00
|
|
|
|
|
|
|
**/
|
|
|
|
EFI_STATUS
|
|
|
|
EFIAPI
|
2011-11-03 13:38:21 +01:00
|
|
|
AhciWaitMmioSet (
|
2010-09-29 07:50:45 +02:00
|
|
|
IN EFI_PCI_IO_PROTOCOL *PciIo,
|
2011-11-03 13:38:21 +01:00
|
|
|
IN UINTN Offset,
|
2010-09-29 07:50:45 +02:00
|
|
|
IN UINT32 MaskValue,
|
|
|
|
IN UINT32 TestValue,
|
|
|
|
IN UINT64 Timeout
|
|
|
|
)
|
|
|
|
{
|
2011-11-15 14:44:06 +01:00
|
|
|
UINT32 Value;
|
2014-05-15 09:09:14 +02:00
|
|
|
UINT64 Delay;
|
|
|
|
BOOLEAN InfiniteWait;
|
2010-09-29 07:50:45 +02:00
|
|
|
|
2014-05-15 09:09:14 +02:00
|
|
|
if (Timeout == 0) {
|
|
|
|
InfiniteWait = TRUE;
|
|
|
|
} else {
|
|
|
|
InfiniteWait = FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
Delay = DivU64x32 (Timeout, 1000) + 1;
|
2010-09-29 07:50:45 +02:00
|
|
|
|
|
|
|
do {
|
2011-11-03 13:38:21 +01:00
|
|
|
//
|
|
|
|
// Access PCI MMIO space to see if the value is the tested one.
|
|
|
|
//
|
|
|
|
Value = AhciReadReg (PciIo, (UINT32)Offset) & MaskValue;
|
2010-09-29 07:50:45 +02:00
|
|
|
|
|
|
|
if (Value == TestValue) {
|
|
|
|
return EFI_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Stall for 100 microseconds.
|
|
|
|
//
|
|
|
|
MicroSecondDelay (100);
|
|
|
|
|
|
|
|
Delay--;
|
2014-05-15 09:09:14 +02:00
|
|
|
} while (InfiniteWait || (Delay > 0));
|
2010-09-29 07:50:45 +02:00
|
|
|
|
2011-11-03 13:38:21 +01:00
|
|
|
return EFI_TIMEOUT;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
Wait for the value of the specified system memory set to the test value.
|
2011-11-15 14:44:06 +01:00
|
|
|
|
2011-11-03 13:38:21 +01:00
|
|
|
@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
|
|
|
|
)
|
|
|
|
{
|
2011-11-15 14:44:06 +01:00
|
|
|
UINT32 Value;
|
2014-05-15 09:09:14 +02:00
|
|
|
UINT64 Delay;
|
|
|
|
BOOLEAN InfiniteWait;
|
|
|
|
|
|
|
|
if (Timeout == 0) {
|
|
|
|
InfiniteWait = TRUE;
|
|
|
|
} else {
|
|
|
|
InfiniteWait = FALSE;
|
|
|
|
}
|
2011-11-03 13:38:21 +01:00
|
|
|
|
2014-05-15 09:09:14 +02:00
|
|
|
Delay = DivU64x32 (Timeout, 1000) + 1;
|
2011-11-03 13:38:21 +01:00
|
|
|
|
|
|
|
do {
|
|
|
|
//
|
2020-02-07 02:07:24 +01:00
|
|
|
// Access system memory to see if the value is the tested one.
|
2011-11-03 13:38:21 +01:00
|
|
|
//
|
|
|
|
// 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--;
|
2014-05-15 09:09:14 +02:00
|
|
|
} while (InfiniteWait || (Delay > 0));
|
2011-11-03 13:38:21 +01:00
|
|
|
|
|
|
|
return EFI_TIMEOUT;
|
2010-09-29 07:50:45 +02:00
|
|
|
}
|
|
|
|
|
2011-05-03 12:31:41 +02:00
|
|
|
/**
|
|
|
|
Check the memory status to the test value.
|
2011-11-15 14:44:06 +01:00
|
|
|
|
2020-11-05 13:48:44 +01:00
|
|
|
@param[in] Address The memory address to test.
|
|
|
|
@param[in] MaskValue The mask value of memory.
|
|
|
|
@param[in] TestValue The test value of memory.
|
2011-05-03 12:31:41 +02:00
|
|
|
|
2020-11-05 13:48:44 +01:00
|
|
|
@retval EFI_NOT_READY The memory is not set.
|
2011-05-03 12:31:41 +02:00
|
|
|
@retval EFI_SUCCESS The memory is correct set.
|
|
|
|
**/
|
|
|
|
EFI_STATUS
|
|
|
|
EFIAPI
|
|
|
|
AhciCheckMemSet (
|
2011-11-03 13:38:21 +01:00
|
|
|
IN UINTN Address,
|
2011-05-19 08:12:58 +02:00
|
|
|
IN UINT32 MaskValue,
|
2020-11-05 13:48:44 +01:00
|
|
|
IN UINT32 TestValue
|
2011-05-03 12:31:41 +02:00
|
|
|
)
|
|
|
|
{
|
|
|
|
UINT32 Value;
|
|
|
|
|
2011-11-03 13:38:21 +01:00
|
|
|
Value = *(volatile UINT32 *)Address;
|
|
|
|
Value &= MaskValue;
|
2011-05-03 12:31:41 +02:00
|
|
|
|
|
|
|
if (Value == TestValue) {
|
|
|
|
return EFI_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2020-11-05 13:48:44 +01:00
|
|
|
return EFI_NOT_READY;
|
2011-05-03 12:31:41 +02:00
|
|
|
}
|
|
|
|
|
2010-09-29 07:50:45 +02:00
|
|
|
/**
|
|
|
|
|
|
|
|
Clear the port interrupt and error status. It will also clear
|
|
|
|
HBA interrupt status.
|
2011-11-15 14:44:06 +01:00
|
|
|
|
2010-09-29 07:50:45 +02:00
|
|
|
@param PciIo The PCI IO protocol instance.
|
|
|
|
@param Port The number of port.
|
2011-11-15 14:44:06 +01:00
|
|
|
|
|
|
|
**/
|
2010-09-29 07:50:45 +02:00
|
|
|
VOID
|
|
|
|
EFIAPI
|
|
|
|
AhciClearPortStatus (
|
|
|
|
IN EFI_PCI_IO_PROTOCOL *PciIo,
|
|
|
|
IN UINT8 Port
|
2011-11-15 14:44:06 +01:00
|
|
|
)
|
2010-09-29 07:50:45 +02:00
|
|
|
{
|
|
|
|
UINT32 Offset;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Clear any error status
|
2011-05-03 12:31:41 +02:00
|
|
|
//
|
2010-09-29 07:50:45 +02:00
|
|
|
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));
|
|
|
|
}
|
|
|
|
|
2011-01-06 06:44:50 +01:00
|
|
|
/**
|
|
|
|
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.
|
2016-10-31 06:46:08 +01:00
|
|
|
@param AhciRegisters The pointer to the EFI_AHCI_REGISTERS.
|
2011-01-06 06:44:50 +01:00
|
|
|
@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,
|
2016-10-31 06:46:08 +01:00
|
|
|
IN EFI_AHCI_REGISTERS *AhciRegisters,
|
2011-01-06 06:44:50 +01:00
|
|
|
IN UINT8 Port,
|
|
|
|
IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock
|
|
|
|
)
|
|
|
|
{
|
2016-10-31 06:46:08 +01:00
|
|
|
UINTN Offset;
|
2011-01-06 06:44:50 +01:00
|
|
|
UINT32 Data;
|
2016-10-31 06:46:08 +01:00
|
|
|
UINTN FisBaseAddr;
|
|
|
|
EFI_STATUS Status;
|
2011-01-06 06:44:50 +01:00
|
|
|
|
|
|
|
ASSERT (PciIo != NULL);
|
|
|
|
|
|
|
|
if (AtaStatusBlock != NULL) {
|
|
|
|
ZeroMem (AtaStatusBlock, sizeof (EFI_ATA_STATUS_BLOCK));
|
|
|
|
|
2016-10-31 06:46:08 +01:00
|
|
|
FisBaseAddr = (UINTN)AhciRegisters->AhciRFis + Port * sizeof (EFI_AHCI_RECEIVED_FIS);
|
|
|
|
Offset = FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET;
|
|
|
|
|
2020-11-05 13:48:44 +01:00
|
|
|
Status = AhciCheckMemSet (Offset, EFI_AHCI_FIS_TYPE_MASK, EFI_AHCI_FIS_REGISTER_D2H);
|
2016-10-31 06:46:08 +01:00
|
|
|
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);
|
|
|
|
}
|
2011-01-06 06:44:50 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-09-29 07:50:45 +02:00
|
|
|
/**
|
|
|
|
Enable the FIS running for giving port.
|
2011-11-15 14:44:06 +01:00
|
|
|
|
2010-09-29 07:50:45 +02:00
|
|
|
@param PciIo The PCI IO protocol instance.
|
|
|
|
@param Port The number of port.
|
2011-11-03 13:38:21 +01:00
|
|
|
@param Timeout The timeout value of enabling FIS, uses 100ns as a unit.
|
2010-09-29 07:50:45 +02:00
|
|
|
|
|
|
|
@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
|
2011-05-03 12:31:41 +02:00
|
|
|
)
|
|
|
|
{
|
2010-09-29 07:50:45 +02:00
|
|
|
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);
|
|
|
|
|
2016-06-21 02:37:06 +02:00
|
|
|
return EFI_SUCCESS;
|
2010-09-29 07:50:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
Disable the FIS running for giving port.
|
|
|
|
|
|
|
|
@param PciIo The PCI IO protocol instance.
|
|
|
|
@param Port The number of port.
|
2011-11-03 13:38:21 +01:00
|
|
|
@param Timeout The timeout value of disabling FIS, uses 100ns as a unit.
|
2010-09-29 07:50:45 +02:00
|
|
|
|
|
|
|
@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
|
2011-11-15 14:44:06 +01:00
|
|
|
)
|
2010-09-29 07:50:45 +02:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
2011-11-15 14:44:06 +01:00
|
|
|
|
2010-09-29 07:50:45 +02:00
|
|
|
//
|
|
|
|
// 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));
|
|
|
|
|
2011-11-03 13:38:21 +01:00
|
|
|
return AhciWaitMmioSet (
|
2011-05-03 12:31:41 +02:00
|
|
|
PciIo,
|
2010-09-29 07:50:45 +02:00
|
|
|
Offset,
|
|
|
|
EFI_AHCI_PORT_CMD_FR,
|
|
|
|
0,
|
|
|
|
Timeout
|
2011-05-03 12:31:41 +02:00
|
|
|
);
|
2010-09-29 07:50:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
Build the command list, command table and prepare the fis receiver.
|
2011-11-15 14:44:06 +01:00
|
|
|
|
2010-09-30 10:44:59 +02:00
|
|
|
@param PciIo The PCI IO protocol instance.
|
2010-09-29 07:50:45 +02:00
|
|
|
@param AhciRegisters The pointer to the EFI_AHCI_REGISTERS.
|
2010-09-30 10:44:59 +02:00
|
|
|
@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.
|
2010-09-29 07:50:45 +02:00
|
|
|
@param DataPhysicalAddr The pointer to the data buffer pci bus master address.
|
|
|
|
@param DataLength The data count to be transferred.
|
|
|
|
|
2011-11-15 14:44:06 +01:00
|
|
|
**/
|
2010-09-29 07:50:45 +02:00
|
|
|
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,
|
2011-11-28 07:19:36 +01:00
|
|
|
IN UINT32 DataLength
|
2011-11-15 14:44:06 +01:00
|
|
|
)
|
2010-09-29 07:50:45 +02:00
|
|
|
{
|
2011-05-03 12:31:41 +02:00
|
|
|
UINT64 BaseAddr;
|
2011-11-28 07:19:36 +01:00
|
|
|
UINT32 PrdtNumber;
|
|
|
|
UINT32 PrdtIndex;
|
2010-09-29 07:50:45 +02:00
|
|
|
UINTN RemainedData;
|
|
|
|
UINTN MemAddr;
|
|
|
|
DATA_64 Data64;
|
|
|
|
UINT32 Offset;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Filling the PRDT
|
2011-11-15 14:44:06 +01:00
|
|
|
//
|
2014-07-22 05:06:43 +02:00
|
|
|
PrdtNumber = (UINT32)DivU64x32 (((UINT64)DataLength + EFI_AHCI_MAX_DATA_PER_PRDT - 1), EFI_AHCI_MAX_DATA_PER_PRDT);
|
2010-09-29 07:50:45 +02:00
|
|
|
|
|
|
|
//
|
|
|
|
// 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;
|
2011-11-15 14:44:06 +01:00
|
|
|
|
2011-05-03 12:31:41 +02:00
|
|
|
ZeroMem ((VOID *)((UINTN)BaseAddr), sizeof (EFI_AHCI_RECEIVED_FIS));
|
2011-11-15 14:44:06 +01:00
|
|
|
|
2010-09-29 07:50:45 +02:00
|
|
|
ZeroMem (AhciRegisters->AhciCommandTable, sizeof (EFI_AHCI_COMMAND_TABLE));
|
|
|
|
|
|
|
|
CommandFis->AhciCFisPmNum = PortMultiplier;
|
2011-11-15 14:44:06 +01:00
|
|
|
|
2010-09-29 07:50:45 +02:00
|
|
|
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));
|
|
|
|
}
|
2011-11-15 14:44:06 +01:00
|
|
|
|
2010-10-14 03:33:04 +02:00
|
|
|
RemainedData = (UINTN)DataLength;
|
2010-09-29 07:50:45 +02:00
|
|
|
MemAddr = (UINTN)DataPhysicalAddr;
|
2011-11-28 07:19:36 +01:00
|
|
|
CommandList->AhciCmdPrdtl = PrdtNumber;
|
2011-11-15 14:44:06 +01:00
|
|
|
|
2010-09-29 07:50:45 +02:00
|
|
|
for (PrdtIndex = 0; PrdtIndex < PrdtNumber; PrdtIndex++) {
|
2011-05-03 12:31:41 +02:00
|
|
|
if (RemainedData < EFI_AHCI_MAX_DATA_PER_PRDT) {
|
2010-09-29 07:50:45 +02:00
|
|
|
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;
|
2011-05-03 12:31:41 +02:00
|
|
|
RemainedData -= EFI_AHCI_MAX_DATA_PER_PRDT;
|
2010-09-29 07:50:45 +02:00
|
|
|
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)
|
2011-11-15 14:44:06 +01:00
|
|
|
);
|
2010-09-29 07:50:45 +02:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-02-07 02:07:24 +01:00
|
|
|
Build a command FIS.
|
2011-11-15 14:44:06 +01:00
|
|
|
|
2010-09-30 10:44:59 +02:00
|
|
|
@param CmdFis A pointer to the EFI_AHCI_COMMAND_FIS data structure.
|
2010-09-29 07:50:45 +02:00
|
|
|
@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
|
|
|
|
//
|
2011-11-15 14:44:06 +01:00
|
|
|
CmdFis->AhciCFisCmdInd = 0x1;
|
2010-09-29 07:50:45 +02:00
|
|
|
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;
|
|
|
|
|
2010-09-30 10:44:59 +02:00
|
|
|
CmdFis->AhciCFisDevHead = (UINT8)(AtaCommandBlock->AtaDeviceHead | 0xE0);
|
2010-09-29 07:50:45 +02:00
|
|
|
}
|
|
|
|
|
2020-11-05 13:48:45 +01:00
|
|
|
/**
|
|
|
|
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));
|
|
|
|
}
|
2021-12-05 23:54:02 +01:00
|
|
|
|
2020-11-05 13:48:45 +01:00
|
|
|
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));
|
2023-03-27 23:37:35 +02:00
|
|
|
return EFI_DEVICE_ERROR;
|
2020-11-05 13:48:45 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return EFI_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2023-03-27 23:37:35 +02:00
|
|
|
/**
|
|
|
|
This function will check if the failed command should be retired. Only error
|
|
|
|
conditions which are a result of transient conditions on a link(either to system or to device).
|
|
|
|
|
|
|
|
@param[in] PciIo Pointer to AHCI controller PciIo.
|
|
|
|
@param[in] Port SATA port index on which to check.
|
|
|
|
|
|
|
|
@retval TRUE Command failure was caused by transient condition and should be retried
|
|
|
|
@retval FALSE Command should not be retried
|
|
|
|
**/
|
|
|
|
BOOLEAN
|
|
|
|
AhciShouldCmdBeRetried (
|
|
|
|
IN EFI_PCI_IO_PROTOCOL *PciIo,
|
|
|
|
IN UINT8 Port
|
|
|
|
)
|
|
|
|
{
|
|
|
|
UINT32 Offset;
|
|
|
|
UINT32 PortInterrupt;
|
|
|
|
UINT32 Serr;
|
|
|
|
UINT32 Tfd;
|
|
|
|
|
|
|
|
Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_IS;
|
|
|
|
PortInterrupt = AhciReadReg (PciIo, Offset);
|
|
|
|
Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SERR;
|
|
|
|
Serr = AhciReadReg (PciIo, Offset);
|
|
|
|
Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;
|
|
|
|
Tfd = AhciReadReg (PciIo, Offset);
|
|
|
|
|
|
|
|
//
|
|
|
|
// This can occur if there was a CRC error on a path from system memory to
|
|
|
|
// host controller.
|
|
|
|
//
|
|
|
|
if (PortInterrupt & EFI_AHCI_PORT_IS_HBDS) {
|
|
|
|
return TRUE;
|
|
|
|
//
|
|
|
|
// This can occur if there was a CRC error detected by host during communication
|
|
|
|
// with the device
|
|
|
|
//
|
|
|
|
} else if ((PortInterrupt & (EFI_AHCI_PORT_IS_IFS | EFI_AHCI_PORT_IS_INFS)) &&
|
|
|
|
(Serr & EFI_AHCI_PORT_SERR_CRCE))
|
|
|
|
{
|
|
|
|
return TRUE;
|
|
|
|
//
|
|
|
|
// This can occur if there was a CRC error detected by device during communication
|
|
|
|
// with the host. Device returns error status to host with D2H FIS.
|
|
|
|
//
|
|
|
|
} else if ((PortInterrupt & EFI_AHCI_PORT_IS_TFES) &&
|
|
|
|
(Tfd & EFI_AHCI_PORT_TFD_ERR_INT_CRC))
|
|
|
|
{
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2020-11-05 13:48:44 +01:00
|
|
|
/**
|
|
|
|
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;
|
|
|
|
}
|
2021-12-05 23:54:02 +01:00
|
|
|
|
2020-11-05 13:48:44 +01:00
|
|
|
//
|
|
|
|
// 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;
|
|
|
|
}
|
2021-12-05 23:54:02 +01:00
|
|
|
|
2020-11-05 13:48:44 +01:00
|
|
|
//
|
|
|
|
// Stall for 100 microseconds.
|
|
|
|
//
|
|
|
|
MicroSecondDelay (100);
|
|
|
|
Delay--;
|
|
|
|
} while (InfiniteWait || (Delay > 0));
|
|
|
|
|
|
|
|
return EFI_TIMEOUT;
|
|
|
|
}
|
|
|
|
|
2020-11-05 13:48:47 +01:00
|
|
|
/**
|
|
|
|
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
|
|
|
|
)
|
|
|
|
{
|
2021-11-16 03:52:07 +01:00
|
|
|
//
|
|
|
|
// Skip NULL pointer
|
|
|
|
//
|
|
|
|
if (AtaStatusBlock == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-11-05 13:48:47 +01:00
|
|
|
//
|
|
|
|
// 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));
|
|
|
|
}
|
|
|
|
|
2010-09-29 07:50:45 +02:00
|
|
|
/**
|
|
|
|
Start a PIO data transfer on specific port.
|
2011-11-15 14:44:06 +01:00
|
|
|
|
2011-05-03 12:31:41 +02:00
|
|
|
@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.
|
2011-11-03 13:38:21 +01:00
|
|
|
@param[in] Timeout The timeout value of non data transfer, uses 100ns as a unit.
|
2011-05-03 12:31:41 +02:00
|
|
|
@param[in] Task Optional. Pointer to the ATA_NONBLOCK_TASK
|
|
|
|
used by non-blocking mode.
|
2010-09-29 07:50:45 +02:00
|
|
|
|
|
|
|
@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
|
2010-09-30 10:44:59 +02:00
|
|
|
EFIAPI
|
2010-09-29 07:50:45 +02:00
|
|
|
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,
|
2011-11-15 14:44:06 +01:00
|
|
|
IN UINT8 AtapiCommandLength,
|
|
|
|
IN BOOLEAN Read,
|
2010-09-29 07:50:45 +02:00
|
|
|
IN EFI_ATA_COMMAND_BLOCK *AtaCommandBlock,
|
|
|
|
IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock,
|
|
|
|
IN OUT VOID *MemoryAddr,
|
|
|
|
IN UINT32 DataCount,
|
2011-05-03 12:31:41 +02:00
|
|
|
IN UINT64 Timeout,
|
|
|
|
IN ATA_NONBLOCK_TASK *Task
|
2010-09-29 07:50:45 +02:00
|
|
|
)
|
|
|
|
{
|
|
|
|
EFI_STATUS Status;
|
|
|
|
EFI_PHYSICAL_ADDRESS PhyAddr;
|
|
|
|
VOID *Map;
|
|
|
|
UINTN MapLength;
|
|
|
|
EFI_PCI_IO_PROTOCOL_OPERATION Flag;
|
|
|
|
EFI_AHCI_COMMAND_FIS CFis;
|
2011-11-15 14:44:06 +01:00
|
|
|
EFI_AHCI_COMMAND_LIST CmdList;
|
2011-11-03 13:38:21 +01:00
|
|
|
UINT32 PrdCount;
|
2020-11-05 13:48:46 +01:00
|
|
|
UINT32 Retry;
|
2022-10-18 17:54:19 +02:00
|
|
|
EFI_STATUS RecoveryStatus;
|
2023-03-27 23:37:35 +02:00
|
|
|
BOOLEAN DoRetry;
|
2010-09-29 07:50:45 +02:00
|
|
|
|
|
|
|
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)) {
|
2011-10-19 07:15:23 +02:00
|
|
|
return EFI_BAD_BUFFER_SIZE;
|
2010-09-29 07:50:45 +02:00
|
|
|
}
|
2011-11-15 14:44:06 +01:00
|
|
|
|
2010-09-29 07:50:45 +02:00
|
|
|
//
|
|
|
|
// 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;
|
|
|
|
|
2020-11-05 13:48:46 +01:00
|
|
|
for (Retry = 0; Retry < AHCI_COMMAND_RETRIES; Retry++) {
|
|
|
|
AhciBuildCommand (
|
|
|
|
PciIo,
|
|
|
|
AhciRegisters,
|
|
|
|
Port,
|
|
|
|
PortMultiplier,
|
|
|
|
&CFis,
|
|
|
|
&CmdList,
|
|
|
|
AtapiCommand,
|
|
|
|
AtapiCommandLength,
|
|
|
|
0,
|
|
|
|
(VOID *)(UINTN)PhyAddr,
|
|
|
|
DataCount
|
|
|
|
);
|
2011-11-15 14:44:06 +01:00
|
|
|
|
2020-11-05 13:48:47 +01:00
|
|
|
DEBUG ((DEBUG_VERBOSE, "Starting command for PIO transfer:\n"));
|
|
|
|
AhciPrintCommandBlock (AtaCommandBlock, DEBUG_VERBOSE);
|
2020-11-05 13:48:46 +01:00
|
|
|
Status = AhciStartCommand (
|
|
|
|
PciIo,
|
|
|
|
Port,
|
|
|
|
0,
|
|
|
|
Timeout
|
|
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
|
|
break;
|
|
|
|
}
|
2011-05-03 12:31:41 +02:00
|
|
|
|
2020-11-05 13:48:46 +01:00
|
|
|
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;
|
|
|
|
}
|
2014-10-22 07:52:48 +02:00
|
|
|
}
|
2020-11-05 13:48:46 +01:00
|
|
|
} else {
|
|
|
|
Status = AhciWaitUntilFisReceived (PciIo, Port, Timeout, SataFisD2H);
|
2011-11-03 13:38:21 +01:00
|
|
|
}
|
2010-09-29 07:50:45 +02:00
|
|
|
|
2020-11-05 13:48:46 +01:00
|
|
|
if (Status == EFI_DEVICE_ERROR) {
|
|
|
|
DEBUG ((DEBUG_ERROR, "PIO command failed at retry %d\n", Retry));
|
2023-03-27 23:37:35 +02:00
|
|
|
DoRetry = AhciShouldCmdBeRetried (PciIo, Port); // needs to be called before error recovery
|
2022-10-18 17:54:19 +02:00
|
|
|
RecoveryStatus = AhciRecoverPortError (PciIo, Port);
|
2023-03-27 23:37:35 +02:00
|
|
|
if (!DoRetry || EFI_ERROR (RecoveryStatus)) {
|
2020-11-05 13:48:46 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
2020-11-05 13:48:45 +01:00
|
|
|
}
|
|
|
|
|
2010-09-29 07:50:45 +02:00
|
|
|
AhciStopCommand (
|
2011-05-03 12:31:41 +02:00
|
|
|
PciIo,
|
2010-09-29 07:50:45 +02:00
|
|
|
Port,
|
|
|
|
Timeout
|
|
|
|
);
|
2011-11-15 14:44:06 +01:00
|
|
|
|
2010-09-29 07:50:45 +02:00
|
|
|
AhciDisableFisReceive (
|
2011-05-03 12:31:41 +02:00
|
|
|
PciIo,
|
2010-09-29 07:50:45 +02:00
|
|
|
Port,
|
|
|
|
Timeout
|
|
|
|
);
|
|
|
|
|
|
|
|
PciIo->Unmap (
|
|
|
|
PciIo,
|
|
|
|
Map
|
|
|
|
);
|
|
|
|
|
2016-10-31 06:46:08 +01:00
|
|
|
AhciDumpPortStatus (PciIo, AhciRegisters, Port, AtaStatusBlock);
|
2020-11-05 13:48:47 +01:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2010-09-29 07:50:45 +02:00
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
Start a DMA data transfer on specific port
|
|
|
|
|
2011-05-03 12:31:41 +02:00
|
|
|
@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.
|
2011-11-03 13:38:21 +01:00
|
|
|
@param[in] Timeout The timeout value of non data transfer, uses 100ns as a unit.
|
2011-05-03 12:31:41 +02:00
|
|
|
@param[in] Task Optional. Pointer to the ATA_NONBLOCK_TASK
|
|
|
|
used by non-blocking mode.
|
2010-09-30 10:44:59 +02:00
|
|
|
|
|
|
|
@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.
|
2011-05-03 12:31:41 +02:00
|
|
|
|
2010-09-29 07:50:45 +02:00
|
|
|
**/
|
|
|
|
EFI_STATUS
|
|
|
|
EFIAPI
|
|
|
|
AhciDmaTransfer (
|
2011-05-03 12:31:41 +02:00
|
|
|
IN ATA_ATAPI_PASS_THRU_INSTANCE *Instance,
|
2010-09-29 07:50:45 +02:00
|
|
|
IN EFI_AHCI_REGISTERS *AhciRegisters,
|
|
|
|
IN UINT8 Port,
|
|
|
|
IN UINT8 PortMultiplier,
|
|
|
|
IN EFI_AHCI_ATAPI_COMMAND *AtapiCommand OPTIONAL,
|
|
|
|
IN UINT8 AtapiCommandLength,
|
2011-11-15 14:44:06 +01:00
|
|
|
IN BOOLEAN Read,
|
2010-09-29 07:50:45 +02:00
|
|
|
IN EFI_ATA_COMMAND_BLOCK *AtaCommandBlock,
|
|
|
|
IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock,
|
|
|
|
IN OUT VOID *MemoryAddr,
|
2011-11-03 13:38:21 +01:00
|
|
|
IN UINT32 DataCount,
|
2011-05-03 12:31:41 +02:00
|
|
|
IN UINT64 Timeout,
|
|
|
|
IN ATA_NONBLOCK_TASK *Task
|
2010-09-29 07:50:45 +02:00
|
|
|
)
|
|
|
|
{
|
|
|
|
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;
|
2011-11-03 13:38:21 +01:00
|
|
|
EFI_PCI_IO_PROTOCOL *PciIo;
|
|
|
|
EFI_TPL OldTpl;
|
2020-11-05 13:48:46 +01:00
|
|
|
UINT32 Retry;
|
2022-10-18 17:54:19 +02:00
|
|
|
EFI_STATUS RecoveryStatus;
|
2023-03-27 23:37:35 +02:00
|
|
|
BOOLEAN DoRetry;
|
2010-09-29 07:50:45 +02:00
|
|
|
|
2011-05-03 12:31:41 +02:00
|
|
|
Map = NULL;
|
|
|
|
PciIo = Instance->PciIo;
|
2010-09-29 07:50:45 +02:00
|
|
|
|
2011-05-03 12:31:41 +02:00
|
|
|
if (PciIo == NULL) {
|
|
|
|
return EFI_INVALID_PARAMETER;
|
2010-09-29 07:50:45 +02:00
|
|
|
}
|
|
|
|
|
2021-03-15 03:55:47 +01:00
|
|
|
//
|
|
|
|
// Set Status to suppress incorrect compiler/analyzer warnings
|
|
|
|
//
|
|
|
|
Status = EFI_SUCCESS;
|
|
|
|
|
2010-09-29 07:50:45 +02:00
|
|
|
//
|
2020-11-05 13:48:46 +01:00
|
|
|
// DMA buffer allocation. Needs to be done only once for both sync and async
|
|
|
|
// DMA transfers irrespective of number of retries.
|
2010-09-29 07:50:45 +02:00
|
|
|
//
|
2020-11-05 13:48:46 +01:00
|
|
|
if ((Task == NULL) || ((Task != NULL) && (Task->Map == NULL))) {
|
2011-05-03 12:31:41 +02:00
|
|
|
if (Read) {
|
|
|
|
Flag = EfiPciIoOperationBusMasterWrite;
|
|
|
|
} else {
|
|
|
|
Flag = EfiPciIoOperationBusMasterRead;
|
|
|
|
}
|
2010-09-29 07:50:45 +02:00
|
|
|
|
2011-05-03 12:31:41 +02:00
|
|
|
MapLength = DataCount;
|
|
|
|
Status = PciIo->Map (
|
|
|
|
PciIo,
|
|
|
|
Flag,
|
|
|
|
MemoryAddr,
|
|
|
|
&MapLength,
|
|
|
|
&PhyAddr,
|
|
|
|
&Map
|
|
|
|
);
|
|
|
|
|
|
|
|
if (EFI_ERROR (Status) || (DataCount != MapLength)) {
|
2011-10-19 07:15:23 +02:00
|
|
|
return EFI_BAD_BUFFER_SIZE;
|
2011-05-03 12:31:41 +02:00
|
|
|
}
|
2021-12-05 23:54:02 +01:00
|
|
|
|
2011-05-03 12:31:41 +02:00
|
|
|
if (Task != NULL) {
|
|
|
|
Task->Map = Map;
|
|
|
|
}
|
2020-11-05 13:48:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if ((Task == NULL) || ((Task != NULL) && !Task->IsStart)) {
|
2011-05-03 12:31:41 +02:00
|
|
|
AhciBuildCommandFis (&CFis, AtaCommandBlock);
|
|
|
|
|
|
|
|
ZeroMem (&CmdList, sizeof (EFI_AHCI_COMMAND_LIST));
|
|
|
|
|
|
|
|
CmdList.AhciCmdCfl = EFI_AHCI_FIS_REGISTER_H2D_LENGTH / 4;
|
|
|
|
CmdList.AhciCmdW = Read ? 0 : 1;
|
2010-09-29 07:50:45 +02:00
|
|
|
}
|
|
|
|
|
2020-11-05 13:48:46 +01:00
|
|
|
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);
|
|
|
|
}
|
2021-12-05 23:54:02 +01:00
|
|
|
|
2020-11-05 13:48:46 +01:00
|
|
|
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
|
|
|
|
);
|
|
|
|
|
2020-11-05 13:48:47 +01:00
|
|
|
DEBUG ((DEBUG_VERBOSE, "Starting command for sync DMA transfer:\n"));
|
|
|
|
AhciPrintCommandBlock (AtaCommandBlock, DEBUG_VERBOSE);
|
2020-11-05 13:48:46 +01:00
|
|
|
Status = AhciStartCommand (
|
|
|
|
PciIo,
|
|
|
|
Port,
|
|
|
|
0,
|
|
|
|
Timeout
|
|
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
|
|
break;
|
|
|
|
}
|
2021-12-05 23:54:02 +01:00
|
|
|
|
2020-11-05 13:48:46 +01:00
|
|
|
Status = AhciWaitUntilFisReceived (PciIo, Port, Timeout, SataFisD2H);
|
|
|
|
if (Status == EFI_DEVICE_ERROR) {
|
|
|
|
DEBUG ((DEBUG_ERROR, "DMA command failed at retry: %d\n", Retry));
|
2023-03-27 23:37:35 +02:00
|
|
|
DoRetry = AhciShouldCmdBeRetried (PciIo, Port); // needs to be called before error recovery
|
2022-10-18 17:54:19 +02:00
|
|
|
RecoveryStatus = AhciRecoverPortError (PciIo, Port);
|
2023-03-27 23:37:35 +02:00
|
|
|
if (!DoRetry || EFI_ERROR (RecoveryStatus)) {
|
2020-11-05 13:48:46 +01:00
|
|
|
break;
|
|
|
|
}
|
2020-11-05 13:48:44 +01:00
|
|
|
} else {
|
2020-11-05 13:48:46 +01:00
|
|
|
break;
|
2020-11-05 13:48:44 +01:00
|
|
|
}
|
|
|
|
}
|
2011-05-03 12:31:41 +02:00
|
|
|
} else {
|
2020-11-05 13:48:46 +01:00
|
|
|
if (!Task->IsStart) {
|
|
|
|
AhciBuildCommand (
|
|
|
|
PciIo,
|
|
|
|
AhciRegisters,
|
|
|
|
Port,
|
|
|
|
PortMultiplier,
|
|
|
|
&CFis,
|
|
|
|
&CmdList,
|
|
|
|
AtapiCommand,
|
|
|
|
AtapiCommandLength,
|
|
|
|
0,
|
|
|
|
(VOID *)(UINTN)PhyAddr,
|
|
|
|
DataCount
|
|
|
|
);
|
|
|
|
|
2020-11-05 13:48:47 +01:00
|
|
|
DEBUG ((DEBUG_VERBOSE, "Starting command for async DMA transfer:\n"));
|
|
|
|
AhciPrintCommandBlock (AtaCommandBlock, DEBUG_VERBOSE);
|
2020-11-05 13:48:46 +01:00
|
|
|
Status = AhciStartCommand (
|
|
|
|
PciIo,
|
|
|
|
Port,
|
|
|
|
0,
|
|
|
|
Timeout
|
|
|
|
);
|
|
|
|
if (!EFI_ERROR (Status)) {
|
|
|
|
Task->IsStart = TRUE;
|
|
|
|
}
|
|
|
|
}
|
2021-12-05 23:54:02 +01:00
|
|
|
|
2020-11-05 13:48:46 +01:00
|
|
|
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));
|
2023-03-27 23:37:35 +02:00
|
|
|
DoRetry = AhciShouldCmdBeRetried (PciIo, Port); // call this before error recovery
|
2022-10-18 17:54:19 +02:00
|
|
|
RecoveryStatus = AhciRecoverPortError (PciIo, Port);
|
2020-11-05 13:48:46 +01:00
|
|
|
//
|
|
|
|
// 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.
|
|
|
|
//
|
2023-03-27 23:37:35 +02:00
|
|
|
if (DoRetry && (RecoveryStatus == EFI_SUCCESS)) {
|
2020-11-05 13:48:46 +01:00
|
|
|
Task->IsStart = FALSE;
|
|
|
|
Status = EFI_NOT_READY;
|
|
|
|
}
|
|
|
|
}
|
2010-09-29 07:50:45 +02:00
|
|
|
|
2020-11-05 13:48:46 +01:00
|
|
|
if (Status == EFI_NOT_READY) {
|
|
|
|
if (!Task->InfiniteWait && (Task->RetryTimes == 0)) {
|
|
|
|
Status = EFI_TIMEOUT;
|
|
|
|
} else {
|
|
|
|
Task->RetryTimes--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-11-05 13:48:45 +01:00
|
|
|
}
|
|
|
|
|
2011-05-03 12:31:41 +02:00
|
|
|
//
|
|
|
|
// For Blocking mode, the command should be stopped, the Fis should be disabled
|
|
|
|
// and the PciIo should be unmapped.
|
2011-11-15 14:44:06 +01:00
|
|
|
// 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
|
2011-05-03 12:31:41 +02:00
|
|
|
// context cleanup, then set the packet's Asb status.
|
|
|
|
//
|
|
|
|
if ((Task == NULL) ||
|
|
|
|
((Task != NULL) && (Status != EFI_NOT_READY))
|
|
|
|
)
|
|
|
|
{
|
|
|
|
AhciStopCommand (
|
2011-11-15 14:44:06 +01:00
|
|
|
PciIo,
|
2011-05-03 12:31:41 +02:00
|
|
|
Port,
|
|
|
|
Timeout
|
|
|
|
);
|
2010-09-29 07:50:45 +02:00
|
|
|
|
2011-05-03 12:31:41 +02:00
|
|
|
AhciDisableFisReceive (
|
2011-11-15 14:44:06 +01:00
|
|
|
PciIo,
|
2011-05-03 12:31:41 +02:00
|
|
|
Port,
|
|
|
|
Timeout
|
|
|
|
);
|
2010-09-29 07:50:45 +02:00
|
|
|
|
2011-05-03 12:31:41 +02:00
|
|
|
PciIo->Unmap (
|
|
|
|
PciIo,
|
|
|
|
(Task != NULL) ? Task->Map : Map
|
|
|
|
);
|
2011-01-06 06:44:50 +01:00
|
|
|
|
2011-05-03 12:31:41 +02:00
|
|
|
if (Task != NULL) {
|
|
|
|
Task->Packet->Asb->AtaStatus = 0x01;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-31 06:46:08 +01:00
|
|
|
AhciDumpPortStatus (PciIo, AhciRegisters, Port, AtaStatusBlock);
|
2020-11-05 13:48:47 +01:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2010-09-29 07:50:45 +02:00
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
Start a non data transfer on specific port.
|
2011-11-15 14:44:06 +01:00
|
|
|
|
2011-05-03 12:31:41 +02:00
|
|
|
@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.
|
2011-11-03 13:38:21 +01:00
|
|
|
@param[in] Timeout The timeout value of non data transfer, uses 100ns as a unit.
|
2011-05-03 12:31:41 +02:00
|
|
|
@param[in] Task Optional. Pointer to the ATA_NONBLOCK_TASK
|
|
|
|
used by non-blocking mode.
|
2010-09-29 07:50:45 +02:00
|
|
|
|
|
|
|
@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.
|
|
|
|
|
2011-11-15 14:44:06 +01:00
|
|
|
**/
|
2010-09-29 07:50:45 +02:00
|
|
|
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,
|
2011-05-03 12:31:41 +02:00
|
|
|
IN UINT64 Timeout,
|
|
|
|
IN ATA_NONBLOCK_TASK *Task
|
|
|
|
)
|
2010-09-29 07:50:45 +02:00
|
|
|
{
|
2011-05-03 12:31:41 +02:00
|
|
|
EFI_STATUS Status;
|
2010-09-29 07:50:45 +02:00
|
|
|
EFI_AHCI_COMMAND_FIS CFis;
|
|
|
|
EFI_AHCI_COMMAND_LIST CmdList;
|
2020-11-05 13:48:46 +01:00
|
|
|
UINT32 Retry;
|
2022-10-18 17:54:19 +02:00
|
|
|
EFI_STATUS RecoveryStatus;
|
2023-03-27 23:37:35 +02:00
|
|
|
BOOLEAN DoRetry;
|
2010-09-29 07:50:45 +02:00
|
|
|
|
|
|
|
//
|
|
|
|
// Package read needed
|
|
|
|
//
|
|
|
|
AhciBuildCommandFis (&CFis, AtaCommandBlock);
|
|
|
|
|
|
|
|
ZeroMem (&CmdList, sizeof (EFI_AHCI_COMMAND_LIST));
|
|
|
|
|
|
|
|
CmdList.AhciCmdCfl = EFI_AHCI_FIS_REGISTER_H2D_LENGTH / 4;
|
|
|
|
|
2020-11-05 13:48:46 +01:00
|
|
|
for (Retry = 0; Retry < AHCI_COMMAND_RETRIES; Retry++) {
|
|
|
|
AhciBuildCommand (
|
|
|
|
PciIo,
|
|
|
|
AhciRegisters,
|
|
|
|
Port,
|
|
|
|
PortMultiplier,
|
|
|
|
&CFis,
|
|
|
|
&CmdList,
|
|
|
|
AtapiCommand,
|
|
|
|
AtapiCommandLength,
|
|
|
|
0,
|
|
|
|
NULL,
|
|
|
|
0
|
|
|
|
);
|
2011-05-03 12:31:41 +02:00
|
|
|
|
2020-11-05 13:48:47 +01:00
|
|
|
DEBUG ((DEBUG_VERBOSE, "Starting command for non data transfer:\n"));
|
|
|
|
AhciPrintCommandBlock (AtaCommandBlock, DEBUG_VERBOSE);
|
2020-11-05 13:48:46 +01:00
|
|
|
Status = AhciStartCommand (
|
|
|
|
PciIo,
|
|
|
|
Port,
|
|
|
|
0,
|
|
|
|
Timeout
|
|
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
|
|
break;
|
|
|
|
}
|
2011-05-03 12:31:41 +02:00
|
|
|
|
2020-11-05 13:48:46 +01:00
|
|
|
Status = AhciWaitUntilFisReceived (PciIo, Port, Timeout, SataFisD2H);
|
|
|
|
if (Status == EFI_DEVICE_ERROR) {
|
|
|
|
DEBUG ((DEBUG_ERROR, "Non data transfer failed at retry %d\n", Retry));
|
2023-03-27 23:37:35 +02:00
|
|
|
DoRetry = AhciShouldCmdBeRetried (PciIo, Port); // call this before error recovery
|
2022-10-18 17:54:19 +02:00
|
|
|
RecoveryStatus = AhciRecoverPortError (PciIo, Port);
|
2023-03-27 23:37:35 +02:00
|
|
|
if (!DoRetry || EFI_ERROR (RecoveryStatus)) {
|
2020-11-05 13:48:46 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
2020-11-05 13:48:45 +01:00
|
|
|
}
|
2011-05-03 12:31:41 +02:00
|
|
|
|
2010-09-29 07:50:45 +02:00
|
|
|
AhciStopCommand (
|
2011-05-03 12:31:41 +02:00
|
|
|
PciIo,
|
2010-09-29 07:50:45 +02:00
|
|
|
Port,
|
|
|
|
Timeout
|
|
|
|
);
|
|
|
|
|
|
|
|
AhciDisableFisReceive (
|
2011-05-03 12:31:41 +02:00
|
|
|
PciIo,
|
2010-09-29 07:50:45 +02:00
|
|
|
Port,
|
|
|
|
Timeout
|
|
|
|
);
|
|
|
|
|
2016-10-31 06:46:08 +01:00
|
|
|
AhciDumpPortStatus (PciIo, AhciRegisters, Port, AtaStatusBlock);
|
2011-01-06 06:44:50 +01:00
|
|
|
|
2020-11-05 13:48:47 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2010-09-29 07:50:45 +02:00
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
Stop command running for giving port
|
2011-11-15 14:44:06 +01:00
|
|
|
|
2010-09-29 07:50:45 +02:00
|
|
|
@param PciIo The PCI IO protocol instance.
|
|
|
|
@param Port The number of port.
|
2011-11-03 13:38:21 +01:00
|
|
|
@param Timeout The timeout value of stop, uses 100ns as a unit.
|
2011-11-15 14:44:06 +01:00
|
|
|
|
2010-09-29 07:50:45 +02:00
|
|
|
@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) {
|
2011-05-03 12:31:41 +02:00
|
|
|
return EFI_SUCCESS;
|
2010-09-29 07:50:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if ((Data & EFI_AHCI_PORT_CMD_ST) != 0) {
|
|
|
|
AhciAndReg (PciIo, Offset, (UINT32) ~(EFI_AHCI_PORT_CMD_ST));
|
|
|
|
}
|
|
|
|
|
2011-11-03 13:38:21 +01:00
|
|
|
return AhciWaitMmioSet (
|
2011-05-03 12:31:41 +02:00
|
|
|
PciIo,
|
2010-09-29 07:50:45 +02:00
|
|
|
Offset,
|
|
|
|
EFI_AHCI_PORT_CMD_CR,
|
|
|
|
0,
|
|
|
|
Timeout
|
2011-05-03 12:31:41 +02:00
|
|
|
);
|
2010-09-29 07:50:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
Start command for give slot on specific port.
|
2011-05-03 12:31:41 +02:00
|
|
|
|
2010-09-29 07:50:45 +02:00
|
|
|
@param PciIo The PCI IO protocol instance.
|
|
|
|
@param Port The number of port.
|
2011-05-03 12:31:41 +02:00
|
|
|
@param CommandSlot The number of Command Slot.
|
2011-11-03 13:38:21 +01:00
|
|
|
@param Timeout The timeout value of start, uses 100ns as a unit.
|
2011-05-03 12:31:41 +02:00
|
|
|
|
2010-09-29 07:50:45 +02:00
|
|
|
@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 (
|
2011-11-15 14:44:06 +01:00
|
|
|
PciIo,
|
2010-09-29 07:50:45 +02:00
|
|
|
Port,
|
|
|
|
Timeout
|
|
|
|
);
|
2011-05-03 12:31:41 +02:00
|
|
|
|
2010-09-29 07:50:45 +02:00
|
|
|
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);
|
2011-05-03 12:31:41 +02:00
|
|
|
|
2010-09-29 07:50:45 +02:00
|
|
|
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;
|
2011-06-14 04:11:34 +02:00
|
|
|
AhciOrReg (PciIo, Offset, EFI_AHCI_PORT_CMD_CLO);
|
2010-09-29 07:50:45 +02:00
|
|
|
|
2011-11-03 13:38:21 +01:00
|
|
|
AhciWaitMmioSet (
|
2011-05-03 12:31:41 +02:00
|
|
|
PciIo,
|
2010-09-29 07:50:45 +02:00
|
|
|
Offset,
|
2011-06-14 04:11:34 +02:00
|
|
|
EFI_AHCI_PORT_CMD_CLO,
|
2010-09-29 07:50:45 +02:00
|
|
|
0,
|
|
|
|
Timeout
|
2011-05-03 12:31:41 +02:00
|
|
|
);
|
2010-09-29 07:50:45 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;
|
|
|
|
AhciOrReg (PciIo, Offset, EFI_AHCI_PORT_CMD_ST | StartCmd);
|
|
|
|
|
2011-01-06 06:44:50 +01:00
|
|
|
//
|
|
|
|
// 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);
|
|
|
|
|
2010-09-29 07:50:45 +02:00
|
|
|
return EFI_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
Do AHCI HBA reset.
|
2011-05-03 12:31:41 +02:00
|
|
|
|
2010-09-29 07:50:45 +02:00
|
|
|
@param PciIo The PCI IO protocol instance.
|
2011-11-03 13:38:21 +01:00
|
|
|
@param Timeout The timeout value of reset, uses 100ns as a unit.
|
2011-05-03 12:31:41 +02:00
|
|
|
|
2010-09-29 07:50:45 +02:00
|
|
|
@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
|
2011-11-15 14:44:06 +01:00
|
|
|
)
|
2010-09-29 07:50:45 +02:00
|
|
|
{
|
2014-05-15 09:09:14 +02:00
|
|
|
UINT64 Delay;
|
2010-09-29 07:50:45 +02:00
|
|
|
UINT32 Value;
|
|
|
|
|
2014-11-10 06:46:21 +01:00
|
|
|
//
|
MdeModulePkg/AtaAtapiPassThru: Ensure GHC.AE bit is always set in Ahci
According to AHCI Spec 1.3 GHC.AE bit description:
"The implementation of this bit is dependent upon the value of the
CAP.SAM bit. If CAP.SAM is '0', then GHC.AE shall be read-write and shall
have a reset value of '0'. If CAP.SAM is '1', then AE shall be read-only
and shall have a reset value of '1'."
Being in AhciMode, for proper operation it is required, that GHC.AE bit
is always set, before any other AHCI registers are written to. Current
AhciMode implementation, both in AhciReset() and AhciModeInitialization()
functions, set GHC.AE bit only depending on 'CAP.SAM == 0' condition,
assuming (according to the AHCI spec), that otherwise it has to be set
anyway. It may however happen, that even if 'CAP.SAM == 1', GHC.AE
requires updating by software.
This patch enables in AhciMode setting GHC.AE in case its initial value
is '0'. It fixes AHCI support for Marvell Armada 70x0 and 80x0 SoC
families. The change is transparent to all other platforms.
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Marcin Wojtas <mw@semihalf.com>
Signed-off-by: Jan Dabros <jsd@semihalf.com>
Reviewed-by: Feng Tian <feng.tian@intel.com>
Reviewed-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
2016-11-24 08:54:33 +01:00
|
|
|
// Make sure that GHC.AE bit is set before accessing any AHCI registers.
|
2014-11-10 06:46:21 +01:00
|
|
|
//
|
MdeModulePkg/AtaAtapiPassThru: Ensure GHC.AE bit is always set in Ahci
According to AHCI Spec 1.3 GHC.AE bit description:
"The implementation of this bit is dependent upon the value of the
CAP.SAM bit. If CAP.SAM is '0', then GHC.AE shall be read-write and shall
have a reset value of '0'. If CAP.SAM is '1', then AE shall be read-only
and shall have a reset value of '1'."
Being in AhciMode, for proper operation it is required, that GHC.AE bit
is always set, before any other AHCI registers are written to. Current
AhciMode implementation, both in AhciReset() and AhciModeInitialization()
functions, set GHC.AE bit only depending on 'CAP.SAM == 0' condition,
assuming (according to the AHCI spec), that otherwise it has to be set
anyway. It may however happen, that even if 'CAP.SAM == 1', GHC.AE
requires updating by software.
This patch enables in AhciMode setting GHC.AE in case its initial value
is '0'. It fixes AHCI support for Marvell Armada 70x0 and 80x0 SoC
families. The change is transparent to all other platforms.
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Marcin Wojtas <mw@semihalf.com>
Signed-off-by: Jan Dabros <jsd@semihalf.com>
Reviewed-by: Feng Tian <feng.tian@intel.com>
Reviewed-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
2016-11-24 08:54:33 +01:00
|
|
|
Value = AhciReadReg (PciIo, EFI_AHCI_GHC_OFFSET);
|
|
|
|
|
|
|
|
if ((Value & EFI_AHCI_GHC_ENABLE) == 0) {
|
2014-11-10 06:46:21 +01:00
|
|
|
AhciOrReg (PciIo, EFI_AHCI_GHC_OFFSET, EFI_AHCI_GHC_ENABLE);
|
|
|
|
}
|
2010-09-29 07:50:45 +02:00
|
|
|
|
|
|
|
AhciOrReg (PciIo, EFI_AHCI_GHC_OFFSET, EFI_AHCI_GHC_RESET);
|
|
|
|
|
2014-05-15 09:09:14 +02:00
|
|
|
Delay = DivU64x32 (Timeout, 1000) + 1;
|
2010-09-29 07:50:45 +02:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2010-10-28 09:44:11 +02:00
|
|
|
/**
|
|
|
|
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.
|
2016-04-08 10:31:32 +02:00
|
|
|
@param PortMultiplier The port multiplier port number.
|
2010-10-28 09:44:11 +02:00
|
|
|
@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,
|
2011-05-03 12:31:41 +02:00
|
|
|
ATA_ATAPI_TIMEOUT,
|
|
|
|
NULL
|
2010-10-28 09:44:11 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
if (EFI_ERROR (Status)) {
|
2013-08-09 05:39:37 +02:00
|
|
|
REPORT_STATUS_CODE (
|
|
|
|
EFI_ERROR_CODE | EFI_ERROR_MINOR,
|
|
|
|
(EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_DISABLED)
|
|
|
|
);
|
2010-10-28 09:44:11 +02:00
|
|
|
return EFI_DEVICE_ERROR;
|
|
|
|
}
|
|
|
|
|
2013-08-09 05:39:37 +02:00
|
|
|
REPORT_STATUS_CODE (
|
|
|
|
EFI_PROGRESS_CODE,
|
|
|
|
(EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_ENABLE)
|
|
|
|
);
|
|
|
|
|
2010-10-28 09:44:11 +02:00
|
|
|
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
|
|
|
|
//
|
2021-11-17 04:21:29 +01:00
|
|
|
DEBUG ((DEBUG_INFO, "The S.M.A.R.T threshold exceeded condition is not detected\n"));
|
2013-08-09 05:39:37 +02:00
|
|
|
REPORT_STATUS_CODE (
|
|
|
|
EFI_PROGRESS_CODE,
|
|
|
|
(EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_UNDERTHRESHOLD)
|
|
|
|
);
|
2010-10-28 09:44:11 +02:00
|
|
|
} else if ((LBAMid == 0xf4) && (LBAHigh == 0x2c)) {
|
|
|
|
//
|
|
|
|
// The threshold exceeded condition is detected by the device
|
|
|
|
//
|
2021-11-17 04:21:29 +01:00
|
|
|
DEBUG ((DEBUG_INFO, "The S.M.A.R.T threshold exceeded condition is detected\n"));
|
2013-08-09 05:39:37 +02:00
|
|
|
REPORT_STATUS_CODE (
|
|
|
|
EFI_PROGRESS_CODE,
|
|
|
|
(EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_OVERTHRESHOLD)
|
|
|
|
);
|
2010-10-28 09:44:11 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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.
|
2016-04-08 10:31:32 +02:00
|
|
|
@param PortMultiplier The port multiplier port number.
|
2010-10-28 09:44:11 +02:00
|
|
|
@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,
|
2011-05-03 12:31:41 +02:00
|
|
|
IN EFI_IDENTIFY_DATA *IdentifyData,
|
2010-10-28 09:44:11 +02:00
|
|
|
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
|
|
|
|
//
|
2021-11-17 04:21:29 +01:00
|
|
|
DEBUG ((
|
|
|
|
DEBUG_INFO,
|
|
|
|
"S.M.A.R.T feature is not supported at port [%d] PortMultiplier [%d]!\n",
|
2010-10-28 09:44:11 +02:00
|
|
|
Port,
|
|
|
|
PortMultiplier
|
|
|
|
));
|
2013-08-09 05:39:37 +02:00
|
|
|
REPORT_STATUS_CODE (
|
|
|
|
EFI_ERROR_CODE | EFI_ERROR_MINOR,
|
|
|
|
(EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_NOTSUPPORTED)
|
|
|
|
);
|
2010-10-28 09:44:11 +02:00
|
|
|
} 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) {
|
2013-08-09 05:39:37 +02:00
|
|
|
REPORT_STATUS_CODE (
|
|
|
|
EFI_PROGRESS_CODE,
|
|
|
|
(EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_DISABLE)
|
|
|
|
);
|
|
|
|
|
2010-10-28 09:44:11 +02:00
|
|
|
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,
|
2011-05-03 12:31:41 +02:00
|
|
|
ATA_ATAPI_TIMEOUT,
|
|
|
|
NULL
|
2010-10-28 09:44:11 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
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,
|
2011-05-03 12:31:41 +02:00
|
|
|
ATA_ATAPI_TIMEOUT,
|
|
|
|
NULL
|
2010-10-28 09:44:11 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2021-11-01 10:44:22 +01:00
|
|
|
|
|
|
|
AhciAtaSmartReturnStatusCheck (
|
|
|
|
PciIo,
|
|
|
|
AhciRegisters,
|
|
|
|
(UINT8)Port,
|
|
|
|
(UINT8)PortMultiplier,
|
|
|
|
AtaStatusBlock
|
|
|
|
);
|
|
|
|
|
2021-11-17 04:21:29 +01:00
|
|
|
DEBUG ((
|
|
|
|
DEBUG_INFO,
|
|
|
|
"Enabled S.M.A.R.T feature at port [%d] PortMultiplier [%d]!\n",
|
2010-10-28 09:44:11 +02:00
|
|
|
Port,
|
|
|
|
PortMultiplier
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-09-29 07:50:45 +02:00
|
|
|
/**
|
|
|
|
Send Buffer cmd to specific device.
|
2011-11-15 14:44:06 +01:00
|
|
|
|
2010-09-30 10:44:59 +02:00
|
|
|
@param PciIo The PCI IO protocol instance.
|
|
|
|
@param AhciRegisters The pointer to the EFI_AHCI_REGISTERS.
|
|
|
|
@param Port The number of port.
|
2016-04-08 10:31:32 +02:00
|
|
|
@param PortMultiplier The port multiplier port number.
|
2010-09-30 10:44:59 +02:00
|
|
|
@param Buffer The data buffer to store IDENTIFY PACKET data.
|
2010-09-29 07:50:45 +02:00
|
|
|
|
|
|
|
@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,
|
2011-11-15 14:44:06 +01:00
|
|
|
IN OUT EFI_IDENTIFY_DATA *Buffer
|
2010-09-29 07:50:45 +02:00
|
|
|
)
|
|
|
|
{
|
|
|
|
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));
|
2011-05-03 12:31:41 +02:00
|
|
|
|
2010-09-29 07:50:45 +02:00
|
|
|
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),
|
2011-11-15 14:44:06 +01:00
|
|
|
ATA_ATAPI_TIMEOUT,
|
2011-05-03 12:31:41 +02:00
|
|
|
NULL
|
2010-09-29 07:50:45 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
Send Buffer cmd to specific device.
|
2011-11-15 14:44:06 +01:00
|
|
|
|
2010-09-30 10:44:59 +02:00
|
|
|
@param PciIo The PCI IO protocol instance.
|
|
|
|
@param AhciRegisters The pointer to the EFI_AHCI_REGISTERS.
|
|
|
|
@param Port The number of port.
|
2016-04-08 10:31:32 +02:00
|
|
|
@param PortMultiplier The port multiplier port number.
|
2010-09-30 10:44:59 +02:00
|
|
|
@param Buffer The data buffer to store IDENTIFY PACKET data.
|
2010-09-29 07:50:45 +02:00
|
|
|
|
|
|
|
@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,
|
2011-11-15 14:44:06 +01:00
|
|
|
IN OUT EFI_IDENTIFY_DATA *Buffer
|
2010-09-29 07:50:45 +02:00
|
|
|
)
|
|
|
|
{
|
|
|
|
EFI_STATUS Status;
|
|
|
|
EFI_ATA_COMMAND_BLOCK AtaCommandBlock;
|
|
|
|
EFI_ATA_STATUS_BLOCK AtaStatusBlock;
|
|
|
|
|
|
|
|
if ((PciIo == NULL) || (AhciRegisters == NULL)) {
|
|
|
|
return EFI_INVALID_PARAMETER;
|
|
|
|
}
|
2011-05-03 12:31:41 +02:00
|
|
|
|
2010-09-29 07:50:45 +02:00
|
|
|
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),
|
2011-05-03 12:31:41 +02:00
|
|
|
ATA_ATAPI_TIMEOUT,
|
|
|
|
NULL
|
2010-09-29 07:50:45 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
Send SET FEATURE cmd on specific device.
|
2011-11-15 14:44:06 +01:00
|
|
|
|
2010-09-30 10:44:59 +02:00
|
|
|
@param PciIo The PCI IO protocol instance.
|
|
|
|
@param AhciRegisters The pointer to the EFI_AHCI_REGISTERS.
|
|
|
|
@param Port The number of port.
|
2016-04-08 10:31:32 +02:00
|
|
|
@param PortMultiplier The port multiplier port number.
|
2010-09-30 10:44:59 +02:00
|
|
|
@param Feature The data to send Feature register.
|
|
|
|
@param FeatureSpecificData The specific data for SET FEATURE cmd.
|
2018-05-29 04:51:25 +02:00
|
|
|
@param Timeout The timeout value of SET FEATURE cmd, uses 100ns as a unit.
|
2010-09-29 07:50:45 +02:00
|
|
|
|
|
|
|
@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,
|
2018-05-29 04:51:25 +02:00
|
|
|
IN UINT32 FeatureSpecificData,
|
|
|
|
IN UINT64 Timeout
|
2010-09-29 07:50:45 +02:00
|
|
|
)
|
|
|
|
{
|
|
|
|
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));
|
2011-05-03 12:31:41 +02:00
|
|
|
|
2010-09-29 07:50:45 +02:00
|
|
|
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,
|
2018-05-29 04:51:25 +02:00
|
|
|
Timeout,
|
2011-05-03 12:31:41 +02:00
|
|
|
NULL
|
2010-09-29 07:50:45 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2011-11-15 14:44:06 +01:00
|
|
|
This function is used to send out ATAPI commands conforms to the Packet Command
|
2010-09-29 07:50:45 +02:00
|
|
|
with PIO Protocol.
|
|
|
|
|
|
|
|
@param PciIo The PCI IO protocol instance.
|
|
|
|
@param AhciRegisters The pointer to the EFI_AHCI_REGISTERS.
|
2011-11-15 14:44:06 +01:00
|
|
|
@param Port The number of port.
|
2010-09-29 07:50:45 +02:00
|
|
|
@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;
|
2021-12-05 23:54:02 +01:00
|
|
|
|
2010-09-29 07:50:45 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2011-05-03 12:31:41 +02:00
|
|
|
if (Length == 0) {
|
2010-09-29 07:50:45 +02:00
|
|
|
Status = AhciNonDataTransfer (
|
|
|
|
PciIo,
|
|
|
|
AhciRegisters,
|
|
|
|
Port,
|
|
|
|
PortMultiplier,
|
|
|
|
Packet->Cdb,
|
|
|
|
Packet->CdbLength,
|
|
|
|
&AtaCommandBlock,
|
|
|
|
&AtaStatusBlock,
|
2011-11-15 14:44:06 +01:00
|
|
|
Packet->Timeout,
|
2011-05-03 12:31:41 +02:00
|
|
|
NULL
|
2010-09-29 07:50:45 +02:00
|
|
|
);
|
|
|
|
} else {
|
2011-06-14 04:11:34 +02:00
|
|
|
Status = AhciPioTransfer (
|
|
|
|
PciIo,
|
|
|
|
AhciRegisters,
|
|
|
|
Port,
|
|
|
|
PortMultiplier,
|
|
|
|
Packet->Cdb,
|
|
|
|
Packet->CdbLength,
|
|
|
|
Read,
|
|
|
|
&AtaCommandBlock,
|
|
|
|
&AtaStatusBlock,
|
|
|
|
Buffer,
|
|
|
|
Length,
|
2011-11-15 14:44:06 +01:00
|
|
|
Packet->Timeout,
|
2011-06-14 04:11:34 +02:00
|
|
|
NULL
|
|
|
|
);
|
2010-09-29 07:50:45 +02:00
|
|
|
}
|
2021-12-05 23:54:02 +01:00
|
|
|
|
2010-09-29 07:50:45 +02:00
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
Allocate transfer-related data struct which is used at AHCI mode.
|
2011-11-15 14:44:06 +01:00
|
|
|
|
2010-09-29 07:50:45 +02:00
|
|
|
@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;
|
2012-11-14 06:12:40 +01:00
|
|
|
UINT32 PortImplementBitMap;
|
2010-09-29 07:50:45 +02:00
|
|
|
UINT8 MaxPortNumber;
|
|
|
|
UINT8 MaxCommandSlotNumber;
|
|
|
|
BOOLEAN Support64Bit;
|
|
|
|
UINT64 MaxReceiveFisSize;
|
|
|
|
UINT64 MaxCommandListSize;
|
|
|
|
UINT64 MaxCommandTableSize;
|
2011-03-08 02:32:34 +01:00
|
|
|
EFI_PHYSICAL_ADDRESS AhciRFisPciAddr;
|
|
|
|
EFI_PHYSICAL_ADDRESS AhciCmdListPciAddr;
|
|
|
|
EFI_PHYSICAL_ADDRESS AhciCommandTablePciAddr;
|
2010-09-29 07:50:45 +02:00
|
|
|
|
|
|
|
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);
|
2010-09-30 10:44:59 +02:00
|
|
|
Support64Bit = (BOOLEAN)(((Capability & BIT31) != 0) ? TRUE : FALSE);
|
2018-06-27 15:08:52 +02:00
|
|
|
|
2012-11-14 06:12:40 +01:00
|
|
|
PortImplementBitMap = AhciReadReg (PciIo, EFI_AHCI_PI_OFFSET);
|
|
|
|
//
|
2020-02-07 02:07:24 +01:00
|
|
|
// Get the highest bit of implemented ports which decides how many bytes are allocated for received FIS.
|
2012-11-14 06:12:40 +01:00
|
|
|
//
|
|
|
|
MaxPortNumber = (UINT8)(UINTN)(HighBitSet32 (PortImplementBitMap) + 1);
|
|
|
|
if (MaxPortNumber == 0) {
|
|
|
|
return EFI_DEVICE_ERROR;
|
|
|
|
}
|
2010-09-29 07:50:45 +02:00
|
|
|
|
|
|
|
MaxReceiveFisSize = MaxPortNumber * sizeof (EFI_AHCI_RECEIVED_FIS);
|
|
|
|
Status = PciIo->AllocateBuffer (
|
|
|
|
PciIo,
|
|
|
|
AllocateAnyPages,
|
|
|
|
EfiBootServicesData,
|
2011-03-08 02:32:34 +01:00
|
|
|
EFI_SIZE_TO_PAGES ((UINTN)MaxReceiveFisSize),
|
2010-09-29 07:50:45 +02:00
|
|
|
&Buffer,
|
|
|
|
0
|
|
|
|
);
|
|
|
|
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
|
|
return EFI_OUT_OF_RESOURCES;
|
|
|
|
}
|
|
|
|
|
2010-10-14 03:33:04 +02:00
|
|
|
ZeroMem (Buffer, (UINTN)MaxReceiveFisSize);
|
2010-09-29 07:50:45 +02:00
|
|
|
|
|
|
|
AhciRegisters->AhciRFis = Buffer;
|
|
|
|
AhciRegisters->MaxReceiveFisSize = MaxReceiveFisSize;
|
2010-10-14 03:33:04 +02:00
|
|
|
Bytes = (UINTN)MaxReceiveFisSize;
|
2010-09-29 07:50:45 +02:00
|
|
|
|
|
|
|
Status = PciIo->Map (
|
|
|
|
PciIo,
|
|
|
|
EfiPciIoOperationBusMasterCommonBuffer,
|
|
|
|
Buffer,
|
|
|
|
&Bytes,
|
2011-03-08 02:32:34 +01:00
|
|
|
&AhciRFisPciAddr,
|
2010-09-29 07:50:45 +02:00
|
|
|
&AhciRegisters->MapRFis
|
|
|
|
);
|
|
|
|
|
|
|
|
if (EFI_ERROR (Status) || (Bytes != MaxReceiveFisSize)) {
|
|
|
|
//
|
2011-11-15 14:44:06 +01:00
|
|
|
// Map error or unable to map the whole RFis buffer into a contiguous region.
|
2010-09-29 07:50:45 +02:00
|
|
|
//
|
|
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
|
|
goto Error6;
|
|
|
|
}
|
|
|
|
|
2011-03-08 02:32:34 +01:00
|
|
|
if ((!Support64Bit) && (AhciRFisPciAddr > 0x100000000ULL)) {
|
2010-09-29 07:50:45 +02:00
|
|
|
//
|
|
|
|
// The AHCI HBA doesn't support 64bit addressing, so should not get a >4G pci bus master address.
|
|
|
|
//
|
|
|
|
Status = EFI_DEVICE_ERROR;
|
|
|
|
goto Error5;
|
|
|
|
}
|
2021-12-05 23:54:02 +01:00
|
|
|
|
2011-03-08 02:32:34 +01:00
|
|
|
AhciRegisters->AhciRFisPciAddr = (EFI_AHCI_RECEIVED_FIS *)(UINTN)AhciRFisPciAddr;
|
2010-09-29 07:50:45 +02:00
|
|
|
|
|
|
|
//
|
|
|
|
// Allocate memory for command list
|
2020-02-07 02:07:24 +01:00
|
|
|
// Note that the implementation is a single task model which only use a command list for all ports.
|
2010-09-29 07:50:45 +02:00
|
|
|
//
|
|
|
|
Buffer = NULL;
|
|
|
|
MaxCommandListSize = MaxCommandSlotNumber * sizeof (EFI_AHCI_COMMAND_LIST);
|
|
|
|
Status = PciIo->AllocateBuffer (
|
|
|
|
PciIo,
|
|
|
|
AllocateAnyPages,
|
|
|
|
EfiBootServicesData,
|
2011-03-08 02:32:34 +01:00
|
|
|
EFI_SIZE_TO_PAGES ((UINTN)MaxCommandListSize),
|
2010-09-29 07:50:45 +02:00
|
|
|
&Buffer,
|
|
|
|
0
|
|
|
|
);
|
|
|
|
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
|
|
//
|
2011-11-15 14:44:06 +01:00
|
|
|
// Free mapped resource.
|
2010-09-29 07:50:45 +02:00
|
|
|
//
|
|
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
|
|
goto Error5;
|
|
|
|
}
|
|
|
|
|
2010-10-14 03:33:04 +02:00
|
|
|
ZeroMem (Buffer, (UINTN)MaxCommandListSize);
|
2010-09-29 07:50:45 +02:00
|
|
|
|
|
|
|
AhciRegisters->AhciCmdList = Buffer;
|
|
|
|
AhciRegisters->MaxCommandListSize = MaxCommandListSize;
|
2010-10-14 03:33:04 +02:00
|
|
|
Bytes = (UINTN)MaxCommandListSize;
|
2010-09-29 07:50:45 +02:00
|
|
|
|
|
|
|
Status = PciIo->Map (
|
|
|
|
PciIo,
|
|
|
|
EfiPciIoOperationBusMasterCommonBuffer,
|
|
|
|
Buffer,
|
|
|
|
&Bytes,
|
2011-03-08 02:32:34 +01:00
|
|
|
&AhciCmdListPciAddr,
|
2010-09-29 07:50:45 +02:00
|
|
|
&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;
|
|
|
|
}
|
|
|
|
|
2011-03-08 02:32:34 +01:00
|
|
|
if ((!Support64Bit) && (AhciCmdListPciAddr > 0x100000000ULL)) {
|
2010-09-29 07:50:45 +02:00
|
|
|
//
|
|
|
|
// The AHCI HBA doesn't support 64bit addressing, so should not get a >4G pci bus master address.
|
|
|
|
//
|
|
|
|
Status = EFI_DEVICE_ERROR;
|
|
|
|
goto Error3;
|
|
|
|
}
|
2021-12-05 23:54:02 +01:00
|
|
|
|
2011-03-08 02:32:34 +01:00
|
|
|
AhciRegisters->AhciCmdListPciAddr = (EFI_AHCI_COMMAND_LIST *)(UINTN)AhciCmdListPciAddr;
|
2010-09-29 07:50:45 +02:00
|
|
|
|
|
|
|
//
|
|
|
|
// 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,
|
2011-03-08 02:32:34 +01:00
|
|
|
EFI_SIZE_TO_PAGES ((UINTN)MaxCommandTableSize),
|
2010-09-29 07:50:45 +02:00
|
|
|
&Buffer,
|
|
|
|
0
|
|
|
|
);
|
|
|
|
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
|
|
//
|
2011-11-15 14:44:06 +01:00
|
|
|
// Free mapped resource.
|
2010-09-29 07:50:45 +02:00
|
|
|
//
|
|
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
|
|
goto Error3;
|
|
|
|
}
|
|
|
|
|
2010-10-14 03:33:04 +02:00
|
|
|
ZeroMem (Buffer, (UINTN)MaxCommandTableSize);
|
2010-09-29 07:50:45 +02:00
|
|
|
|
|
|
|
AhciRegisters->AhciCommandTable = Buffer;
|
|
|
|
AhciRegisters->MaxCommandTableSize = MaxCommandTableSize;
|
2010-10-14 03:33:04 +02:00
|
|
|
Bytes = (UINTN)MaxCommandTableSize;
|
2010-09-29 07:50:45 +02:00
|
|
|
|
|
|
|
Status = PciIo->Map (
|
|
|
|
PciIo,
|
|
|
|
EfiPciIoOperationBusMasterCommonBuffer,
|
|
|
|
Buffer,
|
|
|
|
&Bytes,
|
2011-03-08 02:32:34 +01:00
|
|
|
&AhciCommandTablePciAddr,
|
2010-09-29 07:50:45 +02:00
|
|
|
&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;
|
|
|
|
}
|
|
|
|
|
2011-03-08 02:32:34 +01:00
|
|
|
if ((!Support64Bit) && (AhciCommandTablePciAddr > 0x100000000ULL)) {
|
2010-09-29 07:50:45 +02:00
|
|
|
//
|
|
|
|
// The AHCI HBA doesn't support 64bit addressing, so should not get a >4G pci bus master address.
|
|
|
|
//
|
|
|
|
Status = EFI_DEVICE_ERROR;
|
|
|
|
goto Error1;
|
|
|
|
}
|
2021-12-05 23:54:02 +01:00
|
|
|
|
2011-03-08 02:32:34 +01:00
|
|
|
AhciRegisters->AhciCommandTablePciAddr = (EFI_AHCI_COMMAND_TABLE *)(UINTN)AhciCommandTablePciAddr;
|
2010-09-29 07:50:45 +02:00
|
|
|
|
|
|
|
return EFI_SUCCESS;
|
|
|
|
//
|
2011-11-15 14:44:06 +01:00
|
|
|
// Map error or unable to map the whole CmdList buffer into a contiguous region.
|
2010-09-29 07:50:45 +02:00
|
|
|
//
|
|
|
|
Error1:
|
|
|
|
PciIo->Unmap (
|
|
|
|
PciIo,
|
|
|
|
AhciRegisters->MapCommandTable
|
|
|
|
);
|
|
|
|
Error2:
|
|
|
|
PciIo->FreeBuffer (
|
|
|
|
PciIo,
|
2011-03-08 02:32:34 +01:00
|
|
|
EFI_SIZE_TO_PAGES ((UINTN)MaxCommandTableSize),
|
2010-09-29 07:50:45 +02:00
|
|
|
AhciRegisters->AhciCommandTable
|
|
|
|
);
|
|
|
|
Error3:
|
|
|
|
PciIo->Unmap (
|
|
|
|
PciIo,
|
|
|
|
AhciRegisters->MapCmdList
|
|
|
|
);
|
|
|
|
Error4:
|
|
|
|
PciIo->FreeBuffer (
|
|
|
|
PciIo,
|
2011-03-08 02:32:34 +01:00
|
|
|
EFI_SIZE_TO_PAGES ((UINTN)MaxCommandListSize),
|
2010-09-29 07:50:45 +02:00
|
|
|
AhciRegisters->AhciCmdList
|
|
|
|
);
|
|
|
|
Error5:
|
|
|
|
PciIo->Unmap (
|
|
|
|
PciIo,
|
|
|
|
AhciRegisters->MapRFis
|
|
|
|
);
|
|
|
|
Error6:
|
|
|
|
PciIo->FreeBuffer (
|
|
|
|
PciIo,
|
2011-03-08 02:32:34 +01:00
|
|
|
EFI_SIZE_TO_PAGES ((UINTN)MaxReceiveFisSize),
|
2010-09-29 07:50:45 +02:00
|
|
|
AhciRegisters->AhciRFis
|
|
|
|
);
|
|
|
|
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
2018-05-29 12:12:51 +02:00
|
|
|
/**
|
|
|
|
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
|
|
|
|
//
|
2018-06-11 04:17:01 +02:00
|
|
|
ZeroMem (&DevSlpTiming, sizeof (DevSlpTiming));
|
2018-05-29 12:12:51 +02:00
|
|
|
} 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;
|
|
|
|
}
|
2018-05-29 04:51:25 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2018-05-29 07:08:21 +02:00
|
|
|
/**
|
|
|
|
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);
|
|
|
|
}
|
2021-12-05 23:54:02 +01:00
|
|
|
|
2018-05-29 07:08:21 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2010-09-29 07:50:45 +02:00
|
|
|
/**
|
|
|
|
Initialize ATA host controller at AHCI mode.
|
|
|
|
|
2011-11-15 14:44:06 +01:00
|
|
|
The function is designed to initialize ATA host controller.
|
|
|
|
|
2010-09-29 07:50:45 +02:00
|
|
|
@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;
|
2021-12-05 23:54:02 +01:00
|
|
|
|
2010-09-29 07:50:45 +02:00
|
|
|
EFI_AHCI_REGISTERS *AhciRegisters;
|
2021-12-05 23:54:02 +01:00
|
|
|
|
2010-09-29 07:50:45 +02:00
|
|
|
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;
|
2011-06-14 04:11:34 +02:00
|
|
|
UINT32 PhyDetectDelay;
|
MdeModulePkg/AtaAtapiPassThru: Ensure GHC.AE bit is always set in Ahci
According to AHCI Spec 1.3 GHC.AE bit description:
"The implementation of this bit is dependent upon the value of the
CAP.SAM bit. If CAP.SAM is '0', then GHC.AE shall be read-write and shall
have a reset value of '0'. If CAP.SAM is '1', then AE shall be read-only
and shall have a reset value of '1'."
Being in AhciMode, for proper operation it is required, that GHC.AE bit
is always set, before any other AHCI registers are written to. Current
AhciMode implementation, both in AhciReset() and AhciModeInitialization()
functions, set GHC.AE bit only depending on 'CAP.SAM == 0' condition,
assuming (according to the AHCI spec), that otherwise it has to be set
anyway. It may however happen, that even if 'CAP.SAM == 1', GHC.AE
requires updating by software.
This patch enables in AhciMode setting GHC.AE in case its initial value
is '0'. It fixes AHCI support for Marvell Armada 70x0 and 80x0 SoC
families. The change is transparent to all other platforms.
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Marcin Wojtas <mw@semihalf.com>
Signed-off-by: Jan Dabros <jsd@semihalf.com>
Reviewed-by: Feng Tian <feng.tian@intel.com>
Reviewed-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
2016-11-24 08:54:33 +01:00
|
|
|
UINT32 Value;
|
2011-06-14 04:11:34 +02:00
|
|
|
|
2010-09-29 07:50:45 +02:00
|
|
|
if (Instance == NULL) {
|
|
|
|
return EFI_INVALID_PARAMETER;
|
|
|
|
}
|
|
|
|
|
|
|
|
PciIo = Instance->PciIo;
|
|
|
|
IdeInit = Instance->IdeControllerInit;
|
|
|
|
|
2011-11-15 14:44:06 +01:00
|
|
|
Status = AhciReset (PciIo, EFI_AHCI_BUS_RESET_TIMEOUT);
|
2010-09-29 07:50:45 +02:00
|
|
|
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
|
|
return EFI_DEVICE_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
2014-11-10 06:46:21 +01:00
|
|
|
// Collect AHCI controller information
|
2010-09-29 07:50:45 +02:00
|
|
|
//
|
2014-11-10 06:46:21 +01:00
|
|
|
Capability = AhciReadReg (PciIo, EFI_AHCI_CAPABILITY_OFFSET);
|
MdeModulePkg/AtaAtapiPassThru: Ensure GHC.AE bit is always set in Ahci
According to AHCI Spec 1.3 GHC.AE bit description:
"The implementation of this bit is dependent upon the value of the
CAP.SAM bit. If CAP.SAM is '0', then GHC.AE shall be read-write and shall
have a reset value of '0'. If CAP.SAM is '1', then AE shall be read-only
and shall have a reset value of '1'."
Being in AhciMode, for proper operation it is required, that GHC.AE bit
is always set, before any other AHCI registers are written to. Current
AhciMode implementation, both in AhciReset() and AhciModeInitialization()
functions, set GHC.AE bit only depending on 'CAP.SAM == 0' condition,
assuming (according to the AHCI spec), that otherwise it has to be set
anyway. It may however happen, that even if 'CAP.SAM == 1', GHC.AE
requires updating by software.
This patch enables in AhciMode setting GHC.AE in case its initial value
is '0'. It fixes AHCI support for Marvell Armada 70x0 and 80x0 SoC
families. The change is transparent to all other platforms.
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Marcin Wojtas <mw@semihalf.com>
Signed-off-by: Jan Dabros <jsd@semihalf.com>
Reviewed-by: Feng Tian <feng.tian@intel.com>
Reviewed-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
2016-11-24 08:54:33 +01:00
|
|
|
|
2010-09-29 07:50:45 +02:00
|
|
|
//
|
MdeModulePkg/AtaAtapiPassThru: Ensure GHC.AE bit is always set in Ahci
According to AHCI Spec 1.3 GHC.AE bit description:
"The implementation of this bit is dependent upon the value of the
CAP.SAM bit. If CAP.SAM is '0', then GHC.AE shall be read-write and shall
have a reset value of '0'. If CAP.SAM is '1', then AE shall be read-only
and shall have a reset value of '1'."
Being in AhciMode, for proper operation it is required, that GHC.AE bit
is always set, before any other AHCI registers are written to. Current
AhciMode implementation, both in AhciReset() and AhciModeInitialization()
functions, set GHC.AE bit only depending on 'CAP.SAM == 0' condition,
assuming (according to the AHCI spec), that otherwise it has to be set
anyway. It may however happen, that even if 'CAP.SAM == 1', GHC.AE
requires updating by software.
This patch enables in AhciMode setting GHC.AE in case its initial value
is '0'. It fixes AHCI support for Marvell Armada 70x0 and 80x0 SoC
families. The change is transparent to all other platforms.
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Marcin Wojtas <mw@semihalf.com>
Signed-off-by: Jan Dabros <jsd@semihalf.com>
Reviewed-by: Feng Tian <feng.tian@intel.com>
Reviewed-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
2016-11-24 08:54:33 +01:00
|
|
|
// Make sure that GHC.AE bit is set before accessing any AHCI registers.
|
2010-09-29 07:50:45 +02:00
|
|
|
//
|
MdeModulePkg/AtaAtapiPassThru: Ensure GHC.AE bit is always set in Ahci
According to AHCI Spec 1.3 GHC.AE bit description:
"The implementation of this bit is dependent upon the value of the
CAP.SAM bit. If CAP.SAM is '0', then GHC.AE shall be read-write and shall
have a reset value of '0'. If CAP.SAM is '1', then AE shall be read-only
and shall have a reset value of '1'."
Being in AhciMode, for proper operation it is required, that GHC.AE bit
is always set, before any other AHCI registers are written to. Current
AhciMode implementation, both in AhciReset() and AhciModeInitialization()
functions, set GHC.AE bit only depending on 'CAP.SAM == 0' condition,
assuming (according to the AHCI spec), that otherwise it has to be set
anyway. It may however happen, that even if 'CAP.SAM == 1', GHC.AE
requires updating by software.
This patch enables in AhciMode setting GHC.AE in case its initial value
is '0'. It fixes AHCI support for Marvell Armada 70x0 and 80x0 SoC
families. The change is transparent to all other platforms.
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Marcin Wojtas <mw@semihalf.com>
Signed-off-by: Jan Dabros <jsd@semihalf.com>
Reviewed-by: Feng Tian <feng.tian@intel.com>
Reviewed-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
2016-11-24 08:54:33 +01:00
|
|
|
Value = AhciReadReg (PciIo, EFI_AHCI_GHC_OFFSET);
|
|
|
|
|
|
|
|
if ((Value & EFI_AHCI_GHC_ENABLE) == 0) {
|
2014-11-10 06:46:21 +01:00
|
|
|
AhciOrReg (PciIo, EFI_AHCI_GHC_OFFSET, EFI_AHCI_GHC_ENABLE);
|
|
|
|
}
|
2016-09-05 10:49:44 +02:00
|
|
|
|
|
|
|
//
|
|
|
|
// 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)) {
|
2021-11-17 04:21:29 +01:00
|
|
|
DEBUG ((
|
|
|
|
DEBUG_WARN,
|
2016-09-05 10:49:44 +02:00
|
|
|
"AhciModeInitialization: failed to enable 64-bit DMA on 64-bit capable controller (%r)\n",
|
|
|
|
Status
|
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-09-29 07:50:45 +02:00
|
|
|
//
|
|
|
|
// Get the number of command slots per port supported by this HBA.
|
|
|
|
//
|
2011-06-14 04:11:34 +02:00
|
|
|
MaxPortNumber = (UINT8)((Capability & 0x1F) + 1);
|
2010-09-29 07:50:45 +02:00
|
|
|
|
|
|
|
//
|
|
|
|
// Get the bit map of those ports exposed by this HBA.
|
2011-11-15 14:44:06 +01:00
|
|
|
// It indicates which ports that the HBA supports are available for software to use.
|
2010-09-29 07:50:45 +02:00
|
|
|
//
|
|
|
|
PortImplementBitMap = AhciReadReg (PciIo, EFI_AHCI_PI_OFFSET);
|
2011-11-15 14:44:06 +01:00
|
|
|
|
2010-09-29 07:50:45 +02:00
|
|
|
AhciRegisters = &Instance->AhciRegisters;
|
|
|
|
Status = AhciCreateTransferDescriptor (PciIo, AhciRegisters);
|
|
|
|
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
|
|
return EFI_OUT_OF_RESOURCES;
|
|
|
|
}
|
|
|
|
|
2012-11-09 07:39:56 +01:00
|
|
|
for (Port = 0; Port < EFI_AHCI_MAX_PORTS; Port++) {
|
2017-09-19 10:11:29 +02:00
|
|
|
if ((PortImplementBitMap & (((UINT32)BIT0) << Port)) != 0) {
|
2012-11-09 07:39:56 +01:00
|
|
|
//
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
2011-06-14 04:11:34 +02:00
|
|
|
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);
|
|
|
|
|
2010-09-29 07:50:45 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2011-06-14 04:11:34 +02:00
|
|
|
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);
|
2010-09-29 07:50:45 +02:00
|
|
|
|
2011-06-14 04:11:34 +02:00
|
|
|
//
|
|
|
|
// 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);
|
|
|
|
|
2010-09-29 07:50:45 +02:00
|
|
|
//
|
2017-06-23 16:50:53 +02:00
|
|
|
// Wait for the Phy to detect the presence of a device.
|
2010-09-29 07:50:45 +02:00
|
|
|
//
|
2011-06-14 04:11:34 +02:00
|
|
|
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;
|
2010-09-29 07:50:45 +02:00
|
|
|
}
|
|
|
|
|
2011-06-14 04:11:34 +02:00
|
|
|
MicroSecondDelay (1000);
|
|
|
|
PhyDetectDelay--;
|
|
|
|
} while (PhyDetectDelay > 0);
|
|
|
|
|
|
|
|
if (PhyDetectDelay == 0) {
|
2010-09-29 07:50:45 +02:00
|
|
|
//
|
2011-06-14 04:11:34 +02:00
|
|
|
// No device detected at this port.
|
2012-05-29 09:05:33 +02:00
|
|
|
// Clear PxCMD.SUD for those ports at which there are no device present.
|
2010-09-29 07:50:45 +02:00
|
|
|
//
|
2012-05-29 09:05:33 +02:00
|
|
|
Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;
|
|
|
|
AhciAndReg (PciIo, Offset, (UINT32) ~(EFI_AHCI_PORT_CMD_SUD));
|
2011-06-14 04:11:34 +02:00
|
|
|
continue;
|
|
|
|
}
|
2010-09-29 07:50:45 +02:00
|
|
|
|
2020-11-05 13:48:45 +01:00
|
|
|
Status = AhciWaitDeviceReady (PciIo, Port);
|
|
|
|
if (EFI_ERROR (Status)) {
|
2011-06-14 04:11:34 +02:00
|
|
|
continue;
|
|
|
|
}
|
2010-09-29 07:50:45 +02:00
|
|
|
|
2011-06-14 04:11:34 +02:00
|
|
|
//
|
|
|
|
// 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;
|
2011-11-03 13:38:21 +01:00
|
|
|
Status = AhciWaitMmioSet (
|
2011-11-15 14:44:06 +01:00
|
|
|
PciIo,
|
2011-06-14 04:11:34 +02:00
|
|
|
Offset,
|
|
|
|
0x0000FFFF,
|
|
|
|
0x00000101,
|
|
|
|
EFI_TIMER_PERIOD_SECONDS (16)
|
|
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
|
|
continue;
|
|
|
|
}
|
2010-09-29 07:50:45 +02:00
|
|
|
|
2011-06-14 04:11:34 +02:00
|
|
|
Data = AhciReadReg (PciIo, Offset);
|
|
|
|
if ((Data & EFI_AHCI_ATAPI_SIG_MASK) == EFI_AHCI_ATAPI_DEVICE_SIG) {
|
|
|
|
Status = AhciIdentifyPacket (PciIo, AhciRegisters, Port, 0, &Buffer);
|
2010-09-29 07:50:45 +02:00
|
|
|
|
2011-06-14 04:11:34 +02:00
|
|
|
if (EFI_ERROR (Status)) {
|
2010-09-29 07:50:45 +02:00
|
|
|
continue;
|
|
|
|
}
|
2010-09-30 10:44:59 +02:00
|
|
|
|
2011-06-14 04:11:34 +02:00
|
|
|
DeviceType = EfiIdeCdrom;
|
|
|
|
} else if ((Data & EFI_AHCI_ATAPI_SIG_MASK) == EFI_AHCI_ATA_DEVICE_SIG) {
|
|
|
|
Status = AhciIdentify (PciIo, AhciRegisters, Port, 0, &Buffer);
|
2010-09-29 07:50:45 +02:00
|
|
|
|
|
|
|
if (EFI_ERROR (Status)) {
|
2011-06-14 04:11:34 +02:00
|
|
|
REPORT_STATUS_CODE (EFI_PROGRESS_CODE, (EFI_PERIPHERAL_FIXED_MEDIA | EFI_P_EC_NOT_DETECTED));
|
2010-09-29 07:50:45 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2018-05-29 04:51:25 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-06-14 04:11:34 +02:00
|
|
|
DeviceType = EfiIdeHarddisk;
|
|
|
|
} else {
|
|
|
|
continue;
|
|
|
|
}
|
2021-12-05 23:54:02 +01:00
|
|
|
|
2020-02-07 02:07:24 +01:00
|
|
|
DEBUG ((
|
|
|
|
DEBUG_INFO,
|
|
|
|
"port [%d] port multitplier [%d] has a [%a]\n",
|
2011-06-14 04:11:34 +02:00
|
|
|
Port,
|
|
|
|
0,
|
|
|
|
DeviceType == EfiIdeCdrom ? "cdrom" : "harddisk"
|
|
|
|
));
|
2010-09-29 07:50:45 +02:00
|
|
|
|
2011-06-14 04:11:34 +02:00
|
|
|
//
|
|
|
|
// If the device is a hard disk, then try to enable S.M.A.R.T feature
|
|
|
|
//
|
2012-05-29 08:59:26 +02:00
|
|
|
if ((DeviceType == EfiIdeHarddisk) && PcdGetBool (PcdAtaSmartEnable)) {
|
2011-06-14 04:11:34 +02:00
|
|
|
AhciAtaSmartSupport (
|
|
|
|
PciIo,
|
|
|
|
AhciRegisters,
|
|
|
|
Port,
|
|
|
|
0,
|
|
|
|
&Buffer,
|
|
|
|
NULL
|
|
|
|
);
|
|
|
|
}
|
2010-09-29 07:50:45 +02:00
|
|
|
|
2011-06-14 04:11:34 +02:00
|
|
|
//
|
|
|
|
// Submit identify data to IDE controller init driver
|
|
|
|
//
|
|
|
|
IdeInit->SubmitData (IdeInit, Port, 0, &Buffer);
|
2010-09-30 10:44:59 +02:00
|
|
|
|
2011-06-14 04:11:34 +02:00
|
|
|
//
|
|
|
|
// Now start to config ide device parameter and transfer mode.
|
|
|
|
//
|
|
|
|
Status = IdeInit->CalculateMode (
|
|
|
|
IdeInit,
|
|
|
|
Port,
|
|
|
|
0,
|
|
|
|
&SupportedModes
|
|
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
2021-11-17 04:21:29 +01:00
|
|
|
DEBUG ((DEBUG_ERROR, "Calculate Mode Fail, Status = %r\n", Status));
|
2011-06-14 04:11:34 +02:00
|
|
|
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);
|
|
|
|
|
|
|
|
//
|
2020-02-07 02:07:24 +01:00
|
|
|
// Set supported DMA mode on this IDE device. Note that UDMA & MDMA can't
|
2011-06-14 04:11:34 +02:00
|
|
|
// 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;
|
2011-11-15 14:44:06 +01:00
|
|
|
TransferMode.ModeNumber = (UINT8)SupportedModes->MultiWordDmaMode.Mode;
|
2011-06-14 04:11:34 +02:00
|
|
|
}
|
|
|
|
|
2018-05-29 04:51:25 +02:00
|
|
|
Status = AhciDeviceSetFeature (PciIo, AhciRegisters, Port, 0, 0x03, (UINT32)(*(UINT8 *)&TransferMode), ATA_ATAPI_TIMEOUT);
|
2011-06-14 04:11:34 +02:00
|
|
|
if (EFI_ERROR (Status)) {
|
2021-11-17 04:21:29 +01:00
|
|
|
DEBUG ((DEBUG_ERROR, "Set transfer Mode Fail, Status = %r\n", Status));
|
2011-06-14 04:11:34 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Found a ATA or ATAPI device, add it into the device list.
|
|
|
|
//
|
2016-04-08 10:23:57 +02:00
|
|
|
CreateNewDeviceInfo (Instance, Port, 0xFFFF, DeviceType, &Buffer);
|
2011-06-14 04:11:34 +02:00
|
|
|
if (DeviceType == EfiIdeHarddisk) {
|
|
|
|
REPORT_STATUS_CODE (EFI_PROGRESS_CODE, (EFI_PERIPHERAL_FIXED_MEDIA | EFI_P_PC_ENABLE));
|
2018-05-29 12:12:51 +02:00
|
|
|
AhciEnableDevSlp (
|
|
|
|
PciIo,
|
|
|
|
AhciRegisters,
|
|
|
|
Port,
|
|
|
|
0,
|
|
|
|
&Buffer
|
|
|
|
);
|
2010-09-29 07:50:45 +02:00
|
|
|
}
|
2018-05-29 07:08:21 +02:00
|
|
|
|
|
|
|
//
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
}
|
2010-09-29 07:50:45 +02:00
|
|
|
}
|
|
|
|
}
|
2011-06-14 04:11:34 +02:00
|
|
|
|
2010-09-29 07:50:45 +02:00
|
|
|
return EFI_SUCCESS;
|
|
|
|
}
|