\r
#include "idebus.h"\r
\r
-BOOLEAN SlaveDeviceExist = FALSE;\r
-BOOLEAN MasterDeviceExist = FALSE;\r
+BOOLEAN ChannelDeviceDetected = FALSE;\r
+BOOLEAN SlaveDeviceExist = FALSE;\r
+UINT8 SlaveDeviceType = INVALID_DEVICE_TYPE;\r
+BOOLEAN MasterDeviceExist = FALSE;\r
+UINT8 MasterDeviceType = INVALID_DEVICE_TYPE;\r
\r
/**\r
TODO: Add function description\r
TODO: add return values\r
\r
**/\r
+STATIC\r
BOOLEAN\r
BadIdeDeviceCheck (\r
IN IDE_BLK_IO_DEV *IdeDev\r
return EFI_SUCCESS;\r
}\r
\r
-/**\r
- Read SATA registers to detect SATA disks\r
-\r
- @param IdeDev The BLK_IO private data which specifies the IDE device\r
-\r
-**/\r
-EFI_STATUS\r
-CheckPowerMode (\r
- IDE_BLK_IO_DEV *IdeDev\r
- )\r
-// TODO: EFI_NOT_FOUND - add return value to function comment\r
-// TODO: EFI_SUCCESS - add return value to function comment\r
-// TODO: EFI_NOT_FOUND - add return value to function comment\r
-{\r
- UINT8 ErrorRegister;\r
- EFI_STATUS Status;\r
-\r
- IDEWritePortB (\r
- IdeDev->PciIo,\r
- IdeDev->IoPort->Head,\r
- (UINT8) ((IdeDev->Device << 4) | 0xe0)\r
- );\r
-\r
- //\r
- // Wait 31 seconds for BSY clear. BSY should be in clear state if there exists\r
- // a device (initial state). Normally, BSY is also in clear state if there is\r
- // no device\r
- //\r
- Status = WaitForBSYClear (IdeDev, 31000);\r
- if (EFI_ERROR (Status)) {\r
- return EFI_NOT_FOUND;\r
- }\r
-\r
- //\r
- // select device, read error register\r
- //\r
- IDEWritePortB (\r
- IdeDev->PciIo,\r
- IdeDev->IoPort->Head,\r
- (UINT8) ((IdeDev->Device << 4) | 0xe0)\r
- );\r
- Status = DRDYReady (IdeDev, 200);\r
-\r
- ErrorRegister = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg1.Error);\r
- if ((ErrorRegister == 0x01) || (ErrorRegister == 0x81)) {\r
- return EFI_SUCCESS;\r
- } else {\r
- return EFI_NOT_FOUND;\r
- }\r
-}\r
-\r
//\r
// DiscoverIdeDevice\r
//\r
// TODO: EFI_SUCCESS - add return value to function comment\r
{\r
EFI_STATUS Status;\r
- BOOLEAN SataFlag;\r
-\r
- SataFlag = FALSE;\r
- //\r
- // This extra detection is for SATA disks\r
- //\r
- Status = CheckPowerMode (IdeDev);\r
- if (Status == EFI_SUCCESS) {\r
- SataFlag = TRUE;\r
- }\r
\r
//\r
// If a channel has not been checked, check it now. Then set it to "checked" state\r
// After this step, all devices in this channel have been checked.\r
//\r
- Status = DetectIDEController (IdeDev);\r
-\r
- if ((EFI_ERROR (Status)) && !SataFlag) {\r
- return EFI_NOT_FOUND;\r
+ if (ChannelDeviceDetected == FALSE) {\r
+ Status = DetectIDEController (IdeDev);\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
}\r
\r
+ Status = EFI_NOT_FOUND;\r
+\r
//\r
- // Device exists. test if it is an ATA device\r
+ // Device exists. test if it is an ATA device.\r
+ // Prefer the result from DetectIDEController,\r
+ // if failed, try another device type to handle\r
+ // devices that not follow the spec.\r
//\r
- Status = ATAIdentify (IdeDev);\r
- if (EFI_ERROR (Status)) {\r
- //\r
- // if not ATA device, test if it is an ATAPI device\r
- //\r
- Status = ATAPIIdentify (IdeDev);\r
- if (EFI_ERROR (Status)) {\r
- //\r
- // if not ATAPI device either, return error.\r
- //\r
- return EFI_NOT_FOUND;\r
+ if ((IdeDev->Device == IdeMaster) && (MasterDeviceExist)) {\r
+ if (MasterDeviceType == ATA_DEVICE_TYPE) {\r
+ Status = ATAIdentify (IdeDev);\r
+ if (EFI_ERROR (Status)) {\r
+ Status = ATAPIIdentify (IdeDev);\r
+ if (!EFI_ERROR (Status)) {\r
+ MasterDeviceType = ATAPI_DEVICE_TYPE;\r
+ }\r
+ }\r
+ } else {\r
+ Status = ATAPIIdentify (IdeDev);\r
+ if (EFI_ERROR (Status)) {\r
+ Status = ATAIdentify (IdeDev);\r
+ if (!EFI_ERROR (Status)) {\r
+ MasterDeviceType = ATA_DEVICE_TYPE;\r
+ }\r
+ }\r
}\r
}\r
-\r
+ if ((IdeDev->Device == IdeSlave) && (SlaveDeviceExist)) {\r
+ if (SlaveDeviceType == ATA_DEVICE_TYPE) {\r
+ Status = ATAIdentify (IdeDev);\r
+ if (EFI_ERROR (Status)) {\r
+ Status = ATAPIIdentify (IdeDev);\r
+ if (!EFI_ERROR (Status)) {\r
+ SlaveDeviceType = ATAPI_DEVICE_TYPE;\r
+ }\r
+ }\r
+ } else {\r
+ Status = ATAPIIdentify (IdeDev);\r
+ if (EFI_ERROR (Status)) {\r
+ Status = ATAIdentify (IdeDev);\r
+ if (!EFI_ERROR (Status)) {\r
+ SlaveDeviceType = ATA_DEVICE_TYPE;\r
+ }\r
+ }\r
+ }\r
+ }\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
//\r
// Init Block I/O interface\r
//\r
return EFI_SUCCESS;\r
}\r
\r
+/**\r
+ This interface is used to initialize all state data related to the detection of one\r
+ channel.\r
+\r
+ @retval EFI_SUCCESS Completed Successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+InitializeIDEChannelData (\r
+ VOID\r
+ )\r
+{\r
+ ChannelDeviceDetected = FALSE;\r
+ MasterDeviceExist = FALSE;\r
+ MasterDeviceType = 0xff;\r
+ SlaveDeviceExist = FALSE;\r
+ SlaveDeviceType = 0xff;\r
+ return EFI_SUCCESS;\r
+}\r
+\r
/**\r
This function is called by DiscoverIdeDevice(). It is used for detect\r
whether the IDE device exists in the specified Channel as the specified\r
)\r
{\r
EFI_STATUS Status;\r
- UINT8 ErrorReg;\r
- UINT8 StatusReg;\r
+ UINT8 SectorCountReg;\r
+ UINT8 LBALowReg;\r
+ UINT8 LBAMidReg;\r
+ UINT8 LBAHighReg;\r
UINT8 InitStatusReg;\r
- EFI_STATUS DeviceStatus;\r
-\r
- //\r
- // Slave device has been detected with master device.\r
- //\r
- if ((IdeDev->Device) == 1) {\r
- if (SlaveDeviceExist) {\r
- //\r
- // If master not exists but slave exists, slave have to wait a while\r
- //\r
- if (!MasterDeviceExist) {\r
- //\r
- // if single slave can't be detected, add delay 4s here.\r
- //\r
- gBS->Stall (4000000);\r
- }\r
-\r
- return EFI_SUCCESS;\r
- } else {\r
- return EFI_NOT_FOUND;\r
- }\r
- }\r
+ UINT8 StatusReg;\r
\r
//\r
// Select slave device\r
InitStatusReg = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg.Status);\r
\r
//\r
- // Select master back\r
+ // Select Master back\r
//\r
IDEWritePortB (\r
IdeDev->PciIo,\r
(UINT8) ((0 << 4) | 0xe0)\r
);\r
gBS->Stall (100);\r
+\r
//\r
// Send ATA Device Execut Diagnostic command.\r
// This command should work no matter DRDY is ready or not\r
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Reg.Command, 0x90);\r
\r
Status = WaitForBSYClear (IdeDev, 3500);\r
-\r
- ErrorReg = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg1.Error);\r
-\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG((EFI_D_ERROR, "New detecting method: Send Execute Diagnostic Command: WaitForBSYClear: Status: %d\n", Status));\r
+ return Status;\r
+ }\r
//\r
- // Master Error register is 0x01. D0 passed, D1 passed or not present.\r
- // Master Error register is 0x81. D0 passed, D1 failed. Return.\r
- // Master Error register is other value. D0 failed, D1 passed or not present..\r
+ // Read device signature\r
//\r
- if (ErrorReg == 0x01) {\r
- MasterDeviceExist = TRUE;\r
- DeviceStatus = EFI_SUCCESS;\r
- } else if (ErrorReg == 0x81) {\r
-\r
+ //\r
+ // Select Master\r
+ //\r
+ IDEWritePortB (\r
+ IdeDev->PciIo,\r
+ IdeDev->IoPort->Head,\r
+ (UINT8) ((0 << 4) | 0xe0)\r
+ );\r
+ gBS->Stall (100);\r
+ SectorCountReg = IDEReadPortB (\r
+ IdeDev->PciIo,\r
+ IdeDev->IoPort->SectorCount\r
+ );\r
+ LBALowReg = IDEReadPortB (\r
+ IdeDev->PciIo,\r
+ IdeDev->IoPort->SectorNumber\r
+ );\r
+ LBAMidReg = IDEReadPortB (\r
+ IdeDev->PciIo,\r
+ IdeDev->IoPort->CylinderLsb\r
+ );\r
+ LBAHighReg = IDEReadPortB (\r
+ IdeDev->PciIo,\r
+ IdeDev->IoPort->CylinderMsb\r
+ );\r
+ if ((SectorCountReg == 0x1) &&\r
+ (LBALowReg == 0x1) &&\r
+ (LBAMidReg == 0x0) &&\r
+ (LBAHighReg == 0x0)) {\r
MasterDeviceExist = TRUE;\r
- DeviceStatus = EFI_SUCCESS;\r
- SlaveDeviceExist = FALSE;\r
-\r
- return DeviceStatus;\r
+ MasterDeviceType = ATA_DEVICE_TYPE;\r
} else {\r
- MasterDeviceExist = FALSE;\r
- DeviceStatus = EFI_NOT_FOUND;\r
+ if ((LBAMidReg == 0x14) &&\r
+ (LBAHighReg == 0xeb)) {\r
+ MasterDeviceExist = TRUE;\r
+ MasterDeviceType = ATAPI_DEVICE_TYPE;\r
+ }\r
}\r
\r
//\r
- // Master Error register is not 0x81, Go on check Slave\r
- //\r
-\r
- //\r
- // Stall 20ms to wait for slave device ready if master device not exists\r
+ // For some Hard Drive, it takes some time to get\r
+ // the right signature when operating in single slave mode.\r
+ // We stall 20ms to work around this.\r
//\r
if (!MasterDeviceExist) {\r
gBS->Stall (20000);\r
}\r
\r
//\r
- // select slave\r
+ // Select Slave\r
//\r
IDEWritePortB (\r
IdeDev->PciIo,\r
IdeDev->IoPort->Head,\r
(UINT8) ((1 << 4) | 0xe0)\r
);\r
-\r
- gBS->Stall (300);\r
- ErrorReg = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg1.Error);\r
-\r
- //\r
- // Slave Error register is not 0x01, D1 failed. Return.\r
- //\r
- if (ErrorReg != 0x01) {\r
- SlaveDeviceExist = FALSE;\r
- return DeviceStatus;\r
+ gBS->Stall (100);\r
+ SectorCountReg = IDEReadPortB (\r
+ IdeDev->PciIo,\r
+ IdeDev->IoPort->SectorCount\r
+ );\r
+ LBALowReg = IDEReadPortB (\r
+ IdeDev->PciIo,\r
+ IdeDev->IoPort->SectorNumber\r
+ );\r
+ LBAMidReg = IDEReadPortB (\r
+ IdeDev->PciIo,\r
+ IdeDev->IoPort->CylinderLsb\r
+ );\r
+ LBAHighReg = IDEReadPortB (\r
+ IdeDev->PciIo,\r
+ IdeDev->IoPort->CylinderMsb\r
+ );\r
+ StatusReg = IDEReadPortB (\r
+ IdeDev->PciIo,\r
+ IdeDev->IoPort->Reg.Status\r
+ );\r
+ if ((SectorCountReg == 0x1) &&\r
+ (LBALowReg == 0x1) &&\r
+ (LBAMidReg == 0x0) &&\r
+ (LBAHighReg == 0x0)) {\r
+ SlaveDeviceExist = TRUE;\r
+ SlaveDeviceType = ATA_DEVICE_TYPE;\r
+ } else {\r
+ if ((LBAMidReg == 0x14) &&\r
+ (LBAHighReg == 0xeb)) {\r
+ SlaveDeviceExist = TRUE;\r
+ SlaveDeviceType = ATAPI_DEVICE_TYPE;\r
+ }\r
}\r
\r
- StatusReg = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg.Status);\r
-\r
//\r
- // Most ATAPI devices don't set DRDY bit, so test with a slow but accurate\r
- // "ATAPI TEST UNIT READY" command\r
+ // When single master is plugged, slave device\r
+ // will be wrongly detected. Here's the workaround\r
+ // for ATA devices by detecting DRY bit in status\r
+ // register.\r
+ // NOTE: This workaround doesn't apply to ATAPI.\r
//\r
- if (((StatusReg & DRDY) == 0) && ((InitStatusReg & DRDY) == 0)) {\r
- Status = AtapiTestUnitReady (IdeDev);\r
-\r
- //\r
- // Still fail, Slave doesn't exist.\r
- //\r
- if (EFI_ERROR (Status)) {\r
- SlaveDeviceExist = FALSE;\r
- return DeviceStatus;\r
- }\r
+ if (MasterDeviceExist && SlaveDeviceExist &&\r
+ (StatusReg & DRDY) == 0 &&\r
+ (InitStatusReg & DRDY) == 0 &&\r
+ MasterDeviceType == SlaveDeviceType &&\r
+ SlaveDeviceType != ATAPI_DEVICE_TYPE) {\r
+ SlaveDeviceExist = FALSE;\r
}\r
\r
//\r
- // Error reg is 0x01 and DRDY is ready,\r
- // or ATAPI test unit ready success,\r
- // or init Slave status DRDY is ready\r
- // Slave exists.\r
+ // Indicate this channel has been detected\r
//\r
- SlaveDeviceExist = TRUE;\r
-\r
- return DeviceStatus;\r
-\r
+ ChannelDeviceDetected = TRUE;\r
+ return EFI_SUCCESS;\r
}\r
\r
/**\r
}\r
}\r
\r
- gBS->Stall (15);\r
+ gBS->Stall (30);\r
\r
Delay--;\r
} while (Delay);\r
gBS->FreePool (IdeBlkIoDevice->DevicePath);\r
}\r
\r
+ if (IdeBlkIoDevice->ExitBootServiceEvent != NULL) {\r
+ gBS->CloseEvent (IdeBlkIoDevice->ExitBootServiceEvent);\r
+ IdeBlkIoDevice->ExitBootServiceEvent = NULL;\r
+ }\r
+\r
gBS->FreePool (IdeBlkIoDevice);\r
IdeBlkIoDevice = NULL;\r
\r
\r
//\r
// Wait for command completion\r
+ // For ATA_SMART_CMD, we may need more timeout to let device\r
+ // adjust internal states.\r
//\r
- Status = WaitForBSYClear (IdeDev, ATATIMEOUT);\r
+ if (AtaCommand == ATA_SMART_CMD) {\r
+ Status = WaitForBSYClear (IdeDev, ATASMARTTIMEOUT);\r
+ } else {\r
+ Status = WaitForBSYClear (IdeDev, ATATIMEOUT);\r
+ }\r
if (EFI_ERROR (Status)) {\r
return EFI_DEVICE_ERROR;\r
}\r
//\r
// Send Init drive parameters\r
//\r
- Status = AtaPioDataIn (\r
+ Status = AtaNonDataCommandIn (\r
IdeDev,\r
- NULL,\r
- 0,\r
INIT_DRIVE_PARAM_CMD,\r
(UINT8) (DeviceSelect + DriveParameters->Heads),\r
+ 0,\r
DriveParameters->Sector,\r
0,\r
0,\r
//\r
// Send Set Multiple parameters\r
//\r
- Status = AtaPioDataIn (\r
+ Status = AtaNonDataCommandIn (\r
IdeDev,\r
- NULL,\r
- 0,\r
SET_MULTIPLE_MODE_CMD,\r
DeviceSelect,\r
+ 0,\r
DriveParameters->MultipleSector,\r
0,\r
0,\r
0\r
);\r
-\r
return Status;\r
}\r
\r
\r
return EFI_SUCCESS;\r
}\r
+\r
+/**\r
+ Clear pending IDE interrupt before OS loader/kernel take control of the IDE device.\r
+\r
+ @param[in] Event Pointer to this event\r
+ @param[in] Context Event hanlder private data\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+ClearInterrupt (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT64 IoPortForBmis;\r
+ UINT8 RegisterValue;\r
+ IDE_BLK_IO_DEV *IdeDev;\r
+\r
+ //\r
+ // Get our context\r
+ //\r
+ IdeDev = (IDE_BLK_IO_DEV *) Context;\r
+\r
+ //\r
+ // Obtain IDE IO port registers' base addresses\r
+ //\r
+ Status = ReassignIdeResources (IdeDev);\r
+ if (EFI_ERROR (Status)) {\r
+ return;\r
+ }\r
+\r
+ //\r
+ // Check whether interrupt is pending\r
+ //\r
+\r
+ //\r
+ // Reset IDE device to force it de-assert interrupt pin\r
+ // Note: this will reset all devices on this IDE channel\r
+ //\r
+ AtaSoftReset (IdeDev);\r
+ if (EFI_ERROR (Status)) {\r
+ return;\r
+ }\r
+\r
+ //\r
+ // Get base address of IDE Bus Master Status Regsiter\r
+ //\r
+ if (IdePrimary == IdeDev->Channel) {\r
+ IoPortForBmis = IdeDev->IoPort->BusMasterBaseAddr + BMISP_OFFSET;\r
+ } else {\r
+ if (IdeSecondary == IdeDev->Channel) {\r
+ IoPortForBmis = IdeDev->IoPort->BusMasterBaseAddr + BMISS_OFFSET;\r
+ } else {\r
+ return;\r
+ }\r
+ }\r
+ //\r
+ // Read BMIS register and clear ERROR and INTR bit\r
+ //\r
+ IdeDev->PciIo->Io.Read (\r
+ IdeDev->PciIo,\r
+ EfiPciIoWidthUint8,\r
+ EFI_PCI_IO_PASS_THROUGH_BAR,\r
+ IoPortForBmis,\r
+ 1,\r
+ &RegisterValue\r
+ );\r
+\r
+ RegisterValue |= (BMIS_INTERRUPT | BMIS_ERROR);\r
+\r
+ IdeDev->PciIo->Io.Write (\r
+ IdeDev->PciIo,\r
+ EfiPciIoWidthUint8,\r
+ EFI_PCI_IO_PASS_THROUGH_BAR,\r
+ IoPortForBmis,\r
+ 1,\r
+ &RegisterValue\r
+ );\r
+\r
+ //\r
+ // Select the other device on this channel to ensure this device to release the interrupt pin\r
+ //\r
+ if (IdeDev->Device == 0) {\r
+ RegisterValue = (1 << 4) | 0xe0;\r
+ } else {\r
+ RegisterValue = (0 << 4) | 0xe0;\r
+ }\r
+ IDEWritePortB (\r
+ IdeDev->PciIo,\r
+ IdeDev->IoPort->Head,\r
+ RegisterValue\r
+ );\r
+\r
+}\r