| /** @file | |
| Header file for AHCI mode of ATA host controller. | |
| Copyright (c) 2010 - 2015, Intel Corporation. All rights reserved.<BR> | |
| This program and the accompanying materials | |
| are licensed and made available under the terms and conditions of the BSD License | |
| which accompanies this distribution. The full text of the license may be found at | |
| http://opensource.org/licenses/bsd-license.php | |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
| **/ | |
| #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 writen | |
| @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 writen | |
| @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 writen | |
| @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 ((EFI_D_ERROR, "CheckRegisterStatus()-- %02x : Error : Write Fault\n", StatusBlock.AtaStatus)); | |
| } | |
| if ((StatusBlock.AtaStatus & ATA_STSREG_CORR) != 0) { | |
| DEBUG ((EFI_D_ERROR, "CheckRegisterStatus()-- %02x : Error : Corrected Data\n", StatusBlock.AtaStatus)); | |
| } | |
| if ((StatusBlock.AtaStatus & ATA_STSREG_ERR) != 0) { | |
| if ((StatusBlock.AtaError & ATA_ERRREG_BBK) != 0) { | |
| DEBUG ((EFI_D_ERROR, "CheckRegisterStatus()-- %02x : Error : Bad Block Detected\n", StatusBlock.AtaError)); | |
| } | |
| if ((StatusBlock.AtaError & ATA_ERRREG_UNC) != 0) { | |
| DEBUG ((EFI_D_ERROR, "CheckRegisterStatus()-- %02x : Error : Uncorrectable Data\n", StatusBlock.AtaError)); | |
| } | |
| if ((StatusBlock.AtaError & ATA_ERRREG_MC) != 0) { | |
| DEBUG ((EFI_D_ERROR, "CheckRegisterStatus()-- %02x : Error : Media Change\n", StatusBlock.AtaError)); | |
| } | |
| if ((StatusBlock.AtaError & ATA_ERRREG_ABRT) != 0) { | |
| DEBUG ((EFI_D_ERROR, "CheckRegisterStatus()-- %02x : Error : Abort\n", StatusBlock.AtaError)); | |
| } | |
| if ((StatusBlock.AtaError & ATA_ERRREG_TK0NF) != 0) { | |
| DEBUG ((EFI_D_ERROR, "CheckRegisterStatus()-- %02x : Error : Track 0 Not Found\n", StatusBlock.AtaError)); | |
| } | |
| if ((StatusBlock.AtaError & ATA_ERRREG_AMNF) != 0) { | |
| DEBUG ((EFI_D_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 DRDY bit set in the Status Register. DRDY | |
| bit is set when the device is ready to accept command. Most ATA commands must be | |
| sent after DRDY set except the ATAPI Packet Command. | |
| @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 DRDY bit set within the time out. | |
| @retval EFI_TIMEOUT DRDY bit not set within the time out. | |
| @note Read Status Register will clear interrupt status. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| DRDYReady ( | |
| 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 { | |
| StatusRegister = IdeReadPortB (PciIo, IdeRegisters->CmdOrStatus); | |
| // | |
| // Wait for BSY == 0, then judge if DRDY is set 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_DRDY) == ATA_STSREG_DRDY) { | |
| return EFI_SUCCESS; | |
| } else { | |
| return EFI_DEVICE_ERROR; | |
| } | |
| } | |
| // | |
| // Stall for 100 microseconds. | |
| // | |
| MicroSecondDelay (100); | |
| Delay--; | |
| } while (InfiniteWait || (Delay > 0)); | |
| return EFI_TIMEOUT; | |
| } | |
| /** | |
| This function is used to poll for the DRDY bit set in the Alternate Status Register. | |
| DRDY bit is set when the device is ready to accept command. Most ATA commands must | |
| be sent after DRDY set except the ATAPI Packet Command. | |
| @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 DRDY bit set within the time out. | |
| @retval EFI_TIMEOUT DRDY bit not set within the time out. | |
| @note Read Alternate Status Register will clear interrupt status. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| DRDYReady2 ( | |
| 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 { | |
| AltRegister = IdeReadPortB (PciIo, IdeRegisters->AltOrDev); | |
| // | |
| // Wait for BSY == 0, then judge if DRDY is set 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_DRDY) == ATA_STSREG_DRDY) { | |
| return EFI_SUCCESS; | |
| } else { | |
| return EFI_DEVICE_ERROR; | |
| } | |
| } | |
| // | |
| // 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; | |
| } | |
| /** | |
| 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 | |
| WaitForBSYClear2 ( | |
| IN EFI_PCI_IO_PROTOCOL *PciIo, | |
| IN EFI_IDE_REGISTERS *IdeRegisters, | |
| IN UINT64 Timeout | |
| ) | |
| { | |
| UINT64 Delay; | |
| UINT8 AltStatusRegister; | |
| BOOLEAN InfiniteWait; | |
| ASSERT (PciIo != NULL); | |
| ASSERT (IdeRegisters != NULL); | |
| if (Timeout == 0) { | |
| InfiniteWait = TRUE; | |
| } else { | |
| InfiniteWait = FALSE; | |
| } | |
| Delay = DivU64x32(Timeout, 1000) + 1; | |
| do { | |
| AltStatusRegister = IdeReadPortB (PciIo, IdeRegisters->AltOrDev); | |
| if ((AltStatusRegister & 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 configureation 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; | |
| } | |
| /** | |
| This function is used to implement the Soft Reset on the specified device. But, | |
| the ATA Soft Reset mechanism is so strong a reset method that it will force | |
| resetting on both devices connected to the same cable. | |
| It is called by IdeBlkIoReset(), a interface function of Block | |
| I/O protocol. | |
| This function can also be used by the ATAPI device to perform reset when | |
| ATAPI Reset command is failed. | |
| @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 Soft reset completes successfully. | |
| @retval EFI_DEVICE_ERROR Any step during the reset process is failed. | |
| @note The registers initial values after ATA soft reset are different | |
| to the ATA device and ATAPI device. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| AtaSoftReset ( | |
| IN EFI_PCI_IO_PROTOCOL *PciIo, | |
| IN EFI_IDE_REGISTERS *IdeRegisters, | |
| IN UINT64 Timeout | |
| ) | |
| { | |
| UINT8 DeviceControl; | |
| DeviceControl = 0; | |
| // | |
| // disable Interrupt and set SRST bit to initiate soft reset | |
| // | |
| DeviceControl = ATA_CTLREG_SRST | ATA_CTLREG_IEN_L; | |
| IdeWritePortB (PciIo, IdeRegisters->AltOrDev, DeviceControl); | |
| // | |
| // SRST should assert for at least 5 us, we use 10 us for | |
| // better compatibility | |
| // | |
| MicroSecondDelay (10); | |
| // | |
| // Enable interrupt to support UDMA, and clear SRST bit | |
| // | |
| DeviceControl = 0; | |
| IdeWritePortB (PciIo, IdeRegisters->AltOrDev, DeviceControl); | |
| // | |
| // Wait for at least 10 ms to check BSY status, we use 10 ms | |
| // for better compatibility | |
| // | |
| MicroSecondDelay (10000); | |
| // | |
| // slave device needs at most 31ms to clear BSY | |
| // | |
| if (WaitForBSYClear (PciIo, IdeRegisters, Timeout) == EFI_TIMEOUT) { | |
| return EFI_DEVICE_ERROR; | |
| } | |
| 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 transfered 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 ((EFI_D_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 ((EFI_D_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 premitted. | |
| // | |
| 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 | |
| ); | |
| // | |
| // 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 | |
| ); | |
| 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 ((EFI_D_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 ((EFI_D_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 ((EFI_D_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 ((EFI_D_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((EFI_D_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 ((EFI_D_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 ((EFI_D_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 ((EFI_D_ERROR, "Set transfer Mode Fail, Status = %r\n", Status)); | |
| continue; | |
| } | |
| } | |
| // | |
| // Set supported DMA mode on this IDE device. Note that UDMA & MDMA cann'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 ((EFI_D_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 ((EFI_D_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 driver 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; | |
| Status = 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 ((EFI_D_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; | |
| } | |