/** 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; }
/** 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); // // 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; }
/** The is an event(generally the event is exitBootService event) call back function. Clear pending IDE interrupt before OS loader/kernel take control of the IDE device. @param Event Pointer to this event @param Context Event handler private data **/ VOID EFIAPI ClearInterrupt ( IN EFI_EVENT Event, IN VOID *Context ) { EFI_STATUS Status; UINT64 IoPortForBmis; UINT8 RegisterValue; IDE_BLK_IO_DEV *IdeDev; // // Get our context // IdeDev = (IDE_BLK_IO_DEV *) Context; // // Obtain IDE IO port registers' base addresses // Status = ReassignIdeResources (IdeDev); if (EFI_ERROR (Status)) { return; } // // Check whether interrupt is pending // // // Reset IDE device to force it de-assert interrupt pin // Note: this will reset all devices on this IDE channel // Status = AtaSoftReset (IdeDev); if (EFI_ERROR (Status)) { return; } // // Get base address of IDE Bus Master Status Regsiter // if (IdePrimary == IdeDev->Channel) { IoPortForBmis = IdeDev->IoPort->BusMasterBaseAddr + BMISP_OFFSET; } else { if (IdeSecondary == IdeDev->Channel) { IoPortForBmis = IdeDev->IoPort->BusMasterBaseAddr + BMISS_OFFSET; } else { return; } } // // 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 ); // // Select the other device on this channel to ensure this device to release the interrupt pin // if (IdeDev->Device == 0) { RegisterValue = (1 << 4) | 0xe0; } else { RegisterValue = (0 << 4) | 0xe0; } IDEWritePortB ( IdeDev->PciIo, IdeDev->IoPort->Head, RegisterValue ); }