]> git.proxmox.com Git - mirror_edk2.git/blobdiff - EdkModulePkg/Bus/Pci/IdeBus/Dxe/ide.c
In IdeBus driver block I/O read/write interface, it will always try to use UDMA mode...
[mirror_edk2.git] / EdkModulePkg / Bus / Pci / IdeBus / Dxe / ide.c
index 7d9d6375a40f0381f2ca1316d45d81f731322cb6..4b4a8ef0a355cfe798d31b2dfba5eba993bd3f28 100644 (file)
@@ -1,19 +1,22 @@
 /** @file\r
-  Copyright (c) 2006, Intel Corporation                                                         \r
-  All rights reserved. This program and the accompanying materials                          \r
-  are licensed and made available under the terms and conditions of the BSD License         \r
-  which accompanies this distribution.  The full text of the license may be found at        \r
-  http://opensource.org/licenses/bsd-license.php                                            \r
+  Copyright (c) 2006, Intel Corporation\r
+  All rights reserved. This program and the accompanying materials\r
+  are licensed and made available under the terms and conditions of the BSD License\r
+  which accompanies this distribution.  The full text of the license may be found at\r
+  http://opensource.org/licenses/bsd-license.php\r
 \r
-  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,                     \r
-  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.             \r
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
 \r
 **/\r
 \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
@@ -48,7 +51,7 @@ IDEReadPortB (
 }\r
 \r
 /**\r
-  Reads multiple words of data from the IDE data port. \r
+  Reads multiple words of data from the IDE data port.\r
   Call the IO abstraction once to do the complete read,\r
   not one word at a time\r
 \r
@@ -167,7 +170,7 @@ IDEWritePortW (
 }\r
 \r
 /**\r
-  Write multiple words of data to the IDE data port. \r
+  Write multiple words of data to the IDE data port.\r
   Call the IO abstraction once to do the complete read,\r
   not one word at a time\r
 \r
@@ -225,70 +228,6 @@ IDEWritePortWMultiple (
   gBS->FreePool (WorkingBuffer);\r
 }\r
 \r
-/**\r
-  TODO: Add function description\r
-\r
-  @param  IdeDev TODO: add argument description\r
-\r
-  TODO: add return values\r
-\r
-**/\r
-BOOLEAN\r
-BadIdeDeviceCheck (\r
-  IN IDE_BLK_IO_DEV *IdeDev\r
-  )\r
-{\r
-  //\r
-  //  check whether all registers return 0xff,\r
-  //  if so, deem the channel is disabled.\r
-  //\r
-#ifdef EFI_DEBUG\r
-\r
-  if (IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Data) != 0xff) {\r
-    return FALSE;\r
-  }\r
-\r
-  if (IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg1.Feature) != 0xff) {\r
-    return FALSE;\r
-  }\r
-\r
-  if (IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->SectorCount) != 0xff) {\r
-    return FALSE;\r
-  }\r
-\r
-  if (IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->SectorNumber) != 0xff) {\r
-    return FALSE;\r
-  }\r
-\r
-  if (IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->CylinderLsb) != 0xff) {\r
-    return FALSE;\r
-  }\r
-\r
-  if (IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->CylinderMsb) != 0xff) {\r
-    return FALSE;\r
-  }\r
-\r
-  if (IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Head) != 0xff) {\r
-    return FALSE;\r
-  }\r
-\r
-  if (IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg.Command) != 0xff) {\r
-    return FALSE;\r
-  }\r
-\r
-  if (IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Alt.AltStatus) != 0xff) {\r
-    return FALSE;\r
-  }\r
-\r
-  return TRUE;\r
-\r
-#else\r
-\r
-  return FALSE;\r
-\r
-#endif\r
-}\r
-\r
 //\r
 // GetIdeRegistersBaseAddr\r
 //\r
@@ -297,10 +236,10 @@ BadIdeDeviceCheck (
   use fixed addresses. In Native-PCI mode, get base addresses from BARs in\r
   the PCI IDE controller's Configuration Space.\r
 \r
-  The steps to get IDE IO port registers' base addresses for each channel \r
+  The steps to get IDE IO port registers' base addresses for each channel\r
   as follows:\r
 \r
-  1. Examine the Programming Interface byte of the Class Code fields in PCI IDE \r
+  1. Examine the Programming Interface byte of the Class Code fields in PCI IDE\r
   controller's Configuration Space to determine the operating mode.\r
 \r
   2. a) In 'Compatibility' mode, use fixed addresses shown in the Table 1 below.\r
@@ -331,11 +270,11 @@ BadIdeDeviceCheck (
 \r
   Table 2. BARs for Register Mapping\r
   </pre>\r
-  @note Refer to Intel ICH4 datasheet, Control Block Offset: 03F4h for \r
-  primary, 0374h for secondary. So 2 bytes extra offset should be \r
+  @note Refer to Intel ICH4 datasheet, Control Block Offset: 03F4h for\r
+  primary, 0374h for secondary. So 2 bytes extra offset should be\r
   added to the base addresses read from BARs.\r
 \r
-  For more details, please refer to PCI IDE Controller Specification and Intel \r
+  For more details, please refer to PCI IDE Controller Specification and Intel\r
   ICH4 Datasheet.\r
 \r
   @param  PciIo Pointer to the EFI_PCI_IO_PROTOCOL instance\r
@@ -370,13 +309,13 @@ GetIdeRegistersBaseAddr (
   if ((PciData.Hdr.ClassCode[0] & IDE_PRIMARY_OPERATING_MODE) == 0) {\r
     IdeRegsBaseAddr[IdePrimary].CommandBlockBaseAddr  = 0x1f0;\r
     IdeRegsBaseAddr[IdePrimary].ControlBlockBaseAddr  = 0x3f6;\r
-    IdeRegsBaseAddr[IdePrimary].BusMasterBaseAddr     = \r
+    IdeRegsBaseAddr[IdePrimary].BusMasterBaseAddr     =\r
     (UINT16)((PciData.Device.Bar[4] & 0x0000fff0));\r
   } else {\r
     //\r
     // The BARs should be of IO type\r
     //\r
-    if ((PciData.Device.Bar[0] & bit0) == 0 || \r
+    if ((PciData.Device.Bar[0] & bit0) == 0 ||\r
         (PciData.Device.Bar[1] & bit0) == 0) {\r
       return EFI_UNSUPPORTED;\r
     }\r
@@ -415,8 +354,8 @@ GetIdeRegistersBaseAddr (
 }\r
 \r
 /**\r
-  This function is used to requery IDE resources. The IDE controller will \r
-  probably switch between native and legacy modes during the EFI->CSM->OS \r
+  This function is used to requery IDE resources. The IDE controller will\r
+  probably switch between native and legacy modes during the EFI->CSM->OS\r
   transfer. We do this everytime before an BlkIo operation to ensure its\r
   succeess.\r
 \r
@@ -464,57 +403,6 @@ ReassignIdeResources (
   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
@@ -533,44 +421,67 @@ DiscoverIdeDevice (
 // 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
-  }\r
-  \r
-  //\r
-  // Device exists. test if it is an ATA device\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 (ChannelDeviceDetected == FALSE) {\r
+    Status = DetectIDEController (IdeDev);\r
     if (EFI_ERROR (Status)) {\r
-      //\r
-      // if not ATAPI device either, return error.\r
-      //\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
+  // Prefer the result from DetectIDEController,\r
+  // if failed, try another device type to handle\r
+  // devices that not follow the spec.\r
+  //\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
+  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
@@ -596,19 +507,39 @@ DiscoverIdeDevice (
 }\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
+  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
   Device Number.\r
 \r
-  There is two IDE channels: one is Primary Channel, the other is \r
-  Secondary Channel.(Channel is the logical name for the physical "Cable".) \r
+  There is two IDE channels: one is Primary Channel, the other is\r
+  Secondary Channel.(Channel is the logical name for the physical "Cable".)\r
   Different channel has different register group.\r
 \r
-  On each IDE channel, at most two IDE devices attach, \r
-  one is called Device 0 (Master device), the other is called Device 1 \r
-  (Slave device). The devices on the same channel co-use the same register \r
-  group, so before sending out a command for a specified device via command \r
-  register, it is a must to select the current device to accept the command \r
+  On each IDE channel, at most two IDE devices attach,\r
+  one is called Device 0 (Master device), the other is called Device 1\r
+  (Slave device). The devices on the same channel co-use the same register\r
+  group, so before sending out a command for a specified device via command\r
+  register, it is a must to select the current device to accept the command\r
   by set the device number in the Head/Device Register.\r
 \r
   @param[in] *IdeDev\r
@@ -617,7 +548,7 @@ DiscoverIdeDevice (
 \r
   @retval TRUE\r
   successfully detects device.\r
-  \r
+\r
   @retval FALSE\r
   any failure during detection process will return this\r
   value.\r
@@ -633,32 +564,13 @@ DetectIDEController (
   )\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
+  UINT8       StatusReg;\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
-      \r
   //\r
   // Select slave device\r
   //\r
@@ -675,7 +587,7 @@ DetectIDEController (
   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
@@ -683,6 +595,7 @@ DetectIDEController (
     (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
@@ -690,103 +603,141 @@ DetectIDEController (
   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
-  //\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
-  //\r
-  if (ErrorReg == 0x01) {\r
-    MasterDeviceExist = TRUE;\r
-    DeviceStatus      = EFI_SUCCESS;\r
-  } else if (ErrorReg == 0x81) {\r
-\r
-    MasterDeviceExist = TRUE;\r
-    DeviceStatus      = EFI_SUCCESS;\r
-    SlaveDeviceExist  = FALSE;\r
-\r
-    return DeviceStatus;\r
-  } else {\r
-    MasterDeviceExist = FALSE;\r
-    DeviceStatus      = EFI_NOT_FOUND;\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
-  //\r
-  // Master Error register is not 0x81, Go on check Slave\r
   //\r
-  \r
+  // Read device signature\r
   //\r
-  // Stall 10ms to wait for slave device ready\r
   //\r
-  gBS->Stall (10000);\r
-  \r
-  //\r
-  // select slave\r
+  // Select Master\r
   //\r
   IDEWritePortB (\r
     IdeDev->PciIo,\r
     IdeDev->IoPort->Head,\r
-    (UINT8) ((1 << 4) | 0xe0)\r
+    (UINT8) ((0 << 4) | 0xe0)\r
     );\r
-\r
-  gBS->Stall (300);\r
-  ErrorReg = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg1.Error);\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
+    MasterDeviceType  = ATA_DEVICE_TYPE;\r
+  } else {\r
+    if ((LBAMidReg      == 0x14) &&\r
+        (LBAHighReg     == 0xeb)) {\r
+      MasterDeviceExist = TRUE;\r
+      MasterDeviceType  = ATAPI_DEVICE_TYPE;\r
+    }\r
+  }\r
 \r
   //\r
-  // Slave Error register is not 0x01, D1 failed. Return.\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 (ErrorReg != 0x01) {\r
-    SlaveDeviceExist = FALSE;\r
-    return DeviceStatus;\r
+  if (!MasterDeviceExist) {\r
+    gBS->Stall (20000);\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
+  // Select Slave\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
+  IDEWritePortB (\r
+    IdeDev->PciIo,\r
+    IdeDev->IoPort->Head,\r
+    (UINT8) ((1 << 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
+  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
   //\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
+  // 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
-  SlaveDeviceExist = TRUE;\r
-\r
-  return DeviceStatus;\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
+  // Indicate this channel has been detected\r
+  //\r
+  ChannelDeviceDetected = TRUE;\r
+  return EFI_SUCCESS;\r
 }\r
 \r
 /**\r
-  This function is used to poll for the DRQ bit clear in the Status \r
-  Register. DRQ is cleared when the device is finished transferring data. \r
+  This function is used to poll for the DRQ bit clear in the Status\r
+  Register. DRQ is cleared when the device is finished transferring data.\r
   So this function is called after data transfer is finished.\r
 \r
   @param[in] *IdeDev\r
   pointer pointing to IDE_BLK_IO_DEV data structure, used\r
   to record all the information of the IDE device.\r
-  \r
+\r
   @param[in] TimeoutInMilliSeconds\r
   used to designate the timeout for the DRQ clear.\r
 \r
   @retval EFI_SUCCESS\r
   DRQ bit clear within the time out.\r
-  \r
+\r
   @retval EFI_TIMEOUT\r
   DRQ bit not clear within the time out.\r
 \r
@@ -846,21 +797,21 @@ DRQClear (
 }\r
 \r
 /**\r
-  This function is used to poll for the DRQ bit clear in the Alternate \r
-  Status Register. DRQ is cleared when the device is finished \r
+  This function is used to poll for the DRQ bit clear in the Alternate\r
+  Status Register. DRQ is cleared when the device is finished\r
   transferring data. So this function is called after data transfer\r
   is finished.\r
 \r
   @param[in] *IdeDev\r
   pointer pointing to IDE_BLK_IO_DEV data structure, used\r
   to record all the information of the IDE device.\r
-  \r
+\r
   @param[in] TimeoutInMilliSeconds\r
   used to designate the timeout for the DRQ clear.\r
 \r
   @retval EFI_SUCCESS\r
   DRQ bit clear within the time out.\r
-  \r
+\r
   @retval EFI_TIMEOUT\r
   DRQ bit not clear within the time out.\r
 \r
@@ -920,25 +871,25 @@ DRQClear2 (
 }\r
 \r
 /**\r
-  This function is used to poll for the DRQ bit set in the \r
+  This function is used to poll for the DRQ bit set in the\r
   Status Register.\r
   DRQ is set when the device is ready to transfer data. So this function\r
-  is called after the command is sent to the device and before required \r
+  is called after the command is sent to the device and before required\r
   data is transferred.\r
 \r
   @param[in] IDE_BLK_IO_DEV  IN    *IdeDev\r
   pointer pointing to IDE_BLK_IO_DEV data structure,used\r
   to record all the information of the IDE device.\r
-  \r
+\r
   @param[in] UINTN     IN    TimeoutInMilliSeconds\r
   used to designate the timeout for the DRQ ready.\r
 \r
   @retval EFI_SUCCESS\r
   DRQ bit set within the time out.\r
-  \r
+\r
   @retval EFI_TIMEOUT\r
   DRQ bit not set within the time out.\r
-  \r
+\r
   @retval EFI_ABORTED\r
   DRQ bit not set caused by the command abort.\r
 \r
@@ -998,24 +949,24 @@ DRQReady (
 }\r
 \r
 /**\r
-  This function is used to poll for the DRQ bit set in the \r
-  Alternate Status Register. DRQ is set when the device is ready to \r
-  transfer data. So this function is called after the command \r
+  This function is used to poll for the DRQ bit set in the\r
+  Alternate Status Register. DRQ is set when the device is ready to\r
+  transfer data. So this function is called after the command\r
   is sent to the device and before required data is transferred.\r
 \r
   @param[in] IDE_BLK_IO_DEV  IN    *IdeDev\r
   pointer pointing to IDE_BLK_IO_DEV data structure, used\r
   to record all the information of the IDE device.\r
-  \r
+\r
   @param[in] UINTN     IN    TimeoutInMilliSeconds\r
   used to designate the timeout for the DRQ ready.\r
 \r
   @retval EFI_SUCCESS\r
   DRQ bit set within the time out.\r
-  \r
+\r
   @retval EFI_TIMEOUT\r
   DRQ bit not set within the time out.\r
-  \r
+\r
   @retval EFI_ABORTED\r
   DRQ bit not set caused by the command abort.\r
 \r
@@ -1075,20 +1026,20 @@ DRQReady2 (
 }\r
 \r
 /**\r
-  This function is used to poll for the BSY bit clear in the \r
+  This function is used to poll for the BSY bit clear in the\r
   Status Register. BSY is clear when the device is not busy.\r
   Every command must be sent after device is not busy.\r
 \r
   @param[in] IDE_BLK_IO_DEV  IN    *IdeDev\r
   pointer pointing to IDE_BLK_IO_DEV data structure, used\r
   to record all the information of the IDE device.\r
-  \r
+\r
   @param[in] UINTN     IN    TimeoutInMilliSeconds\r
   used to designate the timeout for the DRQ ready.\r
 \r
   @retval EFI_SUCCESS\r
   BSY bit clear within the time out.\r
-  \r
+\r
   @retval EFI_TIMEOUT\r
   BSY bit not clear within the time out.\r
 \r
@@ -1136,20 +1087,20 @@ WaitForBSYClear (
 // WaitForBSYClear2\r
 //\r
 /**\r
-  This function is used to poll for the BSY bit clear in the \r
+  This function is used to poll for the BSY bit clear in the\r
   Alternate Status Register. BSY is clear when the device is not busy.\r
   Every command must be sent after device is not busy.\r
 \r
   @param[in] IDE_BLK_IO_DEV  IN    *IdeDev\r
   pointer pointing to IDE_BLK_IO_DEV data structure, used\r
   to record all the information of the IDE device.\r
-  \r
+\r
   @param[in] UINTN     IN    TimeoutInMilliSeconds\r
   used to designate the timeout for the DRQ ready.\r
 \r
   @retval EFI_SUCCESS\r
   BSY bit clear within the time out.\r
-  \r
+\r
   @retval EFI_TIMEOUT\r
   BSY bit not clear within the time out.\r
 \r
@@ -1194,21 +1145,21 @@ WaitForBSYClear2 (
 // DRDYReady\r
 //\r
 /**\r
-  This function is used to poll for the DRDY bit set in the \r
-  Status Register. DRDY bit is set when the device is ready \r
-  to accept command. Most ATA commands must be sent after \r
+  This function is used to poll for the DRDY bit set in the\r
+  Status Register. DRDY bit is set when the device is ready\r
+  to accept command. Most ATA commands must be sent after\r
   DRDY set except the ATAPI Packet Command.\r
 \r
   @param[in] IDE_BLK_IO_DEV  IN    *IdeDev\r
   pointer pointing to IDE_BLK_IO_DEV data structure, used\r
   to record all the information of the IDE device.\r
-  \r
+\r
   @param[in] UINTN     IN    TimeoutInMilliSeconds\r
   used to designate the timeout for the DRQ ready.\r
 \r
   @retval EFI_SUCCESS\r
   DRDY bit set within the time out.\r
-  \r
+\r
   @retval EFI_TIMEOUT\r
   DRDY bit not set within the time out.\r
 \r
@@ -1249,7 +1200,7 @@ DRDYReady (
       }\r
     }\r
 \r
-    gBS->Stall (15);\r
+    gBS->Stall (30);\r
 \r
     Delay--;\r
   } while (Delay);\r
@@ -1265,21 +1216,21 @@ DRDYReady (
 // DRDYReady2\r
 //\r
 /**\r
-  This function is used to poll for the DRDY bit set in the \r
-  Alternate Status Register. DRDY bit is set when the device is ready \r
-  to accept command. Most ATA commands must be sent after \r
+  This function is used to poll for the DRDY bit set in the\r
+  Alternate Status Register. DRDY bit is set when the device is ready\r
+  to accept command. Most ATA commands must be sent after\r
   DRDY set except the ATAPI Packet Command.\r
 \r
   @param[in] IDE_BLK_IO_DEV  IN    *IdeDev\r
   pointer pointing to IDE_BLK_IO_DEV data structure, used\r
   to record all the information of the IDE device.\r
-  \r
+\r
   @param[in] UINTN     IN    TimeoutInMilliSeconds\r
   used to designate the timeout for the DRQ ready.\r
 \r
   @retval EFI_SUCCESS\r
   DRDY bit set within the time out.\r
-  \r
+\r
   @retval EFI_TIMEOUT\r
   DRDY bit not set within the time out.\r
 \r
@@ -1336,23 +1287,23 @@ DRDYReady2 (
 // SwapStringChars\r
 //\r
 /**\r
-  This function is a helper function used to change the char order in a \r
+  This function is a helper function used to change the char order in a\r
   string. It is designed specially for the PrintAtaModuleName() function.\r
   After the IDE device is detected, the IDE driver gets the device module\r
-  name by sending ATA command called ATA Identify Command or ATAPI \r
-  Identify Command to the specified IDE device. The module name returned \r
-  is a string of ASCII characters: the first character is bit8--bit15 \r
-  of the first word, the second character is bit0--bit7 of the first word \r
-  and so on. Thus the string can not be print directly before it is \r
-  preprocessed by this func to change the order of characters in \r
+  name by sending ATA command called ATA Identify Command or ATAPI\r
+  Identify Command to the specified IDE device. The module name returned\r
+  is a string of ASCII characters: the first character is bit8--bit15\r
+  of the first word, the second character is bit0--bit7 of the first word\r
+  and so on. Thus the string can not be print directly before it is\r
+  preprocessed by this func to change the order of characters in\r
   each word in the string.\r
 \r
   @param[in] CHAR8 IN    *Destination\r
   Indicates the destination string.\r
-  \r
+\r
   @param[in] CHAR8 IN    *Source\r
   Indicates the source string.\r
-  \r
+\r
   @param[in] UINT8 IN    Size\r
   the length of the string\r
 \r
@@ -1396,7 +1347,7 @@ ReleaseIdeResources (
   //\r
   // Release all the resourses occupied by the IDE_BLK_IO_DEV\r
   //\r
-  \r
+\r
   if (IdeBlkIoDevice->SenseData != NULL) {\r
     gBS->FreePool (IdeBlkIoDevice->SenseData);\r
     IdeBlkIoDevice->SenseData = NULL;\r
@@ -1430,6 +1381,11 @@ ReleaseIdeResources (
     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
@@ -1551,8 +1507,14 @@ AtaNonDataCommandIn (
 \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
@@ -1714,12 +1676,11 @@ SetDriveParameters (
   //\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
@@ -1729,18 +1690,16 @@ SetDriveParameters (
   //\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
@@ -1767,3 +1726,99 @@ EnableInterrupt (
 \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