mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-12-26 16:47:40 +01:00
1393 lines
39 KiB
C
1393 lines
39 KiB
C
|
/* $Id: Ide.c 33540 2010-10-28 09:27:05Z vboxsync $ */
|
||
|
/** @file
|
||
|
* Ide.c
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* Copyright (C) 2009-2010 Oracle Corporation
|
||
|
*
|
||
|
* This file is part of VirtualBox Open Source Edition (OSE), as
|
||
|
* available from http://www.virtualbox.org. This file is free software;
|
||
|
* you can redistribute it and/or modify it under the terms of the GNU
|
||
|
* General Public License (GPL) as published by the Free Software
|
||
|
* Foundation, in version 2 as it comes in the "COPYING" file of the
|
||
|
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
|
||
|
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
|
||
|
*/
|
||
|
|
||
|
/** @file
|
||
|
The file containing the helper functions implement of the Ide Bus driver
|
||
|
|
||
|
Copyright (c) 2006 - 2008, Intel Corporation
|
||
|
All rights reserved. This program and the accompanying materials
|
||
|
are licensed and made available under the terms and conditions of the BSD License
|
||
|
which accompanies this distribution. The full text of the license may be found at
|
||
|
http://opensource.org/licenses/bsd-license.php
|
||
|
|
||
|
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
|
||
|
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
||
|
|
||
|
**/
|
||
|
|
||
|
#include "IdeBus.h"
|
||
|
|
||
|
BOOLEAN ChannelDeviceDetected = FALSE;
|
||
|
BOOLEAN SlaveDeviceExist = FALSE;
|
||
|
UINT8 SlaveDeviceType = INVALID_DEVICE_TYPE;
|
||
|
BOOLEAN MasterDeviceExist = FALSE;
|
||
|
UINT8 MasterDeviceType = INVALID_DEVICE_TYPE;
|
||
|
|
||
|
/**
|
||
|
read a one-byte data from a IDE port.
|
||
|
|
||
|
@param PciIo The PCI IO protocol instance
|
||
|
@param Port the IDE Port number
|
||
|
|
||
|
@return the one-byte data read from IDE port
|
||
|
**/
|
||
|
UINT8
|
||
|
IDEReadPortB (
|
||
|
IN EFI_PCI_IO_PROTOCOL *PciIo,
|
||
|
IN UINT16 Port
|
||
|
)
|
||
|
{
|
||
|
UINT8 Data;
|
||
|
|
||
|
Data = 0;
|
||
|
//
|
||
|
// perform 1-byte data read from register
|
||
|
//
|
||
|
PciIo->Io.Read (
|
||
|
PciIo,
|
||
|
EfiPciIoWidthUint8,
|
||
|
EFI_PCI_IO_PASS_THROUGH_BAR,
|
||
|
(UINT64) Port,
|
||
|
1,
|
||
|
&Data
|
||
|
);
|
||
|
return Data;
|
||
|
}
|
||
|
/**
|
||
|
Reads multiple words of data from the IDE data port.
|
||
|
Call the IO abstraction once to do the complete read,
|
||
|
not one word at a time
|
||
|
|
||
|
@param PciIo Pointer to the EFI_PCI_IO instance
|
||
|
@param Port IO port to read
|
||
|
@param Count No. of UINT16's to read
|
||
|
@param Buffer Pointer to the data buffer for read
|
||
|
|
||
|
**/
|
||
|
VOID
|
||
|
IDEReadPortWMultiple (
|
||
|
IN EFI_PCI_IO_PROTOCOL *PciIo,
|
||
|
IN UINT16 Port,
|
||
|
IN UINTN Count,
|
||
|
OUT VOID *Buffer
|
||
|
)
|
||
|
{
|
||
|
UINT16 *AlignedBuffer;
|
||
|
UINT16 *WorkingBuffer;
|
||
|
UINTN Size;
|
||
|
|
||
|
//
|
||
|
// Prepare an 16-bit aligned working buffer. CpuIo will return failure and
|
||
|
// not perform actual I/O operations if buffer pointer passed in is not at
|
||
|
// natural boundary. The "Buffer" argument is passed in by user and may not
|
||
|
// at 16-bit natural boundary.
|
||
|
//
|
||
|
Size = sizeof (UINT16) * Count;
|
||
|
|
||
|
gBS->AllocatePool (
|
||
|
EfiBootServicesData,
|
||
|
Size + 1,
|
||
|
(VOID**)&WorkingBuffer
|
||
|
);
|
||
|
|
||
|
AlignedBuffer = (UINT16 *) ((UINTN)(((UINTN) WorkingBuffer + 0x1) & (~0x1)));
|
||
|
|
||
|
//
|
||
|
// Perform UINT16 data read from FIFO
|
||
|
//
|
||
|
PciIo->Io.Read (
|
||
|
PciIo,
|
||
|
EfiPciIoWidthFifoUint16,
|
||
|
EFI_PCI_IO_PASS_THROUGH_BAR,
|
||
|
(UINT64) Port,
|
||
|
Count,
|
||
|
(UINT16*)AlignedBuffer
|
||
|
);
|
||
|
|
||
|
//
|
||
|
// Copy data to user buffer
|
||
|
//
|
||
|
CopyMem (Buffer, (UINT16*)AlignedBuffer, Size);
|
||
|
gBS->FreePool (WorkingBuffer);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
write a 1-byte data to a specific IDE port.
|
||
|
|
||
|
@param PciIo PCI IO protocol instance
|
||
|
@param Port The IDE port to be writen
|
||
|
@param Data The data to write to the port
|
||
|
**/
|
||
|
VOID
|
||
|
IDEWritePortB (
|
||
|
IN EFI_PCI_IO_PROTOCOL *PciIo,
|
||
|
IN UINT16 Port,
|
||
|
IN UINT8 Data
|
||
|
)
|
||
|
{
|
||
|
//
|
||
|
// perform 1-byte data write to register
|
||
|
//
|
||
|
PciIo->Io.Write (
|
||
|
PciIo,
|
||
|
EfiPciIoWidthUint8,
|
||
|
EFI_PCI_IO_PASS_THROUGH_BAR,
|
||
|
(UINT64) Port,
|
||
|
1,
|
||
|
&Data
|
||
|
);
|
||
|
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
write a 1-word data to a specific IDE port.
|
||
|
|
||
|
@param PciIo PCI IO protocol instance
|
||
|
@param Port The IDE port to be writen
|
||
|
@param Data The data to write to the port
|
||
|
**/
|
||
|
VOID
|
||
|
IDEWritePortW (
|
||
|
IN EFI_PCI_IO_PROTOCOL *PciIo,
|
||
|
IN UINT16 Port,
|
||
|
IN UINT16 Data
|
||
|
)
|
||
|
{
|
||
|
//
|
||
|
// perform 1-word data write to register
|
||
|
//
|
||
|
PciIo->Io.Write (
|
||
|
PciIo,
|
||
|
EfiPciIoWidthUint16,
|
||
|
EFI_PCI_IO_PASS_THROUGH_BAR,
|
||
|
(UINT64) Port,
|
||
|
1,
|
||
|
&Data
|
||
|
);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Write multiple words of data to the IDE data port.
|
||
|
Call the IO abstraction once to do the complete read,
|
||
|
not one word at a time
|
||
|
|
||
|
@param PciIo Pointer to the EFI_PCI_IO instance
|
||
|
@param Port IO port to read
|
||
|
@param Count No. of UINT16's to read
|
||
|
@param Buffer Pointer to the data buffer for read
|
||
|
|
||
|
**/
|
||
|
VOID
|
||
|
IDEWritePortWMultiple (
|
||
|
IN EFI_PCI_IO_PROTOCOL *PciIo,
|
||
|
IN UINT16 Port,
|
||
|
IN UINTN Count,
|
||
|
IN VOID *Buffer
|
||
|
)
|
||
|
{
|
||
|
UINT16 *AlignedBuffer;
|
||
|
UINT32 *WorkingBuffer;
|
||
|
UINTN Size;
|
||
|
|
||
|
//
|
||
|
// Prepare an 16-bit aligned working buffer. CpuIo will return failure and
|
||
|
// not perform actual I/O operations if buffer pointer passed in is not at
|
||
|
// natural boundary. The "Buffer" argument is passed in by user and may not
|
||
|
// at 16-bit natural boundary.
|
||
|
//
|
||
|
Size = sizeof (UINT16) * Count;
|
||
|
|
||
|
gBS->AllocatePool (
|
||
|
EfiBootServicesData,
|
||
|
Size + 1,
|
||
|
(VOID **) &WorkingBuffer
|
||
|
);
|
||
|
|
||
|
AlignedBuffer = (UINT16 *) ((UINTN)(((UINTN) WorkingBuffer + 0x1) & (~0x1)));
|
||
|
|
||
|
//
|
||
|
// Copy data from user buffer to working buffer
|
||
|
//
|
||
|
CopyMem ((UINT16 *) AlignedBuffer, Buffer, Size);
|
||
|
|
||
|
//
|
||
|
// perform UINT16 data write to the FIFO
|
||
|
//
|
||
|
PciIo->Io.Write (
|
||
|
PciIo,
|
||
|
EfiPciIoWidthFifoUint16,
|
||
|
EFI_PCI_IO_PASS_THROUGH_BAR,
|
||
|
(UINT64) Port,
|
||
|
Count,
|
||
|
(UINT16 *) AlignedBuffer
|
||
|
);
|
||
|
|
||
|
gBS->FreePool (WorkingBuffer);
|
||
|
}
|
||
|
/**
|
||
|
Get IDE IO port registers' base addresses by mode. In 'Compatibility' mode,
|
||
|
use fixed addresses. In Native-PCI mode, get base addresses from BARs in
|
||
|
the PCI IDE controller's Configuration Space.
|
||
|
|
||
|
The steps to get IDE IO port registers' base addresses for each channel
|
||
|
as follows:
|
||
|
|
||
|
1. Examine the Programming Interface byte of the Class Code fields in PCI IDE
|
||
|
controller's Configuration Space to determine the operating mode.
|
||
|
|
||
|
2. a) In 'Compatibility' mode, use fixed addresses shown in the Table 1 below.
|
||
|
<pre>
|
||
|
___________________________________________
|
||
|
| | Command Block | Control Block |
|
||
|
| Channel | Registers | Registers |
|
||
|
|___________|_______________|_______________|
|
||
|
| Primary | 1F0h - 1F7h | 3F6h - 3F7h |
|
||
|
|___________|_______________|_______________|
|
||
|
| Secondary | 170h - 177h | 376h - 377h |
|
||
|
|___________|_______________|_______________|
|
||
|
|
||
|
Table 1. Compatibility resource mappings
|
||
|
</pre>
|
||
|
|
||
|
b) In Native-PCI mode, IDE registers are mapped into IO space using the BARs
|
||
|
in IDE controller's PCI Configuration Space, shown in the Table 2 below.
|
||
|
<pre>
|
||
|
___________________________________________________
|
||
|
| | Command Block | Control Block |
|
||
|
| Channel | Registers | Registers |
|
||
|
|___________|___________________|___________________|
|
||
|
| Primary | BAR at offset 0x10| BAR at offset 0x14|
|
||
|
|___________|___________________|___________________|
|
||
|
| Secondary | BAR at offset 0x18| BAR at offset 0x1C|
|
||
|
|___________|___________________|___________________|
|
||
|
|
||
|
Table 2. BARs for Register Mapping
|
||
|
</pre>
|
||
|
@note Refer to Intel ICH4 datasheet, Control Block Offset: 03F4h for
|
||
|
primary, 0374h for secondary. So 2 bytes extra offset should be
|
||
|
added to the base addresses read from BARs.
|
||
|
|
||
|
For more details, please refer to PCI IDE Controller Specification and Intel
|
||
|
ICH4 Datasheet.
|
||
|
|
||
|
@param PciIo Pointer to the EFI_PCI_IO_PROTOCOL instance
|
||
|
@param IdeRegsBaseAddr Pointer to IDE_REGISTERS_BASE_ADDR to
|
||
|
receive IDE IO port registers' base addresses
|
||
|
|
||
|
@retval EFI_UNSUPPORTED return this value when the BARs is not IO type
|
||
|
@retval EFI_SUCCESS Get the Base address successfully
|
||
|
@retval other read the pci configuration data error
|
||
|
|
||
|
**/
|
||
|
EFI_STATUS
|
||
|
GetIdeRegistersBaseAddr (
|
||
|
IN EFI_PCI_IO_PROTOCOL *PciIo,
|
||
|
OUT IDE_REGISTERS_BASE_ADDR *IdeRegsBaseAddr
|
||
|
)
|
||
|
{
|
||
|
EFI_STATUS Status;
|
||
|
PCI_TYPE00 PciData;
|
||
|
UINT8 PciClass;
|
||
|
UINT8 PciSubClass;
|
||
|
BOOLEAN fIDEAhciEmulation = FALSE;
|
||
|
|
||
|
Status = PciIo->Pci.Read (
|
||
|
PciIo,
|
||
|
EfiPciIoWidthUint8,
|
||
|
0,
|
||
|
sizeof (PciData),
|
||
|
&PciData
|
||
|
);
|
||
|
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
return Status;
|
||
|
}
|
||
|
Status = PciIo->Pci.Read (
|
||
|
PciIo,
|
||
|
EfiPciIoWidthUint8,
|
||
|
PCI_CLASSCODE_OFFSET + 2,
|
||
|
1,
|
||
|
&PciClass
|
||
|
);
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
Status = PciIo->Pci.Read (
|
||
|
PciIo,
|
||
|
EfiPciIoWidthUint8,
|
||
|
PCI_CLASSCODE_OFFSET + 1,
|
||
|
1,
|
||
|
&PciSubClass
|
||
|
);
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
return Status;
|
||
|
}
|
||
|
switch (PciSubClass)
|
||
|
{
|
||
|
case PCI_CLASS_MASS_STORAGE_IDE:
|
||
|
fIDEAhciEmulation = FALSE;
|
||
|
break;
|
||
|
case 0x6:
|
||
|
fIDEAhciEmulation = TRUE;
|
||
|
break;
|
||
|
default:
|
||
|
ASSERT_EFI_ERROR((EFI_UNSUPPORTED));
|
||
|
return EFI_UNSUPPORTED;
|
||
|
}
|
||
|
// DEBUG((DEBUG_INFO, "class primary code: %x\n", PciData.Hdr.ClassCode[0] & IDE_PRIMARY_OPERATING_MODE));
|
||
|
// DEBUG((DEBUG_INFO, "class secondary code: %x\n", PciData.Hdr.ClassCode[0] & IDE_SECONDARY_OPERATING_MODE));
|
||
|
if ((PciData.Hdr.ClassCode[0] & IDE_PRIMARY_OPERATING_MODE) == 0) {
|
||
|
IdeRegsBaseAddr[IdePrimary].CommandBlockBaseAddr = fIDEAhciEmulation ? 0x1e8 : 0x1f0;
|
||
|
IdeRegsBaseAddr[IdePrimary].ControlBlockBaseAddr = fIDEAhciEmulation ? 0x3e6 :0x3f6;
|
||
|
} 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 =
|
||
|
fIDEAhciEmulation ? 0x1e8 : (UINT16) (PciData.Device.Bar[0] & 0x0000fff8);
|
||
|
IdeRegsBaseAddr[IdePrimary].ControlBlockBaseAddr =
|
||
|
fIDEAhciEmulation ? 0x3e6 :(UINT16) ((PciData.Device.Bar[1] & 0x0000fffc) + 2);
|
||
|
}
|
||
|
IdeRegsBaseAddr[IdePrimary].BusMasterBaseAddr =
|
||
|
fIDEAhciEmulation ? 0 : (UINT16)((PciData.Device.Bar[4] & 0x0000fff0));
|
||
|
|
||
|
if ((PciData.Hdr.ClassCode[0] & IDE_SECONDARY_OPERATING_MODE) == 0) {
|
||
|
IdeRegsBaseAddr[IdeSecondary].CommandBlockBaseAddr = fIDEAhciEmulation ? 0x168 : 0x170;
|
||
|
IdeRegsBaseAddr[IdeSecondary].ControlBlockBaseAddr = fIDEAhciEmulation ? 0x366 : 0x376;
|
||
|
} 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 =
|
||
|
fIDEAhciEmulation ? 0x168 : (UINT16) (PciData.Device.Bar[2] & 0x0000fff8);
|
||
|
IdeRegsBaseAddr[IdeSecondary].ControlBlockBaseAddr =
|
||
|
fIDEAhciEmulation ? 0x366 : (UINT16) ((PciData.Device.Bar[3] & 0x0000fffc) + 2);
|
||
|
}
|
||
|
IdeRegsBaseAddr[IdeSecondary].BusMasterBaseAddr =
|
||
|
(UINT16) ((PciData.Device.Bar[fIDEAhciEmulation ? 5 : 4] & 0x0000fff0));
|
||
|
/*
|
||
|
DEBUG((DEBUG_INFO, "%a:%d CommandBlockBaseAddr:%x, "
|
||
|
"ControlBlockBaseAddr:%x, "
|
||
|
"BusMasterBaseAddr:%x\n", __FILE__, __LINE__,
|
||
|
IdeRegsBaseAddr[IdePrimary].CommandBlockBaseAddr,
|
||
|
IdeRegsBaseAddr[IdePrimary].ControlBlockBaseAddr,
|
||
|
IdeRegsBaseAddr[IdePrimary].BusMasterBaseAddr));
|
||
|
DEBUG((DEBUG_INFO, "%a:%d CommandBlockBaseAddr:%x, "
|
||
|
"ControlBlockBaseAddr:%x, "
|
||
|
"BusMasterBaseAddr:%x\n", __FILE__, __LINE__,
|
||
|
IdeRegsBaseAddr[IdeSecondary].CommandBlockBaseAddr,
|
||
|
IdeRegsBaseAddr[IdeSecondary].ControlBlockBaseAddr,
|
||
|
IdeRegsBaseAddr[IdeSecondary].BusMasterBaseAddr));
|
||
|
*/
|
||
|
return EFI_SUCCESS;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
This function is used to requery IDE resources. The IDE controller will
|
||
|
probably switch between native and legacy modes during the EFI->CSM->OS
|
||
|
transfer. We do this everytime before an BlkIo operation to ensure its
|
||
|
success.
|
||
|
|
||
|
@param IdeDev The BLK_IO private data which specifies the IDE device
|
||
|
|
||
|
@retval EFI_INVALID_PARAMETER return this value when the channel is invalid
|
||
|
@retval EFI_SUCCESS reassign the IDE IO resource successfully
|
||
|
@retval other get the IDE current base address effor
|
||
|
|
||
|
**/
|
||
|
EFI_STATUS
|
||
|
ReassignIdeResources (
|
||
|
IN IDE_BLK_IO_DEV *IdeDev
|
||
|
)
|
||
|
{
|
||
|
EFI_STATUS Status;
|
||
|
IDE_REGISTERS_BASE_ADDR IdeRegsBaseAddr[IdeMaxChannel];
|
||
|
UINT16 CommandBlockBaseAddr;
|
||
|
UINT16 ControlBlockBaseAddr;
|
||
|
|
||
|
if (IdeDev->Channel >= IdeMaxChannel) {
|
||
|
return EFI_INVALID_PARAMETER;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Requery IDE IO port registers' base addresses in case of the switch of
|
||
|
// native and legacy modes
|
||
|
//
|
||
|
Status = GetIdeRegistersBaseAddr (IdeDev->PciIo, IdeRegsBaseAddr);
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
ZeroMem (IdeDev->IoPort, sizeof (IDE_BASE_REGISTERS));
|
||
|
CommandBlockBaseAddr = IdeRegsBaseAddr[IdeDev->Channel].CommandBlockBaseAddr;
|
||
|
ControlBlockBaseAddr = IdeRegsBaseAddr[IdeDev->Channel].ControlBlockBaseAddr;
|
||
|
|
||
|
IdeDev->IoPort->Data = CommandBlockBaseAddr;
|
||
|
(*(UINT16 *) &IdeDev->IoPort->Reg1) = (UINT16) (CommandBlockBaseAddr + 0x01);
|
||
|
IdeDev->IoPort->SectorCount = (UINT16) (CommandBlockBaseAddr + 0x02);
|
||
|
IdeDev->IoPort->SectorNumber = (UINT16) (CommandBlockBaseAddr + 0x03);
|
||
|
IdeDev->IoPort->CylinderLsb = (UINT16) (CommandBlockBaseAddr + 0x04);
|
||
|
IdeDev->IoPort->CylinderMsb = (UINT16) (CommandBlockBaseAddr + 0x05);
|
||
|
IdeDev->IoPort->Head = (UINT16) (CommandBlockBaseAddr + 0x06);
|
||
|
|
||
|
(*(UINT16 *) &IdeDev->IoPort->Reg) = (UINT16) (CommandBlockBaseAddr + 0x07);
|
||
|
(*(UINT16 *) &IdeDev->IoPort->Alt) = ControlBlockBaseAddr;
|
||
|
IdeDev->IoPort->DriveAddress = (UINT16) (ControlBlockBaseAddr + 0x01);
|
||
|
IdeDev->IoPort->MasterSlave = (UINT16) ((IdeDev->Device == IdeMaster) ? 1 : 0);
|
||
|
|
||
|
IdeDev->IoPort->BusMasterBaseAddr = IdeRegsBaseAddr[IdeDev->Channel].BusMasterBaseAddr;
|
||
|
return EFI_SUCCESS;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
This function is called by DiscoverIdeDevice(). It is used for detect
|
||
|
whether the IDE device exists in the specified Channel as the specified
|
||
|
Device Number.
|
||
|
|
||
|
There is two IDE channels: one is Primary Channel, the other is
|
||
|
Secondary Channel.(Channel is the logical name for the physical "Cable".)
|
||
|
Different channel has different register group.
|
||
|
|
||
|
On each IDE channel, at most two IDE devices attach,
|
||
|
one is called Device 0 (Master device), the other is called Device 1
|
||
|
(Slave device). The devices on the same channel co-use the same register
|
||
|
group, so before sending out a command for a specified device via command
|
||
|
register, it is a must to select the current device to accept the command
|
||
|
by set the device number in the Head/Device Register.
|
||
|
|
||
|
@param IdeDev pointer to IDE_BLK_IO_DEV data structure, used to record all the
|
||
|
information of the IDE device.
|
||
|
|
||
|
@retval EFI_SUCCESS successfully detects device.
|
||
|
|
||
|
@retval other any failure during detection process will return this value.
|
||
|
|
||
|
**/
|
||
|
EFI_STATUS
|
||
|
DetectIDEController (
|
||
|
IN IDE_BLK_IO_DEV *IdeDev
|
||
|
)
|
||
|
{
|
||
|
EFI_STATUS Status;
|
||
|
UINT8 SectorCountReg;
|
||
|
UINT8 LBALowReg;
|
||
|
UINT8 LBAMidReg;
|
||
|
UINT8 LBAHighReg;
|
||
|
UINT8 InitStatusReg;
|
||
|
UINT8 StatusReg;
|
||
|
|
||
|
//
|
||
|
// Select slave device
|
||
|
//
|
||
|
IDEWritePortB (
|
||
|
IdeDev->PciIo,
|
||
|
IdeDev->IoPort->Head,
|
||
|
(UINT8) ((1 << 4) | 0xe0)
|
||
|
);
|
||
|
gBS->Stall (100);
|
||
|
|
||
|
//
|
||
|
// Save the init slave status register
|
||
|
//
|
||
|
InitStatusReg = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg.Status); //CmdOrStatus
|
||
|
|
||
|
//
|
||
|
// Select Master back
|
||
|
//
|
||
|
IDEWritePortB (
|
||
|
IdeDev->PciIo,
|
||
|
IdeDev->IoPort->Head,
|
||
|
(UINT8) ((0 << 4) | 0xe0)
|
||
|
);
|
||
|
gBS->Stall (100);
|
||
|
|
||
|
//
|
||
|
// Send ATA Device Execut Diagnostic command.
|
||
|
// This command should work no matter DRDY is ready or not
|
||
|
//
|
||
|
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Reg.Command, 0x90);
|
||
|
|
||
|
Status = WaitForBSYClear (IdeDev, 3500);
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
// DEBUG((EFI_D_ERROR, "New detecting method: Send Execute Diagnostic Command: WaitForBSYClear: Status: %d\n", Status));
|
||
|
return Status;
|
||
|
}
|
||
|
//
|
||
|
// Read device signature
|
||
|
//
|
||
|
//
|
||
|
// Select Master
|
||
|
//
|
||
|
IDEWritePortB (
|
||
|
IdeDev->PciIo,
|
||
|
IdeDev->IoPort->Head,
|
||
|
(UINT8) ((0 << 4) | 0xe0)
|
||
|
);
|
||
|
gBS->Stall (100);
|
||
|
SectorCountReg = IDEReadPortB (
|
||
|
IdeDev->PciIo,
|
||
|
IdeDev->IoPort->SectorCount
|
||
|
);
|
||
|
LBALowReg = IDEReadPortB (
|
||
|
IdeDev->PciIo,
|
||
|
IdeDev->IoPort->SectorNumber
|
||
|
);
|
||
|
LBAMidReg = IDEReadPortB (
|
||
|
IdeDev->PciIo,
|
||
|
IdeDev->IoPort->CylinderLsb
|
||
|
);
|
||
|
LBAHighReg = IDEReadPortB (
|
||
|
IdeDev->PciIo,
|
||
|
IdeDev->IoPort->CylinderMsb
|
||
|
);
|
||
|
if ((SectorCountReg == 0x1) &&
|
||
|
(LBALowReg == 0x1) &&
|
||
|
(LBAMidReg == 0x0) &&
|
||
|
(LBAHighReg == 0x0)) {
|
||
|
MasterDeviceExist = TRUE;
|
||
|
MasterDeviceType = ATA_DEVICE_TYPE;
|
||
|
} else {
|
||
|
if ((LBAMidReg == 0x14) &&
|
||
|
(LBAHighReg == 0xeb)) {
|
||
|
MasterDeviceExist = TRUE;
|
||
|
MasterDeviceType = ATAPI_DEVICE_TYPE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// For some Hard Drive, it takes some time to get
|
||
|
// the right signature when operating in single slave mode.
|
||
|
// We stall 20ms to work around this.
|
||
|
//
|
||
|
if (!MasterDeviceExist) {
|
||
|
gBS->Stall (20000);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Select Slave
|
||
|
//
|
||
|
IDEWritePortB (
|
||
|
IdeDev->PciIo,
|
||
|
IdeDev->IoPort->Head,
|
||
|
(UINT8) ((1 << 4) | 0xe0)
|
||
|
);
|
||
|
gBS->Stall (100);
|
||
|
SectorCountReg = IDEReadPortB (
|
||
|
IdeDev->PciIo,
|
||
|
IdeDev->IoPort->SectorCount
|
||
|
);
|
||
|
LBALowReg = IDEReadPortB (
|
||
|
IdeDev->PciIo,
|
||
|
IdeDev->IoPort->SectorNumber
|
||
|
);
|
||
|
LBAMidReg = IDEReadPortB (
|
||
|
IdeDev->PciIo,
|
||
|
IdeDev->IoPort->CylinderLsb
|
||
|
);
|
||
|
LBAHighReg = IDEReadPortB (
|
||
|
IdeDev->PciIo,
|
||
|
IdeDev->IoPort->CylinderMsb
|
||
|
);
|
||
|
StatusReg = IDEReadPortB (
|
||
|
IdeDev->PciIo,
|
||
|
IdeDev->IoPort->Reg.Status
|
||
|
);
|
||
|
if ((SectorCountReg == 0x1) &&
|
||
|
(LBALowReg == 0x1) &&
|
||
|
(LBAMidReg == 0x0) &&
|
||
|
(LBAHighReg == 0x0)) {
|
||
|
SlaveDeviceExist = TRUE;
|
||
|
SlaveDeviceType = ATA_DEVICE_TYPE;
|
||
|
} else {
|
||
|
if ((LBAMidReg == 0x14) &&
|
||
|
(LBAHighReg == 0xeb)) {
|
||
|
SlaveDeviceExist = TRUE;
|
||
|
SlaveDeviceType = ATAPI_DEVICE_TYPE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// When single master is plugged, slave device
|
||
|
// will be wrongly detected. Here's the workaround
|
||
|
// for ATA devices by detecting DRY bit in status
|
||
|
// register.
|
||
|
// NOTE: This workaround doesn't apply to ATAPI.
|
||
|
//
|
||
|
if (MasterDeviceExist && SlaveDeviceExist &&
|
||
|
(StatusReg & ATA_STSREG_DRDY) == 0 &&
|
||
|
(InitStatusReg & ATA_STSREG_DRDY) == 0 &&
|
||
|
MasterDeviceType == SlaveDeviceType &&
|
||
|
SlaveDeviceType != ATAPI_DEVICE_TYPE) {
|
||
|
SlaveDeviceExist = FALSE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Indicate this channel has been detected
|
||
|
//
|
||
|
ChannelDeviceDetected = TRUE;
|
||
|
return EFI_SUCCESS;
|
||
|
}
|
||
|
/**
|
||
|
Detect if there is disk attached to this port
|
||
|
|
||
|
@param IdeDev The BLK_IO private data which specifies the IDE device.
|
||
|
|
||
|
@retval EFI_NOT_FOUND The device or channel is not found
|
||
|
@retval EFI_SUCCESS The device is found
|
||
|
|
||
|
**/
|
||
|
EFI_STATUS
|
||
|
DiscoverIdeDevice (
|
||
|
IN IDE_BLK_IO_DEV *IdeDev
|
||
|
)
|
||
|
{
|
||
|
EFI_STATUS Status;
|
||
|
EFI_STATUS LongPhyStatus;
|
||
|
|
||
|
//
|
||
|
// If a channel has not been checked, check it now. Then set it to "checked" state
|
||
|
// After this step, all devices in this channel have been checked.
|
||
|
//
|
||
|
if (!ChannelDeviceDetected) {
|
||
|
Status = DetectIDEController (IdeDev);
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
return EFI_NOT_FOUND;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Status = EFI_NOT_FOUND;
|
||
|
|
||
|
//
|
||
|
// Device exists. test if it is an ATA device.
|
||
|
// Prefer the result from DetectIDEController,
|
||
|
// if failed, try another device type to handle
|
||
|
// devices that not follow the spec.
|
||
|
//
|
||
|
if ((IdeDev->Device == IdeMaster) && (MasterDeviceExist)) {
|
||
|
if (MasterDeviceType == ATA_DEVICE_TYPE) {
|
||
|
Status = ATAIdentify (IdeDev);
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
Status = ATAPIIdentify (IdeDev);
|
||
|
if (!EFI_ERROR (Status)) {
|
||
|
MasterDeviceType = ATAPI_DEVICE_TYPE;
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
Status = ATAPIIdentify (IdeDev);
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
Status = ATAIdentify (IdeDev);
|
||
|
if (!EFI_ERROR (Status)) {
|
||
|
MasterDeviceType = ATA_DEVICE_TYPE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if ((IdeDev->Device == IdeSlave) && (SlaveDeviceExist)) {
|
||
|
if (SlaveDeviceType == ATA_DEVICE_TYPE) {
|
||
|
Status = ATAIdentify (IdeDev);
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
Status = ATAPIIdentify (IdeDev);
|
||
|
if (!EFI_ERROR (Status)) {
|
||
|
SlaveDeviceType = ATAPI_DEVICE_TYPE;
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
Status = ATAPIIdentify (IdeDev);
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
Status = ATAIdentify (IdeDev);
|
||
|
if (!EFI_ERROR (Status)) {
|
||
|
SlaveDeviceType = ATA_DEVICE_TYPE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
return EFI_NOT_FOUND;
|
||
|
}
|
||
|
//
|
||
|
// Init Block I/O interface
|
||
|
//
|
||
|
LongPhyStatus = AtaEnableLongPhysicalSector (IdeDev);
|
||
|
if (!EFI_ERROR (LongPhyStatus)) {
|
||
|
IdeDev->BlkIo.Revision = EFI_BLOCK_IO_PROTOCOL_REVISION2;
|
||
|
} else {
|
||
|
IdeDev->BlkIo.Revision = EFI_BLOCK_IO_PROTOCOL_REVISION;
|
||
|
}
|
||
|
IdeDev->BlkIo.Reset = IDEBlkIoReset;
|
||
|
IdeDev->BlkIo.ReadBlocks = IDEBlkIoReadBlocks;
|
||
|
IdeDev->BlkIo.WriteBlocks = IDEBlkIoWriteBlocks;
|
||
|
IdeDev->BlkIo.FlushBlocks = IDEBlkIoFlushBlocks;
|
||
|
|
||
|
IdeDev->BlkMedia.LogicalPartition = FALSE;
|
||
|
IdeDev->BlkMedia.WriteCaching = FALSE;
|
||
|
|
||
|
//
|
||
|
// Init Disk Info interface
|
||
|
//
|
||
|
gBS->CopyMem (&IdeDev->DiskInfo.Interface, &gEfiDiskInfoIdeInterfaceGuid, sizeof (EFI_GUID));
|
||
|
IdeDev->DiskInfo.Inquiry = IDEDiskInfoInquiry;
|
||
|
IdeDev->DiskInfo.Identify = IDEDiskInfoIdentify;
|
||
|
IdeDev->DiskInfo.SenseData = IDEDiskInfoSenseData;
|
||
|
IdeDev->DiskInfo.WhichIde = IDEDiskInfoWhichIde;
|
||
|
|
||
|
return EFI_SUCCESS;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
This interface is used to initialize all state data related to the detection of one
|
||
|
channel.
|
||
|
**/
|
||
|
VOID
|
||
|
InitializeIDEChannelData (
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
ChannelDeviceDetected = FALSE;
|
||
|
MasterDeviceExist = FALSE;
|
||
|
MasterDeviceType = 0xff;
|
||
|
SlaveDeviceExist = FALSE;
|
||
|
SlaveDeviceType = 0xff;
|
||
|
}
|
||
|
/**
|
||
|
This function is used to poll for the DRQ bit clear in the Status
|
||
|
Register. DRQ is cleared when the device is finished transferring data.
|
||
|
So this function is called after data transfer is finished.
|
||
|
|
||
|
@param IdeDev pointer pointing to IDE_BLK_IO_DEV data structure, used
|
||
|
to record all the information of the IDE device.
|
||
|
@param TimeoutInMilliSeconds used to designate the timeout for the DRQ clear.
|
||
|
|
||
|
@retval EFI_SUCCESS DRQ bit clear within the time out.
|
||
|
|
||
|
@retval EFI_TIMEOUT DRQ bit not clear within the time out.
|
||
|
|
||
|
@note
|
||
|
Read Status Register will clear interrupt status.
|
||
|
|
||
|
**/
|
||
|
EFI_STATUS
|
||
|
DRQClear (
|
||
|
IN IDE_BLK_IO_DEV *IdeDev,
|
||
|
IN UINTN TimeoutInMilliSeconds
|
||
|
)
|
||
|
{
|
||
|
UINT32 Delay;
|
||
|
UINT8 StatusRegister;
|
||
|
UINT8 ErrorRegister;
|
||
|
|
||
|
Delay = (UINT32) (((TimeoutInMilliSeconds * STALL_1_MILLI_SECOND) / 30) + 1); //1000 -> 33ms
|
||
|
do {
|
||
|
|
||
|
StatusRegister = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg.Status);
|
||
|
|
||
|
//
|
||
|
// wait for BSY == 0 and DRQ == 0
|
||
|
//
|
||
|
if ((StatusRegister & (ATA_STSREG_DRQ | ATA_STSREG_BSY)) == 0) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ((StatusRegister & (ATA_STSREG_BSY | ATA_STSREG_ERR)) == ATA_STSREG_ERR) {
|
||
|
|
||
|
ErrorRegister = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg1.Error);
|
||
|
if ((ErrorRegister & ATA_ERRREG_ABRT) == ATA_ERRREG_ABRT) {
|
||
|
return EFI_ABORTED;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Stall for 30 us
|
||
|
//
|
||
|
gBS->Stall (30);
|
||
|
|
||
|
Delay--;
|
||
|
|
||
|
} while (Delay > 0);
|
||
|
|
||
|
if (Delay == 0) {
|
||
|
return EFI_TIMEOUT;
|
||
|
}
|
||
|
|
||
|
return EFI_SUCCESS;
|
||
|
}
|
||
|
/**
|
||
|
This function is used to poll for the DRQ bit clear in the Alternate
|
||
|
Status Register. DRQ is cleared when the device is finished
|
||
|
transferring data. So this function is called after data transfer
|
||
|
is finished.
|
||
|
|
||
|
@param IdeDev pointer pointing to IDE_BLK_IO_DEV data structure, used
|
||
|
to record all the information of the IDE device.
|
||
|
|
||
|
@param TimeoutInMilliSeconds used to designate the timeout for the DRQ clear.
|
||
|
|
||
|
@retval EFI_SUCCESS DRQ bit clear within the time out.
|
||
|
|
||
|
@retval EFI_TIMEOUT DRQ bit not clear within the time out.
|
||
|
@note Read Alternate Status Register will not clear interrupt status.
|
||
|
|
||
|
**/
|
||
|
EFI_STATUS
|
||
|
DRQClear2 (
|
||
|
IN IDE_BLK_IO_DEV *IdeDev,
|
||
|
IN UINTN TimeoutInMilliSeconds
|
||
|
)
|
||
|
{
|
||
|
UINT32 Delay;
|
||
|
UINT8 AltRegister;
|
||
|
UINT8 ErrorRegister;
|
||
|
|
||
|
Delay = (UINT32) (((TimeoutInMilliSeconds * STALL_1_MILLI_SECOND) / 30) + 1);
|
||
|
do {
|
||
|
|
||
|
AltRegister = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Alt.AltStatus);
|
||
|
|
||
|
//
|
||
|
// wait for BSY == 0 and DRQ == 0
|
||
|
//
|
||
|
if ((AltRegister & (ATA_STSREG_DRQ | ATA_STSREG_BSY)) == 0) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ((AltRegister & (ATA_STSREG_BSY | ATA_STSREG_ERR)) == ATA_STSREG_ERR) {
|
||
|
|
||
|
ErrorRegister = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg1.Error);
|
||
|
if ((ErrorRegister & ATA_ERRREG_ABRT) == ATA_ERRREG_ABRT) {
|
||
|
return EFI_ABORTED;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Stall for 30 us
|
||
|
//
|
||
|
gBS->Stall (30);
|
||
|
|
||
|
Delay--;
|
||
|
|
||
|
} while (Delay > 0);
|
||
|
|
||
|
if (Delay == 0) {
|
||
|
return EFI_TIMEOUT;
|
||
|
}
|
||
|
|
||
|
return EFI_SUCCESS;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
This function is used to poll for the DRQ bit set in the
|
||
|
Status Register.
|
||
|
DRQ is set when the device is ready to transfer data. So this function
|
||
|
is called after the command is sent to the device and before required
|
||
|
data is transferred.
|
||
|
|
||
|
@param IdeDev pointer pointing to IDE_BLK_IO_DEV data structure,used to
|
||
|
record all the information of the IDE device.
|
||
|
@param TimeoutInMilliSeconds used to designate the timeout for the DRQ ready.
|
||
|
|
||
|
@retval EFI_SUCCESS DRQ bit set within the time out.
|
||
|
@retval EFI_TIMEOUT DRQ bit not set within the time out.
|
||
|
@retval EFI_ABORTED DRQ bit not set caused by the command abort.
|
||
|
|
||
|
@note Read Status Register will clear interrupt status.
|
||
|
|
||
|
**/
|
||
|
EFI_STATUS
|
||
|
DRQReady (
|
||
|
IN IDE_BLK_IO_DEV *IdeDev,
|
||
|
IN UINTN TimeoutInMilliSeconds
|
||
|
)
|
||
|
{
|
||
|
UINT32 Delay;
|
||
|
UINT8 StatusRegister;
|
||
|
UINT8 ErrorRegister;
|
||
|
|
||
|
Delay = (UINT32) (((TimeoutInMilliSeconds * STALL_1_MILLI_SECOND) / 30) + 1);
|
||
|
do {
|
||
|
//
|
||
|
// read Status Register will clear interrupt
|
||
|
//
|
||
|
StatusRegister = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg.Status);
|
||
|
|
||
|
//
|
||
|
// BSY==0,DRQ==1
|
||
|
//
|
||
|
if ((StatusRegister & (ATA_STSREG_BSY | ATA_STSREG_DRQ)) == ATA_STSREG_DRQ) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ((StatusRegister & (ATA_STSREG_BSY | ATA_STSREG_ERR)) == ATA_STSREG_ERR) {
|
||
|
|
||
|
ErrorRegister = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg1.Error);
|
||
|
if ((ErrorRegister & ATA_ERRREG_ABRT) == ATA_ERRREG_ABRT) {
|
||
|
return EFI_ABORTED;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Stall for 30 us
|
||
|
//
|
||
|
gBS->Stall (30);
|
||
|
|
||
|
Delay--;
|
||
|
} while (Delay > 0);
|
||
|
|
||
|
if (Delay == 0) {
|
||
|
return EFI_TIMEOUT;
|
||
|
}
|
||
|
|
||
|
return EFI_SUCCESS;
|
||
|
}
|
||
|
/**
|
||
|
This function is used to poll for the DRQ bit set in the Alternate Status Register.
|
||
|
DRQ is set when the device is ready to transfer data. So this function is called after
|
||
|
the command is sent to the device and before required data is transferred.
|
||
|
|
||
|
@param IdeDev pointer pointing to IDE_BLK_IO_DEV data structure, used to
|
||
|
record all the information of the IDE device.
|
||
|
|
||
|
@param TimeoutInMilliSeconds used to designate the timeout for the DRQ ready.
|
||
|
|
||
|
@retval EFI_SUCCESS DRQ bit set within the time out.
|
||
|
@retval EFI_TIMEOUT DRQ bit not set within the time out.
|
||
|
@retval EFI_ABORTED DRQ bit not set caused by the command abort.
|
||
|
@note Read Alternate Status Register will not clear interrupt status.
|
||
|
|
||
|
**/
|
||
|
EFI_STATUS
|
||
|
DRQReady2 (
|
||
|
IN IDE_BLK_IO_DEV *IdeDev,
|
||
|
IN UINTN TimeoutInMilliSeconds
|
||
|
)
|
||
|
{
|
||
|
UINT32 Delay;
|
||
|
UINT8 AltRegister;
|
||
|
UINT8 ErrorRegister;
|
||
|
|
||
|
Delay = (UINT32) (((TimeoutInMilliSeconds * STALL_1_MILLI_SECOND) / 30) + 1);
|
||
|
|
||
|
do {
|
||
|
//
|
||
|
// Read Alternate Status Register will not clear interrupt status
|
||
|
//
|
||
|
AltRegister = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Alt.AltStatus);
|
||
|
//
|
||
|
// BSY == 0 , DRQ == 1
|
||
|
//
|
||
|
if ((AltRegister & (ATA_STSREG_BSY | ATA_STSREG_DRQ)) == ATA_STSREG_DRQ) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ((AltRegister & (ATA_STSREG_BSY | ATA_STSREG_ERR)) == ATA_STSREG_ERR) {
|
||
|
|
||
|
ErrorRegister = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg1.Error);
|
||
|
if ((ErrorRegister & ATA_ERRREG_ABRT) == ATA_ERRREG_ABRT) {
|
||
|
return EFI_ABORTED;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Stall for 30 us
|
||
|
//
|
||
|
gBS->Stall (30);
|
||
|
|
||
|
Delay--;
|
||
|
} while (Delay > 0);
|
||
|
|
||
|
if (Delay == 0) {
|
||
|
return EFI_TIMEOUT;
|
||
|
}
|
||
|
|
||
|
return EFI_SUCCESS;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
This function is used to poll for the BSY bit clear in the Status Register. BSY
|
||
|
is clear when the device is not busy. Every command must be sent after device is not busy.
|
||
|
|
||
|
@param IdeDev pointer pointing to IDE_BLK_IO_DEV data structure, used
|
||
|
to record all the information of the IDE device.
|
||
|
@param TimeoutInMilliSeconds used to designate the timeout for the DRQ ready.
|
||
|
|
||
|
@retval EFI_SUCCESS BSY bit clear within the time out.
|
||
|
@retval EFI_TIMEOUT BSY bit not clear within the time out.
|
||
|
|
||
|
@note Read Status Register will clear interrupt status.
|
||
|
**/
|
||
|
EFI_STATUS
|
||
|
WaitForBSYClear (
|
||
|
IN IDE_BLK_IO_DEV *IdeDev,
|
||
|
IN UINTN TimeoutInMilliSeconds
|
||
|
)
|
||
|
{
|
||
|
UINT32 Delay;
|
||
|
UINT8 StatusRegister;
|
||
|
|
||
|
Delay = (UINT32) (((TimeoutInMilliSeconds * STALL_1_MILLI_SECOND) / 30) + 1);
|
||
|
do {
|
||
|
|
||
|
StatusRegister = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg.Status);
|
||
|
if ((StatusRegister & ATA_STSREG_BSY) == 0x00) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Stall for 30 us
|
||
|
//
|
||
|
gBS->Stall (30);
|
||
|
|
||
|
Delay--;
|
||
|
|
||
|
} while (Delay > 0);
|
||
|
|
||
|
if (Delay == 0) {
|
||
|
return EFI_TIMEOUT;
|
||
|
}
|
||
|
|
||
|
return EFI_SUCCESS;
|
||
|
}
|
||
|
/**
|
||
|
This function is used to poll for the BSY bit clear in the Alternate Status Register.
|
||
|
BSY is clear when the device is not busy. Every command must be sent after device is
|
||
|
not busy.
|
||
|
|
||
|
@param IdeDev pointer pointing to IDE_BLK_IO_DEV data structure, used to record
|
||
|
all the information of the IDE device.
|
||
|
@param TimeoutInMilliSeconds used to designate the timeout for the DRQ ready.
|
||
|
|
||
|
@retval EFI_SUCCESS BSY bit clear within the time out.
|
||
|
@retval EFI_TIMEOUT BSY bit not clear within the time out.
|
||
|
@note Read Alternate Status Register will not clear interrupt status.
|
||
|
|
||
|
**/
|
||
|
EFI_STATUS
|
||
|
WaitForBSYClear2 (
|
||
|
IN IDE_BLK_IO_DEV *IdeDev,
|
||
|
IN UINTN TimeoutInMilliSeconds
|
||
|
)
|
||
|
{
|
||
|
UINT32 Delay;
|
||
|
UINT8 AltRegister;
|
||
|
|
||
|
Delay = (UINT32) (((TimeoutInMilliSeconds * STALL_1_MILLI_SECOND) / 30) + 1);
|
||
|
do {
|
||
|
AltRegister = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Alt.AltStatus);
|
||
|
if ((AltRegister & ATA_STSREG_BSY) == 0x00) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
gBS->Stall (30);
|
||
|
|
||
|
Delay--;
|
||
|
|
||
|
} while (Delay > 0);
|
||
|
|
||
|
if (Delay == 0) {
|
||
|
return EFI_TIMEOUT;
|
||
|
}
|
||
|
|
||
|
return EFI_SUCCESS;
|
||
|
}
|
||
|
/**
|
||
|
This function is used to poll for the DRDY bit set in the Status Register. DRDY
|
||
|
bit is set when the device is ready to accept command. Most ATA commands must be
|
||
|
sent after DRDY set except the ATAPI Packet Command.
|
||
|
|
||
|
@param IdeDev pointer pointing to IDE_BLK_IO_DEV data structure, used
|
||
|
to record all the information of the IDE device.
|
||
|
@param DelayInMilliSeconds used to designate the timeout for the DRQ ready.
|
||
|
|
||
|
@retval EFI_SUCCESS DRDY bit set within the time out.
|
||
|
@retval EFI_TIMEOUT DRDY bit not set within the time out.
|
||
|
|
||
|
@note Read Status Register will clear interrupt status.
|
||
|
**/
|
||
|
EFI_STATUS
|
||
|
DRDYReady (
|
||
|
IN IDE_BLK_IO_DEV *IdeDev,
|
||
|
IN UINTN DelayInMilliSeconds
|
||
|
)
|
||
|
{
|
||
|
UINT32 Delay;
|
||
|
UINT8 StatusRegister;
|
||
|
UINT8 ErrorRegister;
|
||
|
|
||
|
Delay = (UINT32) (((DelayInMilliSeconds * STALL_1_MILLI_SECOND) / 30) + 1);
|
||
|
do {
|
||
|
StatusRegister = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg.Status);
|
||
|
//
|
||
|
// BSY == 0 , DRDY == 1
|
||
|
//
|
||
|
if ((StatusRegister & (ATA_STSREG_DRDY | ATA_STSREG_BSY)) == ATA_STSREG_DRDY) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ((StatusRegister & (ATA_STSREG_BSY | ATA_STSREG_ERR)) == ATA_STSREG_ERR) {
|
||
|
|
||
|
ErrorRegister = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg1.Error);
|
||
|
if ((ErrorRegister & ATA_ERRREG_ABRT) == ATA_ERRREG_ABRT) {
|
||
|
return EFI_ABORTED;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
gBS->Stall (30);
|
||
|
|
||
|
Delay--;
|
||
|
} while (Delay > 0);
|
||
|
|
||
|
if (Delay == 0) {
|
||
|
return EFI_TIMEOUT;
|
||
|
}
|
||
|
|
||
|
return EFI_SUCCESS;
|
||
|
}
|
||
|
/**
|
||
|
This function is used to poll for the DRDY bit set in the Alternate Status Register.
|
||
|
DRDY bit is set when the device is ready to accept command. Most ATA commands must
|
||
|
be sent after DRDY set except the ATAPI Packet Command.
|
||
|
|
||
|
@param IdeDev pointer pointing to IDE_BLK_IO_DEV data structure, used
|
||
|
to record all the information of the IDE device.
|
||
|
@param DelayInMilliSeconds used to designate the timeout for the DRQ ready.
|
||
|
|
||
|
@retval EFI_SUCCESS DRDY bit set within the time out.
|
||
|
@retval EFI_TIMEOUT DRDY bit not set within the time out.
|
||
|
|
||
|
@note Read Alternate Status Register will clear interrupt status.
|
||
|
|
||
|
**/
|
||
|
EFI_STATUS
|
||
|
DRDYReady2 (
|
||
|
IN IDE_BLK_IO_DEV *IdeDev,
|
||
|
IN UINTN DelayInMilliSeconds
|
||
|
)
|
||
|
{
|
||
|
UINT32 Delay;
|
||
|
UINT8 AltRegister;
|
||
|
UINT8 ErrorRegister;
|
||
|
|
||
|
Delay = (UINT32) (((DelayInMilliSeconds * STALL_1_MILLI_SECOND) / 30) + 1);
|
||
|
do {
|
||
|
AltRegister = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Alt.AltStatus); //AltOrDev
|
||
|
//
|
||
|
// BSY == 0 , DRDY == 1
|
||
|
//
|
||
|
if ((AltRegister & (ATA_STSREG_DRDY | ATA_STSREG_BSY)) == ATA_STSREG_DRDY) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ((AltRegister & (ATA_STSREG_BSY | ATA_STSREG_ERR)) == ATA_STSREG_ERR) {
|
||
|
|
||
|
ErrorRegister = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg1.Error);
|
||
|
if ((ErrorRegister & ATA_ERRREG_ABRT) == ATA_ERRREG_ABRT) {
|
||
|
return EFI_ABORTED;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
gBS->Stall (30);
|
||
|
|
||
|
Delay--;
|
||
|
} while (Delay > 0);
|
||
|
|
||
|
if (Delay == 0) {
|
||
|
return EFI_TIMEOUT;
|
||
|
}
|
||
|
|
||
|
return EFI_SUCCESS;
|
||
|
}
|
||
|
/**
|
||
|
Release resources of an IDE device before stopping it.
|
||
|
|
||
|
@param IdeBlkIoDevice Standard IDE device private data structure
|
||
|
|
||
|
**/
|
||
|
VOID
|
||
|
ReleaseIdeResources (
|
||
|
IN IDE_BLK_IO_DEV *IdeBlkIoDevice
|
||
|
)
|
||
|
{
|
||
|
if (IdeBlkIoDevice == NULL) {
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Release all the resources occupied by the IDE_BLK_IO_DEV
|
||
|
//
|
||
|
|
||
|
if (IdeBlkIoDevice->SenseData != NULL) {
|
||
|
gBS->FreePool (IdeBlkIoDevice->SenseData);
|
||
|
IdeBlkIoDevice->SenseData = NULL;
|
||
|
}
|
||
|
|
||
|
if (IdeBlkIoDevice->Cache != NULL) {
|
||
|
gBS->FreePool (IdeBlkIoDevice->Cache);
|
||
|
IdeBlkIoDevice->Cache = NULL;
|
||
|
}
|
||
|
|
||
|
if (IdeBlkIoDevice->IdData != NULL) {
|
||
|
gBS->FreePool (IdeBlkIoDevice->IdData);
|
||
|
IdeBlkIoDevice->IdData = NULL;
|
||
|
}
|
||
|
|
||
|
if (IdeBlkIoDevice->InquiryData != NULL) {
|
||
|
gBS->FreePool (IdeBlkIoDevice->InquiryData);
|
||
|
IdeBlkIoDevice->InquiryData = NULL;
|
||
|
}
|
||
|
|
||
|
if (IdeBlkIoDevice->ControllerNameTable != NULL) {
|
||
|
FreeUnicodeStringTable (IdeBlkIoDevice->ControllerNameTable);
|
||
|
IdeBlkIoDevice->ControllerNameTable = NULL;
|
||
|
}
|
||
|
|
||
|
if (IdeBlkIoDevice->IoPort != NULL) {
|
||
|
gBS->FreePool (IdeBlkIoDevice->IoPort);
|
||
|
}
|
||
|
|
||
|
if (IdeBlkIoDevice->DevicePath != NULL) {
|
||
|
gBS->FreePool (IdeBlkIoDevice->DevicePath);
|
||
|
}
|
||
|
|
||
|
if (IdeBlkIoDevice->ExitBootServiceEvent != NULL) {
|
||
|
gBS->CloseEvent (IdeBlkIoDevice->ExitBootServiceEvent);
|
||
|
IdeBlkIoDevice->ExitBootServiceEvent = NULL;
|
||
|
}
|
||
|
|
||
|
gBS->FreePool (IdeBlkIoDevice);
|
||
|
IdeBlkIoDevice = NULL;
|
||
|
|
||
|
return ;
|
||
|
}
|
||
|
/**
|
||
|
Set the calculated Best transfer mode to a detected device.
|
||
|
|
||
|
@param IdeDev Standard IDE device private data structure
|
||
|
@param TransferMode The device transfer mode to be set
|
||
|
@return Set transfer mode Command execute status.
|
||
|
|
||
|
**/
|
||
|
EFI_STATUS
|
||
|
SetDeviceTransferMode (
|
||
|
IN IDE_BLK_IO_DEV *IdeDev,
|
||
|
IN ATA_TRANSFER_MODE *TransferMode
|
||
|
)
|
||
|
{
|
||
|
EFI_STATUS Status;
|
||
|
UINT8 DeviceSelect;
|
||
|
UINT8 SectorCount;
|
||
|
|
||
|
DeviceSelect = 0;
|
||
|
DeviceSelect = (UINT8) ((IdeDev->Device) << 4);
|
||
|
SectorCount = *((UINT8 *) TransferMode);
|
||
|
|
||
|
//
|
||
|
// Send SET FEATURE command (sub command 0x03) to set pio mode.
|
||
|
//
|
||
|
Status = AtaNonDataCommandIn (
|
||
|
IdeDev,
|
||
|
ATA_CMD_SET_FEATURES,
|
||
|
DeviceSelect,
|
||
|
0x03,
|
||
|
SectorCount,
|
||
|
0,
|
||
|
0,
|
||
|
0
|
||
|
);
|
||
|
|
||
|
return Status;
|
||
|
}
|
||
|
/**
|
||
|
Set drive parameters for devices not support PACKETS command.
|
||
|
|
||
|
@param IdeDev Standard IDE device private data structure
|
||
|
@param DriveParameters The device parameters to be set into the disk
|
||
|
@return SetParameters Command execute status.
|
||
|
|
||
|
**/
|
||
|
EFI_STATUS
|
||
|
SetDriveParameters (
|
||
|
IN IDE_BLK_IO_DEV *IdeDev,
|
||
|
IN ATA_DRIVE_PARMS *DriveParameters
|
||
|
)
|
||
|
{
|
||
|
EFI_STATUS Status;
|
||
|
UINT8 DeviceSelect;
|
||
|
|
||
|
DeviceSelect = 0;
|
||
|
DeviceSelect = (UINT8) ((IdeDev->Device) << 4);
|
||
|
|
||
|
//
|
||
|
// Send Init drive parameters
|
||
|
//
|
||
|
Status = AtaNonDataCommandIn (
|
||
|
IdeDev,
|
||
|
ATA_CMD_INIT_DRIVE_PARAM,
|
||
|
(UINT8) (DeviceSelect + DriveParameters->Heads),
|
||
|
0,
|
||
|
DriveParameters->Sector,
|
||
|
0,
|
||
|
0,
|
||
|
0
|
||
|
);
|
||
|
|
||
|
//
|
||
|
// Send Set Multiple parameters
|
||
|
//
|
||
|
Status = AtaNonDataCommandIn (
|
||
|
IdeDev,
|
||
|
ATA_CMD_SET_MULTIPLE_MODE,
|
||
|
DeviceSelect,
|
||
|
0,
|
||
|
DriveParameters->MultipleSector,
|
||
|
0,
|
||
|
0,
|
||
|
0
|
||
|
);
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Enable Interrupt on IDE controller.
|
||
|
|
||
|
@param IdeDev Standard IDE device private data structure
|
||
|
|
||
|
@retval EFI_SUCCESS Enable Interrupt successfully
|
||
|
**/
|
||
|
EFI_STATUS
|
||
|
EnableInterrupt (
|
||
|
IN IDE_BLK_IO_DEV *IdeDev
|
||
|
)
|
||
|
{
|
||
|
UINT8 DeviceControl;
|
||
|
|
||
|
//
|
||
|
// Enable interrupt for DMA operation
|
||
|
//
|
||
|
DeviceControl = 0;
|
||
|
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Alt.DeviceControl, DeviceControl);
|
||
|
|
||
|
return EFI_SUCCESS;
|
||
|
}
|