git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@3322 6f19259b-4bc3-4df7-8a09-765794883524
		
			
				
	
	
		
			1825 lines
		
	
	
		
			50 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1825 lines
		
	
	
		
			50 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
|   Copyright (c) 2006, Intel Corporation
 | |
|   All rights reserved. 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;
 | |
| 
 | |
| /**
 | |
|   TODO: Add function description
 | |
| 
 | |
|   @param  PciIo TODO: add argument description
 | |
|   @param  Port TODO: add argument description
 | |
| 
 | |
|   TODO: add return values
 | |
| 
 | |
| **/
 | |
| 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,
 | |
|   IN  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);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   TODO: Add function description
 | |
| 
 | |
|   @param  PciIo TODO: add argument description
 | |
|   @param  Port TODO: add argument description
 | |
|   @param  Data TODO: add argument description
 | |
| 
 | |
|   TODO: add return values
 | |
| 
 | |
| **/
 | |
| 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
 | |
|               );
 | |
| 
 | |
| }
 | |
| 
 | |
| /**
 | |
|   TODO: Add function description
 | |
| 
 | |
|   @param  PciIo TODO: add argument description
 | |
|   @param  Port TODO: add argument description
 | |
|   @param  Data TODO: add argument description
 | |
| 
 | |
|   TODO: add return values
 | |
| 
 | |
| **/
 | |
| 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);
 | |
| }
 | |
| 
 | |
| //
 | |
| // GetIdeRegistersBaseAddr
 | |
| //
 | |
| /**
 | |
|   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
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| GetIdeRegistersBaseAddr (
 | |
|   IN  EFI_PCI_IO_PROTOCOL         *PciIo,
 | |
|   OUT IDE_REGISTERS_BASE_ADDR     *IdeRegsBaseAddr
 | |
|   )
 | |
| // TODO:    EFI_UNSUPPORTED - add return value to function comment
 | |
| // TODO:    EFI_UNSUPPORTED - add return value to function comment
 | |
| // TODO:    EFI_SUCCESS - add return value to function comment
 | |
| {
 | |
|   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
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| ReassignIdeResources (
 | |
|   IN  IDE_BLK_IO_DEV  *IdeDev
 | |
|   )
 | |
| // TODO:    EFI_SUCCESS - add return value to function comment
 | |
| {
 | |
|   EFI_STATUS              Status;
 | |
|   IDE_REGISTERS_BASE_ADDR IdeRegsBaseAddr[IdeMaxChannel];
 | |
|   UINT16                  CommandBlockBaseAddr;
 | |
|   UINT16                  ControlBlockBaseAddr;
 | |
| 
 | |
|   //
 | |
|   // 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;
 | |
| }
 | |
| 
 | |
| //
 | |
| // DiscoverIdeDevice
 | |
| //
 | |
| /**
 | |
|   Detect if there is disk connected to this port
 | |
| 
 | |
|   @param  IdeDev The BLK_IO private data which specifies the IDE device
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| DiscoverIdeDevice (
 | |
|   IN IDE_BLK_IO_DEV *IdeDev
 | |
|   )
 | |
| // TODO:    EFI_NOT_FOUND - add return value to function comment
 | |
| // TODO:    EFI_NOT_FOUND - add return value to function comment
 | |
| // TODO:    EFI_SUCCESS - add return value to function comment
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
| 
 | |
|   //
 | |
|   // 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 == FALSE) {
 | |
|     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
 | |
|   //
 | |
|   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.
 | |
| 
 | |
|   @retval EFI_SUCCESS Completed Successfully.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| InitializeIDEChannelData (
 | |
|   VOID
 | |
|   )
 | |
| {
 | |
|   ChannelDeviceDetected = FALSE;
 | |
|   MasterDeviceExist = FALSE;
 | |
|   MasterDeviceType  = 0xff;
 | |
|   SlaveDeviceExist  = FALSE;
 | |
|   SlaveDeviceType   = 0xff;
 | |
|   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[in] *IdeDev
 | |
|   pointer pointing to IDE_BLK_IO_DEV data structure, used
 | |
|   to record all the information of the IDE device.
 | |
| 
 | |
|   @retval TRUE
 | |
|   successfully detects device.
 | |
| 
 | |
|   @retval FALSE
 | |
|   any failure during detection process will return this
 | |
|   value.
 | |
| 
 | |
|   @note
 | |
|   TODO:    EFI_SUCCESS - add return value to function comment
 | |
|   TODO:    EFI_NOT_FOUND - add return value to function comment
 | |
| 
 | |
| **/
 | |
| 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;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   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[in] *IdeDev
 | |
|   pointer pointing to IDE_BLK_IO_DEV data structure, used
 | |
|   to record all the information of the IDE device.
 | |
| 
 | |
|   @param[in] 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
 | |
|   )
 | |
| // TODO: function comment is missing 'Routine Description:'
 | |
| // TODO: function comment is missing 'Arguments:'
 | |
| // TODO:    IdeDev - add argument and description to function comment
 | |
| // TODO:    TimeoutInMilliSeconds - add argument and description to function comment
 | |
| // TODO:    EFI_ABORTED - add return value to function comment
 | |
| {
 | |
|   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);
 | |
| 
 | |
|   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[in] *IdeDev
 | |
|   pointer pointing to IDE_BLK_IO_DEV data structure, used
 | |
|   to record all the information of the IDE device.
 | |
| 
 | |
|   @param[in] 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
 | |
|   )
 | |
| // TODO: function comment is missing 'Routine Description:'
 | |
| // TODO: function comment is missing 'Arguments:'
 | |
| // TODO:    IdeDev - add argument and description to function comment
 | |
| // TODO:    TimeoutInMilliSeconds - add argument and description to function comment
 | |
| // TODO:    EFI_ABORTED - add return value to function comment
 | |
| {
 | |
|   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);
 | |
| 
 | |
|   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[in] IDE_BLK_IO_DEV  IN    *IdeDev
 | |
|   pointer pointing to IDE_BLK_IO_DEV data structure,used
 | |
|   to record all the information of the IDE device.
 | |
| 
 | |
|   @param[in] UINTN     IN    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
 | |
|   )
 | |
| // TODO: function comment is missing 'Routine Description:'
 | |
| // TODO: function comment is missing 'Arguments:'
 | |
| // TODO:    IdeDev - add argument and description to function comment
 | |
| // TODO:    TimeoutInMilliSeconds - add argument and description to function comment
 | |
| {
 | |
|   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);
 | |
| 
 | |
|   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[in] IDE_BLK_IO_DEV  IN    *IdeDev
 | |
|   pointer pointing to IDE_BLK_IO_DEV data structure, used
 | |
|   to record all the information of the IDE device.
 | |
| 
 | |
|   @param[in] UINTN     IN    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
 | |
|   )
 | |
| // TODO: function comment is missing 'Routine Description:'
 | |
| // TODO: function comment is missing 'Arguments:'
 | |
| // TODO:    IdeDev - add argument and description to function comment
 | |
| // TODO:    TimeoutInMilliSeconds - add argument and description to function comment
 | |
| {
 | |
|   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);
 | |
| 
 | |
|   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[in] IDE_BLK_IO_DEV  IN    *IdeDev
 | |
|   pointer pointing to IDE_BLK_IO_DEV data structure, used
 | |
|   to record all the information of the IDE device.
 | |
| 
 | |
|   @param[in] UINTN     IN    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
 | |
|   )
 | |
| // TODO: function comment is missing 'Routine Description:'
 | |
| // TODO: function comment is missing 'Arguments:'
 | |
| // TODO:    IdeDev - add argument and description to function comment
 | |
| // TODO:    TimeoutInMilliSeconds - add argument and description to function comment
 | |
| {
 | |
|   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);
 | |
| 
 | |
|   if (Delay == 0) {
 | |
|     return EFI_TIMEOUT;
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| //
 | |
| // WaitForBSYClear2
 | |
| //
 | |
| /**
 | |
|   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[in] IDE_BLK_IO_DEV  IN    *IdeDev
 | |
|   pointer pointing to IDE_BLK_IO_DEV data structure, used
 | |
|   to record all the information of the IDE device.
 | |
| 
 | |
|   @param[in] UINTN     IN    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
 | |
|   )
 | |
| // TODO: function comment is missing 'Routine Description:'
 | |
| // TODO: function comment is missing 'Arguments:'
 | |
| // TODO:    IdeDev - add argument and description to function comment
 | |
| // TODO:    TimeoutInMilliSeconds - add argument and description to function comment
 | |
| {
 | |
|   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);
 | |
| 
 | |
|   if (Delay == 0) {
 | |
|     return EFI_TIMEOUT;
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| //
 | |
| // DRDYReady
 | |
| //
 | |
| /**
 | |
|   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[in] IDE_BLK_IO_DEV  IN    *IdeDev
 | |
|   pointer pointing to IDE_BLK_IO_DEV data structure, used
 | |
|   to record all the information of the IDE device.
 | |
| 
 | |
|   @param[in] UINTN     IN    TimeoutInMilliSeconds
 | |
|   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
 | |
|   )
 | |
| // TODO: function comment is missing 'Routine Description:'
 | |
| // TODO: function comment is missing 'Arguments:'
 | |
| // TODO:    IdeDev - add argument and description to function comment
 | |
| // TODO:    DelayInMilliSeconds - add argument and description to function comment
 | |
| // TODO:    EFI_ABORTED - add return value to function comment
 | |
| {
 | |
|   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);
 | |
| 
 | |
|   if (Delay == 0) {
 | |
|     return EFI_TIMEOUT;
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| //
 | |
| // DRDYReady2
 | |
| //
 | |
| /**
 | |
|   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[in] IDE_BLK_IO_DEV  IN    *IdeDev
 | |
|   pointer pointing to IDE_BLK_IO_DEV data structure, used
 | |
|   to record all the information of the IDE device.
 | |
| 
 | |
|   @param[in] UINTN     IN    TimeoutInMilliSeconds
 | |
|   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
 | |
|   )
 | |
| // TODO: function comment is missing 'Routine Description:'
 | |
| // TODO: function comment is missing 'Arguments:'
 | |
| // TODO:    IdeDev - add argument and description to function comment
 | |
| // TODO:    DelayInMilliSeconds - add argument and description to function comment
 | |
| // TODO:    EFI_ABORTED - add return value to function comment
 | |
| {
 | |
|   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);
 | |
| 
 | |
|   if (Delay == 0) {
 | |
|     return EFI_TIMEOUT;
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| //
 | |
| // SwapStringChars
 | |
| //
 | |
| /**
 | |
|   This function is a helper function used to change the char order in a
 | |
|   string. It is designed specially for the PrintAtaModuleName() function.
 | |
|   After the IDE device is detected, the IDE driver gets the device module
 | |
|   name by sending ATA command called ATA Identify Command or ATAPI
 | |
|   Identify Command to the specified IDE device. The module name returned
 | |
|   is a string of ASCII characters: the first character is bit8--bit15
 | |
|   of the first word, the second character is BIT0--bit7 of the first word
 | |
|   and so on. Thus the string can not be print directly before it is
 | |
|   preprocessed by this func to change the order of characters in
 | |
|   each word in the string.
 | |
| 
 | |
|   @param[in] CHAR8 IN    *Destination
 | |
|   Indicates the destination string.
 | |
| 
 | |
|   @param[in] CHAR8 IN    *Source
 | |
|   Indicates the source string.
 | |
| 
 | |
|   @param[in] UINT8 IN    Size
 | |
|   the length of the string
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| SwapStringChars (
 | |
|   IN CHAR8  *Destination,
 | |
|   IN CHAR8  *Source,
 | |
|   IN UINT32 Size
 | |
|   )
 | |
| {
 | |
|   UINT32  Index;
 | |
|   CHAR8   Temp;
 | |
| 
 | |
|   for (Index = 0; Index < Size; Index += 2) {
 | |
| 
 | |
|     Temp                    = Source[Index + 1];
 | |
|     Destination[Index + 1]  = Source[Index];
 | |
|     Destination[Index]      = Temp;
 | |
|   }
 | |
| }
 | |
| 
 | |
| //
 | |
| // ReleaseIdeResources
 | |
| //
 | |
| /**
 | |
|   Release resources of an IDE device before stopping it.
 | |
| 
 | |
|   @param[in] *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->pIdData != NULL) {
 | |
|     gBS->FreePool (IdeBlkIoDevice->pIdData);
 | |
|     IdeBlkIoDevice->pIdData = NULL;
 | |
|   }
 | |
| 
 | |
|   if (IdeBlkIoDevice->pInquiryData != NULL) {
 | |
|     gBS->FreePool (IdeBlkIoDevice->pInquiryData);
 | |
|     IdeBlkIoDevice->pInquiryData = 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 ;
 | |
| }
 | |
| 
 | |
| //
 | |
| // SetDeviceTransferMode
 | |
| //
 | |
| /**
 | |
|   Set the calculated Best transfer mode to a detected device
 | |
| 
 | |
|   @param[in] *IdeDev       Standard IDE device private data structure
 | |
|   @param[in] *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
 | |
|   )
 | |
| // TODO: function comment is missing 'Routine Description:'
 | |
| {
 | |
|   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;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Send ATA command into device with NON_DATA protocol
 | |
| 
 | |
|   @param  IdeDev Standard IDE device private data structure
 | |
|   @param  AtaCommand The ATA command to be sent
 | |
|   @param  Device The value in Device register
 | |
|   @param  Feature The value in Feature register
 | |
|   @param  SectorCount The value in SectorCount register
 | |
|   @param  LbaLow The value in LBA_LOW register
 | |
|   @param  LbaMiddle The value in LBA_MIDDLE register
 | |
|   @param  LbaHigh The value in LBA_HIGH register
 | |
| 
 | |
|   @retval  EFI_SUCCESS Reading succeed
 | |
|   @retval  EFI_ABORTED Command failed
 | |
|   @retval  EFI_DEVICE_ERROR Device status error
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| AtaNonDataCommandIn (
 | |
|   IN  IDE_BLK_IO_DEV  *IdeDev,
 | |
|   IN  UINT8           AtaCommand,
 | |
|   IN  UINT8           Device,
 | |
|   IN  UINT8           Feature,
 | |
|   IN  UINT8           SectorCount,
 | |
|   IN  UINT8           LbaLow,
 | |
|   IN  UINT8           LbaMiddle,
 | |
|   IN  UINT8           LbaHigh
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
|   UINT8       StatusRegister;
 | |
| 
 | |
|   Status = WaitForBSYClear (IdeDev, ATATIMEOUT);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Select device (bit4), set LBA mode(bit6) (use 0xe0 for compatibility)
 | |
|   //
 | |
|   IDEWritePortB (
 | |
|     IdeDev->PciIo,
 | |
|     IdeDev->IoPort->Head,
 | |
|     (UINT8) ((IdeDev->Device << 4) | 0xe0)
 | |
|     );
 | |
| 
 | |
|   //
 | |
|   // ATA commands for ATA device must be issued when DRDY is set
 | |
|   //
 | |
|   Status = DRDYReady (IdeDev, ATATIMEOUT);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Pass parameter into device register block
 | |
|   //
 | |
|   IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Head, Device);
 | |
|   IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Reg1.Feature, Feature);
 | |
|   IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->SectorCount, SectorCount);
 | |
|   IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->SectorNumber, LbaLow);
 | |
|   IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->CylinderLsb, LbaMiddle);
 | |
|   IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->CylinderMsb, LbaHigh);
 | |
| 
 | |
|   //
 | |
|   // Send command via Command Register
 | |
|   //
 | |
|   IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Reg.Command, AtaCommand);
 | |
| 
 | |
|   //
 | |
|   // Wait for command completion
 | |
|   // For ATAPI_SMART_CMD, we may need more timeout to let device
 | |
|   // adjust internal states.
 | |
|   //
 | |
|   if (AtaCommand == ATA_CMD_SMART) {
 | |
|     Status = WaitForBSYClear (IdeDev, ATASMARTTIMEOUT);
 | |
|   } else {
 | |
|     Status = WaitForBSYClear (IdeDev, ATATIMEOUT);
 | |
|   }
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
| 
 | |
|   StatusRegister = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg.Status);
 | |
|   if ((StatusRegister & ATA_STSREG_ERR) == ATA_STSREG_ERR) {
 | |
|     //
 | |
|     // Failed to execute command, abort operation
 | |
|     //
 | |
|     return EFI_ABORTED;
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Send ATA Ext command into device with NON_DATA protocol
 | |
| 
 | |
|   @param  IdeDev Standard IDE device private data structure
 | |
|   @param  AtaCommand The ATA command to be sent
 | |
|   @param  Device The value in Device register
 | |
|   @param  Feature The value in Feature register
 | |
|   @param  SectorCount The value in SectorCount register
 | |
|   @param  LbaAddress The LBA address in 48-bit mode
 | |
| 
 | |
|   @retval  EFI_SUCCESS Reading succeed
 | |
|   @retval  EFI_ABORTED Command failed
 | |
|   @retval  EFI_DEVICE_ERROR Device status error
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| AtaNonDataCommandInExt (
 | |
|   IN  IDE_BLK_IO_DEV  *IdeDev,
 | |
|   IN  UINT8           AtaCommand,
 | |
|   IN  UINT8           Device,
 | |
|   IN  UINT16          Feature,
 | |
|   IN  UINT16          SectorCount,
 | |
|   IN  EFI_LBA         LbaAddress
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
|   UINT8       StatusRegister;
 | |
|   UINT8       SectorCount8;
 | |
|   UINT8       Feature8;
 | |
|   UINT8       LbaLow;
 | |
|   UINT8       LbaMid;
 | |
|   UINT8       LbaHigh;
 | |
| 
 | |
|   Status = WaitForBSYClear (IdeDev, ATATIMEOUT);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Select device (bit4), set LBA mode(bit6) (use 0xe0 for compatibility)
 | |
|   //
 | |
|   IDEWritePortB (
 | |
|     IdeDev->PciIo,
 | |
|     IdeDev->IoPort->Head,
 | |
|     (UINT8) ((IdeDev->Device << 4) | 0xe0)
 | |
|     );
 | |
| 
 | |
|   //
 | |
|   // ATA commands for ATA device must be issued when DRDY is set
 | |
|   //
 | |
|   Status = DRDYReady (IdeDev, ATATIMEOUT);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Pass parameter into device register block
 | |
|   //
 | |
|   IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Head, Device);
 | |
| 
 | |
|   //
 | |
|   // Fill the feature register, which is a two-byte FIFO. Need write twice.
 | |
|   //
 | |
|   Feature8 = (UINT8) (Feature >> 8);
 | |
|   IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Reg1.Feature, Feature8);
 | |
| 
 | |
|   Feature8 = (UINT8) Feature;
 | |
|   IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Reg1.Feature, Feature8);
 | |
| 
 | |
|   //
 | |
|   // Fill the sector count register, which is a two-byte FIFO. Need write twice.
 | |
|   //
 | |
|   SectorCount8 = (UINT8) (SectorCount >> 8);
 | |
|   IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->SectorCount, SectorCount8);
 | |
| 
 | |
|   SectorCount8 = (UINT8) SectorCount;
 | |
|   IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->SectorCount, SectorCount8);
 | |
| 
 | |
|   //
 | |
|   // Fill the start LBA registers, which are also two-byte FIFO
 | |
|   //
 | |
|   LbaLow  = (UINT8) RShiftU64 (LbaAddress, 24);
 | |
|   LbaMid  = (UINT8) RShiftU64 (LbaAddress, 32);
 | |
|   LbaHigh = (UINT8) RShiftU64 (LbaAddress, 40);
 | |
|   IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->SectorNumber, LbaLow);
 | |
|   IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->CylinderLsb, LbaMid);
 | |
|   IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->CylinderMsb, LbaHigh);
 | |
| 
 | |
|   LbaLow  = (UINT8) LbaAddress;
 | |
|   LbaMid  = (UINT8) RShiftU64 (LbaAddress, 8);
 | |
|   LbaHigh = (UINT8) RShiftU64 (LbaAddress, 16);
 | |
|   IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->SectorNumber, LbaLow);
 | |
|   IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->CylinderLsb, LbaMid);
 | |
|   IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->CylinderMsb, LbaHigh);
 | |
| 
 | |
|   //
 | |
|   // Send command via Command Register
 | |
|   //
 | |
|   IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Reg.Command, AtaCommand);
 | |
| 
 | |
|   //
 | |
|   // Wait for command completion
 | |
|   //
 | |
|   Status = WaitForBSYClear (IdeDev, ATATIMEOUT);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
| 
 | |
|   StatusRegister = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg.Status);
 | |
|   if ((StatusRegister & ATA_STSREG_ERR) == ATA_STSREG_ERR) {
 | |
|     //
 | |
|     // Failed to execute command, abort operation
 | |
|     //
 | |
|     return EFI_ABORTED;
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| //
 | |
| // SetDriveParameters
 | |
| //
 | |
| /**
 | |
|   Set drive parameters for devices not support PACKETS command
 | |
| 
 | |
|   @param[in] IdeDev       Standard IDE device private data structure
 | |
|   @param[in] 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;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   TODO: Add function description
 | |
| 
 | |
|   @param  IdeDev TODO: add argument description
 | |
| 
 | |
|   @retval  EFI_SUCCESS TODO: Add description for return value
 | |
| 
 | |
| **/
 | |
| 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;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Clear pending IDE interrupt before OS loader/kernel take control of the IDE device.
 | |
| 
 | |
|   @param[in]  Event   Pointer to this event
 | |
|   @param[in]  Context Event hanlder private data
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| EFIAPI
 | |
| ClearInterrupt (
 | |
|   IN EFI_EVENT  Event,
 | |
|   IN VOID       *Context
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS      Status;
 | |
|   UINT64          IoPortForBmis;
 | |
|   UINT8           RegisterValue;
 | |
|   IDE_BLK_IO_DEV  *IdeDev;
 | |
| 
 | |
|   //
 | |
|   // Get our context
 | |
|   //
 | |
|   IdeDev = (IDE_BLK_IO_DEV *) Context;
 | |
| 
 | |
|   //
 | |
|   // Obtain IDE IO port registers' base addresses
 | |
|   //
 | |
|   Status = ReassignIdeResources (IdeDev);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Check whether interrupt is pending
 | |
|   //
 | |
| 
 | |
|   //
 | |
|   // Reset IDE device to force it de-assert interrupt pin
 | |
|   // Note: this will reset all devices on this IDE channel
 | |
|   //
 | |
|   AtaSoftReset (IdeDev);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Get base address of IDE Bus Master Status Regsiter
 | |
|   //
 | |
|   if (IdePrimary == IdeDev->Channel) {
 | |
|     IoPortForBmis = IdeDev->IoPort->BusMasterBaseAddr + BMISP_OFFSET;
 | |
|   } else {
 | |
|     if (IdeSecondary == IdeDev->Channel) {
 | |
|       IoPortForBmis = IdeDev->IoPort->BusMasterBaseAddr + BMISS_OFFSET;
 | |
|     } else {
 | |
|       return;
 | |
|     }
 | |
|   }
 | |
|   //
 | |
|   // Read BMIS register and clear ERROR and INTR bit
 | |
|   //
 | |
|   IdeDev->PciIo->Io.Read (
 | |
|                       IdeDev->PciIo,
 | |
|                       EfiPciIoWidthUint8,
 | |
|                       EFI_PCI_IO_PASS_THROUGH_BAR,
 | |
|                       IoPortForBmis,
 | |
|                       1,
 | |
|                       &RegisterValue
 | |
|                       );
 | |
| 
 | |
|   RegisterValue |= (BMIS_INTERRUPT | BMIS_ERROR);
 | |
| 
 | |
|   IdeDev->PciIo->Io.Write (
 | |
|                       IdeDev->PciIo,
 | |
|                       EfiPciIoWidthUint8,
 | |
|                       EFI_PCI_IO_PASS_THROUGH_BAR,
 | |
|                       IoPortForBmis,
 | |
|                       1,
 | |
|                       &RegisterValue
 | |
|                       );
 | |
| 
 | |
|   //
 | |
|   // Select the other device on this channel to ensure this device to release the interrupt pin
 | |
|   //
 | |
|   if (IdeDev->Device == 0) {
 | |
|     RegisterValue = (1 << 4) | 0xe0;
 | |
|   } else {
 | |
|     RegisterValue = (0 << 4) | 0xe0;
 | |
|   }
 | |
|   IDEWritePortB (
 | |
|     IdeDev->PciIo,
 | |
|     IdeDev->IoPort->Head,
 | |
|     RegisterValue
 | |
|     );
 | |
| 
 | |
| }
 |