+\r
+/**\r
+ This function is called by the AtaBlkIoReadBlocks() to perform\r
+ reading from media in block unit. The function has been enhanced to\r
+ support >120GB access and transfer at most 65536 blocks per command\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
+ @param[in] *DataBuffer A pointer to the destination buffer for the data.\r
+ @param[in] StartLba The starting logical block address to read from\r
+ on the device media.\r
+ @param[in] NumberOfBlocks The number of transfer data blocks.\r
+\r
+ @return The device status of UDMA operation. If the operation is\r
+ successful, return EFI_SUCCESS.\r
+\r
+ TODO: EFI_UNSUPPORTED - add return value to function comment\r
+ TODO: EFI_DEVICE_ERROR - add return value to function comment\r
+ TODO: EFI_DEVICE_ERROR - add return value to function comment\r
+ TODO: EFI_DEVICE_ERROR - add return value to function comment\r
+**/\r
+EFI_STATUS\r
+AtaUdmaRead (\r
+ IN IDE_BLK_IO_DEV *IdeDev,\r
+ IN VOID *DataBuffer,\r
+ IN EFI_LBA StartLba,\r
+ IN UINTN NumberOfBlocks\r
+ )\r
+{\r
+ IDE_DMA_PRD *PrdAddr;\r
+ IDE_DMA_PRD *UsedPrdAddr;\r
+ IDE_DMA_PRD *TempPrdAddr;\r
+ UINT8 RegisterValue;\r
+ UINT8 Device;\r
+ UINT64 IoPortForBmic;\r
+ UINT64 IoPortForBmis;\r
+ UINT64 IoPortForBmid;\r
+ EFI_STATUS Status;\r
+ UINTN PrdTableNum;\r
+ UINTN ByteCount;\r
+ UINTN ByteAvailable;\r
+ UINT8 *PrdBuffer;\r
+ UINTN RemainBlockNum;\r
+ UINT8 DeviceControl;\r
+ UINT32 Count;\r
+ UINTN PageCount;\r
+ VOID *Map;\r
+ VOID *MemPage;\r
+ EFI_PHYSICAL_ADDRESS DeviceAddress;\r
+\r
+ //\r
+ // Channel and device differential\r
+ //\r
+ Device = (UINT8) ((IdeDev->Device << 4) | 0xe0);\r
+\r
+ //\r
+ // Enable interrupt to support UDMA and Select device\r
+ //\r
+ DeviceControl = 0;\r
+ IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Alt.DeviceControl, DeviceControl);\r
+\r
+ IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Head, Device);\r
+\r
+ if (IdePrimary == IdeDev->Channel) {\r
+ IoPortForBmic = IdeDev->IoPort->BusMasterBaseAddr + BMICP_OFFSET;\r
+ IoPortForBmis = IdeDev->IoPort->BusMasterBaseAddr + BMISP_OFFSET;\r
+ IoPortForBmid = IdeDev->IoPort->BusMasterBaseAddr + BMIDP_OFFSET;\r
+ } else {\r
+ if (IdeSecondary == IdeDev->Channel) {\r
+ IoPortForBmic = IdeDev->IoPort->BusMasterBaseAddr + BMICS_OFFSET;\r
+ IoPortForBmis = IdeDev->IoPort->BusMasterBaseAddr + BMISS_OFFSET;\r
+ IoPortForBmid = IdeDev->IoPort->BusMasterBaseAddr + BMIDS_OFFSET;\r
+ } else {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+ }\r
+\r
+ RemainBlockNum = NumberOfBlocks;\r
+ while (RemainBlockNum > 0) {\r
+\r
+ if (RemainBlockNum >= MAX_DMA_COMMAND_SECTORS) {\r
+ //\r
+ // SectorCount is used to record the number of sectors to be read\r
+ // Max 256 sectors can be transfered at a time.\r
+ //\r
+ NumberOfBlocks = MAX_DMA_COMMAND_SECTORS;\r
+ RemainBlockNum -= MAX_DMA_COMMAND_SECTORS;\r
+ } else {\r
+ NumberOfBlocks = (UINT16) RemainBlockNum;\r
+ RemainBlockNum = 0;\r
+ }\r
+\r
+ //\r
+ // Calculate the number of PRD table to make sure the memory region\r
+ // not cross 64K boundary\r
+ //\r
+ ByteCount = NumberOfBlocks * IdeDev->BlkIo.Media->BlockSize;\r
+ PrdTableNum = ((ByteCount >> 16) + 1) + 1;\r
+\r
+ //\r
+ // Build PRD table\r
+ //\r
+ PageCount = EFI_SIZE_TO_PAGES (2 * PrdTableNum * sizeof (IDE_DMA_PRD));\r
+ Status = IdeDev->PciIo->AllocateBuffer (\r
+ IdeDev->PciIo,\r
+ AllocateAnyPages,\r
+ EfiBootServicesData,\r
+ PageCount,\r
+ &MemPage,\r
+ 0\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ ZeroMem ((VOID *) ((UINTN) MemPage), EFI_PAGES_TO_SIZE (PageCount));\r
+\r
+ PrdAddr = (IDE_DMA_PRD *) ((UINTN) MemPage);\r
+ //\r
+ // To make sure PRD is allocated in one 64K page\r
+ //\r
+ if (((UINTN) PrdAddr & 0x0FFFF) > (((UINTN) PrdAddr + PrdTableNum * sizeof (IDE_DMA_PRD) - 1) & 0x0FFFF)) {\r
+ UsedPrdAddr = (IDE_DMA_PRD *) ((UINTN) ((UINT8 *) PrdAddr + 0x10000) & 0xFFFF0000);\r
+ } else {\r
+ if ((UINTN) PrdAddr & 0x03) {\r
+ UsedPrdAddr = (IDE_DMA_PRD *) ((UINTN) ((UINT8 *) PrdAddr + 0x04) & 0xFFFFFFFC);\r
+ } else {\r
+ UsedPrdAddr = PrdAddr;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Build the PRD table\r
+ //\r
+ Status = IdeDev->PciIo->Map (\r
+ IdeDev->PciIo,\r
+ EfiPciIoOperationBusMasterWrite,\r
+ DataBuffer,\r
+ &ByteCount,\r
+ &DeviceAddress,\r
+ &Map\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ IdeDev->PciIo->FreeBuffer (IdeDev->PciIo, PageCount, MemPage);\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ PrdBuffer = (UINT8 *) ((UINTN) DeviceAddress);\r
+ TempPrdAddr = UsedPrdAddr;\r
+ while (TRUE) {\r
+\r
+ ByteAvailable = 0x10000 - ((UINTN) PrdBuffer & 0xFFFF);\r
+\r
+ if (ByteCount <= ByteAvailable) {\r
+ TempPrdAddr->RegionBaseAddr = (UINT32) ((UINTN) PrdBuffer);\r
+ TempPrdAddr->ByteCount = (UINT16) ByteCount;\r
+ TempPrdAddr->EndOfTable = 0x8000;\r
+ break;\r
+ }\r
+\r
+ TempPrdAddr->RegionBaseAddr = (UINT32) ((UINTN) PrdBuffer);\r
+ TempPrdAddr->ByteCount = (UINT16) ByteAvailable;\r
+\r
+ ByteCount -= ByteAvailable;\r
+ PrdBuffer += ByteAvailable;\r
+ TempPrdAddr++;\r
+ }\r
+\r
+ //\r
+ // Set the base address to BMID register\r
+ //\r
+ IdeDev->PciIo->Io.Write (\r
+ IdeDev->PciIo,\r
+ EfiPciIoWidthUint32,\r
+ EFI_PCI_IO_PASS_THROUGH_BAR,\r
+ IoPortForBmid,\r
+ 1,\r
+ &UsedPrdAddr\r
+ );\r
+\r
+ //\r
+ // Set BMIC register to identify the operation direction\r
+ //\r
+ IdeDev->PciIo->Io.Read (\r
+ IdeDev->PciIo,\r
+ EfiPciIoWidthUint8,\r
+ EFI_PCI_IO_PASS_THROUGH_BAR,\r
+ IoPortForBmic,\r
+ 1,\r
+ &RegisterValue\r
+ );\r
+\r
+ RegisterValue |= BMIC_nREAD;\r
+\r
+ IdeDev->PciIo->Io.Write (\r
+ IdeDev->PciIo,\r
+ EfiPciIoWidthUint8,\r
+ EFI_PCI_IO_PASS_THROUGH_BAR,\r
+ IoPortForBmic,\r
+ 1,\r
+ &RegisterValue\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
+ // Issue READ DMA command\r
+ //\r
+ Status = AtaCommandIssue (\r
+ IdeDev,\r
+ READ_DMA_CMD,\r
+ Device,\r
+ 0,\r
+ (UINT16) NumberOfBlocks,\r
+ StartLba\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ IdeDev->PciIo->FreeBuffer (IdeDev->PciIo, PageCount, MemPage);\r
+ IdeDev->PciIo->Unmap (IdeDev->PciIo, Map);\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ //\r
+ // Set START bit of BMIC register\r
+ //\r
+ IdeDev->PciIo->Io.Read (\r
+ IdeDev->PciIo,\r
+ EfiPciIoWidthUint8,\r
+ EFI_PCI_IO_PASS_THROUGH_BAR,\r
+ IoPortForBmic,\r
+ 1,\r
+ &RegisterValue\r
+ );\r
+\r
+ RegisterValue |= BMIC_START;\r
+\r
+ IdeDev->PciIo->Io.Write (\r
+ IdeDev->PciIo,\r
+ EfiPciIoWidthUint8,\r
+ EFI_PCI_IO_PASS_THROUGH_BAR,\r
+ IoPortForBmic,\r
+ 1,\r
+ &RegisterValue\r
+ );\r
+\r
+ //\r
+ // Check the INTERRUPT and ERROR bit of BMIS\r
+ // Max transfer number of sectors for one command is 65536(32Mbyte),\r
+ // it will cost 1 second to transfer these data in UDMA mode 2(33.3MBps).\r
+ // So set the variable Count to 2000, for about 2 second timeout time.\r
+ //\r
+ Count = 2000;\r
+ while (TRUE) {\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
+ if ((RegisterValue & (BMIS_INTERRUPT | BMIS_ERROR)) || (Count == 0)) {\r
+ if ((RegisterValue & BMIS_ERROR) || (Count == 0)) {\r
+ //\r
+ // Clear START bit of BMIC register before return EFI_DEVICE_ERROR\r
+ //\r
+ IdeDev->PciIo->Io.Read (\r
+ IdeDev->PciIo,\r
+ EfiPciIoWidthUint8,\r
+ EFI_PCI_IO_PASS_THROUGH_BAR,\r
+ IoPortForBmic,\r
+ 1,\r
+ &RegisterValue\r
+ );\r
+\r
+ RegisterValue &= ~((UINT8)BMIC_START);\r
+\r
+ IdeDev->PciIo->Io.Write (\r
+ IdeDev->PciIo,\r
+ EfiPciIoWidthUint8,\r
+ EFI_PCI_IO_PASS_THROUGH_BAR,\r
+ IoPortForBmic,\r
+ 1,\r
+ &RegisterValue\r
+ );\r
+ IdeDev->PciIo->FreeBuffer (IdeDev->PciIo, PageCount, MemPage);\r
+ IdeDev->PciIo->Unmap (IdeDev->PciIo, Map);\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+ break;\r
+ }\r
+\r
+ gBS->Stall (1000);\r
+ Count --;\r
+ }\r
+\r
+ IdeDev->PciIo->FreeBuffer (IdeDev->PciIo, PageCount, MemPage);\r
+ IdeDev->PciIo->Unmap (IdeDev->PciIo, Map);\r
+ //\r
+ // Read Status Register of IDE device to clear interrupt\r
+ //\r
+ RegisterValue = IDEReadPortB(IdeDev->PciIo,IdeDev->IoPort->Reg.Status);\r
+ //\r
+ // Clear START bit of BMIC register\r
+ //\r
+ IdeDev->PciIo->Io.Read (\r
+ IdeDev->PciIo,\r
+ EfiPciIoWidthUint8,\r
+ EFI_PCI_IO_PASS_THROUGH_BAR,\r
+ IoPortForBmic,\r
+ 1,\r
+ &RegisterValue\r
+ );\r
+\r
+ RegisterValue &= ~((UINT8) BMIC_START);\r
+\r
+ IdeDev->PciIo->Io.Write (\r
+ IdeDev->PciIo,\r
+ EfiPciIoWidthUint8,\r
+ EFI_PCI_IO_PASS_THROUGH_BAR,\r
+ IoPortForBmic,\r
+ 1,\r
+ &RegisterValue\r
+ );\r
+\r
+ if (RegisterValue & BMIS_ERROR) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ DataBuffer = (UINT8 *) DataBuffer + NumberOfBlocks * IdeDev->BlkIo.Media->BlockSize;\r
+ StartLba += NumberOfBlocks;\r
+ }\r
+\r
+ //\r
+ // Disable interrupt of Select device\r
+ //\r
+ IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Alt.DeviceControl);\r
+ DeviceControl |= IEN_L;\r
+ IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Alt.DeviceControl, DeviceControl);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ This function is called by the AtaBlkIoWriteBlocks() to perform\r
+ writing to media in block unit. The function has been enhanced to\r
+ support >120GB access and transfer at most 65536 blocks per command\r
+\r
+ @param[in] *IdeDev pointer pointing to IDE_BLK_IO_DEV data structure, used\r
+ to record all the information of the IDE device.\r
+\r
+ @param[in] *DataBuffer A pointer to the source buffer for the data.\r
+\r
+ @param[in] StartLba The starting logical block address to write to\r
+ on the device media.\r
+\r
+ @param[in] NumberOfBlocks The number of transfer data blocks.\r
+\r
+ @return The device status of UDMA operation. If the operation is\r
+ successful, return EFI_SUCCESS.\r
+\r
+ TODO: EFI_UNSUPPORTED - add return value to function comment\r
+ TODO: EFI_DEVICE_ERROR - add return value to function comment\r
+ TODO: EFI_DEVICE_ERROR - add return value to function comment\r
+**/\r
+EFI_STATUS\r
+AtaUdmaWriteExt (\r
+ IN IDE_BLK_IO_DEV *IdeDev,\r
+ IN VOID *DataBuffer,\r
+ IN EFI_LBA StartLba,\r
+ IN UINTN NumberOfBlocks\r
+ )\r
+{\r
+ IDE_DMA_PRD *PrdAddr;\r
+ IDE_DMA_PRD *UsedPrdAddr;\r
+ IDE_DMA_PRD *TempPrdAddr;\r
+ UINT8 RegisterValue;\r
+ UINT8 Device;\r
+ UINT64 IoPortForBmic;\r
+ UINT64 IoPortForBmis;\r
+ UINT64 IoPortForBmid;\r
+ EFI_STATUS Status;\r
+ UINTN PrdTableNum;\r
+ UINTN ByteCount;\r
+ UINTN ByteAvailable;\r
+ UINT8 *PrdBuffer;\r
+ UINTN RemainBlockNum;\r
+ UINT8 DeviceControl;\r
+ UINT32 Count;\r
+ UINTN PageCount;\r
+ VOID *Map;\r
+ VOID *MemPage;\r
+ EFI_PHYSICAL_ADDRESS DeviceAddress;\r
+\r
+ //\r
+ // Channel and device differential\r
+ //\r
+ Device = (UINT8) ((IdeDev->Device << 4) | 0xe0);\r
+\r
+ //\r
+ // Enable interrupt to support UDMA and Select device\r
+ //\r
+ DeviceControl = 0;\r
+ IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Alt.DeviceControl, DeviceControl);\r
+\r
+ IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Head, Device);\r
+\r
+ if (IdePrimary == IdeDev->Channel) {\r
+ IoPortForBmic = IdeDev->IoPort->BusMasterBaseAddr + BMICP_OFFSET;\r
+ IoPortForBmis = IdeDev->IoPort->BusMasterBaseAddr + BMISP_OFFSET;\r
+ IoPortForBmid = IdeDev->IoPort->BusMasterBaseAddr + BMIDP_OFFSET;\r
+ } else {\r
+ if (IdeSecondary == IdeDev->Channel) {\r
+ IoPortForBmic = IdeDev->IoPort->BusMasterBaseAddr + BMICS_OFFSET;\r
+ IoPortForBmis = IdeDev->IoPort->BusMasterBaseAddr + BMISS_OFFSET;\r
+ IoPortForBmid = IdeDev->IoPort->BusMasterBaseAddr + BMIDS_OFFSET;\r
+ } else {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+ }\r
+\r
+ RemainBlockNum = NumberOfBlocks;\r
+ while (RemainBlockNum > 0) {\r
+\r
+ if (RemainBlockNum >= MAX_DMA_EXT_COMMAND_SECTORS) {\r
+ //\r
+ // SectorCount is used to record the number of sectors to be read\r
+ // Max 65536 sectors can be transfered at a time.\r
+ //\r
+ NumberOfBlocks = MAX_DMA_EXT_COMMAND_SECTORS;\r
+ RemainBlockNum -= MAX_DMA_EXT_COMMAND_SECTORS;\r
+ } else {\r
+ NumberOfBlocks = (UINT16) RemainBlockNum;\r
+ RemainBlockNum = 0;\r
+ }\r
+\r
+ //\r
+ // Calculate the number of PRD table to make sure the memory region\r
+ // not cross 64K boundary\r
+ //\r
+ ByteCount = NumberOfBlocks * IdeDev->BlkIo.Media->BlockSize;\r
+ PrdTableNum = ((ByteCount >> 16) + 1) + 1;\r
+\r
+ //\r
+ // Build PRD table\r
+ //\r
+ PageCount = EFI_SIZE_TO_PAGES (2 * PrdTableNum * sizeof (IDE_DMA_PRD));\r
+ Status = IdeDev->PciIo->AllocateBuffer (\r
+ IdeDev->PciIo,\r
+ AllocateAnyPages,\r
+ EfiBootServicesData,\r
+ PageCount,\r
+ &MemPage,\r
+ 0\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ ZeroMem ((VOID *) ((UINTN) MemPage), EFI_PAGES_TO_SIZE (PageCount));\r
+\r
+ PrdAddr = (IDE_DMA_PRD *) ((UINTN) MemPage);\r
+ //\r
+ // To make sure PRD is allocated in one 64K page\r
+ //\r
+ if (((UINTN) PrdAddr & 0x0FFFF) > (((UINTN) PrdAddr + PrdTableNum * sizeof (IDE_DMA_PRD) - 1) & 0x0FFFF)) {\r
+ UsedPrdAddr = (IDE_DMA_PRD *) ((UINTN) ((UINT8 *) PrdAddr + 0x10000) & 0xFFFF0000);\r
+ } else {\r
+ if ((UINTN) PrdAddr & 0x03) {\r
+ UsedPrdAddr = (IDE_DMA_PRD *) ((UINTN) ((UINT8 *) PrdAddr + 0x04) & 0xFFFFFFFC);\r
+ } else {\r
+ UsedPrdAddr = PrdAddr;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Build the PRD table\r
+ //\r
+ Status = IdeDev->PciIo->Map (\r
+ IdeDev->PciIo,\r
+ EfiPciIoOperationBusMasterRead,\r
+ DataBuffer,\r
+ &ByteCount,\r
+ &DeviceAddress,\r
+ &Map\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ IdeDev->PciIo->FreeBuffer (IdeDev->PciIo, PageCount, MemPage);\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ PrdBuffer = (UINT8 *) ((UINTN) DeviceAddress);\r
+ TempPrdAddr = UsedPrdAddr;\r
+ while (TRUE) {\r
+\r
+ ByteAvailable = 0x10000 - ((UINTN) PrdBuffer & 0xFFFF);\r
+\r
+ if (ByteCount <= ByteAvailable) {\r
+ TempPrdAddr->RegionBaseAddr = (UINT32) ((UINTN) PrdBuffer);\r
+ TempPrdAddr->ByteCount = (UINT16) ByteCount;\r
+ TempPrdAddr->EndOfTable = 0x8000;\r
+ break;\r
+ }\r
+\r
+ TempPrdAddr->RegionBaseAddr = (UINT32) ((UINTN) PrdBuffer);\r
+ TempPrdAddr->ByteCount = (UINT16) ByteAvailable;\r
+\r
+ ByteCount -= ByteAvailable;\r
+ PrdBuffer += ByteAvailable;\r
+ TempPrdAddr++;\r
+ }\r
+\r
+ //\r
+ // Set the base address to BMID register\r
+ //\r
+ IdeDev->PciIo->Io.Write (\r
+ IdeDev->PciIo,\r
+ EfiPciIoWidthUint32,\r
+ EFI_PCI_IO_PASS_THROUGH_BAR,\r
+ IoPortForBmid,\r
+ 1,\r
+ &UsedPrdAddr\r
+ );\r
+\r
+ //\r
+ // Set BMIC register to identify the operation direction\r
+ //\r
+ IdeDev->PciIo->Io.Read (\r
+ IdeDev->PciIo,\r
+ EfiPciIoWidthUint8,\r
+ EFI_PCI_IO_PASS_THROUGH_BAR,\r
+ IoPortForBmic,\r
+ 1,\r
+ &RegisterValue\r
+ );\r
+ //\r
+ // 0000 1000\r
+ //\r
+ RegisterValue &= ~((UINT8) BMIC_nREAD);\r
+\r
+ IdeDev->PciIo->Io.Write (\r
+ IdeDev->PciIo,\r
+ EfiPciIoWidthUint8,\r
+ EFI_PCI_IO_PASS_THROUGH_BAR,\r
+ IoPortForBmic,\r
+ 1,\r
+ &RegisterValue\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
+ // Issue WRITE DMA EXT command\r
+ //\r
+ Status = AtaCommandIssueExt (\r
+ IdeDev,\r
+ WRITE_DMA_EXT_CMD,\r
+ Device,\r
+ 0,\r
+ (UINT16) NumberOfBlocks,\r
+ StartLba\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ IdeDev->PciIo->FreeBuffer (IdeDev->PciIo, PageCount, MemPage);\r
+ IdeDev->PciIo->Unmap (IdeDev->PciIo, Map);\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ //\r
+ // Set START bit of BMIC register\r
+ //\r
+ IdeDev->PciIo->Io.Read (\r
+ IdeDev->PciIo,\r
+ EfiPciIoWidthUint8,\r
+ EFI_PCI_IO_PASS_THROUGH_BAR,\r
+ IoPortForBmic,\r
+ 1,\r
+ &RegisterValue\r
+ );\r
+\r
+ RegisterValue |= BMIC_START;\r
+\r
+ IdeDev->PciIo->Io.Write (\r
+ IdeDev->PciIo,\r
+ EfiPciIoWidthUint8,\r
+ EFI_PCI_IO_PASS_THROUGH_BAR,\r
+ IoPortForBmic,\r
+ 1,\r
+ &RegisterValue\r
+ );\r
+\r
+ //\r
+ // Check the INTERRUPT and ERROR bit of BMIS\r
+ // Max transfer number of sectors for one command is 65536(32Mbyte),\r
+ // it will cost 1 second to transfer these data in UDMA mode 2(33.3MBps).\r
+ // So set the variable Count to 2000, for about 2 second timeout time.\r
+ //\r
+ Count = 2000;\r
+ while (TRUE) {\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
+ if ((RegisterValue & (BMIS_INTERRUPT | BMIS_ERROR)) || (Count == 0)) {\r
+ if ((RegisterValue & BMIS_ERROR) || (Count == 0)) {\r
+ //\r
+ // Clear START bit of BMIC register before return EFI_DEVICE_ERROR\r
+ //\r
+ IdeDev->PciIo->Io.Read (\r
+ IdeDev->PciIo,\r
+ EfiPciIoWidthUint8,\r
+ EFI_PCI_IO_PASS_THROUGH_BAR,\r
+ IoPortForBmic,\r
+ 1,\r
+ &RegisterValue\r
+ );\r
+\r
+ RegisterValue &= ~((UINT8)BMIC_START);\r
+\r
+ IdeDev->PciIo->Io.Write (\r
+ IdeDev->PciIo,\r
+ EfiPciIoWidthUint8,\r
+ EFI_PCI_IO_PASS_THROUGH_BAR,\r
+ IoPortForBmic,\r
+ 1,\r
+ &RegisterValue\r
+ );\r
+ IdeDev->PciIo->FreeBuffer (IdeDev->PciIo, PageCount, MemPage);\r
+ IdeDev->PciIo->Unmap (IdeDev->PciIo, Map);\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+ break;\r
+ }\r
+\r
+ gBS->Stall (1000);\r
+ Count --;\r
+ }\r
+\r
+ IdeDev->PciIo->FreeBuffer (IdeDev->PciIo, PageCount, MemPage);\r
+ IdeDev->PciIo->Unmap (IdeDev->PciIo, Map);\r
+ //\r
+ // Read Status Register of IDE device to clear interrupt\r
+ //\r
+ RegisterValue = IDEReadPortB(IdeDev->PciIo,IdeDev->IoPort->Reg.Status);\r
+ //\r
+ // Clear START bit of BMIC register\r
+ //\r
+ IdeDev->PciIo->Io.Read (\r
+ IdeDev->PciIo,\r
+ EfiPciIoWidthUint8,\r
+ EFI_PCI_IO_PASS_THROUGH_BAR,\r
+ IoPortForBmic,\r
+ 1,\r
+ &RegisterValue\r
+ );\r
+\r
+ RegisterValue &= ~((UINT8) BMIC_START);\r
+\r
+ IdeDev->PciIo->Io.Write (\r
+ IdeDev->PciIo,\r
+ EfiPciIoWidthUint8,\r
+ EFI_PCI_IO_PASS_THROUGH_BAR,\r
+ IoPortForBmic,\r
+ 1,\r
+ &RegisterValue\r
+ );\r
+\r
+ DataBuffer = (UINT8 *) DataBuffer + NumberOfBlocks * IdeDev->BlkIo.Media->BlockSize;\r
+ StartLba += NumberOfBlocks;\r
+ }\r
+\r
+ //\r
+ // Disable interrupt of Select device\r
+ //\r
+ IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Alt.DeviceControl);\r
+ DeviceControl |= IEN_L;\r
+ IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Alt.DeviceControl, DeviceControl);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ This function is called by the AtaBlkIoWriteBlocks() to perform\r
+ writing to media in block unit. The function has been enhanced to\r
+ support >120GB access and transfer at most 65536 blocks per command\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
+ @param[in] *DataBuffer\r
+ A pointer to the source buffer for the data.\r
+\r
+ @param[in] StartLba\r
+ The starting logical block address to write to\r
+ on the device media.\r
+\r
+ @param[in] NumberOfBlocks\r
+ The number of transfer data blocks.\r
+\r
+ @return The device status of UDMA operation. If the operation is\r
+ successful, return EFI_SUCCESS.\r
+\r
+ TODO: EFI_UNSUPPORTED - add return value to function comment\r
+ TODO: EFI_DEVICE_ERROR - add return value to function comment\r
+ TODO: EFI_DEVICE_ERROR - add return value to function comment\r
+**/\r
+EFI_STATUS\r
+AtaUdmaWrite (\r
+ IN IDE_BLK_IO_DEV *IdeDev,\r
+ IN VOID *DataBuffer,\r
+ IN EFI_LBA StartLba,\r
+ IN UINTN NumberOfBlocks\r
+ )\r
+{\r
+ IDE_DMA_PRD *PrdAddr;\r
+ IDE_DMA_PRD *UsedPrdAddr;\r
+ IDE_DMA_PRD *TempPrdAddr;\r
+ UINT8 RegisterValue;\r
+ UINT8 Device;\r
+ UINT64 IoPortForBmic;\r
+ UINT64 IoPortForBmis;\r
+ UINT64 IoPortForBmid;\r
+ EFI_STATUS Status;\r
+ UINTN PrdTableNum;\r
+ UINTN ByteCount;\r
+ UINTN ByteAvailable;\r
+ UINT8 *PrdBuffer;\r
+ UINTN RemainBlockNum;\r
+ UINT8 DeviceControl;\r
+ UINT32 Count;\r
+ UINTN PageCount;\r
+ VOID *Map;\r
+ VOID *MemPage;\r
+ EFI_PHYSICAL_ADDRESS DeviceAddress;\r
+\r
+ //\r
+ // Channel and device differential\r
+ //\r
+ Device = (UINT8) ((IdeDev->Device << 4) | 0xe0);\r
+\r
+ //\r
+ // Enable interrupt to support UDMA\r
+ //\r
+ DeviceControl = 0;\r
+ IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Alt.DeviceControl, DeviceControl);\r
+\r
+ IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Head, Device);\r
+\r
+ if (IdePrimary == IdeDev->Channel) {\r
+ IoPortForBmic = IdeDev->IoPort->BusMasterBaseAddr + BMICP_OFFSET;\r
+ IoPortForBmis = IdeDev->IoPort->BusMasterBaseAddr + BMISP_OFFSET;\r
+ IoPortForBmid = IdeDev->IoPort->BusMasterBaseAddr + BMIDP_OFFSET;\r
+ } else {\r
+ if (IdeSecondary == IdeDev->Channel) {\r
+ IoPortForBmic = IdeDev->IoPort->BusMasterBaseAddr + BMICS_OFFSET;\r
+ IoPortForBmis = IdeDev->IoPort->BusMasterBaseAddr + BMISS_OFFSET;\r
+ IoPortForBmid = IdeDev->IoPort->BusMasterBaseAddr + BMIDS_OFFSET;\r
+ } else {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+ }\r
+\r
+ RemainBlockNum = NumberOfBlocks;\r
+ while (RemainBlockNum > 0) {\r
+\r
+ if (RemainBlockNum >= MAX_DMA_COMMAND_SECTORS) {\r
+ //\r
+ // SectorCount is used to record the number of sectors to be read\r
+ // Max 256 sectors can be transfered at a time.\r
+ //\r
+ NumberOfBlocks = MAX_DMA_COMMAND_SECTORS;\r
+ RemainBlockNum -= MAX_DMA_COMMAND_SECTORS;\r
+ } else {\r
+ NumberOfBlocks = (UINT16) RemainBlockNum;\r
+ RemainBlockNum = 0;\r
+ }\r
+\r
+ //\r
+ // Calculate the number of PRD table to make sure the memory region\r
+ // not cross 64K boundary\r
+ //\r
+ ByteCount = NumberOfBlocks * IdeDev->BlkIo.Media->BlockSize;\r
+ PrdTableNum = ((ByteCount >> 16) + 1) + 1;\r
+\r
+ //\r
+ // Build PRD table\r
+ //\r
+ PageCount = EFI_SIZE_TO_PAGES (2 * PrdTableNum * sizeof (IDE_DMA_PRD));\r
+ Status = IdeDev->PciIo->AllocateBuffer (\r
+ IdeDev->PciIo,\r
+ AllocateAnyPages,\r
+ EfiBootServicesData,\r
+ PageCount,\r
+ &MemPage,\r
+ 0\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ ZeroMem ((VOID *) ((UINTN) MemPage), EFI_PAGES_TO_SIZE (PageCount));\r
+\r
+ PrdAddr = (IDE_DMA_PRD *) ((UINTN) MemPage);\r
+\r
+ //\r
+ // To make sure PRD is allocated in one 64K page\r
+ //\r
+ if (((UINTN) PrdAddr & 0x0FFFF) > (((UINTN) PrdAddr + PrdTableNum * sizeof (IDE_DMA_PRD) - 1) & 0x0FFFF)) {\r
+ UsedPrdAddr = (IDE_DMA_PRD *) ((UINTN) ((UINT8 *) PrdAddr + 0x10000) & 0xFFFF0000);\r
+ } else {\r
+ if ((UINTN) PrdAddr & 0x03) {\r
+ UsedPrdAddr = (IDE_DMA_PRD *) ((UINTN) ((UINT8 *) PrdAddr + 0x04) & 0xFFFFFFFC);\r
+ } else {\r
+ UsedPrdAddr = PrdAddr;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Build the PRD table\r
+ //\r
+ Status = IdeDev->PciIo->Map (\r
+ IdeDev->PciIo,\r
+ EfiPciIoOperationBusMasterRead,\r
+ DataBuffer,\r
+ &ByteCount,\r
+ &DeviceAddress,\r
+ &Map\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ IdeDev->PciIo->FreeBuffer (IdeDev->PciIo, PageCount, MemPage);\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ PrdBuffer = (UINT8 *) ((UINTN) DeviceAddress);\r
+ TempPrdAddr = UsedPrdAddr;\r
+ while (TRUE) {\r
+\r
+ ByteAvailable = 0x10000 - ((UINTN) PrdBuffer & 0xFFFF);\r
+\r
+ if (ByteCount <= ByteAvailable) {\r
+ TempPrdAddr->RegionBaseAddr = (UINT32) ((UINTN) PrdBuffer);\r
+ TempPrdAddr->ByteCount = (UINT16) ByteCount;\r
+ TempPrdAddr->EndOfTable = 0x8000;\r
+ break;\r
+ }\r
+\r
+ TempPrdAddr->RegionBaseAddr = (UINT32) ((UINTN) PrdBuffer);\r
+ TempPrdAddr->ByteCount = (UINT16) ByteAvailable;\r
+\r
+ ByteCount -= ByteAvailable;\r
+ PrdBuffer += ByteAvailable;\r
+ TempPrdAddr++;\r
+ }\r
+\r
+ //\r
+ // Set the base address to BMID register\r
+ //\r
+ IdeDev->PciIo->Io.Write (\r
+ IdeDev->PciIo,\r
+ EfiPciIoWidthUint32,\r
+ EFI_PCI_IO_PASS_THROUGH_BAR,\r
+ IoPortForBmid,\r
+ 1,\r
+ &UsedPrdAddr\r
+ );\r
+\r
+ //\r
+ // Set BMIC register to identify the operation direction\r
+ //\r
+ IdeDev->PciIo->Io.Read (\r
+ IdeDev->PciIo,\r
+ EfiPciIoWidthUint8,\r
+ EFI_PCI_IO_PASS_THROUGH_BAR,\r
+ IoPortForBmic,\r
+ 1,\r
+ &RegisterValue\r
+ );\r
+ //\r
+ // 0000 1000\r
+ //\r
+ RegisterValue &= ~((UINT8) BMIC_nREAD);\r
+\r
+ IdeDev->PciIo->Io.Write (\r
+ IdeDev->PciIo,\r
+ EfiPciIoWidthUint8,\r
+ EFI_PCI_IO_PASS_THROUGH_BAR,\r
+ IoPortForBmic,\r
+ 1,\r
+ &RegisterValue\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
+ // Issue WRITE DMA command\r
+ //\r
+ Status = AtaCommandIssue (\r
+ IdeDev,\r
+ WRITE_DMA_CMD,\r
+ Device,\r
+ 0,\r
+ (UINT16) NumberOfBlocks,\r
+ StartLba\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ IdeDev->PciIo->FreeBuffer (IdeDev->PciIo, PageCount, MemPage);\r
+ IdeDev->PciIo->Unmap (IdeDev->PciIo, Map);\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ //\r
+ // Set START bit of BMIC register\r
+ //\r
+ IdeDev->PciIo->Io.Read (\r
+ IdeDev->PciIo,\r
+ EfiPciIoWidthUint8,\r
+ EFI_PCI_IO_PASS_THROUGH_BAR,\r
+ IoPortForBmic,\r
+ 1,\r
+ &RegisterValue\r
+ );\r
+\r
+ RegisterValue |= BMIC_START;\r
+\r
+ IdeDev->PciIo->Io.Write (\r
+ IdeDev->PciIo,\r
+ EfiPciIoWidthUint8,\r
+ EFI_PCI_IO_PASS_THROUGH_BAR,\r
+ IoPortForBmic,\r
+ 1,\r
+ &RegisterValue\r
+ );\r
+\r
+ //\r
+ // Check the INTERRUPT and ERROR bit of BMIS\r
+ // Max transfer number of sectors for one command is 65536(32Mbyte),\r
+ // it will cost 1 second to transfer these data in UDMA mode 2(33.3MBps).\r
+ // So set the variable Count to 2000, for about 2 second timeout time.\r
+ //\r
+ Count = 2000;\r
+ while (TRUE) {\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
+ if ((RegisterValue & (BMIS_INTERRUPT | BMIS_ERROR)) || (Count == 0)) {\r
+ if ((RegisterValue & BMIS_ERROR) || (Count == 0)) {\r
+ //\r
+ // Clear START bit of BMIC register before return EFI_DEVICE_ERROR\r
+ //\r
+ IdeDev->PciIo->Io.Read (\r
+ IdeDev->PciIo,\r
+ EfiPciIoWidthUint8,\r
+ EFI_PCI_IO_PASS_THROUGH_BAR,\r
+ IoPortForBmic,\r
+ 1,\r
+ &RegisterValue\r
+ );\r
+\r
+ RegisterValue &= ~((UINT8)BMIC_START);\r
+\r
+ IdeDev->PciIo->Io.Write (\r
+ IdeDev->PciIo,\r
+ EfiPciIoWidthUint8,\r
+ EFI_PCI_IO_PASS_THROUGH_BAR,\r
+ IoPortForBmic,\r
+ 1,\r
+ &RegisterValue\r
+ );\r
+ IdeDev->PciIo->FreeBuffer (IdeDev->PciIo, PageCount, MemPage);\r
+ IdeDev->PciIo->Unmap (IdeDev->PciIo, Map);\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+ break;\r
+ }\r
+\r
+ gBS->Stall (1000);\r
+ Count --;\r
+ }\r
+\r
+ IdeDev->PciIo->FreeBuffer (IdeDev->PciIo, PageCount, MemPage);\r
+ IdeDev->PciIo->Unmap (IdeDev->PciIo, Map);\r
+\r
+ //\r
+ // Read Status Register of IDE device to clear interrupt\r
+ //\r
+ RegisterValue = IDEReadPortB(IdeDev->PciIo,IdeDev->IoPort->Reg.Status);\r
+ //\r
+ // Clear START bit of BMIC register\r
+ //\r
+ IdeDev->PciIo->Io.Read (\r
+ IdeDev->PciIo,\r
+ EfiPciIoWidthUint8,\r
+ EFI_PCI_IO_PASS_THROUGH_BAR,\r
+ IoPortForBmic,\r
+ 1,\r
+ &RegisterValue\r
+ );\r
+\r
+ RegisterValue &= ~((UINT8) BMIC_START);\r
+\r
+ IdeDev->PciIo->Io.Write (\r
+ IdeDev->PciIo,\r
+ EfiPciIoWidthUint8,\r
+ EFI_PCI_IO_PASS_THROUGH_BAR,\r
+ IoPortForBmic,\r
+ 1,\r
+ &RegisterValue\r
+ );\r
+\r
+ DataBuffer = (UINT8 *) DataBuffer + NumberOfBlocks * IdeDev->BlkIo.Media->BlockSize;\r
+ StartLba += NumberOfBlocks;\r
+ }\r
+\r
+ //\r
+ // Disable interrupt of Select device\r
+ //\r
+ IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Alt.DeviceControl);\r
+ DeviceControl |= IEN_L;\r
+ IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Alt.DeviceControl, DeviceControl);\r
+\r
+ return EFI_SUCCESS;\r
+}\r