/*++ | |
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. | |
Module Name: | |
ide.c | |
Abstract: | |
Revision History | |
--*/ | |
#include "idebus.h" | |
BOOLEAN SlaveDeviceExist = FALSE; | |
BOOLEAN MasterDeviceExist = FALSE; | |
UINT8 | |
IDEReadPortB ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN UINT16 Port | |
) | |
/*++ | |
Routine Description: | |
TODO: Add function description | |
Arguments: | |
PciIo - TODO: add argument description | |
Port - TODO: add argument description | |
Returns: | |
TODO: add return values | |
--*/ | |
{ | |
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; | |
} | |
VOID | |
IDEReadPortWMultiple ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN UINT16 Port, | |
IN UINTN Count, | |
IN VOID *Buffer | |
) | |
/*++ | |
Routine Description: | |
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 | |
Arguments: | |
PciIo - Pointer to the EFI_PCI_IO instance | |
Port - IO port to read | |
Count - No. of UINT16's to read | |
Buffer - Pointer to the data buffer for read | |
++*/ | |
// TODO: function comment should end with '--*/' | |
// TODO: function comment is missing 'Returns:' | |
{ | |
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); | |
} | |
VOID | |
IDEWritePortB ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN UINT16 Port, | |
IN UINT8 Data | |
) | |
/*++ | |
Routine Description: | |
TODO: Add function description | |
Arguments: | |
PciIo - TODO: add argument description | |
Port - TODO: add argument description | |
Data - TODO: add argument description | |
Returns: | |
TODO: add return values | |
--*/ | |
{ | |
// | |
// perform 1-byte data write to register | |
// | |
PciIo->Io.Write ( | |
PciIo, | |
EfiPciIoWidthUint8, | |
EFI_PCI_IO_PASS_THROUGH_BAR, | |
(UINT64) Port, | |
1, | |
&Data | |
); | |
} | |
VOID | |
IDEWritePortW ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN UINT16 Port, | |
IN UINT16 Data | |
) | |
/*++ | |
Routine Description: | |
TODO: Add function description | |
Arguments: | |
PciIo - TODO: add argument description | |
Port - TODO: add argument description | |
Data - TODO: add argument description | |
Returns: | |
TODO: add return values | |
--*/ | |
{ | |
// | |
// perform 1-word data write to register | |
// | |
PciIo->Io.Write ( | |
PciIo, | |
EfiPciIoWidthUint16, | |
EFI_PCI_IO_PASS_THROUGH_BAR, | |
(UINT64) Port, | |
1, | |
&Data | |
); | |
} | |
VOID | |
IDEWritePortWMultiple ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN UINT16 Port, | |
IN UINTN Count, | |
IN VOID *Buffer | |
) | |
/*++ | |
Routine Description: | |
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 | |
Arguments: | |
PciIo - Pointer to the EFI_PCI_IO instance | |
Port - IO port to read | |
Count - No. of UINT16's to read | |
Buffer - Pointer to the data buffer for read | |
++*/ | |
// TODO: function comment should end with '--*/' | |
// TODO: function comment is missing 'Returns:' | |
{ | |
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); | |
} | |
BOOLEAN | |
BadIdeDeviceCheck ( | |
IN IDE_BLK_IO_DEV *IdeDev | |
) | |
/*++ | |
Routine Description: | |
TODO: Add function description | |
Arguments: | |
IdeDev - TODO: add argument description | |
Returns: | |
TODO: add return values | |
--*/ | |
{ | |
// | |
// 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 | |
// | |
EFI_STATUS | |
GetIdeRegistersBaseAddr ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
OUT IDE_REGISTERS_BASE_ADDR *IdeRegsBaseAddr | |
) | |
/*++ | |
Routine Description: | |
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. | |
___________________________________________ | |
| | Command Block | Control Block | | |
| Channel | Registers | Registers | | |
|___________|_______________|_______________| | |
| Primary | 1F0h - 1F7h | 3F6h - 3F7h | | |
|___________|_______________|_______________| | |
| Secondary | 170h - 177h | 376h - 377h | | |
|___________|_______________|_______________| | |
Table 1. Compatibility resource mappings | |
b) In Native-PCI mode, IDE registers are mapped into IO space using the BARs | |
in IDE controller's PCI Configuration Space, shown in the Table 2 below. | |
___________________________________________________ | |
| | Command Block | Control Block | | |
| Channel | Registers | Registers | | |
|___________|___________________|___________________| | |
| Primary | BAR at offset 0x10| BAR at offset 0x14| | |
|___________|___________________|___________________| | |
| Secondary | BAR at offset 0x18| BAR at offset 0x1C| | |
|___________|___________________|___________________| | |
Table 2. BARs for Register Mapping | |
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. | |
Arguments: | |
PciIo - Pointer to the EFI_PCI_IO_PROTOCOL instance | |
IdeRegsBaseAddr - Pointer to IDE_REGISTERS_BASE_ADDR to | |
receive IDE IO port registers' base addresses | |
Returns: | |
--*/ | |
// 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; | |
} | |
EFI_STATUS | |
ReassignIdeResources ( | |
IN IDE_BLK_IO_DEV *IdeDev | |
) | |
/*++ | |
Routine Description: | |
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. | |
Arguments: | |
IdeDev - The BLK_IO private data which specifies the IDE device | |
++*/ | |
// TODO: function comment should end with '--*/' | |
// TODO: function comment is missing 'Returns:' | |
// 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; | |
} | |
EFI_STATUS | |
CheckPowerMode ( | |
IDE_BLK_IO_DEV *IdeDev | |
) | |
/*++ | |
Routine Description: | |
Read SATA registers to detect SATA disks | |
Arguments: | |
IdeDev - The BLK_IO private data which specifies the IDE device | |
++*/ | |
// TODO: function comment should end with '--*/' | |
// TODO: function comment is missing 'Returns:' | |
// 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 | |
// | |
EFI_STATUS | |
DiscoverIdeDevice ( | |
IN IDE_BLK_IO_DEV *IdeDev | |
) | |
/*++ | |
Routine Description: | |
Detect if there is disk connected to this port | |
Arguments: | |
IdeDev - The BLK_IO private data which specifies the IDE device | |
++*/ | |
// TODO: function comment should end with '--*/' | |
// TODO: function comment is missing 'Returns:' | |
// 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; | |
} | |
EFI_STATUS | |
DetectIDEController ( | |
IN IDE_BLK_IO_DEV *IdeDev | |
) | |
/*++ | |
Name: DetectIDEController | |
Purpose: | |
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. | |
Parameters: | |
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. | |
Returns: | |
TRUE | |
successfully detects device. | |
FALSE | |
any failure during detection process will return this | |
value. | |
Notes: | |
--*/ | |
// TODO: function comment is missing 'Routine Description:' | |
// TODO: function comment is missing 'Arguments:' | |
// TODO: IdeDev - add argument and description to function comment | |
// TODO: EFI_SUCCESS - add return value to function comment | |
// TODO: EFI_NOT_FOUND - add return value to function comment | |
{ | |
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; | |
} | |
EFI_STATUS | |
DRQClear ( | |
IN IDE_BLK_IO_DEV *IdeDev, | |
IN UINTN TimeoutInMilliSeconds | |
) | |
/*++ | |
Name: DRQClear | |
Purpose: | |
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. | |
Parameters: | |
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. | |
UINTN IN TimeoutInMilliSeconds | |
used to designate the timeout for the DRQ clear. | |
Returns: | |
EFI_SUCCESS | |
DRQ bit clear within the time out. | |
EFI_TIMEOUT | |
DRQ bit not clear within the time out. | |
Notes: | |
Read Status Register will clear interrupt status. | |
--*/ | |
// 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; | |
} | |
EFI_STATUS | |
DRQClear2 ( | |
IN IDE_BLK_IO_DEV *IdeDev, | |
IN UINTN TimeoutInMilliSeconds | |
) | |
/*++ | |
Name: DRQClear2 | |
Purpose: | |
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. | |
Parameters: | |
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. | |
UINTN IN TimeoutInMilliSeconds | |
used to designate the timeout for the DRQ clear. | |
Returns: | |
EFI_SUCCESS | |
DRQ bit clear within the time out. | |
EFI_TIMEOUT | |
DRQ bit not clear within the time out. | |
Notes: | |
Read Alternate Status Register will not clear interrupt status. | |
--*/ | |
// 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; | |
} | |
EFI_STATUS | |
DRQReady ( | |
IN IDE_BLK_IO_DEV *IdeDev, | |
IN UINTN TimeoutInMilliSeconds | |
) | |
/*++ | |
Name: DRQReady | |
Purpose: | |
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. | |
Parameters: | |
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. | |
UINTN IN TimeoutInMilliSeconds | |
used to designate the timeout for the DRQ ready. | |
Returns: | |
EFI_SUCCESS | |
DRQ bit set within the time out. | |
EFI_TIMEOUT | |
DRQ bit not set within the time out. | |
EFI_ABORTED | |
DRQ bit not set caused by the command abort. | |
Notes: | |
Read Status Register will clear interrupt status. | |
--*/ | |
// 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; | |
} | |
EFI_STATUS | |
DRQReady2 ( | |
IN IDE_BLK_IO_DEV *IdeDev, | |
IN UINTN TimeoutInMilliSeconds | |
) | |
/*++ | |
Name: DRQReady2 | |
Purpose: | |
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. | |
Parameters: | |
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. | |
UINTN IN TimeoutInMilliSeconds | |
used to designate the timeout for the DRQ ready. | |
Returns: | |
EFI_SUCCESS | |
DRQ bit set within the time out. | |
EFI_TIMEOUT | |
DRQ bit not set within the time out. | |
EFI_ABORTED | |
DRQ bit not set caused by the command abort. | |
Notes: | |
Read Alternate Status Register will not clear interrupt status. | |
--*/ | |
// 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; | |
} | |
EFI_STATUS | |
WaitForBSYClear ( | |
IN IDE_BLK_IO_DEV *IdeDev, | |
IN UINTN TimeoutInMilliSeconds | |
) | |
/*++ | |
Name: | |
WaitForBSYClear | |
Purpose: | |
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. | |
Parameters: | |
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. | |
UINTN IN TimeoutInMilliSeconds | |
used to designate the timeout for the DRQ ready. | |
Returns: | |
EFI_SUCCESS | |
BSY bit clear within the time out. | |
EFI_TIMEOUT | |
BSY bit not clear within the time out. | |
Notes: | |
Read Status Register will clear interrupt status. | |
--*/ | |
// 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 | |
// | |
EFI_STATUS | |
WaitForBSYClear2 ( | |
IN IDE_BLK_IO_DEV *IdeDev, | |
IN UINTN TimeoutInMilliSeconds | |
) | |
/*++ | |
Name: | |
WaitForBSYClear2 | |
Purpose: | |
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. | |
Parameters: | |
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. | |
UINTN IN TimeoutInMilliSeconds | |
used to designate the timeout for the DRQ ready. | |
Returns: | |
EFI_SUCCESS | |
BSY bit clear within the time out. | |
EFI_TIMEOUT | |
BSY bit not clear within the time out. | |
Notes: | |
Read Alternate Status Register will not clear interrupt status. | |
--*/ | |
// 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 | |
// | |
EFI_STATUS | |
DRDYReady ( | |
IN IDE_BLK_IO_DEV *IdeDev, | |
IN UINTN DelayInMilliSeconds | |
) | |
/*++ | |
Name: | |
DRDYReady | |
Purpose: | |
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. | |
Parameters: | |
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. | |
UINTN IN TimeoutInMilliSeconds | |
used to designate the timeout for the DRQ ready. | |
Returns: | |
EFI_SUCCESS | |
DRDY bit set within the time out. | |
EFI_TIMEOUT | |
DRDY bit not set within the time out. | |
Notes: | |
Read Status Register will clear interrupt status. | |
--*/ | |
// 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 | |
// | |
EFI_STATUS | |
DRDYReady2 ( | |
IN IDE_BLK_IO_DEV *IdeDev, | |
IN UINTN DelayInMilliSeconds | |
) | |
/*++ | |
Name: | |
DRDYReady2 | |
Purpose: | |
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. | |
Parameters: | |
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. | |
UINTN IN TimeoutInMilliSeconds | |
used to designate the timeout for the DRQ ready. | |
Returns: | |
EFI_SUCCESS | |
DRDY bit set within the time out. | |
EFI_TIMEOUT | |
DRDY bit not set within the time out. | |
Notes: | |
Read Alternate Status Register will clear interrupt status. | |
--*/ | |
// 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 | |
// | |
VOID | |
SwapStringChars ( | |
IN CHAR8 *Destination, | |
IN CHAR8 *Source, | |
IN UINT32 Size | |
) | |
/*++ | |
Name: | |
SwapStringChars | |
Purpose: | |
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. | |
Parameters: | |
CHAR8 IN *Destination | |
Indicates the destination string. | |
CHAR8 IN *Source | |
Indicates the source string. | |
UINT8 IN Size | |
the length of the string | |
Returns: | |
none | |
Notes: | |
--*/ | |
// TODO: function comment is missing 'Routine Description:' | |
// TODO: function comment is missing 'Arguments:' | |
// TODO: Destination - add argument and description to function comment | |
// TODO: Source - add argument and description to function comment | |
// TODO: Size - add argument and description to function comment | |
{ | |
UINT32 Index; | |
CHAR8 Temp; | |
for (Index = 0; Index < Size; Index += 2) { | |
Temp = Source[Index + 1]; | |
Destination[Index + 1] = Source[Index]; | |
Destination[Index] = Temp; | |
} | |
} | |
// | |
// ReleaseIdeResources | |
// | |
VOID | |
ReleaseIdeResources ( | |
IN IDE_BLK_IO_DEV *IdeBlkIoDevice | |
) | |
/*++ | |
Routing Description: | |
Release resources of an IDE device before stopping it. | |
Arguments: | |
IdeBlkIoDevice -- Standard IDE device private data structure | |
Returns: | |
NONE | |
---*/ | |
// TODO: function comment is missing 'Routine Description:' | |
{ | |
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 | |
// | |
EFI_STATUS | |
SetDeviceTransferMode ( | |
IN IDE_BLK_IO_DEV *IdeDev, | |
IN ATA_TRANSFER_MODE *TransferMode | |
) | |
/*++ | |
Routing Description: | |
Set the calculated Best transfer mode to a detected device | |
Arguments: | |
IdeDev -- Standard IDE device private data structure | |
TransferMode -- The device transfer mode to be set | |
Returns: | |
Set transfer mode Command execute status | |
---*/ | |
// 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; | |
} | |
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 | |
) | |
/*++ | |
Routine Description: | |
Send ATA command into device with NON_DATA protocol | |
Arguments: | |
IdeDev - Standard IDE device private data structure | |
AtaCommand - The ATA command to be sent | |
Device - The value in Device register | |
Feature - The value in Feature register | |
SectorCount - The value in SectorCount register | |
LbaLow - The value in LBA_LOW register | |
LbaMiddle - The value in LBA_MIDDLE register | |
LbaHigh - The value in LBA_HIGH register | |
Returns: | |
EFI_SUCCESS - Reading succeed | |
EFI_ABORTED - Command failed | |
EFI_DEVICE_ERROR - Device status error | |
--*/ | |
{ | |
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; | |
} | |
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 | |
) | |
/*++ | |
Routine Description: | |
Send ATA Ext command into device with NON_DATA protocol | |
Arguments: | |
IdeDev - Standard IDE device private data structure | |
AtaCommand - The ATA command to be sent | |
Device - The value in Device register | |
Feature - The value in Feature register | |
SectorCount - The value in SectorCount register | |
LbaAddress - The LBA address in 48-bit mode | |
Returns: | |
EFI_SUCCESS - Reading succeed | |
EFI_ABORTED - Command failed | |
EFI_DEVICE_ERROR - Device status error | |
--*/ | |
{ | |
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 | |
// | |
EFI_STATUS | |
SetDriveParameters ( | |
IN IDE_BLK_IO_DEV *IdeDev, | |
IN ATA_DRIVE_PARMS *DriveParameters | |
) | |
/*++ | |
Routine Description: | |
Set drive parameters for devices not support PACKETS command | |
Arguments: | |
IdeDev -- Standard IDE device private data structure | |
DriveParameters -- The device parameters to be set into the disk | |
Returns: | |
SetParameters Command execute status | |
--*/ | |
{ | |
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; | |
} | |
EFI_STATUS | |
EnableInterrupt ( | |
IN IDE_BLK_IO_DEV *IdeDev | |
) | |
/*++ | |
Routine Description: | |
TODO: Add function description | |
Arguments: | |
IdeDev - TODO: add argument description | |
Returns: | |
EFI_SUCCESS - TODO: Add description for return value | |
--*/ | |
{ | |
UINT8 DeviceControl; | |
// | |
// Enable interrupt for DMA operation | |
// | |
DeviceControl = 0; | |
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Alt.DeviceControl, DeviceControl); | |
return EFI_SUCCESS; | |
} |