mirror of https://github.com/acidanthera/audk.git
1765 lines
47 KiB
C
1765 lines
47 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 SlaveDeviceExist = FALSE;
|
|
BOOLEAN MasterDeviceExist = FALSE;
|
|
|
|
/**
|
|
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);
|
|
}
|
|
|
|
/**
|
|
TODO: Add function description
|
|
|
|
@param IdeDev TODO: add argument description
|
|
|
|
TODO: add return values
|
|
|
|
**/
|
|
BOOLEAN
|
|
BadIdeDeviceCheck (
|
|
IN IDE_BLK_IO_DEV *IdeDev
|
|
)
|
|
{
|
|
//
|
|
// check whether all registers return 0xff,
|
|
// if so, deem the channel is disabled.
|
|
//
|
|
#ifdef EFI_DEBUG
|
|
|
|
if (IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Data) != 0xff) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg1.Feature) != 0xff) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->SectorCount) != 0xff) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->SectorNumber) != 0xff) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->CylinderLsb) != 0xff) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->CylinderMsb) != 0xff) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Head) != 0xff) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg.Command) != 0xff) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Alt.AltStatus) != 0xff) {
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
#else
|
|
|
|
return FALSE;
|
|
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
/**
|
|
Read SATA registers to detect SATA disks
|
|
|
|
@param IdeDev The BLK_IO private data which specifies the IDE device
|
|
|
|
**/
|
|
EFI_STATUS
|
|
CheckPowerMode (
|
|
IDE_BLK_IO_DEV *IdeDev
|
|
)
|
|
// TODO: EFI_NOT_FOUND - add return value to function comment
|
|
// TODO: EFI_SUCCESS - add return value to function comment
|
|
// TODO: EFI_NOT_FOUND - add return value to function comment
|
|
{
|
|
UINT8 ErrorRegister;
|
|
EFI_STATUS Status;
|
|
|
|
IDEWritePortB (
|
|
IdeDev->PciIo,
|
|
IdeDev->IoPort->Head,
|
|
(UINT8) ((IdeDev->Device << 4) | 0xe0)
|
|
);
|
|
|
|
//
|
|
// Wait 31 seconds for BSY clear. BSY should be in clear state if there exists
|
|
// a device (initial state). Normally, BSY is also in clear state if there is
|
|
// no device
|
|
//
|
|
Status = WaitForBSYClear (IdeDev, 31000);
|
|
if (EFI_ERROR (Status)) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
//
|
|
// select device, read error register
|
|
//
|
|
IDEWritePortB (
|
|
IdeDev->PciIo,
|
|
IdeDev->IoPort->Head,
|
|
(UINT8) ((IdeDev->Device << 4) | 0xe0)
|
|
);
|
|
Status = DRDYReady (IdeDev, 200);
|
|
|
|
ErrorRegister = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg1.Error);
|
|
if ((ErrorRegister == 0x01) || (ErrorRegister == 0x81)) {
|
|
return EFI_SUCCESS;
|
|
} else {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
BOOLEAN SataFlag;
|
|
|
|
SataFlag = FALSE;
|
|
//
|
|
// This extra detection is for SATA disks
|
|
//
|
|
Status = CheckPowerMode (IdeDev);
|
|
if (Status == EFI_SUCCESS) {
|
|
SataFlag = TRUE;
|
|
}
|
|
|
|
//
|
|
// 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.
|
|
//
|
|
Status = DetectIDEController (IdeDev);
|
|
|
|
if ((EFI_ERROR (Status)) && !SataFlag) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
//
|
|
// Device exists. test if it is an ATA device
|
|
//
|
|
Status = ATAIdentify (IdeDev);
|
|
if (EFI_ERROR (Status)) {
|
|
//
|
|
// if not ATA device, test if it is an ATAPI device
|
|
//
|
|
Status = ATAPIIdentify (IdeDev);
|
|
if (EFI_ERROR (Status)) {
|
|
//
|
|
// if not ATAPI device either, return error.
|
|
//
|
|
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 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 ErrorReg;
|
|
UINT8 StatusReg;
|
|
UINT8 InitStatusReg;
|
|
EFI_STATUS DeviceStatus;
|
|
|
|
//
|
|
// Slave device has been detected with master device.
|
|
//
|
|
if ((IdeDev->Device) == 1) {
|
|
if (SlaveDeviceExist) {
|
|
//
|
|
// If master not exists but slave exists, slave have to wait a while
|
|
//
|
|
if (!MasterDeviceExist) {
|
|
//
|
|
// if single slave can't be detected, add delay 4s here.
|
|
//
|
|
gBS->Stall (4000000);
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
} else {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
}
|
|
|
|
//
|
|
// 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);
|
|
|
|
ErrorReg = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg1.Error);
|
|
|
|
//
|
|
// Master Error register is 0x01. D0 passed, D1 passed or not present.
|
|
// Master Error register is 0x81. D0 passed, D1 failed. Return.
|
|
// Master Error register is other value. D0 failed, D1 passed or not present..
|
|
//
|
|
if (ErrorReg == 0x01) {
|
|
MasterDeviceExist = TRUE;
|
|
DeviceStatus = EFI_SUCCESS;
|
|
} else if (ErrorReg == 0x81) {
|
|
|
|
MasterDeviceExist = TRUE;
|
|
DeviceStatus = EFI_SUCCESS;
|
|
SlaveDeviceExist = FALSE;
|
|
|
|
return DeviceStatus;
|
|
} else {
|
|
MasterDeviceExist = FALSE;
|
|
DeviceStatus = EFI_NOT_FOUND;
|
|
}
|
|
|
|
//
|
|
// Master Error register is not 0x81, Go on check Slave
|
|
//
|
|
|
|
//
|
|
// select slave
|
|
//
|
|
IDEWritePortB (
|
|
IdeDev->PciIo,
|
|
IdeDev->IoPort->Head,
|
|
(UINT8) ((1 << 4) | 0xe0)
|
|
);
|
|
|
|
gBS->Stall (300);
|
|
ErrorReg = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg1.Error);
|
|
|
|
//
|
|
// Slave Error register is not 0x01, D1 failed. Return.
|
|
//
|
|
if (ErrorReg != 0x01) {
|
|
SlaveDeviceExist = FALSE;
|
|
return DeviceStatus;
|
|
}
|
|
|
|
StatusReg = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg.Status);
|
|
|
|
//
|
|
// Most ATAPI devices don't set DRDY bit, so test with a slow but accurate
|
|
// "ATAPI TEST UNIT READY" command
|
|
//
|
|
if (((StatusReg & DRDY) == 0) && ((InitStatusReg & DRDY) == 0)) {
|
|
Status = AtapiTestUnitReady (IdeDev);
|
|
|
|
//
|
|
// Still fail, Slave doesn't exist.
|
|
//
|
|
if (EFI_ERROR (Status)) {
|
|
SlaveDeviceExist = FALSE;
|
|
return DeviceStatus;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Error reg is 0x01 and DRDY is ready,
|
|
// or ATAPI test unit ready success,
|
|
// or init Slave status DRDY is ready
|
|
// Slave exists.
|
|
//
|
|
SlaveDeviceExist = TRUE;
|
|
|
|
return DeviceStatus;
|
|
|
|
}
|
|
|
|
/**
|
|
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 & (DRQ | BSY)) == 0) {
|
|
break;
|
|
}
|
|
|
|
if ((StatusRegister & (BSY | ERR)) == ERR) {
|
|
|
|
ErrorRegister = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg1.Error);
|
|
if ((ErrorRegister & ABRT_ERR) == ABRT_ERR) {
|
|
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 & (DRQ | BSY)) == 0) {
|
|
break;
|
|
}
|
|
|
|
if ((AltRegister & (BSY | ERR)) == ERR) {
|
|
|
|
ErrorRegister = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg1.Error);
|
|
if ((ErrorRegister & ABRT_ERR) == ABRT_ERR) {
|
|
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 & (BSY | DRQ)) == DRQ) {
|
|
break;
|
|
}
|
|
|
|
if ((StatusRegister & (BSY | ERR)) == ERR) {
|
|
|
|
ErrorRegister = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg1.Error);
|
|
if ((ErrorRegister & ABRT_ERR) == ABRT_ERR) {
|
|
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 & (BSY | DRQ)) == DRQ) {
|
|
break;
|
|
}
|
|
|
|
if ((AltRegister & (BSY | ERR)) == ERR) {
|
|
|
|
ErrorRegister = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg1.Error);
|
|
if ((ErrorRegister & ABRT_ERR) == ABRT_ERR) {
|
|
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 & 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 & 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 & (DRDY | BSY)) == DRDY) {
|
|
break;
|
|
}
|
|
|
|
if ((StatusRegister & (BSY | ERR)) == ERR) {
|
|
|
|
ErrorRegister = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg1.Error);
|
|
if ((ErrorRegister & ABRT_ERR) == ABRT_ERR) {
|
|
return EFI_ABORTED;
|
|
}
|
|
}
|
|
|
|
gBS->Stall (15);
|
|
|
|
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 & (DRDY | BSY)) == DRDY) {
|
|
break;
|
|
}
|
|
|
|
if ((AltRegister & (BSY | ERR)) == ERR) {
|
|
|
|
ErrorRegister = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg1.Error);
|
|
if ((ErrorRegister & ABRT_ERR) == ABRT_ERR) {
|
|
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);
|
|
}
|
|
|
|
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,
|
|
SET_FEATURES_CMD,
|
|
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
|
|
//
|
|
Status = WaitForBSYClear (IdeDev, ATATIMEOUT);
|
|
if (EFI_ERROR (Status)) {
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
StatusRegister = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg.Status);
|
|
if ((StatusRegister & ERR) == 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 & ERR) == 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 = AtaPioDataIn (
|
|
IdeDev,
|
|
NULL,
|
|
0,
|
|
INIT_DRIVE_PARAM_CMD,
|
|
(UINT8) (DeviceSelect + DriveParameters->Heads),
|
|
DriveParameters->Sector,
|
|
0,
|
|
0,
|
|
0
|
|
);
|
|
|
|
//
|
|
// Send Set Multiple parameters
|
|
//
|
|
Status = AtaPioDataIn (
|
|
IdeDev,
|
|
NULL,
|
|
0,
|
|
SET_MULTIPLE_MODE_CMD,
|
|
DeviceSelect,
|
|
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;
|
|
}
|