git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@10414 6f19259b-4bc3-4df7-8a09-765794883524
		
			
				
	
	
		
			1331 lines
		
	
	
		
			38 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1331 lines
		
	
	
		
			38 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
|   The file ontaining the helper functions implement of the Ide Bus driver
 | |
|   
 | |
|   Copyright (c) 2006 - 2008, 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 "IdeBus.h"
 | |
| 
 | |
| BOOLEAN ChannelDeviceDetected = FALSE;
 | |
| BOOLEAN SlaveDeviceExist      = FALSE;
 | |
| UINT8   SlaveDeviceType       = INVALID_DEVICE_TYPE;
 | |
| BOOLEAN MasterDeviceExist     = FALSE;
 | |
| UINT8   MasterDeviceType      = INVALID_DEVICE_TYPE;
 | |
| 
 | |
| /**
 | |
|   read a one-byte data from a IDE port.
 | |
| 
 | |
|   @param  PciIo  The PCI IO protocol instance
 | |
|   @param  Port   the IDE Port number 
 | |
| 
 | |
|   @return  the one-byte data read from IDE port
 | |
| **/
 | |
| UINT8
 | |
| IDEReadPortB (
 | |
|   IN  EFI_PCI_IO_PROTOCOL   *PciIo,
 | |
|   IN  UINT16                Port
 | |
|   )
 | |
| {
 | |
|   UINT8 Data;
 | |
| 
 | |
|   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;
 | |
| }
 | |
| /**
 | |
|   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 Pointer to the EFI_PCI_IO instance
 | |
|   @param  Port IO port to read
 | |
|   @param  Count No. of UINT16's to read
 | |
|   @param  Buffer Pointer to the data buffer for read
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| IDEReadPortWMultiple (
 | |
|   IN  EFI_PCI_IO_PROTOCOL   *PciIo,
 | |
|   IN  UINT16                Port,
 | |
|   IN  UINTN                 Count,
 | |
|   OUT VOID                  *Buffer
 | |
|   )
 | |
| {
 | |
|   UINT16  *AlignedBuffer;
 | |
|   UINT16  *WorkingBuffer;
 | |
|   UINTN   Size;
 | |
| 
 | |
|   //
 | |
|   // Prepare an 16-bit alligned working buffer. CpuIo will return failure and
 | |
|   // not perform actual I/O operations if buffer pointer passed in is not at
 | |
|   // natural boundary. The "Buffer" argument is passed in by user and may not
 | |
|   // at 16-bit natural boundary.
 | |
|   //
 | |
|   Size = sizeof (UINT16) * Count;
 | |
| 
 | |
|   gBS->AllocatePool (
 | |
|         EfiBootServicesData,
 | |
|         Size + 1,
 | |
|         (VOID**)&WorkingBuffer
 | |
|         );
 | |
| 
 | |
|   AlignedBuffer = (UINT16 *) ((UINTN)(((UINTN) WorkingBuffer + 0x1) & (~0x1)));
 | |
| 
 | |
|   //
 | |
|   // Perform UINT16 data read from FIFO
 | |
|   //
 | |
|   PciIo->Io.Read (
 | |
|               PciIo,
 | |
|               EfiPciIoWidthFifoUint16,
 | |
|               EFI_PCI_IO_PASS_THROUGH_BAR,
 | |
|               (UINT64) Port,
 | |
|               Count,
 | |
|               (UINT16*)AlignedBuffer
 | |
|               );
 | |
| 
 | |
|   //
 | |
|   // Copy data to user buffer
 | |
|   //
 | |
|   CopyMem (Buffer, (UINT16*)AlignedBuffer, Size);
 | |
|   gBS->FreePool (WorkingBuffer);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   write a 1-byte data to a specific IDE port.
 | |
| 
 | |
|   @param  PciIo  PCI IO protocol instance
 | |
|   @param  Port   The IDE port to be writen
 | |
|   @param  Data   The data to write to the port
 | |
| **/
 | |
| VOID
 | |
| IDEWritePortB (
 | |
|   IN  EFI_PCI_IO_PROTOCOL   *PciIo,
 | |
|   IN  UINT16                Port,
 | |
|   IN  UINT8                 Data
 | |
|   )
 | |
| {
 | |
|   //
 | |
|   // 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  PCI IO protocol instance
 | |
|   @param  Port   The IDE port to be writen
 | |
|   @param  Data   The data to write to the port
 | |
| **/
 | |
| VOID
 | |
| IDEWritePortW (
 | |
|   IN  EFI_PCI_IO_PROTOCOL   *PciIo,
 | |
|   IN  UINT16                Port,
 | |
|   IN  UINT16                Data
 | |
|   )
 | |
| {
 | |
|   //
 | |
|   // perform 1-word data write to register
 | |
|   //
 | |
|   PciIo->Io.Write (
 | |
|               PciIo,
 | |
|               EfiPciIoWidthUint16,
 | |
|               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 Pointer to the EFI_PCI_IO instance
 | |
|   @param  Port IO port to read
 | |
|   @param  Count No. of UINT16's to read
 | |
|   @param  Buffer Pointer to the data buffer for read
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| IDEWritePortWMultiple (
 | |
|   IN  EFI_PCI_IO_PROTOCOL   *PciIo,
 | |
|   IN  UINT16                Port,
 | |
|   IN  UINTN                 Count,
 | |
|   IN  VOID                  *Buffer
 | |
|   )
 | |
| {
 | |
|   UINT16  *AlignedBuffer;
 | |
|   UINT32  *WorkingBuffer;
 | |
|   UINTN   Size;
 | |
| 
 | |
|   //
 | |
|   // Prepare an 16-bit alligned working buffer. CpuIo will return failure and
 | |
|   // not perform actual I/O operations if buffer pointer passed in is not at
 | |
|   // natural boundary. The "Buffer" argument is passed in by user and may not
 | |
|   // at 16-bit natural boundary.
 | |
|   //
 | |
|   Size = sizeof (UINT16) * Count;
 | |
| 
 | |
|   gBS->AllocatePool (
 | |
|         EfiBootServicesData,
 | |
|         Size + 1,
 | |
|         (VOID **) &WorkingBuffer
 | |
|         );
 | |
| 
 | |
|   AlignedBuffer = (UINT16 *) ((UINTN)(((UINTN) WorkingBuffer + 0x1) & (~0x1)));
 | |
| 
 | |
|   //
 | |
|   // Copy data from user buffer to working buffer
 | |
|   //
 | |
|   CopyMem ((UINT16 *) AlignedBuffer, Buffer, Size);
 | |
| 
 | |
|   //
 | |
|   // perform UINT16 data write to the FIFO
 | |
|   //
 | |
|   PciIo->Io.Write (
 | |
|               PciIo,
 | |
|               EfiPciIoWidthFifoUint16,
 | |
|               EFI_PCI_IO_PASS_THROUGH_BAR,
 | |
|               (UINT64) Port,
 | |
|               Count,
 | |
|               (UINT16 *) AlignedBuffer
 | |
|               );
 | |
| 
 | |
|   gBS->FreePool (WorkingBuffer);
 | |
| }
 | |
| /**
 | |
|   Get IDE IO 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 IO 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.
 | |
|   <pre>
 | |
|   ___________________________________________
 | |
|   |           | Command Block | Control Block |
 | |
|   |  Channel  |   Registers   |   Registers   |
 | |
|   |___________|_______________|_______________|
 | |
|   |  Primary  |  1F0h - 1F7h  |  3F6h - 3F7h  |
 | |
|   |___________|_______________|_______________|
 | |
|   | Secondary |  170h - 177h  |  376h - 377h  |
 | |
|   |___________|_______________|_______________|
 | |
| 
 | |
|   Table 1. Compatibility resource mappings
 | |
|   </pre>
 | |
| 
 | |
|   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.
 | |
|   <pre>
 | |
|   ___________________________________________________
 | |
|   |           |   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
 | |
|   </pre>
 | |
|   @note Refer to Intel ICH4 datasheet, Control Block Offset: 03F4h for
 | |
|   primary, 0374h for secondary. So 2 bytes extra offset should be
 | |
|   added to the base addresses read from BARs.
 | |
| 
 | |
|   For more details, please refer to PCI IDE Controller Specification and Intel
 | |
|   ICH4 Datasheet.
 | |
| 
 | |
|   @param  PciIo Pointer to the EFI_PCI_IO_PROTOCOL instance
 | |
|   @param  IdeRegsBaseAddr Pointer to IDE_REGISTERS_BASE_ADDR to
 | |
|            receive IDE IO 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
 | |
| GetIdeRegistersBaseAddr (
 | |
|   IN  EFI_PCI_IO_PROTOCOL         *PciIo,
 | |
|   OUT IDE_REGISTERS_BASE_ADDR     *IdeRegsBaseAddr
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
|   PCI_TYPE00  PciData;
 | |
| 
 | |
|   Status = PciIo->Pci.Read (
 | |
|                         PciIo,
 | |
|                         EfiPciIoWidthUint8,
 | |
|                         0,
 | |
|                         sizeof (PciData),
 | |
|                         &PciData
 | |
|                         );
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   if ((PciData.Hdr.ClassCode[0] & IDE_PRIMARY_OPERATING_MODE) == 0) {
 | |
|     IdeRegsBaseAddr[IdePrimary].CommandBlockBaseAddr  = 0x1f0;
 | |
|     IdeRegsBaseAddr[IdePrimary].ControlBlockBaseAddr  = 0x3f6;
 | |
|     IdeRegsBaseAddr[IdePrimary].BusMasterBaseAddr     =
 | |
|     (UINT16)((PciData.Device.Bar[4] & 0x0000fff0));
 | |
|   } else {
 | |
|     //
 | |
|     // The BARs should be of IO type
 | |
|     //
 | |
|     if ((PciData.Device.Bar[0] & BIT0) == 0 ||
 | |
|         (PciData.Device.Bar[1] & BIT0) == 0) {
 | |
|       return EFI_UNSUPPORTED;
 | |
|     }
 | |
| 
 | |
|     IdeRegsBaseAddr[IdePrimary].CommandBlockBaseAddr  =
 | |
|     (UINT16) (PciData.Device.Bar[0] & 0x0000fff8);
 | |
|     IdeRegsBaseAddr[IdePrimary].ControlBlockBaseAddr  =
 | |
|     (UINT16) ((PciData.Device.Bar[1] & 0x0000fffc) + 2);
 | |
|     IdeRegsBaseAddr[IdePrimary].BusMasterBaseAddr     =
 | |
|     (UINT16) ((PciData.Device.Bar[4] & 0x0000fff0));
 | |
|   }
 | |
| 
 | |
|   if ((PciData.Hdr.ClassCode[0] & IDE_SECONDARY_OPERATING_MODE) == 0) {
 | |
|     IdeRegsBaseAddr[IdeSecondary].CommandBlockBaseAddr  = 0x170;
 | |
|     IdeRegsBaseAddr[IdeSecondary].ControlBlockBaseAddr  = 0x376;
 | |
|     IdeRegsBaseAddr[IdeSecondary].BusMasterBaseAddr     =
 | |
|     (UINT16) ((PciData.Device.Bar[4] & 0x0000fff0));
 | |
|   } else {
 | |
|     //
 | |
|     // The BARs should be of IO type
 | |
|     //
 | |
|     if ((PciData.Device.Bar[2] & BIT0) == 0 ||
 | |
|         (PciData.Device.Bar[3] & BIT0) == 0) {
 | |
|       return EFI_UNSUPPORTED;
 | |
|     }
 | |
| 
 | |
|     IdeRegsBaseAddr[IdeSecondary].CommandBlockBaseAddr  =
 | |
|     (UINT16) (PciData.Device.Bar[2] & 0x0000fff8);
 | |
|     IdeRegsBaseAddr[IdeSecondary].ControlBlockBaseAddr  =
 | |
|     (UINT16) ((PciData.Device.Bar[3] & 0x0000fffc) + 2);
 | |
|     IdeRegsBaseAddr[IdeSecondary].BusMasterBaseAddr     =
 | |
|     (UINT16) ((PciData.Device.Bar[4] & 0x0000fff0));
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   This function is used to requery IDE resources. The IDE controller will
 | |
|   probably switch between native and legacy modes during the EFI->CSM->OS
 | |
|   transfer. We do this everytime before an BlkIo operation to ensure its
 | |
|   succeess.
 | |
| 
 | |
|   @param  IdeDev The BLK_IO private data which specifies the IDE device
 | |
|   
 | |
|   @retval EFI_INVALID_PARAMETER return this value when the channel is invalid
 | |
|   @retval EFI_SUCCESS           reassign the IDE IO resource successfully
 | |
|   @retval other                 get the IDE current base address effor
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| ReassignIdeResources (
 | |
|   IN  IDE_BLK_IO_DEV  *IdeDev
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS              Status;
 | |
|   IDE_REGISTERS_BASE_ADDR IdeRegsBaseAddr[IdeMaxChannel];
 | |
|   UINT16                  CommandBlockBaseAddr;
 | |
|   UINT16                  ControlBlockBaseAddr;
 | |
| 
 | |
|   if (IdeDev->Channel >= IdeMaxChannel) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
|   
 | |
|   //
 | |
|   // Requery IDE IO port registers' base addresses in case of the switch of
 | |
|   // native and legacy modes
 | |
|   //
 | |
|   Status = GetIdeRegistersBaseAddr (IdeDev->PciIo, IdeRegsBaseAddr);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   ZeroMem (IdeDev->IoPort, sizeof (IDE_BASE_REGISTERS));
 | |
|   CommandBlockBaseAddr                = IdeRegsBaseAddr[IdeDev->Channel].CommandBlockBaseAddr;
 | |
|   ControlBlockBaseAddr                = IdeRegsBaseAddr[IdeDev->Channel].ControlBlockBaseAddr;
 | |
| 
 | |
|   IdeDev->IoPort->Data                = CommandBlockBaseAddr;
 | |
|   (*(UINT16 *) &IdeDev->IoPort->Reg1) = (UINT16) (CommandBlockBaseAddr + 0x01);
 | |
|   IdeDev->IoPort->SectorCount         = (UINT16) (CommandBlockBaseAddr + 0x02);
 | |
|   IdeDev->IoPort->SectorNumber        = (UINT16) (CommandBlockBaseAddr + 0x03);
 | |
|   IdeDev->IoPort->CylinderLsb         = (UINT16) (CommandBlockBaseAddr + 0x04);
 | |
|   IdeDev->IoPort->CylinderMsb         = (UINT16) (CommandBlockBaseAddr + 0x05);
 | |
|   IdeDev->IoPort->Head                = (UINT16) (CommandBlockBaseAddr + 0x06);
 | |
| 
 | |
|   (*(UINT16 *) &IdeDev->IoPort->Reg)  = (UINT16) (CommandBlockBaseAddr + 0x07);
 | |
|   (*(UINT16 *) &IdeDev->IoPort->Alt)  = ControlBlockBaseAddr;
 | |
|   IdeDev->IoPort->DriveAddress        = (UINT16) (ControlBlockBaseAddr + 0x01);
 | |
|   IdeDev->IoPort->MasterSlave         = (UINT16) ((IdeDev->Device == IdeMaster) ? 1 : 0);
 | |
| 
 | |
|   IdeDev->IoPort->BusMasterBaseAddr   = IdeRegsBaseAddr[IdeDev->Channel].BusMasterBaseAddr;
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   This function is called by DiscoverIdeDevice(). It 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 IdeDev  pointer to IDE_BLK_IO_DEV data structure, used to record all the
 | |
|                  information of the IDE device.
 | |
| 
 | |
|   @retval EFI_SUCCESS successfully detects device.
 | |
| 
 | |
|   @retval other       any failure during detection process will return this value.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| DetectIDEController (
 | |
|   IN  IDE_BLK_IO_DEV  *IdeDev
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
|   UINT8       SectorCountReg;
 | |
|   UINT8       LBALowReg;
 | |
|   UINT8       LBAMidReg;
 | |
|   UINT8       LBAHighReg;
 | |
|   UINT8       InitStatusReg;
 | |
|   UINT8       StatusReg;
 | |
| 
 | |
|   //
 | |
|   // Select slave device
 | |
|   //
 | |
|   IDEWritePortB (
 | |
|     IdeDev->PciIo,
 | |
|     IdeDev->IoPort->Head,
 | |
|     (UINT8) ((1 << 4) | 0xe0)
 | |
|     );
 | |
|   gBS->Stall (100);
 | |
| 
 | |
|   //
 | |
|   // Save the init slave status register
 | |
|   //
 | |
|   InitStatusReg = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg.Status);
 | |
| 
 | |
|   //
 | |
|   // Select Master back
 | |
|   //
 | |
|   IDEWritePortB (
 | |
|     IdeDev->PciIo,
 | |
|     IdeDev->IoPort->Head,
 | |
|     (UINT8) ((0 << 4) | 0xe0)
 | |
|     );
 | |
|   gBS->Stall (100);
 | |
| 
 | |
|   //
 | |
|   // Send ATA Device Execut Diagnostic command.
 | |
|   // This command should work no matter DRDY is ready or not
 | |
|   //
 | |
|   IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Reg.Command, 0x90);
 | |
| 
 | |
|   Status    = WaitForBSYClear (IdeDev, 3500);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     DEBUG((EFI_D_ERROR, "New detecting method: Send Execute Diagnostic Command: WaitForBSYClear: Status: %d\n", Status));
 | |
|     return Status;
 | |
|   }
 | |
|   //
 | |
|   // Read device signature
 | |
|   //
 | |
|   //
 | |
|   // Select Master
 | |
|   //
 | |
|   IDEWritePortB (
 | |
|     IdeDev->PciIo,
 | |
|     IdeDev->IoPort->Head,
 | |
|     (UINT8) ((0 << 4) | 0xe0)
 | |
|     );
 | |
|   gBS->Stall (100);
 | |
|   SectorCountReg = IDEReadPortB (
 | |
|                      IdeDev->PciIo,
 | |
|                      IdeDev->IoPort->SectorCount
 | |
|                      );
 | |
|   LBALowReg      = IDEReadPortB (
 | |
|                      IdeDev->PciIo,
 | |
|                      IdeDev->IoPort->SectorNumber
 | |
|                      );
 | |
|   LBAMidReg      = IDEReadPortB (
 | |
|                      IdeDev->PciIo,
 | |
|                      IdeDev->IoPort->CylinderLsb
 | |
|                      );
 | |
|   LBAHighReg     = IDEReadPortB (
 | |
|                      IdeDev->PciIo,
 | |
|                      IdeDev->IoPort->CylinderMsb
 | |
|                      );
 | |
|   if ((SectorCountReg == 0x1) &&
 | |
|       (LBALowReg      == 0x1) &&
 | |
|       (LBAMidReg      == 0x0) &&
 | |
|       (LBAHighReg     == 0x0)) {
 | |
|     MasterDeviceExist = TRUE;
 | |
|     MasterDeviceType  = ATA_DEVICE_TYPE;
 | |
|   } else {
 | |
|     if ((LBAMidReg      == 0x14) &&
 | |
|         (LBAHighReg     == 0xeb)) {
 | |
|       MasterDeviceExist = TRUE;
 | |
|       MasterDeviceType  = ATAPI_DEVICE_TYPE;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // For some Hard Drive, it takes some time to get
 | |
|   // the right signature when operating in single slave mode.
 | |
|   // We stall 20ms to work around this.
 | |
|   //
 | |
|   if (!MasterDeviceExist) {
 | |
|     gBS->Stall (20000);
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Select Slave
 | |
|   //
 | |
|   IDEWritePortB (
 | |
|     IdeDev->PciIo,
 | |
|     IdeDev->IoPort->Head,
 | |
|     (UINT8) ((1 << 4) | 0xe0)
 | |
|     );
 | |
|   gBS->Stall (100);
 | |
|   SectorCountReg = IDEReadPortB (
 | |
|                      IdeDev->PciIo,
 | |
|                      IdeDev->IoPort->SectorCount
 | |
|                      );
 | |
|   LBALowReg  = IDEReadPortB (
 | |
|                  IdeDev->PciIo,
 | |
|                  IdeDev->IoPort->SectorNumber
 | |
|                  );
 | |
|   LBAMidReg  = IDEReadPortB (
 | |
|                  IdeDev->PciIo,
 | |
|                  IdeDev->IoPort->CylinderLsb
 | |
|                  );
 | |
|   LBAHighReg = IDEReadPortB (
 | |
|                  IdeDev->PciIo,
 | |
|                  IdeDev->IoPort->CylinderMsb
 | |
|                  );
 | |
|   StatusReg  = IDEReadPortB (
 | |
|                  IdeDev->PciIo,
 | |
|                  IdeDev->IoPort->Reg.Status
 | |
|                  );
 | |
|   if ((SectorCountReg == 0x1) &&
 | |
|       (LBALowReg      == 0x1) &&
 | |
|       (LBAMidReg      == 0x0) &&
 | |
|       (LBAHighReg     == 0x0)) {
 | |
|     SlaveDeviceExist = TRUE;
 | |
|     SlaveDeviceType  = ATA_DEVICE_TYPE;
 | |
|   } else {
 | |
|     if ((LBAMidReg     == 0x14) &&
 | |
|         (LBAHighReg    == 0xeb)) {
 | |
|       SlaveDeviceExist = TRUE;
 | |
|       SlaveDeviceType  = ATAPI_DEVICE_TYPE;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // When single master is plugged, slave device
 | |
|   // will be wrongly detected. Here's the workaround
 | |
|   // for ATA devices by detecting DRY bit in status
 | |
|   // register.
 | |
|   // NOTE: This workaround doesn't apply to ATAPI.
 | |
|   //
 | |
|   if (MasterDeviceExist && SlaveDeviceExist &&
 | |
|       (StatusReg & ATA_STSREG_DRDY) == 0               &&
 | |
|       (InitStatusReg & ATA_STSREG_DRDY) == 0           &&
 | |
|       MasterDeviceType == SlaveDeviceType   &&
 | |
|       SlaveDeviceType != ATAPI_DEVICE_TYPE) {
 | |
|     SlaveDeviceExist = FALSE;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Indicate this channel has been detected
 | |
|   //
 | |
|   ChannelDeviceDetected = TRUE;
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| /**
 | |
|   Detect if there is disk attached to this port
 | |
| 
 | |
|   @param  IdeDev The BLK_IO private data which specifies the IDE device.
 | |
|   
 | |
|   @retval EFI_NOT_FOUND   The device or channel is not found
 | |
|   @retval EFI_SUCCESS     The device is found
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| DiscoverIdeDevice (
 | |
|   IN IDE_BLK_IO_DEV *IdeDev
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
|   EFI_STATUS  LongPhyStatus;
 | |
| 
 | |
|   //
 | |
|   // If a channel has not been checked, check it now. Then set it to "checked" state
 | |
|   // After this step, all devices in this channel have been checked.
 | |
|   //
 | |
|   if (!ChannelDeviceDetected) {
 | |
|     Status = DetectIDEController (IdeDev);
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       return EFI_NOT_FOUND;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   Status = EFI_NOT_FOUND;
 | |
| 
 | |
|   //
 | |
|   // Device exists. test if it is an ATA device.
 | |
|   // Prefer the result from DetectIDEController,
 | |
|   // if failed, try another device type to handle
 | |
|   // devices that not follow the spec.
 | |
|   //
 | |
|   if ((IdeDev->Device == IdeMaster) && (MasterDeviceExist)) {
 | |
|     if (MasterDeviceType == ATA_DEVICE_TYPE) {
 | |
|       Status = ATAIdentify (IdeDev);
 | |
|       if (EFI_ERROR (Status)) {
 | |
|         Status = ATAPIIdentify (IdeDev);
 | |
|         if (!EFI_ERROR (Status)) {
 | |
|           MasterDeviceType = ATAPI_DEVICE_TYPE;
 | |
|         }
 | |
|       }
 | |
|     } else {
 | |
|       Status = ATAPIIdentify (IdeDev);
 | |
|       if (EFI_ERROR (Status)) {
 | |
|         Status = ATAIdentify (IdeDev);
 | |
|         if (!EFI_ERROR (Status)) {
 | |
|           MasterDeviceType = ATA_DEVICE_TYPE;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   if ((IdeDev->Device == IdeSlave) && (SlaveDeviceExist)) {
 | |
|     if (SlaveDeviceType == ATA_DEVICE_TYPE) {
 | |
|       Status = ATAIdentify (IdeDev);
 | |
|       if (EFI_ERROR (Status)) {
 | |
|         Status = ATAPIIdentify (IdeDev);
 | |
|         if (!EFI_ERROR (Status)) {
 | |
|           SlaveDeviceType = ATAPI_DEVICE_TYPE;
 | |
|         }
 | |
|       }
 | |
|     } else {
 | |
|       Status = ATAPIIdentify (IdeDev);
 | |
|       if (EFI_ERROR (Status)) {
 | |
|         Status = ATAIdentify (IdeDev);
 | |
|         if (!EFI_ERROR (Status)) {
 | |
|           SlaveDeviceType = ATA_DEVICE_TYPE;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return EFI_NOT_FOUND;
 | |
|   }
 | |
|   //
 | |
|   // Init Block I/O interface
 | |
|   //
 | |
|   LongPhyStatus = AtaEnableLongPhysicalSector (IdeDev);
 | |
|   if (!EFI_ERROR (LongPhyStatus)) {
 | |
|     IdeDev->BlkIo.Revision = EFI_BLOCK_IO_PROTOCOL_REVISION2;
 | |
|   } else {
 | |
|     IdeDev->BlkIo.Revision = EFI_BLOCK_IO_PROTOCOL_REVISION;
 | |
|   }
 | |
|   IdeDev->BlkIo.Reset               = IDEBlkIoReset;
 | |
|   IdeDev->BlkIo.ReadBlocks          = IDEBlkIoReadBlocks;
 | |
|   IdeDev->BlkIo.WriteBlocks         = IDEBlkIoWriteBlocks;
 | |
|   IdeDev->BlkIo.FlushBlocks         = IDEBlkIoFlushBlocks;
 | |
| 
 | |
|   IdeDev->BlkMedia.LogicalPartition = FALSE;
 | |
|   IdeDev->BlkMedia.WriteCaching     = FALSE;
 | |
| 
 | |
|   //
 | |
|   // Init Disk Info interface
 | |
|   //
 | |
|   gBS->CopyMem (&IdeDev->DiskInfo.Interface, &gEfiDiskInfoIdeInterfaceGuid, sizeof (EFI_GUID));
 | |
|   IdeDev->DiskInfo.Inquiry    = IDEDiskInfoInquiry;
 | |
|   IdeDev->DiskInfo.Identify   = IDEDiskInfoIdentify;
 | |
|   IdeDev->DiskInfo.SenseData  = IDEDiskInfoSenseData;
 | |
|   IdeDev->DiskInfo.WhichIde   = IDEDiskInfoWhichIde;
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   This interface is used to initialize all state data related to the detection of one
 | |
|   channel.
 | |
| **/
 | |
| VOID
 | |
| InitializeIDEChannelData (
 | |
|   VOID
 | |
|   )
 | |
| {
 | |
|   ChannelDeviceDetected = FALSE;
 | |
|   MasterDeviceExist = FALSE;
 | |
|   MasterDeviceType  = 0xff;
 | |
|   SlaveDeviceExist  = FALSE;
 | |
|   SlaveDeviceType   = 0xff;
 | |
| }
 | |
| /**
 | |
|   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 IdeDev                 pointer pointing to IDE_BLK_IO_DEV data structure, used 
 | |
|                                 to record all the information of the IDE device.
 | |
|   @param TimeoutInMilliSeconds  used to designate the timeout for the DRQ clear.
 | |
| 
 | |
|   @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
 | |
| DRQClear (
 | |
|   IN  IDE_BLK_IO_DEV  *IdeDev,
 | |
|   IN  UINTN           TimeoutInMilliSeconds
 | |
|   )
 | |
| {
 | |
|   UINT32  Delay;
 | |
|   UINT8   StatusRegister;
 | |
|   UINT8   ErrorRegister;
 | |
| 
 | |
|   Delay = (UINT32) (((TimeoutInMilliSeconds * STALL_1_MILLI_SECOND) / 30) + 1);
 | |
|   do {
 | |
| 
 | |
|     StatusRegister = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg.Status);
 | |
| 
 | |
|     //
 | |
|     // wait for BSY == 0 and DRQ == 0
 | |
|     //
 | |
|     if ((StatusRegister & (ATA_STSREG_DRQ | ATA_STSREG_BSY)) == 0) {
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     if ((StatusRegister & (ATA_STSREG_BSY | ATA_STSREG_ERR)) == ATA_STSREG_ERR) {
 | |
| 
 | |
|       ErrorRegister = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg1.Error);
 | |
|       if ((ErrorRegister & ATA_ERRREG_ABRT) == ATA_ERRREG_ABRT) {
 | |
|         return EFI_ABORTED;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     //  Stall for 30 us
 | |
|     //
 | |
|     gBS->Stall (30);
 | |
| 
 | |
|     Delay--;
 | |
| 
 | |
|   } while (Delay > 0);
 | |
| 
 | |
|   if (Delay == 0) {
 | |
|     return EFI_TIMEOUT;
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| /**
 | |
|   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 IdeDev                pointer pointing to IDE_BLK_IO_DEV data structure, used 
 | |
|                                to record all the information of the IDE device.
 | |
| 
 | |
|   @param TimeoutInMilliSeconds used to designate the timeout for the DRQ clear.
 | |
| 
 | |
|   @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
 | |
| DRQClear2 (
 | |
|   IN  IDE_BLK_IO_DEV  *IdeDev,
 | |
|   IN  UINTN           TimeoutInMilliSeconds
 | |
|   )
 | |
| {
 | |
|   UINT32  Delay;
 | |
|   UINT8   AltRegister;
 | |
|   UINT8   ErrorRegister;
 | |
| 
 | |
|   Delay = (UINT32) (((TimeoutInMilliSeconds * STALL_1_MILLI_SECOND) / 30) + 1);
 | |
|   do {
 | |
| 
 | |
|     AltRegister = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Alt.AltStatus);
 | |
| 
 | |
|     //
 | |
|     //  wait for BSY == 0 and DRQ == 0
 | |
|     //
 | |
|     if ((AltRegister & (ATA_STSREG_DRQ | ATA_STSREG_BSY)) == 0) {
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     if ((AltRegister & (ATA_STSREG_BSY | ATA_STSREG_ERR)) == ATA_STSREG_ERR) {
 | |
| 
 | |
|       ErrorRegister = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg1.Error);
 | |
|       if ((ErrorRegister & ATA_ERRREG_ABRT) == ATA_ERRREG_ABRT) {
 | |
|         return EFI_ABORTED;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Stall for 30 us
 | |
|     //
 | |
|     gBS->Stall (30);
 | |
| 
 | |
|     Delay--;
 | |
| 
 | |
|   } while (Delay > 0);
 | |
| 
 | |
|   if (Delay == 0) {
 | |
|     return EFI_TIMEOUT;
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   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 IdeDev                pointer pointing to IDE_BLK_IO_DEV data structure,used to
 | |
|                                record all the information of the IDE device.
 | |
|   @param TimeoutInMilliSeconds used to designate the timeout for the DRQ ready.
 | |
| 
 | |
|   @retval EFI_SUCCESS          DRQ bit set within the time out.
 | |
|   @retval EFI_TIMEOUT          DRQ bit not set within the time out.
 | |
|   @retval EFI_ABORTED          DRQ bit not set caused by the command abort.
 | |
| 
 | |
|   @note  Read Status Register will clear interrupt status.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| DRQReady (
 | |
|   IN  IDE_BLK_IO_DEV  *IdeDev,
 | |
|   IN  UINTN           TimeoutInMilliSeconds
 | |
|   )
 | |
| {
 | |
|   UINT32  Delay;
 | |
|   UINT8   StatusRegister;
 | |
|   UINT8   ErrorRegister;
 | |
| 
 | |
|   Delay = (UINT32) (((TimeoutInMilliSeconds * STALL_1_MILLI_SECOND) / 30) + 1);
 | |
|   do {
 | |
|     //
 | |
|     //  read Status Register will clear interrupt
 | |
|     //
 | |
|     StatusRegister = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg.Status);
 | |
| 
 | |
|     //
 | |
|     //  BSY==0,DRQ==1
 | |
|     //
 | |
|     if ((StatusRegister & (ATA_STSREG_BSY | ATA_STSREG_DRQ)) == ATA_STSREG_DRQ) {
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     if ((StatusRegister & (ATA_STSREG_BSY | ATA_STSREG_ERR)) == ATA_STSREG_ERR) {
 | |
| 
 | |
|       ErrorRegister = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg1.Error);
 | |
|       if ((ErrorRegister & ATA_ERRREG_ABRT) == ATA_ERRREG_ABRT) {
 | |
|         return EFI_ABORTED;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Stall for 30 us
 | |
|     //
 | |
|     gBS->Stall (30);
 | |
| 
 | |
|     Delay--;
 | |
|   } while (Delay > 0);
 | |
| 
 | |
|   if (Delay == 0) {
 | |
|     return EFI_TIMEOUT;
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| /**
 | |
|   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 IdeDev                pointer pointing to IDE_BLK_IO_DEV data structure, used to 
 | |
|                                record all the information of the IDE device.
 | |
| 
 | |
|   @param TimeoutInMilliSeconds used to designate the timeout for the DRQ ready.
 | |
| 
 | |
|   @retval EFI_SUCCESS           DRQ bit set within the time out.
 | |
|   @retval EFI_TIMEOUT           DRQ bit not set within the time out.
 | |
|   @retval EFI_ABORTED           DRQ bit not set caused by the command abort.
 | |
|   @note  Read Alternate Status Register will not clear interrupt status.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| DRQReady2 (
 | |
|   IN  IDE_BLK_IO_DEV  *IdeDev,
 | |
|   IN  UINTN           TimeoutInMilliSeconds
 | |
|   )
 | |
| {
 | |
|   UINT32  Delay;
 | |
|   UINT8   AltRegister;
 | |
|   UINT8   ErrorRegister;
 | |
| 
 | |
|   Delay = (UINT32) (((TimeoutInMilliSeconds * STALL_1_MILLI_SECOND) / 30) + 1);
 | |
| 
 | |
|   do {
 | |
|     //
 | |
|     //  Read Alternate Status Register will not clear interrupt status
 | |
|     //
 | |
|     AltRegister = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Alt.AltStatus);
 | |
|     //
 | |
|     // BSY == 0 , DRQ == 1
 | |
|     //
 | |
|     if ((AltRegister & (ATA_STSREG_BSY | ATA_STSREG_DRQ)) == ATA_STSREG_DRQ) {
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     if ((AltRegister & (ATA_STSREG_BSY | ATA_STSREG_ERR)) == ATA_STSREG_ERR) {
 | |
| 
 | |
|       ErrorRegister = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg1.Error);
 | |
|       if ((ErrorRegister & ATA_ERRREG_ABRT) == ATA_ERRREG_ABRT) {
 | |
|         return EFI_ABORTED;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Stall for 30 us
 | |
|     //
 | |
|     gBS->Stall (30);
 | |
| 
 | |
|     Delay--;
 | |
|   } while (Delay > 0);
 | |
| 
 | |
|   if (Delay == 0) {
 | |
|     return EFI_TIMEOUT;
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   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 IdeDev                pointer pointing to IDE_BLK_IO_DEV data structure, used 
 | |
|                                to record all the information of the IDE device.
 | |
|   @param TimeoutInMilliSeconds used to designate the timeout for the DRQ ready.
 | |
| 
 | |
|   @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
 | |
| WaitForBSYClear (
 | |
|   IN  IDE_BLK_IO_DEV  *IdeDev,
 | |
|   IN  UINTN           TimeoutInMilliSeconds
 | |
|   )
 | |
| {
 | |
|   UINT32  Delay;
 | |
|   UINT8   StatusRegister;
 | |
| 
 | |
|   Delay = (UINT32) (((TimeoutInMilliSeconds * STALL_1_MILLI_SECOND) / 30) + 1);
 | |
|   do {
 | |
| 
 | |
|     StatusRegister = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg.Status);
 | |
|     if ((StatusRegister & ATA_STSREG_BSY) == 0x00) {
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Stall for 30 us
 | |
|     //
 | |
|     gBS->Stall (30);
 | |
| 
 | |
|     Delay--;
 | |
| 
 | |
|   } while (Delay > 0);
 | |
| 
 | |
|   if (Delay == 0) {
 | |
|     return EFI_TIMEOUT;
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| /**
 | |
|   This function is used to poll for the BSY bit clear in the Alternate Status Register. 
 | |
|   BSY is clear when the device is not busy. Every command must be sent after device is 
 | |
|   not busy.
 | |
| 
 | |
|   @param IdeDev               pointer pointing to IDE_BLK_IO_DEV data structure, used to record 
 | |
|                               all the information of the IDE device.
 | |
|   @param TimeoutInMilliSeconds used to designate the timeout for the DRQ ready.
 | |
| 
 | |
|   @retval EFI_SUCCESS         BSY bit clear within the time out.
 | |
|   @retval EFI_TIMEOUT         BSY bit not clear within the time out.
 | |
|   @note   Read Alternate Status Register will not clear interrupt status.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| WaitForBSYClear2 (
 | |
|   IN  IDE_BLK_IO_DEV  *IdeDev,
 | |
|   IN  UINTN           TimeoutInMilliSeconds
 | |
|   )
 | |
| {
 | |
|   UINT32  Delay;
 | |
|   UINT8   AltRegister;
 | |
| 
 | |
|   Delay = (UINT32) (((TimeoutInMilliSeconds * STALL_1_MILLI_SECOND) / 30) + 1);
 | |
|   do {
 | |
|     AltRegister = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Alt.AltStatus);
 | |
|     if ((AltRegister & ATA_STSREG_BSY) == 0x00) {
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     gBS->Stall (30);
 | |
| 
 | |
|     Delay--;
 | |
| 
 | |
|   } while (Delay > 0);
 | |
| 
 | |
|   if (Delay == 0) {
 | |
|     return EFI_TIMEOUT;
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| /**
 | |
|   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 IdeDev               pointer pointing to IDE_BLK_IO_DEV data structure, used
 | |
|                               to record all the information of the IDE device.
 | |
|   @param DelayInMilliSeconds  used to designate the timeout for the DRQ ready.
 | |
| 
 | |
|   @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
 | |
| DRDYReady (
 | |
|   IN  IDE_BLK_IO_DEV  *IdeDev,
 | |
|   IN  UINTN           DelayInMilliSeconds
 | |
|   )
 | |
| {
 | |
|   UINT32  Delay;
 | |
|   UINT8   StatusRegister;
 | |
|   UINT8   ErrorRegister;
 | |
| 
 | |
|   Delay = (UINT32) (((DelayInMilliSeconds * STALL_1_MILLI_SECOND) / 30) + 1);
 | |
|   do {
 | |
|     StatusRegister = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg.Status);
 | |
|     //
 | |
|     //  BSY == 0 , DRDY == 1
 | |
|     //
 | |
|     if ((StatusRegister & (ATA_STSREG_DRDY | ATA_STSREG_BSY)) == ATA_STSREG_DRDY) {
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     if ((StatusRegister & (ATA_STSREG_BSY | ATA_STSREG_ERR)) == ATA_STSREG_ERR) {
 | |
| 
 | |
|       ErrorRegister = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg1.Error);
 | |
|       if ((ErrorRegister & ATA_ERRREG_ABRT) == ATA_ERRREG_ABRT) {
 | |
|         return EFI_ABORTED;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     gBS->Stall (30);
 | |
| 
 | |
|     Delay--;
 | |
|   } while (Delay > 0);
 | |
| 
 | |
|   if (Delay == 0) {
 | |
|     return EFI_TIMEOUT;
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| /**
 | |
|   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 IdeDev              pointer pointing to IDE_BLK_IO_DEV data structure, used
 | |
|                              to record all the information of the IDE device.
 | |
|   @param DelayInMilliSeconds used to designate the timeout for the DRQ ready.
 | |
| 
 | |
|   @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
 | |
| DRDYReady2 (
 | |
|   IN  IDE_BLK_IO_DEV  *IdeDev,
 | |
|   IN  UINTN           DelayInMilliSeconds
 | |
|   )
 | |
| {
 | |
|   UINT32  Delay;
 | |
|   UINT8   AltRegister;
 | |
|   UINT8   ErrorRegister;
 | |
| 
 | |
|   Delay = (UINT32) (((DelayInMilliSeconds * STALL_1_MILLI_SECOND) / 30) + 1);
 | |
|   do {
 | |
|     AltRegister = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Alt.AltStatus);
 | |
|     //
 | |
|     //  BSY == 0 , DRDY == 1
 | |
|     //
 | |
|     if ((AltRegister & (ATA_STSREG_DRDY | ATA_STSREG_BSY)) == ATA_STSREG_DRDY) {
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     if ((AltRegister & (ATA_STSREG_BSY | ATA_STSREG_ERR)) == ATA_STSREG_ERR) {
 | |
| 
 | |
|       ErrorRegister = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg1.Error);
 | |
|       if ((ErrorRegister & ATA_ERRREG_ABRT) == ATA_ERRREG_ABRT) {
 | |
|         return EFI_ABORTED;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     gBS->Stall (30);
 | |
| 
 | |
|     Delay--;
 | |
|   } while (Delay > 0);
 | |
| 
 | |
|   if (Delay == 0) {
 | |
|     return EFI_TIMEOUT;
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| /**
 | |
|   Release resources of an IDE device before stopping it.
 | |
| 
 | |
|   @param IdeBlkIoDevice  Standard IDE device private data structure
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| ReleaseIdeResources (
 | |
|   IN  IDE_BLK_IO_DEV  *IdeBlkIoDevice
 | |
|   )
 | |
| {
 | |
|   if (IdeBlkIoDevice == NULL) {
 | |
|     return ;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Release all the resourses occupied by the IDE_BLK_IO_DEV
 | |
|   //
 | |
| 
 | |
|   if (IdeBlkIoDevice->SenseData != NULL) {
 | |
|     gBS->FreePool (IdeBlkIoDevice->SenseData);
 | |
|     IdeBlkIoDevice->SenseData = NULL;
 | |
|   }
 | |
| 
 | |
|   if (IdeBlkIoDevice->Cache != NULL) {
 | |
|     gBS->FreePool (IdeBlkIoDevice->Cache);
 | |
|     IdeBlkIoDevice->Cache = NULL;
 | |
|   }
 | |
| 
 | |
|   if (IdeBlkIoDevice->IdData != NULL) {
 | |
|     gBS->FreePool (IdeBlkIoDevice->IdData);
 | |
|     IdeBlkIoDevice->IdData = NULL;
 | |
|   }
 | |
| 
 | |
|   if (IdeBlkIoDevice->InquiryData != NULL) {
 | |
|     gBS->FreePool (IdeBlkIoDevice->InquiryData);
 | |
|     IdeBlkIoDevice->InquiryData = NULL;
 | |
|   }
 | |
| 
 | |
|   if (IdeBlkIoDevice->ControllerNameTable != NULL) {
 | |
|     FreeUnicodeStringTable (IdeBlkIoDevice->ControllerNameTable);
 | |
|     IdeBlkIoDevice->ControllerNameTable = NULL;
 | |
|   }
 | |
| 
 | |
|   if (IdeBlkIoDevice->IoPort != NULL) {
 | |
|     gBS->FreePool (IdeBlkIoDevice->IoPort);
 | |
|   }
 | |
| 
 | |
|   if (IdeBlkIoDevice->DevicePath != NULL) {
 | |
|     gBS->FreePool (IdeBlkIoDevice->DevicePath);
 | |
|   }
 | |
| 
 | |
|   if (IdeBlkIoDevice->ExitBootServiceEvent != NULL) {
 | |
|     gBS->CloseEvent (IdeBlkIoDevice->ExitBootServiceEvent);
 | |
|     IdeBlkIoDevice->ExitBootServiceEvent = NULL;
 | |
|   }
 | |
| 
 | |
|   gBS->FreePool (IdeBlkIoDevice);
 | |
|   IdeBlkIoDevice = NULL;
 | |
| 
 | |
|   return ;
 | |
| }
 | |
| /**
 | |
|   Set the calculated Best transfer mode to a detected device.
 | |
| 
 | |
|   @param IdeDev       Standard IDE device private data structure
 | |
|   @param TransferMode The device transfer mode to be set
 | |
|   @return Set transfer mode Command execute status.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| SetDeviceTransferMode (
 | |
|   IN IDE_BLK_IO_DEV       *IdeDev,
 | |
|   IN ATA_TRANSFER_MODE    *TransferMode
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
|   UINT8       DeviceSelect;
 | |
|   UINT8       SectorCount;
 | |
| 
 | |
|   DeviceSelect  = 0;
 | |
|   DeviceSelect  = (UINT8) ((IdeDev->Device) << 4);
 | |
|   SectorCount   = *((UINT8 *) TransferMode);
 | |
| 
 | |
|   //
 | |
|   // Send SET FEATURE command (sub command 0x03) to set pio mode.
 | |
|   //
 | |
|   Status = AtaNonDataCommandIn (
 | |
|             IdeDev,
 | |
|             ATA_CMD_SET_FEATURES,
 | |
|             DeviceSelect,
 | |
|             0x03,
 | |
|             SectorCount,
 | |
|             0,
 | |
|             0,
 | |
|             0
 | |
|             );
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| /**
 | |
|   Set drive parameters for devices not support PACKETS command.
 | |
| 
 | |
|   @param IdeDev          Standard IDE device private data structure
 | |
|   @param DriveParameters The device parameters to be set into the disk
 | |
|   @return SetParameters Command execute status.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| SetDriveParameters (
 | |
|   IN IDE_BLK_IO_DEV       *IdeDev,
 | |
|   IN ATA_DRIVE_PARMS      *DriveParameters
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
|   UINT8       DeviceSelect;
 | |
| 
 | |
|   DeviceSelect  = 0;
 | |
|   DeviceSelect  = (UINT8) ((IdeDev->Device) << 4);
 | |
| 
 | |
|   //
 | |
|   // Send Init drive parameters
 | |
|   //
 | |
|   Status = AtaNonDataCommandIn (
 | |
|             IdeDev,
 | |
|             ATA_CMD_INIT_DRIVE_PARAM,
 | |
|             (UINT8) (DeviceSelect + DriveParameters->Heads),
 | |
|             0,
 | |
|             DriveParameters->Sector,
 | |
|             0,
 | |
|             0,
 | |
|             0
 | |
|             );
 | |
| 
 | |
|   //
 | |
|   // Send Set Multiple parameters
 | |
|   //
 | |
|   Status = AtaNonDataCommandIn (
 | |
|             IdeDev,
 | |
|             ATA_CMD_SET_MULTIPLE_MODE,
 | |
|             DeviceSelect,
 | |
|             0,
 | |
|             DriveParameters->MultipleSector,
 | |
|             0,
 | |
|             0,
 | |
|             0
 | |
|             );
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Enable Interrupt on IDE controller.
 | |
| 
 | |
|   @param  IdeDev   Standard IDE device private data structure
 | |
| 
 | |
|   @retval  EFI_SUCCESS Enable Interrupt successfully
 | |
| **/
 | |
| EFI_STATUS
 | |
| EnableInterrupt (
 | |
|   IN IDE_BLK_IO_DEV       *IdeDev
 | |
|   )
 | |
| {
 | |
|   UINT8 DeviceControl;
 | |
| 
 | |
|   //
 | |
|   // Enable interrupt for DMA operation
 | |
|   //
 | |
|   DeviceControl = 0;
 | |
|   IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Alt.DeviceControl, DeviceControl);
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 |