/** @file | |
Header file for AHCI mode of ATA host controller. | |
Copyright (c) 2010 - 2015, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include "AtaAtapiPassThru.h" | |
/** | |
read a one-byte data from a IDE port. | |
@param PciIo A pointer to EFI_PCI_IO_PROTOCOL data structure | |
@param Port The IDE Port number | |
@return the one-byte data read from IDE port | |
**/ | |
UINT8 | |
EFIAPI | |
IdeReadPortB ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN UINT16 Port | |
) | |
{ | |
UINT8 Data; | |
ASSERT (PciIo != NULL); | |
Data = 0; | |
// | |
// perform 1-byte data read from register | |
// | |
PciIo->Io.Read ( | |
PciIo, | |
EfiPciIoWidthUint8, | |
EFI_PCI_IO_PASS_THROUGH_BAR, | |
(UINT64)Port, | |
1, | |
&Data | |
); | |
return Data; | |
} | |
/** | |
write a 1-byte data to a specific IDE port. | |
@param PciIo A pointer to EFI_PCI_IO_PROTOCOL data structure | |
@param Port The IDE port to be written | |
@param Data The data to write to the port | |
**/ | |
VOID | |
EFIAPI | |
IdeWritePortB ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN UINT16 Port, | |
IN UINT8 Data | |
) | |
{ | |
ASSERT (PciIo != NULL); | |
// | |
// perform 1-byte data write to register | |
// | |
PciIo->Io.Write ( | |
PciIo, | |
EfiPciIoWidthUint8, | |
EFI_PCI_IO_PASS_THROUGH_BAR, | |
(UINT64)Port, | |
1, | |
&Data | |
); | |
} | |
/** | |
write a 1-word data to a specific IDE port. | |
@param PciIo A pointer to EFI_PCI_IO_PROTOCOL data structure | |
@param Port The IDE port to be written | |
@param Data The data to write to the port | |
**/ | |
VOID | |
EFIAPI | |
IdeWritePortW ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN UINT16 Port, | |
IN UINT16 Data | |
) | |
{ | |
ASSERT (PciIo != NULL); | |
// | |
// perform 1-word data write to register | |
// | |
PciIo->Io.Write ( | |
PciIo, | |
EfiPciIoWidthUint16, | |
EFI_PCI_IO_PASS_THROUGH_BAR, | |
(UINT64)Port, | |
1, | |
&Data | |
); | |
} | |
/** | |
write a 2-word data to a specific IDE port. | |
@param PciIo A pointer to EFI_PCI_IO_PROTOCOL data structure | |
@param Port The IDE port to be written | |
@param Data The data to write to the port | |
**/ | |
VOID | |
EFIAPI | |
IdeWritePortDW ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN UINT16 Port, | |
IN UINT32 Data | |
) | |
{ | |
ASSERT (PciIo != NULL); | |
// | |
// perform 2-word data write to register | |
// | |
PciIo->Io.Write ( | |
PciIo, | |
EfiPciIoWidthUint32, | |
EFI_PCI_IO_PASS_THROUGH_BAR, | |
(UINT64)Port, | |
1, | |
&Data | |
); | |
} | |
/** | |
Write multiple words of data to the IDE data port. | |
Call the IO abstraction once to do the complete read, | |
not one word at a time | |
@param PciIo A pointer to EFI_PCI_IO_PROTOCOL data structure | |
@param Port IO port to read | |
@param Count No. of UINT16's to read | |
@param Buffer Pointer to the data buffer for read | |
**/ | |
VOID | |
EFIAPI | |
IdeWritePortWMultiple ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN UINT16 Port, | |
IN UINTN Count, | |
IN VOID *Buffer | |
) | |
{ | |
ASSERT (PciIo != NULL); | |
ASSERT (Buffer != NULL); | |
// | |
// perform UINT16 data write to the FIFO | |
// | |
PciIo->Io.Write ( | |
PciIo, | |
EfiPciIoWidthFifoUint16, | |
EFI_PCI_IO_PASS_THROUGH_BAR, | |
(UINT64)Port, | |
Count, | |
(UINT16 *)Buffer | |
); | |
} | |
/** | |
Reads multiple words of data from the IDE data port. | |
Call the IO abstraction once to do the complete read, | |
not one word at a time | |
@param PciIo A pointer to EFI_PCI_IO_PROTOCOL data structure | |
@param Port IO port to read | |
@param Count Number of UINT16's to read | |
@param Buffer Pointer to the data buffer for read | |
**/ | |
VOID | |
EFIAPI | |
IdeReadPortWMultiple ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN UINT16 Port, | |
IN UINTN Count, | |
IN VOID *Buffer | |
) | |
{ | |
ASSERT (PciIo != NULL); | |
ASSERT (Buffer != NULL); | |
// | |
// Perform UINT16 data read from FIFO | |
// | |
PciIo->Io.Read ( | |
PciIo, | |
EfiPciIoWidthFifoUint16, | |
EFI_PCI_IO_PASS_THROUGH_BAR, | |
(UINT64)Port, | |
Count, | |
(UINT16 *)Buffer | |
); | |
} | |
/** | |
This function is used to analyze the Status Register and print out | |
some debug information and if there is ERR bit set in the Status | |
Register, the Error Register's value is also be parsed and print out. | |
@param PciIo A pointer to EFI_PCI_IO_PROTOCOL data structure. | |
@param IdeRegisters A pointer to EFI_IDE_REGISTERS data structure. | |
@param AtaStatusBlock A pointer to EFI_ATA_STATUS_BLOCK data structure. | |
**/ | |
VOID | |
EFIAPI | |
DumpAllIdeRegisters ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN EFI_IDE_REGISTERS *IdeRegisters, | |
IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock | |
) | |
{ | |
EFI_ATA_STATUS_BLOCK StatusBlock; | |
ASSERT (PciIo != NULL); | |
ASSERT (IdeRegisters != NULL); | |
ZeroMem (&StatusBlock, sizeof (EFI_ATA_STATUS_BLOCK)); | |
StatusBlock.AtaStatus = IdeReadPortB (PciIo, IdeRegisters->CmdOrStatus); | |
StatusBlock.AtaError = IdeReadPortB (PciIo, IdeRegisters->ErrOrFeature); | |
StatusBlock.AtaSectorCount = IdeReadPortB (PciIo, IdeRegisters->SectorCount); | |
StatusBlock.AtaSectorCountExp = IdeReadPortB (PciIo, IdeRegisters->SectorCount); | |
StatusBlock.AtaSectorNumber = IdeReadPortB (PciIo, IdeRegisters->SectorNumber); | |
StatusBlock.AtaSectorNumberExp = IdeReadPortB (PciIo, IdeRegisters->SectorNumber); | |
StatusBlock.AtaCylinderLow = IdeReadPortB (PciIo, IdeRegisters->CylinderLsb); | |
StatusBlock.AtaCylinderLowExp = IdeReadPortB (PciIo, IdeRegisters->CylinderLsb); | |
StatusBlock.AtaCylinderHigh = IdeReadPortB (PciIo, IdeRegisters->CylinderMsb); | |
StatusBlock.AtaCylinderHighExp = IdeReadPortB (PciIo, IdeRegisters->CylinderMsb); | |
StatusBlock.AtaDeviceHead = IdeReadPortB (PciIo, IdeRegisters->Head); | |
if (AtaStatusBlock != NULL) { | |
// | |
// Dump the content of all ATA registers. | |
// | |
CopyMem (AtaStatusBlock, &StatusBlock, sizeof (EFI_ATA_STATUS_BLOCK)); | |
} | |
DEBUG_CODE_BEGIN (); | |
if ((StatusBlock.AtaStatus & ATA_STSREG_DWF) != 0) { | |
DEBUG ((DEBUG_ERROR, "CheckRegisterStatus()-- %02x : Error : Write Fault\n", StatusBlock.AtaStatus)); | |
} | |
if ((StatusBlock.AtaStatus & ATA_STSREG_CORR) != 0) { | |
DEBUG ((DEBUG_ERROR, "CheckRegisterStatus()-- %02x : Error : Corrected Data\n", StatusBlock.AtaStatus)); | |
} | |
if ((StatusBlock.AtaStatus & ATA_STSREG_ERR) != 0) { | |
if ((StatusBlock.AtaError & ATA_ERRREG_BBK) != 0) { | |
DEBUG ((DEBUG_ERROR, "CheckRegisterStatus()-- %02x : Error : Bad Block Detected\n", StatusBlock.AtaError)); | |
} | |
if ((StatusBlock.AtaError & ATA_ERRREG_UNC) != 0) { | |
DEBUG ((DEBUG_ERROR, "CheckRegisterStatus()-- %02x : Error : Uncorrectable Data\n", StatusBlock.AtaError)); | |
} | |
if ((StatusBlock.AtaError & ATA_ERRREG_MC) != 0) { | |
DEBUG ((DEBUG_ERROR, "CheckRegisterStatus()-- %02x : Error : Media Change\n", StatusBlock.AtaError)); | |
} | |
if ((StatusBlock.AtaError & ATA_ERRREG_ABRT) != 0) { | |
DEBUG ((DEBUG_ERROR, "CheckRegisterStatus()-- %02x : Error : Abort\n", StatusBlock.AtaError)); | |
} | |
if ((StatusBlock.AtaError & ATA_ERRREG_TK0NF) != 0) { | |
DEBUG ((DEBUG_ERROR, "CheckRegisterStatus()-- %02x : Error : Track 0 Not Found\n", StatusBlock.AtaError)); | |
} | |
if ((StatusBlock.AtaError & ATA_ERRREG_AMNF) != 0) { | |
DEBUG ((DEBUG_ERROR, "CheckRegisterStatus()-- %02x : Error : Address Mark Not Found\n", StatusBlock.AtaError)); | |
} | |
} | |
DEBUG_CODE_END (); | |
} | |
/** | |
This function is used to analyze the Status Register at the condition that BSY is zero. | |
if there is ERR bit set in the Status Register, then return error. | |
@param PciIo A pointer to EFI_PCI_IO_PROTOCOL data structure. | |
@param IdeRegisters A pointer to EFI_IDE_REGISTERS data structure. | |
@retval EFI_SUCCESS No err information in the Status Register. | |
@retval EFI_DEVICE_ERROR Any err information in the Status Register. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
CheckStatusRegister ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN EFI_IDE_REGISTERS *IdeRegisters | |
) | |
{ | |
UINT8 StatusRegister; | |
ASSERT (PciIo != NULL); | |
ASSERT (IdeRegisters != NULL); | |
StatusRegister = IdeReadPortB (PciIo, IdeRegisters->CmdOrStatus); | |
if ((StatusRegister & ATA_STSREG_BSY) == 0) { | |
if ((StatusRegister & (ATA_STSREG_ERR | ATA_STSREG_DWF | ATA_STSREG_CORR)) == 0) { | |
return EFI_SUCCESS; | |
} else { | |
return EFI_DEVICE_ERROR; | |
} | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
This function is used to poll for the DRQ bit clear in the Status | |
Register. DRQ is cleared when the device is finished transferring data. | |
So this function is called after data transfer is finished. | |
@param PciIo A pointer to EFI_PCI_IO_PROTOCOL data structure. | |
@param IdeRegisters A pointer to EFI_IDE_REGISTERS data structure. | |
@param Timeout The time to complete the command, uses 100ns as a unit. | |
@retval EFI_SUCCESS DRQ bit clear within the time out. | |
@retval EFI_TIMEOUT DRQ bit not clear within the time out. | |
@note | |
Read Status Register will clear interrupt status. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
DRQClear ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN EFI_IDE_REGISTERS *IdeRegisters, | |
IN UINT64 Timeout | |
) | |
{ | |
UINT64 Delay; | |
UINT8 StatusRegister; | |
BOOLEAN InfiniteWait; | |
ASSERT (PciIo != NULL); | |
ASSERT (IdeRegisters != NULL); | |
if (Timeout == 0) { | |
InfiniteWait = TRUE; | |
} else { | |
InfiniteWait = FALSE; | |
} | |
Delay = DivU64x32 (Timeout, 1000) + 1; | |
do { | |
StatusRegister = IdeReadPortB (PciIo, IdeRegisters->CmdOrStatus); | |
// | |
// Wait for BSY == 0, then judge if DRQ is clear | |
// | |
if ((StatusRegister & ATA_STSREG_BSY) == 0) { | |
if ((StatusRegister & ATA_STSREG_DRQ) == ATA_STSREG_DRQ) { | |
return EFI_DEVICE_ERROR; | |
} else { | |
return EFI_SUCCESS; | |
} | |
} | |
// | |
// Stall for 100 microseconds. | |
// | |
MicroSecondDelay (100); | |
Delay--; | |
} while (InfiniteWait || (Delay > 0)); | |
return EFI_TIMEOUT; | |
} | |
/** | |
This function is used to poll for the DRQ bit clear in the Alternate | |
Status Register. DRQ is cleared when the device is finished | |
transferring data. So this function is called after data transfer | |
is finished. | |
@param PciIo A pointer to EFI_PCI_IO_PROTOCOL data structure. | |
@param IdeRegisters A pointer to EFI_IDE_REGISTERS data structure. | |
@param Timeout The time to complete the command, uses 100ns as a unit. | |
@retval EFI_SUCCESS DRQ bit clear within the time out. | |
@retval EFI_TIMEOUT DRQ bit not clear within the time out. | |
@note Read Alternate Status Register will not clear interrupt status. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
DRQClear2 ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN EFI_IDE_REGISTERS *IdeRegisters, | |
IN UINT64 Timeout | |
) | |
{ | |
UINT64 Delay; | |
UINT8 AltRegister; | |
BOOLEAN InfiniteWait; | |
ASSERT (PciIo != NULL); | |
ASSERT (IdeRegisters != NULL); | |
if (Timeout == 0) { | |
InfiniteWait = TRUE; | |
} else { | |
InfiniteWait = FALSE; | |
} | |
Delay = DivU64x32 (Timeout, 1000) + 1; | |
do { | |
AltRegister = IdeReadPortB (PciIo, IdeRegisters->AltOrDev); | |
// | |
// Wait for BSY == 0, then judge if DRQ is clear | |
// | |
if ((AltRegister & ATA_STSREG_BSY) == 0) { | |
if ((AltRegister & ATA_STSREG_DRQ) == ATA_STSREG_DRQ) { | |
return EFI_DEVICE_ERROR; | |
} else { | |
return EFI_SUCCESS; | |
} | |
} | |
// | |
// Stall for 100 microseconds. | |
// | |
MicroSecondDelay (100); | |
Delay--; | |
} while (InfiniteWait || (Delay > 0)); | |
return EFI_TIMEOUT; | |
} | |
/** | |
This function is used to poll for the DRQ bit set in the | |
Status Register. | |
DRQ is set when the device is ready to transfer data. So this function | |
is called after the command is sent to the device and before required | |
data is transferred. | |
@param PciIo A pointer to EFI_PCI_IO_PROTOCOL data structure. | |
@param IdeRegisters A pointer to EFI_IDE_REGISTERS data structure. | |
@param Timeout The time to complete the command, uses 100ns as a unit. | |
@retval EFI_SUCCESS BSY bit cleared and DRQ bit set within the | |
timeout. | |
@retval EFI_TIMEOUT BSY bit not cleared within the timeout. | |
@retval EFI_ABORTED Polling abandoned due to command abort. | |
@retval EFI_DEVICE_ERROR Polling abandoned due to a non-abort error. | |
@retval EFI_NOT_READY BSY bit cleared within timeout, and device | |
reported "command complete" by clearing DRQ | |
bit. | |
@note Read Status Register will clear interrupt status. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
DRQReady ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN EFI_IDE_REGISTERS *IdeRegisters, | |
IN UINT64 Timeout | |
) | |
{ | |
UINT64 Delay; | |
UINT8 StatusRegister; | |
UINT8 ErrorRegister; | |
BOOLEAN InfiniteWait; | |
ASSERT (PciIo != NULL); | |
ASSERT (IdeRegisters != NULL); | |
if (Timeout == 0) { | |
InfiniteWait = TRUE; | |
} else { | |
InfiniteWait = FALSE; | |
} | |
Delay = DivU64x32 (Timeout, 1000) + 1; | |
do { | |
// | |
// Read Status Register will clear interrupt | |
// | |
StatusRegister = IdeReadPortB (PciIo, IdeRegisters->CmdOrStatus); | |
// | |
// Wait for BSY == 0, then judge if DRQ is clear or ERR is set | |
// | |
if ((StatusRegister & ATA_STSREG_BSY) == 0) { | |
if ((StatusRegister & ATA_STSREG_ERR) == ATA_STSREG_ERR) { | |
ErrorRegister = IdeReadPortB (PciIo, IdeRegisters->ErrOrFeature); | |
if ((ErrorRegister & ATA_ERRREG_ABRT) == ATA_ERRREG_ABRT) { | |
return EFI_ABORTED; | |
} | |
return EFI_DEVICE_ERROR; | |
} | |
if ((StatusRegister & ATA_STSREG_DRQ) == ATA_STSREG_DRQ) { | |
return EFI_SUCCESS; | |
} else { | |
return EFI_NOT_READY; | |
} | |
} | |
// | |
// Stall for 100 microseconds. | |
// | |
MicroSecondDelay (100); | |
Delay--; | |
} while (InfiniteWait || (Delay > 0)); | |
return EFI_TIMEOUT; | |
} | |
/** | |
This function is used to poll for the DRQ bit set in the Alternate Status Register. | |
DRQ is set when the device is ready to transfer data. So this function is called after | |
the command is sent to the device and before required data is transferred. | |
@param PciIo A pointer to EFI_PCI_IO_PROTOCOL data structure. | |
@param IdeRegisters A pointer to EFI_IDE_REGISTERS data structure. | |
@param Timeout The time to complete the command, uses 100ns as a unit. | |
@retval EFI_SUCCESS BSY bit cleared and DRQ bit set within the | |
timeout. | |
@retval EFI_TIMEOUT BSY bit not cleared within the timeout. | |
@retval EFI_ABORTED Polling abandoned due to command abort. | |
@retval EFI_DEVICE_ERROR Polling abandoned due to a non-abort error. | |
@retval EFI_NOT_READY BSY bit cleared within timeout, and device | |
reported "command complete" by clearing DRQ | |
bit. | |
@note Read Alternate Status Register will not clear interrupt status. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
DRQReady2 ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN EFI_IDE_REGISTERS *IdeRegisters, | |
IN UINT64 Timeout | |
) | |
{ | |
UINT64 Delay; | |
UINT8 AltRegister; | |
UINT8 ErrorRegister; | |
BOOLEAN InfiniteWait; | |
ASSERT (PciIo != NULL); | |
ASSERT (IdeRegisters != NULL); | |
if (Timeout == 0) { | |
InfiniteWait = TRUE; | |
} else { | |
InfiniteWait = FALSE; | |
} | |
Delay = DivU64x32 (Timeout, 1000) + 1; | |
do { | |
// | |
// Read Alternate Status Register will not clear interrupt status | |
// | |
AltRegister = IdeReadPortB (PciIo, IdeRegisters->AltOrDev); | |
// | |
// Wait for BSY == 0, then judge if DRQ is clear or ERR is set | |
// | |
if ((AltRegister & ATA_STSREG_BSY) == 0) { | |
if ((AltRegister & ATA_STSREG_ERR) == ATA_STSREG_ERR) { | |
ErrorRegister = IdeReadPortB (PciIo, IdeRegisters->ErrOrFeature); | |
if ((ErrorRegister & ATA_ERRREG_ABRT) == ATA_ERRREG_ABRT) { | |
return EFI_ABORTED; | |
} | |
return EFI_DEVICE_ERROR; | |
} | |
if ((AltRegister & ATA_STSREG_DRQ) == ATA_STSREG_DRQ) { | |
return EFI_SUCCESS; | |
} else { | |
return EFI_NOT_READY; | |
} | |
} | |
// | |
// Stall for 100 microseconds. | |
// | |
MicroSecondDelay (100); | |
Delay--; | |
} while (InfiniteWait || (Delay > 0)); | |
return EFI_TIMEOUT; | |
} | |
/** | |
This function is used to poll for the BSY bit clear in the Status Register. BSY | |
is clear when the device is not busy. Every command must be sent after device is not busy. | |
@param PciIo A pointer to ATA_ATAPI_PASS_THRU_INSTANCE data structure. | |
@param IdeRegisters A pointer to EFI_IDE_REGISTERS data structure. | |
@param Timeout The time to complete the command, uses 100ns as a unit. | |
@retval EFI_SUCCESS BSY bit clear within the time out. | |
@retval EFI_TIMEOUT BSY bit not clear within the time out. | |
@note Read Status Register will clear interrupt status. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
WaitForBSYClear ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN EFI_IDE_REGISTERS *IdeRegisters, | |
IN UINT64 Timeout | |
) | |
{ | |
UINT64 Delay; | |
UINT8 StatusRegister; | |
BOOLEAN InfiniteWait; | |
ASSERT (PciIo != NULL); | |
ASSERT (IdeRegisters != NULL); | |
if (Timeout == 0) { | |
InfiniteWait = TRUE; | |
} else { | |
InfiniteWait = FALSE; | |
} | |
Delay = DivU64x32 (Timeout, 1000) + 1; | |
do { | |
StatusRegister = IdeReadPortB (PciIo, IdeRegisters->CmdOrStatus); | |
if ((StatusRegister & ATA_STSREG_BSY) == 0x00) { | |
return EFI_SUCCESS; | |
} | |
// | |
// Stall for 100 microseconds. | |
// | |
MicroSecondDelay (100); | |
Delay--; | |
} while (InfiniteWait || (Delay > 0)); | |
return EFI_TIMEOUT; | |
} | |
/** | |
Get IDE i/o port registers' base addresses by mode. | |
In 'Compatibility' mode, use fixed addresses. | |
In Native-PCI mode, get base addresses from BARs in the PCI IDE controller's | |
Configuration Space. | |
The steps to get IDE i/o port registers' base addresses for each channel | |
as follows: | |
1. Examine the Programming Interface byte of the Class Code fields in PCI IDE | |
controller's Configuration Space to determine the operating mode. | |
2. a) In 'Compatibility' mode, use fixed addresses shown in the Table 1 below. | |
___________________________________________ | |
| | Command Block | Control Block | | |
| Channel | Registers | Registers | | |
|___________|_______________|_______________| | |
| Primary | 1F0h - 1F7h | 3F6h - 3F7h | | |
|___________|_______________|_______________| | |
| Secondary | 170h - 177h | 376h - 377h | | |
|___________|_______________|_______________| | |
Table 1. Compatibility resource mappings | |
b) In Native-PCI mode, IDE registers are mapped into IO space using the BARs | |
in IDE controller's PCI Configuration Space, shown in the Table 2 below. | |
___________________________________________________ | |
| | Command Block | Control Block | | |
| Channel | Registers | Registers | | |
|___________|___________________|___________________| | |
| Primary | BAR at offset 0x10| BAR at offset 0x14| | |
|___________|___________________|___________________| | |
| Secondary | BAR at offset 0x18| BAR at offset 0x1C| | |
|___________|___________________|___________________| | |
Table 2. BARs for Register Mapping | |
@param[in] PciIo Pointer to the EFI_PCI_IO_PROTOCOL instance | |
@param[in, out] IdeRegisters Pointer to EFI_IDE_REGISTERS which is used to | |
store the IDE i/o port registers' base addresses | |
@retval EFI_UNSUPPORTED Return this value when the BARs is not IO type | |
@retval EFI_SUCCESS Get the Base address successfully | |
@retval Other Read the pci configuration data error | |
**/ | |
EFI_STATUS | |
EFIAPI | |
GetIdeRegisterIoAddr ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN OUT EFI_IDE_REGISTERS *IdeRegisters | |
) | |
{ | |
EFI_STATUS Status; | |
PCI_TYPE00 PciData; | |
UINT16 CommandBlockBaseAddr; | |
UINT16 ControlBlockBaseAddr; | |
UINT16 BusMasterBaseAddr; | |
if ((PciIo == NULL) || (IdeRegisters == NULL)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
Status = PciIo->Pci.Read ( | |
PciIo, | |
EfiPciIoWidthUint8, | |
0, | |
sizeof (PciData), | |
&PciData | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
BusMasterBaseAddr = (UINT16)((PciData.Device.Bar[4] & 0x0000fff0)); | |
if ((PciData.Hdr.ClassCode[0] & IDE_PRIMARY_OPERATING_MODE) == 0) { | |
CommandBlockBaseAddr = 0x1f0; | |
ControlBlockBaseAddr = 0x3f6; | |
} else { | |
// | |
// The BARs should be of IO type | |
// | |
if (((PciData.Device.Bar[0] & BIT0) == 0) || | |
((PciData.Device.Bar[1] & BIT0) == 0)) | |
{ | |
return EFI_UNSUPPORTED; | |
} | |
CommandBlockBaseAddr = (UINT16)(PciData.Device.Bar[0] & 0x0000fff8); | |
ControlBlockBaseAddr = (UINT16)((PciData.Device.Bar[1] & 0x0000fffc) + 2); | |
} | |
// | |
// Calculate IDE primary channel I/O register base address. | |
// | |
IdeRegisters[EfiIdePrimary].Data = CommandBlockBaseAddr; | |
IdeRegisters[EfiIdePrimary].ErrOrFeature = (UINT16)(CommandBlockBaseAddr + 0x01); | |
IdeRegisters[EfiIdePrimary].SectorCount = (UINT16)(CommandBlockBaseAddr + 0x02); | |
IdeRegisters[EfiIdePrimary].SectorNumber = (UINT16)(CommandBlockBaseAddr + 0x03); | |
IdeRegisters[EfiIdePrimary].CylinderLsb = (UINT16)(CommandBlockBaseAddr + 0x04); | |
IdeRegisters[EfiIdePrimary].CylinderMsb = (UINT16)(CommandBlockBaseAddr + 0x05); | |
IdeRegisters[EfiIdePrimary].Head = (UINT16)(CommandBlockBaseAddr + 0x06); | |
IdeRegisters[EfiIdePrimary].CmdOrStatus = (UINT16)(CommandBlockBaseAddr + 0x07); | |
IdeRegisters[EfiIdePrimary].AltOrDev = ControlBlockBaseAddr; | |
IdeRegisters[EfiIdePrimary].BusMasterBaseAddr = BusMasterBaseAddr; | |
if ((PciData.Hdr.ClassCode[0] & IDE_SECONDARY_OPERATING_MODE) == 0) { | |
CommandBlockBaseAddr = 0x170; | |
ControlBlockBaseAddr = 0x376; | |
} else { | |
// | |
// The BARs should be of IO type | |
// | |
if (((PciData.Device.Bar[2] & BIT0) == 0) || | |
((PciData.Device.Bar[3] & BIT0) == 0)) | |
{ | |
return EFI_UNSUPPORTED; | |
} | |
CommandBlockBaseAddr = (UINT16)(PciData.Device.Bar[2] & 0x0000fff8); | |
ControlBlockBaseAddr = (UINT16)((PciData.Device.Bar[3] & 0x0000fffc) + 2); | |
} | |
// | |
// Calculate IDE secondary channel I/O register base address. | |
// | |
IdeRegisters[EfiIdeSecondary].Data = CommandBlockBaseAddr; | |
IdeRegisters[EfiIdeSecondary].ErrOrFeature = (UINT16)(CommandBlockBaseAddr + 0x01); | |
IdeRegisters[EfiIdeSecondary].SectorCount = (UINT16)(CommandBlockBaseAddr + 0x02); | |
IdeRegisters[EfiIdeSecondary].SectorNumber = (UINT16)(CommandBlockBaseAddr + 0x03); | |
IdeRegisters[EfiIdeSecondary].CylinderLsb = (UINT16)(CommandBlockBaseAddr + 0x04); | |
IdeRegisters[EfiIdeSecondary].CylinderMsb = (UINT16)(CommandBlockBaseAddr + 0x05); | |
IdeRegisters[EfiIdeSecondary].Head = (UINT16)(CommandBlockBaseAddr + 0x06); | |
IdeRegisters[EfiIdeSecondary].CmdOrStatus = (UINT16)(CommandBlockBaseAddr + 0x07); | |
IdeRegisters[EfiIdeSecondary].AltOrDev = ControlBlockBaseAddr; | |
IdeRegisters[EfiIdeSecondary].BusMasterBaseAddr = (UINT16)(BusMasterBaseAddr + 0x8); | |
return EFI_SUCCESS; | |
} | |
/** | |
Send ATA Ext command into device with NON_DATA protocol. | |
@param PciIo A pointer to ATA_ATAPI_PASS_THRU_INSTANCE data structure. | |
@param IdeRegisters A pointer to EFI_IDE_REGISTERS data structure. | |
@param AtaCommandBlock A pointer to EFI_ATA_COMMAND_BLOCK data structure. | |
@param Timeout The time to complete the command, uses 100ns as a unit. | |
@retval EFI_SUCCESS Reading succeed | |
@retval EFI_DEVICE_ERROR Error executing commands on this device. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
AtaIssueCommand ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN EFI_IDE_REGISTERS *IdeRegisters, | |
IN EFI_ATA_COMMAND_BLOCK *AtaCommandBlock, | |
IN UINT64 Timeout | |
) | |
{ | |
EFI_STATUS Status; | |
UINT8 DeviceHead; | |
UINT8 AtaCommand; | |
ASSERT (PciIo != NULL); | |
ASSERT (IdeRegisters != NULL); | |
ASSERT (AtaCommandBlock != NULL); | |
DeviceHead = AtaCommandBlock->AtaDeviceHead; | |
AtaCommand = AtaCommandBlock->AtaCommand; | |
Status = WaitForBSYClear (PciIo, IdeRegisters, Timeout); | |
if (EFI_ERROR (Status)) { | |
return EFI_DEVICE_ERROR; | |
} | |
// | |
// Select device (bit4), set LBA mode(bit6) (use 0xe0 for compatibility) | |
// | |
IdeWritePortB (PciIo, IdeRegisters->Head, (UINT8)(0xe0 | DeviceHead)); | |
// | |
// set all the command parameters | |
// Before write to all the following registers, BSY and DRQ must be 0. | |
// | |
Status = DRQClear2 (PciIo, IdeRegisters, Timeout); | |
if (EFI_ERROR (Status)) { | |
return EFI_DEVICE_ERROR; | |
} | |
// | |
// Fill the feature register, which is a two-byte FIFO. Need write twice. | |
// | |
IdeWritePortB (PciIo, IdeRegisters->ErrOrFeature, AtaCommandBlock->AtaFeaturesExp); | |
IdeWritePortB (PciIo, IdeRegisters->ErrOrFeature, AtaCommandBlock->AtaFeatures); | |
// | |
// Fill the sector count register, which is a two-byte FIFO. Need write twice. | |
// | |
IdeWritePortB (PciIo, IdeRegisters->SectorCount, AtaCommandBlock->AtaSectorCountExp); | |
IdeWritePortB (PciIo, IdeRegisters->SectorCount, AtaCommandBlock->AtaSectorCount); | |
// | |
// Fill the start LBA registers, which are also two-byte FIFO | |
// | |
IdeWritePortB (PciIo, IdeRegisters->SectorNumber, AtaCommandBlock->AtaSectorNumberExp); | |
IdeWritePortB (PciIo, IdeRegisters->SectorNumber, AtaCommandBlock->AtaSectorNumber); | |
IdeWritePortB (PciIo, IdeRegisters->CylinderLsb, AtaCommandBlock->AtaCylinderLowExp); | |
IdeWritePortB (PciIo, IdeRegisters->CylinderLsb, AtaCommandBlock->AtaCylinderLow); | |
IdeWritePortB (PciIo, IdeRegisters->CylinderMsb, AtaCommandBlock->AtaCylinderHighExp); | |
IdeWritePortB (PciIo, IdeRegisters->CylinderMsb, AtaCommandBlock->AtaCylinderHigh); | |
// | |
// Send command via Command Register | |
// | |
IdeWritePortB (PciIo, IdeRegisters->CmdOrStatus, AtaCommand); | |
// | |
// Stall at least 400 microseconds. | |
// | |
MicroSecondDelay (400); | |
return EFI_SUCCESS; | |
} | |
/** | |
This function is used to send out ATA commands conforms to the PIO Data In Protocol. | |
@param[in] PciIo A pointer to ATA_ATAPI_PASS_THRU_INSTANCE data | |
structure. | |
@param[in] IdeRegisters A pointer to EFI_IDE_REGISTERS data structure. | |
@param[in, out] Buffer A pointer to the source buffer for the data. | |
@param[in] ByteCount The length of the data. | |
@param[in] Read Flag used to determine the data transfer direction. | |
Read equals 1, means data transferred from device | |
to host;Read equals 0, means data transferred | |
from host to device. | |
@param[in] AtaCommandBlock A pointer to EFI_ATA_COMMAND_BLOCK data structure. | |
@param[in, out] AtaStatusBlock A pointer to EFI_ATA_STATUS_BLOCK data structure. | |
@param[in] Timeout The time to complete the command, uses 100ns as a unit. | |
@param[in] Task Optional. Pointer to the ATA_NONBLOCK_TASK | |
used by non-blocking mode. | |
@retval EFI_SUCCESS send out the ATA command and device send required data successfully. | |
@retval EFI_DEVICE_ERROR command sent failed. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
AtaPioDataInOut ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN EFI_IDE_REGISTERS *IdeRegisters, | |
IN OUT VOID *Buffer, | |
IN UINT64 ByteCount, | |
IN BOOLEAN Read, | |
IN EFI_ATA_COMMAND_BLOCK *AtaCommandBlock, | |
IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock, | |
IN UINT64 Timeout, | |
IN ATA_NONBLOCK_TASK *Task | |
) | |
{ | |
UINTN WordCount; | |
UINTN Increment; | |
UINT16 *Buffer16; | |
EFI_STATUS Status; | |
if ((PciIo == NULL) || (IdeRegisters == NULL) || (Buffer == NULL) || (AtaCommandBlock == NULL)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// Issue ATA command | |
// | |
Status = AtaIssueCommand (PciIo, IdeRegisters, AtaCommandBlock, Timeout); | |
if (EFI_ERROR (Status)) { | |
Status = EFI_DEVICE_ERROR; | |
goto Exit; | |
} | |
Buffer16 = (UINT16 *)Buffer; | |
// | |
// According to PIO data in protocol, host can perform a series of reads to | |
// the data register after each time device set DRQ ready; | |
// The data size of "a series of read" is command specific. | |
// For most ATA command, data size received from device will not exceed | |
// 1 sector, hence the data size for "a series of read" can be the whole data | |
// size of one command request. | |
// For ATA command such as Read Sector command, the data size of one ATA | |
// command request is often larger than 1 sector, according to the | |
// Read Sector command, the data size of "a series of read" is exactly 1 | |
// sector. | |
// Here for simplification reason, we specify the data size for | |
// "a series of read" to 1 sector (256 words) if data size of one ATA command | |
// request is larger than 256 words. | |
// | |
Increment = 256; | |
// | |
// used to record bytes of currently transferred data | |
// | |
WordCount = 0; | |
while (WordCount < RShiftU64 (ByteCount, 1)) { | |
// | |
// Poll DRQ bit set, data transfer can be performed only when DRQ is ready | |
// | |
Status = DRQReady2 (PciIo, IdeRegisters, Timeout); | |
if (EFI_ERROR (Status)) { | |
Status = EFI_DEVICE_ERROR; | |
goto Exit; | |
} | |
// | |
// Get the byte count for one series of read | |
// | |
if ((WordCount + Increment) > RShiftU64 (ByteCount, 1)) { | |
Increment = (UINTN)(RShiftU64 (ByteCount, 1) - WordCount); | |
} | |
if (Read) { | |
IdeReadPortWMultiple ( | |
PciIo, | |
IdeRegisters->Data, | |
Increment, | |
Buffer16 | |
); | |
} else { | |
IdeWritePortWMultiple ( | |
PciIo, | |
IdeRegisters->Data, | |
Increment, | |
Buffer16 | |
); | |
} | |
Status = CheckStatusRegister (PciIo, IdeRegisters); | |
if (EFI_ERROR (Status)) { | |
Status = EFI_DEVICE_ERROR; | |
goto Exit; | |
} | |
WordCount += Increment; | |
Buffer16 += Increment; | |
} | |
Status = DRQClear (PciIo, IdeRegisters, Timeout); | |
if (EFI_ERROR (Status)) { | |
Status = EFI_DEVICE_ERROR; | |
goto Exit; | |
} | |
Exit: | |
// | |
// Dump All Ide registers to ATA_STATUS_BLOCK | |
// | |
DumpAllIdeRegisters (PciIo, IdeRegisters, AtaStatusBlock); | |
// | |
// Not support the Non-blocking now,just do the blocking process. | |
// | |
return Status; | |
} | |
/** | |
Send ATA command into device with NON_DATA protocol | |
@param[in] PciIo A pointer to ATA_ATAPI_PASS_THRU_INSTANCE | |
data structure. | |
@param[in] IdeRegisters A pointer to EFI_IDE_REGISTERS data structure. | |
@param[in] AtaCommandBlock A pointer to EFI_ATA_COMMAND_BLOCK data | |
structure. | |
@param[in, out] AtaStatusBlock A pointer to EFI_ATA_STATUS_BLOCK data structure. | |
@param[in] Timeout The time to complete the command, uses 100ns as a unit. | |
@param[in] Task Optional. Pointer to the ATA_NONBLOCK_TASK | |
used by non-blocking mode. | |
@retval EFI_SUCCESS Reading succeed | |
@retval EFI_ABORTED Command failed | |
@retval EFI_DEVICE_ERROR Device status error. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
AtaNonDataCommandIn ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN EFI_IDE_REGISTERS *IdeRegisters, | |
IN EFI_ATA_COMMAND_BLOCK *AtaCommandBlock, | |
IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock, | |
IN UINT64 Timeout, | |
IN ATA_NONBLOCK_TASK *Task | |
) | |
{ | |
EFI_STATUS Status; | |
if ((PciIo == NULL) || (IdeRegisters == NULL) || (AtaCommandBlock == NULL)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// Issue ATA command | |
// | |
Status = AtaIssueCommand (PciIo, IdeRegisters, AtaCommandBlock, Timeout); | |
if (EFI_ERROR (Status)) { | |
Status = EFI_DEVICE_ERROR; | |
goto Exit; | |
} | |
// | |
// Wait for command completion | |
// | |
Status = WaitForBSYClear (PciIo, IdeRegisters, Timeout); | |
if (EFI_ERROR (Status)) { | |
Status = EFI_DEVICE_ERROR; | |
goto Exit; | |
} | |
Status = CheckStatusRegister (PciIo, IdeRegisters); | |
if (EFI_ERROR (Status)) { | |
Status = EFI_DEVICE_ERROR; | |
goto Exit; | |
} | |
Exit: | |
// | |
// Dump All Ide registers to ATA_STATUS_BLOCK | |
// | |
DumpAllIdeRegisters (PciIo, IdeRegisters, AtaStatusBlock); | |
// | |
// Not support the Non-blocking now,just do the blocking process. | |
// | |
return Status; | |
} | |
/** | |
Wait for memory to be set. | |
@param[in] PciIo The PCI IO protocol instance. | |
@param[in] IdeRegisters A pointer to EFI_IDE_REGISTERS data structure. | |
@param[in] Timeout The time to complete the command, uses 100ns as a unit. | |
@retval EFI_DEVICE_ERROR The memory is not set. | |
@retval EFI_TIMEOUT The memory setting is time out. | |
@retval EFI_SUCCESS The memory is correct set. | |
**/ | |
EFI_STATUS | |
AtaUdmStatusWait ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN EFI_IDE_REGISTERS *IdeRegisters, | |
IN UINT64 Timeout | |
) | |
{ | |
UINT8 RegisterValue; | |
EFI_STATUS Status; | |
UINT16 IoPortForBmis; | |
UINT64 Delay; | |
BOOLEAN InfiniteWait; | |
if (Timeout == 0) { | |
InfiniteWait = TRUE; | |
} else { | |
InfiniteWait = FALSE; | |
} | |
Delay = DivU64x32 (Timeout, 1000) + 1; | |
do { | |
Status = CheckStatusRegister (PciIo, IdeRegisters); | |
if (EFI_ERROR (Status)) { | |
Status = EFI_DEVICE_ERROR; | |
break; | |
} | |
IoPortForBmis = (UINT16)(IdeRegisters->BusMasterBaseAddr + BMIS_OFFSET); | |
RegisterValue = IdeReadPortB (PciIo, IoPortForBmis); | |
if (((RegisterValue & BMIS_ERROR) != 0) || (Timeout == 0)) { | |
DEBUG ((DEBUG_ERROR, "ATA UDMA operation fails\n")); | |
Status = EFI_DEVICE_ERROR; | |
break; | |
} | |
if ((RegisterValue & BMIS_INTERRUPT) != 0) { | |
Status = EFI_SUCCESS; | |
break; | |
} | |
// | |
// Stall for 100 microseconds. | |
// | |
MicroSecondDelay (100); | |
Delay--; | |
} while (InfiniteWait || (Delay > 0)); | |
return Status; | |
} | |
/** | |
Check if the memory to be set. | |
@param[in] PciIo The PCI IO protocol instance. | |
@param[in] Task Optional. Pointer to the ATA_NONBLOCK_TASK | |
used by non-blocking mode. | |
@param[in] IdeRegisters A pointer to EFI_IDE_REGISTERS data structure. | |
@retval EFI_DEVICE_ERROR The memory setting met a issue. | |
@retval EFI_NOT_READY The memory is not set. | |
@retval EFI_TIMEOUT The memory setting is time out. | |
@retval EFI_SUCCESS The memory is correct set. | |
**/ | |
EFI_STATUS | |
AtaUdmStatusCheck ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN ATA_NONBLOCK_TASK *Task, | |
IN EFI_IDE_REGISTERS *IdeRegisters | |
) | |
{ | |
UINT8 RegisterValue; | |
UINT16 IoPortForBmis; | |
EFI_STATUS Status; | |
Task->RetryTimes--; | |
Status = CheckStatusRegister (PciIo, IdeRegisters); | |
if (EFI_ERROR (Status)) { | |
return EFI_DEVICE_ERROR; | |
} | |
IoPortForBmis = (UINT16)(IdeRegisters->BusMasterBaseAddr + BMIS_OFFSET); | |
RegisterValue = IdeReadPortB (PciIo, IoPortForBmis); | |
if ((RegisterValue & BMIS_ERROR) != 0) { | |
DEBUG ((DEBUG_ERROR, "ATA UDMA operation fails\n")); | |
return EFI_DEVICE_ERROR; | |
} | |
if ((RegisterValue & BMIS_INTERRUPT) != 0) { | |
return EFI_SUCCESS; | |
} | |
if (!Task->InfiniteWait && (Task->RetryTimes == 0)) { | |
return EFI_TIMEOUT; | |
} else { | |
// | |
// The memory is not set. | |
// | |
return EFI_NOT_READY; | |
} | |
} | |
/** | |
Perform an ATA Udma operation (Read, ReadExt, Write, WriteExt). | |
@param[in] Instance A pointer to ATA_ATAPI_PASS_THRU_INSTANCE data | |
structure. | |
@param[in] IdeRegisters A pointer to EFI_IDE_REGISTERS data structure. | |
@param[in] Read Flag used to determine the data transfer | |
direction. Read equals 1, means data transferred | |
from device to host;Read equals 0, means data | |
transferred from host to device. | |
@param[in] DataBuffer A pointer to the source buffer for the data. | |
@param[in] DataLength The length of the data. | |
@param[in] AtaCommandBlock A pointer to EFI_ATA_COMMAND_BLOCK data structure. | |
@param[in, out] AtaStatusBlock A pointer to EFI_ATA_STATUS_BLOCK data structure. | |
@param[in] Timeout The time to complete the command, uses 100ns as a unit. | |
@param[in] Task Optional. Pointer to the ATA_NONBLOCK_TASK | |
used by non-blocking mode. | |
@retval EFI_SUCCESS the operation is successful. | |
@retval EFI_OUT_OF_RESOURCES Build PRD table failed | |
@retval EFI_UNSUPPORTED Unknown channel or operations command | |
@retval EFI_DEVICE_ERROR Ata command execute failed | |
**/ | |
EFI_STATUS | |
EFIAPI | |
AtaUdmaInOut ( | |
IN ATA_ATAPI_PASS_THRU_INSTANCE *Instance, | |
IN EFI_IDE_REGISTERS *IdeRegisters, | |
IN BOOLEAN Read, | |
IN VOID *DataBuffer, | |
IN UINT64 DataLength, | |
IN EFI_ATA_COMMAND_BLOCK *AtaCommandBlock, | |
IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock, | |
IN UINT64 Timeout, | |
IN ATA_NONBLOCK_TASK *Task | |
) | |
{ | |
EFI_STATUS Status; | |
UINT16 IoPortForBmic; | |
UINT16 IoPortForBmis; | |
UINT16 IoPortForBmid; | |
UINTN PrdTableSize; | |
EFI_PHYSICAL_ADDRESS PrdTableMapAddr; | |
VOID *PrdTableMap; | |
EFI_PHYSICAL_ADDRESS PrdTableBaseAddr; | |
EFI_ATA_DMA_PRD *TempPrdBaseAddr; | |
UINTN PrdTableNum; | |
UINT8 RegisterValue; | |
UINTN PageCount; | |
UINTN ByteCount; | |
UINTN ByteRemaining; | |
UINT8 DeviceControl; | |
VOID *BufferMap; | |
EFI_PHYSICAL_ADDRESS BufferMapAddress; | |
EFI_PCI_IO_PROTOCOL_OPERATION PciIoOperation; | |
UINT8 DeviceHead; | |
EFI_PCI_IO_PROTOCOL *PciIo; | |
EFI_TPL OldTpl; | |
UINTN AlignmentMask; | |
UINTN RealPageCount; | |
EFI_PHYSICAL_ADDRESS BaseAddr; | |
EFI_PHYSICAL_ADDRESS BaseMapAddr; | |
Status = EFI_SUCCESS; | |
PrdTableMap = NULL; | |
BufferMap = NULL; | |
PageCount = 0; | |
RealPageCount = 0; | |
BaseAddr = 0; | |
PciIo = Instance->PciIo; | |
if ((PciIo == NULL) || (IdeRegisters == NULL) || (DataBuffer == NULL) || (AtaCommandBlock == NULL)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// Before starting the Blocking BlockIO operation, push to finish all non-blocking | |
// BlockIO tasks. | |
// Delay 1ms to simulate the blocking time out checking. | |
// | |
OldTpl = gBS->RaiseTPL (TPL_NOTIFY); | |
while ((Task == NULL) && (!IsListEmpty (&Instance->NonBlockingTaskList))) { | |
AsyncNonBlockingTransferRoutine (NULL, Instance); | |
// | |
// Stall for 1 milliseconds. | |
// | |
MicroSecondDelay (1000); | |
} | |
gBS->RestoreTPL (OldTpl); | |
// | |
// The data buffer should be even alignment | |
// | |
if (((UINTN)DataBuffer & 0x1) != 0) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// Set relevant IO Port address. | |
// | |
IoPortForBmic = (UINT16)(IdeRegisters->BusMasterBaseAddr + BMIC_OFFSET); | |
IoPortForBmis = (UINT16)(IdeRegisters->BusMasterBaseAddr + BMIS_OFFSET); | |
IoPortForBmid = (UINT16)(IdeRegisters->BusMasterBaseAddr + BMID_OFFSET); | |
// | |
// For Blocking mode, start the command. | |
// For non-blocking mode, when the command is not started, start it, otherwise | |
// go to check the status. | |
// | |
if (((Task != NULL) && (!Task->IsStart)) || (Task == NULL)) { | |
// | |
// Calculate the number of PRD entry. | |
// Every entry in PRD table can specify a 64K memory region. | |
// | |
PrdTableNum = (UINTN)(RShiftU64 (DataLength, 16) + 1); | |
// | |
// Make sure that the memory region of PRD table is not cross 64K boundary | |
// | |
PrdTableSize = PrdTableNum * sizeof (EFI_ATA_DMA_PRD); | |
if (PrdTableSize > 0x10000) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// Allocate buffer for PRD table initialization. | |
// Note Ide Bus Master spec said the descriptor table must be aligned on a 4 byte | |
// boundary and the table cannot cross a 64K boundary in memory. | |
// | |
PageCount = EFI_SIZE_TO_PAGES (PrdTableSize); | |
RealPageCount = PageCount + EFI_SIZE_TO_PAGES (SIZE_64KB); | |
// | |
// Make sure that PageCount plus EFI_SIZE_TO_PAGES (SIZE_64KB) does not overflow. | |
// | |
ASSERT (RealPageCount > PageCount); | |
Status = PciIo->AllocateBuffer ( | |
PciIo, | |
AllocateAnyPages, | |
EfiBootServicesData, | |
RealPageCount, | |
(VOID **)&BaseAddr, | |
0 | |
); | |
if (EFI_ERROR (Status)) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
ByteCount = EFI_PAGES_TO_SIZE (RealPageCount); | |
Status = PciIo->Map ( | |
PciIo, | |
EfiPciIoOperationBusMasterCommonBuffer, | |
(VOID *)(UINTN)BaseAddr, | |
&ByteCount, | |
&BaseMapAddr, | |
&PrdTableMap | |
); | |
if (EFI_ERROR (Status) || (ByteCount != EFI_PAGES_TO_SIZE (RealPageCount))) { | |
// | |
// If the data length actually mapped is not equal to the requested amount, | |
// it means the DMA operation may be broken into several discontinuous smaller chunks. | |
// Can't handle this case. | |
// | |
PciIo->FreeBuffer (PciIo, RealPageCount, (VOID *)(UINTN)BaseAddr); | |
return EFI_OUT_OF_RESOURCES; | |
} | |
ZeroMem ((VOID *)((UINTN)BaseAddr), ByteCount); | |
// | |
// Calculate the 64K align address as PRD Table base address. | |
// | |
AlignmentMask = SIZE_64KB - 1; | |
PrdTableBaseAddr = ((UINTN)BaseAddr + AlignmentMask) & ~AlignmentMask; | |
PrdTableMapAddr = ((UINTN)BaseMapAddr + AlignmentMask) & ~AlignmentMask; | |
// | |
// Map the host address of DataBuffer to DMA master address. | |
// | |
if (Read) { | |
PciIoOperation = EfiPciIoOperationBusMasterWrite; | |
} else { | |
PciIoOperation = EfiPciIoOperationBusMasterRead; | |
} | |
ByteCount = (UINTN)DataLength; | |
Status = PciIo->Map ( | |
PciIo, | |
PciIoOperation, | |
DataBuffer, | |
&ByteCount, | |
&BufferMapAddress, | |
&BufferMap | |
); | |
if (EFI_ERROR (Status) || (ByteCount != DataLength)) { | |
PciIo->Unmap (PciIo, PrdTableMap); | |
PciIo->FreeBuffer (PciIo, RealPageCount, (VOID *)(UINTN)BaseAddr); | |
return EFI_OUT_OF_RESOURCES; | |
} | |
// | |
// According to Ata spec, it requires the buffer address and size to be even. | |
// | |
ASSERT ((BufferMapAddress & 0x1) == 0); | |
ASSERT ((ByteCount & 0x1) == 0); | |
// | |
// Fill the PRD table with appropriate bus master address of data buffer and data length. | |
// | |
ByteRemaining = ByteCount; | |
TempPrdBaseAddr = (EFI_ATA_DMA_PRD *)(UINTN)PrdTableBaseAddr; | |
while (ByteRemaining != 0) { | |
if (ByteRemaining <= 0x10000) { | |
TempPrdBaseAddr->RegionBaseAddr = (UINT32)((UINTN)BufferMapAddress); | |
TempPrdBaseAddr->ByteCount = (UINT16)ByteRemaining; | |
TempPrdBaseAddr->EndOfTable = 0x8000; | |
break; | |
} | |
TempPrdBaseAddr->RegionBaseAddr = (UINT32)((UINTN)BufferMapAddress); | |
TempPrdBaseAddr->ByteCount = (UINT16)0x0; | |
ByteRemaining -= 0x10000; | |
BufferMapAddress += 0x10000; | |
TempPrdBaseAddr++; | |
} | |
// | |
// Start to enable the DMA operation | |
// | |
DeviceHead = AtaCommandBlock->AtaDeviceHead; | |
IdeWritePortB (PciIo, IdeRegisters->Head, (UINT8)(0xe0 | DeviceHead)); | |
// | |
// Enable interrupt to support UDMA | |
// | |
DeviceControl = 0; | |
IdeWritePortB (PciIo, IdeRegisters->AltOrDev, DeviceControl); | |
// | |
// Read BMIS register and clear ERROR and INTR bit | |
// | |
RegisterValue = IdeReadPortB (PciIo, IoPortForBmis); | |
RegisterValue |= (BMIS_INTERRUPT | BMIS_ERROR); | |
IdeWritePortB (PciIo, IoPortForBmis, RegisterValue); | |
// | |
// Set the base address to BMID register | |
// | |
IdeWritePortDW (PciIo, IoPortForBmid, (UINT32)PrdTableMapAddr); | |
// | |
// Set BMIC register to identify the operation direction | |
// | |
RegisterValue = IdeReadPortB (PciIo, IoPortForBmic); | |
if (Read) { | |
RegisterValue |= BMIC_NREAD; | |
} else { | |
RegisterValue &= ~((UINT8)BMIC_NREAD); | |
} | |
IdeWritePortB (PciIo, IoPortForBmic, RegisterValue); | |
if (Task != NULL) { | |
Task->Map = BufferMap; | |
Task->TableMap = PrdTableMap; | |
Task->MapBaseAddress = (EFI_ATA_DMA_PRD *)(UINTN)BaseAddr; | |
Task->PageCount = RealPageCount; | |
Task->IsStart = TRUE; | |
} | |
// | |
// Issue ATA command | |
// | |
Status = AtaIssueCommand (PciIo, IdeRegisters, AtaCommandBlock, Timeout); | |
if (EFI_ERROR (Status)) { | |
Status = EFI_DEVICE_ERROR; | |
goto Exit; | |
} | |
Status = CheckStatusRegister (PciIo, IdeRegisters); | |
if (EFI_ERROR (Status)) { | |
Status = EFI_DEVICE_ERROR; | |
goto Exit; | |
} | |
// | |
// Set START bit of BMIC register | |
// | |
RegisterValue = IdeReadPortB (PciIo, IoPortForBmic); | |
RegisterValue |= BMIC_START; | |
IdeWritePortB (PciIo, IoPortForBmic, RegisterValue); | |
} | |
// | |
// Check the INTERRUPT and ERROR bit of BMIS | |
// | |
if (Task != NULL) { | |
Status = AtaUdmStatusCheck (PciIo, Task, IdeRegisters); | |
} else { | |
Status = AtaUdmStatusWait (PciIo, IdeRegisters, Timeout); | |
} | |
// | |
// For blocking mode, clear registers and free buffers. | |
// For non blocking mode, when the related registers have been set or time | |
// out, or a error has been happened, it needs to clear the register and free | |
// buffer. | |
// | |
if ((Task == NULL) || (Status != EFI_NOT_READY)) { | |
// | |
// Read BMIS register and clear ERROR and INTR bit | |
// | |
RegisterValue = IdeReadPortB (PciIo, IoPortForBmis); | |
RegisterValue |= (BMIS_INTERRUPT | BMIS_ERROR); | |
IdeWritePortB (PciIo, IoPortForBmis, RegisterValue); | |
// | |
// Read Status Register of IDE device to clear interrupt | |
// | |
RegisterValue = IdeReadPortB (PciIo, IdeRegisters->CmdOrStatus); | |
// | |
// Clear START bit of BMIC register | |
// | |
RegisterValue = IdeReadPortB (PciIo, IoPortForBmic); | |
RegisterValue &= ~((UINT8)BMIC_START); | |
IdeWritePortB (PciIo, IoPortForBmic, RegisterValue); | |
// | |
// Disable interrupt of Select device | |
// | |
DeviceControl = IdeReadPortB (PciIo, IdeRegisters->AltOrDev); | |
DeviceControl |= ATA_CTLREG_IEN_L; | |
IdeWritePortB (PciIo, IdeRegisters->AltOrDev, DeviceControl); | |
// | |
// Stall for 10 milliseconds. | |
// | |
MicroSecondDelay (10000); | |
} | |
Exit: | |
// | |
// Free all allocated resource | |
// | |
if ((Task == NULL) || (Status != EFI_NOT_READY)) { | |
if (Task != NULL) { | |
PciIo->Unmap (PciIo, Task->TableMap); | |
PciIo->FreeBuffer (PciIo, Task->PageCount, Task->MapBaseAddress); | |
PciIo->Unmap (PciIo, Task->Map); | |
} else { | |
PciIo->Unmap (PciIo, PrdTableMap); | |
PciIo->FreeBuffer (PciIo, RealPageCount, (VOID *)(UINTN)BaseAddr); | |
PciIo->Unmap (PciIo, BufferMap); | |
} | |
// | |
// Dump All Ide registers to ATA_STATUS_BLOCK | |
// | |
DumpAllIdeRegisters (PciIo, IdeRegisters, AtaStatusBlock); | |
} | |
return Status; | |
} | |
/** | |
This function reads the pending data in the device. | |
@param PciIo A pointer to ATA_ATAPI_PASS_THRU_INSTANCE data structure. | |
@param IdeRegisters A pointer to EFI_IDE_REGISTERS data structure. | |
@retval EFI_SUCCESS Successfully read. | |
@retval EFI_NOT_READY The BSY is set avoiding reading. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
AtaPacketReadPendingData ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN EFI_IDE_REGISTERS *IdeRegisters | |
) | |
{ | |
UINT8 AltRegister; | |
UINT16 TempWordBuffer; | |
AltRegister = IdeReadPortB (PciIo, IdeRegisters->AltOrDev); | |
if ((AltRegister & ATA_STSREG_BSY) == ATA_STSREG_BSY) { | |
return EFI_NOT_READY; | |
} | |
if ((AltRegister & (ATA_STSREG_BSY | ATA_STSREG_DRQ)) == ATA_STSREG_DRQ) { | |
TempWordBuffer = IdeReadPortB (PciIo, IdeRegisters->AltOrDev); | |
while ((TempWordBuffer & (ATA_STSREG_BSY | ATA_STSREG_DRQ)) == ATA_STSREG_DRQ) { | |
IdeReadPortWMultiple ( | |
PciIo, | |
IdeRegisters->Data, | |
1, | |
&TempWordBuffer | |
); | |
TempWordBuffer = IdeReadPortB (PciIo, IdeRegisters->AltOrDev); | |
} | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
This function is called by AtaPacketCommandExecute(). | |
It is used to transfer data between host and device. The data direction is specified | |
by the fourth parameter. | |
@param PciIo A pointer to ATA_ATAPI_PASS_THRU_INSTANCE data structure. | |
@param IdeRegisters A pointer to EFI_IDE_REGISTERS data structure. | |
@param Buffer Buffer contained data transferred between host and device. | |
@param ByteCount Data size in byte unit of the buffer. | |
@param Read Flag used to determine the data transfer direction. | |
Read equals 1, means data transferred from device to host; | |
Read equals 0, means data transferred from host to device. | |
@param Timeout Timeout value for wait DRQ ready before each data stream's transfer | |
, uses 100ns as a unit. | |
@retval EFI_SUCCESS data is transferred successfully. | |
@retval EFI_DEVICE_ERROR the device failed to transfer data. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
AtaPacketReadWrite ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN EFI_IDE_REGISTERS *IdeRegisters, | |
IN OUT VOID *Buffer, | |
IN OUT UINT32 *ByteCount, | |
IN BOOLEAN Read, | |
IN UINT64 Timeout | |
) | |
{ | |
UINT32 RequiredWordCount; | |
UINT32 ActualWordCount; | |
UINT32 WordCount; | |
EFI_STATUS Status; | |
UINT16 *PtrBuffer; | |
PtrBuffer = Buffer; | |
RequiredWordCount = *ByteCount >> 1; | |
// | |
// No data transfer is permitted. | |
// | |
if (RequiredWordCount == 0) { | |
return EFI_SUCCESS; | |
} | |
// | |
// ActualWordCount means the word count of data really transferred. | |
// | |
ActualWordCount = 0; | |
while (ActualWordCount < RequiredWordCount) { | |
// | |
// before each data transfer stream, the host should poll DRQ bit ready, | |
// to see whether indicates device is ready to transfer data. | |
// | |
Status = DRQReady2 (PciIo, IdeRegisters, Timeout); | |
if (EFI_ERROR (Status)) { | |
if (Status == EFI_NOT_READY) { | |
// | |
// Device provided less data than we intended to read, or wanted less | |
// data than we intended to write, but it may still be successful. | |
// | |
break; | |
} else { | |
return Status; | |
} | |
} | |
// | |
// get current data transfer size from Cylinder Registers. | |
// | |
WordCount = IdeReadPortB (PciIo, IdeRegisters->CylinderMsb) << 8; | |
WordCount = WordCount | IdeReadPortB (PciIo, IdeRegisters->CylinderLsb); | |
WordCount = WordCount & 0xffff; | |
WordCount /= 2; | |
WordCount = MIN (WordCount, (RequiredWordCount - ActualWordCount)); | |
if (Read) { | |
IdeReadPortWMultiple ( | |
PciIo, | |
IdeRegisters->Data, | |
WordCount, | |
PtrBuffer | |
); | |
} else { | |
IdeWritePortWMultiple ( | |
PciIo, | |
IdeRegisters->Data, | |
WordCount, | |
PtrBuffer | |
); | |
} | |
// | |
// read status register to check whether error happens. | |
// | |
Status = CheckStatusRegister (PciIo, IdeRegisters); | |
if (EFI_ERROR (Status)) { | |
return EFI_DEVICE_ERROR; | |
} | |
PtrBuffer += WordCount; | |
ActualWordCount += WordCount; | |
} | |
if (Read) { | |
// | |
// In the case where the drive wants to send more data than we need to read, | |
// the DRQ bit will be set and cause delays from DRQClear2(). | |
// We need to read data from the drive until it clears DRQ so we can move on. | |
// | |
AtaPacketReadPendingData (PciIo, IdeRegisters); | |
} | |
// | |
// read status register to check whether error happens. | |
// | |
Status = CheckStatusRegister (PciIo, IdeRegisters); | |
if (EFI_ERROR (Status)) { | |
return EFI_DEVICE_ERROR; | |
} | |
// | |
// After data transfer is completed, normally, DRQ bit should clear. | |
// | |
Status = DRQClear (PciIo, IdeRegisters, Timeout); | |
if (EFI_ERROR (Status)) { | |
return EFI_DEVICE_ERROR; | |
} | |
*ByteCount = ActualWordCount << 1; | |
return Status; | |
} | |
/** | |
This function is used to send out ATAPI commands conforms to the Packet Command | |
with PIO Data In Protocol. | |
@param[in] PciIo Pointer to the EFI_PCI_IO_PROTOCOL instance | |
@param[in] IdeRegisters Pointer to EFI_IDE_REGISTERS which is used to | |
store the IDE i/o port registers' base addresses | |
@param[in] Channel The channel number of device. | |
@param[in] Device The device number of device. | |
@param[in] Packet A pointer to EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET data 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 | |
AtaPacketCommandExecute ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN EFI_IDE_REGISTERS *IdeRegisters, | |
IN UINT8 Channel, | |
IN UINT8 Device, | |
IN EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet | |
) | |
{ | |
EFI_ATA_COMMAND_BLOCK AtaCommandBlock; | |
EFI_STATUS Status; | |
UINT8 Count; | |
UINT8 PacketCommand[12]; | |
ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK)); | |
// | |
// Fill ATAPI Command Packet according to CDB. | |
// For Atapi cmd, its length should be less than or equal to 12 bytes. | |
// | |
if (Packet->CdbLength > 12) { | |
return EFI_INVALID_PARAMETER; | |
} | |
ZeroMem (PacketCommand, 12); | |
CopyMem (PacketCommand, Packet->Cdb, Packet->CdbLength); | |
// | |
// 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); | |
AtaCommandBlock.AtaDeviceHead = (UINT8)(Device << 0x4); | |
AtaCommandBlock.AtaCommand = ATA_CMD_PACKET; | |
IdeWritePortB (PciIo, IdeRegisters->Head, (UINT8)(0xe0 | (Device << 0x4))); | |
// | |
// Disable interrupt | |
// | |
IdeWritePortB (PciIo, IdeRegisters->AltOrDev, ATA_DEFAULT_CTL); | |
// | |
// Issue ATA PACKET command firstly | |
// | |
Status = AtaIssueCommand (PciIo, IdeRegisters, &AtaCommandBlock, Packet->Timeout); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Status = DRQReady (PciIo, IdeRegisters, Packet->Timeout); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
// | |
// Send out ATAPI command packet | |
// | |
for (Count = 0; Count < 6; Count++) { | |
IdeWritePortW (PciIo, IdeRegisters->Data, *((UINT16 *)PacketCommand + Count)); | |
// | |
// Stall for 10 microseconds. | |
// | |
MicroSecondDelay (10); | |
} | |
// | |
// Read/Write the data of ATAPI Command | |
// | |
if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) { | |
Status = AtaPacketReadWrite ( | |
PciIo, | |
IdeRegisters, | |
Packet->InDataBuffer, | |
&Packet->InTransferLength, | |
TRUE, | |
Packet->Timeout | |
); | |
} else { | |
Status = AtaPacketReadWrite ( | |
PciIo, | |
IdeRegisters, | |
Packet->OutDataBuffer, | |
&Packet->OutTransferLength, | |
FALSE, | |
Packet->Timeout | |
); | |
} | |
return Status; | |
} | |
/** | |
Set the calculated Best transfer mode to a detected device. | |
@param Instance A pointer to ATA_ATAPI_PASS_THRU_INSTANCE data structure. | |
@param Channel The channel number of device. | |
@param Device The device number of device. | |
@param TransferMode A pointer to EFI_ATA_TRANSFER_MODE data structure. | |
@param AtaStatusBlock A pointer to EFI_ATA_STATUS_BLOCK data structure. | |
@retval EFI_SUCCESS Set transfer mode successfully. | |
@retval EFI_DEVICE_ERROR Set transfer mode failed. | |
@retval EFI_OUT_OF_RESOURCES Allocate memory failed. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
SetDeviceTransferMode ( | |
IN ATA_ATAPI_PASS_THRU_INSTANCE *Instance, | |
IN UINT8 Channel, | |
IN UINT8 Device, | |
IN EFI_ATA_TRANSFER_MODE *TransferMode, | |
IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_ATA_COMMAND_BLOCK AtaCommandBlock; | |
ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK)); | |
AtaCommandBlock.AtaCommand = ATA_CMD_SET_FEATURES; | |
AtaCommandBlock.AtaDeviceHead = (UINT8)(Device << 0x4); | |
AtaCommandBlock.AtaFeatures = 0x03; | |
AtaCommandBlock.AtaSectorCount = *((UINT8 *)TransferMode); | |
// | |
// Send SET FEATURE command (sub command 0x03) to set pio mode. | |
// | |
Status = AtaNonDataCommandIn ( | |
Instance->PciIo, | |
&Instance->IdeRegisters[Channel], | |
&AtaCommandBlock, | |
AtaStatusBlock, | |
ATA_ATAPI_TIMEOUT, | |
NULL | |
); | |
return Status; | |
} | |
/** | |
Set drive parameters for devices not support PACKETS command. | |
@param Instance A pointer to ATA_ATAPI_PASS_THRU_INSTANCE data structure. | |
@param Channel The channel number of device. | |
@param Device The device number of device. | |
@param DriveParameters A pointer to EFI_ATA_DRIVE_PARMS data structure. | |
@param AtaStatusBlock A pointer to EFI_ATA_STATUS_BLOCK data structure. | |
@retval EFI_SUCCESS Set drive parameter successfully. | |
@retval EFI_DEVICE_ERROR Set drive parameter failed. | |
@retval EFI_OUT_OF_RESOURCES Allocate memory failed. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
SetDriveParameters ( | |
IN ATA_ATAPI_PASS_THRU_INSTANCE *Instance, | |
IN UINT8 Channel, | |
IN UINT8 Device, | |
IN EFI_ATA_DRIVE_PARMS *DriveParameters, | |
IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_ATA_COMMAND_BLOCK AtaCommandBlock; | |
ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK)); | |
AtaCommandBlock.AtaCommand = ATA_CMD_INIT_DRIVE_PARAM; | |
AtaCommandBlock.AtaSectorCount = DriveParameters->Sector; | |
AtaCommandBlock.AtaDeviceHead = (UINT8)((Device << 0x4) + DriveParameters->Heads); | |
// | |
// Send Init drive parameters | |
// | |
Status = AtaNonDataCommandIn ( | |
Instance->PciIo, | |
&Instance->IdeRegisters[Channel], | |
&AtaCommandBlock, | |
AtaStatusBlock, | |
ATA_ATAPI_TIMEOUT, | |
NULL | |
); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_WARN, "Init Drive Parameters Fail, Status = %r\n", Status)); | |
} | |
// | |
// Send Set Multiple parameters | |
// | |
AtaCommandBlock.AtaCommand = ATA_CMD_SET_MULTIPLE_MODE; | |
AtaCommandBlock.AtaSectorCount = DriveParameters->MultipleSector; | |
AtaCommandBlock.AtaDeviceHead = (UINT8)(Device << 0x4); | |
Status = AtaNonDataCommandIn ( | |
Instance->PciIo, | |
&Instance->IdeRegisters[Channel], | |
&AtaCommandBlock, | |
AtaStatusBlock, | |
ATA_ATAPI_TIMEOUT, | |
NULL | |
); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_WARN, "Set Multiple Mode Parameters Fail, Status = %r\n", Status)); | |
} | |
return Status; | |
} | |
/** | |
Send SMART Return Status command to check if the execution of SMART cmd is successful or not. | |
@param Instance A pointer to ATA_ATAPI_PASS_THRU_INSTANCE data structure. | |
@param Channel The channel number of device. | |
@param Device The device number of device. | |
@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 | |
IdeAtaSmartReturnStatusCheck ( | |
IN ATA_ATAPI_PASS_THRU_INSTANCE *Instance, | |
IN UINT8 Channel, | |
IN UINT8 Device, | |
IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_ATA_COMMAND_BLOCK AtaCommandBlock; | |
UINT8 LBAMid; | |
UINT8 LBAHigh; | |
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; | |
AtaCommandBlock.AtaDeviceHead = (UINT8)((Device << 0x4) | 0xe0); | |
// | |
// Send S.M.A.R.T Read Return Status command to device | |
// | |
Status = AtaNonDataCommandIn ( | |
Instance->PciIo, | |
&Instance->IdeRegisters[Channel], | |
&AtaCommandBlock, | |
AtaStatusBlock, | |
ATA_ATAPI_TIMEOUT, | |
NULL | |
); | |
if (EFI_ERROR (Status)) { | |
REPORT_STATUS_CODE ( | |
EFI_ERROR_CODE | EFI_ERROR_MINOR, | |
(EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_DISABLED) | |
); | |
return EFI_DEVICE_ERROR; | |
} | |
REPORT_STATUS_CODE ( | |
EFI_PROGRESS_CODE, | |
(EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_ENABLE) | |
); | |
LBAMid = IdeReadPortB (Instance->PciIo, Instance->IdeRegisters[Channel].CylinderLsb); | |
LBAHigh = IdeReadPortB (Instance->PciIo, Instance->IdeRegisters[Channel].CylinderMsb); | |
if ((LBAMid == 0x4f) && (LBAHigh == 0xc2)) { | |
// | |
// The threshold exceeded condition is not detected by the device | |
// | |
DEBUG ((DEBUG_INFO, "The S.M.A.R.T threshold exceeded condition is not detected\n")); | |
REPORT_STATUS_CODE ( | |
EFI_PROGRESS_CODE, | |
(EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_UNDERTHRESHOLD) | |
); | |
} else if ((LBAMid == 0xf4) && (LBAHigh == 0x2c)) { | |
// | |
// The threshold exceeded condition is detected by the device | |
// | |
DEBUG ((DEBUG_INFO, "The S.M.A.R.T threshold exceeded condition is detected\n")); | |
REPORT_STATUS_CODE ( | |
EFI_PROGRESS_CODE, | |
(EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_OVERTHRESHOLD) | |
); | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Enable SMART command of the disk if supported. | |
@param Instance A pointer to ATA_ATAPI_PASS_THRU_INSTANCE data structure. | |
@param Channel The channel number of device. | |
@param Device The device number of device. | |
@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 | |
IdeAtaSmartSupport ( | |
IN ATA_ATAPI_PASS_THRU_INSTANCE *Instance, | |
IN UINT8 Channel, | |
IN UINT8 Device, | |
IN EFI_IDENTIFY_DATA *IdentifyData, | |
IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_ATA_COMMAND_BLOCK AtaCommandBlock; | |
// | |
// Detect if the device supports S.M.A.R.T. | |
// | |
if ((IdentifyData->AtaData.command_set_supported_82 & 0x0001) != 0x0001) { | |
// | |
// S.M.A.R.T is not supported by the device | |
// | |
DEBUG (( | |
DEBUG_INFO, | |
"S.M.A.R.T feature is not supported at [%a] channel [%a] device!\n", | |
(Channel == 1) ? "secondary" : "primary", | |
(Device == 1) ? "slave" : "master" | |
)); | |
REPORT_STATUS_CODE ( | |
EFI_ERROR_CODE | EFI_ERROR_MINOR, | |
(EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_NOTSUPPORTED) | |
); | |
} else { | |
// | |
// Check if the feature is enabled. If not, then enable S.M.A.R.T. | |
// | |
if ((IdentifyData->AtaData.command_set_feature_enb_85 & 0x0001) != 0x0001) { | |
REPORT_STATUS_CODE ( | |
EFI_PROGRESS_CODE, | |
(EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_DISABLE) | |
); | |
ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK)); | |
AtaCommandBlock.AtaCommand = ATA_CMD_SMART; | |
AtaCommandBlock.AtaFeatures = ATA_SMART_ENABLE_OPERATION; | |
AtaCommandBlock.AtaCylinderLow = ATA_CONSTANT_4F; | |
AtaCommandBlock.AtaCylinderHigh = ATA_CONSTANT_C2; | |
AtaCommandBlock.AtaDeviceHead = (UINT8)((Device << 0x4) | 0xe0); | |
// | |
// Send S.M.A.R.T Enable command to device | |
// | |
Status = AtaNonDataCommandIn ( | |
Instance->PciIo, | |
&Instance->IdeRegisters[Channel], | |
&AtaCommandBlock, | |
AtaStatusBlock, | |
ATA_ATAPI_TIMEOUT, | |
NULL | |
); | |
if (!EFI_ERROR (Status)) { | |
// | |
// Send S.M.A.R.T AutoSave command to device | |
// | |
ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK)); | |
AtaCommandBlock.AtaCommand = ATA_CMD_SMART; | |
AtaCommandBlock.AtaFeatures = 0xD2; | |
AtaCommandBlock.AtaSectorCount = 0xF1; | |
AtaCommandBlock.AtaCylinderLow = ATA_CONSTANT_4F; | |
AtaCommandBlock.AtaCylinderHigh = ATA_CONSTANT_C2; | |
AtaCommandBlock.AtaDeviceHead = (UINT8)((Device << 0x4) | 0xe0); | |
Status = AtaNonDataCommandIn ( | |
Instance->PciIo, | |
&Instance->IdeRegisters[Channel], | |
&AtaCommandBlock, | |
AtaStatusBlock, | |
ATA_ATAPI_TIMEOUT, | |
NULL | |
); | |
if (!EFI_ERROR (Status)) { | |
Status = IdeAtaSmartReturnStatusCheck ( | |
Instance, | |
Channel, | |
Device, | |
AtaStatusBlock | |
); | |
} | |
} | |
} | |
DEBUG (( | |
DEBUG_INFO, | |
"Enabled S.M.A.R.T feature at [%a] channel [%a] device!\n", | |
(Channel == 1) ? "secondary" : "primary", | |
(Device == 1) ? "slave" : "master" | |
)); | |
} | |
return; | |
} | |
/** | |
Sends out an ATA Identify Command to the specified device. | |
This function is called by DiscoverIdeDevice() during its device | |
identification. It sends out the ATA Identify Command to the | |
specified device. Only ATA device responses to this command. If | |
the command succeeds, it returns the Identify data structure which | |
contains information about the device. This function extracts the | |
information it needs to fill the IDE_BLK_IO_DEV data structure, | |
including device type, media block size, media capacity, and etc. | |
@param Instance A pointer to ATA_ATAPI_PASS_THRU_INSTANCE data structure. | |
@param Channel The channel number of device. | |
@param Device The device number of device. | |
@param Buffer A pointer to data buffer which is used to contain IDENTIFY data. | |
@param AtaStatusBlock A pointer to EFI_ATA_STATUS_BLOCK data structure. | |
@retval EFI_SUCCESS Identify ATA device successfully. | |
@retval EFI_DEVICE_ERROR ATA Identify Device Command failed or device is not ATA device. | |
@retval EFI_OUT_OF_RESOURCES Allocate memory failed. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
AtaIdentify ( | |
IN ATA_ATAPI_PASS_THRU_INSTANCE *Instance, | |
IN UINT8 Channel, | |
IN UINT8 Device, | |
IN OUT EFI_IDENTIFY_DATA *Buffer, | |
IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_ATA_COMMAND_BLOCK AtaCommandBlock; | |
ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK)); | |
AtaCommandBlock.AtaCommand = ATA_CMD_IDENTIFY_DRIVE; | |
AtaCommandBlock.AtaDeviceHead = (UINT8)(Device << 0x4); | |
Status = AtaPioDataInOut ( | |
Instance->PciIo, | |
&Instance->IdeRegisters[Channel], | |
Buffer, | |
sizeof (EFI_IDENTIFY_DATA), | |
TRUE, | |
&AtaCommandBlock, | |
AtaStatusBlock, | |
ATA_ATAPI_TIMEOUT, | |
NULL | |
); | |
return Status; | |
} | |
/** | |
This function is called by DiscoverIdeDevice() during its device | |
identification. | |
Its main purpose is to get enough information for the device media | |
to fill in the Media data structure of the Block I/O Protocol interface. | |
There are 5 steps to reach such objective: | |
1. Sends out the ATAPI Identify Command to the specified device. | |
Only ATAPI device responses to this command. If the command succeeds, | |
it returns the Identify data structure which filled with information | |
about the device. Since the ATAPI device contains removable media, | |
the only meaningful information is the device module name. | |
2. Sends out ATAPI Inquiry Packet Command to the specified device. | |
This command will return inquiry data of the device, which contains | |
the device type information. | |
3. Allocate sense data space for future use. We don't detect the media | |
presence here to improvement boot performance, especially when CD | |
media is present. The media detection will be performed just before | |
each BLK_IO read/write | |
@param Instance A pointer to ATA_ATAPI_PASS_THRU_INSTANCE data structure. | |
@param Channel The channel number of device. | |
@param Device The device number of device. | |
@param Buffer A pointer to data buffer which is used to contain IDENTIFY data. | |
@param AtaStatusBlock A pointer to EFI_ATA_STATUS_BLOCK data structure. | |
@retval EFI_SUCCESS Identify ATAPI device successfully. | |
@retval EFI_DEVICE_ERROR ATA Identify Packet Device Command failed or device type | |
is not supported by this IDE driver. | |
@retval EFI_OUT_OF_RESOURCES Allocate memory failed. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
AtaIdentifyPacket ( | |
IN ATA_ATAPI_PASS_THRU_INSTANCE *Instance, | |
IN UINT8 Channel, | |
IN UINT8 Device, | |
IN OUT EFI_IDENTIFY_DATA *Buffer, | |
IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_ATA_COMMAND_BLOCK AtaCommandBlock; | |
ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK)); | |
AtaCommandBlock.AtaCommand = ATA_CMD_IDENTIFY_DEVICE; | |
AtaCommandBlock.AtaDeviceHead = (UINT8)(Device << 0x4); | |
// | |
// Send ATAPI Identify Command to get IDENTIFY data. | |
// | |
Status = AtaPioDataInOut ( | |
Instance->PciIo, | |
&Instance->IdeRegisters[Channel], | |
(VOID *)Buffer, | |
sizeof (EFI_IDENTIFY_DATA), | |
TRUE, | |
&AtaCommandBlock, | |
AtaStatusBlock, | |
ATA_ATAPI_TIMEOUT, | |
NULL | |
); | |
return Status; | |
} | |
/** | |
This function is used for detect whether the IDE device exists in the | |
specified Channel as the specified Device Number. | |
There is two IDE channels: one is Primary Channel, the other is | |
Secondary Channel.(Channel is the logical name for the physical "Cable".) | |
Different channel has different register group. | |
On each IDE channel, at most two IDE devices attach, | |
one is called Device 0 (Master device), the other is called Device 1 | |
(Slave device). The devices on the same channel co-use the same register | |
group, so before sending out a command for a specified device via command | |
register, it is a must to select the current device to accept the command | |
by set the device number in the Head/Device Register. | |
@param Instance A pointer to ATA_ATAPI_PASS_THRU_INSTANCE data structure. | |
@param IdeChannel The channel number of device. | |
@retval EFI_SUCCESS successfully detects device. | |
@retval other any failure during detection process will return this value. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
DetectAndConfigIdeDevice ( | |
IN ATA_ATAPI_PASS_THRU_INSTANCE *Instance, | |
IN UINT8 IdeChannel | |
) | |
{ | |
EFI_STATUS Status; | |
UINT8 SectorCountReg; | |
UINT8 LBALowReg; | |
UINT8 LBAMidReg; | |
UINT8 LBAHighReg; | |
EFI_ATA_DEVICE_TYPE DeviceType; | |
UINT8 IdeDevice; | |
EFI_IDE_REGISTERS *IdeRegisters; | |
EFI_IDENTIFY_DATA Buffer; | |
EFI_IDE_CONTROLLER_INIT_PROTOCOL *IdeInit; | |
EFI_PCI_IO_PROTOCOL *PciIo; | |
EFI_ATA_COLLECTIVE_MODE *SupportedModes; | |
EFI_ATA_TRANSFER_MODE TransferMode; | |
EFI_ATA_DRIVE_PARMS DriveParameters; | |
IdeRegisters = &Instance->IdeRegisters[IdeChannel]; | |
IdeInit = Instance->IdeControllerInit; | |
PciIo = Instance->PciIo; | |
for (IdeDevice = 0; IdeDevice < EfiIdeMaxDevice; IdeDevice++) { | |
// | |
// Select Master or Slave device to get the return signature for ATA DEVICE DIAGNOSTIC cmd. | |
// | |
IdeWritePortB (PciIo, IdeRegisters->Head, (UINT8)((IdeDevice << 4) | 0xe0)); | |
// | |
// Send ATA Device Execut Diagnostic command. | |
// This command should work no matter DRDY is ready or not | |
// | |
IdeWritePortB (PciIo, IdeRegisters->CmdOrStatus, ATA_CMD_EXEC_DRIVE_DIAG); | |
Status = WaitForBSYClear (PciIo, IdeRegisters, 350000000); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "New detecting method: Send Execute Diagnostic Command: WaitForBSYClear: Status: %d\n", Status)); | |
continue; | |
} | |
// | |
// Select Master or Slave device to get the return signature for ATA DEVICE DIAGNOSTIC cmd. | |
// | |
IdeWritePortB (PciIo, IdeRegisters->Head, (UINT8)((IdeDevice << 4) | 0xe0)); | |
// | |
// Stall for 1 milliseconds. | |
// | |
MicroSecondDelay (1000); | |
SectorCountReg = IdeReadPortB (PciIo, IdeRegisters->SectorCount); | |
LBALowReg = IdeReadPortB (PciIo, IdeRegisters->SectorNumber); | |
LBAMidReg = IdeReadPortB (PciIo, IdeRegisters->CylinderLsb); | |
LBAHighReg = IdeReadPortB (PciIo, IdeRegisters->CylinderMsb); | |
// | |
// Refer to ATA/ATAPI 4 Spec, section 9.1 | |
// | |
if ((SectorCountReg == 0x1) && (LBALowReg == 0x1) && (LBAMidReg == 0x0) && (LBAHighReg == 0x0)) { | |
DeviceType = EfiIdeHarddisk; | |
} else if ((LBAMidReg == 0x14) && (LBAHighReg == 0xeb)) { | |
DeviceType = EfiIdeCdrom; | |
} else { | |
continue; | |
} | |
// | |
// Send IDENTIFY cmd to the device to test if it is really attached. | |
// | |
if (DeviceType == EfiIdeHarddisk) { | |
Status = AtaIdentify (Instance, IdeChannel, IdeDevice, &Buffer, NULL); | |
// | |
// if identifying ata device is failure, then try to send identify packet cmd. | |
// | |
if (EFI_ERROR (Status)) { | |
REPORT_STATUS_CODE (EFI_PROGRESS_CODE, (EFI_PERIPHERAL_FIXED_MEDIA | EFI_P_EC_NOT_DETECTED)); | |
DeviceType = EfiIdeCdrom; | |
Status = AtaIdentifyPacket (Instance, IdeChannel, IdeDevice, &Buffer, NULL); | |
} | |
} else { | |
Status = AtaIdentifyPacket (Instance, IdeChannel, IdeDevice, &Buffer, NULL); | |
// | |
// if identifying atapi device is failure, then try to send identify cmd. | |
// | |
if (EFI_ERROR (Status)) { | |
DeviceType = EfiIdeHarddisk; | |
Status = AtaIdentify (Instance, IdeChannel, IdeDevice, &Buffer, NULL); | |
} | |
} | |
if (EFI_ERROR (Status)) { | |
// | |
// No device is found at this port | |
// | |
continue; | |
} | |
DEBUG (( | |
DEBUG_INFO, | |
"[%a] channel [%a] [%a] device\n", | |
(IdeChannel == 1) ? "secondary" : "primary ", | |
(IdeDevice == 1) ? "slave " : "master", | |
DeviceType == EfiIdeCdrom ? "cdrom " : "harddisk" | |
)); | |
// | |
// If the device is a hard disk, then try to enable S.M.A.R.T feature | |
// | |
if ((DeviceType == EfiIdeHarddisk) && PcdGetBool (PcdAtaSmartEnable)) { | |
IdeAtaSmartSupport ( | |
Instance, | |
IdeChannel, | |
IdeDevice, | |
&Buffer, | |
NULL | |
); | |
} | |
// | |
// Submit identify data to IDE controller init driver | |
// | |
IdeInit->SubmitData (IdeInit, IdeChannel, IdeDevice, &Buffer); | |
// | |
// Now start to config ide device parameter and transfer mode. | |
// | |
Status = IdeInit->CalculateMode ( | |
IdeInit, | |
IdeChannel, | |
IdeDevice, | |
&SupportedModes | |
); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "Calculate Mode Fail, Status = %r\n", Status)); | |
continue; | |
} | |
// | |
// Set best supported PIO mode on this IDE device | |
// | |
if (SupportedModes->PioMode.Mode <= EfiAtaPioMode2) { | |
TransferMode.ModeCategory = EFI_ATA_MODE_DEFAULT_PIO; | |
} else { | |
TransferMode.ModeCategory = EFI_ATA_MODE_FLOW_PIO; | |
} | |
TransferMode.ModeNumber = (UINT8)(SupportedModes->PioMode.Mode); | |
if (SupportedModes->ExtModeCount == 0) { | |
Status = SetDeviceTransferMode (Instance, IdeChannel, IdeDevice, &TransferMode, NULL); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "Set transfer Mode Fail, Status = %r\n", Status)); | |
continue; | |
} | |
} | |
// | |
// Set supported DMA mode on this IDE device. Note that UDMA & MDMA can't | |
// be set together. Only one DMA mode can be set to a device. If setting | |
// DMA mode operation fails, we can continue moving on because we only use | |
// PIO mode at boot time. DMA modes are used by certain kind of OS booting | |
// | |
if (SupportedModes->UdmaMode.Valid) { | |
TransferMode.ModeCategory = EFI_ATA_MODE_UDMA; | |
TransferMode.ModeNumber = (UINT8)(SupportedModes->UdmaMode.Mode); | |
Status = SetDeviceTransferMode (Instance, IdeChannel, IdeDevice, &TransferMode, NULL); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "Set transfer Mode Fail, Status = %r\n", Status)); | |
continue; | |
} | |
} else if (SupportedModes->MultiWordDmaMode.Valid) { | |
TransferMode.ModeCategory = EFI_ATA_MODE_MDMA; | |
TransferMode.ModeNumber = (UINT8)SupportedModes->MultiWordDmaMode.Mode; | |
Status = SetDeviceTransferMode (Instance, IdeChannel, IdeDevice, &TransferMode, NULL); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "Set transfer Mode Fail, Status = %r\n", Status)); | |
continue; | |
} | |
} | |
// | |
// Set Parameters for the device: | |
// 1) Init | |
// 2) Establish the block count for READ/WRITE MULTIPLE (EXT) command | |
// | |
if (DeviceType == EfiIdeHarddisk) { | |
// | |
// Init drive parameters | |
// | |
DriveParameters.Sector = (UINT8)((ATA5_IDENTIFY_DATA *)(&Buffer.AtaData))->sectors_per_track; | |
DriveParameters.Heads = (UINT8)(((ATA5_IDENTIFY_DATA *)(&Buffer.AtaData))->heads - 1); | |
DriveParameters.MultipleSector = (UINT8)((ATA5_IDENTIFY_DATA *)(&Buffer.AtaData))->multi_sector_cmd_max_sct_cnt; | |
SetDriveParameters (Instance, IdeChannel, IdeDevice, &DriveParameters, NULL); | |
} | |
// | |
// Set IDE controller Timing Blocks in the PCI Configuration Space | |
// | |
IdeInit->SetTiming (IdeInit, IdeChannel, IdeDevice, SupportedModes); | |
// | |
// IDE controller and IDE device timing is configured successfully. | |
// Now insert the device into device list. | |
// | |
Status = CreateNewDeviceInfo (Instance, IdeChannel, IdeDevice, DeviceType, &Buffer); | |
if (EFI_ERROR (Status)) { | |
continue; | |
} | |
if (DeviceType == EfiIdeHarddisk) { | |
REPORT_STATUS_CODE (EFI_PROGRESS_CODE, (EFI_PERIPHERAL_FIXED_MEDIA | EFI_P_PC_ENABLE)); | |
} | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Initialize ATA host controller at IDE mode. | |
The function is designed to initialize ATA host controller. | |
@param[in] Instance A pointer to the ATA_ATAPI_PASS_THRU_INSTANCE instance. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
IdeModeInitialization ( | |
IN ATA_ATAPI_PASS_THRU_INSTANCE *Instance | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_IDE_CONTROLLER_INIT_PROTOCOL *IdeInit; | |
EFI_PCI_IO_PROTOCOL *PciIo; | |
UINT8 Channel; | |
UINT8 IdeChannel; | |
BOOLEAN ChannelEnabled; | |
UINT8 MaxDevices; | |
IdeInit = Instance->IdeControllerInit; | |
PciIo = Instance->PciIo; | |
Channel = IdeInit->ChannelCount; | |
// | |
// Obtain IDE IO port registers' base addresses | |
// | |
Status = GetIdeRegisterIoAddr (PciIo, Instance->IdeRegisters); | |
if (EFI_ERROR (Status)) { | |
goto ErrorExit; | |
} | |
for (IdeChannel = 0; IdeChannel < Channel; IdeChannel++) { | |
IdeInit->NotifyPhase (IdeInit, EfiIdeBeforeChannelEnumeration, IdeChannel); | |
// | |
// now obtain channel information fron IdeControllerInit protocol. | |
// | |
Status = IdeInit->GetChannelInfo ( | |
IdeInit, | |
IdeChannel, | |
&ChannelEnabled, | |
&MaxDevices | |
); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "[GetChannel, Status=%x]", Status)); | |
continue; | |
} | |
if (!ChannelEnabled) { | |
continue; | |
} | |
ASSERT (MaxDevices <= 2); | |
// | |
// Now inform the IDE Controller Init Module. | |
// | |
IdeInit->NotifyPhase (IdeInit, EfiIdeBeforeChannelReset, IdeChannel); | |
// | |
// No reset channel function implemented. | |
// | |
IdeInit->NotifyPhase (IdeInit, EfiIdeAfterChannelReset, IdeChannel); | |
// | |
// Now inform the IDE Controller Init Module. | |
// | |
IdeInit->NotifyPhase (IdeInit, EfiIdeBusBeforeDevicePresenceDetection, IdeChannel); | |
// | |
// Detect all attached ATA devices and set the transfer mode for each device. | |
// | |
DetectAndConfigIdeDevice (Instance, IdeChannel); | |
} | |
// | |
// All configurations done! Notify IdeController to do post initialization | |
// work such as saving IDE controller PCI settings for S3 resume | |
// | |
IdeInit->NotifyPhase (IdeInit, EfiIdeBusPhaseMaximum, 0); | |
ErrorExit: | |
return Status; | |
} |