audk/EdkModulePkg/Bus/Pci/IdeBus/Dxe/ata.c

3691 lines
104 KiB
C

/*++
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:
ata.c
Abstract:
Revision History
2002-6: Add Atapi6 enhancement, support >120GB hard disk, including
update - ATAIdentity() func
update - AtaBlockIoReadBlocks() func
update - AtaBlockIoWriteBlocks() func
add - AtaAtapi6Identify() func
add - AtaReadSectorsExt() func
add - AtaWriteSectorsExt() func
add - AtaPioDataInExt() func
add - AtaPioDataOutExt() func
--*/
#include "idebus.h"
EFI_STATUS
AtaReadSectorsExt (
IN IDE_BLK_IO_DEV *IdeDev,
IN OUT VOID *DataBuffer,
IN EFI_LBA StartLba,
IN UINTN NumberOfBlocks
);
EFI_STATUS
AtaWriteSectorsExt (
IN IDE_BLK_IO_DEV *IdeDev,
IN VOID *DataBuffer,
IN EFI_LBA StartLba,
IN UINTN NumberOfBlocks
);
EFI_STATUS
AtaPioDataInExt (
IN IDE_BLK_IO_DEV *IdeDev,
IN OUT VOID *Buffer,
IN UINT32 ByteCount,
IN UINT8 AtaCommand,
IN EFI_LBA StartLba,
IN UINT16 SectorCount
);
EFI_STATUS
AtaPioDataOutExt (
IN IDE_BLK_IO_DEV *IdeDev,
IN VOID *Buffer,
IN UINT32 ByteCount,
IN UINT8 AtaCommand,
IN EFI_LBA StartLba,
IN UINT16 SectorCount
);
EFI_STATUS
ATAIdentify (
IN IDE_BLK_IO_DEV *IdeDev
)
/*++
Name:
ATAIdentify
Purpose:
This function is called by DiscoverIdeDevice() during its device
identification. It sends out the ATA Identify Command to the
specified device. Only ATA device responses to this command. If
the command succeeds, it returns the Identify data structure which
contains information about the device. This function extracts the
information it needs to fill the IDE_BLK_IO_DEV data structure,
including device type, media block size, media capacity, and etc.
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:
EFI_SUCCESS
Identify ATA device successfully.
EFI_DEVICE_ERROR
ATA Identify Device Command failed or device is not
ATA device.
Notes:
parameter IdeDev will be updated in this function.
--*/
// TODO: function comment is missing 'Routine Description:'
// TODO: function comment is missing 'Arguments:'
// TODO: IdeDev - add argument and description to function comment
{
EFI_STATUS Status;
EFI_IDENTIFY_DATA *AtaIdentifyPointer;
UINT32 Capacity;
UINT8 DeviceSelect;
//
// AtaIdentifyPointer is used for accommodating returned IDENTIFY data of
// the ATA Identify command
//
AtaIdentifyPointer = (EFI_IDENTIFY_DATA *) AllocateZeroPool (sizeof (EFI_IDENTIFY_DATA));
//
// use ATA PIO Data In protocol to send ATA Identify command
// and receive data from device
//
DeviceSelect = 0;
DeviceSelect = (UINT8) ((IdeDev->Device) << 4);
Status = AtaPioDataIn (
IdeDev,
(VOID *) AtaIdentifyPointer,
sizeof (EFI_IDENTIFY_DATA),
IDENTIFY_DRIVE_CMD,
DeviceSelect,
0,
0,
0,
0
);
//
// If ATA Identify command succeeds, then according to the received
// IDENTIFY data,
// identify the device type ( ATA or not ).
// If ATA device, fill the information in IdeDev.
// If not ATA device, return IDE_DEVICE_ERROR
//
if (!EFI_ERROR (Status)) {
IdeDev->pIdData = AtaIdentifyPointer;
//
// Print ATA Module Name
//
PrintAtaModuleName (IdeDev);
//
// bit 15 of pAtaIdentify->config is used to identify whether device is
// ATA device or ATAPI device.
// if 0, means ATA device; if 1, means ATAPI device.
//
if ((AtaIdentifyPointer->AtaData.config & 0x8000) == 0x00) {
//
// Detect if support S.M.A.R.T. If yes, enable it as default
//
AtaSMARTSupport (IdeDev);
//
// Check whether this device needs 48-bit addressing (ATAPI-6 ata device)
//
Status = AtaAtapi6Identify (IdeDev);
if (!EFI_ERROR (Status)) {
//
// It's a disk with >120GB capacity, initialized in AtaAtapi6Identify()
//
return EFI_SUCCESS;
}
//
// This is a hard disk <= 120GB capacity, treat it as normal hard disk
//
IdeDev->Type = IdeHardDisk;
//
// Block Media Information:
// Media->LogicalPartition , Media->WriteCaching will be filled
// in the DiscoverIdeDevcie() function.
//
IdeDev->BlkIo.Media->IoAlign = 4;
IdeDev->BlkIo.Media->MediaId = 1;
IdeDev->BlkIo.Media->RemovableMedia = FALSE;
IdeDev->BlkIo.Media->MediaPresent = TRUE;
IdeDev->BlkIo.Media->ReadOnly = FALSE;
IdeDev->BlkIo.Media->BlockSize = 0x200;
//
// Calculate device capacity
//
Capacity = ((UINT32)AtaIdentifyPointer->AtaData.user_addressable_sectors_hi << 16) |
AtaIdentifyPointer->AtaData.user_addressable_sectors_lo ;
IdeDev->BlkIo.Media->LastBlock = Capacity - 1;
return EFI_SUCCESS;
}
}
gBS->FreePool (AtaIdentifyPointer);
//
// Make sure the pIdData will not be freed again.
//
IdeDev->pIdData = NULL;
return EFI_DEVICE_ERROR;
}
EFI_STATUS
AtaAtapi6Identify (
IN IDE_BLK_IO_DEV *IdeDev
)
/*++
Name:
AtaAtapi6Identify
Purpose:
This function is called by ATAIdentify() to identity whether this disk
supports ATA/ATAPI6 48bit addressing, ie support >120G capacity
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:
EFI_SUCCESS - The disk specified by IdeDev is a Atapi6 supported one
and 48-bit addressing must be used
EFI_UNSUPPORTED - The disk dosn't not support Atapi6 or it supports but
the capacity is below 120G, 48bit addressing is not
needed
Notes:
This function must be called after DEVICE_IDENTITY command has been
successfully returned
--*/
// TODO: function comment is missing 'Routine Description:'
// TODO: function comment is missing 'Arguments:'
// TODO: IdeDev - add argument and description to function comment
{
UINT8 Index;
EFI_LBA TmpLba;
EFI_LBA Capacity;
EFI_IDENTIFY_DATA *Atapi6IdentifyStruct;
if (IdeDev->pIdData == NULL) {
return EFI_UNSUPPORTED;
}
Atapi6IdentifyStruct = IdeDev->pIdData;
if ((Atapi6IdentifyStruct->AtapiData.cmd_set_support_83 & bit10) == 0) {
//
// The device dosn't support 48 bit addressing
//
return EFI_UNSUPPORTED;
}
//
// 48 bit address feature set is supported, get maximum capacity
//
Capacity = Atapi6IdentifyStruct->AtapiData.max_user_lba_for_48bit_addr[0];
for (Index = 1; Index < 4; Index++) {
//
// Lower byte goes first: word[100] is the lowest word, word[103] is highest
//
TmpLba = Atapi6IdentifyStruct->AtapiData.max_user_lba_for_48bit_addr[Index];
Capacity |= LShiftU64 (TmpLba, 16 * Index);
}
if (Capacity > MAX_28BIT_ADDRESSING_CAPACITY) {
//
// Capacity exceeds 120GB. 48-bit addressing is really needed
//
IdeDev->Type = Ide48bitAddressingHardDisk;
//
// Fill block media information:Media->LogicalPartition ,
// Media->WriteCaching will be filledin the DiscoverIdeDevcie() function.
//
IdeDev->BlkIo.Media->IoAlign = 4;
IdeDev->BlkIo.Media->MediaId = 1;
IdeDev->BlkIo.Media->RemovableMedia = FALSE;
IdeDev->BlkIo.Media->MediaPresent = TRUE;
IdeDev->BlkIo.Media->ReadOnly = FALSE;
IdeDev->BlkIo.Media->BlockSize = 0x200;
IdeDev->BlkIo.Media->LastBlock = Capacity - 1;
return EFI_SUCCESS;
}
return EFI_UNSUPPORTED;
}
VOID
PrintAtaModuleName (
IN IDE_BLK_IO_DEV *IdeDev
)
/*++
Name:
PrintAtaModuleName
Purpose:
This function is called by ATAIdentify() or ATAPIIdentify()
to print device's module name.
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:
no returns.
Notes:
--*/
// TODO: function comment is missing 'Routine Description:'
// TODO: function comment is missing 'Arguments:'
// TODO: IdeDev - add argument and description to function comment
{
if (IdeDev->pIdData == NULL) {
return ;
}
SwapStringChars (IdeDev->ModelName, IdeDev->pIdData->AtaData.ModelName, 40);
IdeDev->ModelName[40] = 0x00;
}
EFI_STATUS
AtaPioDataIn (
IN IDE_BLK_IO_DEV *IdeDev,
IN VOID *Buffer,
IN UINT32 ByteCount,
IN UINT8 AtaCommand,
IN UINT8 Head,
IN UINT8 SectorCount,
IN UINT8 SectorNumber,
IN UINT8 CylinderLsb,
IN UINT8 CylinderMsb
)
/*++
Name:
AtaPioDataIn
Purpose:
This function is used to send out ATA commands conforms to the
PIO Data In Protocol.
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.
VOID IN *Buffer
buffer contained data transferred from device to host.
UINT32 IN ByteCount
data size in byte unit of the buffer.
UINT8 IN AtaCommand
value of the Command Register
UINT8 IN Head
value of the Head/Device Register
UINT8 IN SectorCount
value of the Sector Count Register
UINT8 IN SectorNumber
value of the Sector Number Register
UINT8 IN CylinderLsb
value of the low byte of the Cylinder Register
UINT8 IN CylinderMsb
value of the high byte of the Cylinder Register
Returns:
EFI_SUCCESS
send out the ATA command and device send required
data successfully.
EFI_DEVICE_ERROR
command sent failed.
Notes:
--*/
// TODO: function comment is missing 'Routine Description:'
// TODO: function comment is missing 'Arguments:'
// TODO: IdeDev - add argument and description to function comment
// TODO: Buffer - add argument and description to function comment
// TODO: ByteCount - add argument and description to function comment
// TODO: AtaCommand - add argument and description to function comment
// TODO: Head - add argument and description to function comment
// TODO: SectorCount - add argument and description to function comment
// TODO: SectorNumber - add argument and description to function comment
// TODO: CylinderLsb - add argument and description to function comment
// TODO: CylinderMsb - add argument and description to function comment
{
UINTN WordCount;
UINTN Increment;
UINT16 *Buffer16;
EFI_STATUS Status;
Status = WaitForBSYClear (IdeDev, ATATIMEOUT);
if (EFI_ERROR (Status)) {
return EFI_DEVICE_ERROR;
}
//
// e0:1110,0000-- bit7 and bit5 are reserved bits.
// bit6 set means LBA mode
//
IDEWritePortB (
IdeDev->PciIo,
IdeDev->IoPort->Head,
(UINT8) ((IdeDev->Device << 4) | 0xe0 | Head)
);
//
// All ATAPI device's ATA commands can be issued regardless of the
// state of the DRDY
//
if (IdeDev->Type == IdeHardDisk) {
Status = DRDYReady (IdeDev, ATATIMEOUT);
if (EFI_ERROR (Status)) {
return EFI_DEVICE_ERROR;
}
}
//
// set all the command parameters
// Before write to all the following registers, BSY and DRQ must be 0.
//
Status = DRQClear2 (IdeDev, ATATIMEOUT);
if (EFI_ERROR (Status)) {
return EFI_DEVICE_ERROR;
}
if (AtaCommand == SET_FEATURES_CMD) {
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Reg1.Feature, 0x03);
}
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->SectorCount, SectorCount);
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->SectorNumber, SectorNumber);
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->CylinderLsb, CylinderLsb);
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->CylinderMsb, CylinderMsb);
//
// send command via Command Register
//
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Reg.Command, AtaCommand);
Buffer16 = (UINT16 *) Buffer;
//
// According to PIO data in protocol, host can perform a series of reads to
// the data register after each time device set DRQ ready;
// The data size of "a series of read" is command specific.
// For most ATA command, data size received from device will not exceed
// 1 sector, hence the data size for "a series of read" can be the whole data
// size of one command request.
// For ATA command such as Read Sector command, the data size of one ATA
// command request is often larger than 1 sector, according to the
// Read Sector command, the data size of "a series of read" is exactly 1
// sector.
// Here for simplification reason, we specify the data size for
// "a series of read" to 1 sector (256 words) if data size of one ATA command
// request is larger than 256 words.
//
Increment = 256;
//
// used to record bytes of currently transfered data
//
WordCount = 0;
while (WordCount < ByteCount / 2) {
//
// Poll DRQ bit set, data transfer can be performed only when DRQ is ready.
//
Status = DRQReady2 (IdeDev, ATATIMEOUT);
if (EFI_ERROR (Status)) {
return EFI_DEVICE_ERROR;
}
Status = CheckErrorStatus (IdeDev);
if (EFI_ERROR (Status)) {
return EFI_DEVICE_ERROR;
}
//
// Get the byte count for one series of read
//
if ((WordCount + Increment) > ByteCount / 2) {
Increment = ByteCount / 2 - WordCount;
}
IDEReadPortWMultiple (
IdeDev->PciIo,
IdeDev->IoPort->Data,
Increment,
Buffer16
);
WordCount += Increment;
Buffer16 += Increment;
}
DRQClear (IdeDev, ATATIMEOUT);
return CheckErrorStatus (IdeDev);
}
EFI_STATUS
AtaPioDataOut (
IN IDE_BLK_IO_DEV *IdeDev,
IN VOID *Buffer,
IN UINT32 ByteCount,
IN UINT8 AtaCommand,
IN UINT8 Head,
IN UINT8 SectorCount,
IN UINT8 SectorNumber,
IN UINT8 CylinderLsb,
IN UINT8 CylinderMsb
)
/*++
Name:
AtaPioDataOut
Purpose:
This function is used to send out ATA commands conforms to the
PIO Data Out Protocol.
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.
VOID IN *Buffer
buffer contained data transferred from host to device.
UINT32 IN ByteCount
data size in byte unit of the buffer.
UINT8 IN AtaCommand
value of the Command Register
UINT8 IN Head
value of the Head/Device Register
UINT8 IN SectorCount
value of the Sector Count Register
UINT8 IN SectorNumber
value of the Sector Number Register
UINT8 IN CylinderLsb
value of the low byte of the Cylinder Register
UINT8 IN CylinderMsb
value of the high byte of the Cylinder Register
Returns:
EFI_SUCCESS
send out the ATA command and device received required
data successfully.
EFI_DEVICE_ERROR
command sent failed.
Notes:
--*/
// TODO: function comment is missing 'Routine Description:'
// TODO: function comment is missing 'Arguments:'
// TODO: IdeDev - add argument and description to function comment
// TODO: Buffer - add argument and description to function comment
// TODO: ByteCount - add argument and description to function comment
// TODO: AtaCommand - add argument and description to function comment
// TODO: Head - add argument and description to function comment
// TODO: SectorCount - add argument and description to function comment
// TODO: SectorNumber - add argument and description to function comment
// TODO: CylinderLsb - add argument and description to function comment
// TODO: CylinderMsb - add argument and description to function comment
{
UINTN WordCount;
UINTN Increment;
UINT16 *Buffer16;
EFI_STATUS Status;
Status = WaitForBSYClear (IdeDev, ATATIMEOUT);
if (EFI_ERROR (Status)) {
return EFI_DEVICE_ERROR;
}
//
// select device via Head/Device register.
// Before write Head/Device register, BSY and DRQ must be 0.
//
Status = DRQClear2 (IdeDev, ATATIMEOUT);
if (EFI_ERROR (Status)) {
return EFI_DEVICE_ERROR;
}
//
// e0:1110,0000-- bit7 and bit5 are reserved bits.
// bit6 set means LBA mode
//
IDEWritePortB (
IdeDev->PciIo,
IdeDev->IoPort->Head,
(UINT8) ((IdeDev->Device << 4) | 0xe0 | Head)
);
Status = DRDYReady (IdeDev, ATATIMEOUT);
if (EFI_ERROR (Status)) {
return EFI_DEVICE_ERROR;
}
//
// set all the command parameters
// Before write to all the following registers, BSY and DRQ must be 0.
//
Status = DRQClear2 (IdeDev, ATATIMEOUT);
if (EFI_ERROR (Status)) {
return EFI_DEVICE_ERROR;
}
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->SectorCount, SectorCount);
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->SectorNumber, SectorNumber);
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->CylinderLsb, CylinderLsb);
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->CylinderMsb, CylinderMsb);
//
// send command via Command Register
//
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Reg.Command, AtaCommand);
Buffer16 = (UINT16 *) Buffer;
//
// According to PIO data out protocol, host can perform a series of
// writes to the data register after each time device set DRQ ready;
// The data size of "a series of read" is command specific.
// For most ATA command, data size written to device will not exceed 1 sector,
// hence the data size for "a series of write" can be the data size of one
// command request.
// For ATA command such as Write Sector command, the data size of one
// ATA command request is often larger than 1 sector, according to the
// Write Sector command, the data size of "a series of read" is exactly
// 1 sector.
// Here for simplification reason, we specify the data size for
// "a series of write" to 1 sector (256 words) if data size of one ATA command
// request is larger than 256 words.
//
Increment = 256;
WordCount = 0;
while (WordCount < ByteCount / 2) {
//
// DRQReady2-- read Alternate Status Register to determine the DRQ bit
// data transfer can be performed only when DRQ is ready.
//
Status = DRQReady2 (IdeDev, ATATIMEOUT);
if (EFI_ERROR (Status)) {
return EFI_DEVICE_ERROR;
}
Status = CheckErrorStatus (IdeDev);
if (EFI_ERROR (Status)) {
return EFI_DEVICE_ERROR;
}
//
// Check the remaining byte count is less than 512 bytes
//
if ((WordCount + Increment) > ByteCount / 2) {
Increment = ByteCount / 2 - WordCount;
}
//
// perform a series of write without check DRQ ready
//
IDEWritePortWMultiple (
IdeDev->PciIo,
IdeDev->IoPort->Data,
Increment,
Buffer16
);
WordCount += Increment;
Buffer16 += Increment;
}
DRQClear (IdeDev, ATATIMEOUT);
return CheckErrorStatus (IdeDev);
}
EFI_STATUS
CheckErrorStatus (
IN IDE_BLK_IO_DEV *IdeDev
)
/*++
Name:
CheckErrorStatus
Purpose:
This function is used to analyze the Status Register and print out
some debug information and if there is ERR bit set in the Status
Register, the Error Register's value is also be parsed and print out.
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:
EFI_SUCCESS
No err information in the Status Register.
EFI_DEVICE_ERROR
Any err information in the Status Register.
Notes:
--*/
// TODO: function comment is missing 'Routine Description:'
// TODO: function comment is missing 'Arguments:'
// TODO: IdeDev - add argument and description to function comment
{
UINT8 StatusRegister;
//#ifdef EFI_DEBUG
UINT8 ErrorRegister;
//#endif
StatusRegister = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg.Status);
DEBUG_CODE (
if (StatusRegister & DWF) {
DEBUG (
(EFI_D_BLKIO,
"CheckErrorStatus()-- %02x : Error : Write Fault\n",
StatusRegister)
);
}
if (StatusRegister & CORR) {
DEBUG (
(EFI_D_BLKIO,
"CheckErrorStatus()-- %02x : Error : Corrected Data\n",
StatusRegister)
);
}
if (StatusRegister & ERR) {
ErrorRegister = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg1.Error);
if (ErrorRegister & BBK_ERR) {
DEBUG (
(EFI_D_BLKIO,
"CheckErrorStatus()-- %02x : Error : Bad Block Detected\n",
ErrorRegister)
);
}
if (ErrorRegister & UNC_ERR) {
DEBUG (
(EFI_D_BLKIO,
"CheckErrorStatus()-- %02x : Error : Uncorrectable Data\n",
ErrorRegister)
);
}
if (ErrorRegister & MC_ERR) {
DEBUG (
(EFI_D_BLKIO,
"CheckErrorStatus()-- %02x : Error : Media Change\n",
ErrorRegister)
);
}
if (ErrorRegister & ABRT_ERR) {
DEBUG (
(EFI_D_BLKIO,
"CheckErrorStatus()-- %02x : Error : Abort\n",
ErrorRegister)
);
}
if (ErrorRegister & TK0NF_ERR) {
DEBUG (
(EFI_D_BLKIO,
"CheckErrorStatus()-- %02x : Error : Track 0 Not Found\n",
ErrorRegister)
);
}
if (ErrorRegister & AMNF_ERR) {
DEBUG (
(EFI_D_BLKIO,
"CheckErrorStatus()-- %02x : Error : Address Mark Not Found\n",
ErrorRegister)
);
}
}
);
if ((StatusRegister & (ERR | DWF | CORR)) == 0) {
return EFI_SUCCESS;
}
return EFI_DEVICE_ERROR;
}
EFI_STATUS
AtaReadSectors (
IN IDE_BLK_IO_DEV *IdeDev,
IN VOID *DataBuffer,
IN EFI_LBA Lba,
IN UINTN NumberOfBlocks
)
/*++
Name:
AtaReadSectors
Purpose:
This function is called by the AtaBlkIoReadBlocks() to perform
reading from media in block unit.
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.
VOID IN *DataBuffer
A pointer to the destination buffer for the data.
EFI_LBA IN Lba
The starting logical block address to read from
on the device media.
UINTN IN NumberOfBlocks
The number of transfer data blocks.
Returns:
return status is fully dependent on the return status
of AtaPioDataIn() function.
Notes:
--*/
// TODO: function comment is missing 'Routine Description:'
// TODO: function comment is missing 'Arguments:'
// TODO: IdeDev - add argument and description to function comment
// TODO: DataBuffer - add argument and description to function comment
// TODO: Lba - add argument and description to function comment
// TODO: NumberOfBlocks - add argument and description to function comment
{
EFI_STATUS Status;
UINTN BlocksRemaining;
UINT32 Lba32;
UINT8 Lba0;
UINT8 Lba1;
UINT8 Lba2;
UINT8 Lba3;
UINT8 AtaCommand;
UINT8 SectorCount8;
UINT16 SectorCount;
UINTN ByteCount;
VOID *Buffer;
Buffer = DataBuffer;
//
// Using ATA Read Sector(s) command (opcode=0x20) with PIO DATA IN protocol
//
AtaCommand = READ_SECTORS_CMD;
BlocksRemaining = NumberOfBlocks;
Lba32 = (UINT32) Lba;
Status = EFI_SUCCESS;
while (BlocksRemaining > 0) {
//
// in ATA-3 spec, LBA is in 28 bit width
//
Lba0 = (UINT8) Lba32;
Lba1 = (UINT8) (Lba32 >> 8);
Lba2 = (UINT8) (Lba32 >> 16);
//
// low 4 bit of Lba3 stands for LBA bit24~bit27.
//
Lba3 = (UINT8) ((Lba32 >> 24) & 0x0f);
if (BlocksRemaining >= 0x100) {
//
// SectorCount8 is sent to Sector Count register, 0x00 means 256
// sectors to be read
//
SectorCount8 = 0x00;
//
// SectorCount is used to record the number of sectors to be read
//
SectorCount = 256;
} else {
SectorCount8 = (UINT8) BlocksRemaining;
SectorCount = (UINT16) BlocksRemaining;
}
//
// ByteCount is the number of bytes that will be read
//
ByteCount = SectorCount * (IdeDev->BlkIo.Media->BlockSize);
//
// call AtaPioDataIn() to send Read Sector Command and receive data read
//
Status = AtaPioDataIn (
IdeDev,
Buffer,
(UINT32) ByteCount,
AtaCommand,
Lba3,
SectorCount8,
Lba0,
Lba1,
Lba2
);
if (EFI_ERROR (Status)) {
return Status;
}
Lba32 += SectorCount;
Buffer = ((UINT8 *) Buffer + ByteCount);
BlocksRemaining -= SectorCount;
}
return Status;
}
EFI_STATUS
AtaWriteSectors (
IN IDE_BLK_IO_DEV *IdeDev,
IN VOID *BufferData,
IN EFI_LBA Lba,
IN UINTN NumberOfBlocks
)
/*++
Name:
AtaWriteSectors
Purpose:
This function is called by the AtaBlkIoWriteBlocks() to perform
writing onto media in block unit.
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.
VOID IN *BufferData
A pointer to the source buffer for the data.
EFI_LBA IN Lba
The starting logical block address to write onto
the device media.
UINTN IN NumberOfBlocks
The number of transfer data blocks.
Returns:
return status is fully dependent on the return status
of AtaPioDataOut() function.
Notes:
--*/
// TODO: function comment is missing 'Routine Description:'
// TODO: function comment is missing 'Arguments:'
// TODO: IdeDev - add argument and description to function comment
// TODO: BufferData - add argument and description to function comment
// TODO: Lba - add argument and description to function comment
// TODO: NumberOfBlocks - add argument and description to function comment
{
EFI_STATUS Status;
UINTN BlocksRemaining;
UINT32 Lba32;
UINT8 Lba0;
UINT8 Lba1;
UINT8 Lba2;
UINT8 Lba3;
UINT8 AtaCommand;
UINT8 SectorCount8;
UINT16 SectorCount;
UINTN ByteCount;
VOID *Buffer;
Buffer = BufferData;
//
// Using Write Sector(s) command (opcode=0x30) with PIO DATA OUT protocol
//
AtaCommand = WRITE_SECTORS_CMD;
BlocksRemaining = NumberOfBlocks;
Lba32 = (UINT32) Lba;
Status = EFI_SUCCESS;
while (BlocksRemaining > 0) {
Lba0 = (UINT8) Lba32;
Lba1 = (UINT8) (Lba32 >> 8);
Lba2 = (UINT8) (Lba32 >> 16);
Lba3 = (UINT8) ((Lba32 >> 24) & 0x0f);
if (BlocksRemaining >= 0x100) {
//
// SectorCount8 is sent to Sector Count register, 0x00 means 256 sectors
// to be written
//
SectorCount8 = 0x00;
//
// SectorCount is used to record the number of sectors to be written
//
SectorCount = 256;
} else {
SectorCount8 = (UINT8) BlocksRemaining;
SectorCount = (UINT16) BlocksRemaining;
}
ByteCount = SectorCount * (IdeDev->BlkIo.Media->BlockSize);
Status = AtaPioDataOut (
IdeDev,
Buffer,
(UINT32) ByteCount,
AtaCommand,
Lba3,
SectorCount8,
Lba0,
Lba1,
Lba2
);
if (EFI_ERROR (Status)) {
return Status;
}
Lba32 += SectorCount;
Buffer = ((UINT8 *) Buffer + ByteCount);
BlocksRemaining -= SectorCount;
}
return Status;
}
EFI_STATUS
AtaSoftReset (
IN IDE_BLK_IO_DEV *IdeDev
)
/*++
Name:
AtaSoftReset
Purpose:
This function is used to implement the Soft Reset on the specified
device. But, the ATA Soft Reset mechanism is so strong a reset method
that it will force resetting on both devices connected to the
same cable.
It is called by IdeBlkIoReset(), a interface function of Block
I/O protocol.
This function can also be used by the ATAPI device to perform reset when
ATAPI Reset command is failed.
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:
EFI_SUCCESS
Soft reset completes successfully.
EFI_DEVICE_ERROR
Any step during the reset process is failed.
Notes:
The registers initial values after ATA soft reset are different
to the ATA device and ATAPI device.
--*/
// TODO: function comment is missing 'Routine Description:'
// TODO: function comment is missing 'Arguments:'
// TODO: IdeDev - add argument and description to function comment
{
UINT8 DeviceControl;
DeviceControl = 0;
//
// set SRST bit to initiate soft reset
//
DeviceControl |= SRST;
//
// disable Interrupt
//
DeviceControl |= bit1;
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Alt.DeviceControl, DeviceControl);
gBS->Stall (10);
//
// Enable interrupt to support UDMA, and clear SRST bit
//
DeviceControl = 0;
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Alt.DeviceControl, DeviceControl);
//
// slave device needs at most 31s to clear BSY
//
if (WaitForBSYClear (IdeDev, 31000) == EFI_TIMEOUT) {
return EFI_DEVICE_ERROR;
}
return EFI_SUCCESS;
}
EFI_STATUS
AtaBlkIoReadBlocks (
IN IDE_BLK_IO_DEV *IdeBlkIoDevice,
IN UINT32 MediaId,
IN EFI_LBA LBA,
IN UINTN BufferSize,
OUT VOID *Buffer
)
/*++
Name:
AtaBlkIoReadBlocks
Purpose:
This function is the ATA implementation for ReadBlocks in the
Block I/O Protocol interface.
Parameters:
IDE_BLK_IO_DEV IN *IdeBlkIoDevice
Indicates the calling context.
UINT32 IN MediaId
The media id that the read request is for.
EFI_LBA IN LBA
The starting logical block address to read from
on the device.
UINTN IN BufferSize
The size of the Buffer in bytes. This must be a
multiple of the intrinsic block size of the device.
VOID OUT *Buffer
A pointer to the destination buffer for the data.
The caller is responsible for either having implicit
or explicit ownership of the memory that data is read into.
Returns:
EFI_SUCCESS
Read Blocks successfully.
EFI_DEVICE_ERROR
Read Blocks failed.
EFI_NO_MEDIA
There is no media in the device.
EFI_MEDIA_CHANGE
The MediaId is not for the current media.
EFI_BAD_BUFFER_SIZE
The BufferSize parameter is not a multiple of the
intrinsic block size of the device.
EFI_INVALID_PARAMETER
The read request contains LBAs that are not valid,
or the data buffer is not valid.
Notes:
If Read Block error because of device error, this function will call
AtaSoftReset() function to reset device.
--*/
// TODO: function comment is missing 'Routine Description:'
// TODO: function comment is missing 'Arguments:'
// TODO: IdeBlkIoDevice - add argument and description to function comment
// TODO: MediaId - add argument and description to function comment
// TODO: LBA - add argument and description to function comment
// TODO: BufferSize - add argument and description to function comment
// TODO: Buffer - add argument and description to function comment
// TODO: EFI_MEDIA_CHANGED - add return value to function comment
{
EFI_BLOCK_IO_MEDIA *Media;
UINTN BlockSize;
UINTN NumberOfBlocks;
EFI_STATUS Status;
if (Buffer == NULL) {
return EFI_INVALID_PARAMETER;
}
if (BufferSize == 0) {
return EFI_SUCCESS;
}
Status = EFI_SUCCESS;
//
// Get the intrinsic block size
//
Media = IdeBlkIoDevice->BlkIo.Media;
BlockSize = Media->BlockSize;
NumberOfBlocks = BufferSize / BlockSize;
if (MediaId != Media->MediaId) {
return EFI_MEDIA_CHANGED;
}
if (BufferSize % BlockSize != 0) {
return EFI_BAD_BUFFER_SIZE;
}
if (!(Media->MediaPresent)) {
return EFI_NO_MEDIA;
}
if (LBA > Media->LastBlock) {
return EFI_INVALID_PARAMETER;
}
if ((LBA + NumberOfBlocks - 1) > Media->LastBlock) {
return EFI_INVALID_PARAMETER;
}
if ((Media->IoAlign > 1) && (((UINTN) Buffer & (Media->IoAlign - 1)) != 0)) {
return EFI_INVALID_PARAMETER;
}
if (IdeBlkIoDevice->Type == Ide48bitAddressingHardDisk) {
//
// For ATA/ATAPI-6 device(capcity > 120GB), use ATA-6 read block mechanism
//
Status = AtaUdmaReadExt (IdeBlkIoDevice, Buffer, LBA, NumberOfBlocks);
if (EFI_ERROR (Status)) {
Status = AtaReadSectorsExt (IdeBlkIoDevice, Buffer, LBA, NumberOfBlocks);
}
} else {
//
// For ATA-3 compatible device, use ATA-3 read block mechanism
// Notice DMA operation can only handle 32bit address
//
if ((UINTN) Buffer <= 0xFFFFFFFF) {
Status = AtaUdmaRead (IdeBlkIoDevice, Buffer, LBA, NumberOfBlocks);
}
if (EFI_ERROR (Status) || ((UINTN) Buffer > 0xFFFFFFFF)) {
Status = AtaReadSectors (IdeBlkIoDevice, Buffer, LBA, NumberOfBlocks);
}
}
if (EFI_ERROR (Status)) {
AtaSoftReset (IdeBlkIoDevice);
return EFI_DEVICE_ERROR;
}
return EFI_SUCCESS;
}
EFI_STATUS
AtaBlkIoWriteBlocks (
IN IDE_BLK_IO_DEV *IdeBlkIoDevice,
IN UINT32 MediaId,
IN EFI_LBA LBA,
IN UINTN BufferSize,
OUT VOID *Buffer
)
/*++
Name:
AtaBlkIoWriteBlocks
Purpose:
This function is the ATA implementation for WriteBlocks in the
Block I/O Protocol interface.
Parameters:
IDE_BLK_IO_DEV IN *IdeBlkIoDevice
Indicates the calling context.
UINT32 IN MediaId
The media id that the write request is for.
EFI_LBA IN LBA
The starting logical block address to write onto
the device.
UINTN IN BufferSize
The size of the Buffer in bytes. This must be a
multiple of the intrinsic block size of the device.
VOID OUT *Buffer
A pointer to the source buffer for the data.
The caller is responsible for either having implicit
or explicit ownership of the memory that data is
written from.
Returns:
EFI_SUCCESS
Write Blocks successfully.
EFI_DEVICE_ERROR
Write Blocks failed.
EFI_NO_MEDIA
There is no media in the device.
EFI_MEDIA_CHANGE
The MediaId is not for the current media.
EFI_BAD_BUFFER_SIZE
The BufferSize parameter is not a multiple of the
intrinsic block size of the device.
EFI_INVALID_PARAMETER
The write request contains LBAs that are not valid,
or the data buffer is not valid.
Notes:
If Write Block error because of device error, this function will call
AtaSoftReset() function to reset device.
--*/
// TODO: function comment is missing 'Routine Description:'
// TODO: function comment is missing 'Arguments:'
// TODO: IdeBlkIoDevice - add argument and description to function comment
// TODO: MediaId - add argument and description to function comment
// TODO: LBA - add argument and description to function comment
// TODO: BufferSize - add argument and description to function comment
// TODO: Buffer - add argument and description to function comment
// TODO: EFI_MEDIA_CHANGED - add return value to function comment
{
EFI_BLOCK_IO_MEDIA *Media;
UINTN BlockSize;
UINTN NumberOfBlocks;
EFI_STATUS Status;
if (Buffer == NULL) {
return EFI_INVALID_PARAMETER;
}
if (BufferSize == 0) {
return EFI_SUCCESS;
}
Status = EFI_SUCCESS;
//
// Get the intrinsic block size
//
Media = IdeBlkIoDevice->BlkIo.Media;
BlockSize = Media->BlockSize;
NumberOfBlocks = BufferSize / BlockSize;
if (MediaId != Media->MediaId) {
return EFI_MEDIA_CHANGED;
}
if (BufferSize % BlockSize != 0) {
return EFI_BAD_BUFFER_SIZE;
}
if (LBA > Media->LastBlock) {
return EFI_INVALID_PARAMETER;
}
if ((LBA + NumberOfBlocks - 1) > Media->LastBlock) {
return EFI_INVALID_PARAMETER;
}
if ((Media->IoAlign > 1) && (((UINTN) Buffer & (Media->IoAlign - 1)) != 0)) {
return EFI_INVALID_PARAMETER;
}
if (IdeBlkIoDevice->Type == Ide48bitAddressingHardDisk) {
//
// For ATA/ATAPI-6 device(capcity > 120GB), use ATA-6 write block mechanism
//
Status = AtaUdmaWriteExt (IdeBlkIoDevice, Buffer, LBA, NumberOfBlocks);
if (EFI_ERROR (Status)) {
Status = AtaWriteSectorsExt (IdeBlkIoDevice, Buffer, LBA, NumberOfBlocks);
}
} else {
//
// For ATA-3 compatible device, use ATA-3 write block mechanism
//
Status = AtaUdmaWrite (IdeBlkIoDevice, Buffer, LBA, NumberOfBlocks);
if (EFI_ERROR (Status) || ((UINTN) Buffer > 0xFFFFFFFF)) {
Status = AtaWriteSectors (IdeBlkIoDevice, Buffer, LBA, NumberOfBlocks);
}
}
if (EFI_ERROR (Status)) {
AtaSoftReset (IdeBlkIoDevice);
return EFI_DEVICE_ERROR;
}
return EFI_SUCCESS;
}
EFI_STATUS
AtaReadSectorsExt (
IN IDE_BLK_IO_DEV *IdeDev,
IN VOID *DataBuffer,
IN EFI_LBA StartLba,
IN UINTN NumberOfBlocks
)
/*++
Name:
AtaReadSectorsExt
Purpose:
This function is called by the AtaBlkIoReadBlocks() to perform
reading from media in block unit. The function has been enhanced to
support >120GB access and transfer at most 65536 blocks per 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.
VOID IN *DataBuffer
A pointer to the destination buffer for the data.
EFI_LBA IN StartLba
The starting logical block address to read from
on the device media.
UINTN IN NumberOfBlocks
The number of transfer data blocks.
Returns:
return status is fully dependent on the return status
of AtaPioDataInExt() function.
Notes:
--*/
// TODO: function comment is missing 'Routine Description:'
// TODO: function comment is missing 'Arguments:'
// TODO: IdeDev - add argument and description to function comment
// TODO: DataBuffer - add argument and description to function comment
// TODO: StartLba - add argument and description to function comment
// TODO: NumberOfBlocks - add argument and description to function comment
{
EFI_STATUS Status;
UINTN BlocksRemaining;
EFI_LBA Lba64;
UINT8 AtaCommand;
UINT16 SectorCount;
UINT32 ByteCount;
VOID *Buffer;
//
// Using ATA "Read Sectors Ext" command(opcode=0x24) with PIO DATA IN protocol
//
AtaCommand = READ_SECTORS_EXT_CMD;
Buffer = DataBuffer;
BlocksRemaining = NumberOfBlocks;
Lba64 = StartLba;
Status = EFI_SUCCESS;
while (BlocksRemaining > 0) {
if (BlocksRemaining >= 0x10000) {
//
// SectorCount is used to record the number of sectors to be read
// Max 65536 sectors can be transfered at a time.
//
SectorCount = 0xffff;
} else {
SectorCount = (UINT16) BlocksRemaining;
}
//
// ByteCount is the number of bytes that will be read
//
ByteCount = SectorCount * (IdeDev->BlkIo.Media->BlockSize);
//
// call AtaPioDataInExt() to send Read Sector Command and receive data read
//
Status = AtaPioDataInExt (
IdeDev,
Buffer,
ByteCount,
AtaCommand,
Lba64,
SectorCount
);
if (EFI_ERROR (Status)) {
return Status;
}
Lba64 += SectorCount;
Buffer = ((UINT8 *) Buffer + ByteCount);
BlocksRemaining -= SectorCount;
}
return Status;
}
EFI_STATUS
AtaWriteSectorsExt (
IN IDE_BLK_IO_DEV *IdeDev,
IN VOID *DataBuffer,
IN EFI_LBA StartLba,
IN UINTN NumberOfBlocks
)
/*++
Name:
AtaWriteSectorsExt
Purpose:
This function is called by the AtaBlkIoWriteBlocks() to perform
writing onto media in block unit. The function has been enhanced to
support >120GB access and transfer at most 65536 blocks per 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.
VOID IN *DataBuffer
A pointer to the source buffer for the data.
EFI_LBA IN Lba
The starting logical block address to write onto
the device media.
UINTN IN NumberOfBlocks
The number of transfer data blocks.
Returns:
return status is fully dependent on the return status
of AtaPioDataOutExt() function.
Notes:
--*/
// TODO: function comment is missing 'Routine Description:'
// TODO: function comment is missing 'Arguments:'
// TODO: IdeDev - add argument and description to function comment
// TODO: DataBuffer - add argument and description to function comment
// TODO: StartLba - add argument and description to function comment
// TODO: NumberOfBlocks - add argument and description to function comment
{
EFI_STATUS Status;
EFI_LBA Lba64;
UINTN BlocksRemaining;
UINT8 AtaCommand;
UINT16 SectorCount;
UINT32 ByteCount;
VOID *Buffer;
//
// Using ATA "Write Sectors Ext" cmd(opcode=0x24) with PIO DATA OUT protocol
//
AtaCommand = WRITE_SECTORS_EXT_CMD;
Lba64 = StartLba;
Buffer = DataBuffer;
BlocksRemaining = NumberOfBlocks;
Status = EFI_SUCCESS;
while (BlocksRemaining > 0) {
if (BlocksRemaining >= 0x10000) {
//
// SectorCount is used to record the number of sectors to be written.
// Max 65536 sectors can be transfered at a time.
//
SectorCount = 0xffff;
} else {
SectorCount = (UINT16) BlocksRemaining;
}
//
// ByteCount is the number of bytes that will be written
//
ByteCount = SectorCount * (IdeDev->BlkIo.Media->BlockSize);
//
// Call AtaPioDataOutExt() to send "Write Sectors Ext" Command
//
Status = AtaPioDataOutExt (
IdeDev,
Buffer,
ByteCount,
AtaCommand,
Lba64,
SectorCount
);
if (EFI_ERROR (Status)) {
return Status;
}
Lba64 += SectorCount;
Buffer = ((UINT8 *) Buffer + ByteCount);
BlocksRemaining -= SectorCount;
}
return Status;
}
EFI_STATUS
AtaPioDataInExt (
IN IDE_BLK_IO_DEV *IdeDev,
IN OUT VOID *Buffer,
IN UINT32 ByteCount,
IN UINT8 AtaCommand,
IN EFI_LBA StartLba,
IN UINT16 SectorCount
)
/*++
Name:
AtaPioDataInExt
Purpose:
This function is used to send out ATA commands conforms to the
PIO Data In Protocol, supporting ATA/ATAPI-6 standard
Comparing with ATA-3 data in protocol, we have two differents here:
1. Do NOT wait for DRQ clear before sending command into IDE device.(the
wait will frequently fail... cause writing function return error)
2. Do NOT wait for DRQ clear after all data readed.(the wait greatly
slow down writing performance by 100 times!)
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.
VOID IN OUT *Buffer
buffer contained data transferred from device to host.
UINT32 IN ByteCount
data size in byte unit of the buffer.
UINT8 IN AtaCommand
value of the Command Register
EFI_LBA IN StartLba
the start LBA of this transaction
UINT16 IN SectorCount
the count of sectors to be transfered
Returns:
EFI_SUCCESS
send out the ATA command and device send required
data successfully.
EFI_DEVICE_ERROR
command sent failed.
Notes:
--*/
// TODO: function comment is missing 'Routine Description:'
// TODO: function comment is missing 'Arguments:'
// TODO: IdeDev - add argument and description to function comment
// TODO: Buffer - add argument and description to function comment
// TODO: ByteCount - add argument and description to function comment
// TODO: AtaCommand - add argument and description to function comment
// TODO: StartLba - add argument and description to function comment
// TODO: SectorCount - add argument and description to function comment
{
UINT8 DevSel;
UINT8 SectorCount8;
UINT8 LbaLow;
UINT8 LbaMid;
UINT8 LbaHigh;
UINTN WordCount;
UINTN Increment;
UINT16 *Buffer16;
EFI_STATUS Status;
Status = WaitForBSYClear (IdeDev, ATATIMEOUT);
if (EFI_ERROR (Status)) {
return EFI_DEVICE_ERROR;
}
//
// Select device, set bit6 as 1 to indicate LBA mode is used
//
DevSel = (UINT8) (IdeDev->Device << 4);
DevSel |= 0x40;
IDEWritePortB (
IdeDev->PciIo,
IdeDev->IoPort->Head,
DevSel
);
//
// Wait for DRDY singnal asserting. ATAPI device needn't wait
//
if ( (IdeDev->Type == IdeHardDisk) ||
(IdeDev->Type == Ide48bitAddressingHardDisk)) {
Status = DRDYReady (IdeDev, ATATIMEOUT);
if (EFI_ERROR (Status)) {
return EFI_DEVICE_ERROR;
}
}
//
// Fill feature register if needed
//
if (AtaCommand == SET_FEATURES_CMD) {
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Reg1.Feature, 0x03);
}
//
// 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 (StartLba, 24);
LbaMid = (UINT8) RShiftU64 (StartLba, 32);
LbaHigh = (UINT8) RShiftU64 (StartLba, 40);
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->SectorNumber, LbaLow);
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->CylinderLsb, LbaMid);
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->CylinderMsb, LbaHigh);
LbaLow = (UINT8) StartLba;
LbaMid = (UINT8) RShiftU64 (StartLba, 8);
LbaHigh = (UINT8) RShiftU64 (StartLba, 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, invoking the processing of this command
//
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Reg.Command, AtaCommand);
Buffer16 = (UINT16 *) Buffer;
//
// According to PIO data in protocol, host can perform a series of reads to
// the data register after each time device set DRQ ready;
//
//
// 256 words
//
Increment = 256;
//
// used to record bytes of currently transfered data
//
WordCount = 0;
while (WordCount < ByteCount / 2) {
//
// Poll DRQ bit set, data transfer can be performed only when DRQ is ready.
//
Status = DRQReady2 (IdeDev, ATATIMEOUT);
if (EFI_ERROR (Status)) {
return EFI_DEVICE_ERROR;
}
Status = CheckErrorStatus (IdeDev);
if (EFI_ERROR (Status)) {
return EFI_DEVICE_ERROR;
}
//
// Get the byte count for one series of read
//
if ((WordCount + Increment) > ByteCount / 2) {
Increment = ByteCount / 2 - WordCount;
}
IDEReadPortWMultiple (
IdeDev->PciIo,
IdeDev->IoPort->Data,
Increment,
Buffer16
);
WordCount += Increment;
Buffer16 += Increment;
}
return CheckErrorStatus (IdeDev);
}
EFI_STATUS
AtaPioDataOutExt (
IN IDE_BLK_IO_DEV *IdeDev,
IN VOID *Buffer,
IN UINT32 ByteCount,
IN UINT8 AtaCommand,
IN EFI_LBA StartLba,
IN UINT16 SectorCount
)
/*++
Name:
AtaPioDataOutExt
Purpose:
This function is used to send out ATA commands conforms to the
PIO Data Out Protocol, supporting ATA/ATAPI-6 standard
Comparing with ATA-3 data out protocol, we have two differents here:
1. Do NOT wait for DRQ clear before sending command into IDE device.(the
wait will frequently fail... cause writing function return error)
2. Do NOT wait for DRQ clear after all data readed.(the wait greatly
slow down writing performance by 100 times!)
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.
VOID IN *Buffer
buffer contained data transferred from host to device.
UINT32 IN ByteCount
data size in byte unit of the buffer.
UINT8 IN AtaCommand
value of the Command Register
EFI_LBA IN StartLba
the start LBA of this transaction
UINT16 IN SectorCount
the count of sectors to be transfered
Returns:
EFI_SUCCESS
send out the ATA command and device receive required
data successfully.
EFI_DEVICE_ERROR
command sent failed.
Notes:
--*/
// TODO: function comment is missing 'Routine Description:'
// TODO: function comment is missing 'Arguments:'
// TODO: IdeDev - add argument and description to function comment
// TODO: Buffer - add argument and description to function comment
// TODO: ByteCount - add argument and description to function comment
// TODO: AtaCommand - add argument and description to function comment
// TODO: StartLba - add argument and description to function comment
// TODO: SectorCount - add argument and description to function comment
{
UINT8 DevSel;
UINT8 SectorCount8;
UINT8 LbaLow;
UINT8 LbaMid;
UINT8 LbaHigh;
UINTN WordCount;
UINTN Increment;
UINT16 *Buffer16;
EFI_STATUS Status;
Status = WaitForBSYClear (IdeDev, ATATIMEOUT);
if (EFI_ERROR (Status)) {
return EFI_DEVICE_ERROR;
}
//
// Select device. Set bit6 as 1 to indicate LBA mode is used
//
DevSel = (UINT8) (IdeDev->Device << 4);
DevSel |= 0x40;
IDEWritePortB (
IdeDev->PciIo,
IdeDev->IoPort->Head,
DevSel
);
//
// Wait for DRDY singnal asserting.
//
Status = DRDYReady (IdeDev, ATATIMEOUT);
if (EFI_ERROR (Status)) {
return EFI_DEVICE_ERROR;
}
//
// Fill feature register if needed
//
if (AtaCommand == SET_FEATURES_CMD) {
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Reg1.Feature, 0x03);
}
//
// 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 (StartLba, 24);
LbaMid = (UINT8) RShiftU64 (StartLba, 32);
LbaHigh = (UINT8) RShiftU64 (StartLba, 40);
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->SectorNumber, LbaLow);
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->CylinderLsb, LbaMid);
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->CylinderMsb, LbaHigh);
LbaLow = (UINT8) StartLba;
LbaMid = (UINT8) RShiftU64 (StartLba, 8);
LbaHigh = (UINT8) RShiftU64 (StartLba, 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, invoking the processing of this command
//
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Reg.Command, AtaCommand);
Buffer16 = (UINT16 *) Buffer;
//
// According to PIO Data Out protocol, host can perform a series of writes to
// the data register after each time device set DRQ ready;
//
Increment = 256;
//
// used to record bytes of currently transfered data
//
WordCount = 0;
while (WordCount < ByteCount / 2) {
//
// Poll DRQ bit set, data transfer can be performed only when DRQ is ready.
//
Status = DRQReady2 (IdeDev, ATATIMEOUT);
if (EFI_ERROR (Status)) {
return EFI_DEVICE_ERROR;
}
Status = CheckErrorStatus (IdeDev);
if (EFI_ERROR (Status)) {
return EFI_DEVICE_ERROR;
}
//
// Write data into device by one series of writing to data register
//
if ((WordCount + Increment) > ByteCount / 2) {
Increment = ByteCount / 2 - WordCount;
}
IDEWritePortWMultiple (
IdeDev->PciIo,
IdeDev->IoPort->Data,
Increment,
Buffer16
);
WordCount += Increment;
Buffer16 += Increment;
}
//
// while
//
return CheckErrorStatus (IdeDev);
}
VOID
AtaSMARTSupport (
IN IDE_BLK_IO_DEV *IdeDev
)
/*++
Name:
AtaSMARTSupport
Purpose:
Enable SMART of the disk if supported
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:
NONE
--*/
// TODO: function comment is missing 'Routine Description:'
// TODO: function comment is missing 'Arguments:'
// TODO: IdeDev - add argument and description to function comment
{
EFI_STATUS Status;
BOOLEAN SMARTSupported;
UINT8 Device;
EFI_IDENTIFY_DATA *TmpAtaIdentifyPointer;
UINT8 DeviceSelect;
UINT8 LBAMid;
UINT8 LBAHigh;
//
// Detect if the device supports S.M.A.R.T.
//
if ((IdeDev->pIdData->AtaData.command_set_supported_83 & 0xc000) != 0x4000) {
//
// Data in word 82 is not valid (bit15 shall be zero and bit14 shall be to one)
//
return ;
} else {
if ((IdeDev->pIdData->AtaData.command_set_supported_82 & 0x0001) != 0x0001) {
//
// S.M.A.R.T is not supported by the device
//
SMARTSupported = FALSE;
} else {
SMARTSupported = TRUE;
}
}
if (!SMARTSupported) {
//
// Report nonsupport status code
//
REPORT_STATUS_CODE (
EFI_ERROR_CODE | EFI_ERROR_MINOR,
(EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_NOTSUPPORTED)
);
} else {
//
// Enable this feature
//
REPORT_STATUS_CODE (
EFI_PROGRESS_CODE,
(EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_ENABLE)
);
Device = (UINT8) ((IdeDev->Device << 4) | 0xe0);
Status = AtaNonDataCommandIn (
IdeDev,
ATA_SMART_CMD,
Device,
ATA_SMART_ENABLE_OPERATION,
0,
0,
ATA_CONSTANT_4F,
ATA_CONSTANT_C2
);
//
// Detect if this feature is enabled
//
TmpAtaIdentifyPointer = (EFI_IDENTIFY_DATA *) AllocateZeroPool (sizeof (EFI_IDENTIFY_DATA));
DeviceSelect = (UINT8) ((IdeDev->Device) << 4);
Status = AtaPioDataIn (
IdeDev,
(VOID *) TmpAtaIdentifyPointer,
sizeof (EFI_IDENTIFY_DATA),
IDENTIFY_DRIVE_CMD,
DeviceSelect,
0,
0,
0,
0
);
if (EFI_ERROR (Status)) {
gBS->FreePool (TmpAtaIdentifyPointer);
return ;
}
//
// Check if the feature is enabled
//
if ((TmpAtaIdentifyPointer->AtaData.command_set_feature_enb_85 & 0x0001) == 0x0001) {
//
// Read status data
//
AtaNonDataCommandIn (
IdeDev,
ATA_SMART_CMD,
Device,
ATA_SMART_RETURN_STATUS,
0,
0,
ATA_CONSTANT_4F,
ATA_CONSTANT_C2
);
LBAMid = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->CylinderLsb);
LBAHigh = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->CylinderMsb);
if ((LBAMid == 0x4f) && (LBAHigh == 0xc2)) {
//
// The threshold exceeded condition is not detected by the device
//
REPORT_STATUS_CODE (
EFI_PROGRESS_CODE,
(EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_UNDERTHRESHOLD)
);
} else if ((LBAMid == 0xf4) && (LBAHigh == 0x2c)) {
//
// The threshold exceeded condition is detected by the device
//
REPORT_STATUS_CODE (
EFI_PROGRESS_CODE,
(EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_OVERTHRESHOLD)
);
}
} else {
//
// Report disabled status code
//
REPORT_STATUS_CODE (
EFI_ERROR_CODE | EFI_ERROR_MINOR,
(EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_DISABLED)
);
}
gBS->FreePool (TmpAtaIdentifyPointer);
}
return ;
}
EFI_STATUS
AtaCommandIssueExt (
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_DEVICE_ERROR - Error executing commands on this device
--*/
{
EFI_STATUS Status;
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);
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->SectorNumber, LbaLow);
LbaLow = (UINT8) LbaAddress;
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->SectorNumber, LbaLow);
LbaMid = (UINT8) RShiftU64 (LbaAddress, 32);
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->CylinderLsb, LbaMid);
LbaMid = (UINT8) RShiftU64 (LbaAddress, 8);
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->CylinderLsb, LbaMid);
LbaHigh = (UINT8) RShiftU64 (LbaAddress, 40);
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->CylinderMsb, LbaHigh);
LbaHigh = (UINT8) RShiftU64 (LbaAddress, 16);
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->CylinderMsb, LbaHigh);
//
// Work around for Segate 160G disk writing
//
gBS->Stall (1800);
//
// Send command via Command Register
//
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Reg.Command, AtaCommand);
//
// Stall at least 400ns
//
gBS->Stall (100);
return EFI_SUCCESS;
}
EFI_STATUS
AtaCommandIssue (
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_DEVICE_ERROR - Error executing commands on this device
--*/
{
EFI_STATUS Status;
UINT8 SectorCount8;
UINT8 Feature8;
UINT8 Lba0;
UINT8 Lba1;
UINT8 Lba2;
UINT8 Lba3;
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;
}
Lba0 = (UINT8) LbaAddress;
Lba1 = (UINT8) RShiftU64 (LbaAddress, 8);
Lba2 = (UINT8) RShiftU64 (LbaAddress, 16);
Lba3 = (UINT8) RShiftU64 (LbaAddress, 24);
Device |= Lba3;
//
// 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;
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;
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->SectorCount, SectorCount8);
//
// Fill the start LBA registers, which are also two-byte FIFO
//
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->SectorNumber, Lba0);
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->CylinderLsb, Lba1);
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->CylinderMsb, Lba2);
//
// Send command via Command Register
//
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Reg.Command, AtaCommand);
//
// Stall at least 400ns
//
gBS->Stall (100);
return EFI_SUCCESS;
}
EFI_STATUS
AtaUdmaReadExt (
IN IDE_BLK_IO_DEV *IdeDev,
IN VOID *DataBuffer,
IN EFI_LBA StartLba,
IN UINTN NumberOfBlocks
)
/*++
Name:
AtaUdmaReadExt
Purpose:
This function is called by the AtaBlkIoReadBlocks() to perform
reading from media in block unit. The function has been enhanced to
support >120GB access and transfer at most 65536 blocks per 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.
VOID IN *DataBuffer
A pointer to the destination buffer for the data.
EFI_LBA IN StartLba
The starting logical block address to read from
on the device media.
UINTN IN NumberOfBlocks
The number of transfer data blocks.
Returns:
The device status of UDMA operation. If the operation is
successful, return EFI_SUCCESS.
--*/
// TODO: function comment is missing 'Routine Description:'
// TODO: function comment is missing 'Arguments:'
// TODO: IdeDev - add argument and description to function comment
// TODO: DataBuffer - add argument and description to function comment
// TODO: StartLba - add argument and description to function comment
// TODO: NumberOfBlocks - add argument and description to function comment
// TODO: EFI_UNSUPPORTED - add return value to function comment
// TODO: EFI_DEVICE_ERROR - add return value to function comment
// TODO: EFI_DEVICE_ERROR - add return value to function comment
// TODO: EFI_DEVICE_ERROR - add return value to function comment
{
IDE_DMA_PRD *PrdAddr;
IDE_DMA_PRD *UsedPrdAddr;
IDE_DMA_PRD *TempPrdAddr;
UINT8 RegisterValue;
UINT8 Device;
UINT64 IoPortForBmic;
UINT64 IoPortForBmis;
UINT64 IoPortForBmid;
EFI_STATUS Status;
UINTN PrdTableNum;
UINTN ByteCount;
UINTN ByteAvailable;
UINT8 *PrdBuffer;
UINTN RemainBlockNum;
UINT8 DeviceControl;
//
// Channel and device differential. Select device.
//
Device = (UINT8) ((IdeDev->Device << 4) | 0xe0);
//
// Enable interrupt to support UDMA and Select device
//
DeviceControl = 0;
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Alt.DeviceControl, DeviceControl);
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Head, Device);
if (IdePrimary == IdeDev->Channel) {
IoPortForBmic = IdeDev->IoPort->BusMasterBaseAddr + BMICP_OFFSET;
IoPortForBmis = IdeDev->IoPort->BusMasterBaseAddr + BMISP_OFFSET;
IoPortForBmid = IdeDev->IoPort->BusMasterBaseAddr + BMIDP_OFFSET;
} else {
if (IdeSecondary == IdeDev->Channel) {
IoPortForBmic = IdeDev->IoPort->BusMasterBaseAddr + BMICS_OFFSET;
IoPortForBmis = IdeDev->IoPort->BusMasterBaseAddr + BMISS_OFFSET;
IoPortForBmid = IdeDev->IoPort->BusMasterBaseAddr + BMIDS_OFFSET;
} else {
return EFI_UNSUPPORTED;
}
}
RemainBlockNum = NumberOfBlocks;
while (RemainBlockNum > 0) {
if (RemainBlockNum >= MAX_DMA_EXT_COMMAND_SECTORS) {
//
// SectorCount is used to record the number of sectors to be read
// Max 65536 sectors can be transfered at a time.
//
NumberOfBlocks = MAX_DMA_EXT_COMMAND_SECTORS;
RemainBlockNum -= MAX_DMA_EXT_COMMAND_SECTORS;
} else {
NumberOfBlocks = (UINT16) RemainBlockNum;
RemainBlockNum = 0;
}
//
// Calculate the number of PRD table to make sure the memory region
// not cross 64K boundary
//
ByteCount = NumberOfBlocks * IdeDev->BlkIo.Media->BlockSize;
PrdTableNum = ((ByteCount >> 16) + 1) + 1;
//
// Build PRD table
//
PrdAddr = (IDE_DMA_PRD *) AllocateZeroPool ((2 * PrdTableNum * sizeof (IDE_DMA_PRD)));
//
// To make sure PRD is allocated in one 64K page
//
if (((UINTN) PrdAddr & 0x0FFFF) > (((UINTN) PrdAddr + PrdTableNum * sizeof (IDE_DMA_PRD) - 1) & 0x0FFFF)) {
UsedPrdAddr = (IDE_DMA_PRD *) ((UINTN) ((UINT8 *) PrdAddr + 0x10000) & 0xFFFF0000);
} else {
if ((UINTN) PrdAddr & 0x03) {
UsedPrdAddr = (IDE_DMA_PRD *) ((UINTN) ((UINT8 *) PrdAddr + 0x04) & 0xFFFFFFFC);
} else {
UsedPrdAddr = PrdAddr;
}
}
//
// Build the PRD table
//
PrdBuffer = DataBuffer;
TempPrdAddr = UsedPrdAddr;
while (TRUE) {
ByteAvailable = 0x10000 - ((UINTN) PrdBuffer & 0xFFFF);
if (ByteCount <= ByteAvailable) {
TempPrdAddr->RegionBaseAddr = (UINT32) ((UINTN) PrdBuffer);
TempPrdAddr->ByteCount = (UINT16) ByteCount;
TempPrdAddr->EndOfTable = 0x8000;
break;
}
TempPrdAddr->RegionBaseAddr = (UINT32) ((UINTN) PrdBuffer);
TempPrdAddr->ByteCount = (UINT16) ByteAvailable;
ByteCount -= ByteAvailable;
PrdBuffer += ByteAvailable;
TempPrdAddr++;
}
//
// Set the base address to BMID register
//
IdeDev->PciIo->Io.Write (
IdeDev->PciIo,
EfiPciIoWidthUint32,
EFI_PCI_IO_PASS_THROUGH_BAR,
IoPortForBmid,
1,
&UsedPrdAddr
);
//
// Set BMIC register to identify the operation direction
//
IdeDev->PciIo->Io.Read (
IdeDev->PciIo,
EfiPciIoWidthUint8,
EFI_PCI_IO_PASS_THROUGH_BAR,
IoPortForBmic,
1,
&RegisterValue
);
RegisterValue |= BMIC_nREAD;
IdeDev->PciIo->Io.Write (
IdeDev->PciIo,
EfiPciIoWidthUint8,
EFI_PCI_IO_PASS_THROUGH_BAR,
IoPortForBmic,
1,
&RegisterValue
);
//
// Read BMIS register and clear ERROR and INTR bit
//
IdeDev->PciIo->Io.Read (
IdeDev->PciIo,
EfiPciIoWidthUint8,
EFI_PCI_IO_PASS_THROUGH_BAR,
IoPortForBmis,
1,
&RegisterValue
);
RegisterValue |= BMIS_INTERRUPT | BMIS_ERROR;
IdeDev->PciIo->Io.Write (
IdeDev->PciIo,
EfiPciIoWidthUint8,
EFI_PCI_IO_PASS_THROUGH_BAR,
IoPortForBmis,
1,
&RegisterValue
);
//
// Issue READ DMA EXT command
//
Status = AtaCommandIssueExt (
IdeDev,
READ_DMA_EXT_CMD,
Device,
0,
(UINT16) NumberOfBlocks,
StartLba
);
if (EFI_ERROR (Status)) {
gBS->FreePool (PrdAddr);
return EFI_DEVICE_ERROR;
}
//
// Set START bit of BMIC register
//
IdeDev->PciIo->Io.Read (
IdeDev->PciIo,
EfiPciIoWidthUint8,
EFI_PCI_IO_PASS_THROUGH_BAR,
IoPortForBmic,
1,
&RegisterValue
);
RegisterValue |= BMIC_START;
IdeDev->PciIo->Io.Write (
IdeDev->PciIo,
EfiPciIoWidthUint8,
EFI_PCI_IO_PASS_THROUGH_BAR,
IoPortForBmic,
1,
&RegisterValue
);
//
// Check the INTERRUPT and ERROR bit of BMIS
//
while (TRUE) {
IdeDev->PciIo->Io.Read (
IdeDev->PciIo,
EfiPciIoWidthUint8,
EFI_PCI_IO_PASS_THROUGH_BAR,
IoPortForBmis,
1,
&RegisterValue
);
if (RegisterValue & (BMIS_INTERRUPT | BMIS_ERROR)) {
if (RegisterValue & BMIS_ERROR) {
gBS->FreePool (PrdAddr);
return EFI_DEVICE_ERROR;
}
break;
}
gBS->Stall (1000);
}
gBS->FreePool (PrdAddr);
//
// Set START bit of BMIC register
//
IdeDev->PciIo->Io.Read (
IdeDev->PciIo,
EfiPciIoWidthUint8,
EFI_PCI_IO_PASS_THROUGH_BAR,
IoPortForBmic,
1,
&RegisterValue
);
RegisterValue &= ~((UINT8) BMIC_START);
IdeDev->PciIo->Io.Write (
IdeDev->PciIo,
EfiPciIoWidthUint8,
EFI_PCI_IO_PASS_THROUGH_BAR,
IoPortForBmic,
1,
&RegisterValue
);
if (RegisterValue & BMIS_ERROR) {
return EFI_DEVICE_ERROR;
}
DataBuffer = (UINT8 *) DataBuffer + NumberOfBlocks * IdeDev->BlkIo.Media->BlockSize;
StartLba += NumberOfBlocks;
}
return EFI_SUCCESS;
}
EFI_STATUS
AtaUdmaRead (
IN IDE_BLK_IO_DEV *IdeDev,
IN VOID *DataBuffer,
IN EFI_LBA StartLba,
IN UINTN NumberOfBlocks
)
/*++
Name:
AtaUdmaRead
Purpose:
This function is called by the AtaBlkIoReadBlocks() to perform
reading from media in block unit. The function has been enhanced to
support >120GB access and transfer at most 65536 blocks per 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.
VOID IN *DataBuffer
A pointer to the destination buffer for the data.
EFI_LBA IN StartLba
The starting logical block address to read from
on the device media.
UINTN IN NumberOfBlocks
The number of transfer data blocks.
Returns:
The device status of UDMA operation. If the operation is
successful, return EFI_SUCCESS.
--*/
// TODO: function comment is missing 'Routine Description:'
// TODO: function comment is missing 'Arguments:'
// TODO: IdeDev - add argument and description to function comment
// TODO: DataBuffer - add argument and description to function comment
// TODO: StartLba - add argument and description to function comment
// TODO: NumberOfBlocks - add argument and description to function comment
// TODO: EFI_UNSUPPORTED - add return value to function comment
// TODO: EFI_DEVICE_ERROR - add return value to function comment
// TODO: EFI_DEVICE_ERROR - add return value to function comment
// TODO: EFI_DEVICE_ERROR - add return value to function comment
{
IDE_DMA_PRD *PrdAddr;
IDE_DMA_PRD *UsedPrdAddr;
IDE_DMA_PRD *TempPrdAddr;
UINT8 RegisterValue;
UINT8 Device;
UINT64 IoPortForBmic;
UINT64 IoPortForBmis;
UINT64 IoPortForBmid;
EFI_STATUS Status;
UINTN PrdTableNum;
UINTN ByteCount;
UINTN ByteAvailable;
UINT8 *PrdBuffer;
UINTN RemainBlockNum;
UINT8 DeviceControl;
//
// Channel and device differential
//
Device = (UINT8) ((IdeDev->Device << 4) | 0xe0);
//
// Enable interrupt to support UDMA and Select device
//
DeviceControl = 0;
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Alt.DeviceControl, DeviceControl);
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Head, Device);
if (IdePrimary == IdeDev->Channel) {
IoPortForBmic = IdeDev->IoPort->BusMasterBaseAddr + BMICP_OFFSET;
IoPortForBmis = IdeDev->IoPort->BusMasterBaseAddr + BMISP_OFFSET;
IoPortForBmid = IdeDev->IoPort->BusMasterBaseAddr + BMIDP_OFFSET;
} else {
if (IdeSecondary == IdeDev->Channel) {
IoPortForBmic = IdeDev->IoPort->BusMasterBaseAddr + BMICS_OFFSET;
IoPortForBmis = IdeDev->IoPort->BusMasterBaseAddr + BMISS_OFFSET;
IoPortForBmid = IdeDev->IoPort->BusMasterBaseAddr + BMIDS_OFFSET;
} else {
return EFI_UNSUPPORTED;
}
}
RemainBlockNum = NumberOfBlocks;
while (RemainBlockNum > 0) {
if (RemainBlockNum >= MAX_DMA_COMMAND_SECTORS) {
//
// SectorCount is used to record the number of sectors to be read
// Max 256 sectors can be transfered at a time.
//
NumberOfBlocks = MAX_DMA_COMMAND_SECTORS;
RemainBlockNum -= MAX_DMA_COMMAND_SECTORS;
} else {
NumberOfBlocks = (UINT16) RemainBlockNum;
RemainBlockNum = 0;
}
//
// Calculate the number of PRD table to make sure the memory region
// not cross 64K boundary
//
ByteCount = NumberOfBlocks * IdeDev->BlkIo.Media->BlockSize;
PrdTableNum = ((ByteCount >> 16) + 1) + 1;
//
// Build PRD table
//
PrdAddr = (IDE_DMA_PRD *) AllocateZeroPool ((2 * PrdTableNum * sizeof (IDE_DMA_PRD)));
//
// To make sure PRD is allocated in one 64K page
//
if (((UINTN) PrdAddr & 0x0FFFF) > (((UINTN) PrdAddr + PrdTableNum * sizeof (IDE_DMA_PRD) - 1) & 0x0FFFF)) {
UsedPrdAddr = (IDE_DMA_PRD *) ((UINTN) ((UINT8 *) PrdAddr + 0x10000) & 0xFFFF0000);
} else {
if ((UINTN) PrdAddr & 0x03) {
UsedPrdAddr = (IDE_DMA_PRD *) ((UINTN) ((UINT8 *) PrdAddr + 0x04) & 0xFFFFFFFC);
} else {
UsedPrdAddr = PrdAddr;
}
}
//
// Build the PRD table
//
PrdBuffer = DataBuffer;
TempPrdAddr = UsedPrdAddr;
while (TRUE) {
ByteAvailable = 0x10000 - ((UINTN) PrdBuffer & 0xFFFF);
if (ByteCount <= ByteAvailable) {
TempPrdAddr->RegionBaseAddr = (UINT32) ((UINTN) PrdBuffer);
TempPrdAddr->ByteCount = (UINT16) ByteCount;
TempPrdAddr->EndOfTable = 0x8000;
break;
}
TempPrdAddr->RegionBaseAddr = (UINT32) ((UINTN) PrdBuffer);
TempPrdAddr->ByteCount = (UINT16) ByteAvailable;
ByteCount -= ByteAvailable;
PrdBuffer += ByteAvailable;
TempPrdAddr++;
}
//
// Set the base address to BMID register
//
IdeDev->PciIo->Io.Write (
IdeDev->PciIo,
EfiPciIoWidthUint32,
EFI_PCI_IO_PASS_THROUGH_BAR,
IoPortForBmid,
1,
&UsedPrdAddr
);
//
// Set BMIC register to identify the operation direction
//
IdeDev->PciIo->Io.Read (
IdeDev->PciIo,
EfiPciIoWidthUint8,
EFI_PCI_IO_PASS_THROUGH_BAR,
IoPortForBmic,
1,
&RegisterValue
);
RegisterValue |= BMIC_nREAD;
IdeDev->PciIo->Io.Write (
IdeDev->PciIo,
EfiPciIoWidthUint8,
EFI_PCI_IO_PASS_THROUGH_BAR,
IoPortForBmic,
1,
&RegisterValue
);
//
// Read BMIS register and clear ERROR and INTR bit
//
IdeDev->PciIo->Io.Read (
IdeDev->PciIo,
EfiPciIoWidthUint8,
EFI_PCI_IO_PASS_THROUGH_BAR,
IoPortForBmis,
1,
&RegisterValue
);
RegisterValue |= (BMIS_INTERRUPT | BMIS_ERROR);
IdeDev->PciIo->Io.Write (
IdeDev->PciIo,
EfiPciIoWidthUint8,
EFI_PCI_IO_PASS_THROUGH_BAR,
IoPortForBmis,
1,
&RegisterValue
);
//
// Issue READ DMA command
//
Status = AtaCommandIssue (
IdeDev,
READ_DMA_CMD,
Device,
0,
(UINT16) NumberOfBlocks,
StartLba
);
if (EFI_ERROR (Status)) {
gBS->FreePool (PrdAddr);
return EFI_DEVICE_ERROR;
}
//
// Set START bit of BMIC register
//
IdeDev->PciIo->Io.Read (
IdeDev->PciIo,
EfiPciIoWidthUint8,
EFI_PCI_IO_PASS_THROUGH_BAR,
IoPortForBmic,
1,
&RegisterValue
);
RegisterValue |= BMIC_START;
IdeDev->PciIo->Io.Write (
IdeDev->PciIo,
EfiPciIoWidthUint8,
EFI_PCI_IO_PASS_THROUGH_BAR,
IoPortForBmic,
1,
&RegisterValue
);
//
// Check the INTERRUPT and ERROR bit of BMIS
//
while (TRUE) {
IdeDev->PciIo->Io.Read (
IdeDev->PciIo,
EfiPciIoWidthUint8,
EFI_PCI_IO_PASS_THROUGH_BAR,
IoPortForBmis,
1,
&RegisterValue
);
if (RegisterValue & (BMIS_INTERRUPT | BMIS_ERROR)) {
if (RegisterValue & BMIS_ERROR) {
gBS->FreePool (PrdAddr);
return EFI_DEVICE_ERROR;
}
break;
}
gBS->Stall (1000);
}
gBS->FreePool (PrdAddr);
//
// Set START bit of BMIC register
//
IdeDev->PciIo->Io.Read (
IdeDev->PciIo,
EfiPciIoWidthUint8,
EFI_PCI_IO_PASS_THROUGH_BAR,
IoPortForBmic,
1,
&RegisterValue
);
RegisterValue &= ~((UINT8) BMIC_START);
IdeDev->PciIo->Io.Write (
IdeDev->PciIo,
EfiPciIoWidthUint8,
EFI_PCI_IO_PASS_THROUGH_BAR,
IoPortForBmic,
1,
&RegisterValue
);
if (RegisterValue & BMIS_ERROR) {
return EFI_DEVICE_ERROR;
}
DataBuffer = (UINT8 *) DataBuffer + NumberOfBlocks * IdeDev->BlkIo.Media->BlockSize;
StartLba += NumberOfBlocks;
}
return EFI_SUCCESS;
}
EFI_STATUS
AtaUdmaWriteExt (
IN IDE_BLK_IO_DEV *IdeDev,
IN VOID *DataBuffer,
IN EFI_LBA StartLba,
IN UINTN NumberOfBlocks
)
/*++
Name:
AtaUdmaWriteExt
Purpose:
This function is called by the AtaBlkIoWriteBlocks() to perform
writing to media in block unit. The function has been enhanced to
support >120GB access and transfer at most 65536 blocks per 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.
VOID IN *DataBuffer
A pointer to the source buffer for the data.
EFI_LBA IN StartLba
The starting logical block address to write to
on the device media.
UINTN IN NumberOfBlocks
The number of transfer data blocks.
Returns:
The device status of UDMA operation. If the operation is
successful, return EFI_SUCCESS.
--*/
// TODO: function comment is missing 'Routine Description:'
// TODO: function comment is missing 'Arguments:'
// TODO: IdeDev - add argument and description to function comment
// TODO: DataBuffer - add argument and description to function comment
// TODO: StartLba - add argument and description to function comment
// TODO: NumberOfBlocks - add argument and description to function comment
// TODO: EFI_UNSUPPORTED - add return value to function comment
// TODO: EFI_DEVICE_ERROR - add return value to function comment
// TODO: EFI_DEVICE_ERROR - add return value to function comment
{
IDE_DMA_PRD *PrdAddr;
IDE_DMA_PRD *UsedPrdAddr;
IDE_DMA_PRD *TempPrdAddr;
UINT8 RegisterValue;
UINT8 Device;
UINT64 IoPortForBmic;
UINT64 IoPortForBmis;
UINT64 IoPortForBmid;
EFI_STATUS Status;
UINTN PrdTableNum;
UINTN ByteCount;
UINTN ByteAvailable;
UINT8 *PrdBuffer;
UINTN RemainBlockNum;
UINT8 DeviceControl;
//
// Channel and device differential
//
Device = (UINT8) ((IdeDev->Device << 4) | 0xe0);
//
// Enable interrupt to support UDMA and Select device
//
DeviceControl = 0;
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Alt.DeviceControl, DeviceControl);
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Head, Device);
if (IdePrimary == IdeDev->Channel) {
IoPortForBmic = IdeDev->IoPort->BusMasterBaseAddr + BMICP_OFFSET;
IoPortForBmis = IdeDev->IoPort->BusMasterBaseAddr + BMISP_OFFSET;
IoPortForBmid = IdeDev->IoPort->BusMasterBaseAddr + BMIDP_OFFSET;
} else {
if (IdeSecondary == IdeDev->Channel) {
IoPortForBmic = IdeDev->IoPort->BusMasterBaseAddr + BMICS_OFFSET;
IoPortForBmis = IdeDev->IoPort->BusMasterBaseAddr + BMISS_OFFSET;
IoPortForBmid = IdeDev->IoPort->BusMasterBaseAddr + BMIDS_OFFSET;
} else {
return EFI_UNSUPPORTED;
}
}
RemainBlockNum = NumberOfBlocks;
while (RemainBlockNum > 0) {
if (RemainBlockNum >= MAX_DMA_EXT_COMMAND_SECTORS) {
//
// SectorCount is used to record the number of sectors to be read
// Max 65536 sectors can be transfered at a time.
//
NumberOfBlocks = MAX_DMA_EXT_COMMAND_SECTORS;
RemainBlockNum -= MAX_DMA_EXT_COMMAND_SECTORS;
} else {
NumberOfBlocks = (UINT16) RemainBlockNum;
RemainBlockNum = 0;
}
//
// Calculate the number of PRD table to make sure the memory region
// not cross 64K boundary
//
ByteCount = NumberOfBlocks * IdeDev->BlkIo.Media->BlockSize;
PrdTableNum = ((ByteCount >> 16) + 1) + 1;
//
// Build PRD table
//
PrdAddr = (IDE_DMA_PRD *) AllocateZeroPool ((2 * PrdTableNum * sizeof (IDE_DMA_PRD)));
//
// To make sure PRD is allocated in one 64K page
//
if (((UINTN) PrdAddr & 0x0FFFF) > (((UINTN) PrdAddr + PrdTableNum * sizeof (IDE_DMA_PRD) - 1) & 0x0FFFF)) {
UsedPrdAddr = (IDE_DMA_PRD *) ((UINTN) ((UINT8 *) PrdAddr + 0x10000) & 0xFFFF0000);
} else {
if ((UINTN) PrdAddr & 0x03) {
UsedPrdAddr = (IDE_DMA_PRD *) ((UINTN) ((UINT8 *) PrdAddr + 0x04) & 0xFFFFFFFC);
} else {
UsedPrdAddr = PrdAddr;
}
}
//
// Build the PRD table
//
PrdBuffer = DataBuffer;
TempPrdAddr = UsedPrdAddr;
while (TRUE) {
ByteAvailable = 0x10000 - ((UINTN) PrdBuffer & 0xFFFF);
if (ByteCount <= ByteAvailable) {
TempPrdAddr->RegionBaseAddr = (UINT32) ((UINTN) PrdBuffer);
TempPrdAddr->ByteCount = (UINT16) ByteCount;
TempPrdAddr->EndOfTable = 0x8000;
break;
}
TempPrdAddr->RegionBaseAddr = (UINT32) ((UINTN) PrdBuffer);
TempPrdAddr->ByteCount = (UINT16) ByteAvailable;
ByteCount -= ByteAvailable;
PrdBuffer += ByteAvailable;
TempPrdAddr++;
}
//
// Set the base address to BMID register
//
IdeDev->PciIo->Io.Write (
IdeDev->PciIo,
EfiPciIoWidthUint32,
EFI_PCI_IO_PASS_THROUGH_BAR,
IoPortForBmid,
1,
&UsedPrdAddr
);
//
// Set BMIC register to identify the operation direction
//
IdeDev->PciIo->Io.Read (
IdeDev->PciIo,
EfiPciIoWidthUint8,
EFI_PCI_IO_PASS_THROUGH_BAR,
IoPortForBmic,
1,
&RegisterValue
);
//
// 0000 1000
//
RegisterValue &= ~((UINT8) BMIC_nREAD);
IdeDev->PciIo->Io.Write (
IdeDev->PciIo,
EfiPciIoWidthUint8,
EFI_PCI_IO_PASS_THROUGH_BAR,
IoPortForBmic,
1,
&RegisterValue
);
//
// Read BMIS register and clear ERROR and INTR bit
//
IdeDev->PciIo->Io.Read (
IdeDev->PciIo,
EfiPciIoWidthUint8,
EFI_PCI_IO_PASS_THROUGH_BAR,
IoPortForBmis,
1,
&RegisterValue
);
RegisterValue |= (BMIS_INTERRUPT | BMIS_ERROR);
IdeDev->PciIo->Io.Write (
IdeDev->PciIo,
EfiPciIoWidthUint8,
EFI_PCI_IO_PASS_THROUGH_BAR,
IoPortForBmis,
1,
&RegisterValue
);
//
// Issue WRITE DMA EXT command
//
Status = AtaCommandIssueExt (
IdeDev,
WRITE_DMA_EXT_CMD,
Device,
0,
(UINT16) NumberOfBlocks,
StartLba
);
if (EFI_ERROR (Status)) {
gBS->FreePool (PrdAddr);
return EFI_DEVICE_ERROR;
}
//
// Set START bit of BMIC register
//
IdeDev->PciIo->Io.Read (
IdeDev->PciIo,
EfiPciIoWidthUint8,
EFI_PCI_IO_PASS_THROUGH_BAR,
IoPortForBmic,
1,
&RegisterValue
);
RegisterValue |= BMIC_START;
IdeDev->PciIo->Io.Write (
IdeDev->PciIo,
EfiPciIoWidthUint8,
EFI_PCI_IO_PASS_THROUGH_BAR,
IoPortForBmic,
1,
&RegisterValue
);
//
// Check the INTERRUPT and ERROR bit of BMIS
//
while (TRUE) {
IdeDev->PciIo->Io.Read (
IdeDev->PciIo,
EfiPciIoWidthUint8,
EFI_PCI_IO_PASS_THROUGH_BAR,
IoPortForBmis,
1,
&RegisterValue
);
if (RegisterValue & (BMIS_INTERRUPT | BMIS_ERROR)) {
if (RegisterValue & BMIS_ERROR) {
gBS->FreePool (PrdAddr);
return EFI_DEVICE_ERROR;
}
break;
}
gBS->Stall (1000);
}
gBS->FreePool (PrdAddr);
//
// Set START bit of BMIC register
//
IdeDev->PciIo->Io.Read (
IdeDev->PciIo,
EfiPciIoWidthUint8,
EFI_PCI_IO_PASS_THROUGH_BAR,
IoPortForBmic,
1,
&RegisterValue
);
RegisterValue &= ~((UINT8) BMIC_START);
IdeDev->PciIo->Io.Write (
IdeDev->PciIo,
EfiPciIoWidthUint8,
EFI_PCI_IO_PASS_THROUGH_BAR,
IoPortForBmic,
1,
&RegisterValue
);
DataBuffer = (UINT8 *) DataBuffer + NumberOfBlocks * IdeDev->BlkIo.Media->BlockSize;
StartLba += NumberOfBlocks;
}
return EFI_SUCCESS;
}
EFI_STATUS
AtaUdmaWrite (
IN IDE_BLK_IO_DEV *IdeDev,
IN VOID *DataBuffer,
IN EFI_LBA StartLba,
IN UINTN NumberOfBlocks
)
/*++
Name:
AtaUdmaWrite
Purpose:
This function is called by the AtaBlkIoWriteBlocks() to perform
writing to media in block unit. The function has been enhanced to
support >120GB access and transfer at most 65536 blocks per 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.
VOID IN *DataBuffer
A pointer to the source buffer for the data.
EFI_LBA IN StartLba
The starting logical block address to write to
on the device media.
UINTN IN NumberOfBlocks
The number of transfer data blocks.
Returns:
The device status of UDMA operation. If the operation is
successful, return EFI_SUCCESS.
--*/
// TODO: function comment is missing 'Routine Description:'
// TODO: function comment is missing 'Arguments:'
// TODO: IdeDev - add argument and description to function comment
// TODO: DataBuffer - add argument and description to function comment
// TODO: StartLba - add argument and description to function comment
// TODO: NumberOfBlocks - add argument and description to function comment
// TODO: EFI_UNSUPPORTED - add return value to function comment
// TODO: EFI_DEVICE_ERROR - add return value to function comment
// TODO: EFI_DEVICE_ERROR - add return value to function comment
{
IDE_DMA_PRD *PrdAddr;
IDE_DMA_PRD *UsedPrdAddr;
IDE_DMA_PRD *TempPrdAddr;
UINT8 RegisterValue;
UINT8 Device;
UINT64 IoPortForBmic;
UINT64 IoPortForBmis;
UINT64 IoPortForBmid;
EFI_STATUS Status;
UINTN PrdTableNum;
UINTN ByteCount;
UINTN ByteAvailable;
UINT8 *PrdBuffer;
UINTN RemainBlockNum;
UINT8 DeviceControl;
//
// Channel and device differential
//
Device = (UINT8) ((IdeDev->Device << 4) | 0xe0);
//
// Enable interrupt to support UDMA
//
DeviceControl = 0;
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Alt.DeviceControl, DeviceControl);
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Head, Device);
if (IdePrimary == IdeDev->Channel) {
IoPortForBmic = IdeDev->IoPort->BusMasterBaseAddr + BMICP_OFFSET;
IoPortForBmis = IdeDev->IoPort->BusMasterBaseAddr + BMISP_OFFSET;
IoPortForBmid = IdeDev->IoPort->BusMasterBaseAddr + BMIDP_OFFSET;
} else {
if (IdeSecondary == IdeDev->Channel) {
IoPortForBmic = IdeDev->IoPort->BusMasterBaseAddr + BMICS_OFFSET;
IoPortForBmis = IdeDev->IoPort->BusMasterBaseAddr + BMISS_OFFSET;
IoPortForBmid = IdeDev->IoPort->BusMasterBaseAddr + BMIDS_OFFSET;
} else {
return EFI_UNSUPPORTED;
}
}
RemainBlockNum = NumberOfBlocks;
while (RemainBlockNum > 0) {
if (RemainBlockNum >= MAX_DMA_COMMAND_SECTORS) {
//
// SectorCount is used to record the number of sectors to be read
// Max 256 sectors can be transfered at a time.
//
NumberOfBlocks = MAX_DMA_COMMAND_SECTORS;
RemainBlockNum -= MAX_DMA_COMMAND_SECTORS;
} else {
NumberOfBlocks = (UINT16) RemainBlockNum;
RemainBlockNum = 0;
}
//
// Calculate the number of PRD table to make sure the memory region
// not cross 64K boundary
//
ByteCount = NumberOfBlocks * IdeDev->BlkIo.Media->BlockSize;
PrdTableNum = ((ByteCount >> 16) + 1) + 1;
//
// Build PRD table
//
PrdAddr = (IDE_DMA_PRD *) AllocateZeroPool ((2 * PrdTableNum * sizeof (IDE_DMA_PRD)));
//
// To make sure PRD is allocated in one 64K page
//
if (((UINTN) PrdAddr & 0x0FFFF) > (((UINTN) PrdAddr + PrdTableNum * sizeof (IDE_DMA_PRD) - 1) & 0x0FFFF)) {
UsedPrdAddr = (IDE_DMA_PRD *) ((UINTN) ((UINT8 *) PrdAddr + 0x10000) & 0xFFFF0000);
} else {
if ((UINTN) PrdAddr & 0x03) {
UsedPrdAddr = (IDE_DMA_PRD *) ((UINTN) ((UINT8 *) PrdAddr + 0x04) & 0xFFFFFFFC);
} else {
UsedPrdAddr = PrdAddr;
}
}
//
// Build the PRD table
//
PrdBuffer = DataBuffer;
TempPrdAddr = UsedPrdAddr;
while (TRUE) {
ByteAvailable = 0x10000 - ((UINTN) PrdBuffer & 0xFFFF);
if (ByteCount <= ByteAvailable) {
TempPrdAddr->RegionBaseAddr = (UINT32) ((UINTN) PrdBuffer);
TempPrdAddr->ByteCount = (UINT16) ByteCount;
TempPrdAddr->EndOfTable = 0x8000;
break;
}
TempPrdAddr->RegionBaseAddr = (UINT32) ((UINTN) PrdBuffer);
TempPrdAddr->ByteCount = (UINT16) ByteAvailable;
ByteCount -= ByteAvailable;
PrdBuffer += ByteAvailable;
TempPrdAddr++;
}
//
// Set the base address to BMID register
//
IdeDev->PciIo->Io.Write (
IdeDev->PciIo,
EfiPciIoWidthUint32,
EFI_PCI_IO_PASS_THROUGH_BAR,
IoPortForBmid,
1,
&UsedPrdAddr
);
//
// Set BMIC register to identify the operation direction
//
IdeDev->PciIo->Io.Read (
IdeDev->PciIo,
EfiPciIoWidthUint8,
EFI_PCI_IO_PASS_THROUGH_BAR,
IoPortForBmic,
1,
&RegisterValue
);
//
// 0000 1000
//
RegisterValue &= ~((UINT8) BMIC_nREAD);
IdeDev->PciIo->Io.Write (
IdeDev->PciIo,
EfiPciIoWidthUint8,
EFI_PCI_IO_PASS_THROUGH_BAR,
IoPortForBmic,
1,
&RegisterValue
);
//
// Read BMIS register and clear ERROR and INTR bit
//
IdeDev->PciIo->Io.Read (
IdeDev->PciIo,
EfiPciIoWidthUint8,
EFI_PCI_IO_PASS_THROUGH_BAR,
IoPortForBmis,
1,
&RegisterValue
);
RegisterValue |= (BMIS_INTERRUPT | BMIS_ERROR);
IdeDev->PciIo->Io.Write (
IdeDev->PciIo,
EfiPciIoWidthUint8,
EFI_PCI_IO_PASS_THROUGH_BAR,
IoPortForBmis,
1,
&RegisterValue
);
//
// Issue WRITE DMA command
//
Status = AtaCommandIssue (
IdeDev,
WRITE_DMA_CMD,
Device,
0,
(UINT16) NumberOfBlocks,
StartLba
);
if (EFI_ERROR (Status)) {
gBS->FreePool (PrdAddr);
return EFI_DEVICE_ERROR;
}
//
// Set START bit of BMIC register
//
IdeDev->PciIo->Io.Read (
IdeDev->PciIo,
EfiPciIoWidthUint8,
EFI_PCI_IO_PASS_THROUGH_BAR,
IoPortForBmic,
1,
&RegisterValue
);
RegisterValue |= BMIC_START;
IdeDev->PciIo->Io.Write (
IdeDev->PciIo,
EfiPciIoWidthUint8,
EFI_PCI_IO_PASS_THROUGH_BAR,
IoPortForBmic,
1,
&RegisterValue
);
//
// Check the INTERRUPT and ERROR bit of BMIS
//
while (TRUE) {
IdeDev->PciIo->Io.Read (
IdeDev->PciIo,
EfiPciIoWidthUint8,
EFI_PCI_IO_PASS_THROUGH_BAR,
IoPortForBmis,
1,
&RegisterValue
);
if (RegisterValue & (BMIS_INTERRUPT | BMIS_ERROR)) {
if (RegisterValue & BMIS_ERROR) {
gBS->FreePool (PrdAddr);
return EFI_DEVICE_ERROR;
}
break;
}
gBS->Stall (1000);
}
gBS->FreePool (PrdAddr);
//
// Set START bit of BMIC register
//
IdeDev->PciIo->Io.Read (
IdeDev->PciIo,
EfiPciIoWidthUint8,
EFI_PCI_IO_PASS_THROUGH_BAR,
IoPortForBmic,
1,
&RegisterValue
);
RegisterValue &= ~((UINT8) BMIC_START);
IdeDev->PciIo->Io.Write (
IdeDev->PciIo,
EfiPciIoWidthUint8,
EFI_PCI_IO_PASS_THROUGH_BAR,
IoPortForBmic,
1,
&RegisterValue
);
DataBuffer = (UINT8 *) DataBuffer + NumberOfBlocks * IdeDev->BlkIo.Media->BlockSize;
StartLba += NumberOfBlocks;
}
return EFI_SUCCESS;
}